Added basic widget RSFeedWidget for showing feed items in a QTreeWidget as replacement for the QScrollArea.

- Use sort of QTreeWidget
- Filter items
- Open/collapse selected item with +/-
- Remove selecteds item with dselete key

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@7478 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
thunder2 2014-08-01 14:49:58 +00:00
parent e588b25b67
commit d13aa90b2b
20 changed files with 841 additions and 183 deletions

View File

@ -32,14 +32,14 @@
/** Constructor */
PostedItem::PostedItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome) :
GxsFeedItem(parent, feedId, groupId, messageId, isHome, rsPosted, true, false)
PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome) :
GxsFeedItem(feedHolder, feedId, groupId, messageId, isHome, rsPosted, true, false)
{
setup();
}
PostedItem::PostedItem(FeedHolder *parent, uint32_t feedId, const RsPostedPost &post, bool isHome) :
GxsFeedItem(parent, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsPosted, false, false),
PostedItem::PostedItem(FeedHolder *feedHolder, uint32_t feedId, const RsPostedPost &post, bool isHome) :
GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsPosted, false, false),
mPost(post)
{
setup();
@ -115,7 +115,7 @@ void PostedItem::setContent(const RsPostedPost &post)
// FIX THIS UP LATER.
notes->setText(QString::fromUtf8(post.mNotes.c_str()));
// differences between Feed or Top of Comment.
if (mParent)
if (mFeedHolder)
{
// feed.
frame_notes->hide();
@ -218,9 +218,10 @@ void PostedItem::loadComments()
{
std::cerr << "PostedItem::loadComments()";
std::cerr << std::endl;
if (mParent)
if (mFeedHolder)
{
QString title = QString::fromUtf8(mPost.mMeta.mMsgName.c_str());
mParent->openComments(0, mPost.mMeta.mGroupId, mPost.mMeta.mMsgId, title);
mFeedHolder->openComments(0, mPost.mMeta.mGroupId, mPost.mMeta.mMsgId, title);
}
}

View File

@ -46,6 +46,9 @@ public:
void setContent(const RsPostedPost& post);
virtual void setContent(const QVariant &content);
/* FeedItem */
virtual void expand(bool /*open*/) {}
private slots:
void loadComments();
void makeUpVote();

View File

@ -423,12 +423,8 @@ void PostedListWidget::applyRanking()
QLayout *alayout = ui->scrollAreaWidgetContents->layout();
int counter = 0;
time_t min_ts = 0;
foreach (GxsFeedItem *feedItem, mPostItems)
foreach (PostedItem *item, mPostItems)
{
PostedItem *item = dynamic_cast<PostedItem*>(feedItem);
if (!item) {
continue;
}
std::cerr << "PostedListWidget::applyRanking() Item: " << item;
std::cerr << std::endl;
@ -472,8 +468,11 @@ void PostedListWidget::applyRanking()
void PostedListWidget::clearPosts()
{
GxsMessageFramePostWidget::clearPosts();
/* clear all messages */
foreach (PostedItem *item, mPostItems) {
delete(item);
}
mPostItems.clear();
mPosts.clear();
}
@ -585,12 +584,18 @@ void PostedListWidget::insertRelatedPosts(const uint32_t &token)
applyRanking();
}
void PostedListWidget::setMessageRead(GxsFeedItem *item, bool read)
void PostedListWidget::setAllMessagesRead(bool read)
{
if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(subscribeFlags())) {
return;
}
foreach (PostedItem *item, mPostItems) {
RsGxsGrpMsgIdPair msgPair = std::make_pair(item->groupId(), item->messageId());
uint32_t token;
rsPosted->setMessageReadStatus(token, msgPair, read);
}
}
/*********************** **** **** **** ***********************/

View File

@ -47,6 +47,7 @@ public:
/* GxsMessageFrameWidget */
virtual QIcon groupIcon();
virtual void setAllMessagesRead(bool read);
/* FeedHolder */
virtual QScrollArea *getScrollArea();
@ -62,7 +63,6 @@ protected:
virtual bool insertGroupData(const uint32_t &token, RsGroupMetaData &metaData);
virtual void insertPosts(const uint32_t &token);
virtual void insertRelatedPosts(const uint32_t &token);
virtual void setMessageRead(GxsFeedItem *item, bool read);
virtual void clearPosts();
private slots:
@ -112,6 +112,7 @@ private:
uint32_t mTokenTypeVote;
QMap<RsGxsMessageId, PostedItem*> mPosts;
QList<PostedItem*> mPostItems;
/* UI - from Designer */
Ui::PostedListWidget *ui;

View File

@ -182,7 +182,7 @@ void LineEditClear::setCurrentFilter(int id)
if (action->data().toInt() == id) {
action->setChecked(true);
activateAction(action);
// emit filterChanged(id);
emit filterChanged(id);
break;
}
}

View File

@ -0,0 +1,406 @@
/****************************************************************
* This file is distributed under the following license:
*
* Copyright (c) 2014, RetroShare Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
#include <QKeyEvent>
#include "RSFeedWidget.h"
#include "ui_RSFeedWidget.h"
#include "RSTreeWidgetItem.h"
#include "gui/feeds/FeedItem.h"
#include "gui/gxs/GxsFeedItem.h"
#define COLUMN_FEED 0
RSFeedWidget::RSFeedWidget(QWidget *parent)
: QWidget(parent), ui(new Ui::RSFeedWidget)
{
/* Invoke the Qt Designer generated object setup routine */
ui->setupUi(this);
/* Sort */
mFeedCompareRole = new RSTreeWidgetItemCompareRole;
/* Filter */
mFilterCallback = NULL;
mFilterType = 0;
/* Remove */
mEnableRemove = false;
ui->treeWidget->installEventFilter(this);
}
RSFeedWidget::~RSFeedWidget()
{
delete(mFeedCompareRole);
delete(ui);
}
bool RSFeedWidget::eventFilter(QObject *object, QEvent *event)
{
if (object == ui->treeWidget) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent) {
if (keyEvent->key() == Qt::Key_Plus || keyEvent->key() == Qt::Key_Minus) {
bool open = (keyEvent->key() == Qt::Key_Plus);
QList<FeedItem*> feedItems;
selectedFeedItems(feedItems);
foreach (FeedItem *feedItem, feedItems) {
feedItem->expand(open);
}
return true; // eat event
}
if (mEnableRemove && keyEvent->key() == Qt::Key_Delete) {
QList<QTreeWidgetItem*> selectedItems = ui->treeWidget->selectedItems();
foreach (QTreeWidgetItem *treeItem, selectedItems) {
FeedItem *feedItem = feedItemFromTreeItem(treeItem);
if (feedItem) {
disconnectSignals(feedItem);
delete(feedItem);
}
delete(treeItem);
}
return true; // eat event
}
}
}
}
/* Pass the event on to the parent class */
return QWidget::eventFilter(object, event);
}
void RSFeedWidget::connectSignals(FeedItem *feedItem)
{
connect(feedItem, SIGNAL(feedItemDestroyed(FeedItem*)), this, SLOT(feedItemDestroyed(FeedItem*)));
connect(feedItem, SIGNAL(sizeChanged(FeedItem*)), this, SLOT(feedItemSizeChanged(FeedItem*)));
}
void RSFeedWidget::disconnectSignals(FeedItem *feedItem)
{
disconnect(feedItem, SIGNAL(feedItemDestroyed(FeedItem*)), this, SLOT(feedItemDestroyed(FeedItem*)));
disconnect(feedItem, SIGNAL(sizeChanged(FeedItem*)), this, SLOT(feedItemSizeChanged(FeedItem*)));
}
FeedItem *RSFeedWidget::feedItemFromTreeItem(QTreeWidgetItem *treeItem)
{
return dynamic_cast<FeedItem*>(ui->treeWidget->itemWidget(treeItem, COLUMN_FEED));
}
void RSFeedWidget::addFeedItem(FeedItem *feedItem, Qt::ItemDataRole sortRole, const QVariant &value)
{
if (!feedItem) {
return;
}
QTreeWidgetItem *treeItem = new RSTreeWidgetItem(mFeedCompareRole);
treeItem->setData(COLUMN_FEED, sortRole, value);
ui->treeWidget->addTopLevelItem(treeItem);
ui->treeWidget->setItemWidget(treeItem, 0, feedItem);
connectSignals(feedItem);
filterItem(treeItem, feedItem);
}
void RSFeedWidget::addFeedItem(FeedItem *feedItem, const QMap<Qt::ItemDataRole, QVariant> &sort)
{
if (!feedItem) {
return;
}
QTreeWidgetItem *treeItem = new RSTreeWidgetItem(mFeedCompareRole);
QMap<Qt::ItemDataRole, QVariant>::const_iterator it;
for (it = sort.begin(); it != sort.end(); ++it) {
treeItem->setData(COLUMN_FEED, it.key(), it.value());
}
ui->treeWidget->addTopLevelItem(treeItem);
ui->treeWidget->setItemWidget(treeItem, 0, feedItem);
connectSignals(feedItem);
filterItem(treeItem, feedItem);
}
void RSFeedWidget::setSort(FeedItem *feedItem, Qt::ItemDataRole sortRole, const QVariant &value)
{
if (!feedItem) {
return;
}
QTreeWidgetItem *treeItem = findTreeWidgetItem(feedItem);
if (!treeItem) {
return;
}
treeItem->setData(COLUMN_FEED, sortRole, value);
}
void RSFeedWidget::setSort(FeedItem *feedItem, const QMap<Qt::ItemDataRole, QVariant> &sort)
{
if (!feedItem) {
return;
}
QTreeWidgetItem *treeItem = findTreeWidgetItem(feedItem);
if (!treeItem) {
return;
}
QMap<Qt::ItemDataRole, QVariant>::const_iterator it;
for (it = sort.begin(); it != sort.end(); ++it) {
treeItem->setData(COLUMN_FEED, it.key(), it.value());
}
}
void RSFeedWidget::clear()
{
ui->treeWidget->clear();
}
void RSFeedWidget::setSortRole(Qt::ItemDataRole role, Qt::SortOrder order)
{
setSortingEnabled(true);
mFeedCompareRole->setRole(COLUMN_FEED, role);
ui->treeWidget->sortItems(COLUMN_FEED, order);
}
void RSFeedWidget::setSortingEnabled(bool enable)
{
ui->treeWidget->setSortingEnabled(enable);
}
void RSFeedWidget::setFilterCallback(RSFeedWidgetFilterCallbackFunction callback)
{
mFilterCallback = callback;
filterItems();
}
void RSFeedWidget::setFilter(const QString &text, int type)
{
if (mFilterText == text && mFilterType == type) {
return;
}
mFilterText = text;
mFilterType = type;
filterItems();
}
void RSFeedWidget::setFilterText(const QString &text)
{
setFilter(text, mFilterType);
}
void RSFeedWidget::setFilterType(int type)
{
setFilter(mFilterText, type);
}
void RSFeedWidget::filterItems()
{
if (!mFilterCallback) {
return;
}
QTreeWidgetItemIterator it(ui->treeWidget);
QTreeWidgetItem *item;
while ((item = *it) != NULL) {
++it;
FeedItem *feedItem = feedItemFromTreeItem(item);
if (!feedItem) {
continue;
}
filterItem(item, feedItem);
}
}
void RSFeedWidget::filterItem(QTreeWidgetItem *treeItem, FeedItem *feedItem)
{
if (!mFilterCallback) {
return;
}
treeItem->setHidden(!mFilterCallback(feedItem, mFilterText, mFilterType));
}
void RSFeedWidget::enableRemove(bool enable)
{
mEnableRemove = enable;
}
void RSFeedWidget::setSelectionMode(QAbstractItemView::SelectionMode mode)
{
ui->treeWidget->setSelectionMode(mode);
}
void RSFeedWidget::removeFeedItem(FeedItem *feedItem)
{
if (!feedItem) {
return;
}
disconnectSignals(feedItem);
QTreeWidgetItem *treeItem = findTreeWidgetItem(feedItem);
if (treeItem) {
delete(treeItem);
}
}
void RSFeedWidget::feedItemSizeChanged(FeedItem */*feedItem*/)
{
if (updatesEnabled()) {
setUpdatesEnabled(false);
QApplication::processEvents();
setUpdatesEnabled(true);
} else {
QApplication::processEvents();
}
ui->treeWidget->doItemsLayout();
}
void RSFeedWidget::feedItemDestroyed(FeedItem *feedItem)
{
/* No need to disconnect when object will be destroyed */
QTreeWidgetItem *treeItem = findTreeWidgetItem(feedItem);
if (treeItem) {
delete(treeItem);
}
}
QTreeWidgetItem *RSFeedWidget::findTreeWidgetItem(FeedItem *feedItem)
{
QTreeWidgetItemIterator it(ui->treeWidget);
QTreeWidgetItem *treeItem;
while ((treeItem = *it) != NULL) {
++it;
if (feedItemFromTreeItem(treeItem) == feedItem) {
return treeItem;
}
}
return NULL;
}
class RSFeedWidgetCallback
{
public:
RSFeedWidgetCallback() {}
virtual void callback(FeedItem *feedItem, const QVariant &data) = 0;
};
void RSFeedWidget::withAll(RSFeedWidgetCallbackFunction callback, const QVariant &data)
{
if (!callback) {
return;
}
QTreeWidgetItemIterator it(ui->treeWidget);
QTreeWidgetItem *treeItem;
while ((treeItem = *it) != NULL) {
++it;
FeedItem *feedItem = feedItemFromTreeItem(treeItem);
if (!feedItem) {
continue;
}
callback(feedItem, data);
}
}
FeedItem *RSFeedWidget::findFeedItem(RSFeedWidgetFindCallbackFunction callback, const QVariant &data1, const QVariant &data2)
{
if (!callback) {
return NULL;
}
QTreeWidgetItemIterator it(ui->treeWidget);
QTreeWidgetItem *treeItem;
while ((treeItem = *it) != NULL) {
++it;
FeedItem *feedItem = feedItemFromTreeItem(treeItem);
if (!feedItem) {
continue;
}
if (callback(feedItem, data1, data2)) {
return feedItem;
}
}
return NULL;
}
void RSFeedWidget::selectedFeedItems(QList<FeedItem*> &feedItems)
{
foreach (QTreeWidgetItem *treeItem, ui->treeWidget->selectedItems()) {
FeedItem *feedItem = feedItemFromTreeItem(treeItem);
if (!feedItem) {
continue;
}
feedItems.push_back(feedItem);
}
}
static bool findGxsFeedItemCallback(FeedItem *feedItem, const QVariant &data1, const QVariant &data2)
{
if (!data1.canConvert<RsGxsGroupId>() || !data2.canConvert<RsGxsMessageId>()) {
return false;
}
GxsFeedItem *item = dynamic_cast<GxsFeedItem*>(feedItem);
if (!item) {
return false;
}
if (item->groupId() != data1.value<RsGxsGroupId>() ||
item->messageId() != data2.value<RsGxsMessageId>()) {
return false;
}
return true;
}
GxsFeedItem *RSFeedWidget::findGxsFeedItem(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId)
{
FeedItem *feedItem = findFeedItem(findGxsFeedItemCallback, qVariantFromValue(groupId), qVariantFromValue(messageId));
return dynamic_cast<GxsFeedItem*>(feedItem);
}

View File

@ -0,0 +1,113 @@
/****************************************************************
* This file is distributed under the following license:
*
* Copyright (c) 2014, RetroShare Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
#ifndef _RSFEEDTREEWIDGET_H
#define _RSFEEDTREEWIDGET_H
#include <QAbstractItemView>
#include <QWidget>
#include <QMap>
#include "retroshare/rsgxsifacetypes.h"
#define FEED_TREEWIDGET_SORTROLE Qt::UserRole
class FeedItem;
class QTreeWidgetItem;
class RSTreeWidgetItemCompareRole;
class GxsFeedItem;
namespace Ui {
class RSFeedWidget;
}
typedef void (*RSFeedWidgetCallbackFunction)(FeedItem *feedItem, const QVariant &data);
typedef bool (*RSFeedWidgetFindCallbackFunction)(FeedItem *feedItem, const QVariant &data1, const QVariant &data2);
typedef bool (*RSFeedWidgetFilterCallbackFunction)(FeedItem *feedItem, const QString &text, int filter);
class RSFeedWidget : public QWidget
{
Q_OBJECT
public:
RSFeedWidget(QWidget *parent = 0);
virtual ~RSFeedWidget();
void addFeedItem(FeedItem *feedItem, Qt::ItemDataRole sortRole, const QVariant &value);
void addFeedItem(FeedItem *feedItem, const QMap<Qt::ItemDataRole, QVariant> &sort);
void setSort(FeedItem *feedItem, Qt::ItemDataRole sortRole, const QVariant &value);
void setSort(FeedItem *feedItem, const QMap<Qt::ItemDataRole, QVariant> &sort);
void removeFeedItem(FeedItem *feedItem);
void clear();
void setSortRole(Qt::ItemDataRole role, Qt::SortOrder order);
void setSortingEnabled(bool enable);
void setFilterCallback(RSFeedWidgetFilterCallbackFunction callback);
void enableRemove(bool enable);
void setSelectionMode(QAbstractItemView::SelectionMode mode);
void withAll(RSFeedWidgetCallbackFunction callback, const QVariant &data);
FeedItem *findFeedItem(RSFeedWidgetFindCallbackFunction callback, const QVariant &data1, const QVariant &data2);
void selectedFeedItems(QList<FeedItem*> &feedItems);
/* Convenience functions */
GxsFeedItem *findGxsFeedItem(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId);
public slots:
void setFilter(const QString &text, int type);
void setFilterText(const QString &text);
void setFilterType(int type);
protected:
bool eventFilter(QObject *object, QEvent *event);
private slots:
void feedItemDestroyed(FeedItem *feedItem);
void feedItemSizeChanged(FeedItem *feedItem);
private:
void connectSignals(FeedItem *feedItem);
void disconnectSignals(FeedItem *feedItem);
FeedItem *feedItemFromTreeItem(QTreeWidgetItem *treeItem);
QTreeWidgetItem *findTreeWidgetItem(FeedItem *feedItem);
void filterItems();
void filterItem(QTreeWidgetItem *treeItem, FeedItem *feedItem);
private:
/* Sort */
RSTreeWidgetItemCompareRole *mFeedCompareRole;
/* Filter */
RSFeedWidgetFilterCallbackFunction mFilterCallback;
QString mFilterText;
int mFilterType;
/* Remove */
bool mEnableRemove;
Ui::RSFeedWidget *ui;
};
#endif

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RSFeedWidget</class>
<widget class="QWidget" name="RSFeedWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>337</width>
<height>229</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</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="QTreeWidget" name="treeWidget">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,32 @@
/****************************************************************
* RetroShare is distributed under the following license:
*
* Copyright (C) 2014 RetroShare Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
#include "FeedItem.h"
/** Constructor */
FeedItem::FeedItem(QWidget *parent) : QWidget(parent)
{
}
FeedItem::~FeedItem()
{
emit feedItemDestroyed(this);
}

View File

@ -0,0 +1,44 @@
/****************************************************************
* RetroShare is distributed under the following license:
*
* Copyright (C) 2014 RetroShare Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
#ifndef _FEED_ITEM_H
#define _FEED_ITEM_H
#include <QWidget>
class FeedItem : public QWidget
{
Q_OBJECT
public:
/** Default Constructor */
FeedItem(QWidget *parent = 0);
/** Default Destructor */
virtual ~FeedItem();
virtual void expand(bool open) = 0;
signals:
void sizeChanged(FeedItem *feedItem);
void feedItemDestroyed(FeedItem *feedItem);
};
#endif

View File

@ -48,8 +48,8 @@
#define SELF_LOAD 1
#define DATA_PROVIDED 2
GxsChannelPostItem::GxsChannelPostItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate) :
GxsFeedItem(parent, feedId, groupId, messageId, isHome, rsGxsChannels, true, autoUpdate)
GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate) :
GxsFeedItem(feedHolder, feedId, groupId, messageId, isHome, rsGxsChannels, true, autoUpdate)
{
mMode = SELF_LOAD;
@ -57,8 +57,8 @@ GxsChannelPostItem::GxsChannelPostItem(FeedHolder *parent, uint32_t feedId, cons
}
/** Constructor */
GxsChannelPostItem::GxsChannelPostItem(FeedHolder *parent, uint32_t feedId, const RsGxsChannelPost &post, uint32_t subFlags, bool isHome, bool autoUpdate) :
GxsFeedItem(parent, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsGxsChannels, false, autoUpdate)
GxsChannelPostItem::GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelPost &post, uint32_t subFlags, bool isHome, bool autoUpdate) :
GxsFeedItem(feedHolder, feedId, post.mMeta.mGroupId, post.mMeta.mMsgId, isHome, rsGxsChannels, false, autoUpdate)
{
std::cerr << "GxsChannelPostItem::GxsChannelPostItem() Direct Load";
std::cerr << std::endl;
@ -249,7 +249,7 @@ void GxsChannelPostItem::loadPost(const RsGxsChannelPost &post)
}
// differences between Feed or Top of Comment.
if (mParent)
if (mFeedHolder)
{
ui->commentButton->show();
@ -348,7 +348,8 @@ void GxsChannelPostItem::setReadStatus(bool isNew, bool isUnread)
ui->frame->style()->unpolish(ui->frame);
QPalette palette = ui->frame->palette();
palette.setColor(ui->frame->backgroundRole(), isNew ? COLOR_NEW : COLOR_NORMAL);
palette.setColor(ui->frame->backgroundRole(), isNew ? COLOR_NEW : COLOR_NORMAL); // QScrollArea
palette.setColor(QPalette::Base, isNew ? COLOR_NEW : COLOR_NORMAL); // QTreeWidget
ui->frame->setPalette(palette);
ui->frame->setProperty("new", isNew);
@ -447,14 +448,14 @@ void GxsChannelPostItem::updateItem()
//downloadButton->setEnabled(true);
}
void GxsChannelPostItem::toggle()
void GxsChannelPostItem::expand(bool open)
{
if (mParent)
if (mFeedHolder)
{
mParent->lockLayout(this, true);
mFeedHolder->lockLayout(this, true);
}
if (ui->expandFrame->isHidden())
if (open)
{
ui->expandFrame->show();
ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png")));
@ -469,12 +470,19 @@ void GxsChannelPostItem::toggle()
ui->expandButton->setToolTip(tr("Expand"));
}
if (mParent)
emit sizeChanged(this);
if (mFeedHolder)
{
mParent->lockLayout(this, false);
mFeedHolder->lockLayout(this, false);
}
}
void GxsChannelPostItem::toggle()
{
expand(ui->expandFrame->isHidden());
}
/*********** SPECIFIC FUNCTIONS ***********************/
void GxsChannelPostItem::readAndClearItem()

View File

@ -44,8 +44,8 @@ class GxsChannelPostItem : public GxsFeedItem
public:
/** Default Constructor */
GxsChannelPostItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate);
GxsChannelPostItem(FeedHolder *parent, uint32_t feedId, const RsGxsChannelPost &post, uint32_t subscribeFlags, bool isHome, bool autoUpdate);
GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, bool autoUpdate);
GxsChannelPostItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsChannelPost &post, uint32_t subscribeFlags, bool isHome, bool autoUpdate);
virtual ~GxsChannelPostItem();
virtual void setContent(const QVariant &content);
@ -57,6 +57,9 @@ public:
const QString getMsgLabel() {return RsHtml().formatText(NULL, QString::fromUtf8(mPost.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS); }
const std::list<SubFileItem *> &getFileItems() {return mFileItems; }
/* FeedItem */
virtual void expand(bool open);
protected:
virtual void loadMessage(const uint32_t &token);
virtual RetroShareLink::enumType getLinkType() { return RetroShareLink::TYPE_CHANNEL; }

View File

@ -42,17 +42,17 @@ void GxsFeedItem::removeItem()
std::cerr << std::endl;
#endif
if (mParent)
if (mFeedHolder)
{
mParent->lockLayout(this, true);
mFeedHolder->lockLayout(this, true);
}
hide();
if (mParent)
if (mFeedHolder)
{
mParent->lockLayout(this, false);
mParent->deleteFeedItem(this, mFeedId);
mFeedHolder->lockLayout(this, false);
mFeedHolder->deleteFeedItem(this, mFeedId);
}
}
@ -63,9 +63,9 @@ void GxsFeedItem::comments(const QString &title)
std::cerr << std::endl;
#endif
if (mParent)
if (mFeedHolder)
{
mParent->openComments(mFeedId, mGroupId, mMessageId, title);
mFeedHolder->openComments(mFeedId, mGroupId, mMessageId, title);
}
}
@ -131,14 +131,14 @@ void GxsFeedItem::updateItem()
/***********************************************************/
GxsFeedItem::GxsFeedItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, RsGxsIfaceHelper *iface, bool loadData, bool autoUpdate) :
QWidget(NULL)
GxsFeedItem::GxsFeedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, RsGxsIfaceHelper *iface, bool loadData, bool autoUpdate) :
FeedItem(NULL)
{
std::cerr << "GxsFeedItem::GxsFeedItem()";
std::cerr << std::endl;
/* this are just generally useful for all children */
mParent = parent;
mFeedHolder = feedHolder;
mFeedId = feedId;
mIsHome = isHome;

View File

@ -24,7 +24,10 @@
#ifndef _GXS_GENERIC_FEED_ITEM_H
#define _GXS_GENERIC_FEED_ITEM_H
#include <QMetaType>
#include <retroshare/rsgxsifacehelper.h>
#include "gui/feeds/FeedItem.h"
#include "util/TokenQueue.h"
#include "gui/RetroShareLink.h"
@ -33,13 +36,13 @@
class FeedHolder;
class RsGxsUpdateBroadcastBase;
class GxsFeedItem : public QWidget, public TokenResponse
class GxsFeedItem : public FeedItem, public TokenResponse
{
Q_OBJECT
public:
/** Note parent can = NULL */
GxsFeedItem(FeedHolder *parent, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, RsGxsIfaceHelper *iface, bool loadData, bool autoUpdate);
GxsFeedItem(FeedHolder *feedHolder, uint32_t feedId, const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, bool isHome, RsGxsIfaceHelper *iface, bool loadData, bool autoUpdate);
virtual ~GxsFeedItem();
RsGxsGroupId groupId() { return mGroupId; }
@ -75,7 +78,7 @@ private slots:
void fillDisplay(bool complete);
protected:
FeedHolder *mParent;
FeedHolder *mFeedHolder;
uint32_t mFeedId;
bool mIsHome;
@ -92,4 +95,7 @@ private:
RsGxsUpdateBroadcastBase *mUpdateBroadcastBase;
};
Q_DECLARE_METATYPE(RsGxsGroupId)
Q_DECLARE_METATYPE(RsGxsMessageId)
#endif

View File

@ -116,26 +116,6 @@ void GxsMessageFramePostWidget::updateDisplay(bool complete)
}
}
void GxsMessageFramePostWidget::setAllMessagesRead(bool read)
{
if (mGroupId.isNull() || !IS_GROUP_SUBSCRIBED(mSubscribeFlags)) {
return;
}
foreach (GxsFeedItem *item, mPostItems) {
setMessageRead(item, read);
}
}
void GxsMessageFramePostWidget::clearPosts()
{
/* clear all messages */
foreach (GxsFeedItem *item, mPostItems) {
delete(item);
}
mPostItems.clear();
}
/**************************************************************/
/** Request / Response of Data ********************************/
/**************************************************************/

View File

@ -40,7 +40,6 @@ public:
virtual void setGroupId(const RsGxsGroupId &groupId);
virtual QString groupName(bool withUnreadCount);
// virtual QIcon groupIcon() = 0;
virtual void setAllMessagesRead(bool read);
/* GXS functions */
uint32_t nextTokenType() { return ++mNextTokenType; }
@ -52,8 +51,7 @@ protected:
virtual void updateDisplay(bool complete);
virtual void groupNameChanged(const QString &/*name*/) {}
virtual void setMessageRead(GxsFeedItem *item, bool read) = 0;
virtual void clearPosts();
virtual void clearPosts() = 0;
/* GXS functions */
void requestGroupData();
@ -74,7 +72,6 @@ protected:
uint32_t mTokenTypePosts;
uint32_t mTokenTypeRelatedPosts;
UIStateHelper *mStateHelper;
QList<GxsFeedItem*> mPostItems;
private:
RsGxsGroupId mGroupId; /* current group */

View File

@ -19,6 +19,8 @@
* Boston, MA 02110-1301, USA.
****************************************************************/
#include <QDateTime>
#include "GxsChannelPostsWidget.h"
#include "ui_GxsChannelPostsWidget.h"
#include "gui/feeds/GxsChannelPostItem.h"
@ -31,6 +33,8 @@
#define CHAN_DEFAULT_IMAGE ":/images/channels.png"
#define ROLE_PUBLISH FEED_TREEWIDGET_SORTROLE
/****
* #define DEBUG_CHANNEL
***/
@ -49,6 +53,8 @@ GxsChannelPostsWidget::GxsChannelPostsWidget(const RsGxsGroupId &channelId, QWid
/* Invoke the Qt Designer generated object setup routine */
ui->setupUi(this);
mInProcessSettings = false;
/* Setup UI helper */
// No progress yet
@ -69,8 +75,7 @@ GxsChannelPostsWidget::GxsChannelPostsWidget(const RsGxsGroupId &channelId, QWid
ui->filterLineEdit->addFilter(QIcon(), tr("Title"), FILTER_TITLE, tr("Search Title"));
ui->filterLineEdit->addFilter(QIcon(), tr("Message"), FILTER_MSG, tr("Search Message"));
ui->filterLineEdit->addFilter(QIcon(), tr("Filename"), FILTER_FILE_NAME, tr("Search Filename"));
ui->filterLineEdit->setCurrentFilter( Settings->valueFromGroup("ChannelFeed", "filter", FILTER_TITLE).toInt());
connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString)));
connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->feedWidget, SLOT(setFilterText(QString)));
connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged(int)));
/*************** Setup Left Hand Side (List of Channels) ****************/
@ -81,7 +86,9 @@ GxsChannelPostsWidget::GxsChannelPostsWidget(const RsGxsGroupId &channelId, QWid
ui->nameLabel->setMinimumWidth(20);
mInProcessSettings = false;
/* Initialize feed widget */
ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder);
ui->feedWidget->setFilterCallback(filterItem);
/* load settings */
processSettings(true);
@ -111,18 +118,19 @@ GxsChannelPostsWidget::~GxsChannelPostsWidget()
delete ui;
}
void GxsChannelPostsWidget::processSettings(bool /*load*/)
void GxsChannelPostsWidget::processSettings(bool load)
{
mInProcessSettings = true;
// Settings->beginGroup(QString("ChannelPostsWidget"));
//
// if (load) {
// // load settings
// } else {
// // save settings
// }
//
// Settings->endGroup();
Settings->beginGroup(QString("ChannelPostsWidget"));
if (load) {
// load settings
ui->filterLineEdit->setCurrentFilter(Settings->value("filter", FILTER_TITLE).toInt());
} else {
// save settings
}
Settings->endGroup();
mInProcessSettings = false;
}
@ -155,7 +163,7 @@ QIcon GxsChannelPostsWidget::groupIcon()
QScrollArea *GxsChannelPostsWidget::getScrollArea()
{
return ui->scrollArea;
return NULL;
}
void GxsChannelPostsWidget::deleteFeedItem(QWidget * /*item*/, uint32_t /*type*/)
@ -216,116 +224,85 @@ void GxsChannelPostsWidget::insertChannelDetails(const RsGxsChannelGroup &group)
void GxsChannelPostsWidget::filterChanged(int filter)
{
ui->feedWidget->setFilterType(filter);
if (mInProcessSettings) {
return;
}
filterItems(ui->filterLineEdit->text());
// save index
Settings->setValueToGroup("ChannelFeed", "filter", filter);
Settings->setValueToGroup("ChannelPostsWidget", "filter", filter);
}
void GxsChannelPostsWidget::filterItems(const QString& text)
/*static*/ bool GxsChannelPostsWidget::filterItem(FeedItem *feedItem, const QString &text, int filter)
{
int filter = ui->filterLineEdit->currentFilter();
/* Search exisiting item */
QList<GxsFeedItem*>::iterator lit;
for (lit = mPostItems.begin(); lit != mPostItems.end(); lit++)
{
GxsChannelPostItem *item = dynamic_cast<GxsChannelPostItem*>(*lit);
GxsChannelPostItem *item = dynamic_cast<GxsChannelPostItem*>(feedItem);
if (!item) {
continue;
return true;
}
filterItem(item,text,filter);
}
}
bool GxsChannelPostsWidget::filterItem(GxsChannelPostItem *pItem, const QString &text, const int filter)
{
bool bVisible = text.isEmpty();
switch(filter)
{
case FILTER_TITLE:
bVisible=pItem->getTitleLabel().contains(text,Qt::CaseInsensitive);
bVisible = item->getTitleLabel().contains(text,Qt::CaseInsensitive);
break;
case FILTER_MSG:
bVisible=pItem->getMsgLabel().contains(text,Qt::CaseInsensitive);
bVisible = item->getMsgLabel().contains(text,Qt::CaseInsensitive);
break;
case FILTER_FILE_NAME:
{
std::list<SubFileItem *> fileItems=pItem->getFileItems();
std::list<SubFileItem *> fileItems = item->getFileItems();
std::list<SubFileItem *>::iterator lit;
for(lit = fileItems.begin(); lit != fileItems.end(); lit++)
for(lit = fileItems.begin(); lit != fileItems.end(); ++lit)
{
SubFileItem *fi = *lit;
QString fileName=QString::fromUtf8(fi->FileName().c_str());
bVisible=(bVisible || fileName.contains(text,Qt::CaseInsensitive));
}
QString fileName = QString::fromUtf8(fi->FileName().c_str());
bVisible = (bVisible || fileName.contains(text,Qt::CaseInsensitive));
}
break;
}
default:
bVisible=true;
bVisible = true;
break;
}
pItem->setVisible(bVisible);
return (bVisible);
}
static bool sortChannelMsgSummaryAsc(const RsGxsChannelPost &msg1, const RsGxsChannelPost &msg2)
{
return (msg1.mMeta.mPublishTs > msg2.mMeta.mPublishTs);
}
static bool sortChannelMsgSummaryDesc(const RsGxsChannelPost &msg1, const RsGxsChannelPost &msg2)
{
return (msg1.mMeta.mPublishTs < msg2.mMeta.mPublishTs);
return bVisible;
}
void GxsChannelPostsWidget::insertChannelPosts(std::vector<RsGxsChannelPost> &posts, bool related)
{
std::vector<RsGxsChannelPost>::const_iterator it;
// Do these need sorting? probably.
// can we add that into the request?
if (related) {
/* Sort descending to add posts at top */
std::sort(posts.begin(), posts.end(), sortChannelMsgSummaryDesc);
} else {
std::sort(posts.begin(), posts.end(), sortChannelMsgSummaryAsc);
}
uint32_t subscribeFlags = 0xffffffff;
ui->feedWidget->setSortingEnabled(false);
for (it = posts.begin(); it != posts.end(); it++)
{
const RsGxsChannelPost &msg = *it;
GxsChannelPostItem *item = NULL;
if (related) {
foreach (GxsFeedItem *loopItem, mPostItems) {
if (loopItem->messageId() == it->mMeta.mMsgId) {
item = dynamic_cast<GxsChannelPostItem*>(loopItem);
break;
}
}
FeedItem *feedItem = ui->feedWidget->findGxsFeedItem(msg.mMeta.mGroupId, msg.mMeta.mMsgId);
item = dynamic_cast<GxsChannelPostItem*>(feedItem);
}
if (item) {
item->setContent(*it);
//TODO: Sort timestamp
} else {
item = new GxsChannelPostItem(this, 0, *it, subscribeFlags, true, false);
if (!ui->filterLineEdit->text().isEmpty())
filterItem(item, ui->filterLineEdit->text(), ui->filterLineEdit->currentFilter());
ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(msg.mMeta.mPublishTs));
}
}
mPostItems.push_back(item);
if (related) {
ui->verticalLayout->insertWidget(0, item);
} else {
ui->verticalLayout->addWidget(item);
}
}
}
ui->feedWidget->setSortingEnabled(true);
}
void GxsChannelPostsWidget::clearPosts()
{
ui->feedWidget->clear();
}
void GxsChannelPostsWidget::subscribeGroup(bool subscribe)
@ -391,10 +368,24 @@ void GxsChannelPostsWidget::insertRelatedPosts(const uint32_t &token)
insertChannelPosts(posts, true);
}
void GxsChannelPostsWidget::setMessageRead(GxsFeedItem *item, bool read)
static void setAllMessagesReadCallback(FeedItem *feedItem, const QVariant &data)
{
RsGxsGrpMsgIdPair msgPair = std::make_pair(item->groupId(), item->messageId());
GxsChannelPostItem *channelPostItem = dynamic_cast<GxsChannelPostItem*>(feedItem);
if (!channelPostItem) {
return;
}
RsGxsGrpMsgIdPair msgPair = std::make_pair(channelPostItem->groupId(), channelPostItem->messageId());
uint32_t token;
rsGxsChannels->setMessageReadStatus(token, msgPair, read);
rsGxsChannels->setMessageReadStatus(token, msgPair, data.toBool());
}
void GxsChannelPostsWidget::setAllMessagesRead(bool read)
{
if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(subscribeFlags())) {
return;
}
ui->feedWidget->withAll(setAllMessagesReadCallback, read);
}

View File

@ -32,9 +32,9 @@ namespace Ui {
class GxsChannelPostsWidget;
}
//class ChanMsgItem;
class GxsChannelPostItem;
class QTreeWidgetItem;
class FeedItem;
class GxsChannelPostsWidget : public GxsMessageFramePostWidget, public FeedHolder
{
@ -48,6 +48,7 @@ public:
/* GxsMessageFrameWidget */
virtual QIcon groupIcon();
virtual void setAllMessagesRead(bool read);
/* FeedHolder */
virtual QScrollArea *getScrollArea();
@ -61,14 +62,13 @@ protected:
virtual bool insertGroupData(const uint32_t &token, RsGroupMetaData &metaData);
virtual void insertPosts(const uint32_t &token);
virtual void insertRelatedPosts(const uint32_t &token);
virtual void setMessageRead(GxsFeedItem *item, bool read);
virtual void clearPosts();
private slots:
void createMsg();
void toggleAutoDownload();
void subscribeGroup(bool subscribe);
void filterChanged(int filter);
void filterItems(const QString& text);
//void fillThreadFinished();
//void fillThreadAddMsg(const QString &channelId, const QString &channelMsgId, int current, int count);
@ -77,7 +77,7 @@ private:
void processSettings(bool load);
void setAutoDownload(bool autoDl);
bool filterItem(GxsChannelPostItem *pItem, const QString &text, const int filter);
static bool filterItem(FeedItem *feedItem, const QString &text, int filter);
void insertChannelDetails(const RsGxsChannelGroup &group);
void insertChannelPosts(std::vector<RsGxsChannelPost> &posts, bool related);

View File

@ -14,7 +14,16 @@
<property name="spacing">
<number>4</number>
</property>
<property name="margin">
<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>
@ -26,7 +35,16 @@
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="margin">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
@ -212,40 +230,19 @@
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>679</width>
<height>16</height>
</rect>
</property>
<widget class="RSFeedWidget" name="feedWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
</item>
</layout>
<zorder>toolBarFrame</zorder>
<zorder>scrollArea</zorder>
<zorder>headFrame</zorder>
<zorder>feedWidget</zorder>
</widget>
<customwidgets>
<customwidget>
@ -258,6 +255,12 @@
<extends>QToolButton</extends>
<header>gui/common/SubscribeToolButton.h</header>
</customwidget>
<customwidget>
<class>RSFeedWidget</class>
<extends>QWidget</extends>
<header>gui/common/RSFeedWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../images.qrc"/>

View File

@ -450,6 +450,7 @@ HEADERS += rshare.h \
gui/common/RSPlainTextEdit.h \
gui/common/RSTreeWidget.h \
gui/common/RSTreeWidgetItem.h \
gui/common/RSFeedWidget.h \
gui/common/RSTabWidget.h \
gui/common/RSItemDelegate.h \
gui/common/PeerDefs.h \
@ -495,6 +496,7 @@ HEADERS += rshare.h \
gui/elastic/arrow.h \
gui/elastic/node.h \
gui/NewsFeed.h \
gui/feeds/FeedItem.h \
gui/feeds/FeedHolder.h \
gui/feeds/PeerItem.h \
gui/feeds/MsgItem.h \
@ -615,6 +617,7 @@ FORMS += gui/StartDialog.ui \
gui/common/RSImageBlockWidget.ui \
gui/common/RsCollectionDialog.ui \
gui/common/HeaderFrame.ui \
gui/common/RSFeedWidget.ui \
gui/style/StyleDialog.ui \
gui/dht/DhtWindow.ui \
gui/bwctrl/BwCtrlWindow.ui \
@ -740,6 +743,7 @@ SOURCES += main.cpp \
gui/common/RSPlainTextEdit.cpp \
gui/common/RSTreeWidget.cpp \
gui/common/RSTreeWidgetItem.cpp \
gui/common/RSFeedWidget.cpp \
gui/common/RSTabWidget.cpp \
gui/common/RSItemDelegate.cpp \
gui/common/PeerDefs.cpp \
@ -816,6 +820,7 @@ SOURCES += main.cpp \
gui/elastic/arrow.cpp \
gui/elastic/node.cpp \
gui/NewsFeed.cpp \
gui/feeds/FeedItem.cpp \
gui/feeds/FeedHolder.cpp \
gui/feeds/PeerItem.cpp \
gui/feeds/MsgItem.cpp \