RetroShare/retroshare-gui/src/gui/ChannelFeed.cpp

508 lines
15 KiB
C++
Raw Normal View History

/****************************************************************
* RetroShare is distributed under the following license:
*
* Copyright (C) 2008 Robert Fernie
*
* 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 <QMenu>
#include <QTimer>
#include <QStandardItemModel>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include "ChannelFeed.h"
#include "feeds/ChanMsgItem.h"
#include "common/PopularityDefs.h"
#include "channels/CreateChannel.h"
#include "channels/ChannelDetails.h"
#include "channels/CreateChannelMsg.h"
#include "channels/EditChanDetails.h"
#include "channels/ShareKey.h"
#include "notifyqt.h"
#define CHAN_DEFAULT_IMAGE ":/images/channels.png"
#define WARNING_LIMIT 3600*24*2
/****
* #define CHAN_DEBUG
***/
/** Constructor */
ChannelFeed::ChannelFeed(QWidget *parent)
: RsAutoUpdatePage(1000,parent)
{
/* Invoke the Qt Designer generated object setup routine */
setupUi(this);
connect(actionCreate_Channel, SIGNAL(triggered()), this, SLOT(createChannel()));
connect(postButton, SIGNAL(clicked()), this, SLOT(createMsg()));
connect(subscribeButton, SIGNAL( clicked( void ) ), this, SLOT( subscribeChannel ( void ) ) );
connect(unsubscribeButton, SIGNAL( clicked( void ) ), this, SLOT( unsubscribeChannel ( void ) ) );
connect(setAllAsReadButton, SIGNAL(clicked()), this, SLOT(setAllAsReadClicked()));
connect(NotifyQt::getInstance(), SIGNAL(channelMsgReadSatusChanged(QString,QString,int)), this, SLOT(channelMsgReadSatusChanged(QString,QString,int)));
/*************** Setup Left Hand Side (List of Channels) ****************/
connect(treeWidget, SIGNAL(treeCustomContextMenuRequested(QPoint)), this, SLOT( channelListCustomPopupMenu( QPoint ) ) );
connect(treeWidget, SIGNAL(treeCurrentItemChanged(QString)), this, SLOT(selectChannel(QString)));
mChannelId.clear();
ownChannels = treeWidget->addCategoryItem(tr("Own Channels"), QIcon(), true);
subcribedChannels = treeWidget->addCategoryItem(tr("Subscribed Channels"), QIcon(), true);
popularChannels = treeWidget->addCategoryItem(tr("Popular Channels"), QIcon(), false);
otherChannels = treeWidget->addCategoryItem(tr("Other Channels"), QIcon(), false);
//added from ahead
updateChannelList();
nameLabel->setFont(QFont("MS SANS SERIF", 22));
nameLabel->setMinimumWidth(20);
// Setup Channel Menu:
QMenu *channelmenu = new QMenu();
channelmenu->addAction(actionCreate_Channel);
channelmenu->addSeparator();
channelpushButton->setMenu(channelmenu);
updateChannelMsgs();
}
void ChannelFeed::channelListCustomPopupMenu( QPoint point )
{
ChannelInfo ci;
if (!rsChannels->getChannelInfo(mChannelId, ci)) {
return;
}
QMenu contextMnu(this);
QAction *postchannelAct = new QAction(QIcon(":/images/mail_reply.png"), tr( "Post to Channel" ), &contextMnu);
connect( postchannelAct , SIGNAL( triggered() ), this, SLOT( createMsg() ) );
QAction *subscribechannelAct = new QAction(QIcon(":/images/edit_add24.png"), tr( "Subscribe to Channel" ), &contextMnu);
connect( subscribechannelAct , SIGNAL( triggered() ), this, SLOT( subscribeChannel() ) );
QAction *unsubscribechannelAct = new QAction(QIcon(":/images/cancel.png"), tr( "Unsubscribe to Channel" ), &contextMnu);
connect( unsubscribechannelAct , SIGNAL( triggered() ), this, SLOT( unsubscribeChannel() ) );
QAction *channeldetailsAct = new QAction(QIcon(":/images/info16.png"), tr( "Show Channel Details" ), &contextMnu);
connect( channeldetailsAct , SIGNAL( triggered() ), this, SLOT( showChannelDetails() ) );
//QAction *restoreKeysAct = new QAction(QIcon(":/images/settings16.png"), tr("Restore Publish Rights for Channel" ), &contextMnu);
//connect( restoreKeysAct , SIGNAL( triggered() ), this, SLOT( restoreChannelKeys() ) );
QAction *editChannelDetailAct = new QAction(QIcon(":/images/edit_16.png"), tr("Edit Channel Details"), &contextMnu);
connect( editChannelDetailAct, SIGNAL( triggered() ), this, SLOT( editChannelDetail() ) );
QAction *shareKeyAct = new QAction(QIcon(":/images/gpgp_key_generate.png"), tr("Share Channel"), &contextMnu);
connect( shareKeyAct, SIGNAL( triggered() ), this, SLOT( shareKey() ) );
if ((ci.channelFlags & RS_DISTRIB_PUBLISH) && (ci.channelFlags & RS_DISTRIB_ADMIN)) {
contextMnu.addAction( postchannelAct );
contextMnu.addSeparator();
contextMnu.addAction( editChannelDetailAct);
contextMnu.addAction( shareKeyAct );
contextMnu.addAction( channeldetailsAct );
}
else if ((ci.channelFlags & RS_DISTRIB_PUBLISH) && (ci.channelFlags & RS_DISTRIB_SUBSCRIBED)) {
contextMnu.addAction( postchannelAct );
contextMnu.addSeparator();
contextMnu.addAction( channeldetailsAct );
contextMnu.addAction( shareKeyAct );
} else if (ci.channelFlags & RS_DISTRIB_SUBSCRIBED) {
contextMnu.addAction( unsubscribechannelAct );
contextMnu.addSeparator();
contextMnu.addAction( channeldetailsAct );
// contextMnu.addAction( restoreKeysAct );
} else {
contextMnu.addAction( subscribechannelAct );
contextMnu.addSeparator();
contextMnu.addAction( channeldetailsAct );
// contextMnu.addAction( restoreKeysAct );
}
contextMnu.exec(QCursor::pos());
}
void ChannelFeed::createChannel()
{
CreateChannel cf (this);
cf.exec();
}
/*************************************************************************************/
/*************************************************************************************/
/*************************************************************************************/
void ChannelFeed::deleteFeedItem(QWidget *item, uint32_t type)
{
}
void ChannelFeed::openChat(std::string peerId)
{
}
void ChannelFeed::editChannelDetail(){
EditChanDetails editUi(this, 0, mChannelId);
editUi.exec();
}
void ChannelFeed::shareKey()
{
ShareKey shareUi(this, 0, mChannelId);
shareUi.exec();
}
void ChannelFeed::createMsg()
{
if (mChannelId.empty()) {
return;
}
CreateChannelMsg *msgDialog = new CreateChannelMsg(mChannelId);
msgDialog->show();
/* window will destroy itself! */
}
void ChannelFeed::restoreChannelKeys()
{
rsChannels->channelRestoreKeys(mChannelId);
}
void ChannelFeed::selectChannel(const QString &id)
{
mChannelId = id.toStdString();
updateChannelMsgs();
}
void ChannelFeed::updateDisplay()
{
if (!rsChannels) {
return;
}
std::list<std::string> chanIds;
std::list<std::string>::iterator it;
if (rsChannels->channelsChanged(chanIds)) {
/* update channel list */
updateChannelList();
it = std::find(chanIds.begin(), chanIds.end(), mChannelId);
if (it != chanIds.end()) {
updateChannelMsgs();
}
}
}
static void channelInfoToGroupItemInfo(const ChannelInfo &channelInfo, GroupItemInfo &groupItemInfo)
{
groupItemInfo.id = QString::fromStdString(channelInfo.channelId);
groupItemInfo.name = QString::fromStdWString(channelInfo.channelName);
groupItemInfo.description = QString::fromStdWString(channelInfo.channelDesc);
groupItemInfo.popularity = channelInfo.pop;
groupItemInfo.lastpost = QDateTime::fromTime_t(channelInfo.lastPost);
QPixmap chanImage;
if (channelInfo.pngImageLen) {
chanImage.loadFromData(channelInfo.pngChanImage, channelInfo.pngImageLen, "PNG");
} else {
chanImage = QPixmap(CHAN_DEFAULT_IMAGE);
}
groupItemInfo.icon = QIcon(chanImage);
}
void ChannelFeed::updateChannelList()
{
if (!rsChannels) {
return;
}
std::list<ChannelInfo> channelList;
std::list<ChannelInfo>::iterator it;
rsChannels->getChannelList(channelList);
std::list<std::string> keysAvailable;
std::list<std::string>::iterator keyIt;
rsChannels->getPubKeysAvailableGrpIds(keysAvailable);
/* get the ids for our lists */
QList<GroupItemInfo> adminList;
QList<GroupItemInfo> subList;
QList<GroupItemInfo> popList;
QList<GroupItemInfo> otherList;
std::multimap<uint32_t, GroupItemInfo> popMap;
for(it = channelList.begin(); it != channelList.end(); it++) {
/* sort it into Publish (Own), Subscribed, Popular and Other */
uint32_t flags = it->channelFlags;
GroupItemInfo groupItemInfo;
channelInfoToGroupItemInfo(*it, groupItemInfo);
if ((flags & RS_DISTRIB_ADMIN) && (flags & RS_DISTRIB_PUBLISH) && (flags & RS_DISTRIB_SUBSCRIBED)) {
adminList.push_back(groupItemInfo);
} else {
for (keyIt = keysAvailable.begin(); keyIt != keysAvailable.end(); keyIt++) {
if (it->channelId == *keyIt) {
/* Found Key, set title text to bold and colored blue */
groupItemInfo.privatekey = true;
break;
}
}
if ((flags & RS_DISTRIB_SUBSCRIBED) || ((flags & RS_DISTRIB_SUBSCRIBED) && (flags & RS_DISTRIB_PUBLISH)) ) {
subList.push_back(groupItemInfo);
} else {
/* rate the others by popularity */
popMap.insert(std::make_pair(it->pop, groupItemInfo));
}
}
}
/* iterate backwards through popMap - take the top 5 or 10% of list */
uint32_t popCount = 5;
if (popCount < popMap.size() / 10) {
popCount = popMap.size() / 10;
}
uint32_t i = 0;
std::multimap<uint32_t, GroupItemInfo>::reverse_iterator rit;
for (rit = popMap.rbegin(); rit != popMap.rend(); rit++) {
if (i < popCount) {
popList.push_back(rit->second);
i++;
} else {
otherList.push_back(rit->second);
}
}
/* now we have our lists ---> update entries */
treeWidget->fillGroupItems(ownChannels, adminList);
treeWidget->fillGroupItems(subcribedChannels, subList);
treeWidget->fillGroupItems(popularChannels, popList);
treeWidget->fillGroupItems(otherChannels, otherList);
updateMessageSummaryList("");
}
void ChannelFeed::channelMsgReadSatusChanged(const QString& channelId, const QString& msgId, int status)
{
updateMessageSummaryList(channelId.toStdString());
}
void ChannelFeed::updateMessageSummaryList(const std::string &channelId)
{
QTreeWidgetItem *items[2] = { ownChannels, subcribedChannels };
for (int item = 0; item < 2; item++) {
int child;
int childCount = items[item]->childCount();
for (child = 0; child < childCount; child++) {
QTreeWidgetItem *childItem = items[item]->child(child);
std::string childId = treeWidget->itemId(childItem).toStdString();
if (childId.empty()) {
continue;
}
if (channelId.empty() || childId == channelId) {
/* Calculate unread messages */
unsigned int newMessageCount = 0;
unsigned int unreadMessageCount = 0;
rsChannels->getMessageCount(childId, newMessageCount, unreadMessageCount);
treeWidget->setUnreadCount(childItem, unreadMessageCount);
if (channelId.empty() == false) {
/* Calculate only this channel */
break;
}
}
}
}
}
static bool sortChannelMsgSummary(const ChannelMsgSummary &msg1, const ChannelMsgSummary &msg2)
{
return (msg1.ts > msg2.ts);
}
void ChannelFeed::updateChannelMsgs()
{
if (!rsChannels) {
return;
}
/* replace all the messages with new ones */
std::list<ChanMsgItem *>::iterator mit;
for (mit = mChanMsgItems.begin(); mit != mChanMsgItems.end(); mit++) {
delete (*mit);
}
mChanMsgItems.clear();
ChannelInfo ci;
if (!rsChannels->getChannelInfo(mChannelId, ci)) {
postButton->setEnabled(false);
subscribeButton->setEnabled(false);
unsubscribeButton->setEnabled(false);
setAllAsReadButton->setEnabled(false);
nameLabel->setText(tr("No Channel Selected"));
iconLabel->setPixmap(QPixmap(":/images/channels.png"));
iconLabel->setEnabled(false);
return;
}
if (ci.pngImageLen != 0) {
QPixmap chanImage;
chanImage.loadFromData(ci.pngChanImage, ci.pngImageLen, "PNG");
iconLabel->setPixmap(chanImage);
iconLabel->setStyleSheet("QLabel{border: 3px solid white;}");
} else {
QPixmap defaulImage(CHAN_DEFAULT_IMAGE);
iconLabel->setPixmap(defaulImage);
iconLabel->setStyleSheet("QLabel{border: 2px solid white;border-radius: 10px;}");
}
iconLabel->setEnabled(true);
/* set textcolor for Channel name */
QString channelStr("<span style=\"font-size:22pt; font-weight:500;"
"color:#4F4F4F;\">%1</span>");
/* set Channel name */
QString cname = QString::fromStdWString(ci.channelName);
nameLabel->setText(channelStr.arg(cname));
/* do buttons */
if (ci.channelFlags & RS_DISTRIB_SUBSCRIBED) {
subscribeButton->setEnabled(false);
unsubscribeButton->setEnabled(true);
setAllAsReadButton->setEnabled(true);
} else {
subscribeButton->setEnabled(true);
unsubscribeButton->setEnabled(false);
setAllAsReadButton->setEnabled(false);
}
if (ci.channelFlags & RS_DISTRIB_PUBLISH) {
postButton->setEnabled(true);
} else {
postButton->setEnabled(false);
}
std::list<ChannelMsgSummary> msgs;
std::list<ChannelMsgSummary>::iterator it;
rsChannels->getChannelMsgList(mChannelId, msgs);
msgs.sort(sortChannelMsgSummary);
for(it = msgs.begin(); it != msgs.end(); it++) {
ChanMsgItem *cmi = new ChanMsgItem(this, 0, mChannelId, it->msgId, true);
mChanMsgItems.push_back(cmi);
verticalLayout_2->addWidget(cmi);
}
}
void ChannelFeed::unsubscribeChannel()
{
#ifdef CHAN_DEBUG
std::cerr << "ChannelFeed::unsubscribeChannel()";
std::cerr << std::endl;
#endif
if (rsChannels) {
rsChannels->channelSubscribe(mChannelId, false);
}
updateChannelMsgs();
}
void ChannelFeed::subscribeChannel()
{
#ifdef CHAN_DEBUG
std::cerr << "ChannelFeed::subscribeChannel()";
std::cerr << std::endl;
#endif
if (rsChannels) {
rsChannels->channelSubscribe(mChannelId, true);
}
updateChannelMsgs();
}
void ChannelFeed::showChannelDetails()
{
if (mChannelId.empty()) {
return;
}
if (!rsChannels) {
return;
}
ChannelDetails channelui (this);
channelui.showDetails(mChannelId);
channelui.exec();
}
void ChannelFeed::setAllAsReadClicked()
{
if (mChannelId.empty()) {
return;
}
if (!rsChannels) {
return;
}
ChannelInfo ci;
if (rsChannels->getChannelInfo(mChannelId, ci) == false) {
return;
}
if (ci.channelFlags & RS_DISTRIB_SUBSCRIBED) {
std::list<ChannelMsgSummary> msgs;
std::list<ChannelMsgSummary>::iterator it;
rsChannels->getChannelMsgList(mChannelId, msgs);
for(it = msgs.begin(); it != msgs.end(); it++) {
rsChannels->setMessageStatus(mChannelId, it->msgId, CHANNEL_MSG_STATUS_READ, CHANNEL_MSG_STATUS_READ | CHANNEL_MSG_STATUS_UNREAD_BY_USER);
}
}
}