From a826a8651cd489cfd6ff39528781398eaf162b2f Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 23 Feb 2025 14:50:48 +0100 Subject: [PATCH] added filtering --- retroshare-gui/src/gui/Identity/IdDialog.cpp | 179 +++++++++++++++--- retroshare-gui/src/gui/Identity/IdDialog.h | 7 + .../src/gui/Identity/IdentityListModel.cpp | 2 +- 3 files changed, 157 insertions(+), 31 deletions(-) diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 5327bdbfb..3081e8424 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -146,6 +146,59 @@ class TreeWidgetItem : public QTreeWidgetItem }; #endif +std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere + +class IdListSortFilterProxyModel: public QSortFilterProxyModel +{ +public: + explicit IdListSortFilterProxyModel(const QHeaderView *header,QObject *parent = NULL) + : QSortFilterProxyModel(parent) + , m_header(header) + , m_sortingEnabled(false), m_sortByState(false) + { + setDynamicSortFilter(false); // causes crashes when true. + } + + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override + { +// bool online1 = (left .data(RsFriendListModel::OnlineRole).toInt() != RS_STATUS_OFFLINE); +// bool online2 = (right.data(RsFriendListModel::OnlineRole).toInt() != RS_STATUS_OFFLINE); +// +// if((online1 != online2) && m_sortByState) +// return (m_header->sortIndicatorOrder()==Qt::AscendingOrder)?online1:online2 ; // always put online nodes first + +#ifdef DEBUG_NEW_FRIEND_LIST + std::cerr << "Comparing index " << left << " with index " << right << std::endl; +#endif + return QSortFilterProxyModel::lessThan(left,right); + } + + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override + { + // do not show empty groups + + QModelIndex index = sourceModel()->index(source_row,0,source_parent); + + return index.data(RsIdentityListModel::FilterRole).toString() == RsIdentityListModel::FilterString ; + } + + void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ) override + { + if(m_sortingEnabled) + return QSortFilterProxyModel::sort(column,order) ; + } + + void setSortingEnabled(bool b) { m_sortingEnabled = b ; } + void setSortByState(bool b) { m_sortByState = b ; } + bool sortByState() const { return m_sortByState ; } + +private: + const QHeaderView *m_header ; + bool m_sortingEnabled; + bool m_sortByState; +}; + + /** Constructor */ IdDialog::IdDialog(QWidget *parent) : MainPage(parent) @@ -169,7 +222,15 @@ IdDialog::IdDialog(QWidget *parent) mIdListModel = new RsIdentityListModel(this); - ui->idTreeWidget->setModel(mIdListModel); + mProxyModel = new IdListSortFilterProxyModel(ui->idTreeWidget->header(),this); + + mProxyModel->setSourceModel(mIdListModel); + mProxyModel->setSortRole(RsIdentityListModel::SortRole); + mProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + mProxyModel->setFilterRole(RsIdentityListModel::FilterRole); + mProxyModel->setFilterRegExp(QRegExp(RsIdentityListModel::FilterString)); + + ui->idTreeWidget->setModel(mProxyModel); ui->treeWidget_membership->clear(); ui->treeWidget_membership->setItemDelegateForColumn(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,new GxsIdTreeItemDelegate()); @@ -1206,9 +1267,18 @@ IdDialog::~IdDialog() // save settings processSettings(false); + delete mIdListModel; + delete mProxyModel; delete(ui); } - +void IdDialog::idListItemExpanded(const QModelIndex& index) +{ + mIdListModel->expandItem(mProxyModel->mapToSource(index)); +} +void IdDialog::idListItemCollapsed(const QModelIndex& index) +{ + mIdListModel->collapseItem(mProxyModel->mapToSource(index)); +} static QString getHumanReadableDuration(uint32_t seconds) { if(seconds < 60) @@ -1299,14 +1369,7 @@ void IdDialog::updateIdList() std::cerr << "Updating identity list in widget: stack is:" << std::endl; //print_stacktrace(); - std::set expanded_indices; - std::set > selected_indices; - - saveExpandedPathsAndSelection_idTreeView(expanded_indices, selected_indices); - - mIdListModel->updateIdentityList(); - - restoreExpandedPathsAndSelection_idTreeView(expanded_indices, selected_indices); + applyWhileKeepingTree( [this]() { mIdListModel->updateIdentityList(); }); } #ifdef TO_REMOVE bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, const RsPgpId &ownPgpId, int accept) @@ -1595,12 +1658,8 @@ void IdDialog::updateIdentity() std::set expanded_indexes; std::set > selected_indices; - //saveExpandedPathsAndSelection_idTreeView(expanded_indexes, selected_indices); - loadIdentity(group); - //restoreExpandedPathsAndSelection_idTreeView(expanded_indexes, selected_indices); - }, this ); }); } @@ -2016,7 +2075,7 @@ std::list IdDialog::getSelectedIdentities() const RsGxsId id; if(indx.column() == RsIdentityListModel::COLUMN_THREAD_NAME) // this removes duplicates - if( !(id = mIdListModel->getIdentity(indx)).isNull() ) + if( !(id = mIdListModel->getIdentity(mProxyModel->mapToSource(indx))).isNull() ) res.push_back(id); } @@ -2553,23 +2612,24 @@ void IdDialog::saveExpandedPathsAndSelection_idTreeView(std::set& expan QString s ; if(t==RsIdentityListModel::ENTRY_TYPE_CATEGORY) - s = QString::number(mIdListModel->getCategory(m)); + s = QString::number(mIdListModel->getCategory(mProxyModel->mapToSource(m))); else - s = QString::fromStdString(mIdListModel->getIdentity(m).toStdString()); + s = QString::fromStdString(mIdListModel->getIdentity(mProxyModel->mapToSource(m)).toStdString()); selected_indices.insert(std::make_pair(t,s)); std::cerr << "added " << s.toStdString() << " to selection save" << std::endl; } - for(int row = 0; row < mIdListModel->rowCount(); ++row) + for(int row = 0; row < mProxyModel->rowCount(); ++row) { - auto m = mIdListModel->index(row,0,QModelIndex()); + auto m = mProxyModel->index(row,0); if(ui->idTreeWidget->isExpanded( m )) { - expanded_indexes.insert( QString::number(mIdListModel->getCategory(m))); - std::cerr << "added " << QString::number(mIdListModel->getCategory(m)).toStdString() << " to expanded save" << std::endl; + auto str = QString::number(mIdListModel->getCategory(mProxyModel->mapToSource(m))); + expanded_indexes.insert( str ); + std::cerr << "added " << m << " cat " << str.toStdString() << " to expanded save" << std::endl; } } @@ -2585,19 +2645,17 @@ void IdDialog::restoreExpandedPathsAndSelection_idTreeView(const std::setidTreeWidget->blockSignals(true) ; ui->idTreeWidget->selectionModel()->blockSignals(true); - ui->idTreeWidget->selectionModel()->clear(); - for(auto it:selected_indices) { if(it.first==RsIdentityListModel::ENTRY_TYPE_CATEGORY) - ui->idTreeWidget->selectionModel()->select(mIdListModel->index(it.first,0,QModelIndex()),QItemSelectionModel::Select | QItemSelectionModel::Rows); + ui->idTreeWidget->selectionModel()->select(mProxyModel->mapFromSource(mIdListModel->index(it.first,0,QModelIndex())),QItemSelectionModel::Select | QItemSelectionModel::Rows); else if(it.first==RsIdentityListModel::ENTRY_TYPE_IDENTITY) { - auto indx = mIdListModel->getIndexOfIdentity(RsGxsId(it.second.toStdString())); + auto indx = mProxyModel->mapFromSource(mIdListModel->getIndexOfIdentity(RsGxsId(it.second.toStdString()))); if(indx.isValid()) { - std::cerr << "trying to resotre selection of id " << it.second.toStdString() << std::endl; + std::cerr << "trying to restore selection of id " << it.second.toStdString() << std::endl; ui->idTreeWidget->selectionModel()->select(indx,QItemSelectionModel::Select | QItemSelectionModel::Rows); } else @@ -2606,23 +2664,84 @@ void IdDialog::restoreExpandedPathsAndSelection_idTreeView(const std::setidTreeWidget->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); - ui->idTreeWidget->blockSignals(false) ; - ui->idTreeWidget->selectionModel()->blockSignals(false); - #ifdef DEBUG_NEW_FRIEND_LIST std::cerr << " index to select: \"" << index_to_select.toStdString() << "\"" << std::endl; #endif for(int row = 0; row < mIdListModel->rowCount(); ++row) { - auto m = mIdListModel->index(row,0,QModelIndex()); + auto m = ui->idTreeWidget->model()->index(row,0); + + std::cerr << " index category = " << mIdListModel->getCategory(m) << std::endl; if(expanded_indexes.find(QString::number(mIdListModel->getCategory(m))) != expanded_indexes.end()) { std::cerr << "Restoring expanded index " << QString::number(mIdListModel->getCategory(m)).toStdString() << std::endl; + std::cerr << "Calling setExpanded " << m << std::endl; ui->idTreeWidget->setExpanded(m,true); } else ui->idTreeWidget->setExpanded(m,false); } + ui->idTreeWidget->blockSignals(false) ; + ui->idTreeWidget->selectionModel()->blockSignals(false); } +void IdDialog::applyWhileKeepingTree(std::function predicate) +{ + std::set expanded_indexes; + std::set > selected; + + saveExpandedPathsAndSelection_idTreeView(expanded_indexes, selected); + +#ifdef DEBUG_NEW_FRIEND_LIST + std::cerr << "After collecting selection, selected paths is: \"" << selected.toStdString() << "\", " ; + std::cerr << "expanded paths are: " << std::endl; + for(auto path:expanded_indexes) + std::cerr << " \"" << path.toStdString() << "\"" << std::endl; + std::cerr << "Current sort column is: " << mLastSortColumn << " and order is " << mLastSortOrder << std::endl; +#endif + whileBlocking(ui->idTreeWidget)->clearSelection(); + + // This is a hack to avoid crashes on windows while calling endInsertRows(). I'm not sure wether these crashes are + // due to a Qt bug, or a misuse of the proxy model on my side. Anyway, this solves them for good. + // As a side effect we need to save/restore hidden columns because setSourceModel() resets this setting. + + // save hidden columns and sizes + std::vector col_visible(RsIdentityListModel::COLUMN_THREAD_NB_COLUMNS); + std::vector col_sizes(RsIdentityListModel::COLUMN_THREAD_NB_COLUMNS); + + for(int i=0;iidTreeWidget->isColumnHidden(i); + col_sizes[i] = ui->idTreeWidget->columnWidth(i); + } + +#ifdef DEBUG_NEW_FRIEND_LIST + std::cerr << "Applying predicate..." << std::endl; +#endif + mProxyModel->setSourceModel(nullptr); + + predicate(); + + mProxyModel->setSourceModel(mIdListModel); + restoreExpandedPathsAndSelection_idTreeView(expanded_indexes,selected); + + // restore hidden columns + for(uint32_t i=0;iidTreeWidget->setColumnHidden(i,!col_visible[i]); + ui->idTreeWidget->setColumnWidth(i,col_sizes[i]); + } + + // restore sorting + // sortColumn(mLastSortColumn,mLastSortOrder); +#ifdef DEBUG_NEW_FRIEND_LIST + std::cerr << "Sorting again with sort column: " << mLastSortColumn << " and order " << mLastSortOrder << std::endl; +#endif + mProxyModel->setSortingEnabled(true); +// mProxyModel->sort(mLastSortColumn,mLastSortOrder); + mProxyModel->setSortingEnabled(false); + +// if(selected_index.isValid()) +// ui->idTreeWidget->scrollTo(selected_index); +} diff --git a/retroshare-gui/src/gui/Identity/IdDialog.h b/retroshare-gui/src/gui/Identity/IdDialog.h index 4822e52c7..594f4f5e3 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.h +++ b/retroshare-gui/src/gui/Identity/IdDialog.h @@ -37,6 +37,7 @@ class IdDialog; class UIStateHelper; class QTreeWidgetItem; class RsIdentityListModel; +class IdListSortFilterProxyModel; class IdDialog : public MainPage { @@ -150,14 +151,20 @@ private: void saveExpandedCircleItems(std::vector &expanded_root_items, std::set& expanded_circle_items) const; void restoreExpandedCircleItems(const std::vector& expanded_root_items,const std::set& expanded_circle_items); + void applyWhileKeepingTree(std::function predicate); + RsGxsId getSelectedIdentity() const; std::list getSelectedIdentities() const; + void idListItemExpanded(const QModelIndex& index); + void idListItemCollapsed(const QModelIndex& index); + RsGxsGroupId mId; RsGxsGroupId mIdToNavigate; int filter; RsIdentityListModel *mIdListModel; + IdListSortFilterProxyModel *mProxyModel; void handleEvent_main_thread(std::shared_ptr event); RsEventsHandlerId_t mEventHandlerId_identity; diff --git a/retroshare-gui/src/gui/Identity/IdentityListModel.cpp b/retroshare-gui/src/gui/Identity/IdentityListModel.cpp index 0f14d406f..3d4b579d7 100644 --- a/retroshare-gui/src/gui/Identity/IdentityListModel.cpp +++ b/retroshare-gui/src/gui/Identity/IdentityListModel.cpp @@ -96,7 +96,7 @@ bool RsIdentityListModel::convertIndexToInternalId(const EntryIndex& e,quintptr& return false; } - id = (((uint32_t)e.category_index) << 30) + ((uint32_t)e.identity_index << 2) + ((uint32_t)e.type); + id = ((0x3 & (uint32_t)e.category_index) << 30) + ((uint32_t)e.identity_index << 2) + (0x3 & (uint32_t)e.type); return true; }