Merge pull request #2054 from csoler/v0.6-ChannelsGUI

V0.6 channels gui
This commit is contained in:
csoler 2020-09-11 21:20:20 +02:00 committed by GitHub
commit 37c7a0aa44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1231 additions and 349 deletions

View File

@ -52,6 +52,7 @@ void GxsCommentDialog::init()
connect(ui->refreshButton, SIGNAL(clicked()), this, SLOT(refresh())); connect(ui->refreshButton, SIGNAL(clicked()), this, SLOT(refresh()));
connect(ui->idChooser, SIGNAL(currentIndexChanged( int )), this, SLOT(voterSelectionChanged( int ))); connect(ui->idChooser, SIGNAL(currentIndexChanged( int )), this, SLOT(voterSelectionChanged( int )));
connect(ui->idChooser, SIGNAL(idsLoaded()), this, SLOT(idChooserReady())); connect(ui->idChooser, SIGNAL(idsLoaded()), this, SLOT(idChooserReady()));
connect(ui->treeWidget,SIGNAL(commentsLoaded(int)),this,SLOT(notifyCommentsLoaded(int)));
connect(ui->commentButton, SIGNAL(clicked()), ui->treeWidget, SLOT(makeComment())); connect(ui->commentButton, SIGNAL(clicked()), ui->treeWidget, SLOT(makeComment()));
connect(ui->sortBox, SIGNAL(currentIndexChanged(int)), this, SLOT(sortComments(int))); connect(ui->sortBox, SIGNAL(currentIndexChanged(int)), this, SLOT(sortComments(int)));
@ -95,6 +96,11 @@ void GxsCommentDialog::commentLoad(const RsGxsGroupId &grpId, const std::set<RsG
ui->treeWidget->requestComments(mGrpId,msg_versions,most_recent_msgId); ui->treeWidget->requestComments(mGrpId,msg_versions,most_recent_msgId);
} }
void GxsCommentDialog::notifyCommentsLoaded(int n)
{
emit commentsLoaded(n);
}
void GxsCommentDialog::refresh() void GxsCommentDialog::refresh()
{ {
std::cerr << "GxsCommentDialog::refresh()"; std::cerr << "GxsCommentDialog::refresh()";

View File

@ -48,6 +48,10 @@ private slots:
void idChooserReady(); void idChooserReady();
void voterSelectionChanged( int index ); void voterSelectionChanged( int index );
void sortComments(int); void sortComments(int);
void notifyCommentsLoaded(int n);
signals:
void commentsLoaded(int);
private: private:
void init(); void init();

View File

@ -228,8 +228,10 @@ void GxsCommentTreeWidget::customPopUpMenu(const QPoint& /*point*/)
void GxsCommentTreeWidget::voteUp() void GxsCommentTreeWidget::voteUp()
{ {
std::cerr << "GxsCommentTreeWidget::voteUp()"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::voteUp()";
std::cerr << std::endl; std::cerr << std::endl;
#endif
vote(mGroupId, mLatestMsgId, mCurrentCommentMsgId, mVoterId, true); vote(mGroupId, mLatestMsgId, mCurrentCommentMsgId, mVoterId, true);
} }
@ -237,8 +239,10 @@ void GxsCommentTreeWidget::voteUp()
void GxsCommentTreeWidget::voteDown() void GxsCommentTreeWidget::voteDown()
{ {
std::cerr << "GxsCommentTreeWidget::voteDown()"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::voteDown()";
std::cerr << std::endl; std::cerr << std::endl;
#endif
vote(mGroupId, mLatestMsgId, mCurrentCommentMsgId, mVoterId, false); vote(mGroupId, mLatestMsgId, mCurrentCommentMsgId, mVoterId, false);
} }
@ -246,8 +250,10 @@ void GxsCommentTreeWidget::voteDown()
void GxsCommentTreeWidget::setVoteId(const RsGxsId &voterId) void GxsCommentTreeWidget::setVoteId(const RsGxsId &voterId)
{ {
mVoterId = voterId; mVoterId = voterId;
std::cerr << "GxsCommentTreeWidget::setVoterId(" << mVoterId << ")"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::setVoterId(" << mVoterId << ")";
std::cerr << std::endl; std::cerr << std::endl;
#endif
} }
@ -270,6 +276,7 @@ void GxsCommentTreeWidget::vote(const RsGxsGroupId &groupId, const RsGxsMessageI
vote.mVoteType = GXS_VOTE_DOWN; vote.mVoteType = GXS_VOTE_DOWN;
} }
#ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::vote()"; std::cerr << "GxsCommentTreeWidget::vote()";
std::cerr << std::endl; std::cerr << std::endl;
@ -277,6 +284,7 @@ void GxsCommentTreeWidget::vote(const RsGxsGroupId &groupId, const RsGxsMessageI
std::cerr << "ThreadId : " << vote.mMeta.mThreadId << std::endl; std::cerr << "ThreadId : " << vote.mMeta.mThreadId << std::endl;
std::cerr << "ParentId : " << vote.mMeta.mParentId << std::endl; std::cerr << "ParentId : " << vote.mMeta.mParentId << std::endl;
std::cerr << "AuthorId : " << vote.mMeta.mAuthorId << std::endl; std::cerr << "AuthorId : " << vote.mMeta.mAuthorId << std::endl;
#endif
uint32_t token; uint32_t token;
mCommentService->createNewVote(token, vote); mCommentService->createNewVote(token, vote);
@ -361,13 +369,17 @@ void GxsCommentTreeWidget::requestComments(const RsGxsGroupId& group, const std:
void GxsCommentTreeWidget::service_requestComments(const RsGxsGroupId& group_id,const std::set<RsGxsMessageId> & msgIds) void GxsCommentTreeWidget::service_requestComments(const RsGxsGroupId& group_id,const std::set<RsGxsMessageId> & msgIds)
{ {
/* request comments */ /* request comments */
std::cerr << "GxsCommentTreeWidget::service_requestComments for group " << group_id << std::endl; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::service_requestComments for group " << group_id << std::endl;
#endif
std::vector<RsGxsGrpMsgIdPair> ids_to_ask; std::vector<RsGxsGrpMsgIdPair> ids_to_ask;
for(std::set<RsGxsMessageId>::const_iterator it(msgIds.begin());it!=msgIds.end();++it) for(std::set<RsGxsMessageId>::const_iterator it(msgIds.begin());it!=msgIds.end();++it)
{ {
std::cerr << " asking for msg " << *it << std::endl; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << " asking for msg " << *it << std::endl;
#endif
ids_to_ask.push_back(std::make_pair(group_id,*it)); ids_to_ask.push_back(std::make_pair(group_id,*it));
} }
@ -399,14 +411,18 @@ void GxsCommentTreeWidget::completeItems()
std::map<RsGxsMessageId, QTreeWidgetItem *>::iterator lit; std::map<RsGxsMessageId, QTreeWidgetItem *>::iterator lit;
std::multimap<RsGxsMessageId, QTreeWidgetItem *>::iterator pit; std::multimap<RsGxsMessageId, QTreeWidgetItem *>::iterator pit;
std::cerr << "GxsCommentTreeWidget::completeItems() " << mPendingInsertMap.size(); #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::completeItems() " << mPendingInsertMap.size();
std::cerr << " PendingItems"; std::cerr << " PendingItems";
std::cerr << std::endl; std::cerr << std::endl;
#endif
for(pit = mPendingInsertMap.begin(); pit != mPendingInsertMap.end(); ++pit) for(pit = mPendingInsertMap.begin(); pit != mPendingInsertMap.end(); ++pit)
{ {
std::cerr << "GxsCommentTreeWidget::completeItems() item->parent: " << pit->first; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::completeItems() item->parent: " << pit->first;
std::cerr << std::endl; std::cerr << std::endl;
#endif
if (pit->first != parentId) if (pit->first != parentId)
{ {
@ -425,15 +441,19 @@ void GxsCommentTreeWidget::completeItems()
if (parent) if (parent)
{ {
std::cerr << "GxsCommentTreeWidget::completeItems() Added to Parent"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::completeItems() Added to Parent";
std::cerr << std::endl; std::cerr << std::endl;
#endif
parent->addChild(pit->second); parent->addChild(pit->second);
} }
else if (mMsgVersions.find(parentId) != mMsgVersions.end()) else if (mMsgVersions.find(parentId) != mMsgVersions.end())
{ {
std::cerr << "GxsCommentTreeWidget::completeItems() Added to topLevelItems"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::completeItems() Added to topLevelItems";
std::cerr << std::endl; std::cerr << std::endl;
#endif
topLevelItems.append(pit->second); topLevelItems.append(pit->second);
} }
@ -443,8 +463,10 @@ void GxsCommentTreeWidget::completeItems()
/* missing parent -> insert At Top Level */ /* missing parent -> insert At Top Level */
QTreeWidgetItem *missingItem = service_createMissingItem(pit->first); QTreeWidgetItem *missingItem = service_createMissingItem(pit->first);
std::cerr << "GxsCommentTreeWidget::completeItems() Added MissingItem"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::completeItems() Added MissingItem";
std::cerr << std::endl; std::cerr << std::endl;
#endif
parent = missingItem; parent = missingItem;
parent->addChild(pit->second); parent->addChild(pit->second);
@ -464,9 +486,11 @@ void GxsCommentTreeWidget::completeItems()
void GxsCommentTreeWidget::addItem(RsGxsMessageId itemId, RsGxsMessageId parentId, QTreeWidgetItem *item) void GxsCommentTreeWidget::addItem(RsGxsMessageId itemId, RsGxsMessageId parentId, QTreeWidgetItem *item)
{ {
std::cerr << "GxsCommentTreeWidget::addItem() Id: " << itemId; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::addItem() Id: " << itemId;
std::cerr << " ParentId: " << parentId; std::cerr << " ParentId: " << parentId;
std::cerr << std::endl; std::cerr << std::endl;
#endif
/* store in map -> for children */ /* store in map -> for children */
mLoadingMap[itemId] = item; mLoadingMap[itemId] = item;
@ -475,20 +499,48 @@ void GxsCommentTreeWidget::addItem(RsGxsMessageId itemId, RsGxsMessageId parentI
it = mLoadingMap.find(parentId); it = mLoadingMap.find(parentId);
if (it != mLoadingMap.end()) if (it != mLoadingMap.end())
{ {
std::cerr << "GxsCommentTreeWidget::addItem() Added to Parent"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::addItem() Added to Parent";
std::cerr << std::endl; std::cerr << std::endl;
#endif
it->second->addChild(item); it->second->addChild(item);
} }
else else
{ {
std::cerr << "GxsCommentTreeWidget::addItem() Added to Pending List"; #ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::addItem() Added to Pending List";
std::cerr << std::endl; std::cerr << std::endl;
#endif
mPendingInsertMap.insert(std::make_pair(parentId, item)); mPendingInsertMap.insert(std::make_pair(parentId, item));
} }
} }
int treeCount(QTreeWidget *tree, QTreeWidgetItem *parent = 0)
{
int count = 0;
if (parent == 0) {
int topCount = tree->topLevelItemCount();
for (int i = 0; i < topCount; i++) {
QTreeWidgetItem *item = tree->topLevelItem(i);
if (item->isExpanded()) {
count += treeCount(tree, item);
}
}
count += topCount;
} else {
int childCount = parent->childCount();
for (int i = 0; i < childCount; i++) {
QTreeWidgetItem *item = parent->child(i);
if (item->isExpanded()) {
count += treeCount(tree, item);
}
}
count += childCount;
}
return count;
}
void GxsCommentTreeWidget::loadThread(const uint32_t &token) void GxsCommentTreeWidget::loadThread(const uint32_t &token)
{ {
clearItems(); clearItems();
@ -496,6 +548,8 @@ void GxsCommentTreeWidget::loadThread(const uint32_t &token)
service_loadThread(token); service_loadThread(token);
completeItems(); completeItems();
emit commentsLoaded(treeCount(this));
} }
void GxsCommentTreeWidget::acknowledgeComment(const uint32_t &token) void GxsCommentTreeWidget::acknowledgeComment(const uint32_t &token)
@ -615,8 +669,10 @@ QTreeWidgetItem *GxsCommentTreeWidget::service_createMissingItem(const RsGxsMess
void GxsCommentTreeWidget::loadRequest(const TokenQueue *queue, const TokenRequest &req) void GxsCommentTreeWidget::loadRequest(const TokenQueue *queue, const TokenRequest &req)
{ {
#ifdef DEBUG_GXSCOMMENT_TREEWIDGET
std::cerr << "GxsCommentTreeWidget::loadRequest() UserType: " << req.mUserType; std::cerr << "GxsCommentTreeWidget::loadRequest() UserType: " << req.mUserType;
std::cerr << std::endl; std::cerr << std::endl;
#endif
if (queue != mTokenQueue) if (queue != mTokenQueue)
{ {

View File

@ -80,6 +80,9 @@ public slots:
void markSpammer(); void markSpammer();
void banUser(); void banUser();
signals:
void commentsLoaded(int);
protected: protected:
void vote(const RsGxsGroupId &groupId, const RsGxsMessageId &threadId, void vote(const RsGxsGroupId &groupId, const RsGxsMessageId &threadId,

View File

@ -78,10 +78,19 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId
connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool))); connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool)));
connect(stackedWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint))); connect(stackedWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint)));
connect(generateCheckBox, SIGNAL(toggled(bool)), generateSpinBox, SLOT(setEnabled(bool))); connect(generateCheckBox, SIGNAL(toggled(bool)), generateSpinBox, SLOT(setEnabled(bool)));
connect(aspectRatio_CB,SIGNAL(currentIndexChanged(int)),this,SLOT(changeAspectRatio(int)));
aspectRatio_CB->setItemIcon(0,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-auto.svg"));
aspectRatio_CB->setItemIcon(1,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-1-1.svg"));
aspectRatio_CB->setItemIcon(2,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-3-4.svg"));
aspectRatio_CB->setItemIcon(3,FilesDefs::getIconFromQtResourcePath(":/icons/svg/ratio-16-9.svg"));
generateSpinBox->setEnabled(false); generateSpinBox->setEnabled(false);
thumbNailCb->setVisible(false); preview_W->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE),true);
preview_W->setText("[Text preview]");
thumbNailCb->setVisible(false);
thumbNailCb->setEnabled(false); thumbNailCb->setEnabled(false);
#ifdef CHANNELS_FRAME_CATCHER #ifdef CHANNELS_FRAME_CATCHER
fCatcher = new framecatcher(); fCatcher = new framecatcher();
@ -107,6 +116,19 @@ CreateGxsChannelMsg::~CreateGxsChannelMsg()
#endif #endif
} }
void CreateGxsChannelMsg::changeAspectRatio(int s)
{
switch(s)
{
case 0: break;
case 1: preview_W->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_1_1);
break;
case 2: preview_W->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_2_3);
break;
case 3: preview_W->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9);
break;
}
}
void CreateGxsChannelMsg::contextMenu(QPoint /*point*/) void CreateGxsChannelMsg::contextMenu(QPoint /*point*/)
{ {
QList<RetroShareLink> links ; QList<RetroShareLink> links ;
@ -688,7 +710,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str
// send chan image // send chan image
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
picture.save(&buffer, "PNG"); // writes image into ba in PNG format preview_W->getCroppedScaledPicture().save(&buffer, "PNG"); // writes image into ba in PNG format
post.mThumbnail.copy((uint8_t *) ba.data(), ba.size()); post.mThumbnail.copy((uint8_t *) ba.data(), ba.size());
} }
@ -723,7 +745,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str
void CreateGxsChannelMsg::addThumbnail() void CreateGxsChannelMsg::addThumbnail()
{ {
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 107,156); // these absolute sizes are terrible QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 0,0); // 0,0 means: no scale.
if (img.isNull()) if (img.isNull())
return; return;
@ -731,7 +753,8 @@ void CreateGxsChannelMsg::addThumbnail()
picture = img; picture = img;
// to show the selected // to show the selected
preview_W->setPixmap(picture); preview_W->setPixmap(picture, aspectRatio_CB->currentIndex()==0);
} }
void CreateGxsChannelMsg::loadOriginalChannelPostInfo() void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
@ -775,7 +798,7 @@ void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
if(post.mThumbnail.mData != NULL) if(post.mThumbnail.mData != NULL)
{ {
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL); GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL);
preview_W->setPixmap(picture); preview_W->setPixmap(picture,true);
} }

View File

@ -64,6 +64,7 @@ private slots:
void sendMsg(); void sendMsg();
void pasteLink() ; void pasteLink() ;
void contextMenu(QPoint) ; void contextMenu(QPoint) ;
void changeAspectRatio(int s);
void addThumbnail(); void addThumbnail();
void allowAutoMediaThumbNail(bool); void allowAutoMediaThumbNail(bool);

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>736</width> <width>881</width>
<height>271</height> <height>383</height>
</rect> </rect>
</property> </property>
<property name="acceptDrops"> <property name="acceptDrops">
@ -79,32 +79,35 @@
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="stackedWidgetPage1"> <widget class="QWidget" name="stackedWidgetPage1">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item> <item>
<widget class="ChannelPostThumbnailView" name="preview_W" native="true"/> <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="ChannelPostThumbnailView" name="preview_W" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<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>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
@ -168,7 +171,7 @@ p, li { white-space: pre-wrap; }
<string>Add Channel Thumbnail</string> <string>Add Channel Thumbnail</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../icons.qrc"> <iconset>
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset> <normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
</property> </property>
<property name="iconSize"> <property name="iconSize">
@ -179,13 +182,43 @@ p, li { white-space: pre-wrap; }
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QComboBox" name="aspectRatio_CB">
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<item>
<property name="text">
<string>Auto</string>
</property>
</item>
<item>
<property name="text">
<string>1:1</string>
</property>
</item>
<item>
<property name="text">
<string>3:4</string>
</property>
</item>
<item>
<property name="text">
<string>16:9</string>
</property>
</item>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="addfilepushButton"> <widget class="QPushButton" name="addfilepushButton">
<property name="text"> <property name="text">
<string>Add File to Attach</string> <string>Add File to Attach</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../icons.qrc"> <iconset>
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset> <normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
</property> </property>
<property name="iconSize"> <property name="iconSize">
@ -285,7 +318,7 @@ p, li { white-space: pre-wrap; }
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../icons.qrc"> <iconset>
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset> <normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
</property> </property>
<property name="iconSize"> <property name="iconSize">
@ -309,7 +342,7 @@ p, li { white-space: pre-wrap; }
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>81</width> <width>84</width>
<height>24</height> <height>24</height>
</rect> </rect>
</property> </property>
@ -389,7 +422,7 @@ p, li { white-space: pre-wrap; }
<string>Channel Post</string> <string>Channel Post</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../icons.qrc"> <iconset>
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset> <normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
</property> </property>
<property name="iconSize"> <property name="iconSize">
@ -419,7 +452,7 @@ p, li { white-space: pre-wrap; }
<string>Attachments</string> <string>Attachments</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../icons.qrc"> <iconset>
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset> <normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
</property> </property>
<property name="iconSize"> <property name="iconSize">
@ -491,7 +524,6 @@ p, li { white-space: pre-wrap; }
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../images.qrc"/> <include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -35,9 +35,9 @@ public:
/** Default Destructor */ /** Default Destructor */
~GxsChannelDialog(); ~GxsChannelDialog();
virtual QIcon iconPixmap() const { return QIcon(IMAGE_GXSCHANNELS) ; } //MainPage virtual QIcon iconPixmap() const override { return QIcon(IMAGE_GXSCHANNELS) ; } //MainPage
virtual QString pageName() const { return tr("Channels") ; } //MainPage virtual QString pageName() const override { return tr("Channels") ; } //MainPage
virtual QString helpText() const { return ""; } //MainPage virtual QString helpText() const override { return ""; } //MainPage
void shareOnChannel(const RsGxsGroupId& channel_id, const QList<RetroShareLink>& file_link) ; void shareOnChannel(const RsGxsGroupId& channel_id, const QList<RetroShareLink>& file_link) ;
@ -69,17 +69,17 @@ private slots:
private: private:
/* GxsGroupFrameDialog */ /* GxsGroupFrameDialog */
virtual QString text(TextType type); virtual QString text(TextType type) override;
virtual QString icon(IconType type); virtual QString icon(IconType type) override;
virtual QString settingsGroupName() { return "ChannelDialog"; } virtual QString settingsGroupName() override { return "ChannelDialog"; }
virtual GxsGroupDialog *createNewGroupDialog(); virtual GxsGroupDialog *createNewGroupDialog() override;
virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId); virtual GxsGroupDialog *createGroupDialog(GxsGroupDialog::Mode mode, RsGxsGroupId groupId) override;
virtual int shareKeyType(); virtual int shareKeyType() override;
virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId); virtual GxsMessageFrameWidget *createMessageFrameWidget(const RsGxsGroupId &groupId) override;
virtual void groupTreeCustomActions(RsGxsGroupId grpId, int subscribeFlags, QList<QAction*> &actions); virtual void groupTreeCustomActions(RsGxsGroupId grpId, int subscribeFlags, QList<QAction*> &actions) override;
virtual RsGxsCommentService *getCommentService(); virtual RsGxsCommentService *getCommentService() override;
virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId); virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &grpId, const RsGxsMessageId &msgId) override;
virtual uint32_t requestGroupSummaryType() { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data virtual uint32_t requestGroupSummaryType() override { return GXS_REQUEST_TYPE_GROUP_DATA; } // request complete group data
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event); void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);

View File

@ -306,7 +306,7 @@ void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t&
if(strings.empty()) if(strings.empty())
{ {
mFilteredFiles.clear(); mFilteredFiles.clear();
for(int i=0;i<mFiles.size();++i) for(uint32_t i=0;i<mFiles.size();++i)
mFilteredFiles.push_back(i); mFilteredFiles.push_back(i);
} }
else else
@ -314,7 +314,7 @@ void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t&
mFilteredFiles.clear(); mFilteredFiles.clear();
//mFilteredPosts.push_back(0); //mFilteredPosts.push_back(0);
for(int i=0;i<mFiles.size();++i) for(uint32_t i=0;i<mFiles.size();++i)
{ {
bool passes_strings = true; bool passes_strings = true;

View File

@ -0,0 +1,324 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.cpp *
* *
* Copyright 2020 by 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 <QWheelEvent>
#include <QDateTime>
#include "gui/common/FilesDefs.h"
#include "gui/gxschannels/GxsChannelPostThumbnail.h"
const float ChannelPostThumbnailView::DEFAULT_SIZE_IN_FONT_HEIGHT = 5.0;
const float ChannelPostThumbnailView::FONT_SCALE_FACTOR = 1.5;
ChannelPostThumbnailView::ChannelPostThumbnailView(const RsGxsChannelPost& post,uint32_t flags,QWidget *parent)
: QWidget(parent),mPostTitle(nullptr),mFlags(flags), mAspectRatio(ASPECT_RATIO_2_3)
{
// now fill the data
init(post);
}
ChannelPostThumbnailView::ChannelPostThumbnailView(QWidget *parent,uint32_t flags)
: QWidget(parent),mFlags(flags), mAspectRatio(ASPECT_RATIO_2_3)
{
init(RsGxsChannelPost());
}
ChannelPostThumbnailView::~ChannelPostThumbnailView()
{
delete mPostImage;
}
void ChannelPostThumbnailView::setText(const QString& s)
{
if(mPostTitle == NULL)
{
std::cerr << "(EE) calling setText on a ChannelPostThumbnailView without SHOW_TEXT flag!"<< std::endl;
return;
}
QString ss;
if(s.length() > 30)
ss = s.left(30)+"...";
else
ss =s;
mPostTitle->setText(ss);
}
void ChannelPostThumbnailView::setPixmap(const QPixmap& p, bool guess_aspect_ratio)
{
mPostImage->setPicture(p);
if(guess_aspect_ratio)// aspect ratio is automatically guessed.
{
// compute closest aspect ratio
float r = p.width()/(float)p.height();
if(r < 0.8)
setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_2_3);
else if(r < 1.15)
setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_1_1);
else
setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9);
}
}
void ChannelPostThumbnailView::setAspectRatio(AspectRatio r)
{
mAspectRatio = r;
QFontMetricsF fm(font());
int W = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_w() * fm.height() ;
int H = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_h() * fm.height() ;
mPostImage->setFixedSize(W,H);
mPostImage->reset();
mPostImage->updateView();
}
void ChannelPostThumbnailView::init(const RsGxsChannelPost& post)
{
QString msg = QString::fromUtf8(post.mMeta.mMsgName.c_str());
bool is_msg_new = IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus);
QPixmap thumbnail;
if(post.mThumbnail.mSize > 0)
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL);
else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row)
thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE);
mPostImage = new ZoomableLabel(this);
mPostImage->setEnableZoom(mFlags & FLAG_ALLOW_PAN);
mPostImage->setScaledContents(true);
mPostImage->setPicture(thumbnail);
if(mFlags & FLAG_ALLOW_PAN)
mPostImage->setToolTip(tr("Use mouse to center and zoom into the image"));
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(mPostImage);
QFontMetricsF fm(font());
int W = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_w() * fm.height() ;
int H = THUMBNAIL_OVERSAMPLE_FACTOR * thumbnail_h() * fm.height() ;
mPostImage->setFixedSize(W,H);
if(mFlags & FLAG_SHOW_TEXT)
{
mPostTitle = new QLabel(this);
layout->addWidget(mPostTitle);
QString ss = (msg.length() > 30)? (msg.left(30)+"..."):msg;
mPostTitle->setText(ss);
QFont font = mPostTitle->font();
if(mFlags & ChannelPostThumbnailView::FLAG_SCALE_FONT)
font.setPointSizeF(FONT_SCALE_FACTOR * DEFAULT_SIZE_IN_FONT_HEIGHT / 5.0 * font.pointSizeF());
else
font.setPointSizeF(DEFAULT_SIZE_IN_FONT_HEIGHT / 5.0 * font.pointSizeF());
if(is_msg_new)
font.setBold(true);
mPostTitle->setFont(font);
mPostTitle->setMaximumWidth(W);
mPostTitle->setWordWrap(true);
}
setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding);
layout->addStretch();
setLayout(layout);
adjustSize();
update();
}
ChannelPostThumbnailView::AspectRatio ChannelPostThumbnailView::bestAspectRatio()
{
if(mPostImage->originalImage().isNull())
return ASPECT_RATIO_1_1;
float as = mPostImage->originalImage().height() / (float)mPostImage->originalImage().width() ;
if(as <= 0.8)
return ASPECT_RATIO_16_9;
else if(as < 1.15)
return ASPECT_RATIO_1_1;
else
return ASPECT_RATIO_2_3;
}
QSize ChannelPostThumbnailView::actualSize() const
{
QFontMetricsF fm(font());
if(mPostTitle != nullptr)
{
QMargins cm = layout()->contentsMargins();
return QSize(width(),
mPostTitle->height() + mPostImage->height() + 0.5*fm.height()
+ cm.top() + cm.bottom() + layout()->spacing());
}
else
return size();
}
float ChannelPostThumbnailView::thumbnail_w() const
{
switch(mAspectRatio)
{
default:
case ASPECT_RATIO_1_1:
case ASPECT_RATIO_UNKNOWN: return DEFAULT_SIZE_IN_FONT_HEIGHT;
case ASPECT_RATIO_2_3: return DEFAULT_SIZE_IN_FONT_HEIGHT;
case ASPECT_RATIO_16_9: return DEFAULT_SIZE_IN_FONT_HEIGHT;
}
}
float ChannelPostThumbnailView::thumbnail_h() const
{
switch(mAspectRatio)
{
default:
case ASPECT_RATIO_1_1:
case ASPECT_RATIO_UNKNOWN: return DEFAULT_SIZE_IN_FONT_HEIGHT;
case ASPECT_RATIO_2_3: return DEFAULT_SIZE_IN_FONT_HEIGHT * 3.0/2.0;
case ASPECT_RATIO_16_9: return DEFAULT_SIZE_IN_FONT_HEIGHT * 9.0/16.0;
}
}
void ZoomableLabel::reset()
{
mCenterX = mFullImage.width()/2.0;
mCenterY = mFullImage.height()/2.0;
mZoomFactor = 1.0/std::max(width() / (float)mFullImage.width(), height()/(float)mFullImage.height());
updateView();
}
void ZoomableLabel::mouseMoveEvent(QMouseEvent *me)
{
if(!mZoomEnabled)
return;
float new_center_x = mCenterX - (me->x() - mLastX);
float new_center_y = mCenterY - (me->y() - mLastY);
mLastX = me->x();
mLastY = me->y();
if(new_center_x - 0.5 * width()*mZoomFactor < 0) return;
if(new_center_y - 0.5 *height()*mZoomFactor < 0) return;
if(new_center_x + 0.5 * width()*mZoomFactor >= mFullImage.width()) return;
if(new_center_y + 0.5 *height()*mZoomFactor >=mFullImage.height()) return;
mCenterX = new_center_x;
mCenterY = new_center_y;
updateView();
}
void ZoomableLabel::mousePressEvent(QMouseEvent *me)
{
mMoving = true;
mLastX = me->x();
mLastY = me->y();
}
void ZoomableLabel::mouseReleaseEvent(QMouseEvent *)
{
mMoving = false;
}
void ZoomableLabel::wheelEvent(QWheelEvent *me)
{
if(!mZoomEnabled)
return;
float new_zoom_factor = (me->delta() > 0)?(mZoomFactor*1.05):(mZoomFactor/1.05);
float new_center_x = mCenterX;
float new_center_y = mCenterY;
// Try to find centerX and centerY so that the crop does not overlap the original image
float min_x = 0.5 * width()*new_zoom_factor;
float max_x = mFullImage.width() - 0.5 * width()*new_zoom_factor;
float min_y = 0.5 * height()*new_zoom_factor;
float max_y = mFullImage.height() - 0.5 * height()*new_zoom_factor;
if(min_x >= max_x) return;
if(min_y >= max_y) return;
if(new_center_x < min_x) new_center_x = min_x;
if(new_center_y < min_y) new_center_y = min_y;
if(new_center_x > max_x) new_center_x = max_x;
if(new_center_y > max_y) new_center_y = max_y;
mZoomFactor = new_zoom_factor;
mCenterX = new_center_x;
mCenterY = new_center_y;
updateView();
}
QPixmap ZoomableLabel::extractCroppedScaledPicture() const
{
QRect rect(mCenterX - 0.5 * width()*mZoomFactor, mCenterY - 0.5 * height()*mZoomFactor, width()*mZoomFactor, height()*mZoomFactor);
QPixmap pix = mFullImage.copy(rect).scaledToHeight(height(),Qt::SmoothTransformation);
return pix;
}
void ZoomableLabel::setPicture(const QPixmap& pix)
{
mFullImage = pix;
reset();
updateView();
}
void ZoomableLabel::resizeEvent(QResizeEvent *e)
{
QLabel::resizeEvent(e);
updateView();
}
void ZoomableLabel::updateView()
{
// The new image will be cropped from the original image, using the following rules:
// - first the cropped image size is computed
// - then center is calculated so that
// - the original center is preferred
// - if the crop overlaps the image border, the center is moved.
QRect rect(mCenterX - 0.5 * width()*mZoomFactor, mCenterY - 0.5 * height()*mZoomFactor, width()*mZoomFactor, height()*mZoomFactor);
QLabel::setPixmap(mFullImage.copy(rect));
}

View File

@ -32,6 +32,39 @@
#include "gui/gxs/GxsIdDetails.h" #include "gui/gxs/GxsIdDetails.h"
#include "gui/common/FilesDefs.h" #include "gui/common/FilesDefs.h"
// Class to provide a label in which the image can be zoomed/moved. The widget size is fixed by the GUI and the user can move/zoom the image
// inside the window formed by the widget. When happy, the view-able part of the image can be extracted.
class ZoomableLabel: public QLabel
{
public:
ZoomableLabel(QWidget *parent): QLabel(parent),mZoomFactor(1.0),mCenterX(0.0),mCenterY(0.0),mZoomEnabled(true) {}
void setPicture(const QPixmap& pix);
void setEnableZoom(bool b) { mZoomEnabled = b; }
void reset();
QPixmap extractCroppedScaledPicture() const;
void updateView();
const QPixmap& originalImage() const { return mFullImage ; }
protected:
void mousePressEvent(QMouseEvent *ev) override;
void mouseReleaseEvent(QMouseEvent *ev) override;
void mouseMoveEvent(QMouseEvent *ev) override;
void resizeEvent(QResizeEvent *ev) override;
void wheelEvent(QWheelEvent *me) override;
QPixmap mFullImage;
float mCenterX;
float mCenterY;
float mZoomFactor;
int mLastX,mLastY;
bool mMoving;
bool mZoomEnabled;
};
// Class to paint the thumbnails with title // Class to paint the thumbnails with title
class ChannelPostThumbnailView: public QWidget class ChannelPostThumbnailView: public QWidget
@ -39,8 +72,20 @@ class ChannelPostThumbnailView: public QWidget
Q_OBJECT Q_OBJECT
public: public:
typedef enum {
ASPECT_RATIO_UNKNOWN = 0x00,
ASPECT_RATIO_2_3 = 0x01,
ASPECT_RATIO_1_1 = 0x02,
ASPECT_RATIO_16_9 = 0x03,
} AspectRatio;
// This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. // This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen.
static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0; static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0;
static constexpr uint32_t FLAG_NONE = 0x00;
static constexpr uint32_t FLAG_SHOW_TEXT = 0x01;
static constexpr uint32_t FLAG_ALLOW_PAN = 0x02;
static constexpr uint32_t FLAG_SCALE_FONT= 0x04;
// Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good. // Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good.
@ -49,77 +94,40 @@ public:
static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png"; static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png";
virtual ~ChannelPostThumbnailView() virtual ~ChannelPostThumbnailView();
{ ChannelPostThumbnailView(QWidget *parent=NULL,uint32_t flags=FLAG_ALLOW_PAN | FLAG_SHOW_TEXT | FLAG_SCALE_FONT);
delete lb; ChannelPostThumbnailView(const RsGxsChannelPost& post,uint32_t flags,QWidget *parent=NULL);
delete lt;
}
ChannelPostThumbnailView(QWidget *parent=NULL): QWidget(parent) void init(const RsGxsChannelPost& post);
{
init(FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE), QString("New post"),false);
}
ChannelPostThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL) void setAspectRatio(AspectRatio r);
: QWidget(parent) void setPixmap(const QPixmap& p,bool guess_aspect_ratio) ;
{ QPixmap getCroppedScaledPicture() const { return mPostImage->extractCroppedScaledPicture() ; }
// now fill the data
QPixmap thumbnail; void setText(const QString& s);
if(post.mThumbnail.mSize > 0) // This is used to allow to render the widget into a pixmap without the white space that Qt adds vertically. There is *no way* apparently
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); // to get rid of that bloody space. It depends on the aspect ratio of the image and it only shows up when the text label is shown.
else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row) // The label however has a correct size. It seems that Qt doesn't like widgets with horizontal aspect ratio and forces the size accordingly.
thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE);
init(thumbnail, QString::fromUtf8(post.mMeta.mMsgName.c_str()), IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) ); QSize actualSize() const ;
}
void init(const QPixmap& thumbnail,const QString& msg,bool is_msg_new)
{
QVBoxLayout *layout = new QVBoxLayout(this);
lb = new QLabel(this);
lb->setScaledContents(true);
layout->addWidget(lb);
lt = new QLabel(this);
layout->addWidget(lt);
setLayout(layout);
setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
QFontMetricsF fm(font());
int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ;
int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ;
lb->setFixedSize(W,H);
lb->setPixmap(thumbnail);
lt->setText(msg);
QFont font = lt->font();
if(is_msg_new)
{
font.setBold(true);
lt->setFont(font);
}
lt->setMaximumWidth(W);
lt->setWordWrap(true);
adjustSize();
update();
}
void setPixmap(const QPixmap& p) { lb->setPixmap(p); }
void setText(const QString& s) { lt->setText(s); }
/*!
* \brief bestAspectRatio
* Computes the preferred aspect ratio for the image in the post. The default is 1:1.
* \return the prefered aspect ratio
*/
AspectRatio bestAspectRatio() ;
private: private:
QLabel *lb; static const float DEFAULT_SIZE_IN_FONT_HEIGHT ;
QLabel *lt; static const float FONT_SCALE_FACTOR ;
float thumbnail_w() const;
float thumbnail_h() const;
ZoomableLabel *mPostImage;
QLabel *mPostTitle;
uint32_t mFlags;
AspectRatio mAspectRatio;
}; };

View File

@ -44,7 +44,7 @@ Q_DECLARE_METATYPE(RsGxsChannelPost)
std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent) RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent)
: QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6) : QAbstractItemModel(parent), mTreeMode(RsGxsChannelPostsModel::TREE_MODE_GRID), mColumns(6)
{ {
initEmptyHierarchy(); initEmptyHierarchy();
@ -62,6 +62,16 @@ RsGxsChannelPostsModel::~RsGxsChannelPostsModel()
rsEvents->unregisterEventsHandler(mEventHandlerId); rsEvents->unregisterEventsHandler(mEventHandlerId);
} }
void RsGxsChannelPostsModel::setMode(TreeMode mode)
{
mTreeMode = mode;
if(mode == TREE_MODE_LIST)
setNumColumns(2);
triggerViewUpdate();
}
void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEvent> event) void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{ {
const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get()); const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get());
@ -106,7 +116,7 @@ void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEve
{ {
mPosts[j] = posts[i]; mPosts[j] = posts[i];
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL)); triggerViewUpdate();
} }
} }
},this); },this);
@ -124,25 +134,23 @@ void RsGxsChannelPostsModel::initEmptyHierarchy()
mPosts.clear(); mPosts.clear();
mFilteredPosts.clear(); mFilteredPosts.clear();
// mPosts.resize(1); // adds a sentinel item
// mPosts[0].mMeta.mMsgName = "Root sentinel post" ;
// mFilteredPosts.resize(1);
// mFilteredPosts[0] = 1;
postMods(); postMods();
} }
void RsGxsChannelPostsModel::preMods() void RsGxsChannelPostsModel::preMods()
{ {
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
beginResetModel(); beginResetModel();
} }
void RsGxsChannelPostsModel::postMods() void RsGxsChannelPostsModel::postMods()
{ {
endResetModel(); endResetModel();
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL)); triggerViewUpdate();
}
void RsGxsChannelPostsModel::triggerViewUpdate()
{
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(rowCount()-1,mColumns-1,(void*)NULL));
} }
void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files) void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files)
@ -161,35 +169,30 @@ void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files)
files.push_back(it.second); files.push_back(it.second);
} }
void RsGxsChannelPostsModel::setFilter(const QStringList& strings, uint32_t& count) void RsGxsChannelPostsModel::setFilter(const QStringList& strings,bool only_unread, uint32_t& count)
{ {
preMods(); preMods();
beginRemoveRows(QModelIndex(),0,rowCount()-1); beginRemoveRows(QModelIndex(),0,rowCount()-1);
endRemoveRows(); endRemoveRows();
if(strings.empty()) mFilteredPosts.clear();
//mFilteredPosts.push_back(0);
for(size_t i=0;i<mPosts.size();++i)
{ {
mFilteredPosts.clear(); bool passes_strings = true;
for(size_t i=0;i<mPosts.size();++i)
for(auto& s:strings)
passes_strings = passes_strings && QString::fromStdString(mPosts[i].mMeta.mMsgName).contains(s,Qt::CaseInsensitive);
if(strings.empty())
passes_strings = true;
if(passes_strings && (!only_unread || (IS_MSG_UNREAD(mPosts[i].mMeta.mMsgStatus) || IS_MSG_NEW(mPosts[i].mMeta.mMsgStatus))))
mFilteredPosts.push_back(i); mFilteredPosts.push_back(i);
} }
else
{
mFilteredPosts.clear();
//mFilteredPosts.push_back(0);
for(size_t i=0;i<mPosts.size();++i)
{
bool passes_strings = true;
for(auto& s:strings)
passes_strings = passes_strings && QString::fromStdString(mPosts[i].mMeta.mMsgName).contains(s,Qt::CaseInsensitive);
if(passes_strings)
mFilteredPosts.push_back(i);
}
}
count = mFilteredPosts.size(); count = mFilteredPosts.size();
std::cerr << "After filtering: " << count << " posts remain." << std::endl; std::cerr << "After filtering: " << count << " posts remain." << std::endl;
@ -209,7 +212,12 @@ int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const
return 0; return 0;
if(!parent.isValid()) if(!parent.isValid())
return (mFilteredPosts.size() + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 {
if(mTreeMode == TREE_MODE_GRID)
return (mFilteredPosts.size() + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
else
return mFilteredPosts.size();
}
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
return 0; return 0;
@ -217,7 +225,10 @@ int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const
int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const
{ {
return std::min((int)mFilteredPosts.size(),(int)mColumns) ; if(mTreeMode == TREE_MODE_GRID)
return std::min((int)mFilteredPosts.size(),(int)mColumns) ;
else
return 2;
} }
bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const
@ -284,7 +295,7 @@ QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex
if(row < 0 || column < 0 || column >= (int)mColumns) if(row < 0 || column < 0 || column >= (int)mColumns)
return QModelIndex(); return QModelIndex();
quintptr ref = getChildRef(parent.internalId(),column + row*mColumns); quintptr ref = getChildRef(parent.internalId(),(mTreeMode == TREE_MODE_GRID)?(column + row*mColumns):row);
#ifdef DEBUG_CHANNEL_MODEL #ifdef DEBUG_CHANNEL_MODEL
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl; std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
@ -363,8 +374,6 @@ quintptr RsGxsChannelPostsModel::getParentRow(quintptr ref,int& row) const
int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const
{ {
uint32_t entry = 0 ;
if(ref == quintptr(0)) if(ref == quintptr(0))
return rowCount()-1; return rowCount()-1;
@ -421,7 +430,7 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
} }
} }
QVariant RsGxsChannelPostsModel::sizeHintRole(int col) const QVariant RsGxsChannelPostsModel::sizeHintRole(int /* col */) const
{ {
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
@ -493,7 +502,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto
std::sort(mPosts.begin(),mPosts.end()); std::sort(mPosts.begin(),mPosts.end());
mFilteredPosts.clear(); mFilteredPosts.clear();
for(int i=0;i<mPosts.size();++i) for(uint32_t i=0;i<mPosts.size();++i)
mFilteredPosts.push_back(i); mFilteredPosts.push_back(i);
#ifdef DEBUG_CHANNEL_MODEL #ifdef DEBUG_CHANNEL_MODEL
@ -564,8 +573,6 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
}); });
} }
static bool decreasing_time_comp(const std::pair<time_t,RsGxsMessageId>& e1,const std::pair<time_t,RsGxsMessageId>& e2) { return e2.first < e1.first ; }
void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& posts) void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& posts)
{ {
// collect new versions of posts if any // collect new versions of posts if any
@ -689,10 +696,25 @@ void RsGxsChannelPostsModel::setAllMsgReadStatus(bool read_status)
// No need to call preMods()/postMods() here because we're not changing the model // No need to call preMods()/postMods() here because we're not changing the model
// All operations below are done async // All operations below are done async
RsThread::async([this, read_status]() // 1 - copy all msg/grp id groups, so that changing the mPosts list while calling the async method will not break the work
std::vector<RsGxsGrpMsgIdPair> pairs;
for(uint32_t i=0;i<mPosts.size();++i)
{ {
for(uint32_t i=0;i<mPosts.size();++i) bool post_status = !((IS_MSG_UNREAD(mPosts[i].mMeta.mMsgStatus) || IS_MSG_NEW(mPosts[i].mMeta.mMsgStatus)));
rsGxsChannels->markRead(RsGxsGrpMsgIdPair(mPosts[i].mMeta.mGroupId,mPosts[i].mMeta.mMsgId),read_status);
if(post_status != read_status)
pairs.push_back(RsGxsGrpMsgIdPair(mPosts[i].mMeta.mGroupId,mPosts[i].mMeta.mMsgId));
}
// 2 - then call the async methods
RsThread::async([pairs, read_status]()
{
for(uint32_t i=0;i<pairs.size();++i)
if(!rsGxsChannels->markRead(pairs[i],read_status))
RsErr() << "setAllMsgReadStatus: failed to change status of msg " << pairs[i].first << " in group " << pairs[i].second << " to status " << read_status << std::endl;
}); });
} }
@ -720,15 +742,18 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid)
for(uint32_t i=0;i<mFilteredPosts.size();++i) for(uint32_t i=0;i<mFilteredPosts.size();++i)
{ {
// First look into msg versions, in case the msg is a version of an existing message // First look into msg versions, in case the msg is a version of an existing message
for(auto& msg_id:mPosts[mFilteredPosts[i]].mOlderVersions) for(auto& msg_id:mPosts[mFilteredPosts[i]].mOlderVersions)
if(msg_id == postId) if(msg_id == postId)
{ {
quintptr ref ; quintptr ref ;
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab 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/mColumns,i%mColumns, ref); if(mTreeMode == TREE_MODE_GRID)
return createIndex(i/mColumns,i%mColumns, ref);
else
return createIndex(i,0, ref);
} }
if(mPosts[mFilteredPosts[i]].mMeta.mMsgId == postId) if(mPosts[mFilteredPosts[i]].mMeta.mMsgId == postId)
@ -736,7 +761,10 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid)
quintptr ref ; quintptr ref ;
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab 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/mColumns,i%mColumns, ref); if(mTreeMode == TREE_MODE_GRID)
return createIndex(i/mColumns,i%mColumns, ref);
else
return createIndex(i,0, ref);
} }
} }

View File

@ -92,8 +92,8 @@ public:
#endif #endif
enum TreeMode{ TREE_MODE_UNKWN = 0x00, enum TreeMode{ TREE_MODE_UNKWN = 0x00,
TREE_MODE_PLAIN = 0x01, TREE_MODE_GRID = 0x01,
TREE_MODE_FILES = 0x02, TREE_MODE_LIST = 0x02,
}; };
#ifdef TODO #ifdef TODO
@ -107,11 +107,18 @@ public:
std::vector<std::pair<time_t,RsGxsMessageId> > getPostVersions(const RsGxsMessageId& mid) const; std::vector<std::pair<time_t,RsGxsMessageId> > getPostVersions(const RsGxsMessageId& mid) const;
uint32_t getNumberOfPosts() { return mPosts.size() ; }
const RsGxsChannelPost& post(uint32_t i) const { return mPosts[i]; }
// This method will asynchroneously update the data // This method will asynchroneously update the data
void updateChannel(const RsGxsGroupId& channel_group_id); void updateChannel(const RsGxsGroupId& channel_group_id);
const RsGxsGroupId& currentGroupId() const; const RsGxsGroupId& currentGroupId() const;
void triggerViewUpdate();
void setNumColumns(int n); void setNumColumns(int n);
void setMode(TreeMode mode);
TreeMode getMode() const { return mTreeMode; }
// Retrieve the full list of files for all posts. // Retrieve the full list of files for all posts.
@ -130,7 +137,7 @@ public:
void setMsgReadStatus(const QModelIndex &i, bool read_status); void setMsgReadStatus(const QModelIndex &i, bool read_status);
void setAllMsgReadStatus(bool read_status); void setAllMsgReadStatus(bool read_status);
void setFilter(const QStringList &strings, uint32_t &count) ; void setFilter(const QStringList &strings, bool only_unread,uint32_t &count) ;
#ifdef TODO #ifdef TODO
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
@ -228,8 +235,6 @@ private:
std::vector<int> mFilteredPosts; // stores the list of displayes indices due to filtering. std::vector<int> mFilteredPosts; // stores the list of displayes indices due to filtering.
std::vector<RsGxsChannelPost> mPosts ; // store the list of posts updated from rsForums. std::vector<RsGxsChannelPost> mPosts ; // store the list of posts updated from rsForums.
//std::map<RsGxsMessageId,std::vector<std::pair<time_t,RsGxsMessageId> > > mPostVersions; // stores versions of posts
QColor mTextColorRead ; QColor mTextColorRead ;
QColor mTextColorUnread ; QColor mTextColorUnread ;
QColor mTextColorUnreadChildren; QColor mTextColorUnreadChildren;

View File

@ -61,6 +61,8 @@ static const int CHANNEL_TABS_DETAILS= 0;
static const int CHANNEL_TABS_POSTS = 1; static const int CHANNEL_TABS_POSTS = 1;
static const int CHANNEL_TABS_FILES = 2; static const int CHANNEL_TABS_FILES = 2;
QColor SelectedColor = QRgb(0xff308dc7);
/* View mode */ /* View mode */
#define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FEEDS 1
#define VIEW_MODE_FILES 2 #define VIEW_MODE_FILES 2
@ -74,14 +76,23 @@ static const int CHANNEL_TABS_FILES = 2;
#define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png" #define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png"
#define IMAGE_COPYLINK ":/images/copyrslink.png" #define IMAGE_COPYLINK ":/images/copyrslink.png"
#define IMAGE_GRID_VIEW ":icons/png/menu.png"
#define IMAGE_DOWNLOAD ":icons/png/download.png"
Q_DECLARE_METATYPE(ChannelPostFileInfo) Q_DECLARE_METATYPE(ChannelPostFileInfo)
// Delegate used to paint into the table of thumbnails // Delegate used to paint into the table of thumbnails
int ChannelPostDelegate::cellSize(const QFont& font) const //===============================================================================================================================================//
//=== ChannelPostDelegate ===//
//===============================================================================================================================================//
int ChannelPostDelegate::cellSize(int col,const QFont& font,uint32_t parent_width) const
{ {
return mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height(); if(mUseGrid || col==0)
return mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height();
else
return 0.8*parent_width - mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height();
} }
void ChannelPostDelegate::zoom(bool zoom_or_unzoom) void ChannelPostDelegate::zoom(bool zoom_or_unzoom)
@ -91,53 +102,106 @@ void ChannelPostDelegate::zoom(bool zoom_or_unzoom)
else else
mZoom /= 1.02; mZoom /= 1.02;
std::cerr << "zoom factor: " << mZoom << std::endl; if(mZoom < 0.5)
mZoom = 0.5;
if(mZoom > 2.0)
mZoom = 2.0;
} }
void ChannelPostDelegate::setAspectRatio(ChannelPostThumbnailView::AspectRatio r)
{
mAspectRatio = r;
}
void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{ {
// prepare // prepare
painter->save(); painter->save();
painter->setClipRect(option.rect); painter->setClipRect(option.rect);
RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ; RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ;
painter->fillRect( option.rect, option.backgroundBrush); painter->fillRect( option.rect, option.backgroundBrush);
painter->restore(); painter->restore();
ChannelPostThumbnailView w(post); if(mUseGrid || index.column()==0)
{
// Draw a thumbnail
QPixmap pixmap(w.size()); uint32_t flags = (mUseGrid)?(ChannelPostThumbnailView::FLAG_SHOW_TEXT | ChannelPostThumbnailView::FLAG_SCALE_FONT):0;
ChannelPostThumbnailView w(post,flags);
w.setAspectRatio(mAspectRatio);
w.updateGeometry();
w.adjustSize();
if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row) QPixmap pixmap(w.size());
pixmap.fill(QRgb(0xff308dc7)); // I dont know how to grab the backgroud color for selected objects automatically.
else
pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background
w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row)
pixmap.fill(SelectedColor); // I dont know how to grab the backgroud color for selected objects automatically.
else
pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background
if(mZoom != 1.0) w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background
pixmap = pixmap.scaled(mZoom*pixmap.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) // We extract from the pixmap the part of the widget that we want. Saddly enough, Qt adds some white space
{ // below the widget and there is no way to control that.
QPainter p(&pixmap);
QFontMetricsF fm(option.font);
p.drawPixmap(mZoom*QPoint(6.2*fm.height(),6.9*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(mZoom*7*fm.height(),mZoom*7*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
}
// debug pixmap = pixmap.copy(QRect(0,0,w.actualSize().width(),w.actualSize().height()));
// if(index.row()==0 && index.column()==0)
// {
// QFile file("yourFile.png");
// file.open(QIODevice::WriteOnly);
// pixmap.save(&file, "PNG");
// std::cerr << "Saved pxmap to png" << std::endl;
// }
//std::cerr << "option.rect = " << option.rect.width() << "x" << option.rect.height() << ". fm.height()=" << QFontMetricsF(option.font).height() << std::endl;
painter->drawPixmap(option.rect.topLeft(), // if(index.row()==0 && index.column()==0)
pixmap.scaled(option.rect.width(),option.rect.width()*w.height()/(float)w.width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); // {
// QFile file("yourFile.png");
// file.open(QIODevice::WriteOnly);
// pixmap.save(&file, "PNG");
// file.close();
// }
if(mUseGrid || index.column()==0)
{
if(mZoom != 1.0)
pixmap = pixmap.scaled(mZoom*pixmap.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus))
{
QPainter p(&pixmap);
QFontMetricsF fm(option.font);
p.drawPixmap(mZoom*QPoint(0.1*fm.height(),-3.6*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(mZoom*7*fm.height(),mZoom*7*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
}
}
painter->drawPixmap(option.rect.topLeft(),
pixmap.scaled(option.rect.width(),option.rect.width()*pixmap.height()/(float)pixmap.width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation));
}
else
{
// We're drawing the text on the second column
uint32_t font_height = QFontMetricsF(option.font).height();
QPoint p = option.rect.topLeft();
float y = p.y() + font_height;
painter->save();
if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row)
painter->setPen(SelectedColor);
if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus))
{
QFont font(option.font);
font.setBold(true);
painter->setFont(font);
}
painter->drawText(QPoint(p.x()+0.5*font_height,y),QString::fromUtf8(post.mMeta.mMsgName.c_str()));
y += font_height;
painter->drawText(QPoint(p.x()+0.5*font_height,y),QDateTime::fromSecsSinceEpoch(post.mMeta.mPublishTs).toString(Qt::DefaultLocaleShortDate));
y += font_height;
painter->drawText(QPoint(p.x()+0.5*font_height,y),QString::number(post.mCount)+ " " +((post.mCount>1)?tr("files"):tr("file")) + " (" + QString::number(post.mSize) + " " + tr("bytes") + ")" );
y += font_height;
painter->restore();
}
} }
QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
@ -146,9 +210,36 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM
QFontMetricsF fm(option.font); QFontMetricsF fm(option.font);
return QSize(mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height(),mZoom*COLUMN_SIZE_FONT_FACTOR_H*fm.height()); RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ;
uint32_t flags = (mUseGrid)?(ChannelPostThumbnailView::FLAG_SHOW_TEXT | ChannelPostThumbnailView::FLAG_SCALE_FONT):0;
ChannelPostThumbnailView w(post,flags);
w.setAspectRatio(mAspectRatio);
w.updateGeometry();
w.adjustSize();
//std::cerr << "w.size(): " << w.width() << " x " << w.height() << ". Actual size: " << w.actualSize().width() << " x " << w.actualSize().height() << std::endl;
float aspect_ratio = w.actualSize().height()/(float)w.actualSize().width();
float cell_width = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height();
float cell_height = mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height()*aspect_ratio;
if(mUseGrid || index.column()==0)
return QSize(cell_width,cell_height);
else
return QSize(option.rect.width()-cell_width,cell_height);
} }
void ChannelPostDelegate::setWidgetGrid(bool use_grid)
{
mUseGrid = use_grid;
}
//===============================================================================================================================================//
//=== ChannelPostFilesDelegate ===//
//===============================================================================================================================================//
QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const
{ {
ChannelPostFileInfo file = index.data(Qt::UserRole).value<ChannelPostFileInfo>() ; ChannelPostFileInfo file = index.data(Qt::UserRole).value<ChannelPostFileInfo>() ;
@ -222,6 +313,10 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con
} }
} }
//===============================================================================================================================================//
//=== GxsChannelPostWidgetWithModel ===//
//===============================================================================================================================================//
/** Constructor */ /** Constructor */
GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) :
GxsMessageFrameWidget(rsGxsChannels, parent), GxsMessageFrameWidget(rsGxsChannels, parent),
@ -230,13 +325,25 @@ 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(mChannelPostsModel = new RsGxsChannelPostsModel()); ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/gridlayout.svg"));
ui->viewType_TB->setToolTip(tr("Click to switch to list view"));
connect(ui->viewType_TB,SIGNAL(clicked()),this,SLOT(switchView()));
ui->showUnread_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png"));
ui->showUnread_TB->setChecked(false);
ui->showUnread_TB->setToolTip(tr("Show unread posts only"));
connect(ui->showUnread_TB,SIGNAL(toggled(bool)),this,SLOT(switchOnlyUnread(bool)));
ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel());
ui->postsTree->setItemDelegate(mChannelPostsDelegate = new ChannelPostDelegate()); ui->postsTree->setItemDelegate(mChannelPostsDelegate = new ChannelPostDelegate());
ui->postsTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // prevents bug on w10, since row size depends on widget width ui->postsTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // prevents bug on w10, since row size depends on widget width
ui->postsTree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);// more beautiful if we scroll at pixel level ui->postsTree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);// more beautiful if we scroll at pixel level
ui->postsTree->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); ui->postsTree->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
mChannelPostsDelegate->setAspectRatio(ChannelPostThumbnailView::ASPECT_RATIO_16_9);
connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(updateZoomFactor(bool))); connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(updateZoomFactor(bool)));
connect(ui->commentsDialog,SIGNAL(commentsLoaded(int)),this,SLOT(updateCommentsCount(int)));
ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this)); ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this));
ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate());
@ -262,7 +369,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
ui->postTime_LB->hide(); ui->postTime_LB->hide();
ui->postLogo_LB->hide(); ui->postLogo_LB->hide();
ui->postDetails_TE->setPlaceholderText(tr("No post selected")); ui->postDetails_TE->setPlaceholderText(tr("No text to display"));
// Set initial size of the splitter // Set initial size of the splitter
ui->splitter->setStretchFactor(0, 1); ui->splitter->setStretchFactor(0, 1);
@ -270,8 +377,9 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI
QFontMetricsF fm(font()); QFontMetricsF fm(font());
for(int i=0;i<mChannelPostsModel->columnCount();++i) if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID)
ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(font())); for(int i=0;i<mChannelPostsModel->columnCount();++i)
ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width()));
/* Setup UI helper */ /* Setup UI helper */
@ -339,12 +447,11 @@ void GxsChannelPostsWidgetWithModel::updateZoomFactor(bool zoom_or_unzoom)
mChannelPostsDelegate->zoom(zoom_or_unzoom); mChannelPostsDelegate->zoom(zoom_or_unzoom);
for(int i=0;i<mChannelPostsModel->columnCount();++i) for(int i=0;i<mChannelPostsModel->columnCount();++i)
ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(font())); ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width()));
QSize s = ui->postsTree->size(); QSize s = ui->postsTree->size();
int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(font())))); int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),s.width()))));
std::cerr << "nb columns: " << n_columns << std::endl;
mChannelPostsModel->setNumColumns(n_columns); // forces the update mChannelPostsModel->setNumColumns(n_columns); // forces the update
@ -366,7 +473,28 @@ void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&)
{ {
QMenu menu(this); QMenu menu(this);
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); #ifdef TO_REMOVE
if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID)
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_GRID_VIEW), tr("Switch to list view"), this, SLOT(switchView()));
else
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_GRID_VIEW), tr("Switch to grid view"), this, SLOT(switchView()));
menu.addSeparator();
#endif
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
if(index.isValid())
{
RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ;
if(!post.mFiles.empty())
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DOWNLOAD), tr("Download files"), this, SLOT(download()));
if(!IS_MSG_UNREAD(post.mMeta.mMsgStatus) && !IS_MSG_NEW(post.mMeta.mMsgStatus))
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Mark as unread"), this, SLOT(markMessageUnread()));
}
menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink()));
if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags))
menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost())); menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost()));
@ -374,6 +502,70 @@ void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&)
menu.exec(QCursor::pos()); menu.exec(QCursor::pos());
} }
void GxsChannelPostsWidgetWithModel::markMessageUnread()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
mChannelPostsModel->setMsgReadStatus(index,false);
}
RsGxsMessageId GxsChannelPostsWidgetWithModel::getCurrentItemId() const
{
RsGxsMessageId selected_msg_id ;
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
if(index.isValid())
selected_msg_id = index.data(Qt::UserRole).value<RsGxsChannelPost>().mMeta.mMsgId ;
return selected_msg_id;
}
void GxsChannelPostsWidgetWithModel::selectItem(const RsGxsMessageId& msg_id)
{
auto index = mChannelPostsModel->getIndexOfMessage(msg_id);
ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect);
ui->postsTree->scrollTo(index);//May change if model reloaded
}
void GxsChannelPostsWidgetWithModel::switchView()
{
auto msg_id = getCurrentItemId();
if(mChannelPostsModel->getMode() == RsGxsChannelPostsModel::TREE_MODE_GRID)
{
ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/listlayout.svg"));
ui->viewType_TB->setToolTip(tr("Click to switch to grid view"));
ui->postsTree->setSelectionBehavior(QAbstractItemView::SelectRows);
mChannelPostsDelegate->setWidgetGrid(false);
mChannelPostsModel->setMode(RsGxsChannelPostsModel::TREE_MODE_LIST);
}
else
{
ui->viewType_TB->setIcon(FilesDefs::getIconFromQtResourcePath(":icons/svg/gridlayout.svg"));
ui->viewType_TB->setToolTip(tr("Click to switch to list view"));
ui->postsTree->setSelectionBehavior(QAbstractItemView::SelectItems);
mChannelPostsDelegate->setWidgetGrid(true);
mChannelPostsModel->setMode(RsGxsChannelPostsModel::TREE_MODE_GRID);
handlePostsTreeSizeChange(ui->postsTree->size(),true);
}
for(int i=0;i<mChannelPostsModel->columnCount();++i)
ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(i,font(),ui->postsTree->width()));
selectItem(msg_id);
ui->postsTree->setFocus();
mChannelPostsModel->triggerViewUpdate(); // This is already called by setMode(), but the model cannot know how many
// columns is actually has until we call handlePostsTreeSizeChange(), so
// we have to call it again here.
}
void GxsChannelPostsWidgetWithModel::copyMessageLink() void GxsChannelPostsWidgetWithModel::copyMessageLink()
{ {
try try
@ -405,6 +597,30 @@ void GxsChannelPostsWidgetWithModel::copyMessageLink()
QMessageBox::critical(NULL,tr("Link creation error"),tr("Link could not be created: ")+e.what()); QMessageBox::critical(NULL,tr("Link creation error"),tr("Link could not be created: ")+e.what());
} }
} }
void GxsChannelPostsWidgetWithModel::download()
{
QModelIndex index = ui->postsTree->selectionModel()->currentIndex();
RsGxsChannelPost post = index.data(Qt::UserRole).value<RsGxsChannelPost>() ;
std::string destination;
rsGxsChannels->getChannelDownloadDirectory(mGroup.mMeta.mGroupId,destination);
for(auto file:post.mFiles)
{
std::list<RsPeerId> sources;
std::string destination;
// Add possible direct sources.
FileInfo fileInfo;
rsFiles->FileDetails(file.mHash, RS_FILE_HINTS_REMOTE, fileInfo);
for(std::vector<TransferInfo>::const_iterator it = fileInfo.peers.begin(); it != fileInfo.peers.end(); ++it) {
sources.push_back((*it).peerId);
}
rsFiles->FileRequest(file.mName, file.mHash, file.mSize, destination, RS_FILE_REQ_ANONYMOUS_ROUTING, sources);
}
}
void GxsChannelPostsWidgetWithModel::editPost() void GxsChannelPostsWidgetWithModel::editPost()
{ {
@ -415,12 +631,15 @@ void GxsChannelPostsWidgetWithModel::editPost()
msgDialog->show(); msgDialog->show();
} }
void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s,bool force)
{ {
int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(font())))); if(mChannelPostsModel->getMode() != RsGxsChannelPostsModel::TREE_MODE_GRID)
std::cerr << "nb columns: " << n_columns << std::endl; return;
if(n_columns != mChannelPostsModel->columnCount()) int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(0,font(),ui->postsTree->width()))));
std::cerr << "nb columns: " << n_columns << " current count=" << mChannelPostsModel->columnCount() << std::endl;
if(force || (n_columns != mChannelPostsModel->columnCount()))
mChannelPostsModel->setNumColumns(n_columns); mChannelPostsModel->setNumColumns(n_columns);
} }
@ -461,10 +680,12 @@ void GxsChannelPostsWidgetWithModel::showPostDetails()
ui->postLogo_LB->hide(); ui->postLogo_LB->hide();
ui->postName_LB->hide(); ui->postName_LB->hide();
ui->postTime_LB->hide(); ui->postTime_LB->hide();
mChannelPostFilesModel->clear(); mChannelPostFilesModel->clear();
ui->details_TW->setEnabled(false);
return; return;
} }
ui->details_TW->setEnabled(true);
ui->postLogo_LB->show(); ui->postLogo_LB->show();
ui->postName_LB->show(); ui->postName_LB->show();
@ -506,7 +727,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails()
ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W);
ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str()));
ui->postName_LB->setFixedWidth(W);
ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm")); ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm"));
ui->postTime_LB->setFixedWidth(W); ui->postTime_LB->setFixedWidth(W);
@ -526,6 +747,13 @@ void GxsChannelPostsWidgetWithModel::showPostDetails()
} }
} }
void GxsChannelPostsWidgetWithModel::updateCommentsCount(int n)
{
if(n > 0)
ui->details_TW->setTabText(2,tr("Comments (%1)").arg(n));
else
ui->details_TW->setTabText(2,tr("Comments"));
}
void GxsChannelPostsWidgetWithModel::updateGroupData() void GxsChannelPostsWidgetWithModel::updateGroupData()
{ {
if(groupId().isNull()) if(groupId().isNull())
@ -553,6 +781,8 @@ void GxsChannelPostsWidgetWithModel::updateGroupData()
{ {
mGroup = group; mGroup = group;
mChannelPostsModel->updateChannel(groupId()); mChannelPostsModel->updateChannel(groupId());
whileBlocking(ui->filterLineEdit)->clear();
whileBlocking(ui->showUnread_TB)->setChecked(false);
insertChannelDetails(mGroup); insertChannelDetails(mGroup);
@ -585,10 +815,30 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad()
mChannelPostsModel->getFilesList(files); mChannelPostsModel->getFilesList(files);
mChannelFilesModel->setFiles(files); mChannelFilesModel->setFiles(files);
//ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE);
//ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE);
ui->channelFiles_TV->setAutoSelect(true); ui->channelFiles_TV->setAutoSelect(true);
ui->channelFiles_TV->sortByColumn(0, Qt::AscendingOrder);
ui->infoPosts->setText(QString::number(mChannelPostsModel->getNumberOfPosts()) + " / " + QString::number(mGroup.mMeta.mVisibleMsgCount));
// now compute aspect ratio for posts. We do that by looking at the 5 latest posts and compute the best aspect ratio for them.
std::vector<uint32_t> ar_votes(4,0);
for(uint32_t i=0;i<std::min(mChannelPostsModel->getNumberOfPosts(),5u);++i)
{
const RsGxsChannelPost& post = mChannelPostsModel->post(i);
ChannelPostThumbnailView v(post,ChannelPostThumbnailView::FLAG_SHOW_TEXT | ChannelPostThumbnailView::FLAG_SCALE_FONT);
++ar_votes[ static_cast<uint32_t>( v.bestAspectRatio() )];
}
int best=0;
for(uint32_t i=0;i<4;++i)
if(ar_votes[i] > ar_votes[best])
best = i;
mChannelPostsDelegate->setAspectRatio(static_cast<ChannelPostThumbnailView::AspectRatio>(best));
mChannelPostsModel->triggerViewUpdate();
handlePostsTreeSizeChange(ui->postsTree->size(),true); // force the update
} }
void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete) void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete)
@ -769,8 +1019,6 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou
rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload);
setAutoDownload(autoDownload); setAutoDownload(autoDownload);
RetroShareLink link;
if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags))
{ {
ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) );
@ -789,6 +1037,7 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou
ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount));
if(group.mMeta.mLastPost==0) if(group.mMeta.mLastPost==0)
ui->infoLastPost->setText(tr("Never")); ui->infoLastPost->setText(tr("Never"));
else else
@ -808,8 +1057,11 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou
ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; ui->infoAdministrator->setId(group.mMeta.mAuthorId) ;
link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); if(!group.mMeta.mAuthorId.isNull())
ui->infoAdministrator->setText(link.toHtml()); {
RetroShareLink link = RetroShareLink::createMessage(group.mMeta.mAuthorId, "");
ui->infoAdministrator->setText(link.toHtml());
}
ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs));
@ -850,6 +1102,7 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou
#endif #endif
ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) );
showPostDetails();
} }
#ifdef TODO #ifdef TODO
@ -893,15 +1146,17 @@ void GxsChannelPostsWidgetWithModel::setViewMode(int viewMode)
#endif #endif
} }
void GxsChannelPostsWidgetWithModel::switchOnlyUnread(bool)
{
filterChanged(ui->filterLineEdit->text());
std::cerr << "Switched to unread/read"<< std::endl;
}
void GxsChannelPostsWidgetWithModel::filterChanged(QString s) void GxsChannelPostsWidgetWithModel::filterChanged(QString s)
{ {
QStringList ql = s.split(' ',QString::SkipEmptyParts); QStringList ql = s.split(' ',QString::SkipEmptyParts);
uint32_t count; uint32_t count;
mChannelPostsModel->setFilter(ql,count); mChannelPostsModel->setFilter(ql,ui->showUnread_TB->isChecked(),count);
mChannelFilesModel->setFilter(ql,count); mChannelFilesModel->setFilter(ql,count);
//mChannelPostFilesProxyModel->setFilterKeyColumn(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME);
//mChannelPostFilesProxyModel->setFilterRegExp(s) ;// triggers a re-display. s is actually not used.
} }
#ifdef TODO #ifdef TODO
@ -1148,25 +1403,6 @@ public:
uint32_t mLastToken; uint32_t mLastToken;
}; };
#ifdef TO_REMOVE
static void setAllMessagesReadCallback(FeedItem *feedItem, void *data)
{
GxsChannelPostItem *channelPostItem = dynamic_cast<GxsChannelPostItem*>(feedItem);
if (!channelPostItem) {
return;
}
GxsChannelPostsReadData *readData = (GxsChannelPostsReadData*) data;
bool isRead = !channelPostItem->isUnread() ;
if(channelPostItem->isLoaded() && (isRead == readData->mRead))
return ;
RsGxsGrpMsgIdPair msgPair = std::make_pair(channelPostItem->groupId(), channelPostItem->messageId());
rsGxsChannels->setMessageReadStatus(readData->mLastToken, msgPair, readData->mRead);
}
#endif
void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read, uint32_t& /*token*/) void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read, uint32_t& /*token*/)
{ {
if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags))

View File

@ -28,6 +28,8 @@
#include "gui/gxs/GxsMessageFramePostWidget.h" #include "gui/gxs/GxsMessageFramePostWidget.h"
#include "gui/feeds/FeedHolder.h" #include "gui/feeds/FeedHolder.h"
#include "GxsChannelPostThumbnail.h"
namespace Ui { namespace Ui {
class GxsChannelPostsWidgetWithModel; class GxsChannelPostsWidgetWithModel;
} }
@ -60,21 +62,26 @@ class ChannelPostDelegate: public QAbstractItemDelegate
Q_OBJECT Q_OBJECT
public: public:
ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent), mZoom(1.0){} ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent), mZoom(1.0), mUseGrid(true){}
virtual ~ChannelPostDelegate(){} virtual ~ChannelPostDelegate(){}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
int cellSize(const QFont& font) const; int cellSize(int col, const QFont& font, uint32_t parent_width) const;
void zoom(bool zoom_or_unzoom) ; void zoom(bool zoom_or_unzoom) ;
private: void setWidgetGrid(bool use_grid) ;
void setAspectRatio(ChannelPostThumbnailView::AspectRatio r) ;
private:
static constexpr float IMAGE_MARGIN_FACTOR = 1.0; static constexpr float IMAGE_MARGIN_FACTOR = 1.0;
static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ; static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ;
static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ; static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ;
static constexpr float IMAGE_ZOOM_FACTOR = 1.0; static constexpr float IMAGE_ZOOM_FACTOR = 1.0;
float mZoom; // zoom factor for the whole thumbnail float mZoom; // zoom factor for the whole thumbnail
bool mUseGrid; // wether we use the grid widget or the list widget
ChannelPostThumbnailView::AspectRatio mAspectRatio;
}; };
class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget
@ -135,25 +142,32 @@ protected:
private slots: private slots:
void showPostDetails(); void showPostDetails();
void updateGroupData(); void updateGroupData();
void createMsg(); void download();
void createMsg();
void toggleAutoDownload(); void toggleAutoDownload();
void subscribeGroup(bool subscribe); void subscribeGroup(bool subscribe);
void filterChanged(QString); void filterChanged(QString);
void setViewMode(int viewMode); void setViewMode(int viewMode);
void settingsChanged(); void settingsChanged();
void handlePostsTreeSizeChange(QSize s); void handlePostsTreeSizeChange(QSize s, bool force=false);
void postChannelPostLoad(); void postChannelPostLoad();
void editPost(); void editPost();
void postContextMenu(const QPoint&); void postContextMenu(const QPoint&);
void copyMessageLink(); void copyMessageLink();
void updateZoomFactor(bool zoom_or_unzoom); void updateZoomFactor(bool zoom_or_unzoom);
void switchView();
void switchOnlyUnread(bool b);
void markMessageUnread();
public slots: public slots:
void sortColumnFiles(int col,Qt::SortOrder so); void sortColumnFiles(int col,Qt::SortOrder so);
void sortColumnPostFiles(int col,Qt::SortOrder so); void sortColumnPostFiles(int col,Qt::SortOrder so);
void updateCommentsCount(int n);
private: private:
void processSettings(bool load); void processSettings(bool load);
RsGxsMessageId getCurrentItemId() const;
void selectItem(const RsGxsMessageId& msg_id);
void setAutoDownload(bool autoDl); void setAutoDownload(bool autoDl);
static bool filterItem(FeedItem *feedItem, const QString &text, int filter); static bool filterItem(FeedItem *feedItem, const QString &text, int filter);

View File

@ -120,6 +120,26 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QToolButton" name="showUnread_TB">
<property name="text">
<string>...</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="viewType_TB">
<property name="text">
<string>...</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="viewModeLayout"> <layout class="QHBoxLayout" name="viewModeLayout">
<property name="spacing"> <property name="spacing">
@ -161,7 +181,7 @@
<item> <item>
<widget class="QTabWidget" name="channel_TW"> <widget class="QTabWidget" name="channel_TW">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="tab_3"> <widget class="QWidget" name="tab_3">
<attribute name="title"> <attribute name="title">
@ -199,26 +219,6 @@
</item> </item>
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="3">
<widget class="QLabel" name="infoLastPost">
<property name="text">
<string notr="true">unknown</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Created:</string>
</property>
</widget>
</item>
<item row="2" column="3"> <item row="2" column="3">
<widget class="GxsIdLabel" name="infoAdministrator"> <widget class="GxsIdLabel" name="infoAdministrator">
<property name="text"> <property name="text">
@ -229,26 +229,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Distribution:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="infoPosts">
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2"> <item row="1" column="1" colspan="2">
<widget class="QLabel" name="infoLastPostLabel"> <widget class="QLabel" name="infoLastPostLabel">
<property name="sizePolicy"> <property name="sizePolicy">
@ -268,8 +248,8 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="3"> <item row="4" column="3">
<widget class="QLabel" name="infoDistribution"> <widget class="QLabel" name="infoCreated">
<property name="text"> <property name="text">
<string>unknown</string> <string>unknown</string>
</property> </property>
@ -288,8 +268,48 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="3"> <item row="1" column="3">
<widget class="QLabel" name="infoCreated"> <widget class="QLabel" name="infoLastPost">
<property name="text">
<string notr="true">unknown</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Distribution:</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="2">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Created:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="infoPosts">
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="infoDistribution">
<property name="text"> <property name="text">
<string>unknown</string> <string>unknown</string>
</property> </property>
@ -309,8 +329,11 @@
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;The number of posts at friends is progressively updated.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;The new numbers will show next time you load this channel.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;The two numbers differ depending on your friends synchronization&lt;/p&gt;&lt;p&gt;time limits as well as yours.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text"> <property name="text">
<string>Posts:</string> <string>Posts (locally / at friends):</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -412,9 +435,6 @@ p, li { white-space: pre-wrap; }
<string>Details</string> <string>Details</string>
</attribute> </attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="bottomMargin">
<number>3</number>
</property>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
@ -433,16 +453,6 @@ p, li { white-space: pre-wrap; }
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="postName_LB">
<property name="text">
<string>TextLabel</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="postTime_LB"> <widget class="QLabel" name="postTime_LB">
<property name="text"> <property name="text">
@ -466,11 +476,22 @@ p, li { white-space: pre-wrap; }
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QTextBrowser" name="postDetails_TE"> <layout class="QVBoxLayout" name="verticalLayout_6">
<property name="openExternalLinks"> <item>
<bool>true</bool> <widget class="QLabel" name="postName_LB">
</property> <property name="text">
</widget> <string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="postDetails_TE">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -526,7 +547,7 @@ p, li { white-space: pre-wrap; }
</widget> </widget>
<widget class="QWidget" name="tab_5"> <widget class="QWidget" name="tab_5">
<attribute name="title"> <attribute name="title">
<string>Files</string> <string>All files</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">
<item> <item>

View File

@ -2,8 +2,14 @@
<qresource prefix="/"> <qresource prefix="/">
<file>icons/onion.png</file> <file>icons/onion.png</file>
<file>icons/svg/hidden.svg</file> <file>icons/svg/hidden.svg</file>
<file>icons/svg/ratio-auto.svg</file>
<file>icons/svg/ratio-1-1.svg</file>
<file>icons/svg/ratio-3-4.svg</file>
<file>icons/svg/ratio-16-9.svg</file>
<file>icons/svg/randomness.svg</file> <file>icons/svg/randomness.svg</file>
<file>icons/svg/password.svg</file> <file>icons/svg/password.svg</file>
<file>icons/svg/listlayout.svg</file>
<file>icons/svg/gridlayout.svg</file>
<file>icons/stars/star0.png</file> <file>icons/stars/star0.png</file>
<file>icons/stars/star1.png</file> <file>icons/stars/star1.png</file>
<file>icons/stars/star2.png</file> <file>icons/stars/star2.png</file>

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 35 35" style="enable-background:new 0 0 35 35;" xml:space="preserve" class=""><g transform="matrix(0.922745 0 0 0.922745 1.35197 1.35197)"><g>
<g>
<rect width="9.182" height="15.5" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/>
<rect y="19.5" width="9.182" height="15.5" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/>
<rect x="12.9" y="19.5" width="9.202" height="15.5" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/>
<rect x="12.9" width="9.202" height="15.5" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/>
<rect x="25.82" width="9.18" height="15.5" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/>
<rect x="25.82" y="19.5" width="9.18" height="15.5" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/>
</g>
</g></g> </svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" enable-background="new 0 0 512 512" height="512px" viewBox="0 0 512 512" width="512px" class=""><g transform="matrix(0.94148 0 0 0.94148 14.9811 14.9811)"><path d="m464.883 64.267h-417.766c-25.98 0-47.117 21.136-47.117 47.149 0 25.98 21.137 47.117 47.117 47.117h417.766c25.98 0 47.117-21.137 47.117-47.117 0-26.013-21.137-47.149-47.117-47.149z" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/><path d="m464.883 208.867h-417.766c-25.98 0-47.117 21.136-47.117 47.149 0 25.98 21.137 47.117 47.117 47.117h417.766c25.98 0 47.117-21.137 47.117-47.117 0-26.013-21.137-47.149-47.117-47.149z" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/><path d="m464.883 353.467h-417.766c-25.98 0-47.117 21.137-47.117 47.149 0 25.98 21.137 47.117 47.117 47.117h417.766c25.98 0 47.117-21.137 47.117-47.117 0-26.012-21.137-47.149-47.117-47.149z" data-original="#000000" class="active-path" data-old_color="#000000" fill="#039BD5"/></g> </svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="512"
viewBox="0 -64 512 512"
width="512"
version="1.1"
id="svg18"
sodipodi:docname="ratio-1-1.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata24">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs22" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="705"
id="namedview20"
showgrid="true"
inkscape:zoom="0.65186406"
inkscape:cx="256"
inkscape:cy="342.77966"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g16">
<inkscape:grid
type="xygrid"
id="grid834" />
</sodipodi:namedview>
<g
transform="matrix(0.922745 0 0 0.922745 19.7773 14.833)"
id="g16">
<path
d="m 366.53972,252.68845 c -8.53125,0 -17.0664,-6.39844 -17.0664,-17.06641 v -93.86719 c 0,-8.53125 6.40234,-17.0664 17.0664,-17.0664 10.66797,0 14.9336,6.39843 14.9336,17.0664 v 96 c 0,8.53516 -6.39844,14.9336 -14.9336,14.9336 z m 0,0"
data-original="#000000"
class="active-path"
data-old_color="#000000"
style="fill:#039bd5"
id="path4"
inkscape:connector-curvature="0" />
<path
d="m277.332031 149.332031c0 11.785157-9.550781 21.335938-21.332031 21.335938s-21.332031-9.550781-21.332031-21.335938c0-11.78125 9.550781-21.332031 21.332031-21.332031s21.332031 9.550781 21.332031 21.332031zm0 0"
data-original="#000000"
class="active-path"
data-old_color="#000000"
style="fill:#039BD5"
id="path10" />
<path
d="m277.332031 234.667969c0 11.78125-9.550781 21.332031-21.332031 21.332031s-21.332031-9.550781-21.332031-21.332031c0-11.785157 9.550781-21.335938 21.332031-21.335938s21.332031 9.550781 21.332031 21.335938zm0 0"
data-original="#000000"
class="active-path"
data-old_color="#000000"
style="fill:#039BD5"
id="path12" />
<path
d="m454.398438 384h-394.664063c-34.132813 0-59.734375-25.601562-59.734375-57.601562v-266.664063c0-34.132813 25.601562-59.734375 59.734375-59.734375h394.664063c32 0 57.601562 25.601562 57.601562 59.734375v266.664063c0 32-25.601562 57.601562-57.601562 57.601562zm-394.664063-352c-14.933594 0-27.734375 12.800781-27.734375 27.734375v266.664063c0 14.933593 12.800781 27.734374 27.734375 27.734374h394.664063c14.933593 0 27.734374-12.800781 27.734374-27.734374v-266.664063c0-14.933594-12.800781-27.734375-27.734374-27.734375zm0 0"
data-original="#000000"
class="active-path"
data-old_color="#000000"
style="fill:#039BD5"
id="path14" />
<path
d="m 156.29747,252.68845 c -8.53125,0 -17.0664,-6.39844 -17.0664,-17.0664 v -93.8672 c 0,-8.53125 6.40234,-17.0664 17.0664,-17.0664 10.66797,0 14.9336,6.39843 14.9336,17.0664 v 96.00001 c 0,8.53515 -6.39844,14.93359 -14.9336,14.93359 z"
data-original="#000000"
class="active-path"
data-old_color="#000000"
style="fill:#039bd5;stroke-width:1"
id="path4-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssss" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" height="512" viewBox="0 -64 512 512" width="512" class=""><g transform="matrix(0.932065 0 0 0.932065 17.3913 13.0434)"><path d="m454.398438 384h-394.664063c-34.132813 0-59.734375-25.601562-59.734375-57.601562v-266.664063c0-34.132813 25.601562-59.734375 59.734375-59.734375h394.664063c32 0 57.601562 25.601562 57.601562 59.734375v266.664063c0 32-25.601562 57.601562-57.601562 57.601562zm-394.664063-352c-14.933594 0-27.734375 12.800781-27.734375 27.734375v266.664063c0 14.933593 12.800781 27.734374 27.734375 27.734374h394.664063c14.933593 0 27.734374-12.800781 27.734374-27.734374v-266.664063c0-14.933594-12.800781-27.734375-27.734374-27.734375zm0 0" data-original="#000000" class="active-path" style="fill:#039BD5" data-old_color="#000000"/><path d="m177.066406 256h-10.667968c-21.332032 0-38.398438-17.066406-38.398438-38.398438v-53.335937c0-19.199219 17.066406-36.265625 38.398438-36.265625h32c8.535156 0 14.933593 6.398438 14.933593 17.066406 0 10.667969-6.398437 17.066406-17.066406 17.066406h-29.867187c-2.132813 0-6.398438 2.132813-6.398438 6.402344v10.664063h17.066406c21.332032 0 38.398438 17.066406 38.398438 38.402343v6.398438c-2.132813 14.933594-19.199219 32-38.398438 32zm-17.066406-46.933594v10.667969c0 2.132813 2.132812 6.398437 6.398438 6.398437h10.667968c2.132813 0 6.398438-2.132812 6.398438-6.398437v-6.402344c0-2.132812-2.132813-6.398437-6.398438-6.398437h-17.066406zm0 0" data-original="#000000" class="active-path" style="fill:#039BD5" data-old_color="#000000"/><path d="m390.398438 256h-32c-8.53125 0-17.066407-6.398438-17.066407-17.066406 0-10.667969 6.402344-17.066406 17.066407-17.066406h32c2.136718 0 6.402343-2.132813 6.402343-6.402344v-10.664063h-17.066406c-21.335937 0-38.402344-17.066406-38.402344-38.402343v-6.398438c0-21.332031 17.066407-38.398438 38.402344-38.398438h10.664063c21.335937 0 38.402343 17.066407 38.402343 38.398438v53.332031c-2.132812 25.601563-19.199219 42.667969-38.402343 42.667969zm-10.664063-96c-2.132813 0-6.402344 2.132812-6.402344 6.398438v4.269531c0 2.132812 2.132813 6.398437 6.402344 6.398437h17.066406v-10.667968c0-2.132813-2.132812-6.398438-6.402343-6.398438zm0 0" data-original="#000000" class="active-path" style="fill:#039BD5" data-old_color="#000000"/><path d="m91.734375 256c-8.535156 0-17.066406-6.398438-17.066406-17.066406v-93.867188c0-10.667968 6.398437-17.066406 17.066406-17.066406 10.664063 0 14.933594 6.398438 14.933594 17.066406v96c0 8.535156-6.402344 14.933594-14.933594 14.933594zm0 0" data-original="#000000" class="active-path" style="fill:#039BD5" data-old_color="#000000"/><path d="m298.667969 149.332031c0 11.785157-9.550781 21.335938-21.335938 21.335938-11.78125 0-21.332031-9.550781-21.332031-21.335938 0-11.78125 9.550781-21.332031 21.332031-21.332031 11.785157 0 21.335938 9.550781 21.335938 21.332031zm0 0" data-original="#000000" class="active-path" style="fill:#039BD5" data-old_color="#000000"/><path d="m298.667969 234.667969c0 11.78125-9.550781 21.332031-21.335938 21.332031-11.78125 0-21.332031-9.550781-21.332031-21.332031 0-11.785157 9.550781-21.335938 21.332031-21.335938 11.785157 0 21.335938 9.550781 21.335938 21.335938zm0 0" data-original="#000000" class="active-path" style="fill:#039BD5" data-old_color="#000000"/></g> </svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" height="512" viewBox="0 -64 512 512" width="512"><g transform="matrix(0.922745 0 0 0.922745 19.7773 14.833)"><path d="m390.398438 209.066406h-32c-21.332032 0-38.398438-17.066406-38.398438-38.398437v-25.601563c0-10.667968 6.398438-17.066406 17.066406-17.066406 10.667969 0 17.066406 6.398438 17.066406 17.066406v25.601563c0 2.132812 2.132813 6.398437 6.402344 6.398437h32c8.53125 0 17.066406 6.398438 17.066406 17.066406 0 10.667969-10.667968 14.933594-19.203124 14.933594zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/><path d="m390.398438 256c-8.53125 0-17.066407-6.398438-17.066407-17.066406v-93.867188c0-8.53125 6.402344-17.066406 17.066407-17.066406 10.667968 0 14.933593 6.398438 14.933593 17.066406v96c0 8.535156-6.398437 14.933594-14.933593 14.933594zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/><path d="m177.066406 209.066406h-53.332031c-10.667969 0-17.066406-8.53125-17.066406-17.066406s6.398437-17.066406 17.066406-17.066406h53.332031c8.535156 2.132812 14.933594 8.53125 14.933594 17.066406s-6.398438 17.066406-14.933594 17.066406zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/><path d="m155.734375 256h-32c-10.667969 0-17.066406-6.398438-17.066406-14.933594 0-8.53125 6.398437-17.066406 17.066406-17.066406h32c2.132813 0 6.398437-2.132812 6.398437-6.398438v-53.335937c0-2.132813-2.132812-6.398437-6.398437-6.398437h-32c-10.667969 2.132812-17.066406-4.265626-17.066406-12.800782 0-8.53125 6.398437-17.066406 17.066406-17.066406h32c19.199219 0 36.265625 17.066406 36.265625 38.398438v53.335937c0 19.199219-17.066406 36.265625-36.265625 36.265625zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/><path d="m277.332031 149.332031c0 11.785157-9.550781 21.335938-21.332031 21.335938s-21.332031-9.550781-21.332031-21.335938c0-11.78125 9.550781-21.332031 21.332031-21.332031s21.332031 9.550781 21.332031 21.332031zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/><path d="m277.332031 234.667969c0 11.78125-9.550781 21.332031-21.332031 21.332031s-21.332031-9.550781-21.332031-21.332031c0-11.785157 9.550781-21.335938 21.332031-21.335938s21.332031 9.550781 21.332031 21.335938zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/><path d="m454.398438 384h-394.664063c-34.132813 0-59.734375-25.601562-59.734375-57.601562v-266.664063c0-34.132813 25.601562-59.734375 59.734375-59.734375h394.664063c32 0 57.601562 25.601562 57.601562 59.734375v266.664063c0 32-25.601562 57.601562-57.601562 57.601562zm-394.664063-352c-14.933594 0-27.734375 12.800781-27.734375 27.734375v266.664063c0 14.933593 12.800781 27.734374 27.734375 27.734374h394.664063c14.933593 0 27.734374-12.800781 27.734374-27.734374v-266.664063c0-14.933594-12.800781-27.734375-27.734374-27.734375zm0 0" data-original="#000000" class="active-path" data-old_color="#000000" style="fill:#039BD5"/></g> </svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512" height="512" class="hovered-paths"><g transform="matrix(0.922745 0 0 0.922745 19.7773 19.7773)"><g>
<g>
<g>
<path d="M0,64v384h512V64H0z M480,416H32V96h448V416z" data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
<polygon points="96,160 160,160 160,128 64,128 64,224 96,224 " data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
<polygon points="448,288 416,288 416,352 352,352 352,384 448,384 " data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
<rect x="192" y="128" width="32" height="32" data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
<rect x="64" y="256" width="32" height="32" data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
<rect x="416" y="224" width="32" height="32" data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
<rect x="288" y="352" width="32" height="32" data-original="#000000" class="hovered-path active-path" style="fill:#039BD5" data-old_color="#000000"/>
</g>
</g>
</g></g> </svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -33,17 +33,10 @@ ChannelPage::ChannelPage(QWidget * parent, Qt::WindowFlags flags)
ui.groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each channel in a new tab")); ui.groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each channel in a new tab"));
ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Channel) ; ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Channel) ;
connect(ui.loadThreadCheckBox,SIGNAL(toggled(bool)),this,SLOT(updateLoadThread())) ;
connect(ui.emoteicon_checkBox,SIGNAL(toggled(bool)),this,SLOT(updateEmotes())) ; connect(ui.emoteicon_checkBox,SIGNAL(toggled(bool)),this,SLOT(updateEmotes())) ;
} }
void ChannelPage::updateLoadThread()
{
Settings->setChannelLoadThread(ui.loadThreadCheckBox->isChecked());
NotifyQt::getInstance()->notifySettingsChanged();
}
ChannelPage::~ChannelPage() ChannelPage::~ChannelPage()
{ {
} }
@ -51,7 +44,6 @@ ChannelPage::~ChannelPage()
/** Loads the settings for this page */ /** Loads the settings for this page */
void ChannelPage::load() void ChannelPage::load()
{ {
whileBlocking(ui.loadThreadCheckBox)->setChecked(Settings->getChannelLoadThread());
ui.groupFrameSettingsWidget->loadSettings(GroupFrameSettings::Channel); ui.groupFrameSettingsWidget->loadSettings(GroupFrameSettings::Channel);
Settings->beginGroup(QString("ChannelPostsWidget")); Settings->beginGroup(QString("ChannelPostsWidget"));

View File

@ -42,9 +42,6 @@ public:
private slots: private slots:
void updateEmotes(); void updateEmotes();
protected slots:
void updateLoadThread() ;
private: private:
Ui::ChannelPage ui; Ui::ChannelPage ui;
}; };

View File

@ -29,13 +29,6 @@
<string>General</string> <string>General</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="loadThreadCheckBox">
<property name="text">
<string>Load posts in background (Thread)</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="emoteicon_checkBox"> <widget class="QCheckBox" name="emoteicon_checkBox">
<property name="text"> <property name="text">

View File

@ -1382,7 +1382,8 @@ gxschannels {
gui/gxschannels/GxsChannelPostsModel.cpp \ gui/gxschannels/GxsChannelPostsModel.cpp \
gui/gxschannels/GxsChannelPostFilesModel.cpp \ gui/gxschannels/GxsChannelPostFilesModel.cpp \
gui/gxschannels/GxsChannelFilesWidget.cpp \ gui/gxschannels/GxsChannelFilesWidget.cpp \
gui/gxschannels/GxsChannelFilesStatusWidget.cpp \ gui/gxschannels/GxsChannelPostThumbnail.cpp \
gui/gxschannels/GxsChannelFilesStatusWidget.cpp \
gui/gxschannels/GxsChannelGroupDialog.cpp \ gui/gxschannels/GxsChannelGroupDialog.cpp \
gui/gxschannels/CreateGxsChannelMsg.cpp \ gui/gxschannels/CreateGxsChannelMsg.cpp \
gui/feeds/GxsChannelGroupItem.cpp \ gui/feeds/GxsChannelGroupItem.cpp \

View File

@ -307,7 +307,11 @@ QPixmap misc::getOpenThumbnailedPicture(QWidget *parent, const QString &caption,
if (!getOpenFileName(parent, RshareSettings::LASTDIR_IMAGES, caption, tr("Pictures (*.png *.jpeg *.xpm *.jpg *.tiff *.gif)"), fileName)) if (!getOpenFileName(parent, RshareSettings::LASTDIR_IMAGES, caption, tr("Pictures (*.png *.jpeg *.xpm *.jpg *.tiff *.gif)"), fileName))
return QPixmap(); return QPixmap();
return QPixmap(fileName).scaledToHeight(height, Qt::SmoothTransformation).copy( 0, 0, width, height); if(width > 0 && height > 0)
return QPixmap(fileName).scaledToHeight(height, Qt::SmoothTransformation).copy( 0, 0, width, height);
else
return QPixmap(fileName);
//return QPixmap(fileName).scaledToHeight(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); //return QPixmap(fileName).scaledToHeight(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
} }