mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-11 07:29:38 -05:00
Merge pull request #2846 from csoler/v0.6-Collection
Attempt at making RsCollection faster to use (Needs #144)
This commit is contained in:
commit
d92404d047
@ -1 +1 @@
|
|||||||
Subproject commit 353596b0ee5ea76611eb663b90bf3ab1c9f34ad7
|
Subproject commit 9dd9d7f94a600e8c8478887a4f7784fdc3294034
|
@ -1 +1 @@
|
|||||||
Subproject commit 2ddc86fb575a61170f4c06a00152e3e7dc74c8f4
|
Subproject commit 659423769541169457c41f71c8a038e2d64ba079
|
@ -1 +1 @@
|
|||||||
Subproject commit 526cdfb9c1e9f91fa7ef17dcaf447a353eb0caed
|
Subproject commit 0c16c4a07eea82a5946bf49ebccb41d7c1e82368
|
@ -30,7 +30,7 @@
|
|||||||
#include "gui/RetroShareLink.h"
|
#include "gui/RetroShareLink.h"
|
||||||
#include "retroshare-gui/RsAutoUpdatePage.h"
|
#include "retroshare-gui/RsAutoUpdatePage.h"
|
||||||
#include "gui/msgs/MessageComposer.h"
|
#include "gui/msgs/MessageComposer.h"
|
||||||
#include "gui/common/RsCollection.h"
|
#include "gui/common/RsCollectionDialog.h"
|
||||||
#include "gui/common/FilesDefs.h"
|
#include "gui/common/FilesDefs.h"
|
||||||
#include "gui/common/RsUrlHandler.h"
|
#include "gui/common/RsUrlHandler.h"
|
||||||
#include "gui/settings/rsharesettings.h"
|
#include "gui/settings/rsharesettings.h"
|
||||||
@ -497,25 +497,23 @@ void SearchDialog::collCreate()
|
|||||||
int selectedCount = selectedItems.size() ;
|
int selectedCount = selectedItems.size() ;
|
||||||
QTreeWidgetItem * item ;
|
QTreeWidgetItem * item ;
|
||||||
|
|
||||||
for (int i = 0; i < selectedCount; ++i) {
|
RsFileTree tree;
|
||||||
|
|
||||||
|
for (int i = 0; i < selectedCount; ++i)
|
||||||
|
{
|
||||||
item = selectedItems.at(i) ;
|
item = selectedItems.at(i) ;
|
||||||
|
|
||||||
if (!item->text(SR_HASH_COL).isEmpty()) {
|
if (!item->text(SR_HASH_COL).isEmpty())
|
||||||
|
{
|
||||||
std::string name = item->text(SR_NAME_COL).toUtf8().constData();
|
std::string name = item->text(SR_NAME_COL).toUtf8().constData();
|
||||||
RsFileHash hash( item->text(SR_HASH_COL).toStdString() );
|
RsFileHash hash( item->text(SR_HASH_COL).toStdString() );
|
||||||
uint64_t count = item->text(SR_SIZE_COL).toULongLong();
|
uint64_t count = item->text(SR_SIZE_COL).toULongLong();
|
||||||
|
|
||||||
DirDetails details;
|
tree.addFile(tree.root(),name,hash,count);
|
||||||
details.name = name;
|
|
||||||
details.hash = hash;
|
|
||||||
details.size = count;
|
|
||||||
details.type = DIR_TYPE_FILE;
|
|
||||||
|
|
||||||
dirVec.push_back(details);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection(dirVec,RS_FILE_HINTS_LOCAL).openNewColl(this);
|
RsCollectionDialog::openNewCollection(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchDialog::collModif()
|
void SearchDialog::collModif()
|
||||||
@ -542,12 +540,8 @@ void SearchDialog::collModif()
|
|||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath());
|
||||||
RsCollection collection;
|
|
||||||
collection.openColl(qinfo.absoluteFilePath());
|
|
||||||
}//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString))
|
|
||||||
}//if (qinfo.exists())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchDialog::collView()
|
void SearchDialog::collView()
|
||||||
@ -574,12 +568,8 @@ void SearchDialog::collView()
|
|||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(), true);
|
||||||
RsCollection collection;
|
|
||||||
collection.openColl(qinfo.absoluteFilePath(), true);
|
|
||||||
}//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString))
|
|
||||||
}//if (qinfo.exists())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchDialog::collOpen()
|
void SearchDialog::collOpen()
|
||||||
@ -597,32 +587,35 @@ void SearchDialog::collOpen()
|
|||||||
|
|
||||||
if (rsFiles->FileDetails(hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL
|
if (rsFiles->FileDetails(hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL
|
||||||
| RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE
|
| RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE
|
||||||
| RS_FILE_HINTS_SPEC_ONLY, info)) {
|
| RS_FILE_HINTS_SPEC_ONLY, info))
|
||||||
|
{
|
||||||
/* make path for downloaded files */
|
/* make path for downloaded files */
|
||||||
std::string path;
|
std::string path;
|
||||||
path = info.path;
|
path = info.path;
|
||||||
|
|
||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
|
RsCollection::RsCollectionErrorCode err;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::downloadFiles(RsCollection(qinfo.absoluteFilePath(),err));
|
||||||
RsCollection collection;
|
|
||||||
if (collection.load(qinfo.absoluteFilePath())) {
|
|
||||||
collection.downloadFiles();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection collection;
|
QString fileName;
|
||||||
if (collection.load(this)) {
|
if (!misc::getOpenFileName(nullptr, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Open collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
|
||||||
collection.downloadFiles();
|
return ;
|
||||||
}//if (collection.load(this))
|
|
||||||
|
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
|
||||||
|
|
||||||
|
RsCollection::RsCollectionErrorCode err;
|
||||||
|
RsCollection collection(fileName, err);
|
||||||
|
|
||||||
|
if(err == RsCollection::RsCollectionErrorCode::NO_ERROR)
|
||||||
|
RsCollectionDialog::downloadFiles(collection);
|
||||||
|
else
|
||||||
|
QMessageBox::information(nullptr,tr("Error open RsCollection file"),RsCollection::errorString(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchDialog::downloadDirectory(const QTreeWidgetItem *item, const QString &base)
|
void SearchDialog::downloadDirectory(const QTreeWidgetItem *item, const QString &base)
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include "gui/RetroShareLink.h"
|
#include "gui/RetroShareLink.h"
|
||||||
#include "gui/ShareManager.h"
|
#include "gui/ShareManager.h"
|
||||||
#include "gui/common/PeerDefs.h"
|
#include "gui/common/PeerDefs.h"
|
||||||
#include "gui/common/RsCollection.h"
|
#include "gui/common/RsCollectionDialog.h"
|
||||||
#include "gui/msgs/MessageComposer.h"
|
#include "gui/msgs/MessageComposer.h"
|
||||||
#include "gui/gxschannels/GxsChannelDialog.h"
|
#include "gui/gxschannels/GxsChannelDialog.h"
|
||||||
#include "gui/gxsforums/GxsForumsDialog.h"
|
#include "gui/gxsforums/GxsForumsDialog.h"
|
||||||
@ -652,7 +652,7 @@ void SharedFilesDialog::copyLinks(const QModelIndexList& lst, bool remote,QList<
|
|||||||
|
|
||||||
QString dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName();
|
QString dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName();
|
||||||
|
|
||||||
RetroShareLink link = RetroShareLink::createFileTree(dir_name,ft->mTotalSize,ft->mTotalFiles,QString::fromStdString(ft->toRadix64())) ;
|
RetroShareLink link = RetroShareLink::createFileTree(dir_name,ft->totalFileSize(),ft->numFiles(),QString::fromStdString(ft->toRadix64())) ;
|
||||||
|
|
||||||
if(link.valid())
|
if(link.valid())
|
||||||
urls.push_back(link) ;
|
urls.push_back(link) ;
|
||||||
@ -734,7 +734,32 @@ void SharedFilesDialog::sendLinkTo()
|
|||||||
void SharedFilesDialog::collCreate()
|
void SharedFilesDialog::collCreate()
|
||||||
{
|
{
|
||||||
QModelIndexList lst = getSelected();
|
QModelIndexList lst = getSelected();
|
||||||
model->createCollectionFile(this, lst);
|
|
||||||
|
std::vector <DirDetails> dirVec;
|
||||||
|
model->getDirDetailsFromSelect(lst, dirVec);
|
||||||
|
|
||||||
|
auto RemoteMode = isRemote();
|
||||||
|
FileSearchFlags f = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ;
|
||||||
|
|
||||||
|
QString dir_name;
|
||||||
|
|
||||||
|
if(!RemoteMode)
|
||||||
|
{
|
||||||
|
if(!dirVec.empty())
|
||||||
|
{
|
||||||
|
const DirDetails& details = dirVec[0];
|
||||||
|
dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RsFileTree tree;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dirVec.size();++i)
|
||||||
|
tree.addFileTree(tree.root(),*RsFileTree::fromDirDetails(dirVec[i],RemoteMode,true));
|
||||||
|
|
||||||
|
RsCollectionDialog::openNewCollection(tree);
|
||||||
|
|
||||||
|
//auto ft = RsFileTree::fromDirDetails(details,remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedFilesDialog::collModif()
|
void SharedFilesDialog::collModif()
|
||||||
@ -759,12 +784,8 @@ void SharedFilesDialog::collModif()
|
|||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::editExistingCollection(qinfo.absoluteFilePath());
|
||||||
RsCollection collection;
|
|
||||||
collection.openColl(qinfo.absoluteFilePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedFilesDialog::collView()
|
void SharedFilesDialog::collView()
|
||||||
@ -789,12 +810,8 @@ void SharedFilesDialog::collView()
|
|||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(), true);
|
||||||
RsCollection collection;
|
|
||||||
collection.openColl(qinfo.absoluteFilePath(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedFilesDialog::collOpen()
|
void SharedFilesDialog::collOpen()
|
||||||
@ -821,20 +838,24 @@ void SharedFilesDialog::collOpen()
|
|||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists()) {
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
||||||
RsCollection collection;
|
|
||||||
if (collection.load(qinfo.absoluteFilePath())) {
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(),true);
|
||||||
collection.downloadFiles();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection collection;
|
QString fileName;
|
||||||
if (collection.load(this)) {
|
if (!misc::getOpenFileName(nullptr, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Open collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
|
||||||
collection.downloadFiles();
|
return ;
|
||||||
}
|
|
||||||
|
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
|
||||||
|
|
||||||
|
RsCollection::RsCollectionErrorCode err;
|
||||||
|
RsCollection collection(fileName,err);
|
||||||
|
|
||||||
|
if(err == RsCollection::RsCollectionErrorCode::NO_ERROR)
|
||||||
|
RsCollectionDialog::downloadFiles(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalSharedFilesDialog::playselectedfiles()
|
void LocalSharedFilesDialog::playselectedfiles()
|
||||||
@ -1145,12 +1166,14 @@ void LocalSharedFilesDialog::spawnCustomPopupMenu( QPoint point )
|
|||||||
collViewAct->setEnabled(bIsRsColl);
|
collViewAct->setEnabled(bIsRsColl);
|
||||||
collOpenAct->setEnabled(true);
|
collOpenAct->setEnabled(true);
|
||||||
|
|
||||||
QMenu collectionMenu(tr("Collection"), this);
|
QMenu collectionMenu(tr("Retroshare Collection"), this);
|
||||||
collectionMenu.setIcon(QIcon(IMAGE_LIBRARY));
|
collectionMenu.setIcon(QIcon(IMAGE_LIBRARY));
|
||||||
collectionMenu.addAction(collCreateAct);
|
collectionMenu.addAction(collCreateAct);
|
||||||
collectionMenu.addAction(collModifAct);
|
|
||||||
collectionMenu.addAction(collViewAct);
|
if(bIsRsColl)
|
||||||
collectionMenu.addAction(collOpenAct);
|
collectionMenu.addAction(collModifAct);
|
||||||
|
//collectionMenu.addAction(collViewAct);
|
||||||
|
//collectionMenu.addAction(collOpenAct);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DIR_TYPE_DIR :
|
case DIR_TYPE_DIR :
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#include "gui/SoundManager.h"
|
#include "gui/SoundManager.h"
|
||||||
#include "gui/RetroShareLink.h"
|
#include "gui/RetroShareLink.h"
|
||||||
#include "gui/common/FilesDefs.h"
|
#include "gui/common/FilesDefs.h"
|
||||||
#include "gui/common/RsCollection.h"
|
#include "gui/common/RsCollectionDialog.h"
|
||||||
#include "gui/common/RSTreeView.h"
|
#include "gui/common/RSTreeView.h"
|
||||||
#include "gui/common/RsUrlHandler.h"
|
#include "gui/common/RsUrlHandler.h"
|
||||||
#include "gui/FileTransfer/DetailsDialog.h"
|
#include "gui/FileTransfer/DetailsDialog.h"
|
||||||
@ -1975,7 +1975,7 @@ void TransfersDialog::pasteLink()
|
|||||||
for(auto &it : links)
|
for(auto &it : links)
|
||||||
col.merge_in(it.name(),it.size(),RsFileHash(it.hash().toStdString())) ;
|
col.merge_in(it.name(),it.size(),RsFileHash(it.hash().toStdString())) ;
|
||||||
|
|
||||||
col.downloadFiles();
|
RsCollectionDialog::downloadFiles(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransfersDialog::getDLSelectedItems(std::set<RsFileHash> *ids, std::set<int> *rows)
|
void TransfersDialog::getDLSelectedItems(std::set<RsFileHash> *ids, std::set<int> *rows)
|
||||||
@ -2466,21 +2466,17 @@ void TransfersDialog::collCreate()
|
|||||||
std::set<RsFileHash>::iterator it ;
|
std::set<RsFileHash>::iterator it ;
|
||||||
getDLSelectedItems(&items, NULL);
|
getDLSelectedItems(&items, NULL);
|
||||||
|
|
||||||
|
RsFileTree tree;
|
||||||
|
|
||||||
for (it = items.begin(); it != items.end(); ++it)
|
for (it = items.begin(); it != items.end(); ++it)
|
||||||
{
|
{
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
if (!rsFiles->FileDetails(*it, RS_FILE_HINTS_DOWNLOAD, info)) continue;
|
if (!rsFiles->FileDetails(*it, RS_FILE_HINTS_DOWNLOAD, info)) continue;
|
||||||
|
|
||||||
DirDetails details;
|
tree.addFile(tree.root(),info.fname,info.hash,info.size);
|
||||||
details.name = info.fname;
|
|
||||||
details.hash = info.hash;
|
|
||||||
details.size = info.size;
|
|
||||||
details.type = DIR_TYPE_FILE;
|
|
||||||
|
|
||||||
dirVec.push_back(details);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection(dirVec,RS_FILE_HINTS_LOCAL).openNewColl(this);
|
RsCollectionDialog::openNewCollection(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransfersDialog::collModif()
|
void TransfersDialog::collModif()
|
||||||
@ -2504,12 +2500,8 @@ void TransfersDialog::collModif()
|
|||||||
/* open collection */
|
/* open collection */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath());
|
||||||
RsCollection collection;
|
|
||||||
collection.openColl(qinfo.absoluteFilePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2534,12 +2526,8 @@ void TransfersDialog::collView()
|
|||||||
/* open collection */
|
/* open collection */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(), true);
|
||||||
RsCollection collection;
|
|
||||||
collection.openColl(qinfo.absoluteFilePath(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2564,23 +2552,29 @@ void TransfersDialog::collOpen()
|
|||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
{
|
||||||
RsCollection collection;
|
RsCollection::RsCollectionErrorCode code;
|
||||||
if (collection.load(qinfo.absoluteFilePath())) {
|
RsCollectionDialog::downloadFiles(RsCollection(qinfo.absoluteFilePath(),code));
|
||||||
collection.downloadFiles();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection collection;
|
QString fileName;
|
||||||
if (collection.load(this)) {
|
if (!misc::getOpenFileName(nullptr, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Open collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
|
||||||
collection.downloadFiles();
|
return ;
|
||||||
}
|
|
||||||
|
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
|
||||||
|
|
||||||
|
RsCollection::RsCollectionErrorCode code;
|
||||||
|
RsCollection collection(fileName,code);
|
||||||
|
|
||||||
|
if(code == RsCollection::RsCollectionErrorCode::NO_ERROR)
|
||||||
|
RsCollectionDialog::downloadFiles(collection);
|
||||||
|
else
|
||||||
|
QMessageBox::information(nullptr,tr("Error openning collection file"),RsCollection::errorString(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransfersDialog::collAutoOpen(const QString &fileHash)
|
void TransfersDialog::collAutoOpen(const QString &fileHash)
|
||||||
@ -2592,21 +2586,18 @@ void TransfersDialog::collAutoOpen(const QString &fileHash)
|
|||||||
if (rsFiles->FileDetails(hash, RS_FILE_HINTS_DOWNLOAD, info)) {
|
if (rsFiles->FileDetails(hash, RS_FILE_HINTS_DOWNLOAD, info)) {
|
||||||
|
|
||||||
/* make path for downloaded files */
|
/* make path for downloaded files */
|
||||||
if (info.downloadStatus == FT_STATE_COMPLETE) {
|
if (info.downloadStatus == FT_STATE_COMPLETE)
|
||||||
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
path = info.path + "/" + info.fname;
|
path = info.path + "/" + info.fname;
|
||||||
|
|
||||||
/* open file with a suitable application */
|
/* open file with a suitable application */
|
||||||
QFileInfo qinfo;
|
QFileInfo qinfo;
|
||||||
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
qinfo.setFile(QString::fromUtf8(path.c_str()));
|
||||||
if (qinfo.exists()) {
|
RsCollection::RsCollectionErrorCode err;
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
|
||||||
RsCollection collection;
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
if (collection.load(qinfo.absoluteFilePath(), false)) {
|
RsCollectionDialog::downloadFiles(RsCollection(qinfo.absoluteFilePath(),err));
|
||||||
collection.autoDownloadFiles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@
|
|||||||
#include "gui/statistics/StatisticsWindow.h"
|
#include "gui/statistics/StatisticsWindow.h"
|
||||||
|
|
||||||
#include "gui/connect/ConnectFriendWizard.h"
|
#include "gui/connect/ConnectFriendWizard.h"
|
||||||
#include "gui/common/RsCollection.h"
|
#include "gui/common/RsCollectionDialog.h"
|
||||||
#include "settings/rsettingswin.h"
|
#include "settings/rsettingswin.h"
|
||||||
#include "settings/rsharesettings.h"
|
#include "settings/rsharesettings.h"
|
||||||
#include "common/StatusDefs.h"
|
#include "common/StatusDefs.h"
|
||||||
@ -1705,19 +1705,9 @@ void MainWindow::retroshareLinkActivated(const QUrl &url)
|
|||||||
void MainWindow::openRsCollection(const QString &filename)
|
void MainWindow::openRsCollection(const QString &filename)
|
||||||
{
|
{
|
||||||
QFileInfo qinfo(filename);
|
QFileInfo qinfo(filename);
|
||||||
if (qinfo.exists()) {
|
|
||||||
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
|
|
||||||
RsCollection collection;
|
|
||||||
|
|
||||||
if(!collection.load(filename))
|
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
|
||||||
{
|
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath());
|
||||||
RsErr() << "Could not open Rscollection file " << filename.toStdString();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
collection.downloadFiles();
|
|
||||||
//collection.openColl(qinfo.absoluteFilePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::switchVisibilityStatus(StatusElement e,bool b)
|
void MainWindow::switchVisibilityStatus(StatusElement e,bool b)
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include "gui/common/FilesDefs.h"
|
#include "gui/common/FilesDefs.h"
|
||||||
#include "gui/common/GroupDefs.h"
|
#include "gui/common/GroupDefs.h"
|
||||||
#include "gui/common/RsCollection.h"
|
#include "gui/common/RsCollectionDialog.h"
|
||||||
#include "gui/common/RsUrlHandler.h"
|
#include "gui/common/RsUrlHandler.h"
|
||||||
#include "gui/gxs/GxsIdDetails.h"
|
#include "gui/gxs/GxsIdDetails.h"
|
||||||
#include "retroshare/rsfiles.h"
|
#include "retroshare/rsfiles.h"
|
||||||
@ -1233,31 +1233,6 @@ bool RetroshareDirModel::requestDirDetails(void *ref, bool remote,DirDetails& d)
|
|||||||
return false ;
|
return false ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RetroshareDirModel::createCollectionFile(QWidget *parent, const QModelIndexList &list)
|
|
||||||
{
|
|
||||||
/* if(RemoteMode)
|
|
||||||
{
|
|
||||||
std::cerr << "Cannot create a collection file from remote" << std::endl;
|
|
||||||
return ;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
std::vector <DirDetails> dirVec;
|
|
||||||
getDirDetailsFromSelect(list, dirVec);
|
|
||||||
|
|
||||||
FileSearchFlags f = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ;
|
|
||||||
|
|
||||||
QString dir_name;
|
|
||||||
if(!RemoteMode)
|
|
||||||
{
|
|
||||||
if(!dirVec.empty())
|
|
||||||
{
|
|
||||||
const DirDetails& details = dirVec[0];
|
|
||||||
dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RsCollection(dirVec,f).openNewColl(parent,dir_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RetroshareDirModel::downloadSelected(const QModelIndexList &list,bool interactive)
|
void RetroshareDirModel::downloadSelected(const QModelIndexList &list,bool interactive)
|
||||||
{
|
{
|
||||||
if (!RemoteMode)
|
if (!RemoteMode)
|
||||||
@ -1278,7 +1253,7 @@ void RetroshareDirModel::downloadSelected(const QModelIndexList &list,bool inter
|
|||||||
FileSearchFlags f = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ;
|
FileSearchFlags f = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ;
|
||||||
|
|
||||||
if(interactive)
|
if(interactive)
|
||||||
RsCollection(dirVec,f).downloadFiles() ;
|
RsCollectionDialog::downloadFiles(RsCollection(dirVec,f)) ;
|
||||||
else /* Fire off requests */
|
else /* Fire off requests */
|
||||||
for (int i = 0, n = dirVec.size(); i < n; ++i)
|
for (int i = 0, n = dirVec.size(); i < n; ++i)
|
||||||
{
|
{
|
||||||
|
@ -68,8 +68,6 @@ class RetroshareDirModel : public QAbstractItemModel
|
|||||||
|
|
||||||
/* Callback from GUI */
|
/* Callback from GUI */
|
||||||
void downloadSelected(const QModelIndexList &list, bool interactive);
|
void downloadSelected(const QModelIndexList &list, bool interactive);
|
||||||
void createCollectionFile(QWidget *parent, const QModelIndexList &list);
|
|
||||||
|
|
||||||
void getDirDetailsFromSelect (const QModelIndexList &list, std::vector <DirDetails>& dirVec);
|
void getDirDetailsFromSelect (const QModelIndexList &list, std::vector <DirDetails>& dirVec);
|
||||||
|
|
||||||
int getType ( const QModelIndex & index ) const ;
|
int getType ( const QModelIndex & index ) const ;
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include "HomePage.h"
|
#include "HomePage.h"
|
||||||
#include "chat/ChatDialog.h"
|
#include "chat/ChatDialog.h"
|
||||||
#include "common/PeerDefs.h"
|
#include "common/PeerDefs.h"
|
||||||
#include "common/RsCollection.h"
|
#include "common/RsCollectionDialog.h"
|
||||||
#include "common/RsUrlHandler.h"
|
#include "common/RsUrlHandler.h"
|
||||||
#include "connect/ConfCertDialog.h"
|
#include "connect/ConfCertDialog.h"
|
||||||
#include "connect/ConnectFriendWizard.h"
|
#include "connect/ConnectFriendWizard.h"
|
||||||
@ -1143,11 +1143,13 @@ QString RetroShareLink::toHtmlSize() const
|
|||||||
|
|
||||||
if (type() == TYPE_FILE && RsCollection::isCollectionFile(name())) {
|
if (type() == TYPE_FILE && RsCollection::isCollectionFile(name())) {
|
||||||
FileInfo finfo;
|
FileInfo finfo;
|
||||||
if (rsFiles->FileDetails(RsFileHash(hash().toStdString()), RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL, finfo)) {
|
if (rsFiles->FileDetails(RsFileHash(hash().toStdString()), RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL, finfo))
|
||||||
RsCollection collection;
|
{
|
||||||
if (collection.load(QString::fromUtf8(finfo.path.c_str()), false)) {
|
RsCollection::RsCollectionErrorCode code;
|
||||||
|
RsCollection collection(QString::fromUtf8(finfo.path.c_str()), code) ;
|
||||||
|
|
||||||
|
if(code == RsCollection::RsCollectionErrorCode::NO_ERROR)
|
||||||
size += QString(" [%1]").arg(misc::friendlyUnit(collection.size()));
|
size += QString(" [%1]").arg(misc::friendlyUnit(collection.size()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QString link = QString("<a href=\"%1\">%2</a> <font color=\"blue\">%3</font>").arg(toString()).arg(name()).arg(size);
|
QString link = QString("<a href=\"%1\">%2</a> <font color=\"blue\">%3</font>").arg(toString()).arg(name()).arg(size);
|
||||||
@ -1722,10 +1724,9 @@ static void processList(const QStringList &list, const QString &textSingular, co
|
|||||||
|
|
||||||
case TYPE_FILE_TREE:
|
case TYPE_FILE_TREE:
|
||||||
{
|
{
|
||||||
auto ft = RsFileTree::fromRadix64(
|
auto ft = RsFileTree::fromRadix64(link.radix().toStdString() );
|
||||||
link.radix().toStdString() );
|
RsCollectionDialog::downloadFiles(RsCollection(*ft));
|
||||||
RsCollection(*ft).downloadFiles();
|
break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_CHAT_ROOM:
|
case TYPE_CHAT_ROOM:
|
||||||
@ -1776,7 +1777,7 @@ static void processList(const QStringList &list, const QString &textSingular, co
|
|||||||
|
|
||||||
// were single file links found?
|
// were single file links found?
|
||||||
if (fileLinkFound)
|
if (fileLinkFound)
|
||||||
col.downloadFiles();
|
RsCollectionDialog::downloadFiles(col);
|
||||||
|
|
||||||
int countProcessed = 0;
|
int countProcessed = 0;
|
||||||
int countError = 0;
|
int countError = 0;
|
||||||
|
@ -37,534 +37,411 @@
|
|||||||
|
|
||||||
const QString RsCollection::ExtensionString = QString("rscollection") ;
|
const QString RsCollection::ExtensionString = QString("rscollection") ;
|
||||||
|
|
||||||
RsCollection::RsCollection(QObject *parent)
|
RsCollection::RsCollection()
|
||||||
: QObject(parent), _xml_doc("RsCollection")
|
|
||||||
{
|
{
|
||||||
_root = _xml_doc.createElement("RsCollection");
|
mFileTree = std::unique_ptr<RsFileTree>(new RsFileTree());
|
||||||
_xml_doc.appendChild(_root);
|
}
|
||||||
|
RsCollection::RsCollection(const RsFileTree& ft)
|
||||||
|
{
|
||||||
|
mFileTree = std::unique_ptr<RsFileTree>(new RsFileTree(ft));
|
||||||
|
|
||||||
|
for(uint64_t i=0;i<mFileTree->numFiles();++i)
|
||||||
|
mHashes.insert(std::make_pair(mFileTree->fileData(i).hash,i ));
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection::RsCollection(const RsFileTree& fr)
|
RsCollection::RsCollection(const std::vector<DirDetails>& file_infos,FileSearchFlags flags)
|
||||||
: _xml_doc("RsCollection")
|
: mFileTree(new RsFileTree)
|
||||||
{
|
{
|
||||||
_root = _xml_doc.createElement("RsCollection");
|
if(! ( (flags & RS_FILE_HINTS_LOCAL) || (flags & RS_FILE_HINTS_REMOTE)))
|
||||||
_xml_doc.appendChild(_root);
|
{
|
||||||
|
std::cerr << "(EE) Wrong flags passed to RsCollection constructor. Please fix the code!" << std::endl;
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
recursAddElements(_xml_doc,fr,0,_root) ;
|
for(uint32_t i = 0;i<file_infos.size();++i)
|
||||||
}
|
recursAddElements(mFileTree->root(),file_infos[i],flags) ;
|
||||||
|
|
||||||
RsCollection::RsCollection(const std::vector<DirDetails>& file_infos,FileSearchFlags flags, QObject *parent)
|
for(uint64_t i=0;i<mFileTree->numFiles();++i)
|
||||||
: QObject(parent), _xml_doc("RsCollection")
|
mHashes.insert(std::make_pair(mFileTree->fileData(i).hash,i ));
|
||||||
{
|
|
||||||
_root = _xml_doc.createElement("RsCollection");
|
|
||||||
_xml_doc.appendChild(_root);
|
|
||||||
|
|
||||||
if(! ( (flags & RS_FILE_HINTS_LOCAL) || (flags & RS_FILE_HINTS_REMOTE)))
|
|
||||||
{
|
|
||||||
std::cerr << "(EE) Wrong flags passed to RsCollection constructor. Please fix the code!" << std::endl;
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(uint32_t i = 0;i<file_infos.size();++i)
|
|
||||||
recursAddElements(_xml_doc,file_infos[i],_root,flags) ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RsCollection::~RsCollection()
|
RsCollection::~RsCollection()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RsCollection::downloadFiles() const
|
|
||||||
{
|
|
||||||
// print out the element names of all elements that are direct children
|
|
||||||
// of the outermost element.
|
|
||||||
QDomElement docElem = _xml_doc.documentElement();
|
|
||||||
|
|
||||||
std::vector<ColFileInfo> colFileInfos ;
|
|
||||||
|
|
||||||
recursCollectColFileInfos(docElem,colFileInfos,QString(),false) ;
|
|
||||||
|
|
||||||
RsCollectionDialog(_fileName, colFileInfos, false).exec() ;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RsCollection::autoDownloadFiles() const
|
|
||||||
{
|
|
||||||
QDomElement docElem = _xml_doc.documentElement();
|
|
||||||
|
|
||||||
std::vector<ColFileInfo> colFileInfos;
|
|
||||||
|
|
||||||
recursCollectColFileInfos(docElem,colFileInfos,QString(),false);
|
|
||||||
|
|
||||||
QString dlDir = QString::fromUtf8(rsFiles->getDownloadDirectory().c_str());
|
|
||||||
|
|
||||||
foreach(ColFileInfo colFileInfo, colFileInfos)
|
|
||||||
{
|
|
||||||
autoDownloadFiles(colFileInfo, dlDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RsCollection::autoDownloadFiles(ColFileInfo colFileInfo, QString dlDir) const
|
void RsCollection::autoDownloadFiles(ColFileInfo colFileInfo, QString dlDir) const
|
||||||
{
|
{
|
||||||
if (!colFileInfo.filename_has_wrong_characters)
|
if (!colFileInfo.filename_has_wrong_characters)
|
||||||
{
|
{
|
||||||
QString cleanPath = dlDir + colFileInfo.path ;
|
QString cleanPath = dlDir + colFileInfo.path ;
|
||||||
std::cout << "making directory " << cleanPath.toStdString() << std::endl;
|
std::cout << "making directory " << cleanPath.toStdString() << std::endl;
|
||||||
|
|
||||||
if(!QDir(QApplication::applicationDirPath()).mkpath(cleanPath))
|
if(!QDir(QApplication::applicationDirPath()).mkpath(cleanPath))
|
||||||
std::cerr << "Unable to make path: " + cleanPath.toStdString() << std::endl;
|
std::cerr << "Unable to make path: " + cleanPath.toStdString() << std::endl;
|
||||||
|
|
||||||
if (colFileInfo.type==DIR_TYPE_FILE)
|
if (colFileInfo.type==DIR_TYPE_FILE)
|
||||||
rsFiles->FileRequest(colFileInfo.name.toUtf8().constData(),
|
rsFiles->FileRequest(colFileInfo.name.toUtf8().constData(),
|
||||||
RsFileHash(colFileInfo.hash.toStdString()),
|
RsFileHash(colFileInfo.hash.toStdString()),
|
||||||
colFileInfo.size,
|
colFileInfo.size,
|
||||||
cleanPath.toUtf8().constData(),
|
cleanPath.toUtf8().constData(),
|
||||||
RS_FILE_REQ_ANONYMOUS_ROUTING,
|
RS_FILE_REQ_ANONYMOUS_ROUTING,
|
||||||
std::list<RsPeerId>());
|
std::list<RsPeerId>());
|
||||||
}
|
}
|
||||||
foreach(ColFileInfo colFileInfoChild, colFileInfo.children)
|
foreach(ColFileInfo colFileInfoChild, colFileInfo.children)
|
||||||
{
|
{
|
||||||
autoDownloadFiles(colFileInfoChild, dlDir);
|
autoDownloadFiles(colFileInfoChild, dlDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString purifyFileName(const QString& input,bool& bad)
|
static QString purifyFileName(const QString& input,bool& bad)
|
||||||
{
|
{
|
||||||
static const QString bad_chars = "/\\\"*:?<>|" ;
|
static const QString bad_chars = "/\\\"*:?<>|" ;
|
||||||
bad = false ;
|
bad = false ;
|
||||||
QString output = input ;
|
QString output = input ;
|
||||||
|
|
||||||
for(int i=0;i<output.length();++i)
|
for(int i=0;i<output.length();++i)
|
||||||
for(int j=0;j<bad_chars.length();++j)
|
for(int j=0;j<bad_chars.length();++j)
|
||||||
if(output[i] == bad_chars[j])
|
if(output[i] == bad_chars[j])
|
||||||
{
|
{
|
||||||
output[i] = '_' ;
|
output[i] = '_' ;
|
||||||
bad = true ;
|
bad = true ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return output ;
|
return output ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RsCollection::merge_in(const QString& fname,uint64_t size,const RsFileHash& hash)
|
void RsCollection::merge_in(const QString& fname,uint64_t size,const RsFileHash& hash,RsFileTree::DirIndex parent_index)
|
||||||
{
|
{
|
||||||
ColFileInfo info ;
|
mHashes[hash]= mFileTree->addFile(parent_index,fname.toStdString(),hash,size);
|
||||||
info.type = DIR_TYPE_FILE ;
|
|
||||||
info.name = fname ;
|
|
||||||
info.size = size ;
|
|
||||||
info.hash = QString::fromStdString(hash.toStdString()) ;
|
|
||||||
|
|
||||||
recursAddElements(_xml_doc,info,_root) ;
|
|
||||||
}
|
}
|
||||||
void RsCollection::merge_in(const RsFileTree& tree)
|
void RsCollection::merge_in(const RsFileTree& tree, RsFileTree::DirIndex parent_index)
|
||||||
{
|
{
|
||||||
recursAddElements(_xml_doc,tree,0,_root) ;
|
recursMergeTree(mFileTree->root(),tree,tree.directoryData(parent_index)) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RsCollection::recursCollectColFileInfos(const QDomElement& e,std::vector<ColFileInfo>& colFileInfos,const QString& current_path, bool bad_chars_in_parent) const
|
void RsCollection::recursMergeTree(RsFileTree::DirIndex parent,const RsFileTree& tree,const RsFileTree::DirData& dd)
|
||||||
{
|
{
|
||||||
QDomNode n = e.firstChild() ;
|
for(uint32_t i=0;i<dd.subfiles.size();++i)
|
||||||
#ifdef COLLECTION_DEBUG
|
{
|
||||||
std::cerr << "Parsing element " << e.tagName().toStdString() << std::endl;
|
const RsFileTree::FileData& fd(tree.fileData(dd.subfiles[i]));
|
||||||
#endif
|
mHashes[fd.hash] = mFileTree->addFile(parent,fd.name,fd.hash,fd.size);
|
||||||
|
}
|
||||||
|
for(uint32_t i=0;i<dd.subdirs.size();++i)
|
||||||
|
{
|
||||||
|
const RsFileTree::DirData& ld(tree.directoryData(dd.subdirs[i]));
|
||||||
|
|
||||||
while(!n.isNull())
|
auto new_dir_index = mFileTree->addDirectory(parent,ld.name);
|
||||||
{
|
recursMergeTree(new_dir_index,tree,ld);
|
||||||
QDomElement ee = n.toElement(); // try to convert the node to an element.
|
}
|
||||||
|
|
||||||
#ifdef COLLECTION_DEBUG
|
|
||||||
std::cerr << " Seeing child " << ee.tagName().toStdString() << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(ee.tagName() == QString("File"))
|
|
||||||
{
|
|
||||||
ColFileInfo newChild ;
|
|
||||||
newChild.hash = ee.attribute(QString("sha1")) ;
|
|
||||||
bool bad_chars_detected = false ;
|
|
||||||
newChild.name = purifyFileName(ee.attribute(QString("name")), bad_chars_detected) ;
|
|
||||||
newChild.filename_has_wrong_characters = bad_chars_detected || bad_chars_in_parent ;
|
|
||||||
newChild.size = ee.attribute(QString("size")).toULongLong() ;
|
|
||||||
newChild.path = current_path ;
|
|
||||||
newChild.type = DIR_TYPE_FILE ;
|
|
||||||
|
|
||||||
colFileInfos.push_back(newChild) ;
|
|
||||||
}
|
|
||||||
else if(ee.tagName() == QString("Directory"))
|
|
||||||
{
|
|
||||||
ColFileInfo newParent ;
|
|
||||||
bool bad_chars_detected = false ;
|
|
||||||
QString cleanDirName = purifyFileName(ee.attribute(QString("name")),bad_chars_detected) ;
|
|
||||||
newParent.name=cleanDirName;
|
|
||||||
newParent.filename_has_wrong_characters = bad_chars_detected || bad_chars_in_parent ;
|
|
||||||
newParent.size = 0;
|
|
||||||
newParent.path = current_path ;
|
|
||||||
newParent.type = DIR_TYPE_DIR ;
|
|
||||||
|
|
||||||
recursCollectColFileInfos(ee,newParent.children,current_path + "/" + cleanDirName, bad_chars_in_parent || bad_chars_detected) ;
|
|
||||||
uint32_t size = newParent.children.size();
|
|
||||||
for(uint32_t i=0;i<size;++i)
|
|
||||||
{
|
|
||||||
const ColFileInfo &colFileInfo = newParent.children[i];
|
|
||||||
newParent.size +=colFileInfo.size ;
|
|
||||||
}
|
|
||||||
|
|
||||||
colFileInfos.push_back(newParent) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = n.nextSibling() ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RsCollection::recursAddElements(RsFileTree::DirIndex parent, const DirDetails& dd, FileSearchFlags flags)
|
||||||
void RsCollection::recursAddElements(QDomDocument& doc,const DirDetails& details,QDomElement& e,FileSearchFlags flags) const
|
|
||||||
{
|
{
|
||||||
if (details.type == DIR_TYPE_FILE)
|
if (dd.type == DIR_TYPE_FILE)
|
||||||
{
|
mHashes[dd.hash] = mFileTree->addFile(parent,dd.name,dd.hash,dd.size);
|
||||||
QDomElement f = doc.createElement("File") ;
|
else if (dd.type == DIR_TYPE_DIR)
|
||||||
|
{
|
||||||
|
RsFileTree::DirIndex new_dir_index = mFileTree->addDirectory(parent,dd.name);
|
||||||
|
|
||||||
f.setAttribute(QString("name"),QString::fromUtf8(details.name.c_str())) ;
|
for(uint32_t i=0;i<dd.children.size();++i)
|
||||||
f.setAttribute(QString("sha1"),QString::fromStdString(details.hash.toStdString())) ;
|
{
|
||||||
f.setAttribute(QString("size"),QString::number(details.size)) ;
|
if (!dd.children[i].ref)
|
||||||
|
continue;
|
||||||
|
|
||||||
e.appendChild(f) ;
|
DirDetails subDirDetails;
|
||||||
}
|
|
||||||
else if (details.type == DIR_TYPE_DIR)
|
|
||||||
{
|
|
||||||
QDomElement d = doc.createElement("Directory") ;
|
|
||||||
|
|
||||||
d.setAttribute(QString("name"),QString::fromUtf8(details.name.c_str())) ;
|
if (!rsFiles->RequestDirDetails(dd.children[i].ref, subDirDetails, flags))
|
||||||
|
continue;
|
||||||
|
|
||||||
for(uint32_t i=0;i<details.children.size();++i)
|
recursAddElements(new_dir_index,subDirDetails,flags) ;
|
||||||
{
|
}
|
||||||
if (!details.children[i].ref)
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
DirDetails subDirDetails;
|
|
||||||
|
|
||||||
if (!rsFiles->RequestDirDetails(details.children[i].ref, subDirDetails, flags))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
recursAddElements(doc,subDirDetails,d,flags) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.appendChild(d) ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RsCollection::recursAddElements(QDomDocument& doc,const ColFileInfo& colFileInfo,QDomElement& e) const
|
QString RsCollection::errorString(RsCollectionErrorCode code)
|
||||||
{
|
{
|
||||||
if (colFileInfo.type == DIR_TYPE_FILE)
|
switch(code)
|
||||||
{
|
{
|
||||||
QDomElement f = doc.createElement("File") ;
|
default: [[fallthrough]] ;
|
||||||
|
case RsCollectionErrorCode::UNKNOWN_ERROR: return QObject::tr("Unknown error");
|
||||||
f.setAttribute(QString("name"),colFileInfo.name) ;
|
case RsCollectionErrorCode::NO_ERROR: return QObject::tr("No error");
|
||||||
f.setAttribute(QString("sha1"),colFileInfo.hash) ;
|
case RsCollectionErrorCode::FILE_READ_ERROR: return QObject::tr("Error while openning file");
|
||||||
f.setAttribute(QString("size"),QString::number(colFileInfo.size)) ;
|
case RsCollectionErrorCode::FILE_CONTAINS_HARMFUL_STRINGS: return QObject::tr("Collection file contains potentially harmful code");
|
||||||
|
case RsCollectionErrorCode::INVALID_ROOT_NODE: return QObject::tr("Invalid root node. RsCollection node was expected.");
|
||||||
e.appendChild(f) ;
|
case RsCollectionErrorCode::XML_PARSING_ERROR: return QObject::tr("XML parsing error in collection file");
|
||||||
}
|
}
|
||||||
else if (colFileInfo.type == DIR_TYPE_DIR)
|
|
||||||
{
|
|
||||||
QDomElement d = doc.createElement("Directory") ;
|
|
||||||
|
|
||||||
d.setAttribute(QString("name"),colFileInfo.name) ;
|
|
||||||
|
|
||||||
for (std::vector<ColFileInfo>::const_iterator it = colFileInfo.children.begin(); it != colFileInfo.children.end(); ++it)
|
|
||||||
recursAddElements(doc,(*it),d) ;
|
|
||||||
|
|
||||||
e.appendChild(d) ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RsCollection::recursAddElements(
|
RsCollection::RsCollection(const RsCollection& col)
|
||||||
QDomDocument& doc, const RsFileTree& ft, uint32_t index,
|
: mFileTree(new RsFileTree(*col.mFileTree)),mHashes(col.mHashes)
|
||||||
QDomElement& e ) const
|
|
||||||
{
|
{
|
||||||
std::vector<uint64_t> subdirs;
|
|
||||||
std::vector<RsFileTree::FileData> subfiles ;
|
|
||||||
std::string name;
|
|
||||||
if(!ft.getDirectoryContent(name, subdirs, subfiles, index)) return;
|
|
||||||
|
|
||||||
QDomElement d = doc.createElement("Directory") ;
|
|
||||||
d.setAttribute(QString("name"),QString::fromUtf8(name.c_str())) ;
|
|
||||||
e.appendChild(d) ;
|
|
||||||
|
|
||||||
for (uint32_t i=0;i<subdirs.size();++i)
|
|
||||||
recursAddElements(doc,ft,subdirs[i],d) ;
|
|
||||||
|
|
||||||
for(uint32_t i=0;i<subfiles.size();++i)
|
|
||||||
{
|
|
||||||
QDomElement f = doc.createElement("File") ;
|
|
||||||
|
|
||||||
f.setAttribute(QString("name"),QString::fromUtf8(subfiles[i].name.c_str())) ;
|
|
||||||
f.setAttribute(QString("sha1"),QString::fromStdString(subfiles[i].hash.toStdString())) ;
|
|
||||||
f.setAttribute(QString("size"),QString::number(subfiles[i].size)) ;
|
|
||||||
|
|
||||||
d.appendChild(f) ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void showErrorBox(const QString& fileName, const QString& error)
|
RsCollection::RsCollection(const QString& fileName, RsCollectionErrorCode& error)
|
||||||
|
: mFileTree(new RsFileTree)
|
||||||
{
|
{
|
||||||
QMessageBox mb(QMessageBox::Warning, QObject::tr("Failed to process collection file"), QObject::tr("The collection file %1 could not be opened.\nReported error is: \n\n%2").arg(fileName).arg(error), QMessageBox::Ok);
|
if (!checkFile(fileName,error))
|
||||||
mb.exec();
|
return ;
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
std::cerr << "Cannot open file " << fileName.toStdString() << " !!" << std::endl;
|
||||||
|
error = RsCollectionErrorCode::FILE_READ_ERROR;
|
||||||
|
//showErrorBox(fileName, QApplication::translate("RsCollectionFile", "Cannot open file %1").arg(fileName));
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomDocument xml_doc;
|
||||||
|
bool ok = xml_doc.setContent(&file) ;
|
||||||
|
|
||||||
|
if(!ok)
|
||||||
|
{
|
||||||
|
error = RsCollectionErrorCode::XML_PARSING_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
QDomNode root = xml_doc.elementsByTagName("RsCollection").at(0).toElement();
|
||||||
|
|
||||||
|
if(root.isNull())
|
||||||
|
{
|
||||||
|
error = RsCollectionErrorCode::INVALID_ROOT_NODE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!recursParseXml(xml_doc,root,0))
|
||||||
|
error = RsCollectionErrorCode::XML_PARSING_ERROR;
|
||||||
|
else
|
||||||
|
error = RsCollectionErrorCode::NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RsCollection::load(const QString& fileName, bool showError /* = true*/)
|
// check that the file is a valid rscollection file, and not a lol bomb or some shit like this
|
||||||
|
|
||||||
|
bool RsCollection::checkFile(const QString& fileName, RsCollectionErrorCode& error)
|
||||||
{
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
error = RsCollectionErrorCode::NO_ERROR;
|
||||||
|
|
||||||
if (!checkFile(fileName,showError)) return false;
|
if (!file.open(QIODevice::ReadOnly))
|
||||||
QFile file(fileName);
|
{
|
||||||
|
std::cerr << "Cannot open file " << fileName.toStdString() << " !!" << std::endl;
|
||||||
|
error = RsCollectionErrorCode::FILE_READ_ERROR;
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
//showErrorBox(fileName, QApplication::translate("RsCollectionFile", "Cannot open file %1").arg(fileName));
|
||||||
{
|
return false;
|
||||||
std::cerr << "Cannot open file " << fileName.toStdString() << " !!" << std::endl;
|
}
|
||||||
if (showError) {
|
if (file.reset()){
|
||||||
showErrorBox(fileName, QApplication::translate("RsCollectionFile", "Cannot open file %1").arg(fileName));
|
std::cerr << "Checking this file for bomb elements and various wrong stuff" << std::endl;
|
||||||
}
|
char c ;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = _xml_doc.setContent(&file) ;
|
std::vector<std::string> bad_strings ;
|
||||||
file.close();
|
bad_strings.push_back(std::string("<!entity ")) ;
|
||||||
|
static const int max_size = 12 ; // should be as large as the largest element in bad_strings
|
||||||
|
char current[max_size] = { 0,0,0,0,0,0,0,0,0,0,0,0 } ;
|
||||||
|
int n=0 ;
|
||||||
|
|
||||||
if (ok) {
|
while( !file.atEnd() || n >= 0)
|
||||||
_fileName = fileName;
|
{
|
||||||
} else {
|
if (!file.atEnd())
|
||||||
if (showError) {
|
file.getChar(&c);
|
||||||
showErrorBox(fileName, QApplication::translate("RsCollectionFile", "Error parsing xml file"));
|
else
|
||||||
}
|
c=0;
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
if(c == '\t' || c == '\n' || c == '\b' || c == '\r')
|
||||||
}
|
continue ;
|
||||||
|
|
||||||
// check that the file is a valid rscollection file, and not a lol bomb or some shit like this
|
if (n == max_size || file.atEnd())
|
||||||
bool RsCollection::checkFile(const QString& fileName, bool showError)
|
for(int i=0;i<n-1;++i)
|
||||||
{
|
current[i] = current[i+1] ;
|
||||||
QFile file(fileName);
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
if(n == max_size)
|
||||||
{
|
--n ;
|
||||||
std::cerr << "Cannot open file " << fileName.toStdString() << " !!" << std::endl;
|
|
||||||
if (showError) {
|
|
||||||
showErrorBox(fileName, QApplication::translate("RsCollectionFile", "Cannot open file %1").arg(fileName));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (file.reset()){
|
|
||||||
std::cerr << "Checking this file for bomb elements and various wrong stuff" << std::endl;
|
|
||||||
char c ;
|
|
||||||
|
|
||||||
std::vector<std::string> bad_strings ;
|
if(c >= 'A' && c <= 'Z') c += 'a' - 'A' ;
|
||||||
bad_strings.push_back(std::string("<!entity ")) ;
|
|
||||||
static const int max_size = 12 ; // should be as large as the largest element in bad_strings
|
|
||||||
char current[max_size] = { 0,0,0,0,0,0,0,0,0,0,0,0 } ;
|
|
||||||
int n=0 ;
|
|
||||||
|
|
||||||
while( !file.atEnd() || n >= 0)
|
if(!file.atEnd())
|
||||||
{
|
current[n] = c ;
|
||||||
if (!file.atEnd())
|
else
|
||||||
file.getChar(&c);
|
current[n] = 0 ;
|
||||||
else
|
|
||||||
c=0;
|
|
||||||
|
|
||||||
if(c == '\t' || c == '\n' || c == '\b' || c == '\r')
|
//std::cerr << "n==" << n <<" Checking string " << std::string(current,n+1) << " c = " << std::hex << (int)c << std::dec << std::endl;
|
||||||
continue ;
|
|
||||||
|
|
||||||
if (n == max_size || file.atEnd())
|
for(uint i=0;i<bad_strings.size();++i)
|
||||||
for(int i=0;i<n-1;++i)
|
if(std::string(current,bad_strings[i].length()) == bad_strings[i])
|
||||||
current[i] = current[i+1] ;
|
{
|
||||||
|
//showErrorBox(file.fileName(), QApplication::translate("RsCollectionFile", "This file contains the string \"%1\" and is therefore an invalid collection file. \n\nIf you believe it is correct, remove the corresponding line from the file and re-open it with Retroshare.").arg(bad_strings[i].c_str()));
|
||||||
|
error = RsCollectionErrorCode::FILE_CONTAINS_HARMFUL_STRINGS;
|
||||||
|
file.close();
|
||||||
|
return false ;
|
||||||
|
//std::cerr << "Bad string detected" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
if(n == max_size)
|
if(file.atEnd())
|
||||||
--n ;
|
n-- ;
|
||||||
|
else if(n < max_size)
|
||||||
if(c >= 'A' && c <= 'Z') c += 'a' - 'A' ;
|
++n ;
|
||||||
|
}
|
||||||
if(!file.atEnd())
|
file.close();
|
||||||
current[n] = c ;
|
return true;
|
||||||
else
|
}
|
||||||
current[n] = 0 ;
|
error = RsCollectionErrorCode::UNKNOWN_ERROR;
|
||||||
|
return false;
|
||||||
//std::cerr << "n==" << n <<" Checking string " << std::string(current,n+1) << " c = " << std::hex << (int)c << std::dec << std::endl;
|
|
||||||
|
|
||||||
for(uint i=0;i<bad_strings.size();++i)
|
|
||||||
if(std::string(current,bad_strings[i].length()) == bad_strings[i])
|
|
||||||
{
|
|
||||||
showErrorBox(file.fileName(), QApplication::translate("RsCollectionFile", "This file contains the string \"%1\" and is therefore an invalid collection file. \n\nIf you believe it is correct, remove the corresponding line from the file and re-open it with Retroshare.").arg(bad_strings[i].c_str()));
|
|
||||||
file.close();
|
|
||||||
return false ;
|
|
||||||
//std::cerr << "Bad string detected" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(file.atEnd())
|
|
||||||
n-- ;
|
|
||||||
else if(n < max_size)
|
|
||||||
++n ;
|
|
||||||
}
|
|
||||||
file.close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool RsCollection::load(QWidget *parent)
|
|
||||||
{
|
|
||||||
QString fileName;
|
|
||||||
if (!misc::getOpenFileName(parent, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Open collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
|
|
||||||
|
|
||||||
return load(fileName, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RsCollection::save(const QString& fileName) const
|
bool RsCollection::save(const QString& fileName) const
|
||||||
{
|
{
|
||||||
QFile file(fileName);
|
QFile file(fileName);
|
||||||
|
|
||||||
if (!file.open(QIODevice::WriteOnly))
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
{
|
{
|
||||||
std::cerr << "Cannot write to file " << fileName.toStdString() << " !!" << std::endl;
|
std::cerr << "Cannot write to file " << fileName.toStdString() << " !!" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextStream stream(&file) ;
|
QDomDocument xml_doc ;
|
||||||
stream.setCodec("UTF-8") ;
|
QDomElement root = xml_doc.createElement("RsCollection");
|
||||||
|
|
||||||
stream << _xml_doc.toString() ;
|
const RsFileTree::DirData& root_data(mFileTree->directoryData(mFileTree->root()));
|
||||||
|
|
||||||
file.close();
|
if(!recursExportToXml(xml_doc,root,root_data))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
xml_doc.appendChild(root);
|
||||||
|
|
||||||
|
QTextStream stream(&file) ;
|
||||||
|
stream.setCodec("UTF-8") ;
|
||||||
|
|
||||||
|
stream << xml_doc.toString() ;
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RsCollection::save(QWidget *parent) const
|
bool RsCollection::recursParseXml(QDomDocument& doc,const QDomNode& e,const RsFileTree::DirIndex parent)
|
||||||
{
|
{
|
||||||
QString fileName;
|
mHashes.clear();
|
||||||
if(!misc::getSaveFileName(parent, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Create collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
|
QDomNode n = e.firstChild() ;
|
||||||
return false;
|
#ifdef COLLECTION_DEBUG
|
||||||
|
std::cerr << "Parsing element " << e.tagName().toStdString() << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!fileName.endsWith("." + RsCollection::ExtensionString))
|
while(!n.isNull())
|
||||||
fileName += "." + RsCollection::ExtensionString ;
|
{
|
||||||
|
QDomElement ee = n.toElement(); // try to convert the node to an element.
|
||||||
|
|
||||||
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
|
#ifdef COLLECTION_DEBUG
|
||||||
|
std::cerr << " Seeing child " << ee.tagName().toStdString() << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
return save(fileName);
|
if(ee.tagName() == QString("File"))
|
||||||
|
{
|
||||||
|
RsFileHash hash(ee.attribute(QString("sha1")).toStdString()) ;
|
||||||
|
|
||||||
|
bool bad_chars_detected = false;
|
||||||
|
std::string name = purifyFileName(ee.attribute(QString("name")), bad_chars_detected).toUtf8().constData() ;
|
||||||
|
uint64_t size = ee.attribute(QString("size")).toULongLong() ;
|
||||||
|
|
||||||
|
mHashes[hash] = mFileTree->addFile(parent,name,hash,size);
|
||||||
|
}
|
||||||
|
else if(ee.tagName() == QString("Directory"))
|
||||||
|
{
|
||||||
|
bool bad_chars_detected = false ;
|
||||||
|
std::string cleanDirName = purifyFileName(ee.attribute(QString("name")),bad_chars_detected).toUtf8().constData() ;
|
||||||
|
|
||||||
|
RsFileTree::DirIndex new_dir_index = mFileTree->addDirectory(parent,cleanDirName);
|
||||||
|
|
||||||
|
recursParseXml(doc,ee,new_dir_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.nextSibling() ;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
bool RsCollection::recursExportToXml(QDomDocument& doc,QDomElement& e,const RsFileTree::DirData& dd) const
|
||||||
|
|
||||||
bool RsCollection::openNewColl(QWidget *parent, QString fileName)
|
|
||||||
{
|
{
|
||||||
if(!misc::getSaveFileName(parent, RshareSettings::LASTDIR_EXTRAFILE
|
for(uint32_t i=0;i<dd.subfiles.size();++i)
|
||||||
, QApplication::translate("RsCollectionFile", "Create collection file")
|
{
|
||||||
, QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")"
|
QDomElement f = doc.createElement("File") ;
|
||||||
, fileName,0, QFileDialog::DontConfirmOverwrite))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!fileName.endsWith("." + RsCollection::ExtensionString))
|
const RsFileTree::FileData& fd(mFileTree->fileData(dd.subfiles[i]));
|
||||||
fileName += "." + RsCollection::ExtensionString ;
|
|
||||||
|
|
||||||
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
|
f.setAttribute(QString("name"),QString::fromUtf8(fd.name.c_str())) ;
|
||||||
|
f.setAttribute(QString("sha1"),QString::fromStdString(fd.hash.toStdString())) ;
|
||||||
|
f.setAttribute(QString("size"),QString::number(fd.size)) ;
|
||||||
|
|
||||||
QFile file(fileName) ;
|
e.appendChild(f) ;
|
||||||
|
}
|
||||||
|
|
||||||
if(file.exists())
|
for(uint32_t i=0;i<dd.subdirs.size();++i)
|
||||||
{
|
{
|
||||||
if (!checkFile(fileName,true)) return false;
|
const RsFileTree::DirData& di(mFileTree->directoryData(dd.subdirs[i]));
|
||||||
|
|
||||||
QMessageBox mb;
|
QDomElement d = doc.createElement("Directory") ;
|
||||||
mb.setText(tr("Save Collection File."));
|
d.setAttribute(QString("name"),QString::fromUtf8(di.name.c_str())) ;
|
||||||
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) {
|
if(!recursExportToXml(doc,d,di))
|
||||||
//Nothing to do _xml_doc already up to date
|
return false;
|
||||||
} else if (mb.clickedButton()==btnMerge) {
|
|
||||||
//Open old file to merge it with _xml_doc
|
|
||||||
QDomDocument qddOldFile("RsCollection");
|
|
||||||
if (qddOldFile.setContent(&file)) {
|
|
||||||
QDomElement docOldElem = qddOldFile.elementsByTagName("RsCollection").at(0).toElement();
|
|
||||||
std::vector<ColFileInfo> colOldFileInfos;
|
|
||||||
recursCollectColFileInfos(docOldElem,colOldFileInfos,QString(),false);
|
|
||||||
|
|
||||||
QDomElement root = _xml_doc.elementsByTagName("RsCollection").at(0).toElement();
|
e.appendChild(d) ;
|
||||||
for(uint32_t i = 0;i<colOldFileInfos.size();++i){
|
}
|
||||||
recursAddElements(_xml_doc,colOldFileInfos[i],root) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (mb.clickedButton()==btnCancel) {
|
return true;
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}//if(file.exists())
|
|
||||||
|
|
||||||
_fileName=fileName;
|
|
||||||
std::vector<ColFileInfo> colFileInfos ;
|
|
||||||
|
|
||||||
recursCollectColFileInfos(_xml_doc.documentElement(),colFileInfos,QString(),false) ;
|
|
||||||
|
|
||||||
RsCollectionDialog* rcd = new RsCollectionDialog(fileName, colFileInfos,true);
|
|
||||||
connect(rcd,SIGNAL(saveColl(std::vector<ColFileInfo>, QString)),this,SLOT(saveColl(std::vector<ColFileInfo>, QString))) ;
|
|
||||||
_saved=false;
|
|
||||||
rcd->exec() ;
|
|
||||||
delete rcd;
|
|
||||||
|
|
||||||
return _saved;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RsCollection::openColl(const QString& fileName, bool readOnly /* = false */, bool showError /* = true*/)
|
qulonglong RsCollection::count() const
|
||||||
{
|
{
|
||||||
if (load(fileName, showError)) {
|
return mFileTree->numFiles();
|
||||||
std::vector<ColFileInfo> colFileInfos ;
|
|
||||||
|
|
||||||
recursCollectColFileInfos(_xml_doc.documentElement(),colFileInfos,QString(),false) ;
|
|
||||||
|
|
||||||
RsCollectionDialog* rcd = new RsCollectionDialog(fileName, colFileInfos, true, readOnly);
|
|
||||||
connect(rcd,SIGNAL(saveColl(std::vector<ColFileInfo>, QString)),this,SLOT(saveColl(std::vector<ColFileInfo>, QString))) ;
|
|
||||||
_saved=false;
|
|
||||||
rcd->exec() ;
|
|
||||||
delete rcd;
|
|
||||||
|
|
||||||
return _saved;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qulonglong RsCollection::size()
|
qulonglong RsCollection::size()
|
||||||
{
|
{
|
||||||
QDomElement docElem = _xml_doc.documentElement();
|
return mFileTree->totalFileSize();
|
||||||
|
|
||||||
std::vector<ColFileInfo> colFileInfos;
|
|
||||||
recursCollectColFileInfos(docElem, colFileInfos, QString(),false);
|
|
||||||
|
|
||||||
uint64_t size = 0;
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < colFileInfos.size(); ++i) {
|
|
||||||
size += colFileInfos[i].size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RsCollection::isCollectionFile(const QString &fileName)
|
bool RsCollection::isCollectionFile(const QString &fileName)
|
||||||
{
|
{
|
||||||
QString ext = QFileInfo(fileName).suffix().toLower();
|
QString ext = QFileInfo(fileName).suffix().toLower();
|
||||||
|
|
||||||
return (ext == RsCollection::ExtensionString);
|
return (ext == RsCollection::ExtensionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RsCollection::saveColl(std::vector<ColFileInfo> colFileInfos, const QString &fileName)
|
void RsCollection::updateHashes(const std::map<RsFileHash,RsFileHash>& old_to_new_hashes)
|
||||||
{
|
{
|
||||||
|
for(auto it:old_to_new_hashes)
|
||||||
|
{
|
||||||
|
auto fit = mHashes.find(it.first);
|
||||||
|
|
||||||
QDomElement root = _xml_doc.elementsByTagName("RsCollection").at(0).toElement();
|
if(fit == mHashes.end())
|
||||||
while (root.childNodes().count()>0) root.removeChild(root.firstChild());
|
{
|
||||||
for(uint32_t i = 0;i<colFileInfos.size();++i)
|
RsErr() << "Could not find hash " << it.first << " in RsCollection list of hashes. This is a bug." ;
|
||||||
recursAddElements(_xml_doc,colFileInfos[i],root) ;
|
return ;
|
||||||
|
}
|
||||||
_saved=save(fileName);
|
const auto& fd(mFileTree->fileData(fit->second));
|
||||||
|
|
||||||
|
if(fd.hash != it.first)
|
||||||
|
{
|
||||||
|
RsErr() << "Mismatch hash for file " << fd.name << " (found " << fd.hash << " instead of " << it.first << ") in RsCollection list of hashes. This is a bug." ;
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
mFileTree->updateFile(fit->second,fd.name,it.second,fd.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsCollection::removeFile(RsFileTree::FileIndex index_to_remove,RsFileTree::DirIndex parent_index)
|
||||||
|
{
|
||||||
|
return mFileTree->removeFile(index_to_remove,parent_index);
|
||||||
|
}
|
||||||
|
bool RsCollection::removeDirectory(RsFileTree::DirIndex index_to_remove,RsFileTree::DirIndex parent_index)
|
||||||
|
{
|
||||||
|
return mFileTree->removeDirectory(index_to_remove,parent_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsCollection::cleanup()
|
||||||
|
{
|
||||||
|
RsDbg() << "Cleaning up RsCollection with " << mFileTree->numDirs() << " dirs and " << mFileTree->numFiles() << " files." ;
|
||||||
|
|
||||||
|
mFileTree = RsFileTree::fromTreeCleaned(*mFileTree);
|
||||||
|
|
||||||
|
RsDbg() << "Simplified to " << mFileTree->numDirs() << " dirs and " << mFileTree->numFiles() << " files.";
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@ -53,66 +52,70 @@ public:
|
|||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(ColFileInfo)
|
Q_DECLARE_METATYPE(ColFileInfo)
|
||||||
|
|
||||||
class RsCollection : public QObject
|
class RsCollection
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum class RsCollectionErrorCode:uint8_t {
|
||||||
|
NO_ERROR = 0x00,
|
||||||
|
UNKNOWN_ERROR = 0x01,
|
||||||
|
FILE_READ_ERROR = 0x02,
|
||||||
|
FILE_CONTAINS_HARMFUL_STRINGS = 0x03,
|
||||||
|
INVALID_ROOT_NODE = 0x04,
|
||||||
|
XML_PARSING_ERROR = 0x05,
|
||||||
|
};
|
||||||
|
|
||||||
RsCollection(QObject *parent = 0) ;
|
RsCollection();
|
||||||
// create from list of files and directories
|
RsCollection(const RsCollection&);
|
||||||
RsCollection(const std::vector<DirDetails>& file_entries, FileSearchFlags flags, QObject *parent = 0) ;
|
RsCollection(const std::vector<DirDetails>& file_entries, FileSearchFlags flags) ;
|
||||||
RsCollection(const RsFileTree& fr);
|
RsCollection(const RsFileTree& ft);
|
||||||
virtual ~RsCollection() ;
|
RsCollection(const QString& filename,RsCollectionErrorCode& error_code);
|
||||||
|
|
||||||
void merge_in(const QString& fname,uint64_t size,const RsFileHash& hash) ;
|
static QString errorString(RsCollectionErrorCode code);
|
||||||
void merge_in(const RsFileTree& tree) ;
|
|
||||||
|
virtual ~RsCollection() ;
|
||||||
|
|
||||||
|
void merge_in(const QString& fname,uint64_t size,const RsFileHash& hash,RsFileTree::DirIndex parent_index=0) ;
|
||||||
|
void merge_in(const RsFileTree& tree,RsFileTree::DirIndex parent_index=0) ;
|
||||||
|
|
||||||
|
bool removeFile(RsFileTree::FileIndex index_to_remove,RsFileTree::DirIndex parent_index);
|
||||||
|
bool removeDirectory(RsFileTree::DirIndex index_to_remove,RsFileTree::DirIndex parent_index);
|
||||||
|
|
||||||
|
void cleanup(); // cleans up the collection, which may contain unreferenced files/dirs after lazy editing.
|
||||||
|
|
||||||
static const QString ExtensionString ;
|
static const QString ExtensionString ;
|
||||||
|
|
||||||
// Loads file from disk.
|
|
||||||
bool load(QWidget *parent);
|
|
||||||
bool load(const QString& fileName, bool showError = true);
|
|
||||||
|
|
||||||
// Save to disk
|
// Save to disk
|
||||||
bool save(QWidget *parent) const ;
|
|
||||||
bool save(const QString& fileName) const ;
|
bool save(const QString& fileName) const ;
|
||||||
|
|
||||||
// Open new collection
|
// returns the file tree
|
||||||
bool openNewColl(QWidget *parent, QString fileName = "");
|
const RsFileTree& fileTree() const { return *mFileTree; }
|
||||||
// Open existing collection
|
// total size of files in the collection
|
||||||
bool openColl(const QString& fileName, bool readOnly = false, bool showError = true);
|
qulonglong size();
|
||||||
|
// total number of files in the collection
|
||||||
// Download the content.
|
qulonglong count() const;
|
||||||
void downloadFiles() const ;
|
|
||||||
// Auto Download all the content.
|
|
||||||
void autoDownloadFiles() const ;
|
|
||||||
|
|
||||||
qulonglong size();
|
|
||||||
|
|
||||||
static bool isCollectionFile(const QString& fileName);
|
static bool isCollectionFile(const QString& fileName);
|
||||||
|
|
||||||
private slots:
|
void updateHashes(const std::map<RsFileHash,RsFileHash>& old_to_new_hashes);
|
||||||
void saveColl(std::vector<ColFileInfo> colFileInfos, const QString& fileName);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void recursAddElements(QDomDocument&, const DirDetails&, QDomElement&, FileSearchFlags flags) const ;
|
bool recursExportToXml(QDomDocument& doc,QDomElement& e,const RsFileTree::DirData& dd) const;
|
||||||
void recursAddElements(QDomDocument&,const ColFileInfo&,QDomElement&) const;
|
bool recursParseXml(QDomDocument& doc, const QDomNode &e, RsFileTree::DirIndex dd) ;
|
||||||
void recursAddElements(
|
|
||||||
QDomDocument& doc, const RsFileTree& ft, uint32_t index,
|
// This function is used to populate a RsCollection from locally or remotly shared files.
|
||||||
QDomElement& e ) const;
|
void recursAddElements(RsFileTree::DirIndex parent, const DirDetails& dd, FileSearchFlags flags) ;
|
||||||
|
|
||||||
|
// This function is used to merge an existing RsFileTree into the RsCollection
|
||||||
|
void recursMergeTree(RsFileTree::DirIndex parent, const RsFileTree& tree, const RsFileTree::DirData &dd);
|
||||||
|
|
||||||
|
// check that the file is a valid rscollection file, and not a lol bomb or some shit like this
|
||||||
|
static bool checkFile(const QString &fileName, RsCollectionErrorCode &error);
|
||||||
|
|
||||||
void recursCollectColFileInfos(const QDomElement&,std::vector<ColFileInfo>& colFileInfos,const QString& current_dir,bool bad_chars_in_parent) const ;
|
|
||||||
// check that the file is a valid rscollection file, and not a lol bomb or some shit like this
|
|
||||||
static bool checkFile(const QString &fileName, bool showError);
|
|
||||||
// Auto Download recursively.
|
// Auto Download recursively.
|
||||||
void autoDownloadFiles(ColFileInfo colFileInfo, QString dlDir) const ;
|
void autoDownloadFiles(ColFileInfo colFileInfo, QString dlDir) const ;
|
||||||
|
|
||||||
QDomDocument _xml_doc ;
|
std::unique_ptr<RsFileTree> mFileTree;
|
||||||
QString _fileName ;
|
std::map<RsFileHash,RsFileTree::FileIndex> mHashes; // used to efficiently update files being hashed
|
||||||
bool _saved;
|
|
||||||
QDomElement _root ;
|
|
||||||
|
|
||||||
friend class RsCollectionDialog ;
|
friend class RsCollectionDialog ;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -18,8 +18,10 @@
|
|||||||
* *
|
* *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include "ui_RsCollectionDialog.h"
|
#include "ui_RsCollectionDialog.h"
|
||||||
#include "RsCollection.h"
|
#include "RsCollection.h"
|
||||||
|
#include "RsCollectionModel.h"
|
||||||
#include <QFileSystemModel>
|
#include <QFileSystemModel>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
@ -30,33 +32,45 @@ class RsCollectionDialog: public QDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RsCollectionDialog(const QString& filename
|
|
||||||
, const std::vector<ColFileInfo> &colFileInfos
|
|
||||||
, const bool& creation
|
|
||||||
, const bool& readOnly = false) ;
|
|
||||||
virtual ~RsCollectionDialog();
|
virtual ~RsCollectionDialog();
|
||||||
|
|
||||||
|
// Open new collection
|
||||||
|
static bool openNewCollection(const RsFileTree &tree = RsFileTree());
|
||||||
|
|
||||||
|
// Edit existing collection
|
||||||
|
static bool editExistingCollection(const QString& fileName, bool showError = true);
|
||||||
|
|
||||||
|
// Open existing collection for download
|
||||||
|
static bool openExistingCollection(const QString& fileName, bool showError = true);
|
||||||
|
|
||||||
|
// Open existing collection for download
|
||||||
|
static bool downloadFiles(const RsCollection& collection);
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *obj, QEvent *ev);
|
static QString errorString(RsCollection::RsCollectionErrorCode code);
|
||||||
|
|
||||||
|
void init(const QString& collectionFileName);
|
||||||
|
|
||||||
|
enum RsCollectionDialogMode {
|
||||||
|
UNKNOWN = 0x00,
|
||||||
|
EDIT = 0x01,
|
||||||
|
DOWNLOAD = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
RsCollectionDialog(const QString& filename, RsCollectionDialogMode mode) ;
|
||||||
|
RsCollectionDialog(const RsCollection& coll, RsCollectionDialogMode mode) ;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void directoryLoaded(QString dirLoaded);
|
void directoryLoaded(QString dirLoaded);
|
||||||
void updateSizes() ;
|
void updateSizes() ;
|
||||||
void changeFileName() ;
|
void changeFileName() ;
|
||||||
void add() ;
|
void addSelection() ;
|
||||||
void addRecursive() ;
|
void addSelectionRecursive() ;
|
||||||
void remove() ;
|
void remove() ;
|
||||||
void chooseDestinationDirectory();
|
void chooseDestinationDirectory();
|
||||||
void setDestinationDirectory();
|
void setDestinationDirectory();
|
||||||
void openDestinationDirectoryMenu();
|
void openDestinationDirectoryMenu();
|
||||||
void processItem(QMap<QString, QString> &dirToAdd
|
|
||||||
, int &index
|
|
||||||
, ColFileInfo &parent
|
|
||||||
) ;
|
|
||||||
void makeDir() ;
|
void makeDir() ;
|
||||||
void fileHashingFinished(QList<HashedFile> hashedFiles) ;
|
void fileHashingFinished(QList<HashedFile> hashedFiles) ;
|
||||||
void itemChanged(QTreeWidgetItem* item,int col) ;
|
|
||||||
void updateRemoveDuplicate(bool checked);
|
|
||||||
void cancel() ;
|
void cancel() ;
|
||||||
void download() ;
|
void download() ;
|
||||||
void save() ;
|
void save() ;
|
||||||
@ -66,26 +80,20 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void processSettings(bool bLoad) ;
|
void processSettings(bool bLoad) ;
|
||||||
QTreeWidgetItem* getRootItem();
|
void addSelection(bool recursive) ;
|
||||||
bool updateList();
|
|
||||||
bool addChild(QTreeWidgetItem *parent, const std::vector<ColFileInfo> &child);
|
|
||||||
bool removeItem(QTreeWidgetItem *item, bool &removeOnlyFile) ;
|
|
||||||
void addRecursive(bool recursive) ;
|
|
||||||
bool addAllChild(QFileInfo &fileInfoParent
|
|
||||||
, QMap<QString, QString > &dirToAdd
|
|
||||||
, QStringList &fileToHash
|
|
||||||
, int &count);
|
|
||||||
void saveChild(QTreeWidgetItem *parentItem, ColFileInfo *parentInfo = NULL);
|
|
||||||
|
|
||||||
Ui::RsCollectionDialog ui;
|
Ui::RsCollectionDialog ui;
|
||||||
QString _fileName ;
|
|
||||||
const bool _creationMode ;
|
RsCollectionDialogMode _mode;
|
||||||
const bool _readOnly;
|
|
||||||
std::vector<ColFileInfo> _newColFileInfos ;
|
|
||||||
|
|
||||||
QFileSystemModel *_dirModel;
|
QFileSystemModel *_dirModel;
|
||||||
QSortFilterProxyModel *_tree_proxyModel;
|
QSortFilterProxyModel *_tree_proxyModel;
|
||||||
QItemSelectionModel *_selectionProxy;
|
QItemSelectionModel *_selectionProxy;
|
||||||
bool _dirLoaded;
|
bool _dirLoaded;
|
||||||
QHash<QString,QString> _listOfFilesAddedInDir;
|
QHash<QString,QString> _listOfFilesAddedInDir;
|
||||||
|
|
||||||
|
RsCollectionModel *mCollectionModel;
|
||||||
|
RsCollection *mCollection;
|
||||||
|
|
||||||
|
std::map<QString,RsFileHash> mFilesBeingHashed; // map of file path vs. temporary ID used for the file while hashing
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>600</width>
|
<width>761</width>
|
||||||
<height>400</height>
|
<height>434</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
@ -17,7 +17,7 @@
|
|||||||
<string>Collection</string>
|
<string>Collection</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="../images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/mimetypes/rscollection-16.png</normaloff>:/images/mimetypes/rscollection-16.png</iconset>
|
<normaloff>:/images/mimetypes/rscollection-16.png</normaloff>:/images/mimetypes/rscollection-16.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeGripEnabled">
|
<property name="sizeGripEnabled">
|
||||||
@ -157,7 +157,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="_changeFile">
|
<widget class="QToolButton" name="_changeFile">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>21</width>
|
<width>21</width>
|
||||||
@ -170,6 +170,10 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../icons.qrc">
|
||||||
|
<normaloff>:/icons/browsable_blue_128.png</normaloff>:/icons/browsable_blue_128.png</iconset>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -283,7 +287,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../images.qrc">
|
<iconset resource="../images.qrc">
|
||||||
<normaloff>:/images/feedback_arrow.png</normaloff>:/images/feedback_arrow.png</iconset>
|
<normaloff>:/images/start.png</normaloff>:/images/start.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -309,7 +313,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../images.qrc">
|
<iconset resource="../images.qrc">
|
||||||
<normaloff>:/images/update.png</normaloff>:/images/update.png</iconset>
|
<normaloff>:/images/startall.png</normaloff>:/images/startall.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -334,8 +338,8 @@
|
|||||||
<string notr="true"/>
|
<string notr="true"/>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset>
|
<iconset resource="../images.qrc">
|
||||||
<normaloff>:/images/deletemail24.png</normaloff>:/images/deletemail24.png</iconset>
|
<normaloff>:/images/delete.png</normaloff>:/images/delete.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -382,7 +386,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QTreeWidget" name="_fileEntriesTW">
|
<widget class="QTreeView" name="_fileEntriesTW">
|
||||||
<property name="editTriggers">
|
<property name="editTriggers">
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
</property>
|
</property>
|
||||||
@ -398,11 +402,6 @@
|
|||||||
<property name="allColumnsShowFocus">
|
<property name="allColumnsShowFocus">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string notr="true">1</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -411,26 +410,6 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="buttons_HL">
|
<layout class="QHBoxLayout" name="buttons_HL">
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="_removeDuplicate_CB">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove Duplicate</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="buttons_HSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="downloadFolder_LB">
|
<widget class="QLabel" name="downloadFolder_LB">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -451,6 +430,12 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="downloadFolder_LE">
|
<widget class="QLineEdit" name="downloadFolder_LE">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
<enum>Qt::CustomContextMenu</enum>
|
<enum>Qt::CustomContextMenu</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -462,6 +447,19 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="_cancel_PB">
|
<widget class="QPushButton" name="_cancel_PB">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -517,7 +515,7 @@
|
|||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../images.qrc"/>
|
<include location="../images.qrc"/>
|
||||||
<include location="../../../../plugins/FeedReader/gui/FeedReader_images.qrc"/>
|
<include location="../icons.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
616
retroshare-gui/src/gui/common/RsCollectionModel.cpp
Normal file
616
retroshare-gui/src/gui/common/RsCollectionModel.cpp
Normal file
@ -0,0 +1,616 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <QBrush>
|
||||||
|
|
||||||
|
#include "RsCollectionModel.h"
|
||||||
|
|
||||||
|
// #define DEBUG_COLLECTION_MODEL 1
|
||||||
|
|
||||||
|
static const int COLLECTION_MODEL_FILENAME = 0;
|
||||||
|
static const int COLLECTION_MODEL_SIZE = 1;
|
||||||
|
static const int COLLECTION_MODEL_HASH = 2;
|
||||||
|
static const int COLLECTION_MODEL_COUNT = 3;
|
||||||
|
static const int COLLECTION_MODEL_NB_COLUMN = 4;
|
||||||
|
|
||||||
|
RsCollectionModel::RsCollectionModel(const RsCollection& col, QObject *parent)
|
||||||
|
: QAbstractItemModel(parent),mCollection(col)
|
||||||
|
{
|
||||||
|
postMods();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::ostream& operator<<(std::ostream& o,const RsCollectionModel::EntryIndex& i)
|
||||||
|
{
|
||||||
|
return o << ((i.is_file)?("File"):"Dir") << " with index " << (int)i.index ;
|
||||||
|
}
|
||||||
|
static std::ostream& operator<<(std::ostream& o,const QModelIndex& i)
|
||||||
|
{
|
||||||
|
return o << "QModelIndex (row " << i.row() << ", ref " << i.internalId() << ")" ;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Indernal Id is always a quintptr_t (basically a uint with the size of a pointer). Depending on the
|
||||||
|
// Indernal Id is always a quintptr_t (basically a uint with the size of a pointer). Depending on the
|
||||||
|
// architecture, the pointer may have 4 or 8 bytes. We use the low-level bit for type (0=dir, 1=file) and
|
||||||
|
// the remaining bits for the index (which will be accordingly understood as a FileIndex or a DirIndex)
|
||||||
|
// This way, index 0 is always the top dir.
|
||||||
|
|
||||||
|
bool RsCollectionModel::convertIndexToInternalId(const EntryIndex& e,quintptr& ref)
|
||||||
|
{
|
||||||
|
ref = (e.index << 1) | e.is_file;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsCollectionModel::convertInternalIdToIndex(quintptr ref, EntryIndex& e)
|
||||||
|
{
|
||||||
|
e.is_file = (bool)(ref & 1);
|
||||||
|
e.index = ref >> 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsCollectionModel::rowCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << "Asking rowCount of " << parent << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(parent.column() >= COLLECTION_MODEL_NB_COLUMN)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(!parent.isValid())
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << " root! returning " << mCollection.fileTree().directoryData(0).subdirs.size()
|
||||||
|
+ mCollection.fileTree().directoryData(0).subfiles.size() << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return mCollection.fileTree().directoryData(0).subdirs.size()
|
||||||
|
+ mCollection.fileTree().directoryData(0).subfiles.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryIndex i;
|
||||||
|
if(!convertInternalIdToIndex(parent.internalId(),i))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(i.is_file)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << " file: returning 0" << std::endl;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << " dir: returning " << mCollection.fileTree().directoryData(i.index).subdirs.size() + mCollection.fileTree().directoryData(i.index).subfiles.size()
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
return mCollection.fileTree().directoryData(i.index).subdirs.size() + mCollection.fileTree().directoryData(i.index).subfiles.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsCollectionModel::hasChildren(const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if(!parent.isValid())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
EntryIndex i;
|
||||||
|
if(!convertInternalIdToIndex(parent.internalId(),i))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(i.is_file)
|
||||||
|
return false;
|
||||||
|
else if(mCollection.fileTree().directoryData(i.index).subdirs.size() + mCollection.fileTree().directoryData(i.index).subfiles.size() > 0)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RsCollectionModel::columnCount(const QModelIndex&) const
|
||||||
|
{
|
||||||
|
return COLLECTION_MODEL_NB_COLUMN;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant RsCollectionModel::headerData(int section, Qt::Orientation,int role) const
|
||||||
|
{
|
||||||
|
if(role == Qt::DisplayRole)
|
||||||
|
switch(section)
|
||||||
|
{
|
||||||
|
case 0: return tr("File");
|
||||||
|
case 1: return tr("Size");
|
||||||
|
case 2: return tr("Hash");
|
||||||
|
case 3: return tr("Count");
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex RsCollectionModel::index(int row, int column, const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if(row < 0 || column < 0 || column >= columnCount(parent) || row >= rowCount(parent))
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
EntryIndex parent_index;
|
||||||
|
|
||||||
|
if(!parent.isValid()) // root
|
||||||
|
{
|
||||||
|
parent_index.is_file = false;
|
||||||
|
parent_index.index = 0;
|
||||||
|
}
|
||||||
|
else if(!convertInternalIdToIndex(parent.internalId(),parent_index))
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
if(parent_index.is_file || parent_index.index >= mCollection.fileTree().numDirs())
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
const auto& parentData(mCollection.fileTree().directoryData(parent_index.index));
|
||||||
|
|
||||||
|
if((size_t)row < parentData.subdirs.size())
|
||||||
|
{
|
||||||
|
EntryIndex e;
|
||||||
|
e.is_file = false;
|
||||||
|
e.index = parentData.subdirs[row];
|
||||||
|
|
||||||
|
quintptr ref;
|
||||||
|
convertIndexToInternalId(e,ref);
|
||||||
|
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << "creating index for row " << row << " of parent " << parent << ". result is " << createIndex(row,column,ref) << std::endl;
|
||||||
|
#endif
|
||||||
|
return createIndex(row,column,ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((size_t)row < parentData.subdirs.size() + parentData.subfiles.size())
|
||||||
|
{
|
||||||
|
EntryIndex e;
|
||||||
|
e.is_file = true;
|
||||||
|
e.index = parentData.subfiles[row - parentData.subdirs.size()];
|
||||||
|
|
||||||
|
quintptr ref;
|
||||||
|
convertIndexToInternalId(e,ref);
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << "creating index for row " << row << " of parent " << parent << ". result is " << createIndex(row,column,ref) << std::endl;
|
||||||
|
#endif
|
||||||
|
return createIndex(row,column,ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags RsCollectionModel::flags ( const QModelIndex & index ) const
|
||||||
|
{
|
||||||
|
if(index.isValid() && index.column() == COLLECTION_MODEL_FILENAME)
|
||||||
|
{
|
||||||
|
EntryIndex e;
|
||||||
|
|
||||||
|
if(!convertInternalIdToIndex(index.internalId(),e))
|
||||||
|
return QAbstractItemModel::flags(index) ;
|
||||||
|
|
||||||
|
if(e.is_file)
|
||||||
|
return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable;
|
||||||
|
else
|
||||||
|
return QAbstractItemModel::flags(index) | Qt::ItemIsAutoTristate | Qt::ItemIsUserCheckable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QAbstractItemModel::flags(index) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex RsCollectionModel::parent(const QModelIndex & index) const
|
||||||
|
{
|
||||||
|
if(!index.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
EntryIndex i;
|
||||||
|
if(index.internalId()==0 || !convertInternalIdToIndex(index.internalId(),i))
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
EntryIndex p;
|
||||||
|
p.is_file = false; // all parents are directories
|
||||||
|
int row;
|
||||||
|
|
||||||
|
if(i.is_file)
|
||||||
|
{
|
||||||
|
p.index = mFileInfos[i.index].parent_index;
|
||||||
|
row = mFileInfos[i.index].parent_row;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p.index = mDirInfos[i.index].parent_index;
|
||||||
|
row = mDirInfos[i.index].parent_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
quintptr ref;
|
||||||
|
convertIndexToInternalId(p,ref);
|
||||||
|
|
||||||
|
return createIndex(row,0,ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant RsCollectionModel::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
EntryIndex i;
|
||||||
|
if(!convertInternalIdToIndex(index.internalId(),i))
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << "Asking data of " << i << std::endl;
|
||||||
|
#endif
|
||||||
|
switch(role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole: return displayRole(i,index.column());
|
||||||
|
case Qt::DecorationRole: return decorationRole(i,index.column());
|
||||||
|
case Qt::CheckStateRole: return checkStateRole(i,index.column());
|
||||||
|
case Qt::TextColorRole: return textColorRole(i,index.column());
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RsCollectionModel::setData(const QModelIndex& index,const QVariant& value,int role)
|
||||||
|
{
|
||||||
|
if(!index.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
EntryIndex e;
|
||||||
|
|
||||||
|
if(role==Qt::CheckStateRole && convertInternalIdToIndex(index.internalId(), e))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << "Setting check state of item " << index << " to " << value.toBool() << std::endl;
|
||||||
|
#endif
|
||||||
|
RsFileTree::DirIndex dir_index ;
|
||||||
|
|
||||||
|
if(e.is_file)
|
||||||
|
{
|
||||||
|
mFileInfos[e.index].is_checked = value.toBool();
|
||||||
|
dir_index = mFileInfos[e.index].parent_index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::function<void(RsFileTree::DirIndex,bool)> recursSetCheckFlag = [&](RsFileTree::DirIndex index,bool s) -> void
|
||||||
|
{
|
||||||
|
mDirInfos[index].check_state = (s)?SELECTED:UNSELECTED;
|
||||||
|
auto& dir_data(mCollection.fileTree().directoryData(index));
|
||||||
|
|
||||||
|
mDirInfos[index].total_size = 0;
|
||||||
|
mDirInfos[index].total_count = 0;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dir_data.subdirs.size();++i)
|
||||||
|
{
|
||||||
|
recursSetCheckFlag(dir_data.subdirs[i],s);
|
||||||
|
mDirInfos[index].total_size += mDirInfos[dir_data.subdirs[i]].total_size ;
|
||||||
|
mDirInfos[index].total_count+= mDirInfos[dir_data.subdirs[i]].total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dir_data.subfiles.size();++i)
|
||||||
|
{
|
||||||
|
mFileInfos[dir_data.subfiles[i]].is_checked = s;
|
||||||
|
|
||||||
|
if(s)
|
||||||
|
{
|
||||||
|
mDirInfos[index].total_size += mCollection.fileTree().fileData(dir_data.subfiles[i]).size;
|
||||||
|
++mDirInfos[index].total_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
recursSetCheckFlag(e.index,value.toBool());
|
||||||
|
dir_index = mDirInfos[e.index].parent_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now go up the directories and update the check tristate flag, depending on whether the children are all checked/unchecked or mixed.
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
auto& dit(mDirInfos[dir_index]);
|
||||||
|
|
||||||
|
const RsFileTree::DirData& dir_data(mCollection.fileTree().directoryData(dir_index)); // get the directory data
|
||||||
|
|
||||||
|
bool locally_all_checked = true;
|
||||||
|
bool locally_all_unchecked = true;
|
||||||
|
|
||||||
|
dit.total_size = 0;
|
||||||
|
dit.total_count = 0;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dir_data.subdirs.size();++i)
|
||||||
|
{
|
||||||
|
const auto& dit2(mDirInfos[dir_data.subdirs[i]]);
|
||||||
|
dit.total_size += dit2.total_size;
|
||||||
|
dit.total_count += dit2.total_count;
|
||||||
|
|
||||||
|
if(dit2.check_state == UNSELECTED || dit2.check_state == PARTIALLY_SELECTED)
|
||||||
|
locally_all_checked = false;
|
||||||
|
|
||||||
|
if(dit2.check_state == SELECTED || dit2.check_state == PARTIALLY_SELECTED)
|
||||||
|
locally_all_unchecked = false;
|
||||||
|
}
|
||||||
|
for(uint32_t i=0;i<dir_data.subfiles.size();++i)
|
||||||
|
{
|
||||||
|
const auto& fit2(mFileInfos[dir_data.subfiles[i]]);
|
||||||
|
|
||||||
|
if(fit2.is_checked)
|
||||||
|
{
|
||||||
|
dit.total_size += mCollection.fileTree().fileData(dir_data.subfiles[i]).size;
|
||||||
|
++dit.total_count;
|
||||||
|
locally_all_unchecked = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
locally_all_checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(locally_all_checked)
|
||||||
|
dit.check_state = SELECTED;
|
||||||
|
else if(locally_all_unchecked)
|
||||||
|
dit.check_state = UNSELECTED;
|
||||||
|
else
|
||||||
|
dit.check_state = PARTIALLY_SELECTED;
|
||||||
|
|
||||||
|
if(dir_index == mCollection.fileTree().root())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
dir_index = dit.parent_index; // get the directory data
|
||||||
|
|
||||||
|
}
|
||||||
|
while(true);
|
||||||
|
|
||||||
|
const auto& top_dir(mCollection.fileTree().directoryData(mCollection.fileTree().root()));
|
||||||
|
emit dataChanged(createIndex(0,0,(void*)NULL),
|
||||||
|
createIndex(top_dir.subdirs.size() + top_dir.subfiles.size() - 1,
|
||||||
|
COLLECTION_MODEL_NB_COLUMN-1,
|
||||||
|
(void*)NULL),
|
||||||
|
{ Qt::CheckStateRole });
|
||||||
|
|
||||||
|
emit sizesChanged();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QAbstractItemModel::setData(index,value,role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant RsCollectionModel::textColorRole(const EntryIndex& i,int col) const
|
||||||
|
{
|
||||||
|
if(i.is_file && mFilesBeingHashed.find(mCollection.fileTree().fileData(i.index).hash) != mFilesBeingHashed.end())
|
||||||
|
return QVariant(QBrush(QColor::fromRgbF(0.1,0.9,0.2)));
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QVariant RsCollectionModel::checkStateRole(const EntryIndex& i,int col) const
|
||||||
|
{
|
||||||
|
if(col == COLLECTION_MODEL_FILENAME)
|
||||||
|
{
|
||||||
|
if(i.is_file)
|
||||||
|
{
|
||||||
|
if(mFileInfos[i.index].is_checked)
|
||||||
|
return QVariant(Qt::Checked);
|
||||||
|
else
|
||||||
|
return QVariant(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(mDirInfos[i.index].check_state)
|
||||||
|
{
|
||||||
|
case SELECTED: return QVariant::fromValue((int)Qt::Checked);
|
||||||
|
case PARTIALLY_SELECTED: return QVariant::fromValue((int)Qt::PartiallyChecked);
|
||||||
|
default:
|
||||||
|
case UNSELECTED: return QVariant::fromValue((int)Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QVariant RsCollectionModel::displayRole(const EntryIndex& i,int col) const
|
||||||
|
{
|
||||||
|
switch(col)
|
||||||
|
{
|
||||||
|
case COLLECTION_MODEL_FILENAME: if(i.is_file)
|
||||||
|
return QString::fromUtf8(mCollection.fileTree().fileData(i.index).name.c_str());
|
||||||
|
else
|
||||||
|
return QString::fromUtf8(mCollection.fileTree().directoryData(i.index).name.c_str());
|
||||||
|
|
||||||
|
case COLLECTION_MODEL_SIZE: if(i.is_file)
|
||||||
|
return QVariant((qulonglong)mCollection.fileTree().fileData(i.index).size) ;
|
||||||
|
else
|
||||||
|
return QVariant((qulonglong)mDirInfos[i.index].total_size);
|
||||||
|
|
||||||
|
case COLLECTION_MODEL_HASH: if(i.is_file)
|
||||||
|
{
|
||||||
|
if(mFilesBeingHashed.find(mCollection.fileTree().fileData(i.index).hash)!=mFilesBeingHashed.end())
|
||||||
|
return tr("[File is being hashed]");
|
||||||
|
else
|
||||||
|
return QString::fromStdString(mCollection.fileTree().fileData(i.index).hash.toStdString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
case COLLECTION_MODEL_COUNT: if(i.is_file)
|
||||||
|
return (qulonglong)mFileInfos[i.index].is_checked;
|
||||||
|
else
|
||||||
|
return (qulonglong)(mDirInfos[i.index].total_count);
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QVariant RsCollectionModel::sortRole(const EntryIndex& i,int col) const
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
QVariant RsCollectionModel::decorationRole(const EntryIndex& i,int col) const
|
||||||
|
{
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
RsCollectionModel::EntryIndex RsCollectionModel::getIndex(const QModelIndex& i) const
|
||||||
|
{
|
||||||
|
EntryIndex res;
|
||||||
|
res.is_file = false;
|
||||||
|
res.index = 0;
|
||||||
|
|
||||||
|
convertInternalIdToIndex(i.internalId(),res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
bool RsCollectionModel::isChecked(EntryIndex i)
|
||||||
|
{
|
||||||
|
if(i.is_file)
|
||||||
|
return mFileInfos[i.index].is_checked;
|
||||||
|
else
|
||||||
|
return mDirInfos[i.index].check_state != DirCheckState::UNSELECTED;
|
||||||
|
}
|
||||||
|
void RsCollectionModel::notifyFilesBeingHashed(const std::list<RsFileHash>& files)
|
||||||
|
{
|
||||||
|
mFilesBeingHashed.insert(files.begin(),files.end());
|
||||||
|
}
|
||||||
|
void RsCollectionModel::fileHashingFinished(const RsFileHash& hash)
|
||||||
|
{
|
||||||
|
mFilesBeingHashed.erase(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsCollectionModel::preMods()
|
||||||
|
{
|
||||||
|
mUpdating = true;
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
|
}
|
||||||
|
void RsCollectionModel::postMods()
|
||||||
|
{
|
||||||
|
// update all the local structures
|
||||||
|
|
||||||
|
mDirInfos.clear();
|
||||||
|
mFileInfos.clear();
|
||||||
|
|
||||||
|
mDirInfos.resize(mCollection.fileTree().numDirs());
|
||||||
|
mFileInfos.resize(mCollection.fileTree().numFiles());
|
||||||
|
|
||||||
|
mDirInfos[0].parent_index = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
std::cerr << "Updating from tree: " << std::endl;
|
||||||
|
#endif
|
||||||
|
recursUpdateLocalStructures(mCollection.fileTree().root(),0);
|
||||||
|
|
||||||
|
mUpdating = false;
|
||||||
|
emit layoutChanged();
|
||||||
|
emit sizesChanged();
|
||||||
|
|
||||||
|
// debugDump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsCollectionModel::recursUpdateLocalStructures(RsFileTree::DirIndex dir_index,int depth)
|
||||||
|
{
|
||||||
|
uint64_t total_size = 0;
|
||||||
|
uint64_t total_count = 0;
|
||||||
|
bool all_checked = true;
|
||||||
|
bool all_unchecked = false;
|
||||||
|
|
||||||
|
const auto& dd(mCollection.fileTree().directoryData(dir_index));
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dd.subdirs.size();++i)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
for(int j=0;j<depth;++j) std::cerr << " ";
|
||||||
|
std::cerr << "Dir \"" << mCollection.fileTree().directoryData(dd.subdirs[i]).name << "\"" << std::endl ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
recursUpdateLocalStructures(dd.subdirs[i],depth+1);
|
||||||
|
|
||||||
|
auto& ref(mDirInfos[dd.subdirs[i]]);
|
||||||
|
|
||||||
|
total_size += ref.total_size;
|
||||||
|
total_count += ref.total_count;
|
||||||
|
|
||||||
|
ref.parent_index = dir_index;
|
||||||
|
ref.parent_row = i;
|
||||||
|
|
||||||
|
all_checked = all_checked && (ref.check_state == SELECTED);
|
||||||
|
all_unchecked = all_unchecked && (ref.check_state == UNSELECTED);
|
||||||
|
}
|
||||||
|
for(uint32_t i=0;i<dd.subfiles.size();++i)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_COLLECTION_MODEL
|
||||||
|
for(int j=0;j<depth;++j) std::cerr << " ";
|
||||||
|
std::cerr << "File \"" << mCollection.fileTree().fileData(dd.subfiles[i]).name << "\"" << std::endl;
|
||||||
|
#endif
|
||||||
|
auto& ref(mFileInfos[dd.subfiles[i]]);
|
||||||
|
|
||||||
|
if(ref.is_checked)
|
||||||
|
{
|
||||||
|
total_size += mCollection.fileTree().fileData(dd.subfiles[i]).size;
|
||||||
|
++total_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref.parent_index = dir_index;
|
||||||
|
ref.parent_row = i + dd.subdirs.size();
|
||||||
|
|
||||||
|
all_checked = all_checked && ref.is_checked;
|
||||||
|
all_unchecked = all_unchecked && !ref.is_checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& r(mDirInfos[dir_index]);
|
||||||
|
|
||||||
|
r.total_size = total_size;
|
||||||
|
r.total_count = total_count;
|
||||||
|
|
||||||
|
if(all_checked)
|
||||||
|
r.check_state = SELECTED;
|
||||||
|
else if(all_unchecked)
|
||||||
|
r.check_state = UNSELECTED;
|
||||||
|
else
|
||||||
|
r.check_state = PARTIALLY_SELECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsCollectionModel::debugDump()
|
||||||
|
{
|
||||||
|
std::function<void(RsFileTree::DirIndex,int)> recursDump = [&](RsFileTree::DirIndex indx,int depth) {
|
||||||
|
const auto& dir_data(mCollection.fileTree().directoryData(indx));
|
||||||
|
|
||||||
|
for(int i=0;i<depth;++i) std::cerr << " ";
|
||||||
|
std::cerr << "Directory: \"" << dir_data.name << "\" total_size: " << mDirInfos[indx].total_size << " cnt: " << mDirInfos[indx].total_count << std::endl;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dir_data.subdirs.size();++i)
|
||||||
|
recursDump(dir_data.subdirs[i],depth+1);
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<dir_data.subfiles.size();++i)
|
||||||
|
{
|
||||||
|
for(int i=0;i<depth+1;++i) std::cerr << " ";
|
||||||
|
std::cerr << "File: \"" << mCollection.fileTree().fileData(dir_data.subfiles[i]).name << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::cerr << "mCollectionModel data: " << std::endl;
|
||||||
|
|
||||||
|
recursDump(mCollection.fileTree().root(),0);
|
||||||
|
|
||||||
|
std::function<void(QModelIndex index,int)> recursDump2 = [&](QModelIndex indx,int depth) {
|
||||||
|
|
||||||
|
for(int i=0;i<depth;++i) std::cerr << " ";
|
||||||
|
|
||||||
|
EntryIndex entry;
|
||||||
|
convertInternalIdToIndex(indx.internalId(),entry);
|
||||||
|
|
||||||
|
std::cerr << "Index: " << indx << " has_children: " << RsCollectionModel::hasChildren(indx)
|
||||||
|
<< ", rowCount: " << RsCollectionModel::rowCount(indx)
|
||||||
|
<< ", internalId: " << (uint64_t)indx.internalId()
|
||||||
|
<< ", type: " << (entry.is_file?"file":"dir") << " number " << entry.index
|
||||||
|
<< ", prow: " << (entry.is_file?mFileInfos[entry.index].parent_row:mDirInfos[entry.index].parent_row)
|
||||||
|
<< ", parent: " << RsCollectionModel::parent(indx)
|
||||||
|
<< ", display role: \"" << displayRole(entry,0).toString().toStdString()
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
for(int i=0;i<RsCollectionModel::rowCount(indx);++i)
|
||||||
|
recursDump2(RsCollectionModel::index(i,0,indx),depth+1);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::cerr << "mCollectionModel index structure: " << std::endl;
|
||||||
|
|
||||||
|
recursDump2(QModelIndex(),0);
|
||||||
|
|
||||||
|
std::cerr << "mCollectionModel internal data: " << std::endl;
|
||||||
|
std::cerr << "Directories: "<< std::endl;
|
||||||
|
for(uint32_t i=0;i<mCollection.fileTree().numDirs();++i)
|
||||||
|
std::cerr << " " << i << ": "<< mCollection.fileTree().directoryData(i).name << std::endl;
|
||||||
|
std::cerr << "Files: "<< std::endl;
|
||||||
|
for(uint32_t i=0;i<mCollection.fileTree().numFiles();++i)
|
||||||
|
std::cerr << " " << i << ": " << mCollection.fileTree().fileData(i).name << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
108
retroshare-gui/src/gui/common/RsCollectionModel.h
Normal file
108
retroshare-gui/src/gui/common/RsCollectionModel.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include "RsCollection.h"
|
||||||
|
|
||||||
|
class RsCollectionModel: public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles{ FileNameRole = Qt::UserRole+1, SortRole = Qt::UserRole+2, FilterRole = Qt::UserRole+3 };
|
||||||
|
|
||||||
|
RsCollectionModel(const RsCollection& col, QObject *parent = 0);
|
||||||
|
virtual ~RsCollectionModel() = default;
|
||||||
|
|
||||||
|
/* Callback from Core */
|
||||||
|
void preMods(); // always call this before updating the RsCollection!
|
||||||
|
void postMods(); // always call this after updating the RsCollection!
|
||||||
|
|
||||||
|
/* Callback from GUI */
|
||||||
|
|
||||||
|
void update() ;
|
||||||
|
void filterItems(const std::list<std::string>& keywords, uint32_t& found) ;
|
||||||
|
|
||||||
|
// Overloaded from QAbstractItemModel
|
||||||
|
virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex() ) const override;
|
||||||
|
virtual QModelIndex parent ( const QModelIndex & index ) const override;
|
||||||
|
|
||||||
|
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
virtual bool setData(const QModelIndex& index,const QVariant& value,int role) override;
|
||||||
|
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const override;
|
||||||
|
#ifdef TODO
|
||||||
|
virtual QStringList mimeTypes () const override;
|
||||||
|
virtual QMimeData * mimeData ( const QModelIndexList & indexes ) const override;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0)
|
||||||
|
virtual Qt::DropActions supportedDragActions() const override;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct EntryIndex {
|
||||||
|
bool is_file; // false=dir, true=file
|
||||||
|
uint64_t index;
|
||||||
|
};
|
||||||
|
uint64_t totalSize() const { return mDirInfos[0].total_size; }
|
||||||
|
uint64_t totalSelected() const { return mDirInfos[0].total_count; }
|
||||||
|
|
||||||
|
void notifyFilesBeingHashed(const std::list<RsFileHash>& files);
|
||||||
|
void fileHashingFinished(const RsFileHash& hash);
|
||||||
|
bool isChecked(EntryIndex);
|
||||||
|
|
||||||
|
EntryIndex getIndex(const QModelIndex& i) const;
|
||||||
|
signals:
|
||||||
|
void sizesChanged(); // tells that the total size of the top level dir has changed (due to selection)
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool convertIndexToInternalId(const EntryIndex& e,quintptr& ref);
|
||||||
|
static bool convertInternalIdToIndex(quintptr ref, EntryIndex& e);
|
||||||
|
|
||||||
|
void recursUpdateLocalStructures(RsFileTree::DirIndex dir_index, int depth);
|
||||||
|
|
||||||
|
QVariant displayRole(const EntryIndex&,int col) const ;
|
||||||
|
QVariant sortRole(const EntryIndex&,int col) const ;
|
||||||
|
QVariant decorationRole(const EntryIndex&,int col) const ;
|
||||||
|
QVariant checkStateRole(const EntryIndex& i,int col) const;
|
||||||
|
QVariant textColorRole(const EntryIndex& i,int col) const;
|
||||||
|
//QVariant filterRole(const DirDetails& details,int coln) const;
|
||||||
|
|
||||||
|
void debugDump();
|
||||||
|
|
||||||
|
bool mUpdating ;
|
||||||
|
|
||||||
|
const RsCollection& mCollection;
|
||||||
|
|
||||||
|
enum DirCheckState: uint8_t {
|
||||||
|
UNSELECTED = 0x00,
|
||||||
|
PARTIALLY_SELECTED = 0x01,
|
||||||
|
SELECTED = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModelDirInfo {
|
||||||
|
ModelDirInfo() :parent_index(0),parent_row(0),check_state(SELECTED),total_size(0),total_count(0){}
|
||||||
|
|
||||||
|
RsFileTree::DirIndex parent_index; // index of the parent
|
||||||
|
RsFileTree::DirIndex parent_row; // row of that child, in this parent
|
||||||
|
DirCheckState check_state;
|
||||||
|
uint64_t total_size;
|
||||||
|
uint64_t total_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ModelFileInfo {
|
||||||
|
ModelFileInfo() :parent_index(0),parent_row(0),is_checked(true){}
|
||||||
|
|
||||||
|
RsFileTree::DirIndex parent_index; // index of the parent
|
||||||
|
RsFileTree::DirIndex parent_row; // row of that child, in this parent
|
||||||
|
bool is_checked;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ModelFileInfo> mFileInfos;
|
||||||
|
std::vector<ModelDirInfo> mDirInfos;
|
||||||
|
|
||||||
|
std::set<RsFileHash> mFilesBeingHashed;
|
||||||
|
|
||||||
|
// std::set<void*> mFilteredPointers ;
|
||||||
|
};
|
@ -21,19 +21,13 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include "RsCollection.h"
|
#include "RsCollectionDialog.h"
|
||||||
#include "RsUrlHandler.h"
|
#include "RsUrlHandler.h"
|
||||||
|
|
||||||
bool RsUrlHandler::openUrl(const QUrl& url)
|
bool RsUrlHandler::openUrl(const QUrl& url)
|
||||||
{
|
{
|
||||||
if(url.scheme() == QString("file") && url.toLocalFile().endsWith("."+RsCollection::ExtensionString))
|
if(url.scheme() == QString("file") && url.toLocalFile().endsWith("."+RsCollection::ExtensionString))
|
||||||
{
|
return RsCollectionDialog::openExistingCollection(url.toLocalFile());
|
||||||
RsCollection collection ;
|
|
||||||
if(collection.load(url.toLocalFile()))
|
|
||||||
{
|
|
||||||
collection.downloadFiles() ;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QDesktopServices::openUrl(url) ;
|
return QDesktopServices::openUrl(url) ;
|
||||||
}
|
}
|
||||||
|
@ -519,6 +519,7 @@ HEADERS += rshare.h \
|
|||||||
gui/common/vmessagebox.h \
|
gui/common/vmessagebox.h \
|
||||||
gui/common/RsUrlHandler.h \
|
gui/common/RsUrlHandler.h \
|
||||||
gui/common/RsCollectionDialog.h \
|
gui/common/RsCollectionDialog.h \
|
||||||
|
gui/common/RsCollectionModel.h \
|
||||||
gui/common/rwindow.h \
|
gui/common/rwindow.h \
|
||||||
gui/common/rshtml.h \
|
gui/common/rshtml.h \
|
||||||
gui/common/AvatarDefs.h \
|
gui/common/AvatarDefs.h \
|
||||||
@ -845,6 +846,7 @@ SOURCES += main.cpp \
|
|||||||
gui/common/ElidedLabel.cpp \
|
gui/common/ElidedLabel.cpp \
|
||||||
gui/common/vmessagebox.cpp \
|
gui/common/vmessagebox.cpp \
|
||||||
gui/common/RsCollectionDialog.cpp \
|
gui/common/RsCollectionDialog.cpp \
|
||||||
|
gui/common/RsCollectionModel.cpp \
|
||||||
gui/common/RsUrlHandler.cpp \
|
gui/common/RsUrlHandler.cpp \
|
||||||
gui/common/rwindow.cpp \
|
gui/common/rwindow.cpp \
|
||||||
gui/common/rshtml.cpp \
|
gui/common/rshtml.cpp \
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit ba0de17f41914f1be6754db5bc5b4507b0deaafd
|
Subproject commit 542a8c07bd02f9bb9082f7aba5aaaed54e643fc1
|
@ -1 +1 @@
|
|||||||
Subproject commit 2226ef0a20a001ec0942be6abe5e909c15447d8e
|
Subproject commit 8623304b62294dafbe477573f321a464fef721dd
|
Loading…
Reference in New Issue
Block a user