/**************************************************************** * 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 #include #include #include #include #include #include #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(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(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 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); // Setup Channel Menu: QMenu *channelmenu = new QMenu(); channelmenu->addAction(actionCreate_Channel); channelmenu->addSeparator(); channelpushButton->setMenu(channelmenu); /* 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); shareUi.exec(); } void ChannelFeed::copyChannelLink() { if (mChannelId.empty()) { return; } ChannelInfo ci; if (rsChannels->getChannelInfo(mChannelId, ci)) { RetroShareLink link(RetroShareLink::TYPE_CHANNEL, QString::fromStdWString(ci.channelName), QString::fromStdString(ci.channelId), ""); if (link.valid() && link.type() == RetroShareLink::TYPE_CHANNEL) { std::vector 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 chanIds; std::list::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 channelList; std::list::iterator it; rsChannels->getChannelList(channelList); std::list keysAvailable; std::list::iterator keyIt; rsChannels->getPubKeysAvailableGrpIds(keysAvailable); /* get the ids for our lists */ QList adminList; QList subList; QList popList; QList otherList; std::multimap 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::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::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("%1"); /* 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 msgs; std::list::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 msgs; std::list::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::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")); } }