mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-13 08:29:32 -05:00
improved widgets. Added file list (with dump delegate), labels, etc
This commit is contained in:
parent
e0cf9768fc
commit
a5dd33e085
799
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
Normal file
799
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
Normal file
@ -0,0 +1,799 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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(mFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsGxsChannelPostFilesModel::initEmptyHierarchy(std::vector<RsGxsFile>& files)
|
||||||
|
{
|
||||||
|
preMods();
|
||||||
|
|
||||||
|
files.resize(1); // adds a sentinel item
|
||||||
|
files[0].mName = "Root sentinel post" ;
|
||||||
|
|
||||||
|
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(mFiles.size(),COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
void RsGxsChannelPostsModel::setSortMode(SortMode mode)
|
||||||
|
{
|
||||||
|
preMods();
|
||||||
|
|
||||||
|
mSortMode = mode;
|
||||||
|
|
||||||
|
postMods();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsGxsForumModel::initEmptyHierarchy(std::vector<ForumModelPostEntry>& posts)
|
||||||
|
{
|
||||||
|
preMods();
|
||||||
|
|
||||||
|
posts.resize(1); // adds a sentinel item
|
||||||
|
posts[0].mTitle = "Root sentinel post" ;
|
||||||
|
posts[0].mParent = 0;
|
||||||
|
|
||||||
|
postMods();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const
|
||||||
|
{
|
||||||
|
if(parent.column() > 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(mFiles.empty()) // security. Should never happen.
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(!parent.isValid())
|
||||||
|
return getChildrenCount(0);
|
||||||
|
|
||||||
|
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_THREAD_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[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;
|
||||||
|
|
||||||
|
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 ;
|
||||||
|
}
|
||||||
|
entry = quintptr(val);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if(row < 0 || column < 0 || column >= COLUMN_THREAD_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;
|
||||||
|
|
||||||
|
return QAbstractItemModel::flags(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const
|
||||||
|
{
|
||||||
|
if (index < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ChannelPostFilesModelIndex entry ;
|
||||||
|
|
||||||
|
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size())
|
||||||
|
return 0 ;
|
||||||
|
|
||||||
|
if(entry == 0)
|
||||||
|
{
|
||||||
|
quintptr new_ref;
|
||||||
|
convertTabEntryToRefPointer(index+1,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 >= mFiles.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(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size())
|
||||||
|
return 0 ;
|
||||||
|
|
||||||
|
if(entry == 0)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CHANNEL_MODEL
|
||||||
|
std::cerr << "Children count (flat mode): " << mFiles.size()-1 << std::endl;
|
||||||
|
#endif
|
||||||
|
return ((int)mFiles.size())-1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 >= mFiles.size())
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CHANNEL_MODEL
|
||||||
|
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
|
||||||
|
#endif
|
||||||
|
return QVariant() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RsGxsFile& fmpe(mFiles[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()) ;
|
||||||
|
#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()) ;
|
||||||
|
case SortRole: return sortRole (fmpe,index.column()) ;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant RsGxsForumModel::filterRole(const ForumModelPostEntry& fmpe,int /*column*/) const
|
||||||
|
{
|
||||||
|
if(!mFilteringEnabled || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER))
|
||||||
|
return QVariant(FilterString);
|
||||||
|
|
||||||
|
return QVariant(QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
QVariant RsGxsForumModel::authorRole(const ForumModelPostEntry& fmpe,int column) const
|
||||||
|
{
|
||||||
|
if(column == COLUMN_THREAD_DATA)
|
||||||
|
return QVariant(QString::fromStdString(fmpe.mAuthorId.toStdString()));
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant RsGxsForumModel::sortRole(const ForumModelPostEntry& fmpe,int column) const
|
||||||
|
{
|
||||||
|
switch(column)
|
||||||
|
{
|
||||||
|
case COLUMN_THREAD_DATE: if(mSortMode == SORT_MODE_PUBLISH_TS)
|
||||||
|
return QVariant(QString::number(fmpe.mPublishTs)); // we should probably have leading zeroes here
|
||||||
|
else
|
||||||
|
return QVariant(QString::number(fmpe.mMostRecentTsInThread)); // we should probably have leading zeroes here
|
||||||
|
|
||||||
|
case COLUMN_THREAD_READ: return QVariant((bool)IS_MSG_UNREAD(fmpe.mMsgStatus));
|
||||||
|
case COLUMN_THREAD_DISTRIBUTION: return decorationRole(fmpe,column);
|
||||||
|
case COLUMN_THREAD_AUTHOR:
|
||||||
|
{
|
||||||
|
QString str,comment ;
|
||||||
|
QList<QIcon> icons;
|
||||||
|
GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, false, str, icons, comment,GxsIdDetails::ICON_TYPE_NONE);
|
||||||
|
|
||||||
|
return QVariant(str);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return displayRole(fmpe,column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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();
|
||||||
|
#endif
|
||||||
|
#ifdef TODO
|
||||||
|
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();
|
||||||
|
|
||||||
|
mFiles.clear();
|
||||||
|
initEmptyHierarchy(mFiles);
|
||||||
|
|
||||||
|
postMods();
|
||||||
|
emit channelLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RsGxsChannelPostFilesModel::setFiles(const std::list<RsGxsFile>& files)
|
||||||
|
{
|
||||||
|
preMods();
|
||||||
|
|
||||||
|
beginRemoveRows(QModelIndex(),0,mFiles.size()-1);
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
mFiles.clear();
|
||||||
|
initEmptyHierarchy(mFiles);
|
||||||
|
|
||||||
|
for(auto& file:files)
|
||||||
|
mFiles.push_back(file);
|
||||||
|
|
||||||
|
#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,mFiles.size()-1);
|
||||||
|
endInsertRows();
|
||||||
|
|
||||||
|
postMods();
|
||||||
|
|
||||||
|
emit channelLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
//ChannelPostsModelIndex RsGxsChannelPostsModel::addEntry(std::vector<ChannelPostsModelPostEntry>& posts,const ChannelPostsModelPostEntry& entry)
|
||||||
|
//{
|
||||||
|
// uint32_t N = posts.size();
|
||||||
|
// posts.push_back(entry);
|
||||||
|
//
|
||||||
|
//#ifdef DEBUG_FORUMMODEL
|
||||||
|
// std::cerr << "Added new entry " << N << " children of " << parent << std::endl;
|
||||||
|
//#endif
|
||||||
|
// if(N == parent)
|
||||||
|
// std::cerr << "(EE) trying to add a post as its own parent!" << std::endl;
|
||||||
|
//
|
||||||
|
// return ChannelPostsModelIndex(N);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void RsGxsChannelPostsModel::convertMsgToPostEntry(const RsGxsChannelGroup& mChannelGroup,const RsMsgMetaData& msg, bool /*useChildTS*/, ChannelPostsModelPostEntry& fentry)
|
||||||
|
//{
|
||||||
|
// fentry.mTitle = msg.mMsgName;
|
||||||
|
// fentry.mMsgId = msg.mMsgId;
|
||||||
|
// fentry.mPublishTs = msg.mPublishTs;
|
||||||
|
// fentry.mPostFlags = 0;
|
||||||
|
// fentry.mMsgStatus = msg.mMsgStatus;
|
||||||
|
//
|
||||||
|
// // Early check for a message that should be hidden because its author
|
||||||
|
// // is flagged with a bad reputation
|
||||||
|
//}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_FORUMMODEL
|
||||||
|
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
|
161
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h
Normal file
161
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h *
|
||||||
|
* *
|
||||||
|
* 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/>. *
|
||||||
|
* *
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "retroshare/rsfiles.h"
|
||||||
|
#include "retroshare/rsgxscommon.h"
|
||||||
|
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
// This class holds the actual hierarchy of posts, represented by identifiers
|
||||||
|
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
|
||||||
|
// safely access the data.
|
||||||
|
|
||||||
|
// The model contains a post in place 0 that is the parent of all posts.
|
||||||
|
|
||||||
|
typedef uint32_t ChannelPostFilesModelIndex;
|
||||||
|
|
||||||
|
// This class is the item model used by Qt to display the information
|
||||||
|
|
||||||
|
class RsGxsChannelPostFilesModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RsGxsChannelPostFilesModel(QObject *parent = NULL);
|
||||||
|
~RsGxsChannelPostFilesModel(){}
|
||||||
|
|
||||||
|
static const int COLUMN_THREAD_NB_COLUMNS = 0x03; // columns for name, size, percentage completion
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
enum Columns {
|
||||||
|
COLUMN_THREAD_TITLE =0x00,
|
||||||
|
COLUMN_THREAD_READ =0x01,
|
||||||
|
COLUMN_THREAD_DATE =0x02,
|
||||||
|
COLUMN_THREAD_DISTRIBUTION =0x03,
|
||||||
|
COLUMN_THREAD_AUTHOR =0x04,
|
||||||
|
COLUMN_THREAD_CONTENT =0x05,
|
||||||
|
COLUMN_THREAD_MSGID =0x06,
|
||||||
|
COLUMN_THREAD_DATA =0x07,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Roles{ SortRole = Qt::UserRole+1,
|
||||||
|
ThreadPinnedRole = Qt::UserRole+2,
|
||||||
|
MissingRole = Qt::UserRole+3,
|
||||||
|
StatusRole = Qt::UserRole+4,
|
||||||
|
UnreadChildrenRole = Qt::UserRole+5,
|
||||||
|
FilterRole = Qt::UserRole+6,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00,
|
||||||
|
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
|
||||||
|
QModelIndex getIndexOfFile(const RsFileHash& hash) const;
|
||||||
|
|
||||||
|
// This method will asynchroneously update the data
|
||||||
|
void setFiles(const std::list<RsGxsFile>& files);
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
void setSortMode(SortMode mode) ;
|
||||||
|
|
||||||
|
void setTextColorRead (QColor color) { mTextColorRead = color;}
|
||||||
|
void setTextColorUnread (QColor color) { mTextColorUnread = color;}
|
||||||
|
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
|
||||||
|
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
|
||||||
|
void setTextColorMissing (QColor color) { mTextColorMissing = color;}
|
||||||
|
void setFilter(int column, const QStringList &strings, uint32_t &count) ;
|
||||||
|
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
void clear() ;
|
||||||
|
|
||||||
|
// AbstractItemModel functions.
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
|
||||||
|
QModelIndex parent(const QModelIndex& child) const override;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
// Custom item roles
|
||||||
|
|
||||||
|
QVariant sizeHintRole (int col) const;
|
||||||
|
QVariant displayRole (const RsGxsFile& fmpe, int col) const;
|
||||||
|
QVariant toolTipRole (const RsGxsFile& fmpe, int col) const;
|
||||||
|
QVariant userRole (const RsGxsFile& fmpe, int col) const;
|
||||||
|
#ifdef TODO
|
||||||
|
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief debug_dump
|
||||||
|
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
|
||||||
|
*/
|
||||||
|
void debug_dump();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI.
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef TODO
|
||||||
|
bool mUseChildTS;
|
||||||
|
bool mFilteringEnabled;
|
||||||
|
SortMode mSortMode;
|
||||||
|
#endif
|
||||||
|
void preMods() ;
|
||||||
|
void postMods() ;
|
||||||
|
|
||||||
|
quintptr getParentRow(quintptr ref,int& row) const;
|
||||||
|
quintptr getChildRef(quintptr ref, int index) const;
|
||||||
|
int getChildrenCount(quintptr ref) const;
|
||||||
|
bool getFileData(const QModelIndex& i,RsGxsFile& fmpe) const;
|
||||||
|
|
||||||
|
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref);
|
||||||
|
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
|
||||||
|
|
||||||
|
#ifdef TODO
|
||||||
|
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
|
||||||
|
#endif
|
||||||
|
void initEmptyHierarchy(std::vector<RsGxsFile> &files);
|
||||||
|
|
||||||
|
std::vector<RsGxsFile> mFiles ; // store the list of files for the post
|
||||||
|
};
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "GxsChannelPostsWidgetWithModel.h"
|
#include "GxsChannelPostsWidgetWithModel.h"
|
||||||
#include "GxsChannelPostsModel.h"
|
#include "GxsChannelPostsModel.h"
|
||||||
|
#include "GxsChannelPostFilesModel.h"
|
||||||
#include "ui_GxsChannelPostsWidgetWithModel.h"
|
#include "ui_GxsChannelPostsWidgetWithModel.h"
|
||||||
#include "gui/feeds/GxsChannelPostItem.h"
|
#include "gui/feeds/GxsChannelPostItem.h"
|
||||||
#include "gui/gxs/GxsIdDetails.h"
|
#include "gui/gxs/GxsIdDetails.h"
|
||||||
@ -57,7 +58,7 @@ static const int CHANNEL_TABS_POSTS = 1;
|
|||||||
#define VIEW_MODE_FEEDS 1
|
#define VIEW_MODE_FEEDS 1
|
||||||
#define VIEW_MODE_FILES 2
|
#define VIEW_MODE_FILES 2
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(RsGxsChannelPost*)
|
Q_DECLARE_METATYPE(RsGxsFile)
|
||||||
|
|
||||||
void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
|
void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||||
{
|
{
|
||||||
@ -137,6 +138,73 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM
|
|||||||
return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h);
|
return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||||
|
{
|
||||||
|
QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")};
|
||||||
|
|
||||||
|
QStyleOptionViewItem opt = option;
|
||||||
|
QStyleOptionProgressBarV2 newopt;
|
||||||
|
QRect pixmapRect;
|
||||||
|
QPixmap pixmap;
|
||||||
|
qlonglong fileSize;
|
||||||
|
double dlspeed, multi;
|
||||||
|
int seconds,minutes, hours, days;
|
||||||
|
qlonglong remaining;
|
||||||
|
QString temp ;
|
||||||
|
qlonglong completed;
|
||||||
|
qlonglong downloadtime;
|
||||||
|
qint64 qi64Value;
|
||||||
|
|
||||||
|
// prepare
|
||||||
|
painter->save();
|
||||||
|
painter->setClipRect(opt.rect);
|
||||||
|
|
||||||
|
RsGxsFile file = index.data(Qt::UserRole).value<RsGxsFile>() ;
|
||||||
|
|
||||||
|
QVariant value = index.data(Qt::TextColorRole);
|
||||||
|
|
||||||
|
if(value.isValid() && qvariant_cast<QColor>(value).isValid())
|
||||||
|
opt.palette.setColor(QPalette::Text, qvariant_cast<QColor>(value));
|
||||||
|
|
||||||
|
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
|
||||||
|
|
||||||
|
if(option.state & QStyle::State_Selected)
|
||||||
|
painter->setPen(opt.palette.color(cg, QPalette::HighlightedText));
|
||||||
|
else
|
||||||
|
painter->setPen(opt.palette.color(cg, QPalette::Text));
|
||||||
|
|
||||||
|
switch(index.column())
|
||||||
|
{
|
||||||
|
case 0: painter->drawText(option.rect,Qt::AlignLeft,QString::fromUtf8(file.mName.c_str()));
|
||||||
|
break;
|
||||||
|
case 1: painter->drawText(option.rect,Qt::AlignLeft,QString::number(file.mSize));
|
||||||
|
break;
|
||||||
|
case 2: {
|
||||||
|
FileInfo finfo;
|
||||||
|
if(rsFiles->FileDetails(file.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
|
||||||
|
painter->drawText(option.rect,Qt::AlignLeft,QString::number(finfo.transfered));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
RsGxsFile file = index.data(Qt::UserRole).value<RsGxsFile>() ;
|
||||||
|
|
||||||
|
QFontMetricsF fm(option.font);
|
||||||
|
|
||||||
|
switch(index.column())
|
||||||
|
{
|
||||||
|
case 0: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height());
|
||||||
|
case 1: return QSize(fm.width(QString::number(file.mSize)),fm.height());
|
||||||
|
default:
|
||||||
|
case 2: return QSize(fm.height() * 20,fm.height()) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) :
|
GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) :
|
||||||
GxsMessageFrameWidget(rsGxsChannels, parent),
|
GxsMessageFrameWidget(rsGxsChannels, parent),
|
||||||
@ -145,9 +213,12 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
|
|||||||
/* Invoke the Qt Designer generated object setup routine */
|
/* Invoke the Qt Designer generated object setup routine */
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
ui->postsTree->setModel(mThreadModel = new RsGxsChannelPostsModel());
|
ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel());
|
||||||
ui->postsTree->setItemDelegate(new ChannelPostDelegate());
|
ui->postsTree->setItemDelegate(new ChannelPostDelegate());
|
||||||
|
|
||||||
|
ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel());
|
||||||
|
ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate());
|
||||||
|
|
||||||
connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails()));
|
connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails()));
|
||||||
|
|
||||||
/* Setup UI helper */
|
/* Setup UI helper */
|
||||||
@ -220,7 +291,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
|
|||||||
setAutoDownload(false);
|
setAutoDownload(false);
|
||||||
settingsChanged();
|
settingsChanged();
|
||||||
|
|
||||||
mThreadModel->updateChannel(channelId);
|
mChannelPostsModel->updateChannel(channelId);
|
||||||
|
|
||||||
mEventHandlerId = 0;
|
mEventHandlerId = 0;
|
||||||
// Needs to be asynced because this function is called by another thread!
|
// Needs to be asynced because this function is called by another thread!
|
||||||
@ -264,13 +335,31 @@ void GxsChannelPostsWidgetWithModel::showPostDetails()
|
|||||||
if(!index.isValid())
|
if(!index.isValid())
|
||||||
{
|
{
|
||||||
ui->postDetails_TE->clear();
|
ui->postDetails_TE->clear();
|
||||||
|
ui->postLogo_LB->clear();
|
||||||
|
mChannelPostFilesModel->clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ;
|
RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ;
|
||||||
|
|
||||||
|
mChannelPostFilesModel->setFiles(post.mFiles);
|
||||||
|
|
||||||
std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl;
|
std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl;
|
||||||
|
|
||||||
ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS));
|
ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS));
|
||||||
|
|
||||||
|
QPixmap postImage;
|
||||||
|
|
||||||
|
if (post.mThumbnail.mData != NULL)
|
||||||
|
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL);
|
||||||
|
else
|
||||||
|
postImage = QPixmap(CHAN_DEFAULT_IMAGE);
|
||||||
|
|
||||||
|
int W = QFontMetricsF(font()).height() * 8;
|
||||||
|
|
||||||
|
// Using fixed width so that the post will not displace the text when we browse.
|
||||||
|
|
||||||
|
ui->postLogo_LB->setPixmap(postImage);
|
||||||
|
ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GxsChannelPostsWidgetWithModel::updateGroupData()
|
void GxsChannelPostsWidgetWithModel::updateGroupData()
|
||||||
@ -297,7 +386,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData()
|
|||||||
RsQThreadUtils::postToObject( [this,groups]()
|
RsQThreadUtils::postToObject( [this,groups]()
|
||||||
{
|
{
|
||||||
mGroup = groups[0];
|
mGroup = groups[0];
|
||||||
mThreadModel->updateChannel(groupId());
|
mChannelPostsModel->updateChannel(groupId());
|
||||||
insertChannelDetails(mGroup);
|
insertChannelDetails(mGroup);
|
||||||
} );
|
} );
|
||||||
});
|
});
|
||||||
@ -328,7 +417,7 @@ void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete)
|
|||||||
#warning todo
|
#warning todo
|
||||||
//saveExpandedItems(mSavedExpandedMessages);
|
//saveExpandedItems(mSavedExpandedMessages);
|
||||||
|
|
||||||
//if(mGroupId != mThreadModel->currentGroupId())
|
//if(mGroupId != mChannelPostsModel->currentGroupId())
|
||||||
// mThreadId.clear();
|
// mThreadId.clear();
|
||||||
|
|
||||||
updateGroupData();
|
updateGroupData();
|
||||||
@ -881,7 +970,7 @@ void GxsChannelPostsWidgetWithModel::blank()
|
|||||||
mStateHelper->setWidgetEnabled(ui->postButton, false);
|
mStateHelper->setWidgetEnabled(ui->postButton, false);
|
||||||
mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false);
|
mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false);
|
||||||
|
|
||||||
mThreadModel->clear();
|
mChannelPostsModel->clear();
|
||||||
groupNameChanged(QString());
|
groupNameChanged(QString());
|
||||||
|
|
||||||
//ui->infoWidget->hide();
|
//ui->infoWidget->hide();
|
||||||
@ -1027,3 +1116,4 @@ void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read, uint32_t &t
|
|||||||
|
|
||||||
token = data.mLastToken;
|
token = data.mLastToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,21 @@ class GxsChannelPostItem;
|
|||||||
class QTreeWidgetItem;
|
class QTreeWidgetItem;
|
||||||
class FeedItem;
|
class FeedItem;
|
||||||
class RsGxsChannelPostsModel;
|
class RsGxsChannelPostsModel;
|
||||||
|
class RsGxsChannelPostFilesModel;
|
||||||
|
|
||||||
|
class ChannelPostFilesDelegate: public QAbstractItemDelegate
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChannelPostFilesDelegate(QObject *parent=0) : QAbstractItemDelegate(parent){}
|
||||||
|
virtual ~ChannelPostFilesDelegate(){}
|
||||||
|
|
||||||
|
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
|
||||||
|
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
class ChannelPostDelegate: public QAbstractItemDelegate
|
class ChannelPostDelegate: public QAbstractItemDelegate
|
||||||
{
|
{
|
||||||
@ -137,7 +152,8 @@ private:
|
|||||||
bool mUseThread;
|
bool mUseThread;
|
||||||
RsEventsHandlerId_t mEventHandlerId ;
|
RsEventsHandlerId_t mEventHandlerId ;
|
||||||
|
|
||||||
RsGxsChannelPostsModel *mThreadModel;
|
RsGxsChannelPostsModel *mChannelPostsModel;
|
||||||
|
RsGxsChannelPostFilesModel *mChannelPostFilesModel;
|
||||||
UIStateHelper *mStateHelper;
|
UIStateHelper *mStateHelper;
|
||||||
|
|
||||||
/* UI - from Designer */
|
/* UI - from Designer */
|
||||||
|
@ -421,28 +421,70 @@ p, li { white-space: pre-wrap; }
|
|||||||
</attribute>
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QTabWidget" name="details_TW">
|
<widget class="QTabWidget" name="details_TW">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<kerning>true</kerning>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>2</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Details</string>
|
<string>Details</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<widget class="QTextEdit" name="postDetails_TE">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<property name="geometry">
|
<item>
|
||||||
<rect>
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<x>9</x>
|
<item>
|
||||||
<y>9</y>
|
<widget class="QLabel" name="postLogo_LB">
|
||||||
<width>955</width>
|
<property name="text">
|
||||||
<height>181</height>
|
<string>TextLabel</string>
|
||||||
</rect>
|
</property>
|
||||||
</property>
|
<property name="scaledContents">
|
||||||
</widget>
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="postDetails_TE"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_5">
|
<widget class="QWidget" name="channelPostFiles_tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Files</string>
|
<string>Files</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="channelPostFiles_TV"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="channelPostFilesDL_PB">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download selected files</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="tab_2">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
|
@ -1335,6 +1335,7 @@ gxschannels {
|
|||||||
gui/gxschannels/GxsChannelPostsWidget.h \
|
gui/gxschannels/GxsChannelPostsWidget.h \
|
||||||
gui/gxschannels/GxsChannelPostsWidgetWithModel.h \
|
gui/gxschannels/GxsChannelPostsWidgetWithModel.h \
|
||||||
gui/gxschannels/GxsChannelPostsModel.h \
|
gui/gxschannels/GxsChannelPostsModel.h \
|
||||||
|
gui/gxschannels/GxsChannelPostFilesModel.h \
|
||||||
gui/gxschannels/GxsChannelFilesWidget.h \
|
gui/gxschannels/GxsChannelFilesWidget.h \
|
||||||
gui/gxschannels/GxsChannelFilesStatusWidget.h \
|
gui/gxschannels/GxsChannelFilesStatusWidget.h \
|
||||||
gui/feeds/GxsChannelGroupItem.h \
|
gui/feeds/GxsChannelGroupItem.h \
|
||||||
@ -1354,6 +1355,7 @@ gxschannels {
|
|||||||
gui/gxschannels/GxsChannelPostsWidget.cpp \
|
gui/gxschannels/GxsChannelPostsWidget.cpp \
|
||||||
gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \
|
gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \
|
||||||
gui/gxschannels/GxsChannelPostsModel.cpp \
|
gui/gxschannels/GxsChannelPostsModel.cpp \
|
||||||
|
gui/gxschannels/GxsChannelPostFilesModel.cpp \
|
||||||
gui/gxschannels/GxsChannelFilesWidget.cpp \
|
gui/gxschannels/GxsChannelFilesWidget.cpp \
|
||||||
gui/gxschannels/GxsChannelFilesStatusWidget.cpp \
|
gui/gxschannels/GxsChannelFilesStatusWidget.cpp \
|
||||||
gui/gxschannels/GxsChannelGroupDialog.cpp \
|
gui/gxschannels/GxsChannelGroupDialog.cpp \
|
||||||
|
Loading…
Reference in New Issue
Block a user