/**************************************************************** * RetroShare is distributed under the following license: * * Copyright (C) 2011 - 2011 RetroShare Team * * Cyril Soler (csoler@users.sourceforge.net) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. ****************************************************************/ #include "RsCollectionDialog.h" #include "RsCollection.h" #include "util/misc.h" #include #include #include #include #include #include #include #include #include #include #include #define COLUMN_FILE 0 #define COLUMN_FILEPATH 1 #define COLUMN_SIZE 2 #define COLUMN_HASH 3 #define COLUMN_FILEC 4 #define COLUMN_COUNT 5 // In COLUMN_HASH (COLUMN_FILE reserved for CheckState) #define ROLE_NAME Qt::UserRole + 1 #define ROLE_PATH Qt::UserRole + 2 #define ROLE_TYPE Qt::UserRole + 3 // In COLUMN_SIZE (don't start from 1 to read) #define ROLE_SIZE Qt::UserRole + 4 #define ROLE_SELSIZE Qt::UserRole + 5 // In COLUMN_FILEC (don't start from 1 to read) #define ROLE_FILEC Qt::UserRole + 6 #define ROLE_SELFILEC Qt::UserRole + 7 #define MAX_FILE_ADDED_BEFORE_ASK 500 //Number of file added in Recursive mode before asking to continue #define IMAGE_SEARCH ":/icons/svg/magnifying-glass.svg" /** * @brief The FSMSortFilterProxyModel class sort directory before file. */ class FSMSortFilterProxyModel : public QSortFilterProxyModel { public: FSMSortFilterProxyModel( QObject *parent) : QSortFilterProxyModel(parent) {} protected: virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const { QFileSystemModel *fsm = qobject_cast(sourceModel()); bool asc = (sortOrder() == Qt::AscendingOrder ? true : false) ; QFileInfo leftFileInfo = fsm->fileInfo(left); QFileInfo rightFileInfo = fsm->fileInfo(right); // If Dot move in the beginning if (sourceModel()->data(left).toString() == ".") return asc; if (sourceModel()->data(right).toString() == ".") return !asc; // If DotAndDot move in the beginning if (sourceModel()->data(left).toString() == "..") return asc; if (sourceModel()->data(right).toString() == "..") return !asc; // Move dirs upper if (!leftFileInfo.isDir() && rightFileInfo.isDir()) return !asc; if (leftFileInfo.isDir() && !rightFileInfo.isDir()) return asc; /*If sorting by Size (Take real size, not Display one 10<2)*/ if ((sortColumn()==1) && (!leftFileInfo.isDir() && !rightFileInfo.isDir())) { if (leftFileInfo.size() < rightFileInfo.size()) return true; if (leftFileInfo.size() > rightFileInfo.size()) return false; } /*If sorting by Date Modified (Take real date, not Display one 01-10-2014<02-01-1980)*/ if (sortColumn()==3) { if (leftFileInfo.lastModified() < rightFileInfo.lastModified()) return true; if (leftFileInfo.lastModified() > rightFileInfo.lastModified()) return false; } //Columns found here:https://qt.gitorious.org/qt/qt/source/9e8abb63ba4609887d988ee15ba6daee0b01380e:src/gui/dialogs/qfilesystemmodel.cpp return QSortFilterProxyModel::lessThan(left, right); } }; /** * @brief RsCollectionDialog::RsCollectionDialog * @param collectionFileName: Filename of RSCollection saved * @param colFileInfos: Vector of ColFileInfo to be add in intialization * @param creation: Open dialog as RsColl Creation or RsColl DownLoad * @param readOnly: Open dialog for RsColl as ReadOnly */ RsCollectionDialog::RsCollectionDialog(const QString& collectionFileName , const std::vector& colFileInfos , const bool& creation /* = false*/ , const bool& readOnly) : _fileName(collectionFileName), _creationMode(creation) ,_readOnly(readOnly) { ui.setupUi(this) ; uint32_t size = colFileInfos.size(); for(uint32_t i=0;isetHeaderImage(QPixmap(":/images/library64.png")); if(creation) { ui.headerFrame->setHeaderText(tr("Collection Editor")); ui.downloadFolder_LE->hide(); ui.downloadFolder_LB->hide(); ui.destinationDir_TB->hide(); } else { ui.headerFrame->setHeaderText(tr("Download files")); ui.downloadFolder_LE->show(); ui.downloadFolder_LB->show(); ui.label_filename->hide(); ui._filename_TL->hide(); ui.downloadFolder_LE->setText(QString::fromUtf8(rsFiles->getDownloadDirectory().c_str())) ; QObject::connect(ui.downloadFolder_LE,SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(openDestinationDirectoryMenu())); QObject::connect(ui.destinationDir_TB,SIGNAL(pressed()), this, SLOT(openDestinationDirectoryMenu())); } // 1 - add all elements to the list. ui._fileEntriesTW->setColumnCount(COLUMN_COUNT) ; QTreeWidgetItem *headerItem = ui._fileEntriesTW->headerItem(); headerItem->setText(COLUMN_FILE, tr("File")); headerItem->setText(COLUMN_FILEPATH, tr("File Path")); headerItem->setText(COLUMN_SIZE, tr("Size")); headerItem->setText(COLUMN_HASH, tr("Hash")); headerItem->setText(COLUMN_FILEC, tr("File Count")); bool wrong_chars = !updateList(); // 2 - connect necessary signals/slots connect(ui._changeFile, SIGNAL(clicked()), this, SLOT(changeFileName())); connect(ui._add_PB, SIGNAL(clicked()), this, SLOT(add())); connect(ui._addRecur_PB, SIGNAL(clicked()), this, SLOT(addRecursive())); connect(ui._remove_PB, SIGNAL(clicked()), this, SLOT(remove())); connect(ui._makeDir_PB, SIGNAL(clicked()), this, SLOT(makeDir())); connect(ui._removeDuplicate_CB, SIGNAL(clicked(bool)), this, SLOT(updateRemoveDuplicate(bool))); connect(ui._cancel_PB, SIGNAL(clicked()), this, SLOT(cancel())); connect(ui._save_PB, SIGNAL(clicked()), this, SLOT(save())); connect(ui._download_PB, SIGNAL(clicked()), this, SLOT(download())); connect(ui._hashBox, SIGNAL(fileHashingFinished(QList)), this, SLOT(fileHashingFinished(QList))); connect(ui._fileEntriesTW, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*,int))); // 3 Initialize List _dirModel = new QFileSystemModel(this); _dirModel->setRootPath("/"); _dirModel->setFilter(QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot); _dirLoaded = false; connect(_dirModel, SIGNAL(directoryLoaded(QString)), this, SLOT(directoryLoaded(QString))); _tree_proxyModel = new FSMSortFilterProxyModel(this); _tree_proxyModel->setDynamicSortFilter(true); _tree_proxyModel->setSourceModel(_dirModel); _tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); _tree_proxyModel->setSortRole(Qt::DisplayRole); ui._systemFileTW->setModel(_tree_proxyModel); //Selection Setup _selectionProxy = ui._systemFileTW->selectionModel(); // 4 Restore Configuration // load settings processSettings(true); // 5 Activate button follow creationMode ui._changeFile->setVisible(_creationMode && !_readOnly); ui._makeDir_PB->setVisible(_creationMode && !_readOnly); ui._removeDuplicate_CB->setVisible(_creationMode && !_readOnly); ui._save_PB->setVisible(_creationMode && !_readOnly); ui._treeViewFrame->setVisible(_creationMode && !_readOnly); ui._download_PB->setVisible(!_creationMode && !_readOnly); ui._fileEntriesTW->installEventFilter(this); ui._systemFileTW->installEventFilter(this); // 6 Add HashBox setAcceptDrops(true); ui._hashBox->setDropWidget(this); ui._hashBox->setAutoHide(true); ui._hashBox->setDefaultTransferRequestFlags(RS_FILE_REQ_ANONYMOUS_ROUTING) ; if(wrong_chars) QMessageBox::warning(NULL,tr("Bad filenames have been cleaned"),tr("Some filenames or directory names contained forbidden characters.\nCharacters \",|,/,\\,<,>,*,? will be replaced by '_'.\n Concerned files are listed in red.")) ; } void RsCollectionDialog::openDestinationDirectoryMenu() { QMenu contextMnu( this ); // pop a menu with existing entries and also a custom entry // Now get the list of existing directories. std::list< SharedDirInfo> dirs ; rsFiles->getSharedDirectories( dirs) ; for (std::list::const_iterator it(dirs.begin());it!=dirs.end();++it){ // Check for existence of directory name QFile directory( QString::fromUtf8((*it).filename.c_str())) ; if (!directory.exists()) continue ; if (!(directory.permissions() & QFile::WriteOwner)) continue ; contextMnu.addAction(QString::fromUtf8((*it).filename.c_str()), this, SLOT(setDestinationDirectory()))->setData(QString::fromUtf8( (*it).filename.c_str() ) ) ; } contextMnu.addAction( QIcon(IMAGE_SEARCH),tr("Specify..."),this,SLOT(chooseDestinationDirectory())); contextMnu.exec(QCursor::pos()) ; } void RsCollectionDialog::setDestinationDirectory() { QString dest_dir(qobject_cast(sender())->data().toString()) ; ui.downloadFolder_LE->setText(dest_dir) ; } void RsCollectionDialog::chooseDestinationDirectory() { QString dest_dir = QFileDialog::getExistingDirectory(this,tr("Choose directory")) ; if(dest_dir.isNull()) return ; ui.downloadFolder_LE->setText(dest_dir) ; } /** * @brief RsCollectionDialog::~RsCollectionDialog */ RsCollectionDialog::~RsCollectionDialog() { // save settings processSettings(false); } /** * @brief RsCollectionDialog::eventFilter: Proccess event in object * @param obj: object where event occured * @param event: event occured * @return If we don't have to process event in parent. */ bool RsCollectionDialog::eventFilter(QObject *obj, QEvent *event) { if (obj == ui._fileEntriesTW) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent && (keyEvent->key() == Qt::Key_Space)) { // Space pressed // get state of current item QTreeWidgetItem *item = ui._fileEntriesTW->currentItem(); if (item) { Qt::CheckState checkState = (item->checkState(COLUMN_FILE) == Qt::Checked) ? Qt::Unchecked : Qt::Checked; // set state of all selected items QList selectedItems = ui._fileEntriesTW->selectedItems(); QList::iterator it; for (it = selectedItems.begin(); it != selectedItems.end(); ++it) { if ((*it)->checkState(COLUMN_FILE) != checkState) (*it)->setCheckState(COLUMN_FILE, checkState); } } return true; // eat event } if (keyEvent && (keyEvent->key() == Qt::Key_Delete)) { // Delete pressed remove(); return true; // eat event } if (keyEvent && (keyEvent->key() == Qt::Key_Plus)) { // Plus pressed makeDir(); return true; // eat event } } } if (obj == ui._systemFileTW) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent && ((keyEvent->key() == Qt::Key_Enter) || keyEvent->key() == Qt::Key_Return)) { // Enter pressed if (keyEvent->modifiers() == Qt::ShiftModifier) addRecursive(); else if(keyEvent->modifiers() == Qt::NoModifier) { add(); } return true; // eat event } } } // pass the event on to the parent class return QDialog::eventFilter(obj, event); } /** * @brief RsCollectionDialog::processSettings * @param bLoad: Load or Save dialog's settings */ void RsCollectionDialog::processSettings(bool bLoad) { Settings->beginGroup("RsCollectionDialog"); if (bLoad) { // load settings if(_creationMode && !_readOnly){ // Load windows geometrie restoreGeometry(Settings->value("WindowGeometrie_CM").toByteArray()); // Load splitters state ui._mainSplitter->restoreState(Settings->value("MainSplitterState_CM").toByteArray()); ui._listSplitter->restoreState(Settings->value("ListSplitterState_CM").toByteArray()); // Load system file header configuration ui._systemFileTW->header()->restoreState(Settings->value("SystemFileHeader_CM").toByteArray()); // Load file entries header configuration ui._fileEntriesTW->header()->restoreState(Settings->value("FileEntriesHeader_CM").toByteArray()); } else { // Load windows geometrie restoreGeometry(Settings->value("WindowGeometrie").toByteArray()); // Load splitters state ui._mainSplitter->restoreState(Settings->value("MainSplitterState").toByteArray()); ui._listSplitter->restoreState(Settings->value("ListSplitterState").toByteArray()); // Load system file header configuration ui._systemFileTW->header()->restoreState(Settings->value("SystemFileHeader").toByteArray()); // Load file entries header configuration ui._fileEntriesTW->header()->restoreState(Settings->value("FileEntriesHeader").toByteArray()); } } else { if(_creationMode && !_readOnly){ // Save windows geometrie Settings->setValue("WindowGeometrie_CM",saveGeometry()); // Save splitters state Settings->setValue("MainSplitterState_CM", ui._mainSplitter->saveState()); Settings->setValue("ListSplitterState_CM", ui._listSplitter->saveState()); // Save system file header configuration Settings->setValue("SystemFileHeader_CM", ui._systemFileTW->header()->saveState()); // Save file entries header configuration Settings->setValue("FileEntriesHeader_CM", ui._fileEntriesTW->header()->saveState()); } else { // Save windows geometrie Settings->setValue("WindowGeometrie",saveGeometry()); // Save splitter state Settings->setValue("MainSplitterState", ui._mainSplitter->saveState()); Settings->setValue("ListSplitterState", ui._listSplitter->saveState()); // Save system file header configuration Settings->setValue("SystemFileHeader", ui._systemFileTW->header()->saveState()); // Save file entries header configuration Settings->setValue("FileEntriesHeader", ui._fileEntriesTW->header()->saveState()); } } Settings->endGroup(); } /** * @brief RsCollectionDialog::getRootItem: Create the root Item if not existing * @return: the root item */ QTreeWidgetItem* RsCollectionDialog::getRootItem() { return ui._fileEntriesTW->invisibleRootItem(); // (csoler) I removed this code because it does the job of the invisibleRootItem() method. // // QTreeWidgetItem* root= ui._fileEntriesTW->topLevelItem(0); // if (!root) { // root= new QTreeWidgetItem; // root->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate); // root->setText(COLUMN_FILE, "/"); // root->setToolTip(COLUMN_FILE,tr("This is the root directory.")); // root->setText(COLUMN_FILEPATH, "/"); // root->setText(COLUMN_HASH, ""); // root->setData(COLUMN_HASH, ROLE_NAME, ""); // root->setData(COLUMN_HASH, ROLE_PATH, ""); // root->setData(COLUMN_HASH, ROLE_TYPE, DIR_TYPE_DIR); // root->setText(COLUMN_SIZE, misc::friendlyUnit(0)); // root->setToolTip(COLUMN_SIZE, tr("Real Size: Waiting child...")); // root->setData(COLUMN_SIZE, ROLE_SIZE, 0); // root->setData(COLUMN_SIZE, ROLE_SELSIZE, 0); // root->setText(COLUMN_FILEC, "0"); // root->setToolTip(COLUMN_FILEC, tr("Real File Count: Waiting child...")); // root->setData(COLUMN_FILEC, ROLE_FILEC, 0); // root->setData(COLUMN_FILEC, ROLE_SELFILEC, 0); // ui._fileEntriesTW->addTopLevelItem(root); // } // root->setExpanded(true); // // return root; } /** * @brief RsCollectionDialog::updateList: Update list of item in RsCollection * @return If at least one item have a Wrong Char */ bool RsCollectionDialog::updateList() { bool wrong_chars = false ; wrong_chars = addChild(getRootItem(), _newColFileInfos); _newColFileInfos.clear(); ui._filename_TL->setText(_fileName) ; for (int column = 0; column < ui._fileEntriesTW->columnCount(); ++column) { ui._fileEntriesTW->resizeColumnToContents(column); } updateSizes() ; return !wrong_chars; } /** * @brief RsCollectionDialog::addChild: Add Child Item in list * @param parent: Parent Item * @param child: Child ColFileInfo item to add * @param total_size: Saved total size of children to save in parent * @param total_files: Saved total file of children to save in parent * @return If at least one item have a Wrong Char */ bool RsCollectionDialog::addChild(QTreeWidgetItem* parent, const std::vector& child) { bool wrong_chars = false ; uint32_t childCount = child.size(); for(uint32_t i=0; i founds; QList parentsFounds; parentsFounds = ui._fileEntriesTW->findItems(colFileInfo.path , Qt::MatchExactly | Qt::MatchRecursive, COLUMN_FILEPATH); if (colFileInfo.type == DIR_TYPE_DIR){ founds = ui._fileEntriesTW->findItems(colFileInfo.path + "/" +colFileInfo.name, Qt::MatchExactly | Qt::MatchRecursive, COLUMN_FILEPATH); } else { founds = ui._fileEntriesTW->findItems(colFileInfo.path + "/" +colFileInfo.name, Qt::MatchExactly | Qt::MatchRecursive, COLUMN_FILEPATH); if (ui._removeDuplicate_CB->isChecked()) { founds << ui._fileEntriesTW->findItems(colFileInfo.hash, Qt::MatchExactly | Qt::MatchRecursive, COLUMN_HASH); } } if (founds.empty()) { QTreeWidgetItem *item = new QTreeWidgetItem; //item->setFlags(Qt::ItemIsUserCheckable | item->flags()); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate); item->setCheckState(COLUMN_FILE, Qt::Checked); item->setText(COLUMN_FILE, colFileInfo.name); item->setText(COLUMN_FILEPATH, colFileInfo.path + "/" + colFileInfo.name); item->setText(COLUMN_HASH, colFileInfo.hash); item->setData(COLUMN_HASH, ROLE_NAME, colFileInfo.name); item->setData(COLUMN_HASH, ROLE_PATH, colFileInfo.path); item->setData(COLUMN_HASH, ROLE_TYPE, colFileInfo.type); QFont font = item->font(COLUMN_FILE); if (colFileInfo.type==DIR_TYPE_DIR) { item->setToolTip(COLUMN_FILE,tr("This is a directory. Double-click to expand it.")); font.setBold(true); //Size calculated after for added child after init item->setText(COLUMN_SIZE, misc::friendlyUnit(0)); item->setToolTip(COLUMN_SIZE,tr("Real Size: Waiting child...")); item->setData(COLUMN_SIZE, ROLE_SIZE, 0); item->setData(COLUMN_SIZE, ROLE_SELSIZE, 0); item->setText(COLUMN_FILEC, ""); item->setToolTip(COLUMN_FILEC, tr("Real File Count: Waiting child...")); item->setData(COLUMN_FILEC, ROLE_FILEC, 0); item->setData(COLUMN_FILEC, ROLE_SELFILEC, 0); } else { font.setBold(false); item->setText(COLUMN_SIZE, misc::friendlyUnit(colFileInfo.size)); item->setToolTip(COLUMN_SIZE, tr("Real Size=%1").arg(misc::friendlyUnit(colFileInfo.size))); item->setData(COLUMN_SIZE, ROLE_SIZE, colFileInfo.size); item->setData(COLUMN_SIZE, ROLE_SELSIZE, colFileInfo.size); item->setText(COLUMN_FILEC, "1"); item->setToolTip(COLUMN_FILEC, tr("Real File Count=%1").arg(1)); item->setData(COLUMN_FILEC, ROLE_FILEC, 1); item->setData(COLUMN_FILEC, ROLE_SELFILEC, 1); } item->setFont(COLUMN_FILE, font); item->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); if (colFileInfo.filename_has_wrong_characters) { wrong_chars = true ; item->setTextColor(COLUMN_FILE, QColor(255,80,120)) ; } if (parentsFounds.empty()) { parent->addChild(item); } else { parentsFounds.at(0)->addChild(item); } if (colFileInfo.type == DIR_TYPE_FILE) { //update parents size only for file children QTreeWidgetItem *itemParent = item->parent(); if (itemParent) { while (itemParent) { qulonglong parentSize = itemParent->data(COLUMN_SIZE, ROLE_SIZE).toULongLong() + colFileInfo.size; itemParent->setData(COLUMN_SIZE, ROLE_SIZE, parentSize); itemParent->setToolTip(COLUMN_SIZE, tr("Real Size=%1").arg(misc::friendlyUnit(parentSize))); qulonglong parentSelSize = itemParent->data(COLUMN_SIZE, ROLE_SELSIZE).toULongLong() + colFileInfo.size; itemParent->setData(COLUMN_SIZE, ROLE_SELSIZE, parentSelSize); itemParent->setText(COLUMN_SIZE, misc::friendlyUnit(parentSelSize)); qulonglong parentFileCount = itemParent->data(COLUMN_FILEC, ROLE_FILEC).toULongLong() + 1; itemParent->setData(COLUMN_FILEC, ROLE_FILEC, parentFileCount); itemParent->setToolTip(COLUMN_FILEC, tr("Real File Count=%1").arg(parentFileCount)); qulonglong parentSelFileCount = itemParent->data(COLUMN_FILEC, ROLE_SELFILEC).toULongLong() + 1; itemParent->setData(COLUMN_FILEC, ROLE_SELFILEC, parentSelFileCount); itemParent->setText(COLUMN_FILEC, QString("%1").arg(parentSelFileCount)); itemParent = itemParent->parent(); } } } founds.push_back(item); } if (!founds.empty()) { if (colFileInfo.type == DIR_TYPE_DIR) { wrong_chars |= addChild(founds.at(0), colFileInfo.children); } } } return wrong_chars; } /** * @brief RsCollectionDialog::directoryLoaded: called when ui._treeView have load an directory as QFileSystemModel don't load all in one time * @param dirLoaded: Name of directory just loaded */ void RsCollectionDialog::directoryLoaded(QString dirLoaded) { if(!_dirLoaded) { QFileInfo lastDir = Settings->getLastDir(RshareSettings::LASTDIR_EXTRAFILE); if (lastDir.absoluteFilePath() == "") return; if (lastDir.absoluteFilePath() == dirLoaded) _dirLoaded = true; // Check all parent directory to see if it is loaded //as QFileSystemModel don't load all do { if(lastDir.absolutePath()==dirLoaded) { //Only expand loaded directory QModelIndex lastDirIdx = _dirModel->index(lastDir.absoluteFilePath()); //Select LASTDIR_EXTRAFILE or last parent found when loaded if (!lastDirIdx.isValid()){ _dirLoaded = true; break; } //Ask dirModel to load next parent directory while (_dirModel->canFetchMore(lastDirIdx)) _dirModel->fetchMore(lastDirIdx); //Ask to Expand last loaded parent lastDirIdx = _tree_proxyModel->mapFromSource(lastDirIdx); if (lastDirIdx.isValid()){ ui._systemFileTW->expand(lastDirIdx); ui._systemFileTW->setCurrentIndex(lastDirIdx); } break; } //std::cerr << "lastDir = " << lastDir.absoluteFilePath().toStdString() << std::endl; QDir c = lastDir.dir() ; if(!c.cdUp()) break ; lastDir = QFileInfo(c.path()); } while (true); //(lastDir.absoluteFilePath() != lastDir.absolutePath()); } } /** * @brief RsCollectionDialog::updateSizes: Update Column size of ui._fileEntriesTW) */ void RsCollectionDialog::updateSizes() { ui._selectedFiles_TL->setText(QString::number(getRootItem()->data(COLUMN_FILEC,ROLE_SELFILEC).toULongLong())) ; ui._totalSize_TL->setText(misc::friendlyUnit(getRootItem()->data(COLUMN_SIZE,ROLE_SELSIZE).toULongLong())) ; } /** * @brief purifyFileName: Check if name of file is good or not. * @param input * @param bad * @return */ static QString purifyFileName(const QString& input, bool& bad) { static const QString bad_chars = "/\\\"*:?<>|" ; bad = false ; QString output = input ; for(int i=0;i dirToAdd; int count=0;//to not scan all items on list .count() QModelIndexList milSelectionList = ui._systemFileTW->selectionModel()->selectedIndexes(); foreach (QModelIndex index, milSelectionList) { if (index.column()==0){//Get only FileName QString filePath = _dirModel->filePath(_tree_proxyModel->mapToSource(index)); QFileInfo fileInfo = filePath; if (fileInfo.isDir()) { dirToAdd.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); ++count; if (recursive) { if (!addAllChild(fileInfo, dirToAdd, fileToHash, count)) return; } else { continue; } } if (fileInfo.isFile()){ fileToHash.append(fileInfo.absoluteFilePath()); ++count; if (dirToAdd.contains(fileInfo.absolutePath())) _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); else _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),""); } } } // Process Dirs QTreeWidgetItem *item = NULL; if (!ui._fileEntriesTW->selectedItems().empty()) item= ui._fileEntriesTW->selectedItems().at(0); if (item) { while (item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_DIR) { item = item->parent();//Only Dir as Parent } } int index = 0; while (index < dirToAdd.count()) { ColFileInfo root; if (item && (item != getRootItem())) { root.name = ""; root.path = item->text(COLUMN_FILEPATH); } else { root.name = ""; root.path = ""; } //QMap is ordered, so we get parent before child //Iterator is moved inside this function processItem(dirToAdd, index, root); } //Update liste before attach files to be sure when file is hashed, parent directory exists. updateList(); for (QHash::Iterator it = _listOfFilesAddedInDir.begin(); it != _listOfFilesAddedInDir.end() ; ++it) { QString path = it.value(); //it.value() = "";//Don't reset value, could be an older attachment not terminated. if (dirToAdd.contains(path)){ it.value() = dirToAdd.value(path); } else if(item) { if (item->data(COLUMN_HASH, ROLE_NAME) != "") { it.value() = item->text(COLUMN_FILEPATH); } } } // Process Files once all done ui._hashBox->addAttachments(fileToHash,RS_FILE_REQ_ANONYMOUS_ROUTING /*, 0*/); } /** * @brief RsCollectionDialog::addAllChild: Add children to RsCollection * @param fileInfoParent: Parent's QFileInfo to scan * @param dirToAdd: QMap where directories are added * @param fileToHash: QStringList where files are added * @return false if too many items is selected and aborted */ bool RsCollectionDialog::addAllChild(QFileInfo &fileInfoParent , QMap &dirToAdd , QStringList &fileToHash , int &count) { //Save count only first time to not scan all items on list .count() if (count == 0) count = (dirToAdd.count() + fileToHash.count()); QDir dirParent = fileInfoParent.absoluteFilePath(); dirParent.setFilter(QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot); QFileInfoList childrenList = dirParent.entryInfoList(); foreach (QFileInfo fileInfo, childrenList) { if (count == MAX_FILE_ADDED_BEFORE_ASK) { QMessageBox msgBox; msgBox.setText(tr("Warning, selection contains more than %1 items.").arg(MAX_FILE_ADDED_BEFORE_ASK)); msgBox.setInformativeText("Do you want to continue?"); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); int ret = msgBox.exec(); switch (ret) { case QMessageBox::Yes: break; case QMessageBox::No: return false; break; break; default: // should never be reached break; } } if (fileInfo.isDir()) { dirToAdd.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); ++count; if (!addAllChild(fileInfo, dirToAdd, fileToHash, count)) return false; } if (fileInfo.isFile()){ fileToHash.append(fileInfo.absoluteFilePath()); ++count; if (dirToAdd.contains(fileInfo.absolutePath())) _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),fileInfo.absolutePath()); else _listOfFilesAddedInDir.insert(fileInfo.absoluteFilePath(),""); } } return true; } /** * @brief RsCollectionDialog::remove: Remove selected Items in RSCollection */ void RsCollectionDialog::remove() { bool removeOnlyFile=false; QString listDir; // First, check if selection contains directories for (int curs = 0; curs < ui._fileEntriesTW->selectedItems().count(); ++curs) {// Have to call ui._fileEntriesTW->selectedItems().count() each time as selected could change QTreeWidgetItem *item = NULL; item= ui._fileEntriesTW->selectedItems().at(curs); //Uncheck child directory item if parent is checked if (item != getRootItem()){ if (item->data(COLUMN_HASH, ROLE_TYPE).toUInt() == DIR_TYPE_DIR) { QString path = item->data(COLUMN_HASH, ROLE_PATH).toString(); if (listDir.contains(path) && !path.isEmpty()) { item->setSelected(false); } else { listDir += item->data(COLUMN_HASH, ROLE_NAME).toString() +"
"; } } } } //If directories, ask to remove them or not if (!listDir.isEmpty()){ QMessageBox* msgBox = new QMessageBox(QMessageBox::Information, "", ""); msgBox->setText("Warning, selection contains directories."); //msgBox->setInformativeText(); If text too long, no scroll, so I add an text edit QGridLayout* layout = qobject_cast(msgBox->layout()); if (layout) { int newRow = 1; for (int row = layout->count()-1; row >= 0; --row) { for (int col = layout->columnCount()-1; col >= 0; --col) { QLayoutItem *item = layout->itemAtPosition(row, col); if (item) { int index = layout->indexOf(item->widget()); int r=0, c=0, rSpan=0, cSpan=0; layout->getItemPosition(index, &r, &c, &rSpan, &cSpan); if (r>0) { layout->removeItem(item); layout->addItem(item, r+3, c, rSpan, cSpan); } else if (rSpan>1) { newRow = rSpan + 1; } } } } QLabel *label = new QLabel(tr("Do you want to remove them and all their children, too?")); layout->addWidget(label,newRow, 0, 1, layout->columnCount(), Qt::AlignHCenter ); QTextEdit *edit = new QTextEdit(listDir); edit->setReadOnly(true); edit->setWordWrapMode(QTextOption::NoWrap); layout->addWidget(edit,newRow+1, 0, 1, layout->columnCount(), Qt::AlignHCenter ); } msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); msgBox->setDefaultButton(QMessageBox::Yes); int ret = msgBox->exec(); switch (ret) { case QMessageBox::Yes: break; case QMessageBox::No: removeOnlyFile = true; break; case QMessageBox::Cancel: { delete msgBox; return; } break; default: // should never be reached break; } delete msgBox; } //Remove wanted items int leftItem = 0; // Have to call ui._fileEntriesTW->selectedItems().count() each time as selected change while (ui._fileEntriesTW->selectedItems().count() > leftItem) { QTreeWidgetItem *item = ui._fileEntriesTW->selectedItems().at(leftItem); if (item != getRootItem()){ if (!removeItem(item, removeOnlyFile)) { ++leftItem; } } else { //Get Root change index ++leftItem; } } updateSizes() ; } bool RsCollectionDialog::removeItem(QTreeWidgetItem *item, bool &removeOnlyFile) { if (item){ if ((item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_DIR) || !removeOnlyFile) { int leftItem = 0; while (item->childCount() > leftItem) { if (!removeItem(item->child(0), removeOnlyFile)) { ++leftItem; } } if (leftItem == 0) { //First uncheck item to update parent informations item->setCheckState(COLUMN_FILE,Qt::Unchecked); QTreeWidgetItem *parent = item->parent(); if (parent) { parent->removeChild(item); } else { getRootItem()->removeChild(item); } return true; } else { if (!removeOnlyFile) { std::cerr << "(EE) RsCollectionDialog::removeItem This could never happen." << std::endl; } } } } return false; } /** Process each item to make a new RsCollection item */ void RsCollectionDialog::processItem(QMap &dirToAdd , int &index , ColFileInfo &parent ) { ColFileInfo newChild; int count = dirToAdd.count(); if (index < count) { QString key=dirToAdd.keys().at(index); bool bad_chars_detected = false; QFileInfo fileInfo=key; QString cleanDirName = purifyFileName(fileInfo.fileName(),bad_chars_detected); newChild.name = cleanDirName; newChild.filename_has_wrong_characters = bad_chars_detected; newChild.size = fileInfo.isDir()? 0: fileInfo.size(); newChild.type = fileInfo.isDir()? DIR_TYPE_DIR: DIR_TYPE_FILE ; if (parent.name != "") { newChild.path = parent.path + "/" + parent.name; } else { newChild.path = parent.path; } dirToAdd[key] = newChild.path + "/" + newChild.name; //Move to next item ++index; if (index < count){ QString newKey = dirToAdd.keys().at(index); while ((dirToAdd.value(newKey) == key) && (index < count)) { processItem(dirToAdd, index, newChild); if (index < count)newKey = dirToAdd.keys().at(index); } } //Save parent when child are processed if (parent.name != "") { parent.children.push_back(newChild); parent.size += newChild.size; } else { _newColFileInfos.push_back(newChild); } } } /** * @brief RsCollectionDialog::addDir: Add new empty dir to list */ void RsCollectionDialog::makeDir() { QString childName=""; bool ok, badChar, nameOK = false; // Ask for name while (!nameOK) { childName = QInputDialog::getText(this, tr("New Directory") , tr("Enter the new directory's name") , QLineEdit::Normal, childName, &ok); if (ok && !childName.isEmpty()) { childName = purifyFileName(childName, badChar); nameOK = !badChar; if (badChar) { QMessageBox msgBox; msgBox.setText("The name contains bad characters."); msgBox.setInformativeText("Do you want to use the corrected one?\n" + childName); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Retry | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); int ret = msgBox.exec(); switch (ret) { case QMessageBox::Ok: nameOK = true; break; case QMessageBox::Retry: break; case QMessageBox::Cancel: return; break; default: // should never be reached break; } } } else {//if (ok && !childName.isEmpty()) return; } } QList selected = ui._fileEntriesTW->selectedItems(); if(selected.empty()) { QTreeWidgetItem *item = getRootItem(); ColFileInfo newChild; newChild.name = childName; newChild.filename_has_wrong_characters = false; newChild.size = 0; newChild.type = DIR_TYPE_DIR; newChild.path = item->data(COLUMN_HASH, ROLE_PATH).toString() + "/" + item->data(COLUMN_HASH, ROLE_NAME).toString(); if (item == getRootItem()) newChild.path = ""; _newColFileInfos.push_back(newChild); } else for(auto it(selected.begin());it!=selected.end();++it) { QTreeWidgetItem *item = *it; while(item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_DIR) item = item->parent();//Only Dir as Parent ColFileInfo newChild; newChild.name = childName; newChild.filename_has_wrong_characters = false; newChild.size = 0; newChild.type = DIR_TYPE_DIR; if (item == getRootItem()) newChild.path = ""; else newChild.path = item->data(COLUMN_HASH, ROLE_PATH).toString() + "/" + item->data(COLUMN_HASH, ROLE_NAME).toString(); _newColFileInfos.push_back(newChild); } // // Process all selected items // int count = ui._fileEntriesTW->selectedItems().count(); // int curs = 0; // if (count == 0) curs = -1; // // for (; curs < count; ++curs) // { // QTreeWidgetItem *item = NULL; // if (curs >= 0) { // item= ui._fileEntriesTW->selectedItems().at(curs); // } else { // item = getRootItem(); // } // if (item) { // while (item->parent() != NULL && item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_DIR) { // item = item->parent();//Only Dir as Parent // } // ColFileInfo newChild; // newChild.name = childName; // newChild.filename_has_wrong_characters = false; // newChild.size = 0; // newChild.type = DIR_TYPE_DIR; // newChild.path = item->data(COLUMN_HASH, ROLE_PATH).toString() // + "/" + item->data(COLUMN_HASH, ROLE_NAME).toString(); // if (item == getRootItem()) newChild.path = ""; // // _newColFileInfos.push_back(newChild); // } // } updateList(); } /** * @brief RsCollectionDialog::fileHashingFinished: Connected to ui._hashBox.fileHashingFinished * Add finished File to collection in respective directory * @param hashedFiles: List of the file finished */ void RsCollectionDialog::fileHashingFinished(QList hashedFiles) { std::cerr << "RsCollectionDialog::fileHashingFinished() started." << std::endl; QString message; QList::iterator it; for (it = hashedFiles.begin(); it != hashedFiles.end(); ++it) { HashedFile& hashedFile = *it; ColFileInfo colFileInfo; colFileInfo.name=hashedFile.filename; colFileInfo.path=""; colFileInfo.size=hashedFile.size; colFileInfo.hash=QString::fromStdString(hashedFile.hash.toStdString()); colFileInfo.filename_has_wrong_characters=false; colFileInfo.type=DIR_TYPE_FILE; if(_listOfFilesAddedInDir.value(hashedFile.filepath,"")!="") { //File Added in directory, find its parent colFileInfo.path = _listOfFilesAddedInDir.value(hashedFile.filepath,""); _listOfFilesAddedInDir.remove(hashedFile.filepath); } _newColFileInfos.push_back(colFileInfo); } std::cerr << "RsCollectionDialog::fileHashingFinished message : " << message.toStdString() << std::endl; updateList(); } void RsCollectionDialog::itemChanged(QTreeWidgetItem *item, int col) { if (col != COLUMN_FILE) return; if (item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_FILE) return; //In COLUMN_FILE, normaly, only checkState could change... qulonglong size = item->data(COLUMN_SIZE, ROLE_SIZE).toULongLong(); bool unchecked = (item->checkState(COLUMN_FILE) == Qt::Unchecked); item->setData(COLUMN_SIZE, ROLE_SELSIZE, unchecked?0:size); item->setText(COLUMN_SIZE, misc::friendlyUnit(unchecked?0:size)); item->setData(COLUMN_FILEC, ROLE_SELFILEC, unchecked?0:1); item->setText(COLUMN_FILEC, QString("%1").arg(unchecked?0:1)); //update parents size QTreeWidgetItem *itemParent = item->parent(); while (itemParent) { //When unchecked only remove selected size qulonglong parentSize = itemParent->data(COLUMN_SIZE, ROLE_SELSIZE).toULongLong() + (unchecked?0-size:size); itemParent->setData(COLUMN_SIZE, ROLE_SELSIZE, parentSize); itemParent->setText(COLUMN_SIZE, misc::friendlyUnit(parentSize)); qulonglong parentFileCount = itemParent->data(COLUMN_FILEC, ROLE_SELFILEC).toULongLong() + (unchecked?0-1:1); itemParent->setData(COLUMN_FILEC, ROLE_SELFILEC, parentFileCount); itemParent->setText(COLUMN_FILEC, QString("%1").arg(parentFileCount)); itemParent = itemParent->parent(); } updateSizes() ; } /** * @brief RsCollectionDialog::updateRemoveDuplicate Remove all duplicate file when checked. * @param checked */ void RsCollectionDialog::updateRemoveDuplicate(bool checked) { if (checked) { bool bRemoveAll = false; QTreeWidgetItemIterator it(ui._fileEntriesTW); QTreeWidgetItem *item; while ((item = *it) != NULL) { ++it; if (item->data(COLUMN_HASH, ROLE_TYPE).toUInt() != DIR_TYPE_DIR) { QList founds; founds << ui._fileEntriesTW->findItems(item->text(COLUMN_HASH), Qt::MatchExactly | Qt::MatchRecursive, COLUMN_HASH); if (founds.count() > 1) { bool bRemove = false; if (!bRemoveAll) { QMessageBox* msgBox = new QMessageBox(QMessageBox::Information, "", ""); msgBox->setText("Warning, duplicate file found."); //msgBox->setInformativeText(); If text too long, no scroll, so I add an text edit msgBox->setStandardButtons(QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); msgBox->setDefaultButton(QMessageBox::Yes); QGridLayout* layout = qobject_cast(msgBox->layout()); if (layout) { int newRow = 1; for (int row = layout->count()-1; row >= 0; --row) { for (int col = layout->columnCount()-1; col >= 0; --col) { QLayoutItem *item = layout->itemAtPosition(row, col); if (item) { int index = layout->indexOf(item->widget()); int r=0, c=0, rSpan=0, cSpan=0; layout->getItemPosition(index, &r, &c, &rSpan, &cSpan); if (r>0) { layout->removeItem(item); layout->addItem(item, r+3, c, rSpan, cSpan); } else if (rSpan>1) { newRow = rSpan + 1; } } } } QLabel *label = new QLabel(tr("Do you want to remove this file from the list?")); layout->addWidget(label,newRow, 0, 1, layout->columnCount(), Qt::AlignHCenter ); QTextEdit *edit = new QTextEdit(item->text(COLUMN_FILEPATH)); edit->setReadOnly(true); edit->setWordWrapMode(QTextOption::NoWrap); layout->addWidget(edit,newRow+1, 0, 1, layout->columnCount(), Qt::AlignHCenter ); } int ret = msgBox->exec(); switch (ret) { case QMessageBox::YesToAll: { bRemoveAll = true; } break; case QMessageBox::Yes: { bRemove = true; } break; case QMessageBox::No: break; case QMessageBox::Cancel: { delete msgBox; ui._removeDuplicate_CB->setChecked(false); return; } break; default: // should never be reached break; } delete msgBox; } if (bRemove || bRemoveAll) { //First uncheck item to update parent informations item->setCheckState(COLUMN_FILE,Qt::Unchecked); item->parent()->removeChild(item); } } } } } } /** * @brief RsCollectionDialog::cancel: Cancel RScollection editing or donwloading */ void RsCollectionDialog::cancel() { std::cerr << "Canceling!" << std::endl; close() ; } /** * @brief RsCollectionDialog::download: Start downloading checked items */ void RsCollectionDialog::download() { std::cerr << "Downloading!" << std::endl; QString dldir = ui.downloadFolder_LE->text(); std::cerr << "downloading all these files:" << std::endl; QTreeWidgetItemIterator itemIterator(ui._fileEntriesTW); QTreeWidgetItem *item; while ((item = *itemIterator) != NULL) { ++itemIterator; if (item->checkState(COLUMN_FILE) == Qt::Checked) { std::cerr << item->data(COLUMN_HASH,ROLE_NAME).toString().toStdString() << " " << item->text(COLUMN_HASH).toStdString() << " " << item->text(COLUMN_SIZE).toStdString() << " " << item->data(COLUMN_HASH,ROLE_PATH).toString().toStdString() << std::endl; ColFileInfo colFileInfo; colFileInfo.hash = item->text(COLUMN_HASH); colFileInfo.name = item->data(COLUMN_HASH,ROLE_NAME).toString(); colFileInfo.path = item->data(COLUMN_HASH,ROLE_PATH).toString(); colFileInfo.type = item->data(COLUMN_HASH,ROLE_TYPE).toUInt(); colFileInfo.size = item->data(COLUMN_SIZE,ROLE_SELSIZE).toULongLong(); QString cleanPath = dldir + colFileInfo.path ; std::cerr << "making directory " << cleanPath.toStdString() << std::endl; if(!QDir(QApplication::applicationDirPath()).mkpath(cleanPath)) QMessageBox::warning(NULL,QObject::tr("Unable to make path"),QObject::tr("Unable to make path:")+"
"+cleanPath) ; if (colFileInfo.type==DIR_TYPE_FILE) rsFiles->FileRequest(colFileInfo.name.toUtf8().constData(), RsFileHash(colFileInfo.hash.toStdString()), colFileInfo.size, cleanPath.toUtf8().constData(), RS_FILE_REQ_ANONYMOUS_ROUTING, std::list()); } else {//if (item->checkState(COLUMN_FILE) == Qt::Checked) std::cerr<<"Skipping file : " << item->data(COLUMN_HASH,ROLE_NAME).toString().toStdString() << std::endl; } } close(); } /** * @brief RsCollectionDialog::save: Update collection to save it in caller. */ void RsCollectionDialog::save() { std::cerr << "Saving!" << std::endl; _newColFileInfos.clear(); QTreeWidgetItem* root = getRootItem(); if (root) { saveChild(root); emit saveColl(_newColFileInfos, _fileName); } close(); } /** * @brief RsCollectionDialog::saveChild: Save each child in _newColFileInfos * @param parent */ void RsCollectionDialog::saveChild(QTreeWidgetItem *parentItem, ColFileInfo *parentInfo) { ColFileInfo parent; if (!parentInfo) parentInfo = &parent; parentInfo->checked = (parentItem->checkState(COLUMN_FILE)==Qt::Checked); parentInfo->hash = parentItem->text(COLUMN_HASH); parentInfo->name = parentItem->data(COLUMN_HASH,ROLE_NAME).toString(); parentInfo->path = parentItem->data(COLUMN_HASH,ROLE_PATH).toString(); parentInfo->type = parentItem->data(COLUMN_HASH,ROLE_TYPE).toUInt(); parentInfo->size = parentItem->data(COLUMN_SIZE,ROLE_SELSIZE).toULongLong(); for (int i=0; ichildCount(); ++i) { ColFileInfo child; saveChild(parentItem->child(i), &child); if (parentInfo->name != "") { parentInfo->children.push_back(child); } else { _newColFileInfos.push_back(child); } } }