mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-26 15:28:28 -05:00
828 lines
23 KiB
C++
828 lines
23 KiB
C++
/*******************************************************************************
|
|
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp *
|
|
* *
|
|
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
|
|
* *
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU 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 <QApplication>
|
|
#include <QFontMetrics>
|
|
#include <QModelIndex>
|
|
#include <QIcon>
|
|
|
|
#include "gui/common/FilesDefs.h"
|
|
#include "util/qtthreadsutils.h"
|
|
#include "util/HandleRichText.h"
|
|
#include "util/DateTime.h"
|
|
#include "retroshare/rsgxsflags.h"
|
|
#include "retroshare/rsgxschannels.h"
|
|
#include "retroshare/rsexpr.h"
|
|
|
|
#include "GxsChannelPostFilesModel.h"
|
|
|
|
//#define DEBUG_CHANNEL_MODEL
|
|
|
|
Q_DECLARE_METATYPE(RsGxsFile)
|
|
|
|
static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
|
|
|
|
RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent)
|
|
: QAbstractItemModel(parent)
|
|
{
|
|
initEmptyHierarchy();
|
|
|
|
mTimer = new QTimer;
|
|
connect(mTimer,SIGNAL(timeout()),this,SLOT(update()));
|
|
}
|
|
|
|
void RsGxsChannelPostFilesModel::initEmptyHierarchy()
|
|
{
|
|
preMods();
|
|
|
|
mFiles.clear();
|
|
mFilteredFiles.clear();
|
|
|
|
postMods();
|
|
}
|
|
|
|
void RsGxsChannelPostFilesModel::preMods()
|
|
{
|
|
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
|
|
|
|
beginResetModel();
|
|
}
|
|
void RsGxsChannelPostFilesModel::postMods()
|
|
{
|
|
endResetModel();
|
|
|
|
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
|
|
}
|
|
|
|
void RsGxsChannelPostFilesModel::update()
|
|
{
|
|
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
|
|
}
|
|
|
|
int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const
|
|
{
|
|
if(parent.column() > 0)
|
|
return 0;
|
|
|
|
if(mFilteredFiles.empty()) // security. Should never happen.
|
|
return 0;
|
|
|
|
if(!parent.isValid())
|
|
return mFilteredFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
|
|
|
|
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const
|
|
{
|
|
return COLUMN_FILES_NB_COLUMNS ;
|
|
}
|
|
|
|
// std::vector<std::pair<time_t,RsGxsMessageId> > RsGxsChannelPostsModel::getPostVersions(const RsGxsMessageId& mid) const
|
|
// {
|
|
// auto it = mPostVersions.find(mid);
|
|
//
|
|
// if(it != mPostVersions.end())
|
|
// return it->second;
|
|
// else
|
|
// return std::vector<std::pair<time_t,RsGxsMessageId> >();
|
|
// }
|
|
|
|
bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,RsGxsFile& fmpe) const
|
|
{
|
|
if(!i.isValid())
|
|
return true;
|
|
|
|
quintptr ref = i.internalId();
|
|
uint32_t entry = 0;
|
|
|
|
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size())
|
|
return false ;
|
|
|
|
fmpe = mFiles[mFilteredFiles[entry]];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool RsGxsChannelPostFilesModel::hasChildren(const QModelIndex &parent) const
|
|
{
|
|
if(!parent.isValid())
|
|
return true;
|
|
|
|
return false; // by default, no channel post has children
|
|
}
|
|
|
|
bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref)
|
|
{
|
|
// the pointer is formed the following way:
|
|
//
|
|
// [ 32 bits ]
|
|
//
|
|
// This means that the whole software has the following build-in limitation:
|
|
// * 4 B simultaenous posts. Should be enough !
|
|
|
|
ref = (intptr_t)(entry+1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry)
|
|
{
|
|
intptr_t val = (intptr_t)ref;
|
|
|
|
if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious
|
|
{
|
|
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl;
|
|
return false ;
|
|
}
|
|
if(val==0)
|
|
{
|
|
RsErr() << "(EE) trying to make a ChannelPostsFileModelIndex out of index 0." << std::endl;
|
|
return false;
|
|
}
|
|
entry = val-1;
|
|
|
|
return true;
|
|
}
|
|
|
|
QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const
|
|
{
|
|
if(row < 0 || column < 0 || column >= COLUMN_FILES_NB_COLUMNS)
|
|
return QModelIndex();
|
|
|
|
quintptr ref = getChildRef(parent.internalId(),row);
|
|
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
|
|
#endif
|
|
return createIndex(row,column,ref) ;
|
|
}
|
|
|
|
QModelIndex RsGxsChannelPostFilesModel::parent(const QModelIndex& index) const
|
|
{
|
|
if(!index.isValid())
|
|
return QModelIndex();
|
|
|
|
return QModelIndex(); // there's no hierarchy here. So nothing to do!
|
|
}
|
|
|
|
Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const
|
|
{
|
|
if (!index.isValid())
|
|
return 0;
|
|
|
|
if(index.column() == COLUMN_FILES_FILE)
|
|
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
|
else
|
|
return QAbstractItemModel::flags(index);
|
|
}
|
|
|
|
quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const
|
|
{
|
|
if (index < 0)
|
|
return 0;
|
|
|
|
if(ref == quintptr(0))
|
|
{
|
|
quintptr new_ref;
|
|
convertTabEntryToRefPointer(index,new_ref);
|
|
return new_ref;
|
|
}
|
|
else
|
|
return 0 ;
|
|
}
|
|
|
|
quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const
|
|
{
|
|
ChannelPostFilesModelIndex ref_entry;
|
|
|
|
if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredFiles.size())
|
|
return 0 ;
|
|
|
|
if(ref_entry == 0)
|
|
{
|
|
RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl;
|
|
row = 0;
|
|
}
|
|
else
|
|
row = ref_entry-1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const
|
|
{
|
|
uint32_t entry = 0 ;
|
|
|
|
if(ref == quintptr(0))
|
|
return rowCount()-1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole)
|
|
return QVariant();
|
|
|
|
switch(section)
|
|
{
|
|
case COLUMN_FILES_FILE: return QString("Status");
|
|
case COLUMN_FILES_SIZE: return QString("Size");
|
|
case COLUMN_FILES_NAME: return QString("File");
|
|
default:
|
|
return QString("[No data]");
|
|
}
|
|
}
|
|
|
|
QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
|
|
#endif
|
|
|
|
if(!index.isValid())
|
|
return QVariant();
|
|
|
|
switch(role)
|
|
{
|
|
case Qt::SizeHintRole: return sizeHintRole(index.column()) ;
|
|
case Qt::StatusTipRole:return QVariant();
|
|
default: break;
|
|
}
|
|
|
|
quintptr ref = (index.isValid())?index.internalId():0 ;
|
|
uint32_t entry = 0;
|
|
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
std::cerr << "data(" << index << ")" ;
|
|
#endif
|
|
|
|
if(!ref)
|
|
{
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
std::cerr << " [empty]" << std::endl;
|
|
#endif
|
|
return QVariant() ;
|
|
}
|
|
|
|
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size())
|
|
{
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
|
|
#endif
|
|
return QVariant() ;
|
|
}
|
|
|
|
const RsGxsFile& fmpe(mFiles[mFilteredFiles[entry]]);
|
|
|
|
#ifdef TODO
|
|
if(role == Qt::FontRole)
|
|
{
|
|
QFont font ;
|
|
font.setBold( (fmpe.mPostFlags & (ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN | ForumModelPostEntry::FLAG_POST_IS_PINNED)) || IS_MSG_UNREAD(fmpe.mMsgStatus));
|
|
return QVariant(font);
|
|
}
|
|
|
|
if(role == UnreadChildrenRole)
|
|
return bool(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN);
|
|
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
std::cerr << " [ok]" << std::endl;
|
|
#endif
|
|
#endif
|
|
|
|
switch(role)
|
|
{
|
|
case Qt::DisplayRole: return displayRole (fmpe,index.column()) ;
|
|
case Qt::UserRole: return userRole (fmpe,index.column()) ;
|
|
case SortRole: return sortRole (fmpe,index.column()) ;
|
|
#ifdef TODO
|
|
case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ;
|
|
case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ;
|
|
case Qt::TextColorRole: return textColorRole (fmpe,index.column()) ;
|
|
case Qt::BackgroundRole: return backgroundRole(fmpe,index.column()) ;
|
|
|
|
case FilterRole: return filterRole (fmpe,index.column()) ;
|
|
case ThreadPinnedRole: return pinnedRole (fmpe,index.column()) ;
|
|
case MissingRole: return missingRole (fmpe,index.column()) ;
|
|
case StatusRole: return statusRole (fmpe,index.column()) ;
|
|
#endif
|
|
default:
|
|
return QVariant();
|
|
}
|
|
}
|
|
|
|
void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& count)
|
|
{
|
|
preMods();
|
|
|
|
beginRemoveRows(QModelIndex(),0,rowCount()-1);
|
|
endRemoveRows();
|
|
|
|
if(strings.empty())
|
|
{
|
|
mFilteredFiles.clear();
|
|
for(int i=0;i<mFiles.size();++i)
|
|
mFilteredFiles.push_back(i);
|
|
}
|
|
else
|
|
{
|
|
mFilteredFiles.clear();
|
|
//mFilteredPosts.push_back(0);
|
|
|
|
for(int i=0;i<mFiles.size();++i)
|
|
{
|
|
bool passes_strings = true;
|
|
|
|
for(auto& s:strings)
|
|
passes_strings = passes_strings && QString::fromStdString(mFiles[i].mName).contains(s,Qt::CaseInsensitive);
|
|
|
|
if(passes_strings)
|
|
mFilteredFiles.push_back(i);
|
|
}
|
|
}
|
|
count = mFilteredFiles.size();
|
|
|
|
std::cerr << "After filtering: " << count << " posts remain." << std::endl;
|
|
|
|
beginInsertRows(QModelIndex(),0,rowCount()-1);
|
|
endInsertRows();
|
|
|
|
postMods();
|
|
}
|
|
|
|
class compareOperator
|
|
{
|
|
public:
|
|
compareOperator(int column,Qt::SortOrder order): col(column),ord(order) {}
|
|
|
|
bool operator()(const RsGxsFile& f1,const RsGxsFile& f2) const
|
|
{
|
|
switch(col)
|
|
{
|
|
default:
|
|
case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return (ord==Qt::AscendingOrder)?(f1.mName<f2.mName):(f1.mName>f2.mName);
|
|
case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSize<f2.mSize):(f1.mSize>f2.mSize);
|
|
case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE:
|
|
{
|
|
FileInfo fi1,fi2;
|
|
rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1);
|
|
rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2);
|
|
|
|
return (ord==Qt::AscendingOrder)?(fi1.transfered<fi2.transfered):(fi1.transfered>fi2.transfered);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
int col;
|
|
Qt::SortOrder ord;
|
|
};
|
|
|
|
void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order)
|
|
{
|
|
std::sort(mFiles.begin(),mFiles.end(),compareOperator(column,order));
|
|
|
|
update();
|
|
}
|
|
|
|
#ifdef TODO
|
|
QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*column*/) const
|
|
{
|
|
if( (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING))
|
|
return QVariant(mTextColorMissing);
|
|
|
|
if(IS_MSG_UNREAD(fmpe.mMsgStatus) || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED))
|
|
return QVariant(mTextColorUnread);
|
|
else
|
|
return QVariant(mTextColorRead);
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant RsGxsForumModel::statusRole(const ForumModelPostEntry& fmpe,int column) const
|
|
{
|
|
if(column != COLUMN_THREAD_DATA)
|
|
return QVariant();
|
|
|
|
return QVariant(fmpe.mMsgStatus);
|
|
}
|
|
|
|
uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column,const QStringList& strings)
|
|
{
|
|
QString s ;
|
|
uint32_t count = 0;
|
|
|
|
switch(column)
|
|
{
|
|
default:
|
|
case COLUMN_THREAD_DATE:
|
|
case COLUMN_THREAD_TITLE: s = displayRole(mPosts[i],column).toString();
|
|
break;
|
|
case COLUMN_THREAD_AUTHOR:
|
|
{
|
|
QString comment ;
|
|
QList<QIcon> icons;
|
|
|
|
GxsIdDetails::MakeIdDesc(mPosts[i].mAuthorId, false,s, icons, comment,GxsIdDetails::ICON_TYPE_NONE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(!strings.empty())
|
|
{
|
|
mPosts[i].mPostFlags &= ~(ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER);
|
|
|
|
for(auto iter(strings.begin()); iter != strings.end(); ++iter)
|
|
if(s.contains(*iter,Qt::CaseInsensitive))
|
|
{
|
|
mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER;
|
|
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER |ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER;
|
|
count++;
|
|
}
|
|
|
|
for(uint32_t j=0;j<mPosts[i].mChildren.size();++j)
|
|
{
|
|
uint32_t tmp = recursUpdateFilterStatus(mPosts[i].mChildren[j],column,strings);
|
|
count += tmp;
|
|
|
|
if(tmp > 0)
|
|
mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
void RsGxsForumModel::setFilter(int column,const QStringList& strings,uint32_t& count)
|
|
{
|
|
preMods();
|
|
|
|
if(!strings.empty())
|
|
{
|
|
count = recursUpdateFilterStatus(ForumModelIndex(0),column,strings);
|
|
mFilteringEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
count=0;
|
|
mFilteringEnabled = false;
|
|
}
|
|
|
|
postMods();
|
|
}
|
|
|
|
QVariant RsGxsForumModel::missingRole(const ForumModelPostEntry& fmpe,int /*column*/) const
|
|
{
|
|
if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)
|
|
return QVariant(true);
|
|
else
|
|
return QVariant(false);
|
|
}
|
|
|
|
QVariant RsGxsForumModel::toolTipRole(const ForumModelPostEntry& fmpe,int column) const
|
|
{
|
|
if(column == COLUMN_THREAD_DISTRIBUTION)
|
|
switch(fmpe.mReputationWarningLevel)
|
|
{
|
|
case 3: return QVariant(tr("Information for this identity is currently missing.")) ;
|
|
case 2: return QVariant(tr("You have banned this ID. The message will not be\ndisplayed nor forwarded to your friends.")) ;
|
|
case 1: return QVariant(tr("You have not set an opinion for this person,\n and your friends do not vote positively: Spam regulation \nprevents the message to be forwarded to your friends.")) ;
|
|
case 0: return QVariant(tr("Message will be forwarded to your friends.")) ;
|
|
default:
|
|
return QVariant("[ERROR: missing reputation level information - contact the developers]");
|
|
}
|
|
|
|
if(column == COLUMN_THREAD_AUTHOR)
|
|
{
|
|
QString str,comment ;
|
|
QList<QIcon> icons;
|
|
|
|
if(!GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR))
|
|
return QVariant();
|
|
|
|
int S = QFontMetricsF(QApplication::font()).height();
|
|
QImage pix( (*icons.begin()).pixmap(QSize(4*S,4*S)).toImage());
|
|
|
|
QString embeddedImage;
|
|
if(RsHtml::makeEmbeddedImage(pix.scaled(QSize(4*S,4*S), Qt::KeepAspectRatio, Qt::SmoothTransformation), embeddedImage, 8*S * 8*S))
|
|
comment = "<table><tr><td>" + embeddedImage + "</td><td>" + comment + "</td></table>";
|
|
|
|
return comment;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant RsGxsForumModel::pinnedRole(const ForumModelPostEntry& fmpe,int /*column*/) const
|
|
{
|
|
if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)
|
|
return QVariant(true);
|
|
else
|
|
return QVariant(false);
|
|
}
|
|
|
|
QVariant RsGxsForumModel::backgroundRole(const ForumModelPostEntry& fmpe,int /*column*/) const
|
|
{
|
|
if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)
|
|
return QVariant(QBrush(QColor(255,200,180)));
|
|
|
|
if(mFilteringEnabled && (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_PASSES_FILTER))
|
|
return QVariant(QBrush(QColor(255,240,210)));
|
|
|
|
return QVariant();
|
|
}
|
|
#endif
|
|
|
|
QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const
|
|
{
|
|
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
|
|
|
|
return QVariant( QSize(factor * 170, factor*14 ));
|
|
#ifdef TODO
|
|
switch(col)
|
|
{
|
|
default:
|
|
case COLUMN_THREAD_TITLE: return QVariant( QSize(factor * 170, factor*14 ));
|
|
case COLUMN_THREAD_DATE: return QVariant( QSize(factor * 75 , factor*14 ));
|
|
case COLUMN_THREAD_AUTHOR: return QVariant( QSize(factor * 75 , factor*14 ));
|
|
case COLUMN_THREAD_DISTRIBUTION: return QVariant( QSize(factor * 15 , factor*14 ));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
QVariant RsGxsChannelPostFilesModel::sortRole(const RsGxsFile& fmpe,int column) const
|
|
{
|
|
switch(column)
|
|
{
|
|
case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str()));
|
|
case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize));
|
|
case COLUMN_FILES_FILE:
|
|
{
|
|
FileInfo finfo;
|
|
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
|
|
return qulonglong(finfo.transfered);
|
|
|
|
return QVariant(qulonglong(fmpe.mSize));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return displayRole(fmpe,column);
|
|
}
|
|
}
|
|
|
|
QVariant RsGxsChannelPostFilesModel::displayRole(const RsGxsFile& fmpe,int col) const
|
|
{
|
|
switch(col)
|
|
{
|
|
case 0: return QString::fromUtf8(fmpe.mName.c_str());
|
|
case 1: return QString::number(fmpe.mSize);
|
|
case 2: {
|
|
FileInfo finfo;
|
|
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
|
|
return qulonglong(finfo.transfered);
|
|
else
|
|
return 0;
|
|
}
|
|
default:
|
|
return QString();
|
|
#ifdef TODO
|
|
case COLUMN_THREAD_TITLE: if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_REDACTED)
|
|
return QVariant(tr("[ ... Redacted message ... ]"));
|
|
else if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)
|
|
return QVariant(tr("[PINNED] ") + QString::fromUtf8(fmpe.mTitle.c_str()));
|
|
else
|
|
return QVariant(QString::fromUtf8(fmpe.mTitle.c_str()));
|
|
|
|
case COLUMN_THREAD_READ:return QVariant();
|
|
case COLUMN_THREAD_DATE:{
|
|
if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)
|
|
return QVariant(QString());
|
|
|
|
QDateTime qtime;
|
|
qtime.setTime_t(fmpe.mPublishTs);
|
|
|
|
return QVariant(DateTime::formatDateTime(qtime));
|
|
}
|
|
|
|
case COLUMN_THREAD_DISTRIBUTION:
|
|
case COLUMN_THREAD_AUTHOR:{
|
|
QString name;
|
|
RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString());
|
|
|
|
if(id.isNull())
|
|
return QVariant(tr("[Notification]"));
|
|
if(GxsIdTreeItemDelegate::computeName(id,name))
|
|
return name;
|
|
return QVariant(tr("[Unknown]"));
|
|
}
|
|
case COLUMN_THREAD_MSGID: return QVariant();
|
|
if (filterColumn == COLUMN_THREAD_CONTENT) {
|
|
// need content for filter
|
|
QTextDocument doc;
|
|
doc.setHtml(QString::fromUtf8(msg.mMsg.c_str()));
|
|
item->setText(COLUMN_THREAD_CONTENT, doc.toPlainText().replace(QString("\n"), QString(" ")));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
return QVariant("[ERROR]");
|
|
}
|
|
|
|
QVariant RsGxsChannelPostFilesModel::userRole(const RsGxsFile& fmpe,int col) const
|
|
{
|
|
switch(col)
|
|
{
|
|
default:
|
|
return QVariant::fromValue(fmpe);
|
|
}
|
|
}
|
|
|
|
#ifdef TODO
|
|
QVariant RsGxsForumModel::decorationRole(const ForumModelPostEntry& fmpe,int col) const
|
|
{
|
|
bool exist=false;
|
|
switch(col)
|
|
{
|
|
case COLUMN_THREAD_DISTRIBUTION:
|
|
return QVariant(fmpe.mReputationWarningLevel);
|
|
case COLUMN_THREAD_READ:
|
|
return QVariant(fmpe.mMsgStatus);
|
|
case COLUMN_THREAD_AUTHOR://Return icon as place holder.
|
|
return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.mAuthorId.toStdString()),QIcon(), exist);
|
|
}
|
|
return QVariant();
|
|
}
|
|
#endif
|
|
|
|
void RsGxsChannelPostFilesModel::clear()
|
|
{
|
|
preMods();
|
|
|
|
initEmptyHierarchy();
|
|
|
|
postMods();
|
|
emit channelLoaded();
|
|
}
|
|
|
|
void RsGxsChannelPostFilesModel::setFiles(const std::list<RsGxsFile>& files)
|
|
{
|
|
preMods();
|
|
|
|
beginRemoveRows(QModelIndex(),0,mFilteredFiles.size()-1);
|
|
endRemoveRows();
|
|
|
|
initEmptyHierarchy();
|
|
|
|
for(auto& file:files)
|
|
mFiles.push_back(file);
|
|
|
|
for(uint32_t i=0;i<mFiles.size();++i)
|
|
mFilteredFiles.push_back(i);
|
|
|
|
#ifdef TODO
|
|
recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below) ;
|
|
recursUpdateFilterStatus(0,0,QStringList());
|
|
#endif
|
|
|
|
#ifdef DEBUG_CHANNEL_MODEL
|
|
// debug_dump();
|
|
#endif
|
|
|
|
beginInsertRows(QModelIndex(),0,mFilteredFiles.size()-1);
|
|
endInsertRows();
|
|
|
|
postMods();
|
|
|
|
emit channelLoaded();
|
|
|
|
if(!files.empty())
|
|
mTimer->start(5000);
|
|
else
|
|
mTimer->stop();
|
|
}
|
|
|
|
#ifdef DEBUG_FORUMMODEL
|
|
QModelIndex RsGxsChannelPostFilesModel::getIndexOfFile(const RsFileHash& hash) const
|
|
{
|
|
// Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map.
|
|
|
|
for(uint32_t i=1;i<mFiles.size();++i)
|
|
if(mFiles[i].mHash == hash)
|
|
{
|
|
quintptr ref ;
|
|
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab
|
|
|
|
return createIndex(i-1,0,ref);
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
static void recursPrintModel(const std::vector<ForumModelPostEntry>& entries,ForumModelIndex index,int depth)
|
|
{
|
|
const ForumModelPostEntry& e(entries[index]);
|
|
|
|
QDateTime qtime;
|
|
qtime.setTime_t(e.mPublishTs);
|
|
|
|
std::cerr << std::string(depth*2,' ') << index << " : " << e.mAuthorId.toStdString() << " "
|
|
<< QString("%1").arg((uint32_t)e.mPostFlags,8,16,QChar('0')).toStdString() << " "
|
|
<< QString("%1").arg((uint32_t)e.mMsgStatus,8,16,QChar('0')).toStdString() << " "
|
|
<< qtime.toString().toStdString() << " \"" << e.mTitle << "\"" << std::endl;
|
|
|
|
for(uint32_t i=0;i<e.mChildren.size();++i)
|
|
recursPrintModel(entries,e.mChildren[i],depth+1);
|
|
}
|
|
|
|
void RsGxsForumModel::debug_dump()
|
|
{
|
|
std::cerr << "Model data dump:" << std::endl;
|
|
std::cerr << " Entries: " << mPosts.size() << std::endl;
|
|
|
|
// non recursive print
|
|
|
|
for(uint32_t i=0;i<mPosts.size();++i)
|
|
{
|
|
const ForumModelPostEntry& e(mPosts[i]);
|
|
|
|
std::cerr << " " << i << " : " << e.mMsgId << " (from " << e.mAuthorId.toStdString() << ") "
|
|
<< QString("%1").arg((uint32_t)e.mPostFlags,8,16,QChar('0')).toStdString() << " "
|
|
<< QString("%1").arg((uint32_t)e.mMsgStatus,8,16,QChar('0')).toStdString() << " ";
|
|
|
|
for(uint32_t i=0;i<e.mChildren.size();++i)
|
|
std::cerr << " " << e.mChildren[i] ;
|
|
|
|
QDateTime qtime;
|
|
qtime.setTime_t(e.mPublishTs);
|
|
|
|
std::cerr << " (" << e.mParent << ")";
|
|
std::cerr << " " << qtime.toString().toStdString() << " \"" << e.mTitle << "\"" << std::endl;
|
|
}
|
|
|
|
// recursive print
|
|
recursPrintModel(mPosts,ForumModelIndex(0),0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef TODO
|
|
void RsGxsForumModel::setAuthorOpinion(const QModelIndex& indx, RsOpinion op)
|
|
{
|
|
if(!indx.isValid())
|
|
return ;
|
|
|
|
void *ref = indx.internalPointer();
|
|
uint32_t entry = 0;
|
|
|
|
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size())
|
|
return ;
|
|
|
|
std::cerr << "Setting own opinion for author " << mPosts[entry].mAuthorId
|
|
<< " to " << static_cast<uint32_t>(op) << std::endl;
|
|
RsGxsId author_id = mPosts[entry].mAuthorId;
|
|
|
|
rsReputations->setOwnOpinion(author_id,op) ;
|
|
|
|
// update opinions and distribution flags. No need to re-load all posts.
|
|
|
|
for(uint32_t i=0;i<mPosts.size();++i)
|
|
if(mPosts[i].mAuthorId == author_id)
|
|
{
|
|
computeReputationLevel(mForumGroup.mMeta.mSignFlags,mPosts[i]);
|
|
|
|
// notify the widgets that the data has changed.
|
|
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(0,COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL));
|
|
}
|
|
}
|
|
#endif
|