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

646 lines
18 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 "settings/rsharesettings.h"
#include "channels/CreateChannel.h"
#include "channels/ChannelDetails.h"
#include "channels/CreateChannelMsg.h"
#include "channels/EditChanDetails.h"
#include "channels/ShareKey.h"
#include "notifyqt.h"
#include "RetroShareLink.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(newChannelButton, SIGNAL(clicked()), 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(autoDownload, SIGNAL(clicked()), this, SLOT(toggleAutoDownload()));
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();
/* Set initial size the splitter */
QList<int> sizes;
sizes << 300 << width(); // Qt calculates the right sizes
splitter->setSizes(sizes);
/* Initialize group tree */
treeWidget->initDisplayMenu(displayButton);
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);
/* load settings */
processSettings(true);
updateChannelMsgs();
}
ChannelFeed::~ChannelFeed()
{
// save settings
processSettings(false);
}
void ChannelFeed::processSettings(bool load)
{
Settings->beginGroup(QString("ChannelFeed"));
if (load) {
// load settings
// state of splitter
splitter->restoreState(Settings->value("Splitter").toByteArray());
} else {
// save settings
// state of splitter
Settings->setValue("Splitter", splitter->saveState());
}
treeWidget->processSettings(Settings, load);
Settings->endGroup();
}
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 );
contextMnu.addAction( restoreKeysAct );
} 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 );
}
QAction *action = contextMnu.addAction(QIcon(":/images/copyrslink.png"), tr("Copy RetroShare Link"), this, SLOT(copyChannelLink()));
action->setEnabled(!mChannelId.empty());
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, CHANNEL_KEY_SHARE);
shareUi.exec();
}
void ChannelFeed::copyChannelLink()
{
if (mChannelId.empty()) {
return;
}
ChannelInfo ci;
if (rsChannels->getChannelInfo(mChannelId, ci)) {
RetroShareLink link;
if (link.createChannel(ci.channelId, "")) {
std::vector<RetroShareLink> urls;
urls.push_back(link);
RSLinkClipboard::copyLinks(urls);
}
}
}
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();
bool autoDl = false;
rsChannels->channelGetAutoDl(mChannelId, autoDl);
setAutoDownloadButton(autoDl);
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);
autoDownload->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);
autoDownload->setEnabled(false);
}
if (ci.channelFlags & RS_DISTRIB_PUBLISH) {
postButton->setEnabled(true);
autoDownload->setEnabled(false);
} else {
postButton->setEnabled(false);
}
if(!(ci.channelFlags & RS_DISTRIB_PUBLISH) &&
(ci.channelFlags & RS_DISTRIB_SUBSCRIBED))
autoDownload->setEnabled(true);
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, false);
}
updateChannelMsgs();
}
void ChannelFeed::subscribeChannel()
{
#ifdef CHAN_DEBUG
std::cerr << "ChannelFeed::subscribeChannel()";
std::cerr << std::endl;
#endif
if (rsChannels) {
rsChannels->channelSubscribe(mChannelId, true, 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);
}
}
}
void ChannelFeed::toggleAutoDownload(){
if(mChannelId.empty())
return;
bool autoDl = true;
if(rsChannels->channelGetAutoDl(mChannelId, autoDl)){
// if auto dl is set true, then set false
if(autoDl){
rsChannels->channelSetAutoDl(mChannelId, false);
}else{
rsChannels->channelSetAutoDl(mChannelId, true);
}
setAutoDownloadButton(!autoDl);
}
else{
std::cerr << "Auto Download failed to set"
<< std::endl;
}
}
bool ChannelFeed::navigate(const std::string& channelId, const std::string& msgId)
{
if (channelId.empty()) {
return false;
}
if (treeWidget->activateId(QString::fromStdString(channelId), msgId.empty()) == NULL) {
return false;
}
/* Messages are filled in selectChannel */
if (mChannelId != channelId) {
return false;
}
if (msgId.empty()) {
return true;
}
/* Search exisiting item */
std::list<ChanMsgItem*>::iterator mit;
for (mit = mChanMsgItems.begin(); mit != mChanMsgItems.end(); mit++) {
ChanMsgItem *item = *mit;
if (item->msgId() == msgId) {
// the next two lines are necessary to calculate the layout of the widgets in the scroll area (maybe there is a better solution)
item->show();
QCoreApplication::processEvents();
scrollArea->ensureWidgetVisible(item, 0, 0);
return true;
}
}
return false;
}
void ChannelFeed::setAutoDownloadButton(bool autoDl)
{
if (autoDl) {
autoDownload->setText(tr("Disable Auto-Download"));
}else{
autoDownload->setText(tr("Enable Auto-Download"));
}
}