mirror of
https://github.com/RetroShare/RetroShare.git
synced 2026-01-01 00:30:30 -05:00
Merge ff701fb807 into a17699e166
This commit is contained in:
commit
248b2e18a6
4 changed files with 623 additions and 399 deletions
|
|
@ -1,21 +1,21 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.cpp *
|
||||
* *
|
||||
* *
|
||||
* Copyright (c) 2009 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* *
|
||||
* 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 <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include "SharedFilesDialog.h"
|
||||
|
|
@ -52,9 +52,36 @@
|
|||
#include <QString>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTreeView>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <set>
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -107,7 +134,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 +146,33 @@ public:
|
|||
setDynamicSortFilter(false);
|
||||
}
|
||||
|
||||
void setUploadedOnly(bool val) {
|
||||
m_uploadedOnly = val;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// 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 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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Standard logic for text search keywords
|
||||
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 +187,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
|
||||
|
|
@ -172,16 +227,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)
|
||||
: 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<const RsEvent> event)
|
||||
|
|
@ -214,14 +269,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);
|
||||
|
|
@ -242,15 +304,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("_");
|
||||
|
|
@ -262,25 +319,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 );
|
||||
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*10 );
|
||||
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())) ;
|
||||
|
|
@ -292,17 +346,31 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent)
|
|||
connect(collOpenAct, SIGNAL(triggered()), this, SLOT(collOpen())) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for LocalSharedFilesDialog.
|
||||
* Sets the new "Popular files" label and a more descriptive tooltip.
|
||||
*/
|
||||
LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent)
|
||||
: SharedFilesDialog(false,parent)
|
||||
{
|
||||
// Hide columns after loading the settings
|
||||
// Label updated to "Popular files"
|
||||
uploadedOnly_CB = new QCheckBox(tr("Popular files"), this);
|
||||
|
||||
// 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 settings
|
||||
// Load user settings and apply current view
|
||||
processSettings(true);
|
||||
// Setup the current view model.
|
||||
//
|
||||
changeCurrentViewModel(ui.viewType_CB->currentIndex()) ;
|
||||
|
||||
connect(ui.addShares_PB, SIGNAL(clicked()), this, SLOT(addShares())) ;
|
||||
|
|
@ -314,7 +382,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()) ;
|
||||
}
|
||||
|
||||
|
|
@ -513,6 +580,8 @@ void LocalSharedFilesDialog::showProperColumns()
|
|||
ui.filterPatternLineEdit->show();
|
||||
#endif
|
||||
}
|
||||
// Column 6 must be visible for filtering to work
|
||||
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_UPLOADED, false);
|
||||
}
|
||||
void RemoteSharedFilesDialog::showProperColumns()
|
||||
{
|
||||
|
|
@ -1014,19 +1083,26 @@ void SharedFilesDialog::restoreExpandedPathsAndSelection(const std::set<std::str
|
|||
#ifdef DEBUG_SHARED_FILES_DIALOG
|
||||
std::cerr << "Restoring expanded items. " << std::endl;
|
||||
#endif
|
||||
|
||||
/** Use batch selection to avoid SIGSEGV in selectionModel */
|
||||
QItemSelection batchSelection;
|
||||
|
||||
for(int row = 0; row < ui.dirTreeView->model()->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) ;
|
||||
}
|
||||
|
||||
// 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) ;
|
||||
|
|
@ -1034,27 +1110,37 @@ 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 G: Recursive expansion that only opens folders if they have visible children
|
||||
bool SharedFilesDialog::recursExpandAll(const QModelIndex& index)
|
||||
{
|
||||
ui.dirTreeView->setExpanded(index,true) ;
|
||||
bool hasVisibleChild = false;
|
||||
int childCount = ui.dirTreeView->model()->rowCount(index);
|
||||
|
||||
for(int row=0;row<ui.dirTreeView->model()->rowCount(index);++row)
|
||||
{
|
||||
QModelIndex idx(ui.dirTreeView->model()->index(row,0,index)) ;
|
||||
|
||||
if(ui.dirTreeView->model()->rowCount(idx) > 0)
|
||||
recursExpandAll(idx) ;
|
||||
// First, check all children recursively
|
||||
for(int row = 0; row < childCount; ++row) {
|
||||
if (recursExpandAll(ui.dirTreeView->model()->index(row, 0, index))) {
|
||||
hasVisibleChild = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a folder/person and it has visible children in the proxy, expand it
|
||||
if (hasVisibleChild) {
|
||||
ui.dirTreeView->setExpanded(index, true);
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
|
@ -1102,15 +1188,17 @@ void SharedFilesDialog::recursSaveExpandedItems(const QModelIndex& index,const s
|
|||
void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, const std::string &path,
|
||||
const std::set<std::string>& exp,
|
||||
const std::set<std::string>& hid,
|
||||
const std::set<std::string> &sel)
|
||||
const std::set<std::string> &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();
|
||||
|
|
@ -1128,47 +1216,112 @@ void SharedFilesDialog::recursRestoreExpandedItems(const QModelIndex& index, con
|
|||
|
||||
for(int row=0;row<ui.dirTreeView->model()->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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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<std::string> expanded_indexes,selected_indexes,hidden_indexes;
|
||||
std::set<std::string> 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();
|
||||
|
||||
// 3. Re-enable sorting BEFORE restoring expansion to stabilize the view
|
||||
ui.dirTreeView->setSortingEnabled(true);
|
||||
|
||||
restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes) ;
|
||||
// 4. Re-apply the text filter
|
||||
if (ui.filterPatternLineEdit->text().isEmpty() == false) {
|
||||
FilterItems();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SHARED_FILES_DIALOG
|
||||
std::cerr << "****** updated directories! Re-enabling sorting ******" << std::endl;
|
||||
#endif
|
||||
// 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<std::string>(), found) ;
|
||||
// Ensure UI refresh
|
||||
model->update();
|
||||
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<std::string> keywords ;
|
||||
|
||||
for(auto it(lst.begin()); it != lst.end(); ++it)
|
||||
keywords.push_back((*it).toStdString());
|
||||
|
||||
// Execute core search
|
||||
model->filterItems(keywords, found) ;
|
||||
|
||||
// MODIFICATION: Force refresh to apply FilterRole changes in the Proxy Model
|
||||
model->update();
|
||||
|
||||
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:
|
||||
|
|
@ -1408,7 +1561,7 @@ void SharedFilesDialog::indicatorChanged(int index)
|
|||
else
|
||||
ui.dirTreeView->sortByColumn(SHARED_FILES_DIALOG_COLUMN_NAME, Qt::AscendingOrder);
|
||||
|
||||
updateDisplay() ;
|
||||
//updateDisplay() ;
|
||||
}
|
||||
|
||||
void SharedFilesDialog::onFilterTextEdited()
|
||||
|
|
@ -1502,6 +1655,37 @@ 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 potential proxy index crashes */
|
||||
if (ui.dirTreeView->selectionModel()) {
|
||||
ui.dirTreeView->selectionModel()->clear();
|
||||
}
|
||||
|
||||
// Apply the boolean filter to the proxy models
|
||||
if (tree_proxyModel) tree_proxyModel->setUploadedOnly(checked);
|
||||
if (flat_proxyModel) flat_proxyModel->setUploadedOnly(checked);
|
||||
|
||||
// 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();
|
||||
|
||||
// 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 (including extra files)
|
||||
expandAll();
|
||||
} else {
|
||||
// Return to a clean base state showing only root directories
|
||||
ui.dirTreeView->collapseAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SharedFilesDialog::updateDirTreeView()
|
||||
{
|
||||
if (model == flat_model)
|
||||
|
|
@ -1594,90 +1778,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<DirDetails> result_list ;
|
||||
uint32_t found = 0 ;
|
||||
|
||||
if(text == "")
|
||||
{
|
||||
model->filterItems(std::list<std::string>(),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<std::string> 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<DirDetails> files_info ;
|
||||
|
|
@ -1760,3 +1860,4 @@ void SharedFilesDialog::updateFontSize()
|
|||
ui.dirTreeView->setIconSize(QSize(iconHeight, iconHeight));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/FileTransfer/SharedFilesDialog.h *
|
||||
* *
|
||||
* *
|
||||
* Copyright (c) 2009 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* *
|
||||
* 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 <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef _SHAREDFILESDIALOG_H
|
||||
|
|
@ -29,9 +29,12 @@
|
|||
#include "util/RsProtectedTimer.h"
|
||||
|
||||
#include <set>
|
||||
#include <QCheckBox>
|
||||
#include <QItemSelection>
|
||||
|
||||
class RetroshareDirModel;
|
||||
class QSortFilterProxyModel;
|
||||
class SFDSortFilterProxyModel;
|
||||
|
||||
class SharedFilesDialog : public RsAutoUpdatePage
|
||||
{
|
||||
|
|
@ -83,6 +86,9 @@ private slots:
|
|||
void startFilter();
|
||||
|
||||
void updateDirTreeView();
|
||||
|
||||
/** Slot for the Uploaded Only checkbox filter */
|
||||
void filterUploadedOnlyToggled(bool checked);
|
||||
|
||||
public slots:
|
||||
void changeCurrentViewModel(int viewTypeIndex);
|
||||
|
|
@ -94,11 +100,13 @@ protected:
|
|||
Ui::SharedFilesDialog ui;
|
||||
virtual void processSettings(bool bLoad) = 0;
|
||||
|
||||
void recursRestoreExpandedItems(const QModelIndex& index, const std::string& path, const std::set<std::string>& exp, const std::set<std::string>& vis, const std::set<std::string>& sel);
|
||||
/** signature updated to support selection batching to prevent hangs/crashes */
|
||||
void recursRestoreExpandedItems(const QModelIndex& index, const std::string& path, const std::set<std::string>& exp, const std::set<std::string>& vis, const std::set<std::string>& sel, QItemSelection& batchSelection);
|
||||
void recursSaveExpandedItems(const QModelIndex& index, const std::string &path, std::set<std::string> &exp,std::set<std::string>& vis, std::set<std::string>& sel);
|
||||
void saveExpandedPathsAndSelection(std::set<std::string>& paths,std::set<std::string>& visible_indexes, std::set<std::string>& selected_indexes) ;
|
||||
void restoreExpandedPathsAndSelection(const std::set<std::string>& paths,const std::set<std::string>& visible_indexes, const std::set<std::string>& selected_indexes) ;
|
||||
void recursExpandAll(const QModelIndex& index);
|
||||
// MODIFICATION E: Signature change to return bool (found a child to expand)
|
||||
bool recursExpandAll(const QModelIndex& index);
|
||||
void expandAll();
|
||||
|
||||
protected:
|
||||
|
|
@ -136,8 +144,9 @@ protected:
|
|||
RetroshareDirModel *tree_model;
|
||||
RetroshareDirModel *flat_model;
|
||||
RetroshareDirModel *model;
|
||||
QSortFilterProxyModel *tree_proxyModel;
|
||||
QSortFilterProxyModel *flat_proxyModel;
|
||||
|
||||
SFDSortFilterProxyModel *tree_proxyModel;
|
||||
SFDSortFilterProxyModel *flat_proxyModel;
|
||||
QSortFilterProxyModel *proxyModel;
|
||||
|
||||
QString currentCommand;
|
||||
|
|
@ -148,6 +157,9 @@ protected:
|
|||
RsProtectedTimer* mFilterTimer;
|
||||
|
||||
RsEventsHandlerId_t mEventHandlerId ;
|
||||
|
||||
/** Checkbox to filter files with 0 upload */
|
||||
QCheckBox *uploadedOnly_CB;
|
||||
};
|
||||
|
||||
class LocalSharedFilesDialog : public SharedFilesDialog
|
||||
|
|
@ -208,4 +220,3 @@ class RemoteSharedFilesDialog : public SharedFilesDialog
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
/*******************************************************************************
|
||||
* gui/RemoteDirModel.cpp *
|
||||
* *
|
||||
* gui/RemoteDirModel.cpp *
|
||||
* *
|
||||
* Copyright (c) 2006 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* *
|
||||
* 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 <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include "RemoteDirModel.h"
|
||||
|
|
@ -42,6 +42,7 @@
|
|||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <time.h>
|
||||
#include <iostream> // 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,111 @@ 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<void*> 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++;
|
||||
|
||||
// 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["!!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()));
|
||||
|
||||
// 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;
|
||||
}
|
||||
} // End of else (Normal Files)
|
||||
} // End of if (uploaded > 0)
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
stack.push_back(child.ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// DEBUG: Summary
|
||||
// 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
|
||||
recalculateDirectoryTotals();
|
||||
|
||||
preMods() ;
|
||||
postMods() ;
|
||||
}
|
||||
|
|
@ -250,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))
|
||||
|
|
@ -272,15 +375,13 @@ 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)
|
||||
{
|
||||
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))
|
||||
|
|
@ -288,8 +389,13 @@ int TreeStyle_RDM::rowCount(const QModelIndex &parent) const
|
|||
}
|
||||
return _parentRow.size();
|
||||
}
|
||||
return details.children.size();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
int FlatStyle_RDM::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
|
@ -312,15 +418,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<RsNodeGroupId>& group_ids)
|
||||
{
|
||||
|
|
@ -480,161 +581,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());
|
||||
break;
|
||||
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 QVariant();
|
||||
case REMOTEDIRMODEL_COLUMN_WN_VISU_DIR:
|
||||
return getGroupsString(details.flags,details.parent_groups) ;
|
||||
case REMOTEDIRMODEL_COLUMN_UPLOADED:
|
||||
return "";
|
||||
|
||||
default:
|
||||
return tr("DIR");
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
} /* end of DisplayRole */
|
||||
|
||||
void FlatStyle_RDM::update()
|
||||
{
|
||||
if(_needs_update)
|
||||
|
|
@ -859,6 +805,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))
|
||||
|
|
@ -1524,71 +1479,198 @@ void RetroshareDirModel::getFilePaths(const QModelIndexList &list, std::list<std
|
|||
#endif
|
||||
}
|
||||
|
||||
void RetroshareDirModel::filterItems(const std::list<std::string>& keywords,uint32_t& found)
|
||||
void RetroshareDirModel::filterItems(const std::list<std::string>& 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<DirDetails> result_list ;
|
||||
found = 0 ;
|
||||
std::list<DirDetails> 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();
|
||||
// MODIFICATION: Refresh the model to show all items again
|
||||
update();
|
||||
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())
|
||||
{
|
||||
mFilteredPointers.clear();
|
||||
update();
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
p = det.parent;
|
||||
if (p == NULL) break;
|
||||
|
||||
// Then show only the ones we need
|
||||
if (!rsFiles->RequestDirDetails(p, det, flags))
|
||||
break;
|
||||
|
||||
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 ;
|
||||
mFilteredPointers.insert(p); // Mark parent node as visible
|
||||
|
||||
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
|
||||
// If we reach the Person node (root of files), stop climbing
|
||||
if (det.type == DIR_TYPE_PERSON) break;
|
||||
}
|
||||
}
|
||||
|
||||
// MODIFICATION: Restore the update call to notify the UI of search completion
|
||||
update();
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
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
|
||||
|
|
@ -1753,3 +1835,18 @@ void TreeStyle_RDM::showEmpty(const bool value)
|
|||
_showEmpty = 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
/*******************************************************************************
|
||||
* gui/RemoteDirModel.h *
|
||||
* *
|
||||
* *
|
||||
* Copyright (c) 2006 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* *
|
||||
* 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 <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef REMOTE_DIR_MODEL
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
#include <QAction>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
|
@ -79,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;
|
||||
|
||||
|
|
@ -203,12 +208,20 @@ 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);
|
||||
|
||||
private:
|
||||
QAction *_showEmptyAct;
|
||||
bool _showEmpty;
|
||||
|
||||
// Helper to calculate total upload per directory
|
||||
void recalculateDirectoryTotals();
|
||||
QHash<QString, uint64_t> m_folderUploadTotals;
|
||||
|
||||
protected:
|
||||
mutable std::vector<int> _parentRow ; // used to store the real parent row for non empty child
|
||||
};
|
||||
|
|
@ -237,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 ;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue