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

Abstract item model for channels
This commit is contained in:
csoler 2020-06-24 23:22:44 +02:00 committed by GitHub
commit 69b51fecb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 4225 additions and 340 deletions

View File

@ -1997,7 +1997,9 @@ bool p3GxsCircles::processMembershipRequests(uint32_t token)
// now do another sweep and remove all msgs that are older than the latest // now do another sweep and remove all msgs that are older than the latest
#ifdef DEBUG_CIRCLES
std::cerr << " Cleaning old messages..." << std::endl; std::cerr << " Cleaning old messages..." << std::endl;
#endif
for(uint32_t i=0;i<it->second.size();++i) for(uint32_t i=0;i<it->second.size();++i)
{ {

View File

@ -19,10 +19,43 @@
*******************************************************************************/ *******************************************************************************/
#include <QPainter> #include <QPainter>
#include <QResizeEvent>
#include "RSTreeView.h" #include "RSTreeView.h"
RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent) RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent)
{ {
setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future.
}
void RSTreeView::wheelEvent(QWheelEvent *e)
{
if(e->modifiers() == Qt::ControlModifier)
emit zoomRequested(e->delta() > 0);
else
QTreeView::wheelEvent(e);
}
void RSTreeView::mouseMoveEvent(QMouseEvent *e)
{
QModelIndex idx = indexAt(e->pos());
if(idx != selectionModel()->currentIndex())
selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect);
QTreeView::mouseMoveEvent(e);
}
void RSTreeView::setAutoSelect(bool b)
{
if(b)
setMouseTracking(true);
else
setMouseTracking(false);
}
void RSTreeView::resizeEvent(QResizeEvent *e)
{
emit sizeChanged(e->size());
} }
void RSTreeView::setPlaceholderText(const QString &text) void RSTreeView::setPlaceholderText(const QString &text)

View File

@ -33,8 +33,20 @@ public:
void setPlaceholderText(const QString &text); void setPlaceholderText(const QString &text);
// Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode
// in trees that show editing widgets using a QStyledItemDelegate
void setAutoSelect(bool b);
signals:
void sizeChanged(QSize);
void zoomRequested(bool zoom_or_unzoom);
protected: protected:
void paintEvent(QPaintEvent *event); virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection
virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom
virtual void resizeEvent(QResizeEvent *e) override;
virtual void paintEvent(QPaintEvent *event) override;
QString placeholderText; QString placeholderText;
}; };

View File

@ -36,10 +36,12 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic
/* Invoke the Qt Designer generated QObject setup routine */ /* Invoke the Qt Designer generated QObject setup routine */
ui->setupUi(this); ui->setupUi(this);
//ui->postFrame->setVisible(false); setTokenService(token_service,comment_service);
init();
ui->treeWidget->setup(token_service, comment_service); }
void GxsCommentDialog::init()
{
/* Set header resize modes and initial section sizes */ /* Set header resize modes and initial section sizes */
QHeaderView * ttheader = ui->treeWidget->header () ; QHeaderView * ttheader = ui->treeWidget->header () ;
ttheader->resizeSection (0, 440); ttheader->resizeSection (0, 440);
@ -62,6 +64,20 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic
ui->sortBox->setIconSize(QSize(S*1.5,S*1.5)); ui->sortBox->setIconSize(QSize(S*1.5,S*1.5));
} }
void GxsCommentDialog::setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service)
{
ui->treeWidget->setup(token_service, comment_service);
}
GxsCommentDialog::GxsCommentDialog(QWidget *parent)
: QWidget(parent), ui(new Ui::GxsCommentDialog)
{
/* Invoke the Qt Designer generated QObject setup routine */
ui->setupUi(this);
init();
}
GxsCommentDialog::~GxsCommentDialog() GxsCommentDialog::~GxsCommentDialog()
{ {
delete(ui); delete(ui);

View File

@ -32,9 +32,11 @@ class GxsCommentDialog: public QWidget
Q_OBJECT Q_OBJECT
public: public:
GxsCommentDialog(QWidget *parent);
GxsCommentDialog(QWidget *parent, RsTokenService *token_service, RsGxsCommentService *comment_service); GxsCommentDialog(QWidget *parent, RsTokenService *token_service, RsGxsCommentService *comment_service);
virtual ~GxsCommentDialog(); virtual ~GxsCommentDialog();
void setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service);
void setCommentHeader(QWidget *header); void setCommentHeader(QWidget *header);
void commentLoad(const RsGxsGroupId &grpId, const std::set<RsGxsMessageId> &msg_versions, const RsGxsMessageId &most_recent_msgId); void commentLoad(const RsGxsGroupId &grpId, const std::set<RsGxsMessageId> &msg_versions, const RsGxsMessageId &most_recent_msgId);
@ -48,6 +50,8 @@ private slots:
void sortComments(int); void sortComments(int);
private: private:
void init();
RsGxsGroupId mGrpId; RsGxsGroupId mGrpId;
RsGxsMessageId mMostRecentMsgId; RsGxsMessageId mMostRecentMsgId;
std::set<RsGxsMessageId> mMsgVersions; std::set<RsGxsMessageId> mMsgVersions;

View File

@ -89,7 +89,6 @@ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, QWidget *p
mSubscribedGroups = NULL; mSubscribedGroups = NULL;
mPopularGroups = NULL; mPopularGroups = NULL;
mOtherGroups = NULL; mOtherGroups = NULL;
mMessageWidget = NULL;
/* Setup Queue */ /* Setup Queue */
mInterface = ifaceImpl; mInterface = ifaceImpl;
@ -251,6 +250,13 @@ void GxsGroupFrameDialog::processSettings(bool load)
Settings->endGroup(); Settings->endGroup();
} }
bool GxsGroupFrameDialog::useTabs()
{
GroupFrameSettings groupFrameSettings;
return Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings) && groupFrameSettings.mOpenAllInNewTab;
}
void GxsGroupFrameDialog::settingsChanged() void GxsGroupFrameDialog::settingsChanged()
{ {
GroupFrameSettings groupFrameSettings; GroupFrameSettings groupFrameSettings;
@ -262,17 +268,15 @@ void GxsGroupFrameDialog::settingsChanged()
void GxsGroupFrameDialog::setSingleTab(bool singleTab) void GxsGroupFrameDialog::setSingleTab(bool singleTab)
{ {
if (singleTab) { if (singleTab)
if (!mMessageWidget) { {
mMessageWidget = createMessageWidget(RsGxsGroupId()); while(ui->messageTabWidget->count() > 1)
// remove close button of the the first tab {
ui->messageTabWidget->hideCloseButton(ui->messageTabWidget->indexOf(mMessageWidget)); auto w = ui->messageTabWidget->widget(0) ;
} ui->messageTabWidget->removeTab(0);
} else { delete w;
if (mMessageWidget) {
delete(mMessageWidget);
mMessageWidget = NULL;
} }
ui->messageTabWidget->hideCloseButton(0);
} }
} }
@ -390,6 +394,7 @@ static uint32_t checkDelay(uint32_t time_in_secs)
return 365 * 86400; return 365 * 86400;
} }
void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
{ {
// First separately handle the case of search top level items // First separately handle the case of search top level items
@ -432,12 +437,10 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
QMenu contextMnu(this); QMenu contextMnu(this);
QAction *action; QAction *action;
if (mMessageWidget) {
action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab()));
if (mGroupId.isNull() || messageWidget(mGroupId, true)) {
if(mGroupId.isNull()) // dont enable the open in tab if a tab is already here
action->setEnabled(false); action->setEnabled(false);
}
}
if (isSubscribed) { if (isSubscribed) {
action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup())); action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup()));
@ -673,7 +676,7 @@ bool GxsGroupFrameDialog::getCurrentGroupName(QString& name)
void GxsGroupFrameDialog::markMsgAsRead() void GxsGroupFrameDialog::markMsgAsRead()
{ {
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (msgWidget) { if (msgWidget) {
msgWidget->setAllMessagesRead(true); msgWidget->setAllMessagesRead(true);
} }
@ -681,7 +684,7 @@ void GxsGroupFrameDialog::markMsgAsRead()
void GxsGroupFrameDialog::markMsgAsUnread() void GxsGroupFrameDialog::markMsgAsUnread()
{ {
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (msgWidget) { if (msgWidget) {
msgWidget->setAllMessagesRead(false); msgWidget->setAllMessagesRead(false);
} }
@ -759,7 +762,7 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa
changedCurrentGroup(groupIdString); changedCurrentGroup(groupIdString);
/* search exisiting tab */ /* search exisiting tab */
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (!msgWidget) { if (!msgWidget) {
return false; return false;
} }
@ -771,18 +774,17 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa
return msgWidget->navigate(msgId); return msgWidget->navigate(msgId);
} }
GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId, bool ownTab) GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId)
{ {
int tabCount = ui->messageTabWidget->count(); int tabCount = ui->messageTabWidget->count();
for (int index = 0; index < tabCount; ++index) {
for (int index = 0; index < tabCount; ++index)
{
GxsMessageFrameWidget *childWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index)); GxsMessageFrameWidget *childWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
if (ownTab && mMessageWidget && childWidget == mMessageWidget) {
continue; if (childWidget && childWidget->groupId() == groupId)
}
if (childWidget && childWidget->groupId() == groupId) {
return childWidget; return childWidget;
} }
}
return NULL; return NULL;
} }
@ -790,9 +792,9 @@ GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &gr
GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId) GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId)
{ {
GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId); GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId);
if (!msgWidget) {
if (!msgWidget)
return NULL; return NULL;
}
int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true)); int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true));
ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon()); ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon());
@ -817,40 +819,44 @@ GxsCommentDialog *GxsGroupFrameDialog::commentWidget(const RsGxsMessageId& msgId
return NULL; return NULL;
} }
void GxsGroupFrameDialog::changedCurrentGroup(const QString &groupId) void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId)
{ {
if (mInFill) { if (mInFill) {
return; return;
} }
if (groupId.isEmpty()) { if (groupId.isEmpty())
if (mMessageWidget) { {
mMessageWidget->setGroupId(RsGxsGroupId()); auto w = currentWidget();
ui->messageTabWidget->setCurrentWidget(mMessageWidget);
} if(w)
w->setGroupId(RsGxsGroupId());
return; return;
} }
mGroupId = RsGxsGroupId(groupId.toStdString()); mGroupId = RsGxsGroupId(groupId.toStdString());
if (mGroupId.isNull()) {
if (mGroupId.isNull())
return; return;
}
/* search exisiting tab */ /* search exisiting tab */
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true); GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (!msgWidget) { // check that we have at least one tab
if (mMessageWidget) {
/* not found, use standard tab */
msgWidget = mMessageWidget;
msgWidget->setGroupId(mGroupId);
} else {
/* create new tab */
msgWidget = createMessageWidget(mGroupId);
}
}
if(msgWidget)
ui->messageTabWidget->setCurrentWidget(msgWidget); ui->messageTabWidget->setCurrentWidget(msgWidget);
else
{
if(useTabs() || ui->messageTabWidget->count()==0)
{
msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString()));
ui->messageTabWidget->setCurrentWidget(msgWidget);
}
else
currentWidget()->setGroupId(mGroupId);
}
} }
void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item) void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item)
@ -870,37 +876,31 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId)
} }
/* search exisiting tab */ /* search exisiting tab */
GxsMessageFrameWidget *msgWidget = messageWidget(groupId, true); GxsMessageFrameWidget *msgWidget = createMessageWidget(groupId);
if (!msgWidget) {
/* not found, create new tab */
msgWidget = createMessageWidget(groupId);
}
ui->messageTabWidget->setCurrentWidget(msgWidget); ui->messageTabWidget->setCurrentWidget(msgWidget);
} }
void GxsGroupFrameDialog::messageTabCloseRequested(int index) void GxsGroupFrameDialog::messageTabCloseRequested(int index)
{ {
QWidget *widget = ui->messageTabWidget->widget(index); if(ui->messageTabWidget->count() == 1) /* Don't close single tab */
if (!widget) {
return; return;
}
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(widget); GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
if (msgWidget && msgWidget == mMessageWidget) { delete msgWidget ;
/* Don't close single tab */ }
return;
}
delete(widget); GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const
{
return dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(ui->messageTabWidget->currentIndex()));
} }
void GxsGroupFrameDialog::messageTabChanged(int index) void GxsGroupFrameDialog::messageTabChanged(int index)
{ {
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index)); GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
if (!msgWidget) {
if (!msgWidget)
return; return;
}
ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false); ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false);
} }

View File

@ -181,7 +181,7 @@ private:
// subscribe/unsubscribe ack. // subscribe/unsubscribe ack.
GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId, bool ownTab); GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId);
GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId); GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId);
GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId); GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId);
@ -192,13 +192,15 @@ protected:
bool mCountChildMsgs; // Count unread child messages? bool mCountChildMsgs; // Count unread child messages?
private: private:
GxsMessageFrameWidget *currentWidget() const;
bool useTabs();
bool mInitialized; bool mInitialized;
bool mInFill; bool mInFill;
bool mDistSyncAllowed; bool mDistSyncAllowed;
QString mSettingsName; QString mSettingsName;
RsGxsGroupId mGroupId; RsGxsGroupId mGroupId;
RsGxsIfaceHelper *mInterface; RsGxsIfaceHelper *mInterface;
GxsMessageFrameWidget *mMessageWidget;
QTreeWidgetItem *mYourGroups; QTreeWidgetItem *mYourGroups;
QTreeWidgetItem *mSubscribedGroups; QTreeWidgetItem *mSubscribedGroups;

View File

@ -55,6 +55,7 @@ signals:
void groupChanged(QWidget *widget); void groupChanged(QWidget *widget);
void waitingChanged(QWidget *widget); void waitingChanged(QWidget *widget);
void loadComment(const RsGxsGroupId &groupId, const QVector<RsGxsMessageId>& msg_versions,const RsGxsMessageId &msgId, const QString &title); void loadComment(const RsGxsGroupId &groupId, const QVector<RsGxsMessageId>& msg_versions,const RsGxsMessageId &msgId, const QString &title);
void groupDataLoaded();
protected: protected:
virtual void setAllMessagesReadDo(bool read, uint32_t &token) = 0; virtual void setAllMessagesReadDo(bool read, uint32_t &token) = 0;

View File

@ -72,6 +72,7 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId
connect(addFileButton, SIGNAL(clicked() ), this , SLOT(addExtraFile())); connect(addFileButton, SIGNAL(clicked() ), this , SLOT(addExtraFile()));
connect(addfilepushButton, SIGNAL(clicked() ), this , SLOT(addExtraFile())); connect(addfilepushButton, SIGNAL(clicked() ), this , SLOT(addExtraFile()));
connect(subjectEdit,SIGNAL(textChanged(const QString&)),this,SLOT(updatePreviewText(const QString&)));
connect(addThumbnailButton, SIGNAL(clicked() ), this , SLOT(addThumbnail())); connect(addThumbnailButton, SIGNAL(clicked() ), this , SLOT(addThumbnail()));
connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool))); connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool)));
@ -605,6 +606,11 @@ void CreateGxsChannelMsg::saveChannelInfo(const RsGroupMetaData &meta)
subjectEdit->setFocus(); subjectEdit->setFocus();
} }
void CreateGxsChannelMsg::updatePreviewText(const QString& s)
{
preview_W->setText(s);
}
void CreateGxsChannelMsg::sendMsg() void CreateGxsChannelMsg::sendMsg()
{ {
#ifdef DEBUG_CREATE_GXS_MSG #ifdef DEBUG_CREATE_GXS_MSG
@ -717,7 +723,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"), 156, 107); QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 107,156); // these absolute sizes are terrible
if (img.isNull()) if (img.isNull())
return; return;
@ -725,7 +731,7 @@ void CreateGxsChannelMsg::addThumbnail()
picture = img; picture = img;
// to show the selected // to show the selected
thumbnail_label->setPixmap(picture); preview_W->setPixmap(picture);
} }
void CreateGxsChannelMsg::loadOriginalChannelPostInfo() void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
@ -769,7 +775,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);
thumbnail_label->setPixmap(picture); preview_W->setPixmap(picture);
} }

View File

@ -58,6 +58,7 @@ private slots:
void addExtraFile(); void addExtraFile();
void checkAttachmentReady(); void checkAttachmentReady();
void deleteAttachment(); void deleteAttachment();
void updatePreviewText(const QString &);
void cancelMsg(); void cancelMsg();
void sendMsg(); void sendMsg();

View File

@ -52,52 +52,39 @@
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0"> <item row="2" column="0" colspan="3">
<widget class="QPushButton" name="channelpostButton"> <layout class="QHBoxLayout" name="buttonHLayout">
<item>
<widget class="QCheckBox" name="generateCheckBox">
<property name="text"> <property name="text">
<string>Channel Post</string> <string>Generate mass data</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item>
<widget class="QPushButton" name="attachmentsButton"> <widget class="QSpinBox" name="generateSpinBox">
<property name="text"> <property name="minimum">
<string>Attachments</string> <number>1</number>
</property> </property>
<property name="icon"> <property name="maximum">
<iconset resource="../icons.qrc"> <number>999</number>
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item>
<spacer name="horizontalSpacer"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="standardButtons">
<size> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<width>486</width>
<height>20</height>
</size>
</property> </property>
</spacer> </widget>
</item>
</layout>
</item> </item>
<item row="1" column="0" colspan="3"> <item row="1" column="0" colspan="3">
<widget class="QStackedWidget" name="stackedWidget"> <widget class="QStackedWidget" name="stackedWidget">
@ -114,128 +101,8 @@
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="stackedWidgetPage1"> <widget class="QWidget" name="stackedWidgetPage1">
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin"> <item>
<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 row="1" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="thumbnail_label">
<property name="maximumSize">
<size>
<width>156</width>
<height>107</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="../images.qrc">:/images/thumb-default-video.png</pixmap>
</property>
</widget>
</item>
<item row="0" column="1" colspan="4">
<widget class="QLabel" name="channelAttachLabel">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;&quot;&gt;Attachments:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/images/feedback_arrow.png&quot; /&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; Use Drag and Drop / Add Files button, to Hash new files.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/images/feedback_arrow.png&quot; /&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; Copy/Paste RetroShare links from your shares&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="addThumbnailButton">
<property name="text">
<string>Add Channel Thumbnail</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="addfilepushButton">
<property name="text">
<string>Add File to Attach</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="1" column="3" colspan="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="messageGBox">
<property name="title">
<string>Message</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLineEdit" name="subjectEdit">
<property name="placeholderText">
<string>Title</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="RichTextEdit" name="RichTextEditWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="channelNameHLayout"> <layout class="QHBoxLayout" name="channelNameHLayout">
<item> <item>
<widget class="QLabel" name="channelNameLabel"> <widget class="QLabel" name="channelNameLabel">
@ -268,6 +135,119 @@ p, li { white-space: pre-wrap; }
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="ChannelPostThumbnailView" name="preview_W" native="true"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="channelAttachLabel">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;&quot;&gt;Attachments:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/images/feedback_arrow.png&quot; /&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; Use Drag and Drop / Add Files button, to Hash new files.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/images/feedback_arrow.png&quot; /&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; Copy/Paste RetroShare links from your shares&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="addThumbnailButton">
<property name="text">
<string>Add Channel Thumbnail</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addfilepushButton">
<property name="text">
<string>Add File to Attach</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="messageGBox">
<property name="title">
<string>Message</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLineEdit" name="subjectEdit">
<property name="placeholderText">
<string>Title</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="RichTextEdit" name="RichTextEditWidget" native="true"/>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="stackedWidgetPage2"> <widget class="QWidget" name="stackedWidgetPage2">
@ -329,7 +309,7 @@ p, li { white-space: pre-wrap; }
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>632</width> <width>635</width>
<height>24</height> <height>24</height>
</rect> </rect>
</property> </property>
@ -403,39 +383,65 @@ p, li { white-space: pre-wrap; }
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="3"> <item row="0" column="2">
<layout class="QHBoxLayout" name="buttonHLayout"> <spacer name="horizontalSpacer">
<item>
<widget class="QCheckBox" name="generateCheckBox">
<property name="text">
<string>Generate mass data</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="generateSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons"> <property name="sizeHint" stdset="0">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <size>
<width>486</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="attachmentsButton">
<property name="text">
<string>Attachments</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="0" column="0">
<widget class="QPushButton" name="channelpostButton">
<property name="text">
<string>Channel Post</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<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> </item>
</layout> </layout>
</widget> </widget>
@ -455,10 +461,16 @@ p, li { white-space: pre-wrap; }
<header>util/RichTextEdit.h</header> <header>util/RichTextEdit.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>ChannelPostThumbnailView</class>
<extends>QWidget</extends>
<header>gui/gxschannels/GxsChannelPostThumbnail.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../icons.qrc"/>
<include location="../images.qrc"/> <include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -26,7 +26,7 @@
#include "GxsChannelDialog.h" #include "GxsChannelDialog.h"
#include "GxsChannelGroupDialog.h" #include "GxsChannelGroupDialog.h"
#include "GxsChannelPostsWidget.h" #include "GxsChannelPostsWidgetWithModel.h"
#include "CreateGxsChannelMsg.h" #include "CreateGxsChannelMsg.h"
#include "GxsChannelUserNotify.h" #include "GxsChannelUserNotify.h"
#include "gui/gxs/GxsGroupShareKey.h" #include "gui/gxs/GxsGroupShareKey.h"
@ -204,7 +204,7 @@ int GxsChannelDialog::shareKeyType()
GxsMessageFrameWidget *GxsChannelDialog::createMessageFrameWidget(const RsGxsGroupId &groupId) GxsMessageFrameWidget *GxsChannelDialog::createMessageFrameWidget(const RsGxsGroupId &groupId)
{ {
return new GxsChannelPostsWidget(groupId); return new GxsChannelPostsWidgetWithModel(groupId,this);
} }
void GxsChannelDialog::setDefaultDirectory() void GxsChannelDialog::setDefaultDirectory()

View File

@ -18,6 +18,7 @@
* * * *
*******************************************************************************/ *******************************************************************************/
#include <QMenu>
#include <QTimer> #include <QTimer>
#include <QMessageBox> #include <QMessageBox>
#include <QFileInfo> #include <QFileInfo>
@ -27,11 +28,13 @@
#include "GxsChannelFilesStatusWidget.h" #include "GxsChannelFilesStatusWidget.h"
#include "ui_GxsChannelFilesStatusWidget.h" #include "ui_GxsChannelFilesStatusWidget.h"
#include "gui/common/RsUrlHandler.h" #include "gui/common/RsUrlHandler.h"
#include "gui/common/FilesDefs.h"
#include "util/misc.h"
#include "retroshare/rsfiles.h" #include "retroshare/rsfiles.h"
GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent) : GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent) :
QWidget(parent), mGroupId(groupId), mMessageId(messageId), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget) QWidget(parent), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -40,11 +43,21 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &gro
setSize(mFile.mSize); setSize(mFile.mSize);
/* Connect signals */ /* Connect signals */
connect(ui->downloadToolButton, SIGNAL(clicked()), this, SLOT(download())); connect(ui->downloadPushButton, SIGNAL(clicked()), this, SLOT(download()));
connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume())); connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume()));
connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause())); connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause()));
connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel())); connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel()));
connect(ui->openFolderToolButton, SIGNAL(clicked()), this, SLOT(openFolder())); connect(ui->openFilePushButton, SIGNAL(clicked()), this, SLOT(openFile()));
ui->downloadPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png"));
ui->openFolderToolButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/arrow.png"));
QAction *openfolder = new QAction(tr("Open folder"), this);
connect(openfolder, SIGNAL(triggered()), this, SLOT(openFolder()));
QMenu *menu = new QMenu();
menu->addAction(openfolder);
ui->openFolderToolButton->setMenu(menu);
check(); check();
} }
@ -80,6 +93,17 @@ void GxsChannelFilesStatusWidget::check()
if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
mState = STATE_LOCAL; mState = STATE_LOCAL;
setSize(fileInfo.size); setSize(fileInfo.size);
/* check if the file is a media file */
if (!misc::isPreviewable(QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).suffix()))
{
/* check if the file is not a media file and change text */
ui->openFilePushButton->setText(tr("Open file"));
} else {
ui->openFilePushButton->setText(tr("Play"));
ui->openFilePushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png"));
}
} else { } else {
FileInfo fileInfo; FileInfo fileInfo;
bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo); bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo);
@ -126,11 +150,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_ERROR: case STATE_ERROR:
repeat = 0; repeat = 0;
ui->downloadToolButton->hide(); ui->downloadPushButton->hide();
ui->resumeToolButton->hide(); ui->resumeToolButton->hide();
ui->pauseToolButton->hide(); ui->pauseToolButton->hide();
ui->cancelToolButton->hide(); ui->cancelToolButton->hide();
ui->progressBar->hide(); ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide(); ui->openFolderToolButton->hide();
statusText = tr("Error"); statusText = tr("Error");
@ -140,11 +165,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_REMOTE: case STATE_REMOTE:
repeat = 30000; repeat = 30000;
ui->downloadToolButton->show(); ui->downloadPushButton->show();
ui->resumeToolButton->hide(); ui->resumeToolButton->hide();
ui->pauseToolButton->hide(); ui->pauseToolButton->hide();
ui->cancelToolButton->hide(); ui->cancelToolButton->hide();
ui->progressBar->hide(); ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide(); ui->openFolderToolButton->hide();
break; break;
@ -152,11 +178,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_DOWNLOAD: case STATE_DOWNLOAD:
repeat = 1000; repeat = 1000;
ui->downloadToolButton->hide(); ui->downloadPushButton->hide();
ui->resumeToolButton->hide(); ui->resumeToolButton->hide();
ui->pauseToolButton->show(); ui->pauseToolButton->show();
ui->cancelToolButton->show(); ui->cancelToolButton->show();
ui->progressBar->show(); ui->progressBar->show();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide(); ui->openFolderToolButton->hide();
break; break;
@ -164,11 +191,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_PAUSED: case STATE_PAUSED:
repeat = 1000; repeat = 1000;
ui->downloadToolButton->hide(); ui->downloadPushButton->hide();
ui->resumeToolButton->show(); ui->resumeToolButton->show();
ui->pauseToolButton->hide(); ui->pauseToolButton->hide();
ui->cancelToolButton->show(); ui->cancelToolButton->show();
ui->progressBar->hide(); ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide(); ui->openFolderToolButton->hide();
statusText = tr("Paused"); statusText = tr("Paused");
@ -178,11 +206,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_WAITING: case STATE_WAITING:
repeat = 1000; repeat = 1000;
ui->downloadToolButton->hide(); ui->downloadPushButton->hide();
ui->resumeToolButton->hide(); ui->resumeToolButton->hide();
ui->pauseToolButton->show(); ui->pauseToolButton->show();
ui->cancelToolButton->show(); ui->cancelToolButton->show();
ui->progressBar->hide(); ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide(); ui->openFolderToolButton->hide();
statusText = tr("Waiting"); statusText = tr("Waiting");
@ -192,11 +221,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_CHECKING: case STATE_CHECKING:
repeat = 1000; repeat = 1000;
ui->downloadToolButton->hide(); ui->downloadPushButton->hide();
ui->resumeToolButton->hide(); ui->resumeToolButton->hide();
ui->pauseToolButton->hide(); ui->pauseToolButton->hide();
ui->cancelToolButton->show(); ui->cancelToolButton->show();
ui->progressBar->hide(); ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide(); ui->openFolderToolButton->hide();
statusText = tr("Checking"); statusText = tr("Checking");
@ -206,11 +236,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_LOCAL: case STATE_LOCAL:
repeat = 60000; repeat = 60000;
ui->downloadToolButton->hide(); ui->downloadPushButton->hide();
ui->resumeToolButton->hide(); ui->resumeToolButton->hide();
ui->pauseToolButton->hide(); ui->pauseToolButton->hide();
ui->cancelToolButton->hide(); ui->cancelToolButton->hide();
ui->progressBar->hide(); ui->progressBar->hide();
ui->openFilePushButton->show();
ui->openFolderToolButton->show(); ui->openFolderToolButton->show();
break; break;
@ -296,3 +327,24 @@ void GxsChannelFilesStatusWidget::openFolder()
} }
} }
} }
void GxsChannelFilesStatusWidget::openFile()
{
FileInfo fileInfo;
if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
return;
}
/* open file with a suitable application */
QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(fileInfo.path.c_str()));
if (qinfo.exists()) {
if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) {
std::cerr << "GxsChannelFilesStatusWidget(): can't open file " << fileInfo.path << std::endl;
}
}else{
QMessageBox::information(this, tr("Play File"),
tr("File %1 does not exist at location.").arg(fileInfo.path.c_str()));
return;
}
}

View File

@ -34,7 +34,7 @@ class GxsChannelFilesStatusWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent = 0); explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0);
~GxsChannelFilesStatusWidget(); ~GxsChannelFilesStatusWidget();
private slots: private slots:
@ -44,6 +44,7 @@ private slots:
void pause(); void pause();
void resume(); void resume();
void openFolder(); void openFolder();
void openFile();
private: private:
void setSize(uint64_t size); void setSize(uint64_t size);

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>367</width> <width>421</width>
<height>27</height> <height>29</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -48,7 +48,7 @@
<number>2</number> <number>2</number>
</property> </property>
<item> <item>
<widget class="QToolButton" name="downloadToolButton"> <widget class="QPushButton" name="downloadPushButton">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -126,18 +126,31 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QToolButton" name="openFolderToolButton"> <widget class="QPushButton" name="openFilePushButton">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openFolderToolButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::NoFocus</enum> <enum>Qt::NoFocus</enum>
</property> </property>
<property name="text"> <property name="popupMode">
<string>Open folder</string> <enum>QToolButton::InstantPopup</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -148,6 +161,7 @@
</widget> </widget>
<resources> <resources>
<include location="../images.qrc"/> <include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -86,7 +86,7 @@ GxsChannelFilesWidget::~GxsChannelFilesWidget()
delete ui; delete ui;
} }
void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related) void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost& post, bool related)
{ {
if (related) { if (related) {
removeItems(post.mMeta.mGroupId, post.mMeta.mMsgId); removeItems(post.mMeta.mGroupId, post.mMeta.mMsgId);
@ -113,7 +113,7 @@ void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related)
ui->treeWidget->addTopLevelItem(treeItem); ui->treeWidget->addTopLevelItem(treeItem);
QWidget *statusWidget = new GxsChannelFilesStatusWidget(post.mMeta.mGroupId, post.mMeta.mMsgId, file); QWidget *statusWidget = new GxsChannelFilesStatusWidget(file);
ui->treeWidget->setItemWidget(treeItem, COLUMN_STATUS, statusWidget); ui->treeWidget->setItemWidget(treeItem, COLUMN_STATUS, statusWidget);
filterItem(treeItem); filterItem(treeItem);

View File

@ -0,0 +1,476 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp *
* *
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <QApplication>
#include <QFontMetrics>
#include <QModelIndex>
#include <QIcon>
#include "gui/common/FilesDefs.h"
#include "util/qtthreadsutils.h"
#include "util/HandleRichText.h"
#include "util/DateTime.h"
#include "retroshare/rsgxsflags.h"
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsexpr.h"
#include "GxsChannelPostFilesModel.h"
//#define DEBUG_CHANNEL_MODEL
Q_DECLARE_METATYPE(ChannelPostFileInfo)
static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent)
: QAbstractItemModel(parent)
{
initEmptyHierarchy();
mTimer = new QTimer;
connect(mTimer,SIGNAL(timeout()),this,SLOT(update()));
}
void RsGxsChannelPostFilesModel::initEmptyHierarchy()
{
preMods();
mFiles.clear();
mFilteredFiles.clear();
postMods();
}
void RsGxsChannelPostFilesModel::preMods()
{
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
beginResetModel();
}
void RsGxsChannelPostFilesModel::postMods()
{
endResetModel();
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
}
void RsGxsChannelPostFilesModel::update()
{
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
}
int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const
{
if(parent.column() > 0)
return 0;
if(mFilteredFiles.empty()) // security. Should never happen.
return 0;
if(!parent.isValid())
return mFilteredFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
return 0;
}
int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const
{
return COLUMN_FILES_NB_COLUMNS ;
}
bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,ChannelPostFileInfo& fmpe) const
{
if(!i.isValid())
return true;
quintptr ref = i.internalId();
uint32_t entry = 0;
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size())
return false ;
fmpe = mFiles[mFilteredFiles[entry]];
return true;
}
bool RsGxsChannelPostFilesModel::hasChildren(const QModelIndex &parent) const
{
if(!parent.isValid())
return true;
return false; // by default, no channel post has children
}
bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref)
{
// the pointer is formed the following way:
//
// [ 32 bits ]
//
// This means that the whole software has the following build-in limitation:
// * 4 B simultaenous posts. Should be enough !
ref = (intptr_t)(entry+1);
return true;
}
bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry)
{
intptr_t val = (intptr_t)ref;
if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious
{
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl;
return false ;
}
if(val==0)
{
RsErr() << "(EE) trying to make a ChannelPostsFileModelIndex out of index 0." << std::endl;
return false;
}
entry = val-1;
return true;
}
QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const
{
if(row < 0 || column < 0 || column >= COLUMN_FILES_NB_COLUMNS)
return QModelIndex();
quintptr ref = getChildRef(parent.internalId(),row);
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
#endif
return createIndex(row,column,ref) ;
}
QModelIndex RsGxsChannelPostFilesModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return QModelIndex();
return QModelIndex(); // there's no hierarchy here. So nothing to do!
}
Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
return 0;
if(index.column() == COLUMN_FILES_FILE)
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
else
return QAbstractItemModel::flags(index);
}
quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const
{
if (index < 0)
return 0;
if(ref == quintptr(0))
{
quintptr new_ref;
convertTabEntryToRefPointer(index,new_ref);
return new_ref;
}
else
return 0 ;
}
quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const
{
ChannelPostFilesModelIndex ref_entry;
if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredFiles.size())
return 0 ;
if(ref_entry == 0)
{
RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl;
row = 0;
}
else
row = ref_entry-1;
return 0;
}
int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const
{
uint32_t entry = 0 ;
if(ref == quintptr(0))
return rowCount()-1;
return 0;
}
QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
switch(section)
{
case COLUMN_FILES_FILE: return QString("Status");
case COLUMN_FILES_SIZE: return QString("Size");
case COLUMN_FILES_NAME: return QString("File");
case COLUMN_FILES_DATE: return QString("Published");
default:
return QString("[No data]");
}
}
QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
#endif
if(!index.isValid())
return QVariant();
switch(role)
{
case Qt::SizeHintRole: return sizeHintRole(index.column()) ;
case Qt::StatusTipRole:return QVariant();
default: break;
}
quintptr ref = (index.isValid())?index.internalId():0 ;
uint32_t entry = 0;
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "data(" << index << ")" ;
#endif
if(!ref)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " [empty]" << std::endl;
#endif
return QVariant() ;
}
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size())
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
#endif
return QVariant() ;
}
const ChannelPostFileInfo& fmpe(mFiles[mFilteredFiles[entry]]);
switch(role)
{
case Qt::DisplayRole: return displayRole (fmpe,index.column()) ;
case Qt::UserRole: return userRole (fmpe,index.column()) ;
case SortRole: return sortRole (fmpe,index.column()) ;
default:
return QVariant();
}
}
void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& count)
{
preMods();
beginRemoveRows(QModelIndex(),0,rowCount()-1);
endRemoveRows();
if(strings.empty())
{
mFilteredFiles.clear();
for(int i=0;i<mFiles.size();++i)
mFilteredFiles.push_back(i);
}
else
{
mFilteredFiles.clear();
//mFilteredPosts.push_back(0);
for(int i=0;i<mFiles.size();++i)
{
bool passes_strings = true;
for(auto& s:strings)
passes_strings = passes_strings && QString::fromStdString(mFiles[i].mName).contains(s,Qt::CaseInsensitive);
if(passes_strings)
mFilteredFiles.push_back(i);
}
}
count = mFilteredFiles.size();
std::cerr << "After filtering: " << count << " posts remain." << std::endl;
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
postMods();
}
class compareOperator
{
public:
compareOperator(int column,Qt::SortOrder order): col(column),ord(order) {}
bool operator()(const ChannelPostFileInfo& f1,const ChannelPostFileInfo& f2) const
{
switch(col)
{
default:
case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return (ord==Qt::AscendingOrder)?(f1.mName<f2.mName):(f1.mName>f2.mName);
case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSize<f2.mSize):(f1.mSize>f2.mSize);
case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return (ord==Qt::AscendingOrder)?(f1.mPublishTime<f2.mPublishTime):(f1.mPublishTime>f2.mPublishTime);
case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE:
{
FileInfo fi1,fi2;
rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1);
rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2);
return (ord==Qt::AscendingOrder)?(fi1.transfered<fi2.transfered):(fi1.transfered>fi2.transfered);
}
}
}
private:
int col;
Qt::SortOrder ord;
};
void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order)
{
std::sort(mFiles.begin(),mFiles.end(),compareOperator(column,order));
update();
}
QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const
{
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
return QVariant( QSize(factor * 170, factor*14 ));
}
QVariant RsGxsChannelPostFilesModel::sortRole(const ChannelPostFileInfo& fmpe,int column) const
{
switch(column)
{
case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str()));
case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize));
case COLUMN_FILES_DATE: return QVariant(qulonglong(fmpe.mPublishTime));
case COLUMN_FILES_FILE:
{
FileInfo finfo;
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
return qulonglong(finfo.transfered);
return QVariant(qulonglong(fmpe.mSize));
}
break;
default:
return displayRole(fmpe,column);
}
}
QVariant RsGxsChannelPostFilesModel::displayRole(const ChannelPostFileInfo& fmpe,int col) const
{
switch(col)
{
case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str());
case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize);
case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime);
case COLUMN_FILES_FILE: {
FileInfo finfo;
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
return qulonglong(finfo.transfered);
else
return 0;
}
default:
return QString();
}
return QVariant("[ERROR]");
}
QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,int col) const
{
switch(col)
{
default:
return QVariant::fromValue(fmpe);
}
}
void RsGxsChannelPostFilesModel::clear()
{
preMods();
initEmptyHierarchy();
postMods();
emit channelLoaded();
}
void RsGxsChannelPostFilesModel::setFiles(const std::list<ChannelPostFileInfo> &files)
{
preMods();
beginRemoveRows(QModelIndex(),0,mFilteredFiles.size()-1);
endRemoveRows();
initEmptyHierarchy();
for(auto& file:files)
mFiles.push_back(file);
for(uint32_t i=0;i<mFiles.size();++i)
mFilteredFiles.push_back(i);
#ifdef DEBUG_CHANNEL_MODEL
// debug_dump();
#endif
beginInsertRows(QModelIndex(),0,mFilteredFiles.size()-1);
endInsertRows();
postMods();
emit channelLoaded();
if(!files.empty())
mTimer->start(5000);
else
mTimer->stop();
}

View File

@ -0,0 +1,173 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h *
* *
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include "retroshare/rsfiles.h"
#include "retroshare/rsgxscommon.h"
#include <QModelIndex>
#include <QColor>
// This class holds the actual hierarchy of posts, represented by identifiers
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
// safely access the data.
// The model contains a post in place 0 that is the parent of all posts.
typedef uint32_t ChannelPostFilesModelIndex;
class QTimer;
// This class contains the info for a file as well as additional info such as publication date
struct ChannelPostFileInfo: public RsGxsFile
{
ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t)
: RsGxsFile(gxs_file),mPublishTime(t)
{}
ChannelPostFileInfo() : mPublishTime(0) {}
rstime_t mPublishTime;
};
// This class is the item model used by Qt to display the information
class RsGxsChannelPostFilesModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit RsGxsChannelPostFilesModel(QObject *parent = NULL);
~RsGxsChannelPostFilesModel(){}
enum Columns {
COLUMN_FILES_NAME = 0x00,
COLUMN_FILES_SIZE = 0x01,
COLUMN_FILES_FILE = 0x02,
COLUMN_FILES_DATE = 0x03,
COLUMN_FILES_NB_COLUMNS = 0x04
};
enum Roles{ SortRole = Qt::UserRole+1,
FilterRole = Qt::UserRole+2,
};
#ifdef TODO
enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00,
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
};
#endif
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
// This method will asynchroneously update the data
void setFiles(const std::list<ChannelPostFileInfo>& files);
void setFilter(const QStringList &strings, uint32_t &count) ;
#ifdef TODO
QModelIndex getIndexOfFile(const RsFileHash& hash) const;
void setSortMode(SortMode mode) ;
void setTextColorRead (QColor color) { mTextColorRead = color;}
void setTextColorUnread (QColor color) { mTextColorUnread = color;}
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
void setTextColorMissing (QColor color) { mTextColorMissing = color;}
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
#endif
// Helper functions
void clear() ;
// AbstractItemModel functions.
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
// Custom item roles
QVariant sizeHintRole (int col) const;
QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const;
#ifdef TODO
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
#endif
/*!
* \brief debug_dump
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
*/
void debug_dump();
signals:
void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI.
private slots:
void update();
private:
#ifdef TODO
bool mUseChildTS;
bool mFilteringEnabled;
SortMode mSortMode;
#endif
void preMods() ;
void postMods() ;
quintptr getParentRow(quintptr ref,int& row) const;
quintptr getChildRef(quintptr ref, int index) const;
int getChildrenCount(quintptr ref) const;
bool getFileData(const QModelIndex& i, ChannelPostFileInfo &fmpe) const;
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref);
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
#ifdef TODO
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
#endif
void initEmptyHierarchy();
std::vector<int> mFilteredFiles ; // store the list of files for the post
std::vector<ChannelPostFileInfo> mFiles ; // store the list of files for the post
QTimer *mTimer;
};

View File

@ -0,0 +1,123 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h *
* *
* 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/>. *
* *
*******************************************************************************/
#pragma once
#include <string>
#include <QWidget>
#include <QLabel>
#include <QLayout>
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsidentity.h"
#include "gui/gxs/GxsIdDetails.h"
#include "gui/common/FilesDefs.h"
// Class to paint the thumbnails with title
class ChannelPostThumbnailView: public QWidget
{
public:
// 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;
// Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good.
static const int THUMBNAIL_W = 4;
static const int THUMBNAIL_H = 6;
static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png";
virtual ~ChannelPostThumbnailView()
{
delete lb;
delete lt;
}
ChannelPostThumbnailView(QWidget *parent=NULL): QWidget(parent)
{
init(FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE), QString("New post"),false);
}
ChannelPostThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL)
: QWidget(parent)
{
// now fill the data
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);
init(thumbnail, QString::fromUtf8(post.mMeta.mMsgName.c_str()), IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) );
}
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); }
private:
QLabel *lb;
QLabel *lt;
};

View File

@ -0,0 +1,736 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp *
* *
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <QApplication>
#include <QFontMetrics>
#include <QModelIndex>
#include <QIcon>
#include "retroshare/rsgxsflags.h"
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsexpr.h"
#include "gui/common/FilesDefs.h"
#include "util/qtthreadsutils.h"
#include "util/HandleRichText.h"
#include "util/DateTime.h"
#include "GxsChannelPostsModel.h"
#include "GxsChannelPostFilesModel.h"
//#define DEBUG_CHANNEL_MODEL
Q_DECLARE_METATYPE(RsMsgMetaData)
Q_DECLARE_METATYPE(RsGxsChannelPost)
std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent)
: QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6)
{
initEmptyHierarchy();
mEventHandlerId = 0;
// Needs to be asynced because this function is called by another thread!
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
{
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
}, mEventHandlerId, RsEventType::GXS_CHANNELS );
}
RsGxsChannelPostsModel::~RsGxsChannelPostsModel()
{
rsEvents->unregisterEventsHandler(mEventHandlerId);
}
void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{
const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get());
if(!e)
return;
switch(e->mChannelEventCode)
{
case RsChannelEventCode::UPDATED_MESSAGE:
case RsChannelEventCode::READ_STATUS_CHANGED:
{
// Normally we should just emit dataChanged() on the index of the data that has changed:
//
// We need to update the data!
if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId)
RsThread::async([this, e]()
{
// 1 - get message data from p3GxsChannels
std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> comments;
std::vector<RsGxsVote> votes;
if(!rsGxsChannels->getChannelContent(mChannelGroup.mMeta.mGroupId,std::set<RsGxsMessageId>{ e->mChannelMsgId }, posts,comments,votes))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << e->mChannelGroupId << "/" << e->mChannelMsgId << std::endl;
return;
}
// 2 - update the model in the UI thread.
RsQThreadUtils::postToObject( [posts,comments,votes,this]()
{
for(uint32_t i=0;i<posts.size();++i)
{
// linear search. Not good at all, but normally this is for a single post.
for(uint32_t j=0;j<mPosts.size();++j)
if(mPosts[j].mMeta.mMsgId == posts[i].mMeta.mMsgId)
{
mPosts[j] = posts[i];
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL));
}
}
},this);
});
default:
break;
}
}
}
void RsGxsChannelPostsModel::initEmptyHierarchy()
{
preMods();
mPosts.clear();
mFilteredPosts.clear();
// mPosts.resize(1); // adds a sentinel item
// mPosts[0].mMeta.mMsgName = "Root sentinel post" ;
// mFilteredPosts.resize(1);
// mFilteredPosts[0] = 1;
postMods();
}
void RsGxsChannelPostsModel::preMods()
{
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
beginResetModel();
}
void RsGxsChannelPostsModel::postMods()
{
endResetModel();
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL));
}
void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files)
{
// We use an intermediate map so as to remove duplicates
std::map<RsFileHash,ChannelPostFileInfo> files_map;
for(uint32_t i=0;i<mPosts.size();++i)
for(auto& file:mPosts[i].mFiles)
files_map.insert(std::make_pair(file.mHash,ChannelPostFileInfo(file,mPosts[i].mMeta.mPublishTs)));
files.clear();
for(auto& it:files_map)
files.push_back(it.second);
}
void RsGxsChannelPostsModel::setFilter(const QStringList& strings, uint32_t& count)
{
preMods();
beginRemoveRows(QModelIndex(),0,rowCount()-1);
endRemoveRows();
if(strings.empty())
{
mFilteredPosts.clear();
for(int i=0;i<mPosts.size();++i)
mFilteredPosts.push_back(i);
}
else
{
mFilteredPosts.clear();
//mFilteredPosts.push_back(0);
for(int 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();
std::cerr << "After filtering: " << count << " posts remain." << std::endl;
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
postMods();
}
int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const
{
if(parent.column() > 0)
return 0;
if(mFilteredPosts.empty()) // security. Should never happen.
return 0;
if(!parent.isValid())
return (mFilteredPosts.size() + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
return 0;
}
int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const
{
return std::min((int)mFilteredPosts.size(),(int)mColumns) ;
}
bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const
{
if(!i.isValid())
return true;
quintptr ref = i.internalId();
uint32_t entry = 0;
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
return false ;
fmpe = mPosts[mFilteredPosts[entry]];
return true;
}
bool RsGxsChannelPostsModel::hasChildren(const QModelIndex &parent) const
{
if(!parent.isValid())
return true;
return false; // by default, no channel post has children
}
bool RsGxsChannelPostsModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref)
{
// the pointer is formed the following way:
//
// [ 32 bits ]
//
// This means that the whole software has the following build-in limitation:
// * 4 B simultaenous posts. Should be enough !
ref = (intptr_t)(entry+1);
return true;
}
bool RsGxsChannelPostsModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry)
{
intptr_t val = (intptr_t)ref;
if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious
{
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl;
return false ;
}
if(val==0)
{
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of index 0." << std::endl;
return false;
}
entry = val - 1;
return true;
}
QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex & parent) const
{
if(row < 0 || column < 0 || column >= mColumns)
return QModelIndex();
quintptr ref = getChildRef(parent.internalId(),column + row*mColumns);
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
#endif
return createIndex(row,column,ref) ;
}
QModelIndex RsGxsChannelPostsModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return QModelIndex();
return QModelIndex(); // there's no hierarchy here. So nothing to do!
}
Qt::ItemFlags RsGxsChannelPostsModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
void RsGxsChannelPostsModel::setNumColumns(int n)
{
if(n < 1)
{
RsErr() << __PRETTY_FUNCTION__ << " Attempt to set a number of column of 0. This is wrong." << std::endl;
return;
}
preMods();
beginRemoveRows(QModelIndex(),0,rowCount()-1);
endRemoveRows();
mColumns = n;
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
postMods();
}
quintptr RsGxsChannelPostsModel::getChildRef(quintptr ref,int index) const
{
if (index < 0)
return 0;
if(ref == quintptr(0))
{
quintptr new_ref;
convertTabEntryToRefPointer(index,new_ref);
return new_ref;
}
else
return 0 ;
}
quintptr RsGxsChannelPostsModel::getParentRow(quintptr ref,int& row) const
{
ChannelPostsModelIndex ref_entry;
if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredPosts.size())
return 0 ;
if(ref_entry == 0)
{
RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl;
row = 0;
}
else
row = ref_entry-1;
return 0;
}
int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const
{
uint32_t entry = 0 ;
if(ref == quintptr(0))
return rowCount()-1;
return 0;
}
QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
#endif
if(!index.isValid())
return QVariant();
switch(role)
{
case Qt::SizeHintRole: return sizeHintRole(index.column()) ;
case Qt::StatusTipRole:return QVariant();
default: break;
}
quintptr ref = (index.isValid())?index.internalId():0 ;
uint32_t entry = 0;
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "data(" << index << ")" ;
#endif
if(!ref)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " [empty]" << std::endl;
#endif
return QVariant() ;
}
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
#endif
return QVariant() ;
}
const RsGxsChannelPost& fmpe(mPosts[mFilteredPosts[entry]]);
switch(role)
{
case Qt::DisplayRole: return displayRole (fmpe,index.column()) ;
case Qt::UserRole: return userRole (fmpe,index.column()) ;
default:
return QVariant();
}
}
QVariant RsGxsChannelPostsModel::sizeHintRole(int col) const
{
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
return QVariant( QSize(factor * 170, factor*14 ));
}
QVariant RsGxsChannelPostsModel::displayRole(const RsGxsChannelPost& fmpe,int col) const
{
switch(col)
{
default:
return QString::fromUtf8(fmpe.mMeta.mMsgName.c_str());
}
return QVariant("[ERROR]");
}
QVariant RsGxsChannelPostsModel::userRole(const RsGxsChannelPost& fmpe,int col) const
{
switch(col)
{
default:
return QVariant::fromValue(fmpe);
}
}
const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const
{
return mChannelGroup.mMeta.mGroupId;
}
void RsGxsChannelPostsModel::updateChannel(const RsGxsGroupId& channel_group_id)
{
if(channel_group_id.isNull())
return;
update_posts(channel_group_id);
}
void RsGxsChannelPostsModel::clear()
{
preMods();
mPosts.clear();
initEmptyHierarchy();
postMods();
emit channelPostsLoaded();
}
bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2)
{
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
}
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
{
preMods();
beginRemoveRows(QModelIndex(),0,rowCount()-1);
endRemoveRows();
mPosts.clear();
mChannelGroup = group;
createPostsArray(posts);
std::sort(mPosts.begin(),mPosts.end());
mFilteredPosts.clear();
for(int i=0;i<mPosts.size();++i)
mFilteredPosts.push_back(i);
#ifdef DEBUG_CHANNEL_MODEL
// debug_dump();
#endif
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
postMods();
emit channelPostsLoaded();
}
void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
{
if(group_id.isNull())
return;
RsThread::async([this, group_id]()
{
// 1 - get message data from p3GxsChannels
std::list<RsGxsGroupId> channelIds;
std::vector<RsMsgMetaData> msg_metas;
std::vector<RsGxsChannelGroup> groups;
channelIds.push_back(group_id);
if(!rsGxsChannels->getChannelsInfo(channelIds,groups) || groups.size() != 1)
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel group info for channel " << group_id << std::endl;
return;
}
RsGxsChannelGroup group = groups[0];
// We use the heap because the arrays need to be stored accross async
std::vector<RsGxsChannelPost> *posts = new std::vector<RsGxsChannelPost>();
std::vector<RsGxsComment> *comments = new std::vector<RsGxsComment>();
std::vector<RsGxsVote> *votes = new std::vector<RsGxsVote>();
if(!rsGxsChannels->getChannelAllContent(group_id, *posts,*comments,*votes))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel messages for channel " << group_id << std::endl;
return;
}
// 2 - update the model in the UI thread.
RsQThreadUtils::postToObject( [group,posts,comments,votes,this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete, note that
* Qt::QueuedConnection is important!
*/
setPosts(group,*posts) ;
delete posts;
delete comments;
delete votes;
}, this );
});
}
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)
{
// collect new versions of posts if any
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Inserting channel posts" << std::endl;
#endif
std::vector<uint32_t> new_versions ;
for (uint32_t i=0;i<posts.size();++i)
{
if(posts[i].mMeta.mOrigMsgId == posts[i].mMeta.mMsgId)
posts[i].mMeta.mOrigMsgId.clear();
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " " << i << ": name=\"" << posts[i].mMeta.mMsgName << "\" msg_id=" << posts[i].mMeta.mMsgId << ": orig msg id = " << posts[i].mMeta.mOrigMsgId << std::endl;
#endif
if(!posts[i].mMeta.mOrigMsgId.isNull())
new_versions.push_back(i) ;
}
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "New versions: " << new_versions.size() << std::endl;
#endif
if(!new_versions.empty())
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " New versions present. Replacing them..." << std::endl;
std::cerr << " Creating search map." << std::endl;
#endif
// make a quick search map
std::map<RsGxsMessageId,uint32_t> search_map ;
for (uint32_t i=0;i<posts.size();++i)
search_map[posts[i].mMeta.mMsgId] = i ;
for(uint32_t i=0;i<new_versions.size();++i)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " Taking care of new version at index " << new_versions[i] << std::endl;
#endif
uint32_t current_index = new_versions[i] ;
uint32_t source_index = new_versions[i] ;
#ifdef DEBUG_CHANNEL_MODEL
RsGxsMessageId source_msg_id = posts[source_index].mMeta.mMsgId ;
#endif
// What we do is everytime we find a replacement post, we climb up the replacement graph until we find the original post
// (or the most recent version of it). When we reach this post, we replace it with the data of the source post.
// In the mean time, all other posts have their MsgId cleared, so that the posts are removed from the list.
//std::vector<uint32_t> versions ;
std::map<RsGxsMessageId,uint32_t>::const_iterator vit ;
while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId)))
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ;
#endif
// Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple
// times. In this case, we only need to replace the post with the newest version
//uint32_t prev_index = current_index ;
current_index = vit->second ;
if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further.
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " already erased. Stopping." << std::endl;
#endif
break ;
}
if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " and is more recent => following" << std::endl;
#endif
for(std::set<RsGxsMessageId>::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt)
posts[source_index].mOlderVersions.insert(*itt);
posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId);
posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored
}
#ifdef DEBUG_CHANNEL_MODEL
else
std::cerr << " but is older -> Stopping" << std::endl;
#endif
}
}
}
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Now adding " << posts.size() << " posts into array structure..." << std::endl;
#endif
mPosts.clear();
for (std::vector<RsGxsChannelPost>::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it)
{
if(!(*it).mMeta.mMsgId.isNull())
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " adding post \"" << (*it).mMeta.mMsgName << "\"" << std::endl;
#endif
mPosts.push_back(*it);
}
#ifdef DEBUG_CHANNEL_MODEL
else
std::cerr << " skipped older version post \"" << (*it).mMeta.mMsgName << "\"" << std::endl;
#endif
}
}
void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_status,bool with_children)
{
if(!i.isValid())
return ;
// no need to call preMods()/postMods() here because we'renot changing the model
quintptr ref = i.internalId();
uint32_t entry = 0;
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
return ;
#warning TODO
// bool has_unread_below, has_read_below;
// recursSetMsgReadStatus(entry,read_status,with_children) ;
// recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below);
}
QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) const
{
// Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map.
RsGxsMessageId postId = mid;
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
for(auto& msg_id:mPosts[mFilteredPosts[i]].mOlderVersions)
if(msg_id == postId)
{
quintptr ref ;
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab
return createIndex(i/mColumns,i%mColumns, ref);
}
if(mPosts[mFilteredPosts[i]].mMeta.mMsgId == postId)
{
quintptr ref ;
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab
return createIndex(i/mColumns,i%mColumns, ref);
}
}
return QModelIndex();
}

View File

@ -0,0 +1,239 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h *
* *
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsgxsifacetypes.h"
#include "retroshare/rsevents.h"
#include <QModelIndex>
#include <QColor>
struct ChannelPostFileInfo;
// This class holds the actual hierarchy of posts, represented by identifiers
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
// safely access the data.
// The model contains a post in place 0 that is the parent of all posts.
typedef uint32_t ChannelPostsModelIndex;
// struct ChannelPostsModelPostEntry
// {
// ChannelPostsModelPostEntry() : mPublishTs(0),mPostFlags(0),mMsgStatus(0),prow(0) {}
//
// enum { // flags for display of posts. To be used in mPostFlags
// FLAG_POST_IS_PINNED = 0x0001,
// FLAG_POST_IS_MISSING = 0x0002,
// FLAG_POST_IS_REDACTED = 0x0004,
// FLAG_POST_HAS_UNREAD_CHILDREN = 0x0008,
// FLAG_POST_HAS_READ_CHILDREN = 0x0010,
// FLAG_POST_PASSES_FILTER = 0x0020,
// FLAG_POST_CHILDREN_PASSES_FILTER = 0x0040,
// };
//
// std::string mTitle ;
// RsGxsMessageId mMsgId;
// uint32_t mPublishTs;
// uint32_t mPostFlags;
// int mMsgStatus;
//
// int prow ;// parent row, which basically means position in the array of posts
// };
// This class is the item model used by Qt to display the information
class RsGxsChannelPostsModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit RsGxsChannelPostsModel(QObject *parent = NULL);
virtual ~RsGxsChannelPostsModel() override;
static const int COLUMN_THREAD_NB_COLUMNS = 0x01;
#ifdef TODO
enum Columns {
COLUMN_THREAD_TITLE =0x00,
COLUMN_THREAD_READ =0x01,
COLUMN_THREAD_DATE =0x02,
COLUMN_THREAD_DISTRIBUTION =0x03,
COLUMN_THREAD_AUTHOR =0x04,
COLUMN_THREAD_CONTENT =0x05,
COLUMN_THREAD_MSGID =0x06,
COLUMN_THREAD_DATA =0x07,
};
enum Roles{ SortRole = Qt::UserRole+1,
ThreadPinnedRole = Qt::UserRole+2,
MissingRole = Qt::UserRole+3,
StatusRole = Qt::UserRole+4,
UnreadChildrenRole = Qt::UserRole+5,
FilterRole = Qt::UserRole+6,
};
#endif
enum TreeMode{ TREE_MODE_UNKWN = 0x00,
TREE_MODE_PLAIN = 0x01,
TREE_MODE_FILES = 0x02,
};
#ifdef TODO
enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00,
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
};
#endif
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const;
std::vector<std::pair<time_t,RsGxsMessageId> > getPostVersions(const RsGxsMessageId& mid) const;
// This method will asynchroneously update the data
void updateChannel(const RsGxsGroupId& channel_group_id);
const RsGxsGroupId& currentGroupId() const;
void setNumColumns(int n);
// Retrieve the full list of files for all posts.
void getFilesList(std::list<ChannelPostFileInfo> &files);
#ifdef TODO
void setSortMode(SortMode mode) ;
void setTextColorRead (QColor color) { mTextColorRead = color;}
void setTextColorUnread (QColor color) { mTextColorUnread = color;}
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
void setTextColorMissing (QColor color) { mTextColorMissing = color;}
#endif
void setMsgReadStatus(const QModelIndex &i, bool read_status, bool with_children);
void setFilter(const QStringList &strings, uint32_t &count) ;
#ifdef TODO
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
#endif
// Helper functions
bool getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const ;
void clear() ;
// AbstractItemModel functions.
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// Custom item roles
QVariant sizeHintRole (int col) const;
QVariant displayRole (const RsGxsChannelPost& fmpe, int col) const;
QVariant toolTipRole (const RsGxsChannelPost& fmpe, int col) const;
QVariant userRole (const RsGxsChannelPost& fmpe, int col) const;
#ifdef TODO
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
#endif
/*!
* \brief debug_dump
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
*/
void debug_dump();
signals:
void channelPostsLoaded(); // emitted after the posts have been loaded.
private:
RsGxsChannelGroup mChannelGroup;
#ifdef TODO
bool mUseChildTS;
bool mFilteringEnabled;
SortMode mSortMode;
#endif
TreeMode mTreeMode;
uint32_t mColumns;
void preMods() ;
void postMods() ;
quintptr getParentRow(quintptr ref,int& row) const;
quintptr getChildRef(quintptr ref, int index) const;
int getChildrenCount(quintptr ref) const;
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr &ref);
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry);
void update_posts(const RsGxsGroupId& group_id);
#ifdef TODO
void setForumMessageSummary(const std::vector<RsGxsForumMsg>& messages);
#endif
void recursUpdateReadStatusAndTimes(ChannelPostsModelIndex i,bool& has_unread_below,bool& has_read_below);
uint32_t recursUpdateFilterStatus(ChannelPostsModelIndex i,int column,const QStringList& strings);
void recursSetMsgReadStatus(ChannelPostsModelIndex i,bool read_status,bool with_children);
#ifdef TODO
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
#endif
//static ChannelModelIndex addEntry(std::vector<RsGxsChannelPost>& posts,const ChannelModelPostEntry& entry,ChannelModelIndex parent);
//static void convertMsgToPostEntry(const RsGxsChannelGroup &mChannelGroup, const RsMsgMetaData &msg, bool useChildTS, ChannelModelPostEntry& fentry);
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
void createPostsArray(std::vector<RsGxsChannelPost> &posts);
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
void initEmptyHierarchy();
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
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::map<RsGxsMessageId,std::vector<std::pair<time_t,RsGxsMessageId> > > mPostVersions; // stores versions of posts
QColor mTextColorRead ;
QColor mTextColorUnread ;
QColor mTextColorUnreadChildren;
QColor mTextColorNotSubscribed ;
QColor mTextColorMissing ;
RsEventsHandlerId_t mEventHandlerId ;
friend class const_iterator;
};

View File

@ -551,13 +551,9 @@ void GxsChannelPostsWidget::createPostItem(const RsGxsChannelPost& post, bool re
ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs)); ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs));
} }
#ifdef TODO
ui->fileWidget->addFiles(post, related); ui->fileWidget->addFiles(post, related);
#endif
} }
void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool related, int current, int count) void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool related, int current, int count)
{ {
/* show fill progress */ /* show fill progress */

View File

@ -98,7 +98,7 @@ private:
int viewMode(); int viewMode();
void insertChannelDetails(const RsGxsChannelGroup &group); void insertChannelDetails(const RsGxsChannelGroup &group);
void insertChannelPosts(std::vector<RsGxsChannelPost> &posts, GxsMessageFramePostThread *thread, bool related); void insertChannelPosts(std::vector<RsGxsChannelPost>& posts, GxsMessageFramePostThread *thread, bool related);
void createPostItem(const RsGxsChannelPost &post, bool related); void createPostItem(const RsGxsChannelPost &post, bool related);
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event); void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,184 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h *
* *
* Copyright 2013 by Robert Fernie <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#ifndef _GXS_CHANNELPOSTSWIDGET_H
#define _GXS_CHANNELPOSTSWIDGET_H
#include <map>
#include <QStyledItemDelegate>
#include "gui/gxs/GxsMessageFramePostWidget.h"
#include "gui/feeds/FeedHolder.h"
namespace Ui {
class GxsChannelPostsWidgetWithModel;
}
class GxsChannelPostItem;
class QTreeWidgetItem;
class QSortFilterProxyModel;
class FeedItem;
class RsGxsChannelPostsModel;
class RsGxsChannelPostFilesModel;
class RsGxsChannelPostFilesProxyModel;
class ChannelPostFilesDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
ChannelPostFilesDelegate(QObject *parent=0) : QStyledItemDelegate(parent){}
virtual ~ChannelPostFilesDelegate(){}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
class ChannelPostDelegate: public QAbstractItemDelegate
{
Q_OBJECT
public:
ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent), mZoom(1.0){}
virtual ~ChannelPostDelegate(){}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
int cellSize(const QFont& font) const;
void zoom(bool zoom_or_unzoom) ;
private:
static constexpr float IMAGE_MARGIN_FACTOR = 1.0;
static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ;
static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ;
static constexpr float IMAGE_ZOOM_FACTOR = 1.0;
float mZoom; // zoom factor for the whole thumbnail
};
class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget
{
Q_OBJECT
public:
/* Filters */
enum Filter {
FILTER_TITLE = 1,
FILTER_MSG = 2,
FILTER_FILE_NAME = 3
};
public:
/** Default Constructor */
GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent = 0);
/** Default Destructor */
~GxsChannelPostsWidgetWithModel();
/* GxsMessageFrameWidget */
virtual QIcon groupIcon();
virtual void groupIdChanged() { updateDisplay(true); }
virtual QString groupName(bool) override;
virtual bool navigate(const RsGxsMessageId&) override;
void updateDisplay(bool complete);
#ifdef TODO
/* FeedHolder */
virtual QScrollArea *getScrollArea();
virtual void deleteFeedItem(FeedItem *feedItem, uint32_t type);
virtual void openChat(const RsPeerId& peerId);
#endif
virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector<RsGxsMessageId> &msg_versions, const RsGxsMessageId &msgId, const QString &title);
protected:
/* GxsMessageFramePostWidget */
virtual void groupNameChanged(const QString &name);
#ifdef TODO
virtual bool insertGroupData(const RsGxsGenericGroupData *data) override;
#endif
virtual bool useThread() { return mUseThread; }
virtual void blank() ;
#ifdef TODO
virtual bool getGroupData(RsGxsGenericGroupData *& data) override;
virtual void getMsgData(const std::set<RsGxsMessageId>& msgIds,std::vector<RsGxsGenericMsgData*>& posts) override;
virtual void getAllMsgData(std::vector<RsGxsGenericMsgData*>& posts) override;
virtual void insertPosts(const std::vector<RsGxsGenericMsgData*>& posts) override;
virtual void insertAllPosts(const std::vector<RsGxsGenericMsgData*>& posts, GxsMessageFramePostThread *thread) override;
#endif
/* GxsMessageFrameWidget */
virtual void setAllMessagesReadDo(bool read, uint32_t &token);
private slots:
void showPostDetails();
void updateGroupData();
void createMsg();
void toggleAutoDownload();
void subscribeGroup(bool subscribe);
void filterChanged(QString);
void setViewMode(int viewMode);
void settingsChanged();
void handlePostsTreeSizeChange(QSize s);
void postChannelPostLoad();
void editPost();
void postContextMenu(const QPoint&);
void copyMessageLink();
void updateZoomFactor(bool zoom_or_unzoom);
public slots:
void sortColumnFiles(int col,Qt::SortOrder so);
void sortColumnPostFiles(int col,Qt::SortOrder so);
private:
void processSettings(bool load);
void setAutoDownload(bool autoDl);
static bool filterItem(FeedItem *feedItem, const QString &text, int filter);
int viewMode();
void insertChannelDetails(const RsGxsChannelGroup &group);
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
private:
QAction *mAutoDownloadAction;
RsGxsChannelGroup mGroup;
bool mUseThread;
RsEventsHandlerId_t mEventHandlerId ;
RsGxsChannelPostsModel *mChannelPostsModel;
RsGxsChannelPostFilesModel *mChannelPostFilesModel;
RsGxsChannelPostFilesModel *mChannelFilesModel;
ChannelPostDelegate *mChannelPostsDelegate;
RsGxsMessageId mSelectedPost;
/* UI - from Designer */
Ui::GxsChannelPostsWidgetWithModel *ui;
};
#endif

View File

@ -0,0 +1,601 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GxsChannelPostsWidgetWithModel</class>
<widget class="QWidget" name="GxsChannelPostsWidgetWithModel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>977</width>
<height>628</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>4</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>
<widget class="QFrame" name="toolBarFrame">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="SubscribeToolButton" name="subscribeToolButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Subscribe</string>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="postButton">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="toolTip">
<string>Post to Channel</string>
</property>
<property name="text">
<string>Add new post</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add.png</normaloff>:/icons/png/add.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="viewModeLayout">
<property name="spacing">
<number>0</number>
</property>
</layout>
</item>
<item>
<widget class="LineEditClear" name="filterLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Search channels</string>
</property>
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTabWidget" name="channel_TW">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Channel details</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="StyledElidedLabel" name="channelName_LB">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
<strikeout>false</strikeout>
</font>
</property>
<property name="text">
<string>Channel title</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="logoLabel">
<property name="text">
<string>TextLabel</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<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">
<widget class="GxsIdLabel" name="infoAdministrator">
<property name="text">
<string>unknown</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</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="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">
<widget class="QLabel" name="infoLastPostLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Last Post:</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="infoDistribution">
<property name="text">
<string>unknown</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Administrator:</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="infoCreated">
<property name="text">
<string>unknown</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLabel" name="infoPostsLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Posts:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="infoDescription">
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;Description&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="openLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Posts</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="RSTreeView" name="postsTree">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideNone</enum>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
<widget class="QTabWidget" name="details_TW">
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Details</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="postLogo_LB">
<property name="text">
<string>TextLabel</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</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>
<widget class="QLabel" name="postTime_LB">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="postDetails_TE">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="channelPostFiles_tab">
<attribute name="title">
<string>Files</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="RSTreeView" name="channelPostFiles_TV">
<property name="editTriggers">
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="indentation">
<number>5</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Comments</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<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>
<widget class="GxsCommentDialog" name="commentsDialog" native="true"/>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_5">
<attribute name="title">
<string>Files</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="RSTreeView" name="channelFiles_TV">
<property name="editTriggers">
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked</set>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
<action name="actionFeeds">
<property name="text">
<string>Feeds</string>
</property>
</action>
<action name="actionFiles">
<property name="text">
<string>Files</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>GxsIdLabel</class>
<extends>QLabel</extends>
<header>gui/gxs/GxsIdLabel.h</header>
</customwidget>
<customwidget>
<class>SubscribeToolButton</class>
<extends>QToolButton</extends>
<header>gui/common/SubscribeToolButton.h</header>
</customwidget>
<customwidget>
<class>StyledElidedLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledElidedLabel.h</header>
</customwidget>
<customwidget>
<class>RSTreeView</class>
<extends>QTreeView</extends>
<header>gui/common/RSTreeView.h</header>
</customwidget>
<customwidget>
<class>GxsCommentDialog</class>
<extends>QWidget</extends>
<header>gui/gxs/GxsCommentDialog.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LineEditClear</class>
<extends>QLineEdit</extends>
<header>gui/common/LineEditClear.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -123,9 +123,9 @@ public:
{ {
default: default:
case 3: case 3:
case 0: icon = QIcon(IMAGE_VOID); break; case 0: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_VOID); break;
case 1: icon = QIcon(IMAGE_WARNING_YELLOW); break; case 1: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_WARNING_YELLOW); break;
case 2: icon = QIcon(IMAGE_WARNING_RED); break; case 2: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_WARNING_RED); break;
} }
QPixmap pix = icon.pixmap(r.size()); QPixmap pix = icon.pixmap(r.size());
@ -172,9 +172,9 @@ public:
else else
{ {
if (unread) if (unread)
icon = QIcon(":/images/message-state-unread.png"); icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png");
else else
icon = QIcon(":/images/message-state-read.png"); icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png");
} }
QPixmap pix = icon.pixmap(r.size()); QPixmap pix = icon.pixmap(r.size());
@ -462,7 +462,7 @@ QString GxsForumThreadWidget::groupName(bool withUnreadCount)
QIcon GxsForumThreadWidget::groupIcon() QIcon GxsForumThreadWidget::groupIcon()
{ {
if (mNewCount) { if (mNewCount) {
return QIcon(":/images/message-state-new.png"); return FilesDefs::getIconFromQtResourcePath(":/images/message-state-new.png");
} }
return QIcon(); return QIcon();
@ -498,6 +498,7 @@ void GxsForumThreadWidget::recursRestoreExpandedItems(const QModelIndex& /*index
ui->threadTreeWidget->setExpanded( mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(*it)) ,true) ; ui->threadTreeWidget->setExpanded( mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(*it)) ,true) ;
} }
void GxsForumThreadWidget::updateDisplay(bool complete) void GxsForumThreadWidget::updateDisplay(bool complete)
{ {
#ifdef DEBUG_FORUMS #ifdef DEBUG_FORUMS
@ -560,35 +561,35 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
#ifdef DEBUG_FORUMS #ifdef DEBUG_FORUMS
std::cerr << "Clicked on msg " << current_post.mMsgId << std::endl; std::cerr << "Clicked on msg " << current_post.mMsgId << std::endl;
#endif #endif
QAction *editAct = new QAction(QIcon(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu); QAction *editAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu);
connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage())); connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage()));
bool is_pinned = mForumGroup.mPinnedPosts.ids.find(mThreadId) != mForumGroup.mPinnedPosts.ids.end(); bool is_pinned = mForumGroup.mPinnedPosts.ids.find(mThreadId) != mForumGroup.mPinnedPosts.ids.end();
QAction *pinUpPostAct = new QAction(QIcon(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu); QAction *pinUpPostAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu);
connect(pinUpPostAct , SIGNAL(triggered()), this, SLOT(togglePinUpPost())); connect(pinUpPostAct , SIGNAL(triggered()), this, SLOT(togglePinUpPost()));
QAction *replyAct = new QAction(QIcon(IMAGE_REPLY), tr("Reply"), &contextMnu); QAction *replyAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REPLY), tr("Reply"), &contextMnu);
connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage())); connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage()));
QAction *replyauthorAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu); QAction *replyauthorAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu);
connect(replyauthorAct, SIGNAL(triggered()), this, SLOT(reply_with_private_message())); connect(replyauthorAct, SIGNAL(triggered()), this, SLOT(reply_with_private_message()));
QAction *flagaspositiveAct = new QAction(QIcon(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu); QAction *flagaspositiveAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu);
flagaspositiveAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; flagaspositiveAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ;
flagaspositiveAct->setData(static_cast<uint32_t>(RsOpinion::POSITIVE)); flagaspositiveAct->setData(static_cast<uint32_t>(RsOpinion::POSITIVE));
connect(flagaspositiveAct, SIGNAL(triggered()), this, SLOT(flagperson())); connect(flagaspositiveAct, SIGNAL(triggered()), this, SLOT(flagperson()));
QAction *flagasneutralAct = new QAction(QIcon(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu); QAction *flagasneutralAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu);
flagasneutralAct->setToolTip(tr("Doing this, you trust your friends to decide to forward this message or not.")) ; flagasneutralAct->setToolTip(tr("Doing this, you trust your friends to decide to forward this message or not.")) ;
flagasneutralAct->setData(static_cast<uint32_t>(RsOpinion::NEUTRAL)); flagasneutralAct->setData(static_cast<uint32_t>(RsOpinion::NEUTRAL));
connect(flagasneutralAct, SIGNAL(triggered()), this, SLOT(flagperson())); connect(flagasneutralAct, SIGNAL(triggered()), this, SLOT(flagperson()));
QAction *flagasnegativeAct = new QAction(QIcon(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu); QAction *flagasnegativeAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu);
flagasnegativeAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; flagasnegativeAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ;
flagasnegativeAct->setData(static_cast<uint32_t>(RsOpinion::NEGATIVE)); flagasnegativeAct->setData(static_cast<uint32_t>(RsOpinion::NEGATIVE));
connect(flagasnegativeAct, SIGNAL(triggered()), this, SLOT(flagperson())); connect(flagasnegativeAct, SIGNAL(triggered()), this, SLOT(flagperson()));
QAction *newthreadAct = new QAction(QIcon(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu); QAction *newthreadAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu);
newthreadAct->setEnabled (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)); newthreadAct->setEnabled (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags));
connect(newthreadAct , SIGNAL(triggered()), this, SLOT(createthread())); connect(newthreadAct , SIGNAL(triggered()), this, SLOT(createthread()));
@ -598,19 +599,19 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
QAction* collapseAll = new QAction(tr( "Collapse all"), &contextMnu); QAction* collapseAll = new QAction(tr( "Collapse all"), &contextMnu);
connect(collapseAll, SIGNAL(triggered()), ui->threadTreeWidget, SLOT(collapseAll())); connect(collapseAll, SIGNAL(triggered()), ui->threadTreeWidget, SLOT(collapseAll()));
QAction *markMsgAsRead = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu); QAction *markMsgAsRead = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu);
connect(markMsgAsRead, SIGNAL(triggered()), this, SLOT(markMsgAsRead())); connect(markMsgAsRead, SIGNAL(triggered()), this, SLOT(markMsgAsRead()));
QAction *markMsgAsReadChildren = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu); QAction *markMsgAsReadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu);
connect(markMsgAsReadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsReadChildren())); connect(markMsgAsReadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsReadChildren()));
QAction *markMsgAsUnread = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu); QAction *markMsgAsUnread = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu);
connect(markMsgAsUnread, SIGNAL(triggered()), this, SLOT(markMsgAsUnread())); connect(markMsgAsUnread, SIGNAL(triggered()), this, SLOT(markMsgAsUnread()));
QAction *markMsgAsUnreadChildren = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu); QAction *markMsgAsUnreadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu);
connect(markMsgAsUnreadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsUnreadChildren())); connect(markMsgAsUnreadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsUnreadChildren()));
QAction *showinpeopleAct = new QAction(QIcon(":/images/info16.png"), tr("Show author in people tab"), &contextMnu); QAction *showinpeopleAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("Show author in people tab"), &contextMnu);
connect(showinpeopleAct, SIGNAL(triggered()), this, SLOT(showInPeopleTab())); connect(showinpeopleAct, SIGNAL(triggered()), this, SLOT(showInPeopleTab()));
if (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) if (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags))
@ -663,7 +664,7 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
contextMnu.addAction(replyAct); contextMnu.addAction(replyAct);
contextMnu.addAction(newthreadAct); contextMnu.addAction(newthreadAct);
QAction* action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); QAction* action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink()));
action->setEnabled(!groupId().isNull() && !mThreadId.isNull()); action->setEnabled(!groupId().isNull() && !mThreadId.isNull());
contextMnu.addSeparator(); contextMnu.addSeparator();
contextMnu.addAction(markMsgAsRead); contextMnu.addAction(markMsgAsRead);
@ -755,7 +756,7 @@ void GxsForumThreadWidget::togglethreadview_internal()
{ {
// if (ui->expandButton->isChecked()) { // if (ui->expandButton->isChecked()) {
ui->postText->setVisible(true); ui->postText->setVisible(true);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/edit_remove24.png")));
ui->expandButton->setToolTip(tr("Hide")); ui->expandButton->setToolTip(tr("Hide"));
// } else { // } else {
// ui->postText->setVisible(false); // ui->postText->setVisible(false);

View File

@ -60,6 +60,7 @@
<file>icons/png/anonymous.png</file> <file>icons/png/anonymous.png</file>
<file>icons/png/attach-image.png</file> <file>icons/png/attach-image.png</file>
<file>icons/png/attach.png</file> <file>icons/png/attach.png</file>
<file>icons/png/arrow.png</file>
<file>icons/png/cert.png</file> <file>icons/png/cert.png</file>
<file>icons/png/channels-notify.png</file> <file>icons/png/channels-notify.png</file>
<file>icons/png/channels.png</file> <file>icons/png/channels.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

View File

@ -745,12 +745,7 @@ GxsForumMsgItem QFrame#frame{
background-color: white; background-color: white;
} }
GxsChannelPostsWidget QFrame#infoFrame GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton {
{
}
GxsChannelPostsWidget QToolButton#subscribeToolButton {
font: bold; font: bold;
font-size: 14px; font-size: 14px;
color: white; color: white;
@ -759,18 +754,18 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton {
max-height: 27px; max-height: 27px;
} }
GxsChannelPostsWidget QToolButton#subscribeToolButton:hover { GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:hover {
background: #03b1f3; background: #03b1f3;
border-radius: 4px; border-radius: 4px;
} }
GxsChannelPostsWidget QToolButton#subscribeToolButton:pressed { GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:pressed {
background: #03b1f3; background: #03b1f3;
border-radius: 4px; border-radius: 4px;
border: 1px solid gray; border: 1px solid gray;
} }
GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled { GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:disabled {
background: gray; background: gray;
border-radius: 4px; border-radius: 4px;
border: 1px solid gray; border: 1px solid gray;
@ -778,19 +773,35 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled {
} }
/* only for MenuButtonPopup */ /* only for MenuButtonPopup */
GxsChannelPostsWidget QToolButton#subscribeToolButton[popupMode="1"] { GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton[popupMode="1"] {
padding-right: 0px; padding-right: 0px;
} }
GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-arrow { GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-arrow {
image: none; image: none;
} }
GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-button { GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-button {
image: none; image: none;
} }
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator {
image: none;
}
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] {
padding-right: 0px;
}
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator {
image: none;
}
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] {
padding-right: 0px;
}
GxsGroupDialog QLabel#groupLogo{ GxsGroupDialog QLabel#groupLogo{
border: 2px solid #CCCCCC; border: 2px solid #CCCCCC;
border-radius: 3px; border-radius: 3px;

View File

@ -84,7 +84,7 @@ ConfCertDialog QLabel#servicePermissionsLabel
qproperty-fontSizeFactor: 125; qproperty-fontSizeFactor: 125;
} }
GxsChannelPostsWidget QLabel#nameLabel GxsChannelPostsWidgetWithModel QLabel#channelName_LB
{ {
qproperty-fontSizeFactor: 250; qproperty-fontSizeFactor: 250;
} }

View File

@ -1337,13 +1337,19 @@ gxschannels {
gui/gxschannels/GxsChannelGroupDialog.h \ gui/gxschannels/GxsChannelGroupDialog.h \
gui/gxschannels/CreateGxsChannelMsg.h \ gui/gxschannels/CreateGxsChannelMsg.h \
gui/gxschannels/GxsChannelPostsWidget.h \ gui/gxschannels/GxsChannelPostsWidget.h \
gui/gxschannels/GxsChannelPostsWidgetWithModel.h \
gui/gxschannels/GxsChannelPostsModel.h \
gui/gxschannels/GxsChannelPostFilesModel.h \
gui/gxschannels/GxsChannelFilesWidget.h \ gui/gxschannels/GxsChannelFilesWidget.h \
gui/gxschannels/GxsChannelPostThumbnail.h \
gui/gxschannels/GxsChannelFilesStatusWidget.h \ gui/gxschannels/GxsChannelFilesStatusWidget.h \
gui/feeds/GxsChannelGroupItem.h \ gui/feeds/GxsChannelGroupItem.h \
gui/feeds/GxsChannelPostItem.h \ gui/feeds/GxsChannelPostItem.h \
gui/gxschannels/GxsChannelUserNotify.h gui/gxschannels/GxsChannelUserNotify.h
FORMS += gui/gxschannels/GxsChannelPostsWidget.ui \ FORMS += \
gui/gxschannels/GxsChannelPostsWidgetWithModel.ui \
gui/gxschannels/GxsChannelPostsWidget.ui \
gui/gxschannels/GxsChannelFilesWidget.ui \ gui/gxschannels/GxsChannelFilesWidget.ui \
gui/gxschannels/GxsChannelFilesStatusWidget.ui \ gui/gxschannels/GxsChannelFilesStatusWidget.ui \
gui/gxschannels/CreateGxsChannelMsg.ui \ gui/gxschannels/CreateGxsChannelMsg.ui \
@ -1352,6 +1358,9 @@ gxschannels {
SOURCES += gui/gxschannels/GxsChannelDialog.cpp \ SOURCES += gui/gxschannels/GxsChannelDialog.cpp \
gui/gxschannels/GxsChannelPostsWidget.cpp \ gui/gxschannels/GxsChannelPostsWidget.cpp \
gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \
gui/gxschannels/GxsChannelPostsModel.cpp \
gui/gxschannels/GxsChannelPostFilesModel.cpp \
gui/gxschannels/GxsChannelFilesWidget.cpp \ gui/gxschannels/GxsChannelFilesWidget.cpp \
gui/gxschannels/GxsChannelFilesStatusWidget.cpp \ gui/gxschannels/GxsChannelFilesStatusWidget.cpp \
gui/gxschannels/GxsChannelGroupDialog.cpp \ gui/gxschannels/GxsChannelGroupDialog.cpp \