RetroShare/retroshare-gui/src/gui/GxsForumsDialog.cpp
thunder2 799cf15555 Fixed crash in GxsGroupDialog in the second constructor.
Changed mode on GxsGroupDialog to enum.
Added different header text for every mode in PostedGroupDialog, WikiGroupDialog and GxsForumGroupDialog.
Removed not needed files of GxsForums.

git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-gxs-b1@5908 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2012-11-28 22:27:19 +00:00

706 lines
20 KiB
C++

/****************************************************************
* 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 <QMessageBox>
#include "GxsForumsDialog.h"
#include "gxs/GxsForumGroupDialog.h"
#include "gxsforums/GxsForumThreadWidget.h"
#include "settings/rsharesettings.h"
#include "RetroShareLink.h"
#include "channels/ShareKey.h"
#include "notifyqt.h"
// These should be in retroshare/ folder.
#include "gxs/rsgxsflags.h"
//#define DEBUG_FORUMS
/* Images for TreeWidget */
#define IMAGE_FOLDER ":/images/folder16.png"
#define IMAGE_FOLDERGREEN ":/images/folder_green.png"
#define IMAGE_FOLDERRED ":/images/folder_red.png"
#define IMAGE_FOLDERYELLOW ":/images/folder_yellow.png"
#define IMAGE_FORUM ":/images/konversation.png"
#define IMAGE_SUBSCRIBE ":/images/edit_add24.png"
#define IMAGE_UNSUBSCRIBE ":/images/cancel.png"
#define IMAGE_INFO ":/images/info16.png"
#define IMAGE_NEWFORUM ":/images/new_forum16.png"
#define IMAGE_FORUMAUTHD ":/images/konv_message2.png"
#define IMAGE_COPYLINK ":/images/copyrslink.png"
/*
* Transformation Notes:
* there are still a couple of things that the new forums differ from Old version.
* these will need to be addressed in the future.
* -> Missing Messages are not handled yet.
* -> Child TS (for sorting) is not handled by GXS, this will probably have to be done in the GUI.
* -> Need to handle IDs properly.
* -> Popularity not handled in GXS yet.
* -> Much more to do.
*/
/** Constructor */
GxsForumsDialog::GxsForumsDialog(QWidget *parent)
: RsAutoUpdatePage(1000,parent)
{
/* Invoke the Qt Designer generated object setup routine */
ui.setupUi(this);
/* Setup Queue */
mForumQueue = new TokenQueue(rsGxsForums->getTokenService(), this);
connect(ui.forumTreeWidget, SIGNAL(treeCustomContextMenuRequested(QPoint)), this, SLOT(forumListCustomPopupMenu(QPoint)));
connect(ui.newForumButton, SIGNAL(clicked()), this, SLOT(newforum()));
connect(ui.forumTreeWidget, SIGNAL(treeItemClicked(QString)), this, SLOT(changedForum(QString)));
connect(ui.threadTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(threadTabCloseRequested(int)));
connect(NotifyQt::getInstance(), SIGNAL(forumMsgReadSatusChanged(QString,QString,int)), this, SLOT(forumMsgReadSatusChanged(QString,QString,int)));
// HACK - TEMPORARY HIJACKING THIS BUTTON FOR REFRESH.
connect(ui.refreshButton, SIGNAL(clicked()), this, SLOT(forceUpdateDisplay()));
/* Set initial size the splitter */
QList<int> sizes;
sizes << 300 << width(); // Qt calculates the right sizes
ui.splitter->setSizes(sizes);
/* create forum tree */
yourForums = ui.forumTreeWidget->addCategoryItem(tr("Your Forums"), QIcon(IMAGE_FOLDER), true);
subscribedForums = ui.forumTreeWidget->addCategoryItem(tr("Subscribed Forums"), QIcon(IMAGE_FOLDERRED), true);
popularForums = ui.forumTreeWidget->addCategoryItem(tr("Popular Forums"), QIcon(IMAGE_FOLDERGREEN), false);
otherForums = ui.forumTreeWidget->addCategoryItem(tr("Other Forums"), QIcon(IMAGE_FOLDERYELLOW), false);
// load settings
processSettings(true);
/* Hide platform specific features */
#ifdef Q_WS_WIN
#endif
}
GxsForumsDialog::~GxsForumsDialog()
{
// save settings
processSettings(false);
delete(mForumQueue);
}
//#AFTER MERGE UserNotify *GxsForumsDialog::getUserNotify(QObject *parent)
//{
// return new GxsForumUserNotify(parent);
//}
void GxsForumsDialog::processSettings(bool bLoad)
{
Settings->beginGroup(QString("GxsForumsDialog"));
if (bLoad) {
// load settings
// state of splitter
ui.splitter->restoreState(Settings->value("Splitter").toByteArray());
} else {
// save settings
// state of splitter
Settings->setValue("Splitter", ui.splitter->saveState());
}
ui.forumTreeWidget->processSettings(Settings, bLoad);
Settings->endGroup();
}
void GxsForumsDialog::forumListCustomPopupMenu(QPoint /*point*/)
{
int subscribeFlags = ui.forumTreeWidget->subscribeFlags(QString::fromStdString(mForumId));
QMenu contextMnu(this);
QAction *action = contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Subscribe to Forum"), this, SLOT(subscribeToForum()));
action->setDisabled (mForumId.empty() || IS_GROUP_SUBSCRIBED(subscribeFlags));
action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe to Forum"), this, SLOT(unsubscribeToForum()));
action->setEnabled (!mForumId.empty() && IS_GROUP_SUBSCRIBED(subscribeFlags));
contextMnu.addSeparator();
contextMnu.addAction(QIcon(IMAGE_NEWFORUM), tr("New Forum"), this, SLOT(newforum()));
action = contextMnu.addAction(QIcon(IMAGE_INFO), tr("Show Forum Details"), this, SLOT(showForumDetails()));
action->setEnabled (!mForumId.empty ());
action = contextMnu.addAction(QIcon(":/images/settings16.png"), tr("Edit Forum Details"), this, SLOT(editForumDetails()));
action->setEnabled (!mForumId.empty () && IS_GROUP_ADMIN(subscribeFlags));
QAction *shareKeyAct = new QAction(QIcon(":/images/gpgp_key_generate.png"), tr("Share Forum"), &contextMnu);
connect( shareKeyAct, SIGNAL( triggered() ), this, SLOT( shareKey() ) );
shareKeyAct->setEnabled(!mForumId.empty() && IS_GROUP_ADMIN(subscribeFlags));
contextMnu.addAction( shareKeyAct);
QAction *restoreKeysAct = new QAction(QIcon(":/images/settings16.png"), tr("Restore Publish Rights for Forum" ), &contextMnu);
connect( restoreKeysAct , SIGNAL( triggered() ), this, SLOT( restoreForumKeys() ) );
restoreKeysAct->setEnabled(!mForumId.empty() && !IS_GROUP_ADMIN(subscribeFlags));
contextMnu.addAction( restoreKeysAct);
action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyForumLink()));
action->setEnabled(!mForumId.empty());
contextMnu.addSeparator();
action = contextMnu.addAction(QIcon(":/images/message-mail-read.png"), tr("Mark all as read"), this, SLOT(markMsgAsReadAll()));
action->setEnabled (!mForumId.empty () && IS_GROUP_SUBSCRIBED(subscribeFlags));
action = contextMnu.addAction(QIcon(":/images/message-mail.png"), tr("Mark all as unread"), this, SLOT(markMsgAsUnreadAll()));
action->setEnabled (!mForumId.empty () && IS_GROUP_SUBSCRIBED(subscribeFlags));
#ifdef DEBUG_FORUMS
contextMnu.addSeparator();
action = contextMnu.addAction("Generate mass data", this, SLOT(generateMassData()));
action->setEnabled (!mCurrForumId.empty() && IS_GROUP_SUBSCRIBED(mSubscribeFlags));
#endif
contextMnu.exec(QCursor::pos());
}
void GxsForumsDialog::restoreForumKeys(void)
{
QMessageBox::warning(this, "RetroShare", "ToDo");
#ifdef TOGXS
rsGxsForums->groupRestoreKeys(mCurrForumId);
#endif
}
void GxsForumsDialog::updateDisplay()
{
std::list<std::string> forumIds;
std::list<std::string>::iterator it;
if (!rsGxsForums)
return;
#if 0
// TODO groupsChanged... HACK XXX.
if ((rsGxsForums->groupsChanged(forumIds)) || (rsGxsForums->updated()))
{
/* update Forums List */
insertForums();
it = std::find(forumIds.begin(), forumIds.end(), mCurrForumId);
if (it != forumIds.end())
{
/* update threads as well */
insertThreads();
}
}
#endif
/* The proper version (above) can be done with a data request -> TODO */
if (rsGxsForums->updated())
{
/* update Forums List */
insertForums();
/* update threads as well */
//#TODO insertThreads();
}
}
// HACK until update works.
void GxsForumsDialog::forceUpdateDisplay()
{
std::cerr << "GxsForumsDialog::forceUpdateDisplay()";
std::cerr << std::endl;
/* update Forums List */
insertForums();
}
void GxsForumsDialog::forumInfoToGroupItemInfo(const RsGroupMetaData &forumInfo, GroupItemInfo &groupItemInfo)
{
groupItemInfo.id = QString::fromStdString(forumInfo.mGroupId);
groupItemInfo.name = QString::fromUtf8(forumInfo.mGroupName.c_str());
//groupItemInfo.description = QString::fromUtf8(forumInfo.forumDesc);
groupItemInfo.popularity = forumInfo.mPop;
groupItemInfo.lastpost = QDateTime::fromTime_t(forumInfo.mLastPost);
groupItemInfo.subscribeFlags = forumInfo.mSubscribeFlags;
#if TOGXS
if (forumInfo.mGroupFlags & RS_DISTRIB_AUTHEN_REQ) {
groupItemInfo.name += " (" + tr("AUTHD") + ")";
groupItemInfo.icon = QIcon(IMAGE_FORUMAUTHD);
}
else
#endif
{
groupItemInfo.icon = QIcon(IMAGE_FORUM);
}
}
/***** INSERT FORUM LISTS *****/
void GxsForumsDialog::insertForumsData(const std::list<RsGroupMetaData> &forumList)
{
std::list<RsGroupMetaData>::const_iterator it;
QList<GroupItemInfo> adminList;
QList<GroupItemInfo> subList;
QList<GroupItemInfo> popList;
QList<GroupItemInfo> otherList;
std::multimap<uint32_t, GroupItemInfo> popMap;
for (it = forumList.begin(); it != forumList.end(); it++) {
/* sort it into Publish (Own), Subscribed, Popular and Other */
uint32_t flags = it->mSubscribeFlags;
GroupItemInfo groupItemInfo;
forumInfoToGroupItemInfo(*it, groupItemInfo);
if (IS_GROUP_ADMIN(flags)) {
adminList.push_back(groupItemInfo);
} else if (IS_GROUP_SUBSCRIBED(flags)) {
/* subscribed forum */
subList.push_back(groupItemInfo);
} else {
/* rate the others by popularity */
popMap.insert(std::make_pair(it->mPop, 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;
uint32_t popLimit = 0;
std::multimap<uint32_t, GroupItemInfo>::reverse_iterator rit;
for(rit = popMap.rbegin(); ((rit != popMap.rend()) && (i < popCount)); rit++, i++) ;
if (rit != popMap.rend()) {
popLimit = rit->first;
}
for (rit = popMap.rbegin(); rit != popMap.rend(); rit++) {
if (rit->second.popularity < (int) popLimit) {
otherList.append(rit->second);
} else {
popList.append(rit->second);
}
}
/* now we can add them in as a tree! */
ui.forumTreeWidget->fillGroupItems(yourForums, adminList);
ui.forumTreeWidget->fillGroupItems(subscribedForums, subList);
ui.forumTreeWidget->fillGroupItems(popularForums, popList);
ui.forumTreeWidget->fillGroupItems(otherForums, otherList);
updateMessageSummaryList("");
}
void GxsForumsDialog::changedForum(const QString &id)
{
mForumId = id.toStdString();
if (mForumId.empty()) {
return;
}
// requestGroupSummary_CurrentForum(mForumId);
/* search exisiting tab */
GxsForumThreadWidget *threadWidget = NULL;
int tabCount = ui.threadTabWidget->count();
for (int index = 0; index < tabCount; ++index) {
GxsForumThreadWidget *childWidget = dynamic_cast<GxsForumThreadWidget*>(ui.threadTabWidget->widget(index));
if (childWidget && childWidget->forumId() == id.toStdString()) {
threadWidget = childWidget;
break;
}
}
if (!threadWidget) {
/* create a thread widget */
threadWidget = new GxsForumThreadWidget(id.toStdString());
ui.threadTabWidget->addTab(threadWidget, tr("Loading"));
connect(threadWidget, SIGNAL(forumChanged(QWidget*)), this, SLOT(threadTabChanged(QWidget*)));
}
ui.threadTabWidget->setCurrentWidget(threadWidget);
}
void GxsForumsDialog::threadTabCloseRequested(int index)
{
GxsForumThreadWidget *threadWidget = dynamic_cast<GxsForumThreadWidget*>(ui.threadTabWidget->widget(index));
if (threadWidget) {
delete(threadWidget);
}
}
void GxsForumsDialog::threadTabChanged(QWidget *widget)
{
int index = ui.threadTabWidget->indexOf(widget);
if (index < 0) {
return;
}
GxsForumThreadWidget *threadWidget = dynamic_cast<GxsForumThreadWidget*>(ui.threadTabWidget->widget(index));
if (!threadWidget) {
return;
}
ui.threadTabWidget->setTabText(index, threadWidget->forumName());
}
QString GxsForumsDialog::titleFromInfo(const RsMsgMetaData &meta)
{
// NOTE - NOTE SURE HOW THIS WILL WORK!
#ifdef TOGXS
if (meta.mMsgStatus & RS_DISTRIB_MISSING_MSG) {
return QApplication::translate("GxsForumsDialog", "[ ... Missing Message ... ]");
}
#endif
return QString::fromUtf8(meta.mMsgName.c_str());
}
QString GxsForumsDialog::messageFromInfo(const RsGxsForumMsg &msg)
{
#ifdef TOGXS
if (msg.mMeta.mMsgStatus & RS_DISTRIB_MISSING_MSG) {
return QApplication::translate("GxsForumsDialog", "Placeholder for missing Message");
}
#endif
return QString::fromUtf8(msg.mMsg.c_str());
}
void GxsForumsDialog::copyForumLink()
{
if (mForumId.empty()) {
return;
}
// THIS CODE CALLS getForumInfo() to verify that the Ids are valid.
// As we are switching to Request/Response this is now harder to do...
// So not bothering any more - shouldn't be necessary.
// IF we get errors - fix them, rather than patching here.
#if 0
ForumInfo fi;
if (rsGxsForums->getForumInfo(mCurrForumId, fi)) {
RetroShareLink link;
if (link.createForum(fi.forumId, "")) {
QList<RetroShareLink> urls;
urls.push_back(link);
RSLinkClipboard::copyLinks(urls);
}
}
#endif
QMessageBox::warning(this, "RetroShare", "ToDo");
}
void GxsForumsDialog::newforum()
{
GxsForumGroupDialog cf(mForumQueue, this);
cf.exec ();
}
void GxsForumsDialog::subscribeToForum()
{
forumSubscribe(true);
}
void GxsForumsDialog::unsubscribeToForum()
{
forumSubscribe(false);
}
void GxsForumsDialog::forumSubscribe(bool subscribe)
{
if (mForumId.empty()) {
return;
}
uint32_t token;
rsGxsForums->subscribeToGroup(token, mForumId, subscribe);
}
void GxsForumsDialog::showForumDetails()
{
if (mForumId.empty()) {
return;
}
RsGxsForumGroup grp;
grp.mMeta.mGroupId = mForumId;
GxsForumGroupDialog cf(grp, GxsGroupDialog::MODE_SHOW, this);
cf.exec ();
}
void GxsForumsDialog::editForumDetails()
{
if (mForumId.empty()) {
return;
}
RsGxsForumGroup grp;
grp.mMeta.mGroupId = mForumId;
GxsForumGroupDialog cf(grp, GxsGroupDialog::MODE_EDIT, this);
cf.exec ();
}
void GxsForumsDialog::shareKey()
{
ShareKey shareUi(this, 0, mForumId, FORUM_KEY_SHARE);
shareUi.exec();
}
void GxsForumsDialog::updateMessageSummaryList(std::string forumId)
{
QTreeWidgetItem *items[2] = { yourForums, subscribedForums };
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 = ui.forumTreeWidget->itemId(childItem).toStdString();
if (childId.empty()) {
continue;
}
if (forumId.empty() || childId == forumId) {
/* calculate unread messages */
unsigned int newMessageCount = 0;
unsigned int unreadMessageCount = 0;
//#TODO rsGxsForums->getMessageCount(childId, newMessageCount, unreadMessageCount);
std::cerr << "IMPLEMENT rsGxsForums->getMessageCount()";
std::cerr << std::endl;
ui.forumTreeWidget->setUnreadCount(childItem, unreadMessageCount);
if (forumId.empty() == false) {
/* Calculate only this forum */
break;
}
}
}
}
}
bool GxsForumsDialog::navigate(const std::string& forumId, const std::string& msgId)
{
if (forumId.empty()) {
return false;
}
if (ui.forumTreeWidget->activateId(QString::fromStdString(forumId), msgId.empty()) == NULL) {
return false;
}
/* Threads are filled in changedForum */
if (mForumId != forumId) {
return false;
}
if (msgId.empty()) {
return true;
}
//#TODO
// if (mThreadLoading) {
// mThreadLoad.FocusMsgId = msgId;
// return true;
// }
/* Search exisiting item */
// QTreeWidgetItemIterator itemIterator(ui.threadTreeWidget);
// QTreeWidgetItem *item = NULL;
// while ((item = *itemIterator) != NULL) {
// itemIterator++;
// if (item->data(COLUMN_THREAD_DATA, ROLE_THREAD_MSGID).toString().toStdString() == msgId) {
// ui.threadTreeWidget->setCurrentItem(item);
// ui.threadTreeWidget->setFocus();
// return true;
// }
// }
return false;
}
void GxsForumsDialog::generateMassData()
{
#ifdef DEBUG_FORUMS
if (mCurrForumId.empty ()) {
return;
}
if (QMessageBox::question(this, "Generate mass data", "Do you really want to generate mass data ?", QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
return;
}
for (int thread = 1; thread < 1000; thread++) {
ForumMsgInfo threadInfo;
threadInfo.forumId = mCurrForumId;
threadInfo.title = QString("Test %1").arg(thread, 3, 10, QChar('0')).toStdWString();
threadInfo.msg = QString("That is only a test").toStdWString();
if (rsGxsForums->ForumMessageSend(threadInfo) == false) {
return;
}
for (int msg = 1; msg < 3; msg++) {
ForumMsgInfo msgInfo;
msgInfo.forumId = mCurrForumId;
msgInfo.threadId = threadInfo.msgId;
msgInfo.parentId = threadInfo.msgId;
msgInfo.title = threadInfo.title;
msgInfo.msg = threadInfo.msg;
if (rsGxsForums->ForumMessageSend(msgInfo) == false) {
return;
}
}
}
#endif
}
/*********************** **** **** **** ***********************/
/** Request / Response of Data ********************************/
/*********************** **** **** **** ***********************/
#define FORUMSV2DIALOG_LISTING 1
//#define FORUMSV2DIALOG_CURRENTFORUM 2
void GxsForumsDialog::insertForums()
{
requestGroupSummary();
}
void GxsForumsDialog::requestGroupSummary()
{
std::cerr << "GxsForumsDialog::requestGroupSummary()";
std::cerr << std::endl;
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_META;
uint32_t token;
mForumQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, FORUMSV2DIALOG_LISTING);
}
void GxsForumsDialog::loadGroupSummary(const uint32_t &token)
{
std::cerr << "GxsForumsDialog::loadGroupSummary()";
std::cerr << std::endl;
std::list<RsGroupMetaData> groupInfo;
rsGxsForums->getGroupSummary(token, groupInfo);
if (groupInfo.size() > 0)
{
insertForumsData(groupInfo);
}
else
{
std::cerr << "GxsForumsDialog::loadGroupSummary() ERROR No Groups...";
std::cerr << std::endl;
}
}
/*********************** **** **** **** ***********************/
/*********************** **** **** **** ***********************/
//void GxsForumsDialog::requestGroupSummary_CurrentForum(const std::string &forumId)
//{
// RsTokReqOptions opts;
// opts.mReqType = GXS_REQUEST_TYPE_GROUP_META;
// std::list<std::string> grpIds;
// grpIds.push_back(forumId);
// std::cerr << "GxsForumsDialog::requestGroupSummary_CurrentForum(" << forumId << ")";
// std::cerr << std::endl;
// uint32_t token;
// mForumQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, grpIds, FORUMSV2DIALOG_CURRENTFORUM);
//}
//void GxsForumsDialog::loadGroupSummary_CurrentForum(const uint32_t &token)
//{
// std::cerr << "GxsForumsDialog::loadGroupSummary_CurrentForum()";
// std::cerr << std::endl;
// std::list<RsGroupMetaData> groupInfo;
// rsGxsForums->getGroupSummary(token, groupInfo);
// if (groupInfo.size() == 1)
// {
// RsGroupMetaData fi = groupInfo.front();
// mSubscribeFlags = fi.mSubscribeFlags;
// }
// else
// {
// resetData();
// std::cerr << "GxsForumsDialog::loadGroupSummary_CurrentForum() ERROR Invalid Number of Groups...";
// std::cerr << std::endl;
// }
// setValid(true);
//}
/*********************** **** **** **** ***********************/
/*********************** **** **** **** ***********************/
void GxsForumsDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req)
{
std::cerr << "GxsForumsDialog::loadRequest() UserType: " << req.mUserType;
std::cerr << std::endl;
if (queue == mForumQueue)
{
/* now switch on req */
switch(req.mUserType)
{
case FORUMSV2DIALOG_LISTING:
loadGroupSummary(req.mToken);
break;
// case FORUMSV2DIALOG_CURRENTFORUM:
// loadGroupSummary_CurrentForum(req.mToken);
// break;
default:
std::cerr << "GxsForumsDialog::loadRequest() ERROR: INVALID TYPE";
std::cerr << std::endl;
break;
}
}
}