From ff9dd91fc832166c9f53cdb0e05d7766ff616cde Mon Sep 17 00:00:00 2001 From: jolavillette Date: Wed, 24 Dec 2025 06:23:29 +0100 Subject: [PATCH 01/10] Improve upload statistics --- .../gui/FileTransfer/SharedFilesDialog.cpp | 64 ++++++++-- .../src/gui/FileTransfer/SharedFilesDialog.h | 25 ++-- retroshare-gui/src/gui/RemoteDirModel.cpp | 112 ++++++++++++++++-- retroshare-gui/src/gui/RemoteDirModel.h | 19 ++- 4 files changed, 189 insertions(+), 31 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 6e7c31777..f3ae5d878 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -1,21 +1,21 @@ /******************************************************************************* * retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp * - * * + * * * Copyright (c) 2009 Retroshare Team * - * * + * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * - * * + * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Affero General Public License for more details. * - * * + * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * - * * + * * *******************************************************************************/ #include "SharedFilesDialog.h" @@ -52,6 +52,7 @@ #include #include #include +#include // Ajouté #include @@ -107,7 +108,8 @@ const QString Image_AddNewAssotiationForFile = ":/icons/svg/options.svg"; class SFDSortFilterProxyModel : public QSortFilterProxyModel { public: - SFDSortFilterProxyModel(RetroshareDirModel *dirModel, QObject *parent) : QSortFilterProxyModel(parent) + SFDSortFilterProxyModel(RetroshareDirModel *dirModel, QObject *parent) + : QSortFilterProxyModel(parent), m_uploadedOnly(false) { m_dirModel = dirModel; @@ -118,7 +120,27 @@ public: setDynamicSortFilter(false); } + void setUploadedOnly(bool val) { + m_uploadedOnly = val; + invalidateFilter(); + } + protected: + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const + { + // 1. Custom Upload Check + if (m_uploadedOnly) { + // Check column 6 (Uploaded) + QModelIndex uploadIdx = sourceModel()->index(source_row, SHARED_FILES_DIALOG_COLUMN_UPLOADED, source_parent); + QString uploadStr = sourceModel()->data(uploadIdx, Qt::DisplayRole).toString(); + // If empty or "-", treat as 0 -> filtered out + if (uploadStr.isEmpty() || uploadStr == "-") return false; + } + + // 2. Default logic (preserves the "filtered" role check used by RetroShare for search/filtering) + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); + } + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const { bool dirLeft = (m_dirModel->getType(left) == DIR_TYPE_DIR); @@ -133,6 +155,7 @@ protected: private: RetroshareDirModel *m_dirModel; + bool m_uploadedOnly; }; // This class allows to draw the item in the share flags column using an appropriate size @@ -179,6 +202,14 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); + // --- Ajout de la CheckBox --- + uploadedOnly_CB = new QCheckBox(tr("Uploaded only"), this); + uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded")); + // On l'ajoute au layout horizontal existant qui contient les filtres + ui.horizontalLayout_2->addWidget(uploadedOnly_CB); + connect(uploadedOnly_CB, SIGNAL(toggled(bool)), this, SLOT(filterUploadedOnlyToggled(bool))); + // ---------------------------- + //connect(notify, SIGNAL(filesPreModChanged(bool)), this, SLOT(preModDirectories(bool))); //connect(notify, SIGNAL(filesPostModChanged(bool)), this, SLOT(postModDirectories(bool))); @@ -513,6 +544,9 @@ void LocalSharedFilesDialog::showProperColumns() ui.filterPatternLineEdit->show(); #endif } + // MODIFICATION: On s'assure que la colonne Uploaded est visible en mode local + // (Le filtre ne fonctionnera que si la colonne contient des données) + ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_UPLOADED, false); } void RemoteSharedFilesDialog::showProperColumns() { @@ -1408,7 +1442,7 @@ void SharedFilesDialog::indicatorChanged(int index) else ui.dirTreeView->sortByColumn(SHARED_FILES_DIALOG_COLUMN_NAME, Qt::AscendingOrder); - updateDisplay() ; + //updateDisplay() ; } void SharedFilesDialog::onFilterTextEdited() @@ -1502,6 +1536,21 @@ void SharedFilesDialog::startFilter() FilterItems(); } +void SharedFilesDialog::filterUploadedOnlyToggled(bool checked) +{ + tree_proxyModel->setUploadedOnly(checked); + flat_proxyModel->setUploadedOnly(checked); + + // Forcer le rafraîchissement + tree_proxyModel->invalidate(); + flat_proxyModel->invalidate(); + + // En mode Arbre, il peut être nécessaire d'étendre les éléments pour voir les résultats + if(checked && ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { + expandAll(); + } +} + void SharedFilesDialog::updateDirTreeView() { if (model == flat_model) @@ -1760,3 +1809,4 @@ void SharedFilesDialog::updateFontSize() ui.dirTreeView->setIconSize(QSize(iconHeight, iconHeight)); } } + diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index 375b6c075..0883fbf54 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -1,21 +1,21 @@ /******************************************************************************* * retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h * - * * + * * * Copyright (c) 2009 Retroshare Team * - * * + * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * - * * + * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Affero General Public License for more details. * - * * + * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * - * * + * * *******************************************************************************/ #ifndef _SHAREDFILESDIALOG_H @@ -29,9 +29,11 @@ #include "util/RsProtectedTimer.h" #include +#include // Ajout pour la checkbox class RetroshareDirModel; class QSortFilterProxyModel; +class SFDSortFilterProxyModel; // Forward declaration class SharedFilesDialog : public RsAutoUpdatePage { @@ -83,6 +85,9 @@ private slots: void startFilter(); void updateDirTreeView(); + + // Nouveau slot pour le filtre + void filterUploadedOnlyToggled(bool checked); public slots: void changeCurrentViewModel(int viewTypeIndex); @@ -136,8 +141,10 @@ protected: RetroshareDirModel *tree_model; RetroshareDirModel *flat_model; RetroshareDirModel *model; - QSortFilterProxyModel *tree_proxyModel; - QSortFilterProxyModel *flat_proxyModel; + + // Changé le type pour utiliser notre classe personnalisée + SFDSortFilterProxyModel *tree_proxyModel; + SFDSortFilterProxyModel *flat_proxyModel; QSortFilterProxyModel *proxyModel; QString currentCommand; @@ -148,6 +155,9 @@ protected: RsProtectedTimer* mFilterTimer; RsEventsHandlerId_t mEventHandlerId ; + + // Nouvelle CheckBox + QCheckBox *uploadedOnly_CB; }; class LocalSharedFilesDialog : public SharedFilesDialog @@ -208,4 +218,3 @@ class RemoteSharedFilesDialog : public SharedFilesDialog }; #endif - diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 9aa1386ee..73fc3f9e1 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -1,21 +1,21 @@ /******************************************************************************* - * gui/RemoteDirModel.cpp * - * * + * gui/RemoteDirModel.cpp * + * * * Copyright (c) 2006 Retroshare Team * - * * + * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * - * * + * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Affero General Public License for more details. * - * * + * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * - * * + * * *******************************************************************************/ #include "RemoteDirModel.h" @@ -42,6 +42,7 @@ #include #include #include +#include // Keep iostream for debug logs /***** * #define RDM_DEBUG @@ -58,8 +59,8 @@ #define REMOTEDIRMODEL_COLUMN_COUNT 7 #define RETROSHARE_DIR_MODEL_FILTER_STRING "filtered" -static const uint32_t FLAT_VIEW_MAX_REFS_PER_SECOND = 2000 ; -static const size_t FLAT_VIEW_MAX_REFS_TABLE_SIZE = 10000 ; // +static const uint32_t FLAT_VIEW_MAX_REFS_PER_SECOND = 10000 ; +static const size_t FLAT_VIEW_MAX_REFS_TABLE_SIZE = 30000 ; // static const uint32_t FLAT_VIEW_MIN_DELAY_BETWEEN_UPDATES = 120 ; // dont rebuild ref list more than every 2 mins. RetroshareDirModel::RetroshareDirModel(bool mode, QObject *parent) @@ -148,8 +149,76 @@ void RetroshareDirModel::treeStyle() categoryIcon.addPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/folderopen.png"), QIcon::Normal, QIcon::On); peerIcon = FilesDefs::getIconFromQtResourcePath(":/icons/folder-account.svg"); } + +void TreeStyle_RDM::recalculateDirectoryTotals() +{ + // DEBUG: Start of recalculation + // std::cerr << "UPLOAD_DBG: --- Starting recalculateDirectoryTotals ---" << std::endl; + + m_folderUploadTotals.clear(); + + // Only relevant for local files. Remote files do not expose upload statistics per file in the same way here. + if(RemoteMode) + return; + + std::vector stack; + // Start with NULL (Root) + stack.push_back(NULL); + + int processedFiles = 0; + + while(!stack.empty()) + { + void* ref = stack.back(); + stack.pop_back(); + + DirDetails details; + if(requestDirDetails(ref, RemoteMode, details)) + { + if(details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) + { + uint64_t uploaded = rsFiles->getCumulativeUpload(details.hash); + if(uploaded > 0) + { + processedFiles++; + // details.path for a file is the directory containing it. + QString currentPath = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); + + // Iterate up the directory tree + while(!currentPath.isEmpty() && currentPath != ".") + { + m_folderUploadTotals[currentPath] += uploaded; + + // Get parent directory using QFileInfo logic + QString parent = QFileInfo(currentPath).path(); + + // Break if we reached root or top (QFileInfo returns path itself if no parent) + if(parent == currentPath || parent.isEmpty()) { + break; + } + currentPath = parent; + } + } + } + // Included DIR_TYPE_PERSON to allow recursion into root "My Files" + else if(details.type == DIR_TYPE_DIR || details.type == DIR_TYPE_ROOT || details.type == DIR_TYPE_PERSON) + { + for(const auto& child : details.children) + { + stack.push_back(child.ref); + } + } + } + } + // DEBUG: Summary + std::cerr << "UPLOAD_DBG: --- Finished Recalc. Processed " << processedFiles << " active files. Map size: " << m_folderUploadTotals.size() << " ---" << std::endl; +} + void TreeStyle_RDM::update() { + // Recalculate totals before notifying view update + recalculateDirectoryTotals(); + preMods() ; postMods() ; } @@ -610,7 +679,6 @@ QVariant TreeStyle_RDM::displayRole(const DirDetails& details,int coln) const { case REMOTEDIRMODEL_COLUMN_NAME: return QString::fromUtf8(details.name.c_str()); - break; case REMOTEDIRMODEL_COLUMN_FILENB: if (details.children.size() > 1) { @@ -622,11 +690,34 @@ QVariant TreeStyle_RDM::displayRole(const DirDetails& details,int coln) const case REMOTEDIRMODEL_COLUMN_AGE: return misc::timeRelativeToNow(details.max_mtime); case REMOTEDIRMODEL_COLUMN_FRIEND_ACCESS: - return QVariant(); + return getFlagsString(details.flags); case REMOTEDIRMODEL_COLUMN_WN_VISU_DIR: return getGroupsString(details.flags,details.parent_groups) ; case REMOTEDIRMODEL_COLUMN_UPLOADED: + { + // New logic: Check if we have a calculated total for this directory + + // Based on logs, the 'details.path' for a directory IS the full path to that directory. + // We do NOT need to append the name. + + QString path = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); + + // Fallback / Safety: try constructed path if direct path fails (covers root vs sub-dir anomalies) + auto it = m_folderUploadTotals.find(path); + + // DEBUG: Display role request + std::cerr << "UPLOAD_DBG: Display Role for DIR. Name: " << details.name + << " | Raw Path: " << details.path + << " | Lookup Key: [" << path.toStdString() << "]" << std::endl; + + if(it != m_folderUploadTotals.end() && it.value() > 0) + { + std::cerr << "UPLOAD_DBG: -> FOUND! Value: " << it.value() << std::endl; + return misc::friendlyUnit(it.value()); + } + return ""; + } default: return tr("DIR"); @@ -1753,3 +1844,4 @@ void TreeStyle_RDM::showEmpty(const bool value) _showEmpty = value; update(); } + diff --git a/retroshare-gui/src/gui/RemoteDirModel.h b/retroshare-gui/src/gui/RemoteDirModel.h index b7811533e..befcb78e0 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.h +++ b/retroshare-gui/src/gui/RemoteDirModel.h @@ -1,21 +1,21 @@ -/******************************************************************************* + /******************************************************************************* * gui/RemoteDirModel.h * - * * + * * * Copyright (c) 2006 Retroshare Team * - * * + * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * * published by the Free Software Foundation, either version 3 of the * * License, or (at your option) any later version. * - * * + * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Affero General Public License for more details. * - * * + * * * You should have received a copy of the GNU Affero General Public License * * along with this program. If not, see . * - * * + * * *******************************************************************************/ #ifndef REMOTE_DIR_MODEL @@ -28,6 +28,8 @@ #include #include #include +#include // Changed from map to QHash for efficiency with QString +#include #include #include @@ -209,6 +211,11 @@ class TreeStyle_RDM: public RetroshareDirModel private: QAction *_showEmptyAct; bool _showEmpty; + + // Helper: Calculate totals using normalized paths + void recalculateDirectoryTotals(); + QHash m_folderUploadTotals; + protected: mutable std::vector _parentRow ; // used to store the real parent row for non empty child }; From 0b8066eac964e5332d72271ee470cb565eac17e9 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Wed, 24 Dec 2025 09:34:27 +0100 Subject: [PATCH 02/10] Fix SIGSEV in QSortFilterProxyModel::parent --- .../src/gui/FileTransfer/SharedFilesDialog.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index f3ae5d878..bff8885b8 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -1538,14 +1538,19 @@ void SharedFilesDialog::startFilter() void SharedFilesDialog::filterUploadedOnlyToggled(bool checked) { + // CRITICAL FIX: Clear selection before filtering to avoid SIGSEGV in QSortFilterProxyModel::parent + // This happens because QItemSelectionModel tries to access parents of items being hidden/removed. + if (ui.dirTreeView->selectionModel()) { + ui.dirTreeView->selectionModel()->clear(); + } + + // Apply filter tree_proxyModel->setUploadedOnly(checked); flat_proxyModel->setUploadedOnly(checked); - // Forcer le rafraîchissement - tree_proxyModel->invalidate(); - flat_proxyModel->invalidate(); + // Note: invalidate() calls removed as setUploadedOnly already calls invalidateFilter() - // En mode Arbre, il peut être nécessaire d'étendre les éléments pour voir les résultats + // In Tree view, we might need to expand items to see results if they are deep if(checked && ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { expandAll(); } From 303e3e2a96d9978bfdeec7aae3a058abd6422949 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Wed, 24 Dec 2025 18:50:13 +0100 Subject: [PATCH 03/10] Various UI improvements --- .../gui/FileTransfer/SharedFilesDialog.cpp | 31 ++++++++++--------- .../src/gui/FileTransfer/SharedFilesDialog.h | 8 ++--- retroshare-gui/src/gui/RemoteDirModel.cpp | 30 ++++++++++-------- retroshare-gui/src/gui/RemoteDirModel.h | 6 ++-- 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index bff8885b8..813491423 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -52,7 +52,8 @@ #include #include #include -#include // Ajouté +#include // Added +#include // Added #include @@ -128,7 +129,7 @@ public: protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - // 1. Custom Upload Check + // 1. Custom Upload Check (MODIFIED) if (m_uploadedOnly) { // Check column 6 (Uploaded) QModelIndex uploadIdx = sourceModel()->index(source_row, SHARED_FILES_DIALOG_COLUMN_UPLOADED, source_parent); @@ -137,7 +138,6 @@ protected: if (uploadStr.isEmpty() || uploadStr == "-") return false; } - // 2. Default logic (preserves the "filtered" role check used by RetroShare for search/filtering) return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } @@ -155,7 +155,7 @@ protected: private: RetroshareDirModel *m_dirModel; - bool m_uploadedOnly; + bool m_uploadedOnly; // Added member }; // This class allows to draw the item in the share flags column using an appropriate size @@ -197,19 +197,11 @@ SharedFilesDialog::~SharedFilesDialog() } /** Constructor */ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) - : RsAutoUpdatePage(1000,parent), model(NULL) + : RsAutoUpdatePage(1000,parent), model(NULL), uploadedOnly_CB(NULL) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); - // --- Ajout de la CheckBox --- - uploadedOnly_CB = new QCheckBox(tr("Uploaded only"), this); - uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded")); - // On l'ajoute au layout horizontal existant qui contient les filtres - ui.horizontalLayout_2->addWidget(uploadedOnly_CB); - connect(uploadedOnly_CB, SIGNAL(toggled(bool)), this, SLOT(filterUploadedOnlyToggled(bool))); - // ---------------------------- - //connect(notify, SIGNAL(filesPreModChanged(bool)), this, SLOT(preModDirectories(bool))); //connect(notify, SIGNAL(filesPostModChanged(bool)), this, SLOT(postModDirectories(bool))); @@ -293,7 +285,8 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FILENB , charWidth*15 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_SIZE , charWidth*10 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_AGE , charWidth*6 ); - header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*10 ); + // MODIFIED: Reduced column width for Access (from 10 to 4) as requested + header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*4 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR , charWidth*20 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_UPLOADED , charWidth*20 ); @@ -326,6 +319,16 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent) : SharedFilesDialog(false,parent) { + // MODIFIED: Create and insert the checkbox only here (for My files) + uploadedOnly_CB = new QCheckBox(tr("Uploaded only"), this); + uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded")); + + // Insert immediately after the view selector + int cbIndex = ui.horizontalLayout_2->indexOf(ui.viewType_CB); + ui.horizontalLayout_2->insertWidget(cbIndex + 1, uploadedOnly_CB); + + connect(uploadedOnly_CB, SIGNAL(toggled(bool)), this, SLOT(filterUploadedOnlyToggled(bool))); + // Hide columns after loading the settings ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ; ui.downloadButton->hide() ; diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index 0883fbf54..e79885221 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -29,7 +29,7 @@ #include "util/RsProtectedTimer.h" #include -#include // Ajout pour la checkbox +#include // For the new filter feature class RetroshareDirModel; class QSortFilterProxyModel; @@ -86,7 +86,7 @@ private slots: void updateDirTreeView(); - // Nouveau slot pour le filtre + // Slot for the new checkbox filter void filterUploadedOnlyToggled(bool checked); public slots: @@ -142,7 +142,7 @@ protected: RetroshareDirModel *flat_model; RetroshareDirModel *model; - // Changé le type pour utiliser notre classe personnalisée + // Changed to specific Proxy model type to access new methods SFDSortFilterProxyModel *tree_proxyModel; SFDSortFilterProxyModel *flat_proxyModel; QSortFilterProxyModel *proxyModel; @@ -156,7 +156,7 @@ protected: RsEventsHandlerId_t mEventHandlerId ; - // Nouvelle CheckBox + // New Checkbox member (protected to be accessible by LocalSharedFilesDialog) QCheckBox *uploadedOnly_CB; }; diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 73fc3f9e1..80a0971f1 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -211,7 +211,7 @@ void TreeStyle_RDM::recalculateDirectoryTotals() } } // DEBUG: Summary - std::cerr << "UPLOAD_DBG: --- Finished Recalc. Processed " << processedFiles << " active files. Map size: " << m_folderUploadTotals.size() << " ---" << std::endl; + // std::cerr << "UPLOAD_DBG: --- Finished Recalc. Processed " << processedFiles << " active files. Map size: " << m_folderUploadTotals.size() << " ---" << std::endl; } void TreeStyle_RDM::update() @@ -381,15 +381,10 @@ int FlatStyle_RDM::columnCount(const QModelIndex &/*parent*/) const return REMOTEDIRMODEL_COLUMN_COUNT; } -QString RetroshareDirModel::getFlagsString(FileStorageFlags flags) +// MODIFIED: Return empty string to hide B, S, N letters +QString RetroshareDirModel::getFlagsString(FileStorageFlags /*flags*/) { - char str[11] = "- - -" ; - - if(flags & DIR_FLAGS_BROWSABLE) str[0] = 'B' ; - if(flags & DIR_FLAGS_ANONYMOUS_SEARCH) str[3] = 'S' ; - if(flags & DIR_FLAGS_ANONYMOUS_DOWNLOAD) str[6] = 'N' ; - - return QString(str) ; + return QString("") ; } QString RetroshareDirModel::getGroupsString(FileStorageFlags flags,const std::list& group_ids) { @@ -706,13 +701,13 @@ QVariant TreeStyle_RDM::displayRole(const DirDetails& details,int coln) const auto it = m_folderUploadTotals.find(path); // DEBUG: Display role request - std::cerr << "UPLOAD_DBG: Display Role for DIR. Name: " << details.name - << " | Raw Path: " << details.path - << " | Lookup Key: [" << path.toStdString() << "]" << std::endl; + // std::cerr << "UPLOAD_DBG: Display Role for DIR. Name: " << details.name + // << " | Raw Path: " << details.path + // << " | Lookup Key: [" << path.toStdString() << "]" << std::endl; if(it != m_folderUploadTotals.end() && it.value() > 0) { - std::cerr << "UPLOAD_DBG: -> FOUND! Value: " << it.value() << std::endl; + // std::cerr << "UPLOAD_DBG: -> FOUND! Value: " << it.value() << std::endl; return misc::friendlyUnit(it.value()); } @@ -950,6 +945,15 @@ QVariant RetroshareDirModel::data(const QModelIndex &index, int role) const if (role == Qt::ForegroundRole) { + // MODIFIED: Colorize Uploaded column (Directory=Blue, File=Green) + if (coln == REMOTEDIRMODEL_COLUMN_UPLOADED) + { + if (details.type == DIR_TYPE_DIR) + return QColor(Qt::blue); + else if (details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) + return QColor(Qt::darkGreen); + } + if((details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) && details.hash.isNull()) return QVariant(QColor(Qt::green)) ; else if(ageIndicator != IND_ALWAYS && details.max_mtime + ageIndicator < time(NULL)) diff --git a/retroshare-gui/src/gui/RemoteDirModel.h b/retroshare-gui/src/gui/RemoteDirModel.h index befcb78e0..37d731fee 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.h +++ b/retroshare-gui/src/gui/RemoteDirModel.h @@ -1,4 +1,4 @@ - /******************************************************************************* +/******************************************************************************* * gui/RemoteDirModel.h * * * * Copyright (c) 2006 Retroshare Team * @@ -28,7 +28,7 @@ #include #include #include -#include // Changed from map to QHash for efficiency with QString +#include #include #include @@ -212,7 +212,7 @@ class TreeStyle_RDM: public RetroshareDirModel QAction *_showEmptyAct; bool _showEmpty; - // Helper: Calculate totals using normalized paths + // Helper to calculate total upload per directory void recalculateDirectoryTotals(); QHash m_folderUploadTotals; From 7226f8afcc82ccdee93a4675e9956943b30440a9 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Thu, 25 Dec 2025 21:34:57 +0100 Subject: [PATCH 04/10] Fix crash in QItemSelectionRange destructor --- .../gui/FileTransfer/SharedFilesDialog.cpp | 59 ++++++++++--------- .../src/gui/FileTransfer/SharedFilesDialog.h | 13 ++-- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 813491423..815182b09 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -52,8 +52,8 @@ #include #include #include -#include // Added -#include // Added +#include +#include #include @@ -129,15 +129,16 @@ public: protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - // 1. Custom Upload Check (MODIFIED) + // Custom check for "Uploaded only" filter if (m_uploadedOnly) { - // Check column 6 (Uploaded) + // Retrieve data from column 6 QModelIndex uploadIdx = sourceModel()->index(source_row, SHARED_FILES_DIALOG_COLUMN_UPLOADED, source_parent); QString uploadStr = sourceModel()->data(uploadIdx, Qt::DisplayRole).toString(); - // If empty or "-", treat as 0 -> filtered out + // If empty or "-", treat as 0 and filter row out if (uploadStr.isEmpty() || uploadStr == "-") return false; } + // Standard RetroShare logic for search/keywords return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } @@ -155,7 +156,7 @@ protected: private: RetroshareDirModel *m_dirModel; - bool m_uploadedOnly; // Added member + bool m_uploadedOnly; }; // This class allows to draw the item in the share flags column using an appropriate size @@ -285,7 +286,7 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FILENB , charWidth*15 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_SIZE , charWidth*10 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_AGE , charWidth*6 ); - // MODIFIED: Reduced column width for Access (from 10 to 4) as requested + // REDUCED WIDTH: Set to 4 instead of 10 to only fit icons header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*4 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR , charWidth*20 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_UPLOADED , charWidth*20 ); @@ -319,13 +320,12 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent) : SharedFilesDialog(false,parent) { - // MODIFIED: Create and insert the checkbox only here (for My files) + // CREATION & POSITIONING: Box created only here and placed right of view selector uploadedOnly_CB = new QCheckBox(tr("Uploaded only"), this); uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded")); - // Insert immediately after the view selector - int cbIndex = ui.horizontalLayout_2->indexOf(ui.viewType_CB); - ui.horizontalLayout_2->insertWidget(cbIndex + 1, uploadedOnly_CB); + int viewTypeIdx = ui.horizontalLayout_2->indexOf(ui.viewType_CB); + ui.horizontalLayout_2->insertWidget(viewTypeIdx + 1, uploadedOnly_CB); connect(uploadedOnly_CB, SIGNAL(toggled(bool)), this, SLOT(filterUploadedOnlyToggled(bool))); @@ -547,8 +547,7 @@ void LocalSharedFilesDialog::showProperColumns() ui.filterPatternLineEdit->show(); #endif } - // MODIFICATION: On s'assure que la colonne Uploaded est visible en mode local - // (Le filtre ne fonctionnera que si la colonne contient des données) + // Column 6 must be visible for filtering to work ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_UPLOADED, false); } void RemoteSharedFilesDialog::showProperColumns() @@ -1051,12 +1050,18 @@ void SharedFilesDialog::restoreExpandedPathsAndSelection(const std::setmodel()->rowCount(); ++row) { std::string path = ui.dirTreeView->model()->index(row,0).data(Qt::DisplayRole).toString().toStdString(); - recursRestoreExpandedItems(ui.dirTreeView->model()->index(row,0),path,expanded_indexes,hidden_indexes,selected_indexes); + recursRestoreExpandedItems(ui.dirTreeView->model()->index(row,0),path,expanded_indexes,hidden_indexes,selected_indexes, batchSelection); } - //QItemSelection selection ; + + if(!batchSelection.isEmpty()) + ui.dirTreeView->selectionModel()->select(batchSelection, QItemSelectionModel::Select | QItemSelectionModel::Rows); ui.dirTreeView->blockSignals(false) ; } @@ -1139,15 +1144,17 @@ void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const s void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, const std::string &path, const std::set& exp, const std::set& hid, - const std::set &sel) + const std::set &sel, + QItemSelection& batchSelection) { std::string local_path = path+"/"+index.data(Qt::DisplayRole).toString().toStdString(); #ifdef DEBUG_SHARED_FILES_DIALOG std::cerr << "at index " << index.row() << ". data[1]=" << local_path << std::endl; #endif + /** Collect index for batch selection later */ if(sel.find(local_path) != sel.end()) - ui.dirTreeView->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + batchSelection.select(index, index); // Disable hidden check as we don't use it and Qt bug: https://bugreports.qt.io/browse/QTBUG-11438 /*bool invisible = hid.find(local_path) != hid.end(); @@ -1165,9 +1172,9 @@ void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, con for(int row=0;rowmodel()->rowCount(index);++row) #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) - recursRestoreExpandedItems(ui.dirTreeView->model()->index(row,0,index),local_path,exp,hid,sel) ; + recursRestoreExpandedItems(ui.dirTreeView->model()->index(row,0,index),local_path,exp,hid,sel, batchSelection) ; #else - recursRestoreExpandedItems(index.child(row,0),local_path,exp,hid,sel) ; + recursRestoreExpandedItems(index.child(row,0),local_path,exp,hid,sel, batchSelection) ; #endif } } @@ -1193,10 +1200,11 @@ void SharedFilesDialog::postModDirectories(bool local) if (ui.filterPatternLineEdit->text().isEmpty() == false) FilterItems(); - ui.dirTreeView->setSortingEnabled(true); - + /** FIX: Restore selection and expansion BEFORE enabling sorting to avoid mismatch */ restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; + ui.dirTreeView->setSortingEnabled(true); + #ifdef DEBUG_SHARED_FILES_DIALOG std::cerr << "****** updated directories! Re-enabling sorting ******" << std::endl; #endif @@ -1541,19 +1549,16 @@ void SharedFilesDialog::startFilter() void SharedFilesDialog::filterUploadedOnlyToggled(bool checked) { - // CRITICAL FIX: Clear selection before filtering to avoid SIGSEGV in QSortFilterProxyModel::parent - // This happens because QItemSelectionModel tries to access parents of items being hidden/removed. + /** FIX: Clear selection before filtering to avoid SIGSEGV in parent calculations */ if (ui.dirTreeView->selectionModel()) { ui.dirTreeView->selectionModel()->clear(); } - // Apply filter + // Apply the boolean filter to both proxies tree_proxyModel->setUploadedOnly(checked); flat_proxyModel->setUploadedOnly(checked); - // Note: invalidate() calls removed as setUploadedOnly already calls invalidateFilter() - - // In Tree view, we might need to expand items to see results if they are deep + // Expand to show results in tree view if(checked && ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { expandAll(); } diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index e79885221..2012dd21b 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -29,11 +29,12 @@ #include "util/RsProtectedTimer.h" #include -#include // For the new filter feature +#include +#include class RetroshareDirModel; class QSortFilterProxyModel; -class SFDSortFilterProxyModel; // Forward declaration +class SFDSortFilterProxyModel; class SharedFilesDialog : public RsAutoUpdatePage { @@ -86,7 +87,7 @@ private slots: void updateDirTreeView(); - // Slot for the new checkbox filter + /** Slot for the Uploaded Only checkbox filter */ void filterUploadedOnlyToggled(bool checked); public slots: @@ -99,7 +100,8 @@ protected: Ui::SharedFilesDialog ui; virtual void processSettings(bool bLoad) = 0; - void recursRestoreExpandedItems(const QModelIndex& index, const std::string& path, const std::set& exp, const std::set& vis, const std::set& sel); + /** signature updated to support selection batching to prevent hangs/crashes */ + void recursRestoreExpandedItems(const QModelIndex& index, const std::string& path, const std::set& exp, const std::set& vis, const std::set& sel, QItemSelection& batchSelection); 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) ; @@ -142,7 +144,6 @@ protected: RetroshareDirModel *flat_model; RetroshareDirModel *model; - // Changed to specific Proxy model type to access new methods SFDSortFilterProxyModel *tree_proxyModel; SFDSortFilterProxyModel *flat_proxyModel; QSortFilterProxyModel *proxyModel; @@ -156,7 +157,7 @@ protected: RsEventsHandlerId_t mEventHandlerId ; - // New Checkbox member (protected to be accessible by LocalSharedFilesDialog) + /** Checkbox to filter files with 0 upload */ QCheckBox *uploadedOnly_CB; }; From 23bf0a242f11f97c89f3f088d3856840ef6b9dec Mon Sep 17 00:00:00 2001 From: jolavillette Date: Fri, 26 Dec 2025 22:16:17 +0100 Subject: [PATCH 05/10] Fix autocollapse in tree view --- .../gui/FileTransfer/SharedFilesDialog.cpp | 267 ++++++------ retroshare-gui/src/gui/RemoteDirModel.cpp | 403 ++++++++---------- 2 files changed, 302 insertions(+), 368 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 815182b09..e90a5c139 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -57,6 +57,31 @@ #include +/** + * Helper class to block signals and show wait cursor during UI updates. + * Prevents the view from reacting to model changes while we are + * restoring the tree state. + */ +class QCursorContextBlocker +{ +public: + QCursorContextBlocker(QWidget *w) + : mW(w) + { + mW->setCursor(Qt::WaitCursor); + mW->blockSignals(true); + } + + ~QCursorContextBlocker() + { + mW->setCursor(Qt::ArrowCursor); + mW->blockSignals(false); + } + +private: + QWidget *mW ; +}; + #define SHARED_FILES_DIALOG_COLUMN_NAME 0 #define SHARED_FILES_DIALOG_COLUMN_FILENB 1 #define SHARED_FILES_DIALOG_COLUMN_SIZE 2 @@ -196,16 +221,16 @@ SharedFilesDialog::~SharedFilesDialog() delete flat_model; delete tree_proxyModel; } -/** Constructor */ + +/** + * Constructor for the base SharedFilesDialog. + */ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) : RsAutoUpdatePage(1000,parent), model(NULL), uploadedOnly_CB(NULL) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); - //connect(notify, SIGNAL(filesPreModChanged(bool)), this, SLOT(preModDirectories(bool))); - //connect(notify, SIGNAL(filesPostModChanged(bool)), this, SLOT(postModDirectories(bool))); - mEventHandlerId = 0; rsEvents->registerEventsHandler([this](std::shared_ptr event) @@ -238,14 +263,21 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) connect(ui.dirTreeView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT( spawnCustomPopupMenu( QPoint ) ) ); connect(ui.indicatorCBox, SIGNAL(currentIndexChanged(int)), this, SLOT(indicatorChanged(int))); + // Ensure the combo box items are not bold + QFont normalFont = ui.viewType_CB->font(); + normalFont.setBold(false); + ui.viewType_CB->setFont(normalFont); + + // Reset specific items font role to ensure no bolding from UI files + for(int i = 0; i < ui.viewType_CB->count(); ++i) { + ui.viewType_CB->setItemData(i, normalFont, Qt::FontRole); + } + tree_model = new TreeStyle_RDM(remote_mode); flat_model = new FlatStyle_RDM(remote_mode); 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); @@ -266,15 +298,10 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) connect(ui.filterStartButton, SIGNAL(clicked()), this, SLOT(startFilter())); connect(ui.filterPatternLineEdit, SIGNAL(returnPressed()), this, SLOT(startFilter())); connect(ui.filterPatternLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(onFilterTextEdited())); - //Hidden by default, shown on onFilterTextEdited + 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())); - - /* Set header resize modes and initial section sizes */ QHeaderView * header = ui.dirTreeView->header () ; #if QT_VERSION < QT_VERSION_CHECK(5,11,0) int charWidth = ui.dirTreeView->fontMetrics().width("_"); @@ -286,26 +313,22 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FILENB , charWidth*15 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_SIZE , charWidth*10 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_AGE , charWidth*6 ); - // REDUCED WIDTH: Set to 4 instead of 10 to only fit icons header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*4 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR , charWidth*20 ); header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_UPLOADED , charWidth*20 ); header->setStretchLastSection(true); - - /* Set Multi Selection */ ui.dirTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); - /* Hide platform specific features */ - copylinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy retroshare Links to Clipboard" ), this ); - connect( copylinkAct , SIGNAL( triggered() ), this, SLOT( copyLink() ) ); - copylinkhtmlAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy retroshare Links to Clipboard (HTML)" ), this ); - connect( copylinkhtmlAct , SIGNAL( triggered() ), this, SLOT( copyLinkhtml() ) ); - sendlinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Send retroshare Links" ), this ); - connect( sendlinkAct , SIGNAL( triggered() ), this, SLOT( sendLinkTo( ) ) ); + copylinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy retroshare Links to Clipboard" ), this ); + connect( copylinkAct , SIGNAL( triggered() ), this, SLOT( copyLink() ) ); + copylinkhtmlAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Copy retroshare Links to Clipboard (HTML)" ), this ); + connect( copylinkhtmlAct , SIGNAL( triggered() ), this, SLOT( copyLinkhtml() ) ); + sendlinkAct = new QAction(QIcon(IMAGE_COPYLINK), tr( "Send retroshare Links" ), this ); + connect( sendlinkAct , SIGNAL( triggered() ), this, SLOT( sendLinkTo( ) ) ); - removeExtraFileAct = new QAction(QIcon(IMAGE_UNSHAREEXTRA), tr( "Stop sharing this file" ), this ); - connect( removeExtraFileAct , SIGNAL( triggered() ), this, SLOT( removeExtraFile() ) ); + removeExtraFileAct = new QAction(QIcon(IMAGE_UNSHAREEXTRA), tr( "Stop sharing this file" ), this ); + connect( removeExtraFileAct , SIGNAL( triggered() ), this, SLOT( removeExtraFile() ) ); collCreateAct= new QAction(QIcon(IMAGE_COLLCREATE), tr("Create Collection..."), this) ; connect(collCreateAct,SIGNAL(triggered()),this,SLOT(collCreate())) ; @@ -317,26 +340,25 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) connect(collOpenAct, SIGNAL(triggered()), this, SLOT(collOpen())) ; } +/** + * Constructor for LocalSharedFilesDialog. + */ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent) : SharedFilesDialog(false,parent) { - // CREATION & POSITIONING: Box created only here and placed right of view selector - uploadedOnly_CB = new QCheckBox(tr("Uploaded only"), this); - uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded")); + // Updated label to "Popular files" as requested + uploadedOnly_CB = new QCheckBox(tr("Popular files"), this); + uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded by others")); - int viewTypeIdx = ui.horizontalLayout_2->indexOf(ui.viewType_CB); - ui.horizontalLayout_2->insertWidget(viewTypeIdx + 1, uploadedOnly_CB); + int cbIndex = ui.horizontalLayout_2->indexOf(ui.viewType_CB); + ui.horizontalLayout_2->insertWidget(cbIndex + 1, uploadedOnly_CB); connect(uploadedOnly_CB, SIGNAL(toggled(bool)), this, SLOT(filterUploadedOnlyToggled(bool))); - // Hide columns after loading the settings ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ; ui.downloadButton->hide() ; - // load settings processSettings(true); - // Setup the current view model. - // changeCurrentViewModel(ui.viewType_CB->currentIndex()) ; connect(ui.addShares_PB, SIGNAL(clicked()), this, SLOT(addShares())) ; @@ -348,7 +370,6 @@ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent) connect(openfolderAct, SIGNAL(triggered()), this, SLOT(openfolder())) ; ui.titleBarPixmap->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_MYFILES)) ; - ui.dirTreeView->setItemDelegateForColumn(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS,new ShareFlagsItemDelegate()) ; } @@ -1179,41 +1200,99 @@ void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, con } } - -void SharedFilesDialog::postModDirectories(bool local) +/** + * Handles directory updates after model changes. + * Fixed to prevent autocollapse by restoring state after sorting. + */ +void SharedFilesDialog::postModDirectories(bool local) { if (isRemote() == local) return; - std::set expanded_indexes,selected_indexes,hidden_indexes; + std::set expanded_indexes, selected_indexes, hidden_indexes; - saveExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "Saving expanded items. " << expanded_indexes.size() << " items found" << std::endl; -#endif + // 1. Save current state + saveExpandedPathsAndSelection(expanded_indexes, hidden_indexes, selected_indexes) ; - /* Notify both models, only one is visible */ + // 2. Update models tree_model->postMods(); flat_model->postMods(); ui.dirTreeView->update() ; - if (ui.filterPatternLineEdit->text().isEmpty() == false) - FilterItems(); - - /** FIX: Restore selection and expansion BEFORE enabling sorting to avoid mismatch */ - restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ; - + // 3. Re-enable sorting BEFORE restoring expansion to stabilize the view ui.dirTreeView->setSortingEnabled(true); -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "****** updated directories! Re-enabling sorting ******" << std::endl; -#endif + // 4. Re-apply the text filter + if (ui.filterPatternLineEdit->text().isEmpty() == false) { + FilterItems(); + } + + // 5. Finally restore expansion on a stable model + restoreExpandedPathsAndSelection(expanded_indexes, hidden_indexes, selected_indexes) ; #if QT_VERSION < QT_VERSION_CHECK (6, 0, 0) QCoreApplication::flush(); #endif } +/** + * Applies text filtering to the current model. + * Optimized to avoid recursive model resets. + */ +void SharedFilesDialog::FilterItems() +{ +#ifdef DONT_USE_SEARCH_IN_TREE_VIEW + if(proxyModel == tree_proxyModel) + return; +#endif + + QString text = ui.filterPatternLineEdit->text(); + + if(mLastFilterText == text) + { + return ; + } + + mLastFilterText = text ; + + QCursorContextBlocker q(ui.dirTreeView) ; + QCoreApplication::processEvents() ; + + uint32_t found = 0 ; + + if(text == "") + { + model->filterItems(std::list(), found) ; + if (tree_proxyModel) tree_proxyModel->invalidate(); + if (flat_proxyModel) flat_proxyModel->invalidate(); + return ; + } + + if(text.length() < 3) + return ; + + QStringList lst = text.split(" ", QtSkipEmptyParts) ; + std::list keywords ; + + for(auto it(lst.begin()); it != lst.end(); ++it) + keywords.push_back((*it).toStdString()); + + model->filterItems(keywords, found) ; + + if (tree_proxyModel) tree_proxyModel->invalidate(); + if (flat_proxyModel) flat_proxyModel->invalidate(); + + 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 + ui.filterPatternFrame->setToolTip(tr("Found %1 results.").arg(found)) ; +} + class ChannelCompare { public: @@ -1656,90 +1735,6 @@ void SharedFilesDialog::restoreInvisibleItems() } #endif -class QCursorContextBlocker -{ - public: - QCursorContextBlocker(QWidget *w) - : mW(w) - { - mW->setCursor(Qt::WaitCursor); - mW->blockSignals(true) ; - } - - ~QCursorContextBlocker() - { - mW->setCursor(Qt::ArrowCursor); - mW->blockSignals(false) ; - } - - private: - QWidget *mW ; -}; - -void SharedFilesDialog::FilterItems() -{ -#ifdef DONT_USE_SEARCH_IN_TREE_VIEW - if(proxyModel == tree_proxyModel) - return; -#endif - - QString text = ui.filterPatternLineEdit->text(); - - if(mLastFilterText == text) // do not filter again if we already did. This is an optimization - { -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "Last text is equal to text. skipping" << std::endl; -#endif - return ; - } - -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << "New last text. Performing the filter on string \"" << text.toStdString() << "\"" << std::endl; -#endif - mLastFilterText = text ; - - QCursorContextBlocker q(ui.dirTreeView) ; - - QCoreApplication::processEvents() ; - - std::list result_list ; - uint32_t found = 0 ; - - if(text == "") - { - model->filterItems(std::list(),found) ; - model->update() ; - return ; - } - - if(text.length() < 3) - return ; - - //FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; - QStringList lst = text.split(" ",QtSkipEmptyParts) ; - std::list keywords ; - - for(auto it(lst.begin());it!=lst.end();++it) - keywords.push_back((*it).toStdString()); - - model->filterItems(keywords,found) ; - model->update() ; - - 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 - ui.filterPatternFrame->setToolTip(tr("Found %1 results.").arg(found)) ; - -#ifdef DEBUG_SHARED_FILES_DIALOG - std::cerr << found << " results found by search." << std::endl; -#endif -} - void SharedFilesDialog::removeExtraFile() { std::list files_info ; diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 80a0971f1..523605756 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -544,183 +544,6 @@ QVariant RetroshareDirModel::decorationRole(const DirDetails& details,int coln) } /* end of DecorationRole */ -QVariant TreeStyle_RDM::displayRole(const DirDetails& details,int coln) const -{ - - /* - * Person: name, id, 0, 0; - * File : name, size, rank, (0) ts - * Dir : name, (0) count, (0) path, (0) ts - */ - - - if (details.type == DIR_TYPE_PERSON) /* Person */ - { - switch(coln) - { - case REMOTEDIRMODEL_COLUMN_NAME: { - //SharedDirStats stats ; - QString res ; - - if(RemoteMode) - res = QString::fromUtf8(rsPeers->getPeerName(details.id).c_str()); - else if(details.id == rsPeers->getOwnId()) - res = tr("My files"); - else - res = tr("Temporary shared files"); - - return res ; - } - case REMOTEDIRMODEL_COLUMN_FILENB: { - SharedDirStats stats ; - - if(RemoteMode) - rsFiles->getSharedDirStatistics(details.id,stats) ; - else if(details.id == rsPeers->getOwnId()) - rsFiles->getSharedDirStatistics(rsPeers->getOwnId(),stats) ; - else - stats.total_number_of_files = details.children.size(); - - if(stats.total_number_of_files > 0) - { - if (stats.total_number_of_files > 1) - return QString::number(stats.total_number_of_files) + " " + tr("Files"); - else - return QString::number(stats.total_number_of_files) + " " + tr("File"); - } - return tr("Empty"); - } - case REMOTEDIRMODEL_COLUMN_SIZE: { - SharedDirStats stats ; - - if(RemoteMode) - rsFiles->getSharedDirStatistics(details.id,stats) ; - else if(details.id == rsPeers->getOwnId()) - rsFiles->getSharedDirStatistics(rsPeers->getOwnId(),stats) ; - else - return QString(); - - if(stats.total_shared_size > 0) - return misc::friendlyUnit(stats.total_shared_size) ; - - return QString(); - } - case REMOTEDIRMODEL_COLUMN_AGE: - if(!isNewerThanEpoque(details.max_mtime)) - return QString(); - else if(details.id != rsPeers->getOwnId()) - return QString(); - else - return misc::timeRelativeToNow(details.max_mtime); - case REMOTEDIRMODEL_COLUMN_UPLOADED: - { - if(!RemoteMode && details.id == rsPeers->getOwnId()) // Totals in "My files" row - { - uint64_t n = rsFiles->getCumulativeUploadNum(); - if(n) - return QString(misc::friendlyUnit(rsFiles->getCumulativeUploadAll()) + QString(" - %1 files").arg(n)); - else - return QString("-"); - } - } - default: - return QString() ; - } - } - else if (details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) /* File */ - { - switch(coln) - { - case REMOTEDIRMODEL_COLUMN_NAME: - return QString::fromUtf8(details.name.c_str()); - case REMOTEDIRMODEL_COLUMN_FILENB: - return QVariant(); - case REMOTEDIRMODEL_COLUMN_SIZE: - return misc::friendlyUnit(details.size); - case REMOTEDIRMODEL_COLUMN_AGE: - { - if(details.type == DIR_TYPE_FILE) - return misc::timeRelativeToNow(details.max_mtime); - else if(details.type == DIR_TYPE_EXTRA_FILE) - { - FileInfo fi; - if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA , fi)) - return misc::timeRelativeToNow((rstime_t)fi.age-(30 * 3600 * 24)); // AFI_DEFAULT_PERIOD - return QString(); - } - else - return QString(); - } - case REMOTEDIRMODEL_COLUMN_FRIEND_ACCESS: - return QVariant(); - case REMOTEDIRMODEL_COLUMN_WN_VISU_DIR: - return getGroupsString(details.flags,details.parent_groups) ; - case REMOTEDIRMODEL_COLUMN_UPLOADED: - { - uint64_t x = rsFiles->getCumulativeUpload(details.hash); - if(x) - return misc::friendlyUnit(x); - else - return QString(); - } - - default: - return tr("FILE"); - } - } - else if (details.type == DIR_TYPE_DIR) /* Dir */ - { - switch(coln) - { - case REMOTEDIRMODEL_COLUMN_NAME: - return QString::fromUtf8(details.name.c_str()); - case REMOTEDIRMODEL_COLUMN_FILENB: - if (details.children.size() > 1) - { - return QString::number(details.children.size()) + " " + tr("Files"); - } - return QString::number(details.children.size()) + " " + tr("File"); - case REMOTEDIRMODEL_COLUMN_SIZE: - return misc::friendlyUnit(details.size); - case REMOTEDIRMODEL_COLUMN_AGE: - return misc::timeRelativeToNow(details.max_mtime); - case REMOTEDIRMODEL_COLUMN_FRIEND_ACCESS: - return getFlagsString(details.flags); - case REMOTEDIRMODEL_COLUMN_WN_VISU_DIR: - return getGroupsString(details.flags,details.parent_groups) ; - case REMOTEDIRMODEL_COLUMN_UPLOADED: - { - // New logic: Check if we have a calculated total for this directory - - // Based on logs, the 'details.path' for a directory IS the full path to that directory. - // We do NOT need to append the name. - - QString path = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); - - // Fallback / Safety: try constructed path if direct path fails (covers root vs sub-dir anomalies) - auto it = m_folderUploadTotals.find(path); - - // DEBUG: Display role request - // std::cerr << "UPLOAD_DBG: Display Role for DIR. Name: " << details.name - // << " | Raw Path: " << details.path - // << " | Lookup Key: [" << path.toStdString() << "]" << std::endl; - - if(it != m_folderUploadTotals.end() && it.value() > 0) - { - // std::cerr << "UPLOAD_DBG: -> FOUND! Value: " << it.value() << std::endl; - return misc::friendlyUnit(it.value()); - } - - return ""; - } - - default: - return tr("DIR"); - } - } - return QVariant(); -} /* end of DisplayRole */ - void FlatStyle_RDM::update() { if(_needs_update) @@ -1619,71 +1442,187 @@ void RetroshareDirModel::getFilePaths(const QModelIndexList &list, std::list& keywords,uint32_t& found) +void RetroshareDirModel::filterItems(const std::list& keywords, uint32_t& found) { - FileSearchFlags flags = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; + FileSearchFlags flags = RemoteMode ? RS_FILE_HINTS_REMOTE : RS_FILE_HINTS_LOCAL; - std::list result_list ; - found = 0 ; + 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) ; + 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()) + return ; - 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(); - mFilteredPointers.clear(); + // Mark matching pointers and their hierarchy as visible + for(auto it(result_list.begin()); it != result_list.end(); ++it) + { + DirDetails& det(*it) ; + void *p = det.ref ; + mFilteredPointers.insert(p) ; + ++found ; -#ifdef RDM_SEARCH_DEBUG - std::cerr << "Found this result: " << std::endl; -#endif + while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_EXTRA_FILE || det.type == DIR_TYPE_DIR) + { + p = det.parent ; + if (!rsFiles->RequestDirDetails(p, det, flags)) + break; - // 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_EXTRA_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 - } -#ifdef RDM_SEARCH_DEBUG - std::cerr << mFilteredPointers.size() << " pointers in filter set." << std::endl; -#endif + mFilteredPointers.insert(p) ; + } + } + + // CRITICAL: Removed update() call to prevent full model reset and collapse } +QVariant TreeStyle_RDM::displayRole(const DirDetails& details, int coln) const +{ + if (details.type == DIR_TYPE_PERSON) /* Person */ + { + switch(coln) + { + case REMOTEDIRMODEL_COLUMN_NAME: + { + QString res ; + if(RemoteMode) + res = QString::fromUtf8(rsPeers->getPeerName(details.id).c_str()); + else if(details.id == rsPeers->getOwnId()) + res = tr("My files"); + else + res = tr("Temporary shared files"); + return res ; + } + break; + + case REMOTEDIRMODEL_COLUMN_FILENB: + { + SharedDirStats stats ; + if(RemoteMode) + rsFiles->getSharedDirStatistics(details.id,stats) ; + else if(details.id == rsPeers->getOwnId()) + rsFiles->getSharedDirStatistics(rsPeers->getOwnId(),stats) ; + else + stats.total_number_of_files = details.children.size(); + + if(stats.total_number_of_files > 0) + { + if (stats.total_number_of_files > 1) + return QString::number(stats.total_number_of_files) + " " + tr("Files"); + else + return QString::number(stats.total_number_of_files) + " " + tr("File"); + } + return tr("Empty"); + } + break; + + case REMOTEDIRMODEL_COLUMN_SIZE: + { + SharedDirStats stats ; + if(RemoteMode) + rsFiles->getSharedDirStatistics(details.id,stats) ; + else if(details.id == rsPeers->getOwnId()) + rsFiles->getSharedDirStatistics(rsPeers->getOwnId(),stats) ; + else + return QString(); + + if(stats.total_shared_size > 0) + return misc::friendlyUnit(stats.total_shared_size) ; + return QString(); + } + break; + + case REMOTEDIRMODEL_COLUMN_AGE: + { + if(!isNewerThanEpoque(details.max_mtime)) + return QString(); + else if(details.id != rsPeers->getOwnId()) + return QString(); + else + return misc::timeRelativeToNow(details.max_mtime); + } + break; + + case REMOTEDIRMODEL_COLUMN_UPLOADED: + { + if(!RemoteMode && details.id == rsPeers->getOwnId()) + { + uint64_t n = rsFiles->getCumulativeUploadNum(); + if(n) + return QString(misc::friendlyUnit(rsFiles->getCumulativeUploadAll()) + QString(" - %1 files").arg(n)); + else + return QString("-"); + } + return QString(); + } + break; + + default: return QString(); + } + } + else if (details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) /* File */ + { + switch(coln) + { + case REMOTEDIRMODEL_COLUMN_NAME: return QString::fromUtf8(details.name.c_str()); + case REMOTEDIRMODEL_COLUMN_SIZE: return misc::friendlyUnit(details.size); + case REMOTEDIRMODEL_COLUMN_AGE: + { + if(details.type == DIR_TYPE_FILE) + return misc::timeRelativeToNow(details.max_mtime); + else if(details.type == DIR_TYPE_EXTRA_FILE) + { + FileInfo fi; + if (rsFiles->FileDetails(details.hash, RS_FILE_HINTS_EXTRA , fi)) + return misc::timeRelativeToNow((rstime_t)fi.age-(30 * 3600 * 24)); + return QString(); + } + return QString(); + } + case REMOTEDIRMODEL_COLUMN_UPLOADED: + { + uint64_t x = rsFiles->getCumulativeUpload(details.hash); + return x ? misc::friendlyUnit(x) : QString(); + } + break; + default: return QVariant(); + } + } + else if (details.type == DIR_TYPE_DIR) /* Directory */ + { + switch(coln) + { + case REMOTEDIRMODEL_COLUMN_NAME: return QString::fromUtf8(details.name.c_str()); + case REMOTEDIRMODEL_COLUMN_FILENB: + return QString::number(details.children.size()) + " " + (details.children.size() > 1 ? tr("Files") : tr("File")); + case REMOTEDIRMODEL_COLUMN_SIZE: return misc::friendlyUnit(details.size); + case REMOTEDIRMODEL_COLUMN_AGE: return misc::timeRelativeToNow(details.max_mtime); + case REMOTEDIRMODEL_COLUMN_FRIEND_ACCESS: return getFlagsString(details.flags); + case REMOTEDIRMODEL_COLUMN_WN_VISU_DIR: return getGroupsString(details.flags,details.parent_groups) ; + case REMOTEDIRMODEL_COLUMN_UPLOADED: + { + QString path = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); + auto it = m_folderUploadTotals.find(path); + return (it != m_folderUploadTotals.end() && it.value() > 0) ? misc::friendlyUnit(it.value()) : ""; + } + break; + default: return QVariant(); + } + } + return QVariant(); +} /* Drag and Drop Functionality */ QMimeData * RetroshareDirModel::mimeData ( const QModelIndexList & indexes ) const From d2abef8de18da88f7e3a2d819f76b3918ecf18c9 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Fri, 26 Dec 2025 22:42:13 +0100 Subject: [PATCH 06/10] Fix weird behaviour when unchecking "Popular file" box --- .../gui/FileTransfer/SharedFilesDialog.cpp | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index e90a5c139..e62fb17ec 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -342,22 +342,28 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent) /** * Constructor for LocalSharedFilesDialog. + * Sets the new "Popular files" label and a more descriptive tooltip. */ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent) : SharedFilesDialog(false,parent) { - // Updated label to "Popular files" as requested + // Label updated to "Popular files" uploadedOnly_CB = new QCheckBox(tr("Popular files"), this); - uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been uploaded by others")); + // Updated tooltip to be more descriptive of the "Popularity" (upload activity) + uploadedOnly_CB->setToolTip(tr("Show only files and folders that have been successfully uploaded to other peers")); + + // Positioning the checkbox next to the view selector int cbIndex = ui.horizontalLayout_2->indexOf(ui.viewType_CB); ui.horizontalLayout_2->insertWidget(cbIndex + 1, uploadedOnly_CB); connect(uploadedOnly_CB, SIGNAL(toggled(bool)), this, SLOT(filterUploadedOnlyToggled(bool))); + // Ensure proper columns are visible for local sharing ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ; ui.downloadButton->hide() ; + // Load user settings and apply current view processSettings(true); changeCurrentViewModel(ui.viewType_CB->currentIndex()) ; @@ -1626,20 +1632,34 @@ void SharedFilesDialog::startFilter() FilterItems(); } +/** + * Handles the "Popular files" checkbox logic. + * Correctly manages tree expansion and returns to a clean state when unchecked. + */ void SharedFilesDialog::filterUploadedOnlyToggled(bool checked) { - /** FIX: Clear selection before filtering to avoid SIGSEGV in parent calculations */ + /** FIX: Clear selection before filtering to avoid potential proxy index crashes */ if (ui.dirTreeView->selectionModel()) { ui.dirTreeView->selectionModel()->clear(); } - // Apply the boolean filter to both proxies - tree_proxyModel->setUploadedOnly(checked); - flat_proxyModel->setUploadedOnly(checked); + // Apply the boolean filter to the proxy models + if (tree_proxyModel) tree_proxyModel->setUploadedOnly(checked); + if (flat_proxyModel) flat_proxyModel->setUploadedOnly(checked); - // Expand to show results in tree view - if(checked && ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { - expandAll(); + // Notify the views that the filter state has changed to trigger a redraw + if (tree_proxyModel) tree_proxyModel->invalidate(); + if (flat_proxyModel) flat_proxyModel->invalidate(); + + // Handle Tree View specific behavior: Expand on check, Collapse on uncheck + if(ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { + if (checked) { + // Reveal all popular files found in subdirectories + expandAll(); + } else { + // Return to a clean base state showing only root directories + ui.dirTreeView->collapseAll(); + } } } From cf43068d930c741d4ee8e373598b85b16afe88d5 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 30 Dec 2025 13:14:27 +0100 Subject: [PATCH 07/10] fix temporary shared files not being displayed in tree view when popular files box is checked --- .../gui/FileTransfer/SharedFilesDialog.cpp | 66 +++++++++++++----- .../src/gui/FileTransfer/SharedFilesDialog.h | 2 +- retroshare-gui/src/gui/RemoteDirModel.cpp | 67 ++++++++++++------- 3 files changed, 92 insertions(+), 43 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index e62fb17ec..99593ce7e 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -152,18 +152,29 @@ public: } protected: + + // MODIFICATION 1: Update the filter to allow folders (Person/Dir) to stay visible virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - // Custom check for "Uploaded only" filter if (m_uploadedOnly) { - // Retrieve data from column 6 + // Get the type of the current row to avoid filtering out parent folders + QModelIndex typeIdx = sourceModel()->index(source_row, 0, source_parent); + int type = m_dirModel->getType(typeIdx); + + // ALWAYS accept Person (Temporary shared files) and Directories + // so the filter can look at the files inside them. + if (type == DIR_TYPE_PERSON || type == DIR_TYPE_DIR) { + return true; + } + + // For files, check the Uploaded column (column 6) QModelIndex uploadIdx = sourceModel()->index(source_row, SHARED_FILES_DIALOG_COLUMN_UPLOADED, source_parent); QString uploadStr = sourceModel()->data(uploadIdx, Qt::DisplayRole).toString(); - // If empty or "-", treat as 0 and filter row out + if (uploadStr.isEmpty() || uploadStr == "-") return false; } - // Standard RetroShare logic for search/keywords + // Standard search logic return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } @@ -1093,9 +1104,10 @@ void SharedFilesDialog::restoreExpandedPathsAndSelection(const std::setblockSignals(false) ; } +// MODIFICATION 3: Update the main expand loop void SharedFilesDialog::expandAll() { - if(ui.dirTreeView->model() == NULL || ui.dirTreeView->model() == flat_proxyModel) // this method causes infinite loops on flat models + if(ui.dirTreeView->model() == NULL || ui.dirTreeView->model() == flat_proxyModel) // this method causes infinite loops on flat models return ; ui.dirTreeView->blockSignals(true) ; @@ -1103,27 +1115,45 @@ void SharedFilesDialog::expandAll() #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)); + // Call the new smart recursive function for each root node + recursExpandAll(ui.dirTreeView->model()->index(row, 0)); } ui.dirTreeView->blockSignals(false) ; - } -void SharedFilesDialog::recursExpandAll(const QModelIndex& index) +// MODIFICATION 2: Smart recursive expansion that only opens folders leading to visible files +bool SharedFilesDialog::recursExpandAll(const QModelIndex& index) { - ui.dirTreeView->setExpanded(index,true) ; + // Get the type of the current item (need to map to source model first) + int type = model->getType(proxyModel->mapToSource(index)); - for(int row=0;rowmodel()->rowCount(index);++row) - { - QModelIndex idx(ui.dirTreeView->model()->index(row,0,index)) ; - - if(ui.dirTreeView->model()->rowCount(idx) > 0) - recursExpandAll(idx) ; + // If it's a file, it means it's visible (otherwise it wouldn't be in the Proxy Model) + // We return true to tell the parent folder to expand. + if (type == DIR_TYPE_FILE || type == DIR_TYPE_EXTRA_FILE) { + return true; } + + // If it's a folder or person node, check its children + bool hasVisibleFileChild = false; + int childCount = ui.dirTreeView->model()->rowCount(index); + + for(int row = 0; row < childCount; ++row) { + QModelIndex childIdx = ui.dirTreeView->model()->index(row, 0, index); + if (recursExpandAll(childIdx)) { + hasVisibleFileChild = true; + } + } + + // Only expand this folder if at least one child is a visible file + if (hasVisibleFileChild) { + ui.dirTreeView->setExpanded(index, true); + } + + return hasVisibleFileChild; } void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const std::string& path, @@ -1651,10 +1681,10 @@ void SharedFilesDialog::filterUploadedOnlyToggled(bool checked) if (tree_proxyModel) tree_proxyModel->invalidate(); if (flat_proxyModel) flat_proxyModel->invalidate(); - // Handle Tree View specific behavior: Expand on check, Collapse on uncheck + // MODIFICATION 5: Handle Tree View specific behavior: Expand on check, Collapse on uncheck if(ui.viewType_CB->currentIndex() == VIEW_TYPE_TREE) { if (checked) { - // Reveal all popular files found in subdirectories + // Reveal all popular files found in subdirectories (including extra files) expandAll(); } else { // Return to a clean base state showing only root directories diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index 2012dd21b..dc791b324 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -105,7 +105,7 @@ 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); + bool recursExpandAll(const QModelIndex& index); void expandAll(); protected: diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 523605756..60661813b 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -341,7 +341,7 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const /* else PERSON/DIR*/ #ifdef RDM_DEBUG - std::cerr << "lookup PER/DIR #" << details.size; + std::cerr << "lookup PER/DIR #" << details.size; std::cerr << std::endl; #endif if ((details.type == DIR_TYPE_ROOT) && !_showEmpty && RemoteMode) @@ -357,8 +357,23 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const } return _parentRow.size(); } - return details.children.size(); + + // MODIFICATION 3: Count filtered children correctly + if (!mFilteredPointers.empty()) + { + int visibleCount = 0; + for(uint32_t i = 0; i < details.children.size(); ++i) + { + // Check if this specific child pointer is in the visible set + if (mFilteredPointers.find(details.children[i].ref) != mFilteredPointers.end()) + visibleCount++; + } + return visibleCount; + } + + return details.children.size(); } + int FlatStyle_RDM::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); @@ -1475,14 +1490,21 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin mFilteredPointers.insert(p) ; ++found ; - while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_EXTRA_FILE || det.type == DIR_TYPE_DIR) - { - p = det.parent ; - if (!rsFiles->RequestDirDetails(p, det, flags)) - break; + // MODIFICATION 2: Include DIR_TYPE_PERSON in the parent visibility loop + while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_EXTRA_FILE || + det.type == DIR_TYPE_DIR || det.type == DIR_TYPE_PERSON) + { + p = det.parent; + if (p == NULL) break; - mFilteredPointers.insert(p) ; - } + if (!rsFiles->RequestDirDetails(p, det, flags)) + break; + + mFilteredPointers.insert(p); // Mark parent node as visible + + // If we reach the Person node (root of extra list), stop climbing + if (det.type == DIR_TYPE_PERSON) break; + } } // CRITICAL: Removed update() call to prevent full model reset and collapse @@ -1507,28 +1529,25 @@ QVariant TreeStyle_RDM::displayRole(const DirDetails& details, int coln) const } break; - case REMOTEDIRMODEL_COLUMN_FILENB: + case REMOTEDIRMODEL_COLUMN_FILENB: { - SharedDirStats stats ; + SharedDirStats stats; if(RemoteMode) - rsFiles->getSharedDirStatistics(details.id,stats) ; + rsFiles->getSharedDirStatistics(details.id, stats) ; else if(details.id == rsPeers->getOwnId()) - rsFiles->getSharedDirStatistics(rsPeers->getOwnId(),stats) ; - else - stats.total_number_of_files = details.children.size(); - - if(stats.total_number_of_files > 0) - { - if (stats.total_number_of_files > 1) - return QString::number(stats.total_number_of_files) + " " + tr("Files"); - else - return QString::number(stats.total_number_of_files) + " " + tr("File"); + rsFiles->getSharedDirStatistics(rsPeers->getOwnId(), stats) ; + else { + // MODIFICATION 4: Specific handling for "Temporary shared files" node. + // Return the count of children directly from the DirDetails structure. + uint32_t nb = details.children.size(); + if(nb > 1) return QString::number(nb) + " " + tr("Files"); + if(nb == 1) return QString::number(nb) + " " + tr("File"); + return tr("Empty"); } - return tr("Empty"); } break; - case REMOTEDIRMODEL_COLUMN_SIZE: + case REMOTEDIRMODEL_COLUMN_SIZE: { SharedDirStats stats ; if(RemoteMode) From 96b434935bf37b6eafbb369f35e52a683e59b8c2 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 30 Dec 2025 14:24:26 +0100 Subject: [PATCH 08/10] more fix to correctly display relevant dirs and files when the box is checked --- .../gui/FileTransfer/SharedFilesDialog.cpp | 55 +++++-------- .../src/gui/FileTransfer/SharedFilesDialog.h | 1 + retroshare-gui/src/gui/RemoteDirModel.cpp | 81 +++++++++++++++---- retroshare-gui/src/gui/RemoteDirModel.h | 8 ++ 4 files changed, 95 insertions(+), 50 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 99593ce7e..2aa316617 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -153,28 +153,23 @@ public: protected: - // MODIFICATION 1: Update the filter to allow folders (Person/Dir) to stay visible + // MODIFICATION F: Strict filtering logic for the Proxy Model. + // This function decides if a row should be displayed or hidden. virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (m_uploadedOnly) { - // Get the type of the current row to avoid filtering out parent folders - QModelIndex typeIdx = sourceModel()->index(source_row, 0, source_parent); - int type = m_dirModel->getType(typeIdx); + // Get the index in the source model to access the internal pointer (ref) + QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent); + void *ref = source_index.internalPointer(); - // ALWAYS accept Person (Temporary shared files) and Directories - // so the filter can look at the files inside them. - if (type == DIR_TYPE_PERSON || type == DIR_TYPE_DIR) { - return true; + // MODIFICATION F: Check if this specific node or its descendants have cumulative uploads. + // If the branch has 0 uploads, we reject the row (return false). + if (!m_dirModel->hasUploads(ref)) { + return false; } - - // For files, check the Uploaded column (column 6) - QModelIndex uploadIdx = sourceModel()->index(source_row, SHARED_FILES_DIALOG_COLUMN_UPLOADED, source_parent); - QString uploadStr = sourceModel()->data(uploadIdx, Qt::DisplayRole).toString(); - - if (uploadStr.isEmpty() || uploadStr == "-") return false; } - // Standard search logic + // Standard logic for text search keywords return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } @@ -1125,35 +1120,27 @@ void SharedFilesDialog::expandAll() ui.dirTreeView->blockSignals(false) ; } -// MODIFICATION 2: Smart recursive expansion that only opens folders leading to visible files +// MODIFICATION G: Recursive expansion that only opens folders if they have visible children bool SharedFilesDialog::recursExpandAll(const QModelIndex& index) { - // Get the type of the current item (need to map to source model first) - int type = model->getType(proxyModel->mapToSource(index)); - - // If it's a file, it means it's visible (otherwise it wouldn't be in the Proxy Model) - // We return true to tell the parent folder to expand. - if (type == DIR_TYPE_FILE || type == DIR_TYPE_EXTRA_FILE) { - return true; - } - - // If it's a folder or person node, check its children - bool hasVisibleFileChild = false; + bool hasVisibleChild = false; int childCount = ui.dirTreeView->model()->rowCount(index); - + + // First, check all children recursively for(int row = 0; row < childCount; ++row) { - QModelIndex childIdx = ui.dirTreeView->model()->index(row, 0, index); - if (recursExpandAll(childIdx)) { - hasVisibleFileChild = true; + if (recursExpandAll(ui.dirTreeView->model()->index(row, 0, index))) { + hasVisibleChild = true; } } - // Only expand this folder if at least one child is a visible file - if (hasVisibleFileChild) { + // If this is a folder/person and it has visible children in the proxy, expand it + if (hasVisibleChild) { ui.dirTreeView->setExpanded(index, true); } - return hasVisibleFileChild; + // Return true if this node is a file (leaf) or a folder with visible children + int type = model->getType(proxyModel->mapToSource(index)); + return (type == DIR_TYPE_FILE || type == DIR_TYPE_EXTRA_FILE || hasVisibleChild); } void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const std::string& path, diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h index dc791b324..7d41287e6 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h @@ -105,6 +105,7 @@ 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) ; +// MODIFICATION E: Signature change to return bool (found a child to expand) bool recursExpandAll(const QModelIndex& index); void expandAll(); diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 60661813b..8f4e9a6dd 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -181,26 +181,36 @@ void TreeStyle_RDM::recalculateDirectoryTotals() if(uploaded > 0) { processedFiles++; - // details.path for a file is the directory containing it. - QString currentPath = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); - - // Iterate up the directory tree - while(!currentPath.isEmpty() && currentPath != ".") + + // MODIFICATION C: Track uploads for Extra Files separately using a special key. + // Normal files climb the directory tree, but Extra Files use a virtual root. + if (details.type == DIR_TYPE_EXTRA_FILE) { - m_folderUploadTotals[currentPath] += uploaded; + m_folderUploadTotals["!!RS_EXTRA_FILES_ROOT!!"] += uploaded; + } + else + { + // details.path for a file is the directory containing it. + QString currentPath = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); - // Get parent directory using QFileInfo logic - QString parent = QFileInfo(currentPath).path(); - - // Break if we reached root or top (QFileInfo returns path itself if no parent) - if(parent == currentPath || parent.isEmpty()) { - break; + // Iterate up the directory tree to add this file's upload total to all parent folders + while(!currentPath.isEmpty() && currentPath != ".") + { + m_folderUploadTotals[currentPath] += uploaded; + + // Get parent directory using QFileInfo logic + QString parent = QFileInfo(currentPath).path(); + + // Break if we reached root or top (QFileInfo returns path itself if no parent) + if(parent == currentPath || parent.isEmpty()) { + break; + } + currentPath = parent; } - currentPath = parent; - } - } + } // End of else (Normal Files) + } // End of if (uploaded > 0) } - // Included DIR_TYPE_PERSON to allow recursion into root "My Files" + // Included DIR_TYPE_PERSON to allow recursion into root "My Files" and "Temporary shared files" else if(details.type == DIR_TYPE_DIR || details.type == DIR_TYPE_ROOT || details.type == DIR_TYPE_PERSON) { for(const auto& child : details.children) @@ -214,6 +224,31 @@ void TreeStyle_RDM::recalculateDirectoryTotals() // std::cerr << "UPLOAD_DBG: --- Finished Recalc. Processed " << processedFiles << " active files. Map size: " << m_folderUploadTotals.size() << " ---" << std::endl; } +// MODIFICATION D: Check if a specific node or any of its descendants have uploads +bool TreeStyle_RDM::hasUploads(void *ref) const +{ + DirDetails details; + if (!requestDirDetails(ref, RemoteMode, details)) return false; + + // For files, check their individual cumulative upload + if (details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) { + return rsFiles->getCumulativeUpload(details.hash) > 0; + } + + // For the Extra Files root person node + if (details.type == DIR_TYPE_PERSON && details.id != rsPeers->getOwnId()) { + return m_folderUploadTotals.value("!!RS_EXTRA_FILES_ROOT!!", 0) > 0; + } + + // For standard directories, check the pre-calculated totals map + if (details.type == DIR_TYPE_DIR) { + QString path = QDir::cleanPath(QString::fromUtf8(details.path.c_str())); + return m_folderUploadTotals.value(path, 0) > 0; + } + + return true; // Keep "My Files" root or other root nodes visible +} + void TreeStyle_RDM::update() { // Recalculate totals before notifying view update @@ -1807,3 +1842,17 @@ void TreeStyle_RDM::showEmpty(const bool value) update(); } +// MODIFICATION I: Simple implementation for Flat View +bool FlatStyle_RDM::hasUploads(void *ref) const +{ + DirDetails details; + if (!requestDirDetails(ref, RemoteMode, details)) return false; + + // In Flat View, we only care if the file itself has been uploaded + if (details.type == DIR_TYPE_FILE || details.type == DIR_TYPE_EXTRA_FILE) { + return rsFiles->getCumulativeUpload(details.hash) > 0; + } + + return false; +} + diff --git a/retroshare-gui/src/gui/RemoteDirModel.h b/retroshare-gui/src/gui/RemoteDirModel.h index 37d731fee..5f81d5e8f 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.h +++ b/retroshare-gui/src/gui/RemoteDirModel.h @@ -81,6 +81,9 @@ class RetroshareDirModel : public QAbstractItemModel bool requestDirDetails(void *ref, bool remote,DirDetails& d) const; + // MODIFICATION A: Virtual method to check if a branch has cumulative uploads + virtual bool hasUploads(void *ref) const = 0; + virtual void update() {} virtual void updateRef(const QModelIndex&) const =0; @@ -205,6 +208,9 @@ class TreeStyle_RDM: public RetroshareDirModel virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + // MODIFICATION B: Implementation for Tree Style + virtual bool hasUploads(void *ref) const; + private slots: void showEmpty(const bool value); @@ -244,6 +250,8 @@ class FlatStyle_RDM: public RetroshareDirModel //Overloaded from RetroshareDirModel virtual void postMods();/* Callback from Core */ virtual void updateRef(const QModelIndex&) const {} + // MODIFICATION H: Implement hasUploads for Flat Style to fix compilation + virtual bool hasUploads(void *ref) const; virtual QVariant displayRole(const DirDetails&,int) const ; virtual QVariant sortRole(const QModelIndex&,const DirDetails&,int) const ; From 72cef3fe1f61c084fdd85935a543e18e60747616 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 30 Dec 2025 20:19:36 +0100 Subject: [PATCH 09/10] fix search not working in My files --- .../gui/FileTransfer/SharedFilesDialog.cpp | 7 ++++ retroshare-gui/src/gui/RemoteDirModel.cpp | 35 ++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index 2aa316617..ccd07ae7c 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -1286,6 +1286,10 @@ void SharedFilesDialog::FilterItems() if(text == "") { model->filterItems(std::list(), found) ; + + // MODIFICATION: Ensure the model and its totals are updated + model->update(); + if (tree_proxyModel) tree_proxyModel->invalidate(); if (flat_proxyModel) flat_proxyModel->invalidate(); return ; @@ -1302,6 +1306,9 @@ void SharedFilesDialog::FilterItems() model->filterItems(keywords, found) ; + // MODIFICATION: Refresh the model to reflect search results and updated row counts + model->update(); + if (tree_proxyModel) tree_proxyModel->invalidate(); if (flat_proxyModel) flat_proxyModel->invalidate(); diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 8f4e9a6dd..1979694b4 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -1502,6 +1502,8 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin if(keywords.empty()) { mFilteredPointers.clear(); + // MODIFICATION: Call update to refresh the view when the filter is cleared + update(); return ; } else if(keywords.size() > 1) @@ -1513,7 +1515,11 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin rsFiles->SearchKeywords(keywords, result_list, flags) ; if(result_list.empty()) + { + mFilteredPointers.clear(); + update(); return ; + } mFilteredPointers.clear(); @@ -1525,24 +1531,27 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin mFilteredPointers.insert(p) ; ++found ; - // MODIFICATION 2: Include DIR_TYPE_PERSON in the parent visibility loop - while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_EXTRA_FILE || - det.type == DIR_TYPE_DIR || det.type == DIR_TYPE_PERSON) - { - p = det.parent; - if (p == NULL) break; + // Climb the directory tree to mark all parents as visible + while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_EXTRA_FILE || + det.type == DIR_TYPE_DIR || det.type == DIR_TYPE_PERSON) + { + p = det.parent; + if (p == NULL) break; - if (!rsFiles->RequestDirDetails(p, det, flags)) - break; + if (!rsFiles->RequestDirDetails(p, det, flags)) + break; - mFilteredPointers.insert(p); // Mark parent node as visible + mFilteredPointers.insert(p); // Mark parent node as visible - // If we reach the Person node (root of extra list), stop climbing - if (det.type == DIR_TYPE_PERSON) break; - } + // If we reach the Person node (root of extra list), stop climbing + if (det.type == DIR_TYPE_PERSON) break; + } } - // CRITICAL: Removed update() call to prevent full model reset and collapse + // MODIFICATION: Restore the update call. + // This triggers beginResetModel/endResetModel which notifies the view + // that row counts and visibility have changed. + update(); } QVariant TreeStyle_RDM::displayRole(const DirDetails& details, int coln) const From ff701fb807cf92bef8da54524d9b8a61f22ee53a Mon Sep 17 00:00:00 2001 From: jolavillette Date: Tue, 30 Dec 2025 20:43:15 +0100 Subject: [PATCH 10/10] new attempt at fixing search in My files --- .../gui/FileTransfer/SharedFilesDialog.cpp | 7 ++--- retroshare-gui/src/gui/RemoteDirModel.cpp | 29 +++++-------------- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp index ccd07ae7c..c2e647366 100644 --- a/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp +++ b/retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp @@ -1286,10 +1286,8 @@ void SharedFilesDialog::FilterItems() if(text == "") { model->filterItems(std::list(), found) ; - - // MODIFICATION: Ensure the model and its totals are updated + // Ensure UI refresh model->update(); - if (tree_proxyModel) tree_proxyModel->invalidate(); if (flat_proxyModel) flat_proxyModel->invalidate(); return ; @@ -1304,9 +1302,10 @@ void SharedFilesDialog::FilterItems() for(auto it(lst.begin()); it != lst.end(); ++it) keywords.push_back((*it).toStdString()); + // Execute core search model->filterItems(keywords, found) ; - // MODIFICATION: Refresh the model to reflect search results and updated row counts + // MODIFICATION: Force refresh to apply FilterRole changes in the Proxy Model model->update(); if (tree_proxyModel) tree_proxyModel->invalidate(); diff --git a/retroshare-gui/src/gui/RemoteDirModel.cpp b/retroshare-gui/src/gui/RemoteDirModel.cpp index 1979694b4..a151d70d6 100644 --- a/retroshare-gui/src/gui/RemoteDirModel.cpp +++ b/retroshare-gui/src/gui/RemoteDirModel.cpp @@ -354,7 +354,6 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const if ((!ref) && RemoteMode) _parentRow.clear(); //Only clear it when asking root child number and in remote mode. - DirDetails details ; if (! requestDirDetails(ref, RemoteMode,details)) @@ -383,8 +382,6 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const { DirDetails childDetails; //Scan all children to know if they are empty. - //And save their real row index - //Prefer do like that than modify requestDirDetails with a new flag (rsFiles->RequestDirDetails) for(uint64_t i = 0; i < details.children.size(); ++i) { if (requestDirDetails(details.children[i].ref, RemoteMode,childDetails) && (childDetails.children.size() > 0)) @@ -393,19 +390,9 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const return _parentRow.size(); } - // MODIFICATION 3: Count filtered children correctly - if (!mFilteredPointers.empty()) - { - int visibleCount = 0; - for(uint32_t i = 0; i < details.children.size(); ++i) - { - // Check if this specific child pointer is in the visible set - if (mFilteredPointers.find(details.children[i].ref) != mFilteredPointers.end()) - visibleCount++; - } - return visibleCount; - } - + // MODIFICATION: Removed the manual mFilteredPointers count logic. + // QSortFilterProxyModel relies on the source model reporting all rows. + // Filtering here creates an index mismatch that results in an empty list. return details.children.size(); } @@ -1502,7 +1489,7 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin if(keywords.empty()) { mFilteredPointers.clear(); - // MODIFICATION: Call update to refresh the view when the filter is cleared + // MODIFICATION: Refresh the model to show all items again update(); return ; } @@ -1531,7 +1518,7 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin mFilteredPointers.insert(p) ; ++found ; - // Climb the directory tree to mark all parents as visible + // Climb the directory tree to mark all parents as visible for Tree View while(det.type == DIR_TYPE_FILE || det.type == DIR_TYPE_EXTRA_FILE || det.type == DIR_TYPE_DIR || det.type == DIR_TYPE_PERSON) { @@ -1543,14 +1530,12 @@ void RetroshareDirModel::filterItems(const std::list& keywords, uin mFilteredPointers.insert(p); // Mark parent node as visible - // If we reach the Person node (root of extra list), stop climbing + // If we reach the Person node (root of files), stop climbing if (det.type == DIR_TYPE_PERSON) break; } } - // MODIFICATION: Restore the update call. - // This triggers beginResetModel/endResetModel which notifies the view - // that row counts and visibility have changed. + // MODIFICATION: Restore the update call to notify the UI of search completion update(); }