diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 2fce386ae..d56df8834 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -36,6 +36,8 @@ #include "gui/settings/rsharesettings.h" #include "util/QtVersion.h" #include "util/RsAction.h" +#include "util/misc.h" +#include "util/rstime.h" #include #include @@ -167,17 +169,24 @@ SharedFilesDialog::SharedFilesDialog(RetroshareDirModel *_tree_model,RetroshareD flat_model = _flat_model ; connect(flat_model, SIGNAL(layoutChanged()), this, SLOT(updateDirTreeView()) ); + // For filtering items we use a trick: the underlying model will use this FilterRole role to highlight selected items + // while the filterProxyModel will select them using the pre-chosen string "filtered". + tree_proxyModel = new SFDSortFilterProxyModel(tree_model, this); tree_proxyModel->setSourceModel(tree_model); tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); tree_proxyModel->setSortRole(RetroshareDirModel::SortRole); tree_proxyModel->sort(COLUMN_NAME); + tree_proxyModel->setFilterRole(RetroshareDirModel::FilterRole); + tree_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ; flat_proxyModel = new SFDSortFilterProxyModel(flat_model, this); flat_proxyModel->setSourceModel(flat_model); flat_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); flat_proxyModel->setSortRole(RetroshareDirModel::SortRole); flat_proxyModel->sort(COLUMN_NAME); + flat_proxyModel->setFilterRole(RetroshareDirModel::FilterRole); + flat_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ; // Mr.Alice: I removed this because it causes a crash for some obscur reason. Apparently when the model is changed, the proxy model cannot // deal with the change by itself. Should I call something specific? I've no idea. Removing this does not seem to cause any harm either. @@ -193,9 +202,9 @@ SharedFilesDialog::SharedFilesDialog(RetroshareDirModel *_tree_model,RetroshareD ui.filterClearButton->hide(); ui.filterStartButton->hide(); - mFilterTimer = new RsProtectedTimer( this ); - mFilterTimer->setSingleShot( true ); // Ensure the timer will fire only once after it was started - connect(mFilterTimer, SIGNAL(timeout()), this, SLOT(filterRegExpChanged())); +// mFilterTimer = new RsProtectedTimer( this ); +// mFilterTimer->setSingleShot( true ); // Ensure the timer will fire only once after it was started +// connect(mFilterTimer, SIGNAL(timeout()), this, SLOT(filterRegExpChanged())); /* Set header resize modes and initial section sizes */ QHeaderView * header = ui.dirTreeView->header () ; @@ -932,6 +941,48 @@ void SharedFilesDialog::restoreExpandedPathsAndSelection(const std::setblockSignals(false) ; } +void SharedFilesDialog::expandAll() +{ + if(ui.dirTreeView->model() == NULL) + return ; + + ui.dirTreeView->blockSignals(true) ; + +#ifdef DEBUG_SHARED_FILES_DIALOG + std::cerr << "Restoring expanded items. " << std::endl; +#endif + for(int row = 0; row < ui.dirTreeView->model()->rowCount(); ++row) + { + std::string path = ui.dirTreeView->model()->index(row,0).data(Qt::DisplayRole).toString().toStdString(); + recursExpandAll(ui.dirTreeView->model()->index(row,0)); + } + //QItemSelection selection ; + + ui.dirTreeView->blockSignals(false) ; + +} + +void SharedFilesDialog::recursExpandAll(const QModelIndex& index) +{ + ui.dirTreeView->setExpanded(index,true) ; + + for(int row=0;rowmodel()->rowCount(index);++row) + { + QModelIndex idx(index.child(row,0)) ; + + if(ui.dirTreeView->model()->rowCount(idx) > 0) + recursExpandAll(idx) ; + +// QModelIndex midx = proxyModel->mapToSource(idx) ; +// +// if (!midx.isValid()) +// continue ; +// +// if (model->getType(midx) != DIR_TYPE_FILE) +// recursExpandAll(idx) ; + } +} + void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const std::string& path,std::set& exp, std::set& vis, std::set& sel @@ -983,8 +1034,8 @@ void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, con bool invisible = vis.find(local_path) != vis.end(); ui.dirTreeView->setRowHidden(index.row(),index.parent(),invisible ) ; - if(invisible) - mHiddenIndexes.push_back(proxyModel->mapToSource(index)); +// if(invisible) +// mHiddenIndexes.push_back(proxyModel->mapToSource(index)); if(!invisible && exp.find(local_path) != exp.end()) { @@ -1287,6 +1338,7 @@ void SharedFilesDialog::onFilterTextEdited() #endif } +#ifdef DEPRECATED_CODE void SharedFilesDialog::filterRegExpChanged() { QString text = ui.filterPatternLineEdit->text(); @@ -1323,17 +1375,8 @@ void SharedFilesDialog::filterRegExpChanged() ui.filterStartButton->setEnabled(true) ; ui.filterPatternFrame->setToolTip(QString()); - - /* unpolish widget to clear the stylesheet's palette cache */ - // ui.filterPatternFrame->style()->unpolish(ui.filterPatternFrame); - - // QPalette palette = ui.filterPatternLineEdit->palette(); - // palette.setColor(ui.filterPatternLineEdit->backgroundRole(), color); - // ui.filterPatternLineEdit->setPalette(palette); - - // //ui.searchLineFrame->setProperty("valid", valid); - // Rshare::refreshStyleSheet(ui.filterPatternFrame, false); } +#endif /* clear Filter */ void SharedFilesDialog::clearFilter() @@ -1368,13 +1411,14 @@ void SharedFilesDialog::updateDirTreeView() ui.dirTreeView->setToolTip(""); } +//#define DEBUG_SHARED_FILES_DIALOG + +#ifdef DEPRECATED_CODE // This macro make the search expand all items that contain the searched text. // A bug however, makes RS expand everything when nothing is selected, which is a pain. #define EXPAND_WHILE_SEARCHING 1 -//#define DEBUG_SHARED_FILES_DIALOG - void recursMakeVisible(QTreeView *tree,const QSortFilterProxyModel *proxyModel,const QModelIndex& indx,uint32_t depth,const std::vector >& pointers,QList& hidden_list) { #ifdef DEBUG_SHARED_FILES_DIALOG @@ -1430,6 +1474,8 @@ void recursMakeVisible(QTreeView *tree,const QSortFilterProxyModel *proxyModel,c void SharedFilesDialog::restoreInvisibleItems() { + std::cerr << "Restoring " << mHiddenIndexes.size() << " invisible indexes" << std::endl; + for(QList::const_iterator it(mHiddenIndexes.begin());it!=mHiddenIndexes.end();++it) { QModelIndex indx = proxyModel->mapFromSource(*it); @@ -1440,6 +1486,7 @@ void SharedFilesDialog::restoreInvisibleItems() mHiddenIndexes.clear(); } +#endif class QCursorContextBlocker { @@ -1476,122 +1523,49 @@ void SharedFilesDialog::FilterItems() return ; } - std::cerr << "New last text. Performing the filter" << std::endl; + std::cerr << "New last text. Performing the filter on string \"" << text.toStdString() << "\"" << std::endl; mLastFilterText = text ; model->update() ; - restoreInvisibleItems(); QCursorContextBlocker q(ui.dirTreeView) ; - if(proxyModel == tree_proxyModel) + QCoreApplication::processEvents() ; + + std::list result_list ; + uint32_t found = 0 ; + + if(text == "") { - QCoreApplication::processEvents() ; - - std::list keywords ; - std::list result_list ; - - if(text == "") - return ; - - if(text.length() < 3) - return ; - - FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; - QStringList lst = text.split(" ",QString::SkipEmptyParts) ; - - for(auto it(lst.begin());it!=lst.end();++it) - keywords.push_back((*it).toStdString()); - - if(keywords.size() > 1) - { - RsRegularExpression::NameExpression exp(RsRegularExpression::ContainsAllStrings,keywords,true); - rsFiles->SearchBoolExp(&exp,result_list, flags) ; - } - else - rsFiles->SearchKeywords(keywords,result_list, flags) ; - -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "Found " << result_list.size() << " results" << std::endl; -#endif - - size_t resSize = result_list.size(); - if(resSize == 0) - { - ui.filterPatternFrame->setToolTip(tr("No result.")) ; - return ; - } - if(resSize > MAX_SEARCH_RESULTS) - { - ui.filterPatternFrame->setToolTip(tr("More than %1 results. Add more/longer search words to select less.").arg(MAX_SEARCH_RESULTS)) ; - return ; - } - ui.filterPatternFrame->setToolTip(tr("Found %1 results.").arg(resSize)) ; - -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "Found this result: " << std::endl; -#endif - std::vector > pointers(2,std::set()); // at least two levels need to be here. - - // Then show only the ones we need - for(auto it(result_list.begin());it!=result_list.end();++it) - { -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << (void*)(*it).ref << " parents: " ; -#endif - - DirDetails& det(*it) ; - void *p = NULL; - std::list lst ; - - lst.push_back(det.ref) ; - - while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_DIR) - { - p = det.parent ; - rsFiles->RequestDirDetails( p, det, flags); - -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << " " << (void*)p << "(" << (int)det.type << ")"; -#endif - - lst.push_front(p) ; - } - -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << std::endl; -#endif - - uint32_t u=0; - for(auto it2(lst.begin());it2!=lst.end();++it2,++u) - { - if(pointers.size() <= u) - pointers.resize(u+5) ; - - pointers[u].insert(*it2) ; - } - } - - int rowCount = ui.dirTreeView->model()->rowCount(); - for (int row = 0; row < rowCount; ++row) - recursMakeVisible(ui.dirTreeView,proxyModel,ui.dirTreeView->model()->index(row, COLUMN_NAME),0,pointers,mHiddenIndexes); + model->filterItems(std::list(),found) ; + return ; } + + if(text.length() < 3) + return ; + + FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; + QStringList lst = text.split(" ",QString::SkipEmptyParts) ; + std::list keywords ; + + for(auto it(lst.begin());it!=lst.end();++it) + keywords.push_back((*it).toStdString()); + + model->filterItems(keywords,found) ; + + if(found > 0) + expandAll(); + + if(found == 0) + ui.filterPatternFrame->setToolTip(tr("No result.")) ; + else if(found > MAX_SEARCH_RESULTS) + ui.filterPatternFrame->setToolTip(tr("More than %1 results. Add more/longer search words to select less.").arg(MAX_SEARCH_RESULTS)) ; else - { - int rowCount = ui.dirTreeView->model()->rowCount(); - for (int row = 0; row < rowCount; ++row) - flat_FilterItem(ui.dirTreeView->model()->index(row, COLUMN_NAME), text, 0); - } + ui.filterPatternFrame->setToolTip(tr("Found %1 results.").arg(found)) ; -#ifdef DEPRECATED_CODE - int rowCount = ui.dirTreeView->model()->rowCount(); - for (int row = 0; row < rowCount; ++row) - if(proxyModel == tree_proxyModel) - tree_FilterItem(ui.dirTreeView->model()->index(row, COLUMN_NAME), text, 0); - else - flat_FilterItem(ui.dirTreeView->model()->index(row, COLUMN_NAME), text, 0); -#endif + std::cerr << found << " results found by search." << std::endl; } +#ifdef DEPRECATED_CODE bool SharedFilesDialog::flat_FilterItem(const QModelIndex &index, const QString &text, int /*level*/) { if(index.data(RetroshareDirModel::FileNameRole).toString().contains(text, Qt::CaseInsensitive)) @@ -1641,3 +1615,4 @@ bool SharedFilesDialog::tree_FilterItem(const QModelIndex &index, const QString return (visible || visibleChildCount); } +#endif diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index 63a57683c..0d35260ee 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -77,7 +77,7 @@ private slots: void indicatorChanged(int index); void onFilterTextEdited(); - void filterRegExpChanged(); + //void filterRegExpChanged(); void clearFilter(); void startFilter(); @@ -97,6 +97,8 @@ protected: void recursSaveExpandedItems(const QModelIndex& index, const std::string &path, std::set &exp,std::set& vis, std::set& sel); void saveExpandedPathsAndSelection(std::set& paths,std::set& visible_indexes, std::set& selected_indexes) ; void restoreExpandedPathsAndSelection(const std::set& paths,const std::set& visible_indexes, const std::set& selected_indexes) ; + void recursExpandAll(const QModelIndex& index); + void expandAll(); protected: //now context menu are created again every time theu are called ( in some @@ -113,8 +115,6 @@ protected: bool tree_FilterItem(const QModelIndex &index, const QString &text, int level); bool flat_FilterItem(const QModelIndex &index, const QString &text, int level); - void restoreInvisibleItems(); - QModelIndexList getSelected(); /** Defines the actions for the context menu for QTreeWidget */ @@ -142,8 +142,6 @@ protected: QString lastFilterString; QString mLastFilterText ; RsProtectedTimer* mFilterTimer; - - QList mHiddenIndexes; }; class LocalSharedFilesDialog : public SharedFilesDialog diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index f6d0df88f..5bb189486 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -30,6 +30,7 @@ #include "retroshare/rsfiles.h" #include "retroshare/rspeers.h" #include "util/misc.h" +#include "retroshare/rsexpr.h" #include #include @@ -44,6 +45,7 @@ /***** * #define RDM_DEBUG ****/ +#define RDM_SEARCH_DEBUG static const uint32_t FLAT_VIEW_MAX_REFS_PER_SECOND = 2000 ; static const size_t FLAT_VIEW_MAX_REFS_TABLE_SIZE = 10000 ; // @@ -170,7 +172,7 @@ bool TreeStyle_RDM::hasChildren(const QModelIndex &parent) const } /* PERSON/DIR*/ #ifdef RDM_DEBUG - std::cerr << "lookup PER/DIR #" << details->count; + std::cerr << "lookup PER/DIR #" << details.count; std::cerr << std::endl; #endif return (details.count > 0); /* do we have children? */ @@ -229,7 +231,7 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const /* else PERSON/DIR*/ #ifdef RDM_DEBUG - std::cerr << "lookup PER/DIR #" << details->count; + std::cerr << "lookup PER/DIR #" << details.count; std::cerr << std::endl; #endif if ((details.type == DIR_TYPE_ROOT) && !_showEmpty && RemoteMode) @@ -367,11 +369,19 @@ const QIcon& RetroshareDirModel::getFlagsIcon(FileStorageFlags flags) static_icons[n] = new QIcon(pix); - std::cerr << "Generated icon for flags " << std::hex << flags << std::endl; + std::cerr << "Generated icon for flags " << std::hex << flags << std::dec << std::endl; } return *static_icons[n] ; } +QVariant RetroshareDirModel::filterRole(const DirDetails& details,int coln) const +{ + if(mFilteredPointers.empty() || mFilteredPointers.find(details.ref) != mFilteredPointers.end()) + return QString(RETROSHARE_DIR_MODEL_FILTER_STRING); + else + return QVariant(); +} + QVariant RetroshareDirModel::decorationRole(const DirDetails& details,int coln) const { if(coln == COLUMN_FRIEND_ACCESS) @@ -803,9 +813,14 @@ QVariant RetroshareDirModel::data(const QModelIndex &index, int role) const if (role == SortRole) return sortRole(index,details,coln) ; + if (role == FilterRole) + return filterRole(details,coln) ; + return QVariant(); } + + /**** //void RetroshareDirModel::getAgeIndicatorRec(const DirDetails &details, QString &ret) const { // if (details.type == DIR_TYPE_FILE) { @@ -1026,7 +1041,7 @@ QModelIndex TreeStyle_RDM::parent( const QModelIndex & index ) const } #ifdef RDM_DEBUG - std::cerr << "success index(" << details->prow << ",0," << details->parent << ")"; + std::cerr << "success index(" << details.prow << ",0," << details.parent << ")"; std::cerr << std::endl; std::cerr << "Creating index 3 row=" << details.prow << ", column=" << 0 << ", ref=" << (void*)details.parent << std::endl; @@ -1339,10 +1354,10 @@ void RetroshareDirModel::getFileInfoFromIndexList(const QModelIndexList& list, s #ifdef RDM_DEBUG std::cerr << "::::::::::::FileRecommend:::: " << std::endl; - std::cerr << "Name: " << details->name << std::endl; - std::cerr << "Hash: " << details->hash << std::endl; - std::cerr << "Size: " << details->count << std::endl; - std::cerr << "Path: " << details->path << std::endl; + std::cerr << "Name: " << details.name << std::endl; + std::cerr << "Hash: " << details.hash << std::endl; + std::cerr << "Size: " << details.count << std::endl; + std::cerr << "Path: " << details.path << std::endl; #endif // Note: for directories, the returned hash, is the peer id, so if we collect // dirs, we need to be a bit more conservative for the @@ -1449,6 +1464,70 @@ void RetroshareDirModel::getFilePaths(const QModelIndexList &list, std::list& keywords,uint32_t& found) +{ + FileSearchFlags flags = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; + + std::list result_list ; + found = 0 ; + + if(keywords.empty()) + { + mFilteredPointers.clear(); + return ; + } + else if(keywords.size() > 1) + { + RsRegularExpression::NameExpression exp(RsRegularExpression::ContainsAllStrings,keywords,true); + rsFiles->SearchBoolExp(&exp,result_list, flags) ; + } + else + rsFiles->SearchKeywords(keywords,result_list, flags) ; + +#ifdef RDM_SEARCH_DEBUG + std::cerr << "Found " << result_list.size() << " results" << std::endl; +#endif + + if(result_list.empty()) // in this case we dont clear the list of filtered items, so that we can keep the old filter list + return ; + + mFilteredPointers.clear(); + +#ifdef RDM_SEARCH_DEBUG + std::cerr << "Found this result: " << std::endl; +#endif + + // Then show only the ones we need + + for(auto it(result_list.begin());it!=result_list.end();++it) + { + DirDetails& det(*it) ; +#ifdef RDM_SEARCH_DEBUG + std::cerr << (void*)(*it).ref << " name=\"" << det.name << "\" parents: " ; +#endif + void *p = det.ref ; + mFilteredPointers.insert(p) ; + ++found ; + + while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_DIR) + { + p = det.parent ; + rsFiles->RequestDirDetails( p, det, flags); + +#ifdef RDM_SEARCH_DEBUG + std::cerr << " " << (void*)p << "(" << (int)det.type << ")"; +#endif + mFilteredPointers.insert(p) ; + } + +#ifdef RDM_SEARCH_DEBUG + std::cerr << std::endl; +#endif + } + std::cerr << mFilteredPointers.size() << " pointers in filter set." << std::endl; +} + + /* Drag and Drop Functionality */ QMimeData * RetroshareDirModel::mimeData ( const QModelIndexList & indexes ) const { @@ -1468,10 +1547,10 @@ QMimeData * RetroshareDirModel::mimeData ( const QModelIndexList & indexes ) con #ifdef RDM_DEBUG std::cerr << "::::::::::::FileDrag:::: " << std::endl; - std::cerr << "Name: " << details->name << std::endl; - std::cerr << "Hash: " << details->hash << std::endl; - std::cerr << "Size: " << details->count << std::endl; - std::cerr << "Path: " << details->path << std::endl; + std::cerr << "Name: " << details.name << std::endl; + std::cerr << "Hash: " << details.hash << std::endl; + std::cerr << "Size: " << details.count << std::endl; + std::cerr << "Path: " << details.path << std::endl; #endif if (details.type != DIR_TYPE_FILE) diff --git a/retroshare-gui/src/gui/RemoteDirModel.h b/retroshare-gui/src/gui/RemoteDirModel.h index 6b4fe8967..b7ff971dc 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.h +++ b/retroshare-gui/src/gui/RemoteDirModel.h @@ -39,6 +39,7 @@ #define COLUMN_FRIEND_ACCESS 4 #define COLUMN_WN_VISU_DIR 5 #define COLUMN_COUNT 6 +#define RETROSHARE_DIR_MODEL_FILTER_STRING "filtered" class DirDetails; @@ -60,7 +61,7 @@ class RetroshareDirModel : public QAbstractItemModel Q_OBJECT public: - enum Roles{ FileNameRole = Qt::UserRole+1, SortRole = Qt::UserRole+2 }; + enum Roles{ FileNameRole = Qt::UserRole+1, SortRole = Qt::UserRole+2, FilterRole = Qt::UserRole+3 }; RetroshareDirModel(bool mode, QObject *parent = 0); virtual ~RetroshareDirModel() {} @@ -94,7 +95,8 @@ class RetroshareDirModel : public QAbstractItemModel virtual QMenu* getContextMenu(QMenu* contextMenu) {return contextMenu;} - public: + void filterItems(const std::list& keywords, uint32_t& found) ; + //Overloaded from QAbstractItemModel virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QStringList mimeTypes () const; @@ -118,6 +120,7 @@ class RetroshareDirModel : public QAbstractItemModel virtual QVariant sortRole(const QModelIndex&,const DirDetails&,int) const =0; QVariant decorationRole(const DirDetails&,int) const ; + QVariant filterRole(const DirDetails& details,int coln) const; uint32_t ageIndicator; @@ -172,6 +175,8 @@ class RetroshareDirModel : public QAbstractItemModel mutable time_t mLastReq; bool mUpdating ; + + std::set mFilteredPointers ; }; // This class shows the classical hierarchical directory view of shared files