mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-09-18 11:54:53 -04:00
Merge branch 'master' into TheWire-rework-ui
This commit is contained in:
commit
0522c7907a
128 changed files with 8255 additions and 2140 deletions
|
@ -24,6 +24,7 @@
|
|||
#include <QMenu>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <retroshare/rspeers.h>
|
||||
#include <retroshare/rsidentity.h>
|
||||
|
@ -696,34 +697,32 @@ void CreateCircleDialog::loadIdentities()
|
|||
{
|
||||
RsThread::async([this]()
|
||||
{
|
||||
std::list<RsGroupMetaData> ids_meta;
|
||||
std::list<RsGroupMetaData> ids_meta;
|
||||
|
||||
if(!rsIdentity->getIdentitiesSummaries(ids_meta))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities ids for all identities" << std::endl;
|
||||
RS_ERR("failed to retrieve identities ids for all identities");
|
||||
return;
|
||||
}
|
||||
std::set<RsGxsId> ids;
|
||||
}
|
||||
|
||||
for(auto& meta:ids_meta)
|
||||
ids.insert(RsGxsId(meta.mGroupId)) ;
|
||||
std::set<RsGxsId> ids;
|
||||
for(auto& meta:ids_meta) ids.insert(RsGxsId(meta.mGroupId));
|
||||
|
||||
std::vector<RsGxsIdGroup> id_groups;
|
||||
|
||||
if(!rsIdentity->getIdentitiesInfo(ids,id_groups))
|
||||
auto id_groups = std::make_unique<std::vector<RsGxsIdGroup>>();
|
||||
if(!rsIdentity->getIdentitiesInfo(ids, *id_groups))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for all identities" << std::endl;
|
||||
RS_ERR("failed to retrieve identities group info for all identities");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RsQThreadUtils::postToObject( [id_groups,this]()
|
||||
RsQThreadUtils::postToObject(
|
||||
[id_groups = std::move(id_groups), this]()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete */
|
||||
|
||||
fillIdentitiesList(id_groups) ;
|
||||
|
||||
fillIdentitiesList(*id_groups);
|
||||
}, this );
|
||||
});
|
||||
|
||||
|
|
|
@ -340,6 +340,10 @@ void GenCertDialog::setupState()
|
|||
ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto);
|
||||
|
||||
ui.cbUseBob->setVisible(hidden_state && !tor_auto);
|
||||
#ifndef RS_USE_I2P_BOB
|
||||
ui.cbUseBob->setDisabled(true);
|
||||
ui.cbUseBob->setToolTip(tr("BOB support is not available"));
|
||||
#endif
|
||||
|
||||
if(!mAllFieldsOk)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "util/misc.h"
|
||||
#include "util/QtVersion.h"
|
||||
#include "util/rstime.h"
|
||||
#include "util/rsdebug.h"
|
||||
|
||||
#include "retroshare/rsgxsflags.h"
|
||||
#include "retroshare/rsmsgs.h"
|
||||
|
@ -55,6 +56,7 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
/******
|
||||
* #define ID_DEBUG 1
|
||||
|
@ -506,21 +508,24 @@ void IdDialog::updateCircles()
|
|||
std::cerr << "Retrieving post data for post " << mThreadId << std::endl;
|
||||
#endif
|
||||
|
||||
std::list<RsGroupMetaData> circle_metas ;
|
||||
/* This can be big so use a smart pointer to just copy the pointer
|
||||
* instead of copying the whole list accross the lambdas */
|
||||
auto circle_metas = std::make_unique<std::list<RsGroupMetaData>>();
|
||||
|
||||
if(!rsGxsCircles->getCirclesSummaries(circle_metas))
|
||||
if(!rsGxsCircles->getCirclesSummaries(*circle_metas))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve circles group info list" << std::endl;
|
||||
RS_ERR("failed to retrieve circles group info list");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RsQThreadUtils::postToObject( [circle_metas,this]()
|
||||
RsQThreadUtils::postToObject(
|
||||
[circle_metas = std::move(circle_metas), this]()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete */
|
||||
|
||||
loadCircles(circle_metas);
|
||||
loadCircles(*circle_metas);
|
||||
|
||||
}, this );
|
||||
|
||||
|
@ -1305,19 +1310,17 @@ void IdDialog::updateIdList()
|
|||
return;
|
||||
}
|
||||
|
||||
std::map<RsGxsGroupId,RsGxsIdGroup> ids_set;
|
||||
auto ids_set = std::make_unique<std::map<RsGxsGroupId,RsGxsIdGroup>>();
|
||||
for(auto it(groups.begin()); it!=groups.end(); ++it)
|
||||
(*ids_set)[(*it).mMeta.mGroupId] = *it;
|
||||
|
||||
for(auto it(groups.begin());it!=groups.end();++it)
|
||||
ids_set[(*it).mMeta.mGroupId] = *it;
|
||||
|
||||
RsQThreadUtils::postToObject( [ids_set,this]()
|
||||
RsQThreadUtils::postToObject(
|
||||
[ids_set = std::move(ids_set), this] ()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete */
|
||||
|
||||
loadIdentities(ids_set);
|
||||
|
||||
loadIdentities(*ids_set);
|
||||
}, this );
|
||||
|
||||
});
|
||||
|
@ -1575,6 +1578,14 @@ void IdDialog::loadIdentities(const std::map<RsGxsGroupId,RsGxsIdGroup>& ids_set
|
|||
/* count items */
|
||||
int itemCount = contactsItem->childCount() + allItem->childCount() + ownItem->childCount();
|
||||
ui->label_count->setText( "(" + QString::number( itemCount ) + ")" );
|
||||
|
||||
int contactsCount = contactsItem->childCount() ;
|
||||
int allCount = allItem->childCount() ;
|
||||
int ownCount = ownItem->childCount();
|
||||
|
||||
contactsItem->setText(0, tr("My contacts") + " (" + QString::number( contactsCount ) + ")" );
|
||||
allItem->setText(0, tr("All") + " (" + QString::number( allCount ) + ")" );
|
||||
ownItem->setText(0, tr("My own identities") + " (" + QString::number( ownCount ) + ")" );
|
||||
|
||||
navigate(RsGxsId(oldCurrentId));
|
||||
filterIds();
|
||||
|
@ -1851,10 +1862,17 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
|
|||
{
|
||||
case RsServiceType::CHANNELS: service_name = tr("Channels") ;service_type = RetroShareLink::TYPE_CHANNEL ; break ;
|
||||
case RsServiceType::FORUMS: service_name = tr("Forums") ; service_type = RetroShareLink::TYPE_FORUM ; break ;
|
||||
case RsServiceType::POSTED: service_name = tr("Posted") ; service_type = RetroShareLink::TYPE_POSTED ; break ;
|
||||
case RsServiceType::POSTED: service_name = tr("Boards") ; service_type = RetroShareLink::TYPE_POSTED ; break ;
|
||||
case RsServiceType::CHAT: service_name = tr("Chat") ; service_type = RetroShareLink::TYPE_CHAT_ROOM ; break ;
|
||||
|
||||
case RsServiceType::GXS_TRANS: return tr("GxsMail author ");
|
||||
#ifdef TODO
|
||||
// We need a RS link for circles if we want to do that.
|
||||
//
|
||||
case RsServiceType::GXSCIRCLE: service_name = tr("GxsCircles"); service_type = RetroShareLink::TYPE_CIRCLES; break ;
|
||||
#endif
|
||||
default:
|
||||
service_name = tr("Unknown"); service_type = RetroShareLink::TYPE_UNKNOWN ;
|
||||
service_name = tr("Unknown (service=")+QString::number((int)u.mServiceId,16)+")"; service_type = RetroShareLink::TYPE_UNKNOWN ;
|
||||
}
|
||||
|
||||
switch(u.mUsageCode)
|
||||
|
@ -1874,10 +1892,25 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
|
|||
return tr("Group author for group %1 in service %2").arg(QString::fromStdString(u.mGrpId.toStdString())).arg(service_name);
|
||||
break ;
|
||||
case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION:
|
||||
case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE: // Identities are stamped regularly by crawlign the set of messages for all groups. That helps keepign the useful identities in hand.
|
||||
case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE: // Identities are stamped regularly by crawling the set of messages for all groups. That helps keepign the useful identities in hand.
|
||||
{
|
||||
RetroShareLink l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mMsgId,tr("Message/vote/comment"));
|
||||
return tr("%1 in %2 tab").arg(l.toHtml()).arg(service_name) ;
|
||||
RetroShareLink l;
|
||||
|
||||
std::cerr << "Signature validation/keep alive signature:" << std::endl;
|
||||
std::cerr << " service ID = " << std::hex << (uint16_t)u.mServiceId << std::dec << std::endl;
|
||||
std::cerr << " u.mGrpId = " << u.mGrpId << std::endl;
|
||||
std::cerr << " u.mMsgId = " << u.mMsgId << std::endl;
|
||||
std::cerr << " u.mParentId = " << u.mParentId << std::endl;
|
||||
std::cerr << " u.mThreadId = " << u.mThreadId << std::endl;
|
||||
|
||||
if(service_type == RetroShareLink::TYPE_CHANNEL && !u.mThreadId.isNull())
|
||||
l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mThreadId,tr("Vote/comment"));
|
||||
else if(service_type == RetroShareLink::TYPE_POSTED && !u.mThreadId.isNull())
|
||||
l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mThreadId,tr("Vote"));
|
||||
else
|
||||
l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mMsgId,tr("Message"));
|
||||
|
||||
return tr("%1 in %2 service").arg(l.toHtml()).arg(service_name) ;
|
||||
}
|
||||
case RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION: // Chat lobby msgs are signed, so each time one comes, or a chat lobby event comes, a signature verificaiton happens.
|
||||
{
|
||||
|
@ -1903,9 +1936,13 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
|
|||
{
|
||||
return tr("Signature in distant tunnel system.");
|
||||
}
|
||||
case RsIdentityUsage::IDENTITY_DATA_UPDATE: // Group update on that identity data. Can be avatar, name, etc.
|
||||
case RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC: // Group update on that identity data. Can be avatar, name, etc.
|
||||
{
|
||||
return tr("Update of identity data.");
|
||||
return tr("Received from GXS sync.");
|
||||
}
|
||||
case RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY: // Own friend sended his own ids
|
||||
{
|
||||
return tr("Friend node identity received through discovery.");
|
||||
}
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CHECK: // Any signature verified for that identity
|
||||
{
|
||||
|
@ -1913,11 +1950,18 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
|
|||
}
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION: // Any signature made by that identity
|
||||
{
|
||||
return tr("Generic signature.");
|
||||
return tr("Generic signature creation (e.g. chat room message, global router,...).");
|
||||
}
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION: return tr("Generic encryption.");
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION: return tr("Generic decryption.");
|
||||
case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK: return tr("Membership verification in circle %1.").arg(QString::fromStdString(u.mGrpId.toStdString()));
|
||||
case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK:
|
||||
{
|
||||
RsGxsCircleDetails det;
|
||||
if(rsGxsCircles->getCircleDetails(RsGxsCircleId(u.mGrpId),det))
|
||||
return tr("Membership verification in circle \"%1\" (%2).").arg(QString::fromUtf8(det.mCircleName.c_str())).arg(QString::fromStdString(u.mGrpId.toStdString()));
|
||||
else
|
||||
return tr("Membership verification in circle (ID=%1).").arg(QString::fromStdString(u.mGrpId.toStdString()));
|
||||
}
|
||||
|
||||
#warning TODO! csoler 2017-01-03: Add the different strings and translations here.
|
||||
default:
|
||||
|
|
|
@ -210,8 +210,8 @@ void IdEditDialog::setupExistingId(const RsGxsGroupId& keyId)
|
|||
RsThread::async([this,keyId]()
|
||||
{
|
||||
std::vector<RsGxsIdGroup> datavector;
|
||||
|
||||
bool res = rsIdentity->getIdentitiesInfo(std::set<RsGxsId>({(RsGxsId)keyId}),datavector);
|
||||
bool res = rsIdentity->getIdentitiesInfo(
|
||||
std::set<RsGxsId>({(RsGxsId)keyId}), datavector );
|
||||
|
||||
RsQThreadUtils::postToObject( [this,keyId,res,datavector]()
|
||||
{
|
||||
|
|
|
@ -146,6 +146,8 @@ void PostedCardView::setup()
|
|||
QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this);
|
||||
connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink()));
|
||||
|
||||
QAction *showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this);
|
||||
connect(showInPeopleAct, SIGNAL(triggered()), this, SLOT(showAuthorInPeople()));
|
||||
|
||||
int S = QFontMetricsF(font()).height() ;
|
||||
|
||||
|
@ -157,6 +159,8 @@ void PostedCardView::setup()
|
|||
|
||||
QMenu *menu = new QMenu();
|
||||
menu->addAction(CopyLinkAction);
|
||||
menu->addSeparator();
|
||||
menu->addAction(showInPeopleAct);
|
||||
ui->shareButton->setMenu(menu);
|
||||
|
||||
ui->clearButton->hide();
|
||||
|
@ -172,90 +176,106 @@ void PostedCardView::fill()
|
|||
// return;
|
||||
// }
|
||||
|
||||
QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png");
|
||||
RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId);
|
||||
bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE);
|
||||
|
||||
mInFill = true;
|
||||
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
|
||||
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
|
||||
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
|
||||
ui->dateLabel->setText(timestamp2);
|
||||
ui->dateLabel->setToolTip(timestamp);
|
||||
|
||||
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
|
||||
|
||||
// Use QUrl to check/parse our URL
|
||||
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
|
||||
QByteArray urlarray(mPost.mLink.c_str());
|
||||
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
|
||||
QString urlstr = "Invalid Link";
|
||||
QString sitestr = "Invalid Link";
|
||||
|
||||
bool urlOkay = url.isValid();
|
||||
if (urlOkay)
|
||||
{
|
||||
QString scheme = url.scheme();
|
||||
if ((scheme != "https")
|
||||
&& (scheme != "http")
|
||||
&& (scheme != "ftp")
|
||||
&& (scheme != "retroshare"))
|
||||
{
|
||||
urlOkay = false;
|
||||
sitestr = "Invalid Link Scheme";
|
||||
}
|
||||
}
|
||||
|
||||
if (urlOkay)
|
||||
{
|
||||
urlstr = QString("<a href=\"");
|
||||
urlstr += QString(url.toEncoded());
|
||||
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
|
||||
urlstr += messageName();
|
||||
urlstr += QString(" </span></a>");
|
||||
|
||||
QString siteurl = url.toEncoded();
|
||||
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
|
||||
|
||||
ui->titleLabel->setText(urlstr);
|
||||
}else
|
||||
{
|
||||
ui->titleLabel->setText(messageName());
|
||||
|
||||
}
|
||||
|
||||
if (urlarray.isEmpty())
|
||||
{
|
||||
ui->siteLabel->hide();
|
||||
}
|
||||
|
||||
ui->siteLabel->setText(sitestr);
|
||||
|
||||
if(mPost.mImage.mData != NULL)
|
||||
{
|
||||
QPixmap pixmap;
|
||||
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
|
||||
// Wiping data - as its been passed to thumbnail.
|
||||
|
||||
QPixmap scaledpixmap;
|
||||
if(pixmap.width() > 800){
|
||||
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
|
||||
ui->pictureLabel->setPixmap(scaledpixmap);
|
||||
}else{
|
||||
ui->pictureLabel->setPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
else if (mPost.mImage.mData == NULL)
|
||||
{
|
||||
if(redacted) {
|
||||
ui->commentButton->setDisabled(true);
|
||||
ui->voteUpButton->setDisabled(true);
|
||||
ui->voteDownButton->setDisabled(true);
|
||||
ui->picture_frame->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->picture_frame->show();
|
||||
}
|
||||
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
|
||||
ui->titleLabel->setText(tr( "<p><font color=\"#ff0000\"><b>The author of this message (with ID %1) is banned.</b>").arg(QString::fromStdString(mPost.mMeta.mAuthorId.toStdString()))) ;
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
ui->dateLabel->setText(timestamp);
|
||||
} else {
|
||||
|
||||
QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png");
|
||||
|
||||
mInFill = true;
|
||||
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
|
||||
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
|
||||
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
|
||||
ui->dateLabel->setText(timestamp2);
|
||||
ui->dateLabel->setToolTip(timestamp);
|
||||
|
||||
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
|
||||
|
||||
// Use QUrl to check/parse our URL
|
||||
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
|
||||
QByteArray urlarray(mPost.mLink.c_str());
|
||||
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
|
||||
QString urlstr = "Invalid Link";
|
||||
QString sitestr = "Invalid Link";
|
||||
|
||||
bool urlOkay = url.isValid();
|
||||
if (urlOkay)
|
||||
{
|
||||
QString scheme = url.scheme();
|
||||
if ((scheme != "https")
|
||||
&& (scheme != "http")
|
||||
&& (scheme != "ftp")
|
||||
&& (scheme != "retroshare"))
|
||||
{
|
||||
urlOkay = false;
|
||||
sitestr = "Invalid Link Scheme";
|
||||
}
|
||||
}
|
||||
|
||||
if (urlOkay)
|
||||
{
|
||||
urlstr = QString("<a href=\"");
|
||||
urlstr += QString(url.toEncoded());
|
||||
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
|
||||
urlstr += messageName();
|
||||
urlstr += QString(" </span></a>");
|
||||
|
||||
QString siteurl = url.toEncoded();
|
||||
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
|
||||
|
||||
ui->titleLabel->setText(urlstr);
|
||||
}else
|
||||
{
|
||||
ui->titleLabel->setText(messageName());
|
||||
|
||||
}
|
||||
|
||||
if (urlarray.isEmpty())
|
||||
{
|
||||
ui->siteLabel->hide();
|
||||
}
|
||||
|
||||
ui->siteLabel->setText(sitestr);
|
||||
|
||||
if(mPost.mImage.mData != NULL)
|
||||
{
|
||||
QPixmap pixmap;
|
||||
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
|
||||
// Wiping data - as its been passed to thumbnail.
|
||||
|
||||
QPixmap scaledpixmap;
|
||||
if(pixmap.width() > 800){
|
||||
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
|
||||
ui->pictureLabel->setPixmap(scaledpixmap);
|
||||
}else{
|
||||
ui->pictureLabel->setPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
else if (mPost.mImage.mData == NULL)
|
||||
{
|
||||
ui->picture_frame->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->picture_frame->show();
|
||||
}
|
||||
}
|
||||
|
||||
//QString score = "Hot" + QString::number(post.mHotScore);
|
||||
//score += " Top" + QString::number(post.mTopScore);
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "gui/common/FilesDefs.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
#include "util/HandleRichText.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/Identity/IdDialog.h"
|
||||
#include "PhotoView.h"
|
||||
#include "ui_PostedItem.h"
|
||||
|
||||
|
@ -338,6 +340,24 @@ void BasePostedItem::viewPicture()
|
|||
/* window will destroy itself! */
|
||||
}
|
||||
|
||||
void BasePostedItem::showAuthorInPeople()
|
||||
{
|
||||
if(mPost.mMeta.mAuthorId.isNull())
|
||||
{
|
||||
std::cerr << "(EE) GxsForumThreadWidget::loadMsgData_showAuthorInPeople() ERROR Missing Message Data...";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
/* window will destroy itself! */
|
||||
IdDialog *idDialog = dynamic_cast<IdDialog*>(MainWindow::getPage(MainWindow::People));
|
||||
|
||||
if (!idDialog)
|
||||
return ;
|
||||
|
||||
MainWindow::showWindow(MainWindow::People);
|
||||
idDialog->navigate(RsGxsId(mPost.mMeta.mAuthorId));
|
||||
}
|
||||
|
||||
//========================================================================================
|
||||
// PostedItem //
|
||||
//========================================================================================
|
||||
|
@ -394,6 +414,8 @@ void PostedItem::setup()
|
|||
QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this);
|
||||
connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink()));
|
||||
|
||||
QAction *showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this);
|
||||
connect(showInPeopleAct, SIGNAL(triggered()), this, SLOT(showAuthorInPeople()));
|
||||
|
||||
int S = QFontMetricsF(font()).height() ;
|
||||
|
||||
|
@ -407,6 +429,8 @@ void PostedItem::setup()
|
|||
|
||||
QMenu *menu = new QMenu();
|
||||
menu->addAction(CopyLinkAction);
|
||||
menu->addSeparator();
|
||||
menu->addAction(showInPeopleAct);
|
||||
ui->shareButton->setMenu(menu);
|
||||
|
||||
ui->clearButton->hide();
|
||||
|
@ -438,8 +462,6 @@ void PostedItem::makeUpVote()
|
|||
emit vote(msgId, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PostedItem::setComment(const RsGxsComment& cmt)
|
||||
{
|
||||
ui->newCommentLabel->show();
|
||||
|
@ -459,97 +481,115 @@ void PostedItem::setCommentsSize(int comNb)
|
|||
|
||||
void PostedItem::fill()
|
||||
{
|
||||
RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_POSTED, mGroupMeta.mGroupId, groupName());
|
||||
ui->nameLabel->setText(link.toHtml());
|
||||
RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId);
|
||||
bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE);
|
||||
|
||||
QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png");
|
||||
if(redacted) {
|
||||
ui->expandButton->setDisabled(true);
|
||||
ui->commentButton->setDisabled(true);
|
||||
ui->voteUpButton->setDisabled(true);
|
||||
ui->voteDownButton->setDisabled(true);
|
||||
|
||||
mInFill = true;
|
||||
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
|
||||
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
|
||||
ui->thumbnailLabel->setPixmap( QPixmap(":/images/thumb-default.png"));
|
||||
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
|
||||
ui->titleLabel->setText(tr( "<p><font color=\"#ff0000\"><b>The author of this message (with ID %1) is banned.</b>").arg(QString::fromStdString(mPost.mMeta.mAuthorId.toStdString()))) ;
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
ui->dateLabel->setText(timestamp);
|
||||
} else {
|
||||
RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_POSTED, mGroupMeta.mGroupId, groupName());
|
||||
ui->nameLabel->setText(link.toHtml());
|
||||
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
|
||||
ui->dateLabel->setText(timestamp2);
|
||||
ui->dateLabel->setToolTip(timestamp);
|
||||
QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png");
|
||||
|
||||
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
|
||||
mInFill = true;
|
||||
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
|
||||
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
|
||||
|
||||
// Use QUrl to check/parse our URL
|
||||
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
|
||||
QByteArray urlarray(mPost.mLink.c_str());
|
||||
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
|
||||
QString urlstr = "Invalid Link";
|
||||
QString sitestr = "Invalid Link";
|
||||
QDateTime qtime;
|
||||
qtime.setTime_t(mPost.mMeta.mPublishTs);
|
||||
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
|
||||
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
|
||||
ui->dateLabel->setText(timestamp2);
|
||||
ui->dateLabel->setToolTip(timestamp);
|
||||
|
||||
bool urlOkay = url.isValid();
|
||||
if (urlOkay)
|
||||
{
|
||||
QString scheme = url.scheme();
|
||||
if ((scheme != "https")
|
||||
&& (scheme != "http")
|
||||
&& (scheme != "ftp")
|
||||
&& (scheme != "retroshare"))
|
||||
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
|
||||
|
||||
// Use QUrl to check/parse our URL
|
||||
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
|
||||
QByteArray urlarray(mPost.mLink.c_str());
|
||||
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
|
||||
QString urlstr = "Invalid Link";
|
||||
QString sitestr = "Invalid Link";
|
||||
|
||||
bool urlOkay = url.isValid();
|
||||
if (urlOkay)
|
||||
{
|
||||
urlOkay = false;
|
||||
sitestr = "Invalid Link Scheme";
|
||||
QString scheme = url.scheme();
|
||||
if ((scheme != "https")
|
||||
&& (scheme != "http")
|
||||
&& (scheme != "ftp")
|
||||
&& (scheme != "retroshare"))
|
||||
{
|
||||
urlOkay = false;
|
||||
sitestr = "Invalid Link Scheme";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (urlOkay)
|
||||
{
|
||||
urlstr = QString("<a href=\"");
|
||||
urlstr += QString(url.toEncoded());
|
||||
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
|
||||
urlstr += messageName();
|
||||
urlstr += QString(" </span></a>");
|
||||
if (urlOkay)
|
||||
{
|
||||
urlstr = QString("<a href=\"");
|
||||
urlstr += QString(url.toEncoded());
|
||||
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
|
||||
urlstr += messageName();
|
||||
urlstr += QString(" </span></a>");
|
||||
|
||||
QString siteurl = url.toEncoded();
|
||||
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
|
||||
QString siteurl = url.toEncoded();
|
||||
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
|
||||
|
||||
ui->titleLabel->setText(urlstr);
|
||||
}else
|
||||
{
|
||||
ui->titleLabel->setText(messageName());
|
||||
ui->titleLabel->setText(urlstr);
|
||||
}else
|
||||
{
|
||||
ui->titleLabel->setText(messageName());
|
||||
|
||||
}
|
||||
|
||||
if (urlarray.isEmpty())
|
||||
{
|
||||
ui->siteLabel->hide();
|
||||
}
|
||||
|
||||
ui->siteLabel->setText(sitestr);
|
||||
|
||||
if(mPost.mImage.mData != NULL)
|
||||
{
|
||||
QPixmap pixmap;
|
||||
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
|
||||
// Wiping data - as its been passed to thumbnail.
|
||||
|
||||
QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
ui->thumbnailLabel->setPixmap(sqpixmap);
|
||||
ui->thumbnailLabel->setToolTip(tr("Click to view Picture"));
|
||||
|
||||
QPixmap scaledpixmap;
|
||||
if(pixmap.width() > 800){
|
||||
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
|
||||
ui->pictureLabel->setPixmap(scaledpixmap);
|
||||
}else{
|
||||
ui->pictureLabel->setPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
else if (urlOkay && (mPost.mImage.mData == NULL))
|
||||
{
|
||||
ui->expandButton->setDisabled(true);
|
||||
ui->thumbnailLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(LINK_IMAGE));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->expandButton->setDisabled(true);
|
||||
ui->thumbnailLabel->setPixmap(sqpixmap2);
|
||||
|
||||
if (urlarray.isEmpty())
|
||||
{
|
||||
ui->siteLabel->hide();
|
||||
}
|
||||
|
||||
ui->siteLabel->setText(sitestr);
|
||||
|
||||
if(mPost.mImage.mData != NULL)
|
||||
{
|
||||
QPixmap pixmap;
|
||||
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
|
||||
// Wiping data - as its been passed to thumbnail.
|
||||
|
||||
QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
ui->thumbnailLabel->setPixmap(sqpixmap);
|
||||
ui->thumbnailLabel->setToolTip(tr("Click to view Picture"));
|
||||
|
||||
QPixmap scaledpixmap;
|
||||
if(pixmap.width() > 800){
|
||||
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
|
||||
ui->pictureLabel->setPixmap(scaledpixmap);
|
||||
}else{
|
||||
ui->pictureLabel->setPixmap(pixmap);
|
||||
}
|
||||
}
|
||||
else if (urlOkay && (mPost.mImage.mData == NULL))
|
||||
{
|
||||
ui->expandButton->setDisabled(true);
|
||||
ui->thumbnailLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(LINK_IMAGE));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->expandButton->setDisabled(true);
|
||||
ui->thumbnailLabel->setPixmap(sqpixmap2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -701,5 +741,3 @@ void PostedItem::toggleNotes()
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ private slots:
|
|||
void readAndClearItem();
|
||||
void copyMessageLink();
|
||||
void viewPicture();
|
||||
void showAuthorInPeople();
|
||||
|
||||
signals:
|
||||
void vote(const RsGxsGrpMsgIdPair& msgId, bool up);
|
||||
|
|
|
@ -298,199 +298,10 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="logoLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../icons.qrc">:/icons/png/postedlinks.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="namelabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Popularity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="poplabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="infoPostsLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Posts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="infoPosts">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="createdlabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Created</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="createdinfolabel">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Administrator:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="GxsIdLabel" name="infoAdministrator">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Distribution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLabel" name="infoDistribution">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="infoLastPostLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Last Post:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="infoLastPost">
|
||||
<property name="text">
|
||||
<string notr="true">unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
|
@ -503,7 +314,7 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QTextBrowser" name="infoDescription">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
|
@ -526,6 +337,194 @@ p, li { white-space: pre-wrap; }
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Popularity</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="poplabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="infoPostsLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Posts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="infoPosts">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="createdlabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Created</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="createdinfolabel">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Administrator:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="GxsIdLabel" name="infoAdministrator">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Distribution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="infoDistribution">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="infoLastPostLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Last Post:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="infoLastPost">
|
||||
<property name="text">
|
||||
<string notr="true">unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="logoLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../icons.qrc">:/icons/png/postedlinks.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="namelabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <retroshare/rsstatus.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#define COLUMN_NAME 0
|
||||
#define COLUMN_CHECK 0
|
||||
|
@ -250,24 +251,21 @@ void FriendSelectionWidget::loadIdentities()
|
|||
|
||||
if(!rsIdentity->getIdentitiesSummaries(ids_meta))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for all identities" << std::endl;
|
||||
RS_ERR("failed to retrieve identities group info for all identities");
|
||||
return;
|
||||
}
|
||||
std::vector<RsGxsGroupId> ids;
|
||||
}
|
||||
|
||||
for(auto& meta:ids_meta)
|
||||
ids.push_back(meta.mGroupId) ;
|
||||
auto ids = std::make_unique<std::vector<RsGxsGroupId>>();
|
||||
for(auto& meta: ids_meta) ids->push_back(meta.mGroupId);
|
||||
|
||||
RsQThreadUtils::postToObject( [ids,this]()
|
||||
RsQThreadUtils::postToObject(
|
||||
[ids = std::move(ids), this]()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete */
|
||||
|
||||
gxsIds = ids; // we do that is the GUI thread. Dont try it on another thread!
|
||||
|
||||
fillList() ;
|
||||
|
||||
// We do that is the GUI thread. Dont try it on another thread!
|
||||
gxsIds = *ids;
|
||||
/* TODO: To furter optimize away a copy gxsIds could be a unique_ptr
|
||||
* too */
|
||||
fillList();
|
||||
}, this );
|
||||
});
|
||||
}
|
||||
|
|
|
@ -397,6 +397,19 @@ bool GroupTreeWidget::isSearchRequestResult(QPoint &point,QString& group_id,uint
|
|||
return search_req_id > 0;
|
||||
}
|
||||
|
||||
bool GroupTreeWidget::isSearchRequestResultItem(QTreeWidgetItem *item,QString& group_id,uint32_t& search_req_id)
|
||||
{
|
||||
QTreeWidgetItem *parent = item->parent();
|
||||
|
||||
if(parent == NULL)
|
||||
return false ;
|
||||
|
||||
search_req_id = parent->data(COLUMN_DATA, ROLE_REQUEST_ID).toUInt();
|
||||
group_id = itemId(item) ;
|
||||
|
||||
return search_req_id > 0;
|
||||
}
|
||||
|
||||
bool GroupTreeWidget::isSearchRequestItem(QPoint &point,uint32_t& search_req_id)
|
||||
{
|
||||
QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
|
||||
|
@ -463,6 +476,17 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList<
|
|||
item->setData(COLUMN_DATA, ROLE_NAME, itemInfo.name);
|
||||
item->setData(COLUMN_DATA, ROLE_DESCRIPTION, itemInfo.description);
|
||||
|
||||
// Add children for context strings. This happens in the search.
|
||||
while(nullptr != item->takeChild(0));
|
||||
|
||||
for(auto str:itemInfo.context_strings)
|
||||
if(!str.empty())
|
||||
{
|
||||
QTreeWidgetItem *it = new QTreeWidgetItem(QStringList(QString::fromUtf8(str.c_str())));
|
||||
it->setData(COLUMN_DATA,ROLE_ID,itemInfo.id);
|
||||
item->addChild(it);
|
||||
}
|
||||
|
||||
/* Set last post */
|
||||
qlonglong lastPost = itemInfo.lastpost.toTime_t();
|
||||
item->setData(COLUMN_DATA, ROLE_LASTPOST, -lastPost); // negative for correct sorting
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#ifndef GROUPTREEWIDGET_H
|
||||
#define GROUPTREEWIDGET_H
|
||||
|
||||
#include<set>
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QDateTime>
|
||||
|
||||
|
@ -47,16 +49,17 @@ public:
|
|||
{}
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString description;
|
||||
int popularity;
|
||||
QDateTime lastpost;
|
||||
QIcon icon;
|
||||
bool publishKey;
|
||||
bool adminKey;
|
||||
quint32 subscribeFlags;
|
||||
quint32 max_visible_posts ;
|
||||
QString id;
|
||||
QString name;
|
||||
QString description;
|
||||
int popularity;
|
||||
QDateTime lastpost;
|
||||
QIcon icon;
|
||||
bool publishKey;
|
||||
bool adminKey;
|
||||
quint32 subscribeFlags;
|
||||
quint32 max_visible_posts ;
|
||||
std::set<std::string> context_strings;
|
||||
};
|
||||
|
||||
//cppcheck-suppress noConstructor
|
||||
|
@ -94,6 +97,7 @@ public:
|
|||
|
||||
bool isSearchRequestItem(QPoint &point,uint32_t& search_req_id);
|
||||
bool isSearchRequestResult(QPoint &point, QString &group_id, uint32_t& search_req_id);
|
||||
bool isSearchRequestResultItem(QTreeWidgetItem *item,QString& group_id,uint32_t& search_req_id);
|
||||
|
||||
QTreeWidgetItem *getItemFromId(const QString &id);
|
||||
QTreeWidgetItem *activateId(const QString &id, bool focus);
|
||||
|
|
|
@ -19,10 +19,43 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include "RSTreeView.h"
|
||||
|
||||
RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent)
|
||||
{
|
||||
setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future.
|
||||
}
|
||||
|
||||
void RSTreeView::wheelEvent(QWheelEvent *e)
|
||||
{
|
||||
if(e->modifiers() == Qt::ControlModifier)
|
||||
emit zoomRequested(e->delta() > 0);
|
||||
else
|
||||
QTreeView::wheelEvent(e);
|
||||
}
|
||||
|
||||
void RSTreeView::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
QModelIndex idx = indexAt(e->pos());
|
||||
|
||||
if(idx != selectionModel()->currentIndex())
|
||||
selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect);
|
||||
|
||||
QTreeView::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
void RSTreeView::setAutoSelect(bool b)
|
||||
{
|
||||
if(b)
|
||||
setMouseTracking(true);
|
||||
else
|
||||
setMouseTracking(false);
|
||||
}
|
||||
|
||||
void RSTreeView::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
emit sizeChanged(e->size());
|
||||
}
|
||||
|
||||
void RSTreeView::setPlaceholderText(const QString &text)
|
||||
|
|
|
@ -33,8 +33,20 @@ public:
|
|||
|
||||
void setPlaceholderText(const QString &text);
|
||||
|
||||
// Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode
|
||||
// in trees that show editing widgets using a QStyledItemDelegate
|
||||
|
||||
void setAutoSelect(bool b);
|
||||
|
||||
signals:
|
||||
void sizeChanged(QSize);
|
||||
void zoomRequested(bool zoom_or_unzoom);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection
|
||||
virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom
|
||||
virtual void resizeEvent(QResizeEvent *e) override;
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
QString placeholderText;
|
||||
};
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
#ifndef ELNODE_H
|
||||
#define ELNODE_H
|
||||
|
||||
#include "graphwidget.h"
|
||||
|
||||
#include <retroshare/rstypes.h>
|
||||
|
||||
#include <QApplication>
|
||||
#if QT_VERSION >= 0x040600
|
||||
#include <QGraphicsObject>
|
||||
|
@ -30,9 +34,7 @@
|
|||
#include <QGraphicsItem>
|
||||
#endif
|
||||
#include <QList>
|
||||
|
||||
#include <retroshare/rstypes.h>
|
||||
#include "graphwidget.h"
|
||||
#include <QPainterPath>
|
||||
|
||||
class Edge;
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
|
@ -36,10 +36,12 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic
|
|||
/* Invoke the Qt Designer generated QObject setup routine */
|
||||
ui->setupUi(this);
|
||||
|
||||
//ui->postFrame->setVisible(false);
|
||||
|
||||
ui->treeWidget->setup(token_service, comment_service);
|
||||
setTokenService(token_service,comment_service);
|
||||
init();
|
||||
}
|
||||
|
||||
void GxsCommentDialog::init()
|
||||
{
|
||||
/* Set header resize modes and initial section sizes */
|
||||
QHeaderView * ttheader = ui->treeWidget->header () ;
|
||||
ttheader->resizeSection (0, 440);
|
||||
|
@ -62,6 +64,20 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic
|
|||
ui->sortBox->setIconSize(QSize(S*1.5,S*1.5));
|
||||
}
|
||||
|
||||
void GxsCommentDialog::setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service)
|
||||
{
|
||||
ui->treeWidget->setup(token_service, comment_service);
|
||||
}
|
||||
|
||||
GxsCommentDialog::GxsCommentDialog(QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::GxsCommentDialog)
|
||||
{
|
||||
/* Invoke the Qt Designer generated QObject setup routine */
|
||||
ui->setupUi(this);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
GxsCommentDialog::~GxsCommentDialog()
|
||||
{
|
||||
delete(ui);
|
||||
|
|
|
@ -32,9 +32,11 @@ class GxsCommentDialog: public QWidget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GxsCommentDialog(QWidget *parent);
|
||||
GxsCommentDialog(QWidget *parent, RsTokenService *token_service, RsGxsCommentService *comment_service);
|
||||
virtual ~GxsCommentDialog();
|
||||
|
||||
void setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service);
|
||||
void setCommentHeader(QWidget *header);
|
||||
void commentLoad(const RsGxsGroupId &grpId, const std::set<RsGxsMessageId> &msg_versions, const RsGxsMessageId &most_recent_msgId);
|
||||
|
||||
|
@ -48,6 +50,8 @@ private slots:
|
|||
void sortComments(int);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
RsGxsGroupId mGrpId;
|
||||
RsGxsMessageId mMostRecentMsgId;
|
||||
std::set<RsGxsMessageId> mMsgVersions;
|
||||
|
|
|
@ -18,6 +18,14 @@
|
|||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include "GxsCommentTreeWidget.h"
|
||||
|
||||
#include "gui/common/FilesDefs.h"
|
||||
#include "gui/common/RSElidedItemDelegate.h"
|
||||
#include "gui/common/RSTreeWidgetItem.h"
|
||||
#include "gui/gxs/GxsCreateCommentDialog.h"
|
||||
#include "gui/gxs/GxsIdTreeWidgetItem.h"
|
||||
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
|
@ -25,15 +33,9 @@
|
|||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include "gui/common/RSElidedItemDelegate.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
#include "gui/gxs/GxsCommentTreeWidget.h"
|
||||
#include "gui/gxs/GxsCreateCommentDialog.h"
|
||||
#include "gui/gxs/GxsIdTreeWidgetItem.h"
|
||||
#include "gui/common/RSTreeWidgetItem.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define PCITEM_COLUMN_COMMENT 0
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "gui/settings/rsharesettings.h"
|
||||
#include "gui/RetroShareLink.h"
|
||||
#include "gui/gxs/GxsGroupShareKey.h"
|
||||
#include "gui/common/GroupTreeWidget.h"
|
||||
#include "gui/common/RSTreeWidget.h"
|
||||
#include "gui/notifyqt.h"
|
||||
#include "gui/common/UIStateHelper.h"
|
||||
|
@ -89,7 +90,6 @@ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, QWidget *p
|
|||
mSubscribedGroups = NULL;
|
||||
mPopularGroups = NULL;
|
||||
mOtherGroups = NULL;
|
||||
mMessageWidget = NULL;
|
||||
|
||||
/* Setup Queue */
|
||||
mInterface = ifaceImpl;
|
||||
|
@ -251,6 +251,13 @@ void GxsGroupFrameDialog::processSettings(bool load)
|
|||
Settings->endGroup();
|
||||
}
|
||||
|
||||
bool GxsGroupFrameDialog::useTabs()
|
||||
{
|
||||
GroupFrameSettings groupFrameSettings;
|
||||
|
||||
return Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings) && groupFrameSettings.mOpenAllInNewTab;
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::settingsChanged()
|
||||
{
|
||||
GroupFrameSettings groupFrameSettings;
|
||||
|
@ -262,17 +269,15 @@ void GxsGroupFrameDialog::settingsChanged()
|
|||
|
||||
void GxsGroupFrameDialog::setSingleTab(bool singleTab)
|
||||
{
|
||||
if (singleTab) {
|
||||
if (!mMessageWidget) {
|
||||
mMessageWidget = createMessageWidget(RsGxsGroupId());
|
||||
// remove close button of the the first tab
|
||||
ui->messageTabWidget->hideCloseButton(ui->messageTabWidget->indexOf(mMessageWidget));
|
||||
}
|
||||
} else {
|
||||
if (mMessageWidget) {
|
||||
delete(mMessageWidget);
|
||||
mMessageWidget = NULL;
|
||||
}
|
||||
if (singleTab)
|
||||
{
|
||||
while(ui->messageTabWidget->count() > 1)
|
||||
{
|
||||
auto w = ui->messageTabWidget->widget(0) ;
|
||||
ui->messageTabWidget->removeTab(0);
|
||||
delete w;
|
||||
}
|
||||
ui->messageTabWidget->hideCloseButton(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,55 +291,50 @@ void GxsGroupFrameDialog::updateDisplay(bool complete)
|
|||
if(complete) // || !getGrpIds().empty() || !getGrpIdsMeta().empty()) {
|
||||
updateGroupSummary(); /* Update group list */
|
||||
|
||||
updateSearchResults() ;
|
||||
// updateSearchResults() ;
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::updateSearchResults()
|
||||
{
|
||||
const std::set<TurtleRequestId>& reqs = getSearchRequests();
|
||||
for(auto& it:mSearchGroupsItems)
|
||||
updateSearchResults(it.first);
|
||||
}
|
||||
|
||||
for(auto it(reqs.begin());it!=reqs.end();++it)
|
||||
{
|
||||
std::cerr << "updating search ID " << std::hex << *it << std::dec << std::endl;
|
||||
void GxsGroupFrameDialog::updateSearchResults(const TurtleRequestId& sid)
|
||||
{
|
||||
std::cerr << "updating search ID " << std::hex << sid << std::dec << std::endl;
|
||||
|
||||
std::map<RsGxsGroupId,RsGxsGroupSummary> group_infos;
|
||||
std::map<RsGxsGroupId,RsGxsGroupSearchResults> group_infos;
|
||||
|
||||
getDistantSearchResults(*it,group_infos) ;
|
||||
getDistantSearchResults(sid,group_infos) ;
|
||||
|
||||
std::cerr << "retrieved " << std::endl;
|
||||
std::cerr << "retrieved " << std::endl;
|
||||
|
||||
auto it2 = mSearchGroupsItems.find(*it);
|
||||
auto it2 = mSearchGroupsItems.find(sid);
|
||||
|
||||
if(mSearchGroupsItems.end() == it2)
|
||||
{
|
||||
std::cerr << "GxsGroupFrameDialog::updateSearchResults(): received result notification for req " << std::hex << *it << std::dec << " but no item present!" << std::endl;
|
||||
continue ; // we could create the item just as well but since this situation is not supposed to happen, I prefer to make this a failure case.
|
||||
}
|
||||
QList<GroupItemInfo> group_items ;
|
||||
|
||||
QList<GroupItemInfo> group_items ;
|
||||
for(auto it3(group_infos.begin());it3!=group_infos.end();++it3)
|
||||
{
|
||||
std::cerr << " adding group " << it3->first << " " << it3->second.mGroupId << " \"" << it3->second.mGroupName << "\"" << std::endl;
|
||||
for(auto s:it3->second.mSearchContexts)
|
||||
std::cerr << " Context string \"" << s << "\"" << std::endl;
|
||||
|
||||
for(auto it3(group_infos.begin());it3!=group_infos.end();++it3)
|
||||
if(mCachedGroupMetas.find(it3->first) == mCachedGroupMetas.end())
|
||||
{
|
||||
std::cerr << " adding new group " << it3->first << " "
|
||||
<< it3->second.mGroupId << " \""
|
||||
<< it3->second.mGroupName << "\"" << std::endl;
|
||||
GroupItemInfo i;
|
||||
i.id = QString(it3->second.mGroupId.toStdString().c_str());
|
||||
i.name = QString::fromUtf8(it3->second.mGroupName.c_str());
|
||||
i.popularity = 0; // could be set to the number of hits
|
||||
i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs);
|
||||
i.subscribeFlags = 0; // irrelevant here
|
||||
i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags);
|
||||
i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags);
|
||||
i.max_visible_posts = it3->second.mNumberOfMessages;
|
||||
i.context_strings = it3->second.mSearchContexts;
|
||||
|
||||
GroupItemInfo i;
|
||||
i.id = QString(it3->second.mGroupId.toStdString().c_str());
|
||||
i.name = QString::fromUtf8(it3->second.mGroupName.c_str());
|
||||
i.popularity = 0; // could be set to the number of hits
|
||||
i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs);
|
||||
i.subscribeFlags = 0; // irrelevant here
|
||||
i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags);
|
||||
i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags);
|
||||
i.max_visible_posts = it3->second.mNumberOfMessages;
|
||||
group_items.push_back(i);
|
||||
}
|
||||
|
||||
group_items.push_back(i);
|
||||
}
|
||||
|
||||
ui->groupTreeWidget->fillGroupItems(it2->second, group_items);
|
||||
}
|
||||
ui->groupTreeWidget->fillGroupItems(it2->second, group_items);
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::todo()
|
||||
|
@ -360,13 +360,22 @@ void GxsGroupFrameDialog::removeCurrentSearch()
|
|||
mSearchGroupsItems.erase(it);
|
||||
|
||||
mKnownGroups.erase(search_request_id);
|
||||
|
||||
clearDistantSearchResults(search_request_id);
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::removeAllSearches()
|
||||
{
|
||||
for(auto it(mSearchGroupsItems.begin());it!=mSearchGroupsItems.end();++it)
|
||||
ui->groupTreeWidget->removeSearchItem(it->second) ;
|
||||
{
|
||||
QString group_id;
|
||||
TurtleRequestId search_request_id;
|
||||
|
||||
if(ui->groupTreeWidget->isSearchRequestResultItem(it->second,group_id,search_request_id))
|
||||
clearDistantSearchResults(search_request_id);
|
||||
|
||||
ui->groupTreeWidget->removeSearchItem(it->second) ;
|
||||
}
|
||||
mSearchGroupsItems.clear();
|
||||
mKnownGroups.clear();
|
||||
}
|
||||
|
@ -390,6 +399,7 @@ static uint32_t checkDelay(uint32_t time_in_secs)
|
|||
|
||||
return 365 * 86400;
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
|
||||
{
|
||||
// First separately handle the case of search top level items
|
||||
|
@ -432,12 +442,10 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
|
|||
QMenu contextMnu(this);
|
||||
QAction *action;
|
||||
|
||||
if (mMessageWidget) {
|
||||
action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab()));
|
||||
if (mGroupId.isNull() || messageWidget(mGroupId, true)) {
|
||||
action->setEnabled(false);
|
||||
}
|
||||
}
|
||||
action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab()));
|
||||
|
||||
if(mGroupId.isNull()) // dont enable the open in tab if a tab is already here
|
||||
action->setEnabled(false);
|
||||
|
||||
if (isSubscribed) {
|
||||
action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup()));
|
||||
|
@ -673,7 +681,7 @@ bool GxsGroupFrameDialog::getCurrentGroupName(QString& name)
|
|||
|
||||
void GxsGroupFrameDialog::markMsgAsRead()
|
||||
{
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false);
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
|
||||
if (msgWidget) {
|
||||
msgWidget->setAllMessagesRead(true);
|
||||
}
|
||||
|
@ -681,7 +689,7 @@ void GxsGroupFrameDialog::markMsgAsRead()
|
|||
|
||||
void GxsGroupFrameDialog::markMsgAsUnread()
|
||||
{
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false);
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
|
||||
if (msgWidget) {
|
||||
msgWidget->setAllMessagesRead(false);
|
||||
}
|
||||
|
@ -759,7 +767,7 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa
|
|||
changedCurrentGroup(groupIdString);
|
||||
|
||||
/* search exisiting tab */
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false);
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
|
||||
if (!msgWidget) {
|
||||
return false;
|
||||
}
|
||||
|
@ -771,17 +779,16 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa
|
|||
return msgWidget->navigate(msgId);
|
||||
}
|
||||
|
||||
GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId, bool ownTab)
|
||||
GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId)
|
||||
{
|
||||
int tabCount = ui->messageTabWidget->count();
|
||||
for (int index = 0; index < tabCount; ++index) {
|
||||
|
||||
for (int index = 0; index < tabCount; ++index)
|
||||
{
|
||||
GxsMessageFrameWidget *childWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
|
||||
if (ownTab && mMessageWidget && childWidget == mMessageWidget) {
|
||||
continue;
|
||||
}
|
||||
if (childWidget && childWidget->groupId() == groupId) {
|
||||
|
||||
if (childWidget && childWidget->groupId() == groupId)
|
||||
return childWidget;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -790,9 +797,9 @@ GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &gr
|
|||
GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId)
|
||||
{
|
||||
GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId);
|
||||
if (!msgWidget) {
|
||||
|
||||
if (!msgWidget)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true));
|
||||
ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon());
|
||||
|
@ -817,40 +824,44 @@ GxsCommentDialog *GxsGroupFrameDialog::commentWidget(const RsGxsMessageId& msgId
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::changedCurrentGroup(const QString &groupId)
|
||||
void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId)
|
||||
{
|
||||
if (mInFill) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (groupId.isEmpty()) {
|
||||
if (mMessageWidget) {
|
||||
mMessageWidget->setGroupId(RsGxsGroupId());
|
||||
ui->messageTabWidget->setCurrentWidget(mMessageWidget);
|
||||
}
|
||||
if (groupId.isEmpty())
|
||||
{
|
||||
auto w = currentWidget();
|
||||
|
||||
if(w)
|
||||
w->setGroupId(RsGxsGroupId());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mGroupId = RsGxsGroupId(groupId.toStdString());
|
||||
if (mGroupId.isNull()) {
|
||||
|
||||
if (mGroupId.isNull())
|
||||
return;
|
||||
}
|
||||
|
||||
/* search exisiting tab */
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true);
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
|
||||
|
||||
if (!msgWidget) {
|
||||
if (mMessageWidget) {
|
||||
/* not found, use standard tab */
|
||||
msgWidget = mMessageWidget;
|
||||
msgWidget->setGroupId(mGroupId);
|
||||
} else {
|
||||
/* create new tab */
|
||||
msgWidget = createMessageWidget(mGroupId);
|
||||
// check that we have at least one tab
|
||||
|
||||
if(msgWidget)
|
||||
ui->messageTabWidget->setCurrentWidget(msgWidget);
|
||||
else
|
||||
{
|
||||
if(useTabs() || ui->messageTabWidget->count()==0)
|
||||
{
|
||||
msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString()));
|
||||
ui->messageTabWidget->setCurrentWidget(msgWidget);
|
||||
}
|
||||
else
|
||||
currentWidget()->setGroupId(mGroupId);
|
||||
}
|
||||
|
||||
ui->messageTabWidget->setCurrentWidget(msgWidget);
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item)
|
||||
|
@ -870,37 +881,31 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId)
|
|||
}
|
||||
|
||||
/* search exisiting tab */
|
||||
GxsMessageFrameWidget *msgWidget = messageWidget(groupId, true);
|
||||
if (!msgWidget) {
|
||||
/* not found, create new tab */
|
||||
msgWidget = createMessageWidget(groupId);
|
||||
}
|
||||
GxsMessageFrameWidget *msgWidget = createMessageWidget(groupId);
|
||||
|
||||
ui->messageTabWidget->setCurrentWidget(msgWidget);
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::messageTabCloseRequested(int index)
|
||||
{
|
||||
QWidget *widget = ui->messageTabWidget->widget(index);
|
||||
if (!widget) {
|
||||
if(ui->messageTabWidget->count() == 1) /* Don't close single tab */
|
||||
return;
|
||||
}
|
||||
|
||||
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(widget);
|
||||
if (msgWidget && msgWidget == mMessageWidget) {
|
||||
/* Don't close single tab */
|
||||
return;
|
||||
}
|
||||
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
|
||||
delete msgWidget ;
|
||||
}
|
||||
|
||||
delete(widget);
|
||||
GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const
|
||||
{
|
||||
return dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(ui->messageTabWidget->currentIndex()));
|
||||
}
|
||||
|
||||
void GxsGroupFrameDialog::messageTabChanged(int index)
|
||||
{
|
||||
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
|
||||
if (!msgWidget) {
|
||||
|
||||
if (!msgWidget)
|
||||
return;
|
||||
}
|
||||
|
||||
ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false);
|
||||
}
|
||||
|
@ -1074,15 +1079,20 @@ void GxsGroupFrameDialog::updateGroupSummary()
|
|||
{
|
||||
RsThread::async([this]()
|
||||
{
|
||||
std::list<RsGxsGenericGroupData*> groupInfo;
|
||||
auto groupInfo = new std::list<RsGxsGenericGroupData*>() ;
|
||||
|
||||
if(!getGroupData(groupInfo))
|
||||
if(!getGroupData(*groupInfo))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info " << std::endl;
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info." << std::endl;
|
||||
delete groupInfo;
|
||||
return;
|
||||
}
|
||||
if(groupInfo.empty())
|
||||
if(groupInfo->empty())
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " no group info collected." << std::endl;
|
||||
delete groupInfo;
|
||||
return;
|
||||
}
|
||||
|
||||
RsQThreadUtils::postToObject( [this,groupInfo]()
|
||||
{
|
||||
|
@ -1092,7 +1102,7 @@ void GxsGroupFrameDialog::updateGroupSummary()
|
|||
* Qt::QueuedConnection is important!
|
||||
*/
|
||||
|
||||
insertGroupsData(groupInfo);
|
||||
insertGroupsData(*groupInfo);
|
||||
updateSearchResults();
|
||||
|
||||
mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false);
|
||||
|
@ -1111,12 +1121,14 @@ void GxsGroupFrameDialog::updateGroupSummary()
|
|||
|
||||
// now delete the data that is not used anymore
|
||||
|
||||
for(auto& g:groupInfo)
|
||||
for(auto& g:*groupInfo)
|
||||
{
|
||||
mCachedGroupMetas[g->mMeta.mGroupId] = g->mMeta;
|
||||
delete g;
|
||||
}
|
||||
|
||||
delete groupInfo;
|
||||
|
||||
}, this );
|
||||
});
|
||||
}
|
||||
|
|
|
@ -161,7 +161,9 @@ private:
|
|||
virtual void groupTreeCustomActions(RsGxsGroupId /*grpId*/, int /*subscribeFlags*/, QList<QAction*> &/*actions*/) {}
|
||||
virtual RsGxsCommentService *getCommentService() { return NULL; }
|
||||
virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &/*grpId*/, const RsGxsMessageId &/*msgId*/) { return NULL; }
|
||||
virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map<RsGxsGroupId,RsGxsGroupSummary>& /* group_infos */){ return false ;}
|
||||
virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map<RsGxsGroupId,RsGxsGroupSearchResults>& /* group_infos */){ return false ;}
|
||||
virtual void clearDistantSearchResults(TurtleRequestId /* id */) {}
|
||||
virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id){ return nullptr ;}
|
||||
|
||||
void initUi();
|
||||
|
||||
|
@ -181,24 +183,27 @@ private:
|
|||
|
||||
// subscribe/unsubscribe ack.
|
||||
|
||||
GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId, bool ownTab);
|
||||
GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId);
|
||||
GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId);
|
||||
|
||||
GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId);
|
||||
|
||||
protected:
|
||||
void updateSearchResults();
|
||||
void updateSearchResults(const TurtleRequestId &sid);
|
||||
void updateSearchResults(); // update all searches
|
||||
|
||||
bool mCountChildMsgs; // Count unread child messages?
|
||||
|
||||
private:
|
||||
GxsMessageFrameWidget *currentWidget() const;
|
||||
bool useTabs();
|
||||
|
||||
bool mInitialized;
|
||||
bool mInFill;
|
||||
bool mDistSyncAllowed;
|
||||
QString mSettingsName;
|
||||
RsGxsGroupId mGroupId;
|
||||
RsGxsIfaceHelper *mInterface;
|
||||
GxsMessageFrameWidget *mMessageWidget;
|
||||
|
||||
QTreeWidgetItem *mYourGroups;
|
||||
QTreeWidgetItem *mSubscribedGroups;
|
||||
|
|
|
@ -18,21 +18,23 @@
|
|||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QThread>
|
||||
#include <QTimerEvent>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include <math.h>
|
||||
#include <util/rsdir.h>
|
||||
#include "gui/common/AvatarDialog.h"
|
||||
#include "GxsIdDetails.h"
|
||||
|
||||
#include "gui/common/AvatarDialog.h"
|
||||
#include "retroshare-gui/RsAutoUpdatePage.h"
|
||||
|
||||
#include <retroshare/rspeers.h>
|
||||
#include <util/rsdir.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMutexLocker>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QThread>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include <iostream>
|
||||
#include <QPainter>
|
||||
#include <cmath>
|
||||
|
||||
/* Images for tag icons */
|
||||
#define IMAGE_LOADING ":/images/folder-draft.png"
|
||||
|
|
|
@ -55,6 +55,7 @@ signals:
|
|||
void groupChanged(QWidget *widget);
|
||||
void waitingChanged(QWidget *widget);
|
||||
void loadComment(const RsGxsGroupId &groupId, const QVector<RsGxsMessageId>& msg_versions,const RsGxsMessageId &msgId, const QString &title);
|
||||
void groupDataLoaded();
|
||||
|
||||
protected:
|
||||
virtual void setAllMessagesReadDo(bool read, uint32_t &token) = 0;
|
||||
|
|
|
@ -72,7 +72,8 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId
|
|||
|
||||
connect(addFileButton, SIGNAL(clicked() ), this , SLOT(addExtraFile()));
|
||||
connect(addfilepushButton, SIGNAL(clicked() ), this , SLOT(addExtraFile()));
|
||||
|
||||
connect(subjectEdit,SIGNAL(textChanged(const QString&)),this,SLOT(updatePreviewText(const QString&)));
|
||||
|
||||
connect(addThumbnailButton, SIGNAL(clicked() ), this , SLOT(addThumbnail()));
|
||||
connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool)));
|
||||
connect(stackedWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint)));
|
||||
|
@ -605,6 +606,11 @@ void CreateGxsChannelMsg::saveChannelInfo(const RsGroupMetaData &meta)
|
|||
subjectEdit->setFocus();
|
||||
}
|
||||
|
||||
void CreateGxsChannelMsg::updatePreviewText(const QString& s)
|
||||
{
|
||||
preview_W->setText(s);
|
||||
}
|
||||
|
||||
void CreateGxsChannelMsg::sendMsg()
|
||||
{
|
||||
#ifdef DEBUG_CREATE_GXS_MSG
|
||||
|
@ -717,7 +723,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str
|
|||
|
||||
void CreateGxsChannelMsg::addThumbnail()
|
||||
{
|
||||
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 156, 107);
|
||||
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 107,156); // these absolute sizes are terrible
|
||||
|
||||
if (img.isNull())
|
||||
return;
|
||||
|
@ -725,7 +731,7 @@ void CreateGxsChannelMsg::addThumbnail()
|
|||
picture = img;
|
||||
|
||||
// to show the selected
|
||||
thumbnail_label->setPixmap(picture);
|
||||
preview_W->setPixmap(picture);
|
||||
}
|
||||
|
||||
void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
|
||||
|
@ -769,7 +775,7 @@ void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
|
|||
if(post.mThumbnail.mData != NULL)
|
||||
{
|
||||
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL);
|
||||
thumbnail_label->setPixmap(picture);
|
||||
preview_W->setPixmap(picture);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ private slots:
|
|||
void addExtraFile();
|
||||
void checkAttachmentReady();
|
||||
void deleteAttachment();
|
||||
void updatePreviewText(const QString &);
|
||||
|
||||
void cancelMsg();
|
||||
void sendMsg();
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>671</width>
|
||||
<height>513</height>
|
||||
<width>736</width>
|
||||
<height>271</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
|
@ -20,7 +20,7 @@
|
|||
<iconset resource="../images.qrc">
|
||||
<normaloff>:/images/logo/logo_16.png</normaloff>:/images/logo/logo_16.png</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="CreateGxsChannelMsgGLayout">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -33,7 +33,10 @@
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<property name="horizontalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
|
@ -52,53 +55,15 @@
|
|||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="channelpostButton">
|
||||
<property name="text">
|
||||
<string>Channel Post</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="attachmentsButton">
|
||||
<property name="text">
|
||||
<string>Attachments</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>486</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="horizontalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="mouseTracking">
|
||||
|
@ -114,7 +79,10 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="stackedWidgetPage1">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -127,146 +95,158 @@
|
|||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="thumbnail_label">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>156</width>
|
||||
<height>107</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeIncrement">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../images.qrc">:/images/thumb-default-video.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ChannelPostThumbnailView" name="preview_W" native="true"/>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="4">
|
||||
<widget class="QLabel" name="channelAttachLabel">
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="channelNameHLayout">
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelNameLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Channel Post to:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="channelName">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelAttachLabel">
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Attachments:</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Use Drag and Drop / Add Files button, to Hash new files.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Copy/Paste RetroShare links from your shares</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="addThumbnailButton">
|
||||
<property name="text">
|
||||
<string>Add Channel Thumbnail</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="addfilepushButton">
|
||||
<property name="text">
|
||||
<string>Add File to Attach</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3" colspan="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="addThumbnailButton">
|
||||
<property name="text">
|
||||
<string>Add Channel Thumbnail</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addfilepushButton">
|
||||
<property name="text">
|
||||
<string>Add File to Attach</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="subjectEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="messageGBox">
|
||||
<property name="title">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLineEdit" name="subjectEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="RichTextEdit" name="RichTextEditWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="channelNameHLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="channelNameLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Channel Post to:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="channelName">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<item>
|
||||
<widget class="RichTextEdit" name="RichTextEditWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -276,7 +256,7 @@ p, li { white-space: pre-wrap; }
|
|||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
|
@ -329,7 +309,7 @@ p, li { white-space: pre-wrap; }
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>632</width>
|
||||
<width>81</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -403,6 +383,53 @@ p, li { white-space: pre-wrap; }
|
|||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="channelpostButton">
|
||||
<property name="text">
|
||||
<string>Channel Post</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>486</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="attachmentsButton">
|
||||
<property name="text">
|
||||
<string>Attachments</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="buttonHLayout">
|
||||
<item>
|
||||
|
@ -455,10 +482,16 @@ p, li { white-space: pre-wrap; }
|
|||
<header>util/RichTextEdit.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ChannelPostThumbnailView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/gxschannels/GxsChannelPostThumbnail.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../icons.qrc"/>
|
||||
<include location="../images.qrc"/>
|
||||
<include location="../icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
#include "GxsChannelDialog.h"
|
||||
#include "GxsChannelGroupDialog.h"
|
||||
#include "GxsChannelPostsWidget.h"
|
||||
#include "GxsChannelPostsWidgetWithModel.h"
|
||||
#include "CreateGxsChannelMsg.h"
|
||||
#include "GxsChannelUserNotify.h"
|
||||
#include "gui/gxs/GxsGroupShareKey.h"
|
||||
|
@ -61,9 +61,7 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> ev
|
|||
{
|
||||
const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get());
|
||||
|
||||
if(!e)
|
||||
return;
|
||||
|
||||
if(e)
|
||||
switch(e->mChannelEventCode)
|
||||
{
|
||||
case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]];
|
||||
|
@ -72,11 +70,6 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> ev
|
|||
updateGroupStatisticsReal(e->mChannelGroupId); // update the list immediately
|
||||
break;
|
||||
|
||||
case RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT:
|
||||
mSearchResults.insert(e->mDistantSearchRequestId);
|
||||
updateSearchResults();
|
||||
break;
|
||||
|
||||
case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]];
|
||||
case RsChannelEventCode::SUBSCRIBE_STATUS_CHANGED:
|
||||
updateDisplay(true);
|
||||
|
@ -89,6 +82,13 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> ev
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
const RsGxsChannelSearchResultEvent*f = dynamic_cast<const RsGxsChannelSearchResultEvent*>(event.get());
|
||||
|
||||
if(nullptr != f)
|
||||
for(auto it:f->mSearchResultsMap)
|
||||
updateSearchResults(it.first);
|
||||
}
|
||||
|
||||
GxsChannelDialog::~GxsChannelDialog()
|
||||
|
@ -204,7 +204,7 @@ int GxsChannelDialog::shareKeyType()
|
|||
|
||||
GxsMessageFrameWidget *GxsChannelDialog::createMessageFrameWidget(const RsGxsGroupId &groupId)
|
||||
{
|
||||
return new GxsChannelPostsWidget(groupId);
|
||||
return new GxsChannelPostsWidgetWithModel(groupId,this);
|
||||
}
|
||||
|
||||
void GxsChannelDialog::setDefaultDirectory()
|
||||
|
@ -396,21 +396,36 @@ void GxsChannelDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *gro
|
|||
groupItemInfo.description = QString::fromUtf8(channelGroupData->mDescription.c_str());
|
||||
}
|
||||
|
||||
void GxsChannelDialog::clearDistantSearchResults(TurtleRequestId id)
|
||||
{
|
||||
rsGxsChannels->clearDistantSearchResults(id);
|
||||
}
|
||||
|
||||
TurtleRequestId GxsChannelDialog::distantSearch(const QString& search_string)
|
||||
{
|
||||
return rsGxsChannels->turtleSearchRequest(search_string.toStdString()) ;
|
||||
}
|
||||
|
||||
bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId,RsGxsGroupSummary>& group_infos)
|
||||
bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId,RsGxsGroupSearchResults>& group_infos)
|
||||
{
|
||||
return rsGxsChannels->retrieveDistantSearchResults(id,group_infos);
|
||||
}
|
||||
|
||||
RsGxsGenericGroupData *GxsChannelDialog::getDistantSearchResultGroupData(const RsGxsGroupId& group_id)
|
||||
{
|
||||
RsGxsChannelGroup channel_group;
|
||||
|
||||
if(rsGxsChannels->getDistantSearchResultGroupData(group_id,channel_group))
|
||||
return new RsGxsGenericGroupData(channel_group);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GxsChannelDialog::checkRequestGroup(const RsGxsGroupId& grpId)
|
||||
{
|
||||
RsGxsChannelGroup distant_group;
|
||||
|
||||
if( rsGxsChannels->retrieveDistantGroup(grpId,distant_group)) // normally we should also check that the group meta is not already here.
|
||||
if( rsGxsChannels->getDistantSearchResultGroupData(grpId,distant_group)) // normally we should also check that the group meta is not already here.
|
||||
{
|
||||
std::cerr << "GxsChannelDialog::checkRequestGroup() sending turtle request for group data for group " << grpId << std::endl;
|
||||
rsGxsChannels->turtleGroupRequest(grpId);
|
||||
|
|
|
@ -43,10 +43,12 @@ public:
|
|||
|
||||
protected:
|
||||
/* GxsGroupFrameDialog */
|
||||
virtual bool getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId,RsGxsGroupSummary>& group_infos);
|
||||
virtual bool getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId, RsGxsGroupSearchResults> &group_infos) override;
|
||||
virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id) override;
|
||||
|
||||
virtual TurtleRequestId distantSearch(const QString& search_string) ;
|
||||
virtual void checkRequestGroup(const RsGxsGroupId& grpId) ;
|
||||
virtual TurtleRequestId distantSearch(const QString& search_string) override;
|
||||
virtual void checkRequestGroup(const RsGxsGroupId& grpId) override ;
|
||||
virtual void clearDistantSearchResults(TurtleRequestId id) override;
|
||||
|
||||
// Implementation of some abstract methods in GxsGroupFrameDialog
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <QMenu>
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include <QFileInfo>
|
||||
|
@ -27,11 +28,13 @@
|
|||
#include "GxsChannelFilesStatusWidget.h"
|
||||
#include "ui_GxsChannelFilesStatusWidget.h"
|
||||
#include "gui/common/RsUrlHandler.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
#include "retroshare/rsfiles.h"
|
||||
|
||||
GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent) :
|
||||
QWidget(parent), mGroupId(groupId), mMessageId(messageId), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget)
|
||||
GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent) :
|
||||
QWidget(parent), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
|
@ -40,11 +43,21 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &gro
|
|||
setSize(mFile.mSize);
|
||||
|
||||
/* Connect signals */
|
||||
connect(ui->downloadToolButton, SIGNAL(clicked()), this, SLOT(download()));
|
||||
connect(ui->downloadPushButton, SIGNAL(clicked()), this, SLOT(download()));
|
||||
connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume()));
|
||||
connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause()));
|
||||
connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel()));
|
||||
connect(ui->openFolderToolButton, SIGNAL(clicked()), this, SLOT(openFolder()));
|
||||
connect(ui->openFilePushButton, SIGNAL(clicked()), this, SLOT(openFile()));
|
||||
|
||||
ui->downloadPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png"));
|
||||
ui->openFolderToolButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/arrow.png"));
|
||||
|
||||
QAction *openfolder = new QAction(tr("Open folder"), this);
|
||||
connect(openfolder, SIGNAL(triggered()), this, SLOT(openFolder()));
|
||||
|
||||
QMenu *menu = new QMenu();
|
||||
menu->addAction(openfolder);
|
||||
ui->openFolderToolButton->setMenu(menu);
|
||||
|
||||
check();
|
||||
}
|
||||
|
@ -80,6 +93,17 @@ void GxsChannelFilesStatusWidget::check()
|
|||
if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
|
||||
mState = STATE_LOCAL;
|
||||
setSize(fileInfo.size);
|
||||
|
||||
/* check if the file is a media file */
|
||||
if (!misc::isPreviewable(QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).suffix()))
|
||||
{
|
||||
/* check if the file is not a media file and change text */
|
||||
ui->openFilePushButton->setText(tr("Open file"));
|
||||
} else {
|
||||
ui->openFilePushButton->setText(tr("Play"));
|
||||
ui->openFilePushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png"));
|
||||
}
|
||||
|
||||
} else {
|
||||
FileInfo fileInfo;
|
||||
bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo);
|
||||
|
@ -126,11 +150,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_ERROR:
|
||||
repeat = 0;
|
||||
|
||||
ui->downloadToolButton->hide();
|
||||
ui->downloadPushButton->hide();
|
||||
ui->resumeToolButton->hide();
|
||||
ui->pauseToolButton->hide();
|
||||
ui->cancelToolButton->hide();
|
||||
ui->progressBar->hide();
|
||||
ui->openFilePushButton->hide();
|
||||
ui->openFolderToolButton->hide();
|
||||
|
||||
statusText = tr("Error");
|
||||
|
@ -140,11 +165,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_REMOTE:
|
||||
repeat = 30000;
|
||||
|
||||
ui->downloadToolButton->show();
|
||||
ui->downloadPushButton->show();
|
||||
ui->resumeToolButton->hide();
|
||||
ui->pauseToolButton->hide();
|
||||
ui->cancelToolButton->hide();
|
||||
ui->progressBar->hide();
|
||||
ui->openFilePushButton->hide();
|
||||
ui->openFolderToolButton->hide();
|
||||
|
||||
break;
|
||||
|
@ -152,11 +178,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_DOWNLOAD:
|
||||
repeat = 1000;
|
||||
|
||||
ui->downloadToolButton->hide();
|
||||
ui->downloadPushButton->hide();
|
||||
ui->resumeToolButton->hide();
|
||||
ui->pauseToolButton->show();
|
||||
ui->cancelToolButton->show();
|
||||
ui->progressBar->show();
|
||||
ui->openFilePushButton->hide();
|
||||
ui->openFolderToolButton->hide();
|
||||
|
||||
break;
|
||||
|
@ -164,11 +191,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_PAUSED:
|
||||
repeat = 1000;
|
||||
|
||||
ui->downloadToolButton->hide();
|
||||
ui->downloadPushButton->hide();
|
||||
ui->resumeToolButton->show();
|
||||
ui->pauseToolButton->hide();
|
||||
ui->cancelToolButton->show();
|
||||
ui->progressBar->hide();
|
||||
ui->openFilePushButton->hide();
|
||||
ui->openFolderToolButton->hide();
|
||||
|
||||
statusText = tr("Paused");
|
||||
|
@ -178,11 +206,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_WAITING:
|
||||
repeat = 1000;
|
||||
|
||||
ui->downloadToolButton->hide();
|
||||
ui->downloadPushButton->hide();
|
||||
ui->resumeToolButton->hide();
|
||||
ui->pauseToolButton->show();
|
||||
ui->cancelToolButton->show();
|
||||
ui->progressBar->hide();
|
||||
ui->openFilePushButton->hide();
|
||||
ui->openFolderToolButton->hide();
|
||||
|
||||
statusText = tr("Waiting");
|
||||
|
@ -192,11 +221,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_CHECKING:
|
||||
repeat = 1000;
|
||||
|
||||
ui->downloadToolButton->hide();
|
||||
ui->downloadPushButton->hide();
|
||||
ui->resumeToolButton->hide();
|
||||
ui->pauseToolButton->hide();
|
||||
ui->cancelToolButton->show();
|
||||
ui->progressBar->hide();
|
||||
ui->openFilePushButton->hide();
|
||||
ui->openFolderToolButton->hide();
|
||||
|
||||
statusText = tr("Checking");
|
||||
|
@ -206,11 +236,12 @@ void GxsChannelFilesStatusWidget::check()
|
|||
case STATE_LOCAL:
|
||||
repeat = 60000;
|
||||
|
||||
ui->downloadToolButton->hide();
|
||||
ui->downloadPushButton->hide();
|
||||
ui->resumeToolButton->hide();
|
||||
ui->pauseToolButton->hide();
|
||||
ui->cancelToolButton->hide();
|
||||
ui->progressBar->hide();
|
||||
ui->openFilePushButton->show();
|
||||
ui->openFolderToolButton->show();
|
||||
|
||||
break;
|
||||
|
@ -296,3 +327,24 @@ void GxsChannelFilesStatusWidget::openFolder()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GxsChannelFilesStatusWidget::openFile()
|
||||
{
|
||||
FileInfo fileInfo;
|
||||
if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* open file with a suitable application */
|
||||
QFileInfo qinfo;
|
||||
qinfo.setFile(QString::fromUtf8(fileInfo.path.c_str()));
|
||||
if (qinfo.exists()) {
|
||||
if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) {
|
||||
std::cerr << "GxsChannelFilesStatusWidget(): can't open file " << fileInfo.path << std::endl;
|
||||
}
|
||||
}else{
|
||||
QMessageBox::information(this, tr("Play File"),
|
||||
tr("File %1 does not exist at location.").arg(fileInfo.path.c_str()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class GxsChannelFilesStatusWidget : public QWidget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent = 0);
|
||||
explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0);
|
||||
~GxsChannelFilesStatusWidget();
|
||||
|
||||
private slots:
|
||||
|
@ -44,6 +44,7 @@ private slots:
|
|||
void pause();
|
||||
void resume();
|
||||
void openFolder();
|
||||
void openFile();
|
||||
|
||||
private:
|
||||
void setSize(uint64_t size);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>367</width>
|
||||
<height>27</height>
|
||||
<width>421</width>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -48,7 +48,7 @@
|
|||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="downloadToolButton">
|
||||
<widget class="QPushButton" name="downloadPushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
|
@ -126,18 +126,31 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="openFolderToolButton">
|
||||
<widget class="QPushButton" name="openFilePushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Play</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="openFolderToolButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open folder</string>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -148,6 +161,7 @@
|
|||
</widget>
|
||||
<resources>
|
||||
<include location="../images.qrc"/>
|
||||
<include location="../icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -86,7 +86,7 @@ GxsChannelFilesWidget::~GxsChannelFilesWidget()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related)
|
||||
void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost& post, bool related)
|
||||
{
|
||||
if (related) {
|
||||
removeItems(post.mMeta.mGroupId, post.mMeta.mMsgId);
|
||||
|
@ -113,7 +113,7 @@ void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related)
|
|||
|
||||
ui->treeWidget->addTopLevelItem(treeItem);
|
||||
|
||||
QWidget *statusWidget = new GxsChannelFilesStatusWidget(post.mMeta.mGroupId, post.mMeta.mMsgId, file);
|
||||
QWidget *statusWidget = new GxsChannelFilesStatusWidget(file);
|
||||
ui->treeWidget->setItemWidget(treeItem, COLUMN_STATUS, statusWidget);
|
||||
|
||||
filterItem(treeItem);
|
||||
|
|
476
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
Normal file
476
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
Normal file
|
@ -0,0 +1,476 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp *
|
||||
* *
|
||||
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFontMetrics>
|
||||
#include <QModelIndex>
|
||||
#include <QIcon>
|
||||
|
||||
#include "gui/common/FilesDefs.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
#include "util/HandleRichText.h"
|
||||
#include "util/DateTime.h"
|
||||
#include "retroshare/rsgxsflags.h"
|
||||
#include "retroshare/rsgxschannels.h"
|
||||
#include "retroshare/rsexpr.h"
|
||||
|
||||
#include "GxsChannelPostFilesModel.h"
|
||||
|
||||
//#define DEBUG_CHANNEL_MODEL
|
||||
|
||||
Q_DECLARE_METATYPE(ChannelPostFileInfo)
|
||||
|
||||
static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
|
||||
|
||||
RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
initEmptyHierarchy();
|
||||
|
||||
mTimer = new QTimer;
|
||||
connect(mTimer,SIGNAL(timeout()),this,SLOT(update()));
|
||||
}
|
||||
|
||||
void RsGxsChannelPostFilesModel::initEmptyHierarchy()
|
||||
{
|
||||
preMods();
|
||||
|
||||
mFiles.clear();
|
||||
mFilteredFiles.clear();
|
||||
|
||||
postMods();
|
||||
}
|
||||
|
||||
void RsGxsChannelPostFilesModel::preMods()
|
||||
{
|
||||
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
|
||||
|
||||
beginResetModel();
|
||||
}
|
||||
void RsGxsChannelPostFilesModel::postMods()
|
||||
{
|
||||
endResetModel();
|
||||
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
|
||||
}
|
||||
|
||||
void RsGxsChannelPostFilesModel::update()
|
||||
{
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
|
||||
}
|
||||
|
||||
int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if(parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if(mFilteredFiles.empty()) // security. Should never happen.
|
||||
return 0;
|
||||
|
||||
if(!parent.isValid())
|
||||
return mFilteredFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
|
||||
|
||||
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return COLUMN_FILES_NB_COLUMNS ;
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,ChannelPostFileInfo& fmpe) const
|
||||
{
|
||||
if(!i.isValid())
|
||||
return true;
|
||||
|
||||
quintptr ref = i.internalId();
|
||||
uint32_t entry = 0;
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size())
|
||||
return false ;
|
||||
|
||||
fmpe = mFiles[mFilteredFiles[entry]];
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostFilesModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid())
|
||||
return true;
|
||||
|
||||
return false; // by default, no channel post has children
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref)
|
||||
{
|
||||
// the pointer is formed the following way:
|
||||
//
|
||||
// [ 32 bits ]
|
||||
//
|
||||
// This means that the whole software has the following build-in limitation:
|
||||
// * 4 B simultaenous posts. Should be enough !
|
||||
|
||||
ref = (intptr_t)(entry+1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry)
|
||||
{
|
||||
intptr_t val = (intptr_t)ref;
|
||||
|
||||
if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious
|
||||
{
|
||||
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl;
|
||||
return false ;
|
||||
}
|
||||
if(val==0)
|
||||
{
|
||||
RsErr() << "(EE) trying to make a ChannelPostsFileModelIndex out of index 0." << std::endl;
|
||||
return false;
|
||||
}
|
||||
entry = val-1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const
|
||||
{
|
||||
if(row < 0 || column < 0 || column >= COLUMN_FILES_NB_COLUMNS)
|
||||
return QModelIndex();
|
||||
|
||||
quintptr ref = getChildRef(parent.internalId(),row);
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
|
||||
#endif
|
||||
return createIndex(row,column,ref) ;
|
||||
}
|
||||
|
||||
QModelIndex RsGxsChannelPostFilesModel::parent(const QModelIndex& index) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
return QModelIndex(); // there's no hierarchy here. So nothing to do!
|
||||
}
|
||||
|
||||
Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
|
||||
if(index.column() == COLUMN_FILES_FILE)
|
||||
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
||||
else
|
||||
return QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
return 0;
|
||||
|
||||
if(ref == quintptr(0))
|
||||
{
|
||||
quintptr new_ref;
|
||||
convertTabEntryToRefPointer(index,new_ref);
|
||||
return new_ref;
|
||||
}
|
||||
else
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const
|
||||
{
|
||||
ChannelPostFilesModelIndex ref_entry;
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredFiles.size())
|
||||
return 0 ;
|
||||
|
||||
if(ref_entry == 0)
|
||||
{
|
||||
RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl;
|
||||
row = 0;
|
||||
}
|
||||
else
|
||||
row = ref_entry-1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const
|
||||
{
|
||||
uint32_t entry = 0 ;
|
||||
|
||||
if(ref == quintptr(0))
|
||||
return rowCount()-1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
switch(section)
|
||||
{
|
||||
case COLUMN_FILES_FILE: return QString("Status");
|
||||
case COLUMN_FILES_SIZE: return QString("Size");
|
||||
case COLUMN_FILES_NAME: return QString("File");
|
||||
case COLUMN_FILES_DATE: return QString("Published");
|
||||
default:
|
||||
return QString("[No data]");
|
||||
}
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
|
||||
#endif
|
||||
|
||||
if(!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
switch(role)
|
||||
{
|
||||
case Qt::SizeHintRole: return sizeHintRole(index.column()) ;
|
||||
case Qt::StatusTipRole:return QVariant();
|
||||
default: break;
|
||||
}
|
||||
|
||||
quintptr ref = (index.isValid())?index.internalId():0 ;
|
||||
uint32_t entry = 0;
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "data(" << index << ")" ;
|
||||
#endif
|
||||
|
||||
if(!ref)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " [empty]" << std::endl;
|
||||
#endif
|
||||
return QVariant() ;
|
||||
}
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
|
||||
#endif
|
||||
return QVariant() ;
|
||||
}
|
||||
|
||||
const ChannelPostFileInfo& fmpe(mFiles[mFilteredFiles[entry]]);
|
||||
|
||||
switch(role)
|
||||
{
|
||||
case Qt::DisplayRole: return displayRole (fmpe,index.column()) ;
|
||||
case Qt::UserRole: return userRole (fmpe,index.column()) ;
|
||||
case SortRole: return sortRole (fmpe,index.column()) ;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& count)
|
||||
{
|
||||
preMods();
|
||||
|
||||
beginRemoveRows(QModelIndex(),0,rowCount()-1);
|
||||
endRemoveRows();
|
||||
|
||||
if(strings.empty())
|
||||
{
|
||||
mFilteredFiles.clear();
|
||||
for(int i=0;i<mFiles.size();++i)
|
||||
mFilteredFiles.push_back(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
mFilteredFiles.clear();
|
||||
//mFilteredPosts.push_back(0);
|
||||
|
||||
for(int i=0;i<mFiles.size();++i)
|
||||
{
|
||||
bool passes_strings = true;
|
||||
|
||||
for(auto& s:strings)
|
||||
passes_strings = passes_strings && QString::fromStdString(mFiles[i].mName).contains(s,Qt::CaseInsensitive);
|
||||
|
||||
if(passes_strings)
|
||||
mFilteredFiles.push_back(i);
|
||||
}
|
||||
}
|
||||
count = mFilteredFiles.size();
|
||||
|
||||
std::cerr << "After filtering: " << count << " posts remain." << std::endl;
|
||||
|
||||
beginInsertRows(QModelIndex(),0,rowCount()-1);
|
||||
endInsertRows();
|
||||
|
||||
postMods();
|
||||
}
|
||||
|
||||
class compareOperator
|
||||
{
|
||||
public:
|
||||
compareOperator(int column,Qt::SortOrder order): col(column),ord(order) {}
|
||||
|
||||
bool operator()(const ChannelPostFileInfo& f1,const ChannelPostFileInfo& f2) const
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
default:
|
||||
case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return (ord==Qt::AscendingOrder)?(f1.mName<f2.mName):(f1.mName>f2.mName);
|
||||
case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSize<f2.mSize):(f1.mSize>f2.mSize);
|
||||
case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return (ord==Qt::AscendingOrder)?(f1.mPublishTime<f2.mPublishTime):(f1.mPublishTime>f2.mPublishTime);
|
||||
case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE:
|
||||
{
|
||||
FileInfo fi1,fi2;
|
||||
rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1);
|
||||
rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2);
|
||||
|
||||
return (ord==Qt::AscendingOrder)?(fi1.transfered<fi2.transfered):(fi1.transfered>fi2.transfered);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
int col;
|
||||
Qt::SortOrder ord;
|
||||
};
|
||||
|
||||
void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order)
|
||||
{
|
||||
std::sort(mFiles.begin(),mFiles.end(),compareOperator(column,order));
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const
|
||||
{
|
||||
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
|
||||
|
||||
return QVariant( QSize(factor * 170, factor*14 ));
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostFilesModel::sortRole(const ChannelPostFileInfo& fmpe,int column) const
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str()));
|
||||
case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize));
|
||||
case COLUMN_FILES_DATE: return QVariant(qulonglong(fmpe.mPublishTime));
|
||||
case COLUMN_FILES_FILE:
|
||||
{
|
||||
FileInfo finfo;
|
||||
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
|
||||
return qulonglong(finfo.transfered);
|
||||
|
||||
return QVariant(qulonglong(fmpe.mSize));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return displayRole(fmpe,column);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostFilesModel::displayRole(const ChannelPostFileInfo& fmpe,int col) const
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str());
|
||||
case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize);
|
||||
case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime);
|
||||
case COLUMN_FILES_FILE: {
|
||||
FileInfo finfo;
|
||||
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
|
||||
return qulonglong(finfo.transfered);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return QString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
return QVariant("[ERROR]");
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,int col) const
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
default:
|
||||
return QVariant::fromValue(fmpe);
|
||||
}
|
||||
}
|
||||
|
||||
void RsGxsChannelPostFilesModel::clear()
|
||||
{
|
||||
preMods();
|
||||
|
||||
initEmptyHierarchy();
|
||||
|
||||
postMods();
|
||||
emit channelLoaded();
|
||||
}
|
||||
|
||||
void RsGxsChannelPostFilesModel::setFiles(const std::list<ChannelPostFileInfo> &files)
|
||||
{
|
||||
preMods();
|
||||
|
||||
beginRemoveRows(QModelIndex(),0,mFilteredFiles.size()-1);
|
||||
endRemoveRows();
|
||||
|
||||
initEmptyHierarchy();
|
||||
|
||||
for(auto& file:files)
|
||||
mFiles.push_back(file);
|
||||
|
||||
for(uint32_t i=0;i<mFiles.size();++i)
|
||||
mFilteredFiles.push_back(i);
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
// debug_dump();
|
||||
#endif
|
||||
|
||||
beginInsertRows(QModelIndex(),0,mFilteredFiles.size()-1);
|
||||
endInsertRows();
|
||||
|
||||
postMods();
|
||||
|
||||
emit channelLoaded();
|
||||
|
||||
if(!files.empty())
|
||||
mTimer->start(5000);
|
||||
else
|
||||
mTimer->stop();
|
||||
}
|
173
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h
Normal file
173
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h *
|
||||
* *
|
||||
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "retroshare/rsfiles.h"
|
||||
#include "retroshare/rsgxscommon.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QColor>
|
||||
|
||||
// This class holds the actual hierarchy of posts, represented by identifiers
|
||||
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
|
||||
// safely access the data.
|
||||
|
||||
// The model contains a post in place 0 that is the parent of all posts.
|
||||
|
||||
typedef uint32_t ChannelPostFilesModelIndex;
|
||||
|
||||
class QTimer;
|
||||
|
||||
// This class contains the info for a file as well as additional info such as publication date
|
||||
|
||||
struct ChannelPostFileInfo: public RsGxsFile
|
||||
{
|
||||
ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t)
|
||||
: RsGxsFile(gxs_file),mPublishTime(t)
|
||||
{}
|
||||
|
||||
ChannelPostFileInfo() : mPublishTime(0) {}
|
||||
|
||||
rstime_t mPublishTime;
|
||||
};
|
||||
|
||||
// This class is the item model used by Qt to display the information
|
||||
|
||||
class RsGxsChannelPostFilesModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RsGxsChannelPostFilesModel(QObject *parent = NULL);
|
||||
~RsGxsChannelPostFilesModel(){}
|
||||
|
||||
enum Columns {
|
||||
COLUMN_FILES_NAME = 0x00,
|
||||
COLUMN_FILES_SIZE = 0x01,
|
||||
COLUMN_FILES_FILE = 0x02,
|
||||
COLUMN_FILES_DATE = 0x03,
|
||||
COLUMN_FILES_NB_COLUMNS = 0x04
|
||||
};
|
||||
|
||||
enum Roles{ SortRole = Qt::UserRole+1,
|
||||
FilterRole = Qt::UserRole+2,
|
||||
};
|
||||
|
||||
#ifdef TODO
|
||||
enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00,
|
||||
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
|
||||
};
|
||||
#endif
|
||||
|
||||
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
|
||||
|
||||
// This method will asynchroneously update the data
|
||||
void setFiles(const std::list<ChannelPostFileInfo>& files);
|
||||
void setFilter(const QStringList &strings, uint32_t &count) ;
|
||||
|
||||
#ifdef TODO
|
||||
QModelIndex getIndexOfFile(const RsFileHash& hash) const;
|
||||
void setSortMode(SortMode mode) ;
|
||||
|
||||
void setTextColorRead (QColor color) { mTextColorRead = color;}
|
||||
void setTextColorUnread (QColor color) { mTextColorUnread = color;}
|
||||
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
|
||||
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
|
||||
void setTextColorMissing (QColor color) { mTextColorMissing = color;}
|
||||
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
|
||||
#endif
|
||||
|
||||
// Helper functions
|
||||
|
||||
void clear() ;
|
||||
|
||||
// AbstractItemModel functions.
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
// Custom item roles
|
||||
|
||||
QVariant sizeHintRole (int col) const;
|
||||
QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const;
|
||||
QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const;
|
||||
QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const;
|
||||
QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const;
|
||||
QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const;
|
||||
#ifdef TODO
|
||||
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief debug_dump
|
||||
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
|
||||
*/
|
||||
void debug_dump();
|
||||
|
||||
signals:
|
||||
void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI.
|
||||
|
||||
private slots:
|
||||
void update();
|
||||
|
||||
private:
|
||||
#ifdef TODO
|
||||
bool mUseChildTS;
|
||||
bool mFilteringEnabled;
|
||||
SortMode mSortMode;
|
||||
#endif
|
||||
void preMods() ;
|
||||
void postMods() ;
|
||||
|
||||
quintptr getParentRow(quintptr ref,int& row) const;
|
||||
quintptr getChildRef(quintptr ref, int index) const;
|
||||
int getChildrenCount(quintptr ref) const;
|
||||
bool getFileData(const QModelIndex& i, ChannelPostFileInfo &fmpe) const;
|
||||
|
||||
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref);
|
||||
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
|
||||
|
||||
#ifdef TODO
|
||||
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
|
||||
#endif
|
||||
void initEmptyHierarchy();
|
||||
|
||||
std::vector<int> mFilteredFiles ; // store the list of files for the post
|
||||
std::vector<ChannelPostFileInfo> mFiles ; // store the list of files for the post
|
||||
|
||||
QTimer *mTimer;
|
||||
};
|
125
retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h
Normal file
125
retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h *
|
||||
* *
|
||||
* Copyright 2020 by Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
|
||||
#include "retroshare/rsgxschannels.h"
|
||||
#include "retroshare/rsidentity.h"
|
||||
|
||||
#include "gui/gxs/GxsIdDetails.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
|
||||
// Class to paint the thumbnails with title
|
||||
|
||||
class ChannelPostThumbnailView: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen.
|
||||
static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0;
|
||||
|
||||
// Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good.
|
||||
|
||||
static const int THUMBNAIL_W = 4;
|
||||
static const int THUMBNAIL_H = 6;
|
||||
|
||||
static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png";
|
||||
|
||||
virtual ~ChannelPostThumbnailView()
|
||||
{
|
||||
delete lb;
|
||||
delete lt;
|
||||
}
|
||||
|
||||
ChannelPostThumbnailView(QWidget *parent=NULL): QWidget(parent)
|
||||
{
|
||||
init(FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE), QString("New post"),false);
|
||||
}
|
||||
|
||||
ChannelPostThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL)
|
||||
: QWidget(parent)
|
||||
{
|
||||
// now fill the data
|
||||
|
||||
QPixmap thumbnail;
|
||||
|
||||
if(post.mThumbnail.mSize > 0)
|
||||
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL);
|
||||
else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row)
|
||||
thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE);
|
||||
|
||||
init(thumbnail, QString::fromUtf8(post.mMeta.mMsgName.c_str()), IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) );
|
||||
|
||||
}
|
||||
|
||||
void init(const QPixmap& thumbnail,const QString& msg,bool is_msg_new)
|
||||
{
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
||||
lb = new QLabel(this);
|
||||
lb->setScaledContents(true);
|
||||
layout->addWidget(lb);
|
||||
|
||||
lt = new QLabel(this);
|
||||
layout->addWidget(lt);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
|
||||
|
||||
QFontMetricsF fm(font());
|
||||
int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ;
|
||||
int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ;
|
||||
|
||||
lb->setFixedSize(W,H);
|
||||
lb->setPixmap(thumbnail);
|
||||
|
||||
lt->setText(msg);
|
||||
|
||||
QFont font = lt->font();
|
||||
|
||||
if(is_msg_new)
|
||||
{
|
||||
font.setBold(true);
|
||||
lt->setFont(font);
|
||||
}
|
||||
|
||||
lt->setMaximumWidth(W);
|
||||
lt->setWordWrap(true);
|
||||
|
||||
adjustSize();
|
||||
update();
|
||||
}
|
||||
|
||||
void setPixmap(const QPixmap& p) { lb->setPixmap(p); }
|
||||
void setText(const QString& s) { lt->setText(s); }
|
||||
|
||||
private:
|
||||
QLabel *lb;
|
||||
QLabel *lt;
|
||||
};
|
||||
|
736
retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp
Normal file
736
retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp
Normal file
|
@ -0,0 +1,736 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp *
|
||||
* *
|
||||
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFontMetrics>
|
||||
#include <QModelIndex>
|
||||
#include <QIcon>
|
||||
|
||||
#include "retroshare/rsgxsflags.h"
|
||||
#include "retroshare/rsgxschannels.h"
|
||||
#include "retroshare/rsexpr.h"
|
||||
|
||||
#include "gui/common/FilesDefs.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
#include "util/HandleRichText.h"
|
||||
#include "util/DateTime.h"
|
||||
|
||||
#include "GxsChannelPostsModel.h"
|
||||
#include "GxsChannelPostFilesModel.h"
|
||||
|
||||
//#define DEBUG_CHANNEL_MODEL
|
||||
|
||||
Q_DECLARE_METATYPE(RsMsgMetaData)
|
||||
|
||||
Q_DECLARE_METATYPE(RsGxsChannelPost)
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
|
||||
|
||||
RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent)
|
||||
: QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6)
|
||||
{
|
||||
initEmptyHierarchy();
|
||||
|
||||
mEventHandlerId = 0;
|
||||
// Needs to be asynced because this function is called by another thread!
|
||||
|
||||
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
|
||||
}, mEventHandlerId, RsEventType::GXS_CHANNELS );
|
||||
}
|
||||
|
||||
RsGxsChannelPostsModel::~RsGxsChannelPostsModel()
|
||||
{
|
||||
rsEvents->unregisterEventsHandler(mEventHandlerId);
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
|
||||
{
|
||||
const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get());
|
||||
|
||||
if(!e)
|
||||
return;
|
||||
|
||||
switch(e->mChannelEventCode)
|
||||
{
|
||||
case RsChannelEventCode::UPDATED_MESSAGE:
|
||||
case RsChannelEventCode::READ_STATUS_CHANGED:
|
||||
{
|
||||
// Normally we should just emit dataChanged() on the index of the data that has changed:
|
||||
//
|
||||
// We need to update the data!
|
||||
|
||||
if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId)
|
||||
RsThread::async([this, e]()
|
||||
{
|
||||
// 1 - get message data from p3GxsChannels
|
||||
|
||||
std::vector<RsGxsChannelPost> posts;
|
||||
std::vector<RsGxsComment> comments;
|
||||
std::vector<RsGxsVote> votes;
|
||||
|
||||
if(!rsGxsChannels->getChannelContent(mChannelGroup.mMeta.mGroupId,std::set<RsGxsMessageId>{ e->mChannelMsgId }, posts,comments,votes))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << e->mChannelGroupId << "/" << e->mChannelMsgId << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 - update the model in the UI thread.
|
||||
|
||||
RsQThreadUtils::postToObject( [posts,comments,votes,this]()
|
||||
{
|
||||
for(uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
// linear search. Not good at all, but normally this is for a single post.
|
||||
|
||||
for(uint32_t j=0;j<mPosts.size();++j)
|
||||
if(mPosts[j].mMeta.mMsgId == posts[i].mMeta.mMsgId)
|
||||
{
|
||||
mPosts[j] = posts[i];
|
||||
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL));
|
||||
}
|
||||
}
|
||||
},this);
|
||||
});
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::initEmptyHierarchy()
|
||||
{
|
||||
preMods();
|
||||
|
||||
mPosts.clear();
|
||||
mFilteredPosts.clear();
|
||||
// mPosts.resize(1); // adds a sentinel item
|
||||
// mPosts[0].mMeta.mMsgName = "Root sentinel post" ;
|
||||
// mFilteredPosts.resize(1);
|
||||
// mFilteredPosts[0] = 1;
|
||||
|
||||
postMods();
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::preMods()
|
||||
{
|
||||
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
|
||||
|
||||
beginResetModel();
|
||||
}
|
||||
void RsGxsChannelPostsModel::postMods()
|
||||
{
|
||||
endResetModel();
|
||||
|
||||
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL));
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::getFilesList(std::list<ChannelPostFileInfo>& files)
|
||||
{
|
||||
// We use an intermediate map so as to remove duplicates
|
||||
|
||||
std::map<RsFileHash,ChannelPostFileInfo> files_map;
|
||||
|
||||
for(uint32_t i=0;i<mPosts.size();++i)
|
||||
for(auto& file:mPosts[i].mFiles)
|
||||
files_map.insert(std::make_pair(file.mHash,ChannelPostFileInfo(file,mPosts[i].mMeta.mPublishTs)));
|
||||
|
||||
files.clear();
|
||||
|
||||
for(auto& it:files_map)
|
||||
files.push_back(it.second);
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::setFilter(const QStringList& strings, uint32_t& count)
|
||||
{
|
||||
preMods();
|
||||
|
||||
beginRemoveRows(QModelIndex(),0,rowCount()-1);
|
||||
endRemoveRows();
|
||||
|
||||
if(strings.empty())
|
||||
{
|
||||
mFilteredPosts.clear();
|
||||
for(int i=0;i<mPosts.size();++i)
|
||||
mFilteredPosts.push_back(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
mFilteredPosts.clear();
|
||||
//mFilteredPosts.push_back(0);
|
||||
|
||||
for(int i=0;i<mPosts.size();++i)
|
||||
{
|
||||
bool passes_strings = true;
|
||||
|
||||
for(auto& s:strings)
|
||||
passes_strings = passes_strings && QString::fromStdString(mPosts[i].mMeta.mMsgName).contains(s,Qt::CaseInsensitive);
|
||||
|
||||
if(passes_strings)
|
||||
mFilteredPosts.push_back(i);
|
||||
}
|
||||
}
|
||||
count = mFilteredPosts.size();
|
||||
|
||||
std::cerr << "After filtering: " << count << " posts remain." << std::endl;
|
||||
|
||||
beginInsertRows(QModelIndex(),0,rowCount()-1);
|
||||
endInsertRows();
|
||||
|
||||
postMods();
|
||||
}
|
||||
|
||||
int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if(parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if(mFilteredPosts.empty()) // security. Should never happen.
|
||||
return 0;
|
||||
|
||||
if(!parent.isValid())
|
||||
return (mFilteredPosts.size() + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
|
||||
|
||||
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return std::min((int)mFilteredPosts.size(),(int)mColumns) ;
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const
|
||||
{
|
||||
if(!i.isValid())
|
||||
return true;
|
||||
|
||||
quintptr ref = i.internalId();
|
||||
uint32_t entry = 0;
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
|
||||
return false ;
|
||||
|
||||
fmpe = mPosts[mFilteredPosts[entry]];
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostsModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
if(!parent.isValid())
|
||||
return true;
|
||||
|
||||
return false; // by default, no channel post has children
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostsModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref)
|
||||
{
|
||||
// the pointer is formed the following way:
|
||||
//
|
||||
// [ 32 bits ]
|
||||
//
|
||||
// This means that the whole software has the following build-in limitation:
|
||||
// * 4 B simultaenous posts. Should be enough !
|
||||
|
||||
ref = (intptr_t)(entry+1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RsGxsChannelPostsModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry)
|
||||
{
|
||||
intptr_t val = (intptr_t)ref;
|
||||
|
||||
if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious
|
||||
{
|
||||
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl;
|
||||
return false ;
|
||||
}
|
||||
if(val==0)
|
||||
{
|
||||
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of index 0." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
entry = val - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex & parent) const
|
||||
{
|
||||
if(row < 0 || column < 0 || column >= mColumns)
|
||||
return QModelIndex();
|
||||
|
||||
quintptr ref = getChildRef(parent.internalId(),column + row*mColumns);
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
|
||||
#endif
|
||||
return createIndex(row,column,ref) ;
|
||||
}
|
||||
|
||||
QModelIndex RsGxsChannelPostsModel::parent(const QModelIndex& index) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
return QModelIndex(); // there's no hierarchy here. So nothing to do!
|
||||
}
|
||||
|
||||
Qt::ItemFlags RsGxsChannelPostsModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
|
||||
return QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::setNumColumns(int n)
|
||||
{
|
||||
if(n < 1)
|
||||
{
|
||||
RsErr() << __PRETTY_FUNCTION__ << " Attempt to set a number of column of 0. This is wrong." << std::endl;
|
||||
return;
|
||||
}
|
||||
preMods();
|
||||
|
||||
beginRemoveRows(QModelIndex(),0,rowCount()-1);
|
||||
endRemoveRows();
|
||||
|
||||
mColumns = n;
|
||||
|
||||
beginInsertRows(QModelIndex(),0,rowCount()-1);
|
||||
endInsertRows();
|
||||
|
||||
postMods();
|
||||
}
|
||||
|
||||
quintptr RsGxsChannelPostsModel::getChildRef(quintptr ref,int index) const
|
||||
{
|
||||
if (index < 0)
|
||||
return 0;
|
||||
|
||||
if(ref == quintptr(0))
|
||||
{
|
||||
quintptr new_ref;
|
||||
convertTabEntryToRefPointer(index,new_ref);
|
||||
return new_ref;
|
||||
}
|
||||
else
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
quintptr RsGxsChannelPostsModel::getParentRow(quintptr ref,int& row) const
|
||||
{
|
||||
ChannelPostsModelIndex ref_entry;
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredPosts.size())
|
||||
return 0 ;
|
||||
|
||||
if(ref_entry == 0)
|
||||
{
|
||||
RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl;
|
||||
row = 0;
|
||||
}
|
||||
else
|
||||
row = ref_entry-1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const
|
||||
{
|
||||
uint32_t entry = 0 ;
|
||||
|
||||
if(ref == quintptr(0))
|
||||
return rowCount()-1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
|
||||
#endif
|
||||
|
||||
if(!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
switch(role)
|
||||
{
|
||||
case Qt::SizeHintRole: return sizeHintRole(index.column()) ;
|
||||
case Qt::StatusTipRole:return QVariant();
|
||||
default: break;
|
||||
}
|
||||
|
||||
quintptr ref = (index.isValid())?index.internalId():0 ;
|
||||
uint32_t entry = 0;
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "data(" << index << ")" ;
|
||||
#endif
|
||||
|
||||
if(!ref)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " [empty]" << std::endl;
|
||||
#endif
|
||||
return QVariant() ;
|
||||
}
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
|
||||
#endif
|
||||
return QVariant() ;
|
||||
}
|
||||
|
||||
const RsGxsChannelPost& fmpe(mPosts[mFilteredPosts[entry]]);
|
||||
|
||||
switch(role)
|
||||
{
|
||||
case Qt::DisplayRole: return displayRole (fmpe,index.column()) ;
|
||||
case Qt::UserRole: return userRole (fmpe,index.column()) ;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostsModel::sizeHintRole(int col) const
|
||||
{
|
||||
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
|
||||
|
||||
return QVariant( QSize(factor * 170, factor*14 ));
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostsModel::displayRole(const RsGxsChannelPost& fmpe,int col) const
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
default:
|
||||
return QString::fromUtf8(fmpe.mMeta.mMsgName.c_str());
|
||||
}
|
||||
|
||||
|
||||
return QVariant("[ERROR]");
|
||||
}
|
||||
|
||||
QVariant RsGxsChannelPostsModel::userRole(const RsGxsChannelPost& fmpe,int col) const
|
||||
{
|
||||
switch(col)
|
||||
{
|
||||
default:
|
||||
return QVariant::fromValue(fmpe);
|
||||
}
|
||||
}
|
||||
|
||||
const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const
|
||||
{
|
||||
return mChannelGroup.mMeta.mGroupId;
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::updateChannel(const RsGxsGroupId& channel_group_id)
|
||||
{
|
||||
if(channel_group_id.isNull())
|
||||
return;
|
||||
|
||||
update_posts(channel_group_id);
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::clear()
|
||||
{
|
||||
preMods();
|
||||
|
||||
mPosts.clear();
|
||||
initEmptyHierarchy();
|
||||
|
||||
postMods();
|
||||
emit channelPostsLoaded();
|
||||
}
|
||||
|
||||
bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2)
|
||||
{
|
||||
return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs;
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost>& posts)
|
||||
{
|
||||
preMods();
|
||||
|
||||
beginRemoveRows(QModelIndex(),0,rowCount()-1);
|
||||
endRemoveRows();
|
||||
|
||||
mPosts.clear();
|
||||
mChannelGroup = group;
|
||||
|
||||
createPostsArray(posts);
|
||||
|
||||
std::sort(mPosts.begin(),mPosts.end());
|
||||
|
||||
mFilteredPosts.clear();
|
||||
for(int i=0;i<mPosts.size();++i)
|
||||
mFilteredPosts.push_back(i);
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
// debug_dump();
|
||||
#endif
|
||||
|
||||
beginInsertRows(QModelIndex(),0,rowCount()-1);
|
||||
endInsertRows();
|
||||
|
||||
postMods();
|
||||
|
||||
emit channelPostsLoaded();
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id)
|
||||
{
|
||||
if(group_id.isNull())
|
||||
return;
|
||||
|
||||
RsThread::async([this, group_id]()
|
||||
{
|
||||
// 1 - get message data from p3GxsChannels
|
||||
|
||||
std::list<RsGxsGroupId> channelIds;
|
||||
std::vector<RsMsgMetaData> msg_metas;
|
||||
std::vector<RsGxsChannelGroup> groups;
|
||||
|
||||
channelIds.push_back(group_id);
|
||||
|
||||
if(!rsGxsChannels->getChannelsInfo(channelIds,groups) || groups.size() != 1)
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel group info for channel " << group_id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
RsGxsChannelGroup group = groups[0];
|
||||
|
||||
// We use the heap because the arrays need to be stored accross async
|
||||
|
||||
std::vector<RsGxsChannelPost> *posts = new std::vector<RsGxsChannelPost>();
|
||||
std::vector<RsGxsComment> *comments = new std::vector<RsGxsComment>();
|
||||
std::vector<RsGxsVote> *votes = new std::vector<RsGxsVote>();
|
||||
|
||||
if(!rsGxsChannels->getChannelAllContent(group_id, *posts,*comments,*votes))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel messages for channel " << group_id << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 - update the model in the UI thread.
|
||||
|
||||
RsQThreadUtils::postToObject( [group,posts,comments,votes,this]()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete, note that
|
||||
* Qt::QueuedConnection is important!
|
||||
*/
|
||||
|
||||
setPosts(group,*posts) ;
|
||||
|
||||
delete posts;
|
||||
delete comments;
|
||||
delete votes;
|
||||
|
||||
}, this );
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
static bool decreasing_time_comp(const std::pair<time_t,RsGxsMessageId>& e1,const std::pair<time_t,RsGxsMessageId>& e2) { return e2.first < e1.first ; }
|
||||
|
||||
void RsGxsChannelPostsModel::createPostsArray(std::vector<RsGxsChannelPost>& posts)
|
||||
{
|
||||
// collect new versions of posts if any
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Inserting channel posts" << std::endl;
|
||||
#endif
|
||||
|
||||
std::vector<uint32_t> new_versions ;
|
||||
for (uint32_t i=0;i<posts.size();++i)
|
||||
{
|
||||
if(posts[i].mMeta.mOrigMsgId == posts[i].mMeta.mMsgId)
|
||||
posts[i].mMeta.mOrigMsgId.clear();
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " " << i << ": name=\"" << posts[i].mMeta.mMsgName << "\" msg_id=" << posts[i].mMeta.mMsgId << ": orig msg id = " << posts[i].mMeta.mOrigMsgId << std::endl;
|
||||
#endif
|
||||
|
||||
if(!posts[i].mMeta.mOrigMsgId.isNull())
|
||||
new_versions.push_back(i) ;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "New versions: " << new_versions.size() << std::endl;
|
||||
#endif
|
||||
|
||||
if(!new_versions.empty())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " New versions present. Replacing them..." << std::endl;
|
||||
std::cerr << " Creating search map." << std::endl;
|
||||
#endif
|
||||
|
||||
// make a quick search map
|
||||
std::map<RsGxsMessageId,uint32_t> search_map ;
|
||||
for (uint32_t i=0;i<posts.size();++i)
|
||||
search_map[posts[i].mMeta.mMsgId] = i ;
|
||||
|
||||
for(uint32_t i=0;i<new_versions.size();++i)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " Taking care of new version at index " << new_versions[i] << std::endl;
|
||||
#endif
|
||||
|
||||
uint32_t current_index = new_versions[i] ;
|
||||
uint32_t source_index = new_versions[i] ;
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
RsGxsMessageId source_msg_id = posts[source_index].mMeta.mMsgId ;
|
||||
#endif
|
||||
|
||||
// What we do is everytime we find a replacement post, we climb up the replacement graph until we find the original post
|
||||
// (or the most recent version of it). When we reach this post, we replace it with the data of the source post.
|
||||
// In the mean time, all other posts have their MsgId cleared, so that the posts are removed from the list.
|
||||
|
||||
//std::vector<uint32_t> versions ;
|
||||
std::map<RsGxsMessageId,uint32_t>::const_iterator vit ;
|
||||
|
||||
while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId)))
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ;
|
||||
#endif
|
||||
|
||||
// Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple
|
||||
// times. In this case, we only need to replace the post with the newest version
|
||||
|
||||
//uint32_t prev_index = current_index ;
|
||||
current_index = vit->second ;
|
||||
|
||||
if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further.
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " already erased. Stopping." << std::endl;
|
||||
#endif
|
||||
break ;
|
||||
}
|
||||
|
||||
if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs)
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " and is more recent => following" << std::endl;
|
||||
#endif
|
||||
for(std::set<RsGxsMessageId>::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt)
|
||||
posts[source_index].mOlderVersions.insert(*itt);
|
||||
|
||||
posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId);
|
||||
posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored
|
||||
}
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
else
|
||||
std::cerr << " but is older -> Stopping" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << "Now adding " << posts.size() << " posts into array structure..." << std::endl;
|
||||
#endif
|
||||
|
||||
mPosts.clear();
|
||||
|
||||
for (std::vector<RsGxsChannelPost>::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it)
|
||||
{
|
||||
if(!(*it).mMeta.mMsgId.isNull())
|
||||
{
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
std::cerr << " adding post \"" << (*it).mMeta.mMsgName << "\"" << std::endl;
|
||||
#endif
|
||||
mPosts.push_back(*it);
|
||||
}
|
||||
#ifdef DEBUG_CHANNEL_MODEL
|
||||
else
|
||||
std::cerr << " skipped older version post \"" << (*it).mMeta.mMsgName << "\"" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_status,bool with_children)
|
||||
{
|
||||
if(!i.isValid())
|
||||
return ;
|
||||
|
||||
// no need to call preMods()/postMods() here because we'renot changing the model
|
||||
|
||||
quintptr ref = i.internalId();
|
||||
uint32_t entry = 0;
|
||||
|
||||
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size())
|
||||
return ;
|
||||
|
||||
#warning TODO
|
||||
// bool has_unread_below, has_read_below;
|
||||
// recursSetMsgReadStatus(entry,read_status,with_children) ;
|
||||
// recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below);
|
||||
}
|
||||
|
||||
QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) const
|
||||
{
|
||||
// Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map.
|
||||
|
||||
RsGxsMessageId postId = mid;
|
||||
|
||||
for(uint32_t i=0;i<mFilteredPosts.size();++i)
|
||||
{
|
||||
// First look into msg versions, in case the msg is a version of an existing message
|
||||
|
||||
for(auto& msg_id:mPosts[mFilteredPosts[i]].mOlderVersions)
|
||||
if(msg_id == postId)
|
||||
{
|
||||
quintptr ref ;
|
||||
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab
|
||||
|
||||
return createIndex(i/mColumns,i%mColumns, ref);
|
||||
}
|
||||
|
||||
if(mPosts[mFilteredPosts[i]].mMeta.mMsgId == postId)
|
||||
{
|
||||
quintptr ref ;
|
||||
convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab
|
||||
|
||||
return createIndex(i/mColumns,i%mColumns, ref);
|
||||
}
|
||||
}
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
239
retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h
Normal file
239
retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h *
|
||||
* *
|
||||
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include "retroshare/rsgxschannels.h"
|
||||
#include "retroshare/rsgxsifacetypes.h"
|
||||
#include "retroshare/rsevents.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QColor>
|
||||
|
||||
struct ChannelPostFileInfo;
|
||||
|
||||
// This class holds the actual hierarchy of posts, represented by identifiers
|
||||
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
|
||||
// safely access the data.
|
||||
|
||||
// The model contains a post in place 0 that is the parent of all posts.
|
||||
|
||||
typedef uint32_t ChannelPostsModelIndex;
|
||||
|
||||
// struct ChannelPostsModelPostEntry
|
||||
// {
|
||||
// ChannelPostsModelPostEntry() : mPublishTs(0),mPostFlags(0),mMsgStatus(0),prow(0) {}
|
||||
//
|
||||
// enum { // flags for display of posts. To be used in mPostFlags
|
||||
// FLAG_POST_IS_PINNED = 0x0001,
|
||||
// FLAG_POST_IS_MISSING = 0x0002,
|
||||
// FLAG_POST_IS_REDACTED = 0x0004,
|
||||
// FLAG_POST_HAS_UNREAD_CHILDREN = 0x0008,
|
||||
// FLAG_POST_HAS_READ_CHILDREN = 0x0010,
|
||||
// FLAG_POST_PASSES_FILTER = 0x0020,
|
||||
// FLAG_POST_CHILDREN_PASSES_FILTER = 0x0040,
|
||||
// };
|
||||
//
|
||||
// std::string mTitle ;
|
||||
// RsGxsMessageId mMsgId;
|
||||
// uint32_t mPublishTs;
|
||||
// uint32_t mPostFlags;
|
||||
// int mMsgStatus;
|
||||
//
|
||||
// int prow ;// parent row, which basically means position in the array of posts
|
||||
// };
|
||||
|
||||
// This class is the item model used by Qt to display the information
|
||||
|
||||
class RsGxsChannelPostsModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RsGxsChannelPostsModel(QObject *parent = NULL);
|
||||
virtual ~RsGxsChannelPostsModel() override;
|
||||
|
||||
static const int COLUMN_THREAD_NB_COLUMNS = 0x01;
|
||||
|
||||
#ifdef TODO
|
||||
enum Columns {
|
||||
COLUMN_THREAD_TITLE =0x00,
|
||||
COLUMN_THREAD_READ =0x01,
|
||||
COLUMN_THREAD_DATE =0x02,
|
||||
COLUMN_THREAD_DISTRIBUTION =0x03,
|
||||
COLUMN_THREAD_AUTHOR =0x04,
|
||||
COLUMN_THREAD_CONTENT =0x05,
|
||||
COLUMN_THREAD_MSGID =0x06,
|
||||
COLUMN_THREAD_DATA =0x07,
|
||||
};
|
||||
|
||||
enum Roles{ SortRole = Qt::UserRole+1,
|
||||
ThreadPinnedRole = Qt::UserRole+2,
|
||||
MissingRole = Qt::UserRole+3,
|
||||
StatusRole = Qt::UserRole+4,
|
||||
UnreadChildrenRole = Qt::UserRole+5,
|
||||
FilterRole = Qt::UserRole+6,
|
||||
};
|
||||
#endif
|
||||
|
||||
enum TreeMode{ TREE_MODE_UNKWN = 0x00,
|
||||
TREE_MODE_PLAIN = 0x01,
|
||||
TREE_MODE_FILES = 0x02,
|
||||
};
|
||||
|
||||
#ifdef TODO
|
||||
enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00,
|
||||
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
|
||||
};
|
||||
#endif
|
||||
|
||||
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
|
||||
QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const;
|
||||
|
||||
std::vector<std::pair<time_t,RsGxsMessageId> > getPostVersions(const RsGxsMessageId& mid) const;
|
||||
|
||||
// This method will asynchroneously update the data
|
||||
void updateChannel(const RsGxsGroupId& channel_group_id);
|
||||
const RsGxsGroupId& currentGroupId() const;
|
||||
|
||||
void setNumColumns(int n);
|
||||
|
||||
// Retrieve the full list of files for all posts.
|
||||
|
||||
void getFilesList(std::list<ChannelPostFileInfo> &files);
|
||||
|
||||
#ifdef TODO
|
||||
void setSortMode(SortMode mode) ;
|
||||
|
||||
void setTextColorRead (QColor color) { mTextColorRead = color;}
|
||||
void setTextColorUnread (QColor color) { mTextColorUnread = color;}
|
||||
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
|
||||
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
|
||||
void setTextColorMissing (QColor color) { mTextColorMissing = color;}
|
||||
#endif
|
||||
|
||||
void setMsgReadStatus(const QModelIndex &i, bool read_status, bool with_children);
|
||||
void setFilter(const QStringList &strings, uint32_t &count) ;
|
||||
|
||||
#ifdef TODO
|
||||
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
|
||||
#endif
|
||||
|
||||
// Helper functions
|
||||
|
||||
bool getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const ;
|
||||
void clear() ;
|
||||
|
||||
// AbstractItemModel functions.
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
// Custom item roles
|
||||
|
||||
QVariant sizeHintRole (int col) const;
|
||||
QVariant displayRole (const RsGxsChannelPost& fmpe, int col) const;
|
||||
QVariant toolTipRole (const RsGxsChannelPost& fmpe, int col) const;
|
||||
QVariant userRole (const RsGxsChannelPost& fmpe, int col) const;
|
||||
#ifdef TODO
|
||||
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
|
||||
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief debug_dump
|
||||
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
|
||||
*/
|
||||
void debug_dump();
|
||||
|
||||
signals:
|
||||
void channelPostsLoaded(); // emitted after the posts have been loaded.
|
||||
|
||||
private:
|
||||
RsGxsChannelGroup mChannelGroup;
|
||||
|
||||
#ifdef TODO
|
||||
bool mUseChildTS;
|
||||
bool mFilteringEnabled;
|
||||
SortMode mSortMode;
|
||||
#endif
|
||||
TreeMode mTreeMode;
|
||||
|
||||
uint32_t mColumns;
|
||||
|
||||
void preMods() ;
|
||||
void postMods() ;
|
||||
|
||||
quintptr getParentRow(quintptr ref,int& row) const;
|
||||
quintptr getChildRef(quintptr ref, int index) const;
|
||||
int getChildrenCount(quintptr ref) const;
|
||||
|
||||
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr &ref);
|
||||
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
|
||||
static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry);
|
||||
|
||||
void update_posts(const RsGxsGroupId& group_id);
|
||||
|
||||
#ifdef TODO
|
||||
void setForumMessageSummary(const std::vector<RsGxsForumMsg>& messages);
|
||||
#endif
|
||||
void recursUpdateReadStatusAndTimes(ChannelPostsModelIndex i,bool& has_unread_below,bool& has_read_below);
|
||||
uint32_t recursUpdateFilterStatus(ChannelPostsModelIndex i,int column,const QStringList& strings);
|
||||
void recursSetMsgReadStatus(ChannelPostsModelIndex i,bool read_status,bool with_children);
|
||||
|
||||
#ifdef TODO
|
||||
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
|
||||
#endif
|
||||
//static ChannelModelIndex addEntry(std::vector<RsGxsChannelPost>& posts,const ChannelModelPostEntry& entry,ChannelModelIndex parent);
|
||||
//static void convertMsgToPostEntry(const RsGxsChannelGroup &mChannelGroup, const RsMsgMetaData &msg, bool useChildTS, ChannelModelPostEntry& fentry);
|
||||
|
||||
//void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector<RsMsgMetaData> &msgs_array, std::vector<ChannelPostsModelPostEntry> &posts, std::map<RsGxsMessageId, std::vector<std::pair<time_t, RsGxsMessageId> > > &mPostVersions);
|
||||
void createPostsArray(std::vector<RsGxsChannelPost> &posts);
|
||||
void setPosts(const RsGxsChannelGroup& group, std::vector<RsGxsChannelPost> &posts);
|
||||
void initEmptyHierarchy();
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
||||
std::vector<int> mFilteredPosts; // stores the list of displayes indices due to filtering.
|
||||
std::vector<RsGxsChannelPost> mPosts ; // store the list of posts updated from rsForums.
|
||||
|
||||
//std::map<RsGxsMessageId,std::vector<std::pair<time_t,RsGxsMessageId> > > mPostVersions; // stores versions of posts
|
||||
|
||||
QColor mTextColorRead ;
|
||||
QColor mTextColorUnread ;
|
||||
QColor mTextColorUnreadChildren;
|
||||
QColor mTextColorNotSubscribed ;
|
||||
QColor mTextColorMissing ;
|
||||
|
||||
RsEventsHandlerId_t mEventHandlerId ;
|
||||
friend class const_iterator;
|
||||
};
|
|
@ -551,13 +551,9 @@ void GxsChannelPostsWidget::createPostItem(const RsGxsChannelPost& post, bool re
|
|||
ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs));
|
||||
}
|
||||
|
||||
#ifdef TODO
|
||||
ui->fileWidget->addFiles(post, related);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool related, int current, int count)
|
||||
{
|
||||
/* show fill progress */
|
||||
|
@ -862,11 +858,12 @@ bool GxsChannelPostsWidget::getGroupData(RsGxsGenericGroupData *& data)
|
|||
{
|
||||
RsGxsChannelGroup distant_group;
|
||||
|
||||
if(rsGxsChannels->retrieveDistantGroup(groupId(),distant_group))
|
||||
if(rsGxsChannels->getDistantSearchResultGroupData(groupId(),distant_group))
|
||||
{
|
||||
insertChannelDetails(distant_group);
|
||||
data = new RsGxsChannelGroup(distant_group);
|
||||
data = new RsGxsChannelGroup(distant_group);
|
||||
mGroup = distant_group; // make a local copy to pass on to items
|
||||
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ private:
|
|||
int viewMode();
|
||||
|
||||
void insertChannelDetails(const RsGxsChannelGroup &group);
|
||||
void insertChannelPosts(std::vector<RsGxsChannelPost> &posts, GxsMessageFramePostThread *thread, bool related);
|
||||
void insertChannelPosts(std::vector<RsGxsChannelPost>& posts, GxsMessageFramePostThread *thread, bool related);
|
||||
|
||||
void createPostItem(const RsGxsChannelPost &post, bool related);
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,184 @@
|
|||
/*******************************************************************************
|
||||
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h *
|
||||
* *
|
||||
* Copyright 2013 by Robert Fernie <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef _GXS_CHANNELPOSTSWIDGET_H
|
||||
#define _GXS_CHANNELPOSTSWIDGET_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include "gui/gxs/GxsMessageFramePostWidget.h"
|
||||
#include "gui/feeds/FeedHolder.h"
|
||||
|
||||
namespace Ui {
|
||||
class GxsChannelPostsWidgetWithModel;
|
||||
}
|
||||
|
||||
class GxsChannelPostItem;
|
||||
class QTreeWidgetItem;
|
||||
class QSortFilterProxyModel;
|
||||
class FeedItem;
|
||||
class RsGxsChannelPostsModel;
|
||||
class RsGxsChannelPostFilesModel;
|
||||
class RsGxsChannelPostFilesProxyModel;
|
||||
|
||||
class ChannelPostFilesDelegate: public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChannelPostFilesDelegate(QObject *parent=0) : QStyledItemDelegate(parent){}
|
||||
virtual ~ChannelPostFilesDelegate(){}
|
||||
|
||||
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const override;
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
class ChannelPostDelegate: public QAbstractItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent), mZoom(1.0){}
|
||||
virtual ~ChannelPostDelegate(){}
|
||||
|
||||
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
|
||||
int cellSize(const QFont& font) const;
|
||||
void zoom(bool zoom_or_unzoom) ;
|
||||
private:
|
||||
static constexpr float IMAGE_MARGIN_FACTOR = 1.0;
|
||||
static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ;
|
||||
static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ;
|
||||
static constexpr float IMAGE_ZOOM_FACTOR = 1.0;
|
||||
|
||||
float mZoom; // zoom factor for the whole thumbnail
|
||||
};
|
||||
|
||||
class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/* Filters */
|
||||
enum Filter {
|
||||
FILTER_TITLE = 1,
|
||||
FILTER_MSG = 2,
|
||||
FILTER_FILE_NAME = 3
|
||||
};
|
||||
|
||||
public:
|
||||
/** Default Constructor */
|
||||
GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent = 0);
|
||||
/** Default Destructor */
|
||||
~GxsChannelPostsWidgetWithModel();
|
||||
|
||||
/* GxsMessageFrameWidget */
|
||||
virtual QIcon groupIcon();
|
||||
virtual void groupIdChanged() { updateDisplay(true); }
|
||||
virtual QString groupName(bool) override;
|
||||
virtual bool navigate(const RsGxsMessageId&) override;
|
||||
|
||||
void updateDisplay(bool complete);
|
||||
|
||||
#ifdef TODO
|
||||
/* FeedHolder */
|
||||
virtual QScrollArea *getScrollArea();
|
||||
virtual void deleteFeedItem(FeedItem *feedItem, uint32_t type);
|
||||
virtual void openChat(const RsPeerId& peerId);
|
||||
#endif
|
||||
virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector<RsGxsMessageId> &msg_versions, const RsGxsMessageId &msgId, const QString &title);
|
||||
|
||||
protected:
|
||||
/* GxsMessageFramePostWidget */
|
||||
virtual void groupNameChanged(const QString &name);
|
||||
|
||||
#ifdef TODO
|
||||
virtual bool insertGroupData(const RsGxsGenericGroupData *data) override;
|
||||
#endif
|
||||
virtual bool useThread() { return mUseThread; }
|
||||
virtual void blank() ;
|
||||
|
||||
#ifdef TODO
|
||||
virtual bool getGroupData(RsGxsGenericGroupData *& data) override;
|
||||
virtual void getMsgData(const std::set<RsGxsMessageId>& msgIds,std::vector<RsGxsGenericMsgData*>& posts) override;
|
||||
virtual void getAllMsgData(std::vector<RsGxsGenericMsgData*>& posts) override;
|
||||
virtual void insertPosts(const std::vector<RsGxsGenericMsgData*>& posts) override;
|
||||
virtual void insertAllPosts(const std::vector<RsGxsGenericMsgData*>& posts, GxsMessageFramePostThread *thread) override;
|
||||
#endif
|
||||
|
||||
/* GxsMessageFrameWidget */
|
||||
virtual void setAllMessagesReadDo(bool read, uint32_t &token);
|
||||
|
||||
private slots:
|
||||
void showPostDetails();
|
||||
void updateGroupData();
|
||||
void createMsg();
|
||||
void toggleAutoDownload();
|
||||
void subscribeGroup(bool subscribe);
|
||||
void filterChanged(QString);
|
||||
void setViewMode(int viewMode);
|
||||
void settingsChanged();
|
||||
void handlePostsTreeSizeChange(QSize s);
|
||||
void postChannelPostLoad();
|
||||
void editPost();
|
||||
void postContextMenu(const QPoint&);
|
||||
void copyMessageLink();
|
||||
void updateZoomFactor(bool zoom_or_unzoom);
|
||||
|
||||
public slots:
|
||||
void sortColumnFiles(int col,Qt::SortOrder so);
|
||||
void sortColumnPostFiles(int col,Qt::SortOrder so);
|
||||
|
||||
private:
|
||||
void processSettings(bool load);
|
||||
|
||||
void setAutoDownload(bool autoDl);
|
||||
static bool filterItem(FeedItem *feedItem, const QString &text, int filter);
|
||||
|
||||
int viewMode();
|
||||
|
||||
void insertChannelDetails(const RsGxsChannelGroup &group);
|
||||
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
|
||||
|
||||
private:
|
||||
QAction *mAutoDownloadAction;
|
||||
|
||||
RsGxsChannelGroup mGroup;
|
||||
bool mUseThread;
|
||||
RsEventsHandlerId_t mEventHandlerId ;
|
||||
|
||||
RsGxsChannelPostsModel *mChannelPostsModel;
|
||||
RsGxsChannelPostFilesModel *mChannelPostFilesModel;
|
||||
RsGxsChannelPostFilesModel *mChannelFilesModel;
|
||||
ChannelPostDelegate *mChannelPostsDelegate;
|
||||
|
||||
RsGxsMessageId mSelectedPost;
|
||||
|
||||
/* UI - from Designer */
|
||||
Ui::GxsChannelPostsWidgetWithModel *ui;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,601 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GxsChannelPostsWidgetWithModel</class>
|
||||
<widget class="QWidget" name="GxsChannelPostsWidgetWithModel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>977</width>
|
||||
<height>628</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>4</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="QFrame" name="toolBarFrame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Box</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="SubscribeToolButton" name="subscribeToolButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">Subscribe</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::MenuButtonPopup</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="postButton">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Post to Channel</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add new post</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../icons.qrc">
|
||||
<normaloff>:/icons/png/add.png</normaloff>:/icons/png/add.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="viewModeLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LineEditClear" name="filterLineEdit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Search channels</string>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="channel_TW">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>Channel details</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="StyledElidedLabel" name="channelName_LB">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
<strikeout>false</strikeout>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Channel title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="logoLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="3">
|
||||
<widget class="QLabel" name="infoLastPost">
|
||||
<property name="text">
|
||||
<string notr="true">unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Created:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="GxsIdLabel" name="infoAdministrator">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Distribution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="infoPosts">
|
||||
<property name="text">
|
||||
<string notr="true">0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLabel" name="infoLastPostLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Last Post:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLabel" name="infoDistribution">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Administrator:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<widget class="QLabel" name="infoCreated">
|
||||
<property name="text">
|
||||
<string>unknown</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QLabel" name="infoPostsLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Posts:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="infoDescription">
|
||||
<property name="html">
|
||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_4">
|
||||
<attribute name="title">
|
||||
<string>Posts</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="RSTreeView" name="postsTree">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
<property name="textElideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="details_TW">
|
||||
<property name="font">
|
||||
<font>
|
||||
<kerning>true</kerning>
|
||||
</font>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Details</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="postLogo_LB">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="postName_LB">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="postTime_LB">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="postDetails_TE">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="channelPostFiles_tab">
|
||||
<attribute name="title">
|
||||
<string>Files</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="RSTreeView" name="channelPostFiles_TV">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Comments</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<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="GxsCommentDialog" name="commentsDialog" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_5">
|
||||
<attribute name="title">
|
||||
<string>Files</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="RSTreeView" name="channelFiles_TV">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionFeeds">
|
||||
<property name="text">
|
||||
<string>Feeds</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFiles">
|
||||
<property name="text">
|
||||
<string>Files</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>GxsIdLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>gui/gxs/GxsIdLabel.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>SubscribeToolButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/common/SubscribeToolButton.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>StyledElidedLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>gui/common/StyledElidedLabel.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RSTreeView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>gui/common/RSTreeView.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>GxsCommentDialog</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/gxs/GxsCommentDialog.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LineEditClear</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/common/LineEditClear.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../icons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -669,8 +669,11 @@ QVariant RsGxsForumModel::displayRole(const ForumModelPostEntry& fmpe,int col) c
|
|||
return QVariant(DateTime::formatDateTime(qtime));
|
||||
}
|
||||
|
||||
case COLUMN_THREAD_DISTRIBUTION:
|
||||
case COLUMN_THREAD_AUTHOR:{
|
||||
case COLUMN_THREAD_DISTRIBUTION: // passthrough // handled by delegate.
|
||||
case COLUMN_THREAD_MSGID:
|
||||
return QVariant();
|
||||
case COLUMN_THREAD_AUTHOR:
|
||||
{
|
||||
QString name;
|
||||
RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString());
|
||||
|
||||
|
@ -680,7 +683,6 @@ QVariant RsGxsForumModel::displayRole(const ForumModelPostEntry& fmpe,int col) c
|
|||
return name;
|
||||
return QVariant(tr("[Unknown]"));
|
||||
}
|
||||
case COLUMN_THREAD_MSGID: return QVariant();
|
||||
#ifdef TODO
|
||||
if (filterColumn == COLUMN_THREAD_CONTENT) {
|
||||
// need content for filter
|
||||
|
|
|
@ -123,9 +123,9 @@ public:
|
|||
{
|
||||
default:
|
||||
case 3:
|
||||
case 0: icon = QIcon(IMAGE_VOID); break;
|
||||
case 1: icon = QIcon(IMAGE_WARNING_YELLOW); break;
|
||||
case 2: icon = QIcon(IMAGE_WARNING_RED); break;
|
||||
case 0: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_VOID); break;
|
||||
case 1: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_WARNING_YELLOW); break;
|
||||
case 2: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_WARNING_RED); break;
|
||||
}
|
||||
|
||||
QPixmap pix = icon.pixmap(r.size());
|
||||
|
@ -134,6 +134,13 @@ public:
|
|||
const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2);
|
||||
painter->drawPixmap(r.topLeft() + p, pix);
|
||||
}
|
||||
|
||||
virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
|
||||
{
|
||||
static auto img(FilesDefs::getPixmapFromQtResourcePath(IMAGE_WARNING_YELLOW));
|
||||
|
||||
return QSize(img.width()*1.2,option.rect.height());
|
||||
}
|
||||
};
|
||||
|
||||
class ReadStatusItemDelegate: public QStyledItemDelegate
|
||||
|
@ -172,9 +179,9 @@ public:
|
|||
else
|
||||
{
|
||||
if (unread)
|
||||
icon = QIcon(":/images/message-state-unread.png");
|
||||
icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png");
|
||||
else
|
||||
icon = QIcon(":/images/message-state-read.png");
|
||||
icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png");
|
||||
}
|
||||
|
||||
QPixmap pix = icon.pixmap(r.size());
|
||||
|
@ -183,6 +190,13 @@ public:
|
|||
const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2);
|
||||
painter->drawPixmap(r.topLeft() + p, pix);
|
||||
}
|
||||
|
||||
virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override
|
||||
{
|
||||
static auto img(FilesDefs::getPixmapFromQtResourcePath(":/images/message-state-unread.png"));
|
||||
|
||||
return QSize(img.width()*1.2,option.rect.height());
|
||||
}
|
||||
};
|
||||
|
||||
class ForumPostSortFilterProxyModel: public QSortFilterProxyModel
|
||||
|
@ -299,9 +313,10 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
|
|||
QHeaderView * ttheader = ui->threadTreeWidget->header () ;
|
||||
ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_DATE, 140*f);
|
||||
ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_TITLE, 440*f);
|
||||
ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION, 24*f);
|
||||
ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_AUTHOR, 150*f);
|
||||
ttheader->resizeSection (RsGxsForumModel::COLUMN_THREAD_READ, 24*f);
|
||||
|
||||
ui->threadTreeWidget->resizeColumnToContents(RsGxsForumModel::COLUMN_THREAD_DISTRIBUTION);
|
||||
ui->threadTreeWidget->resizeColumnToContents(RsGxsForumModel::COLUMN_THREAD_READ);
|
||||
|
||||
QHeaderView_setSectionResizeModeColumn(ttheader, RsGxsForumModel::COLUMN_THREAD_TITLE, QHeaderView::Interactive);
|
||||
QHeaderView_setSectionResizeModeColumn(ttheader, RsGxsForumModel::COLUMN_THREAD_DATE, QHeaderView::Interactive);
|
||||
|
@ -462,7 +477,7 @@ QString GxsForumThreadWidget::groupName(bool withUnreadCount)
|
|||
QIcon GxsForumThreadWidget::groupIcon()
|
||||
{
|
||||
if (mNewCount) {
|
||||
return QIcon(":/images/message-state-new.png");
|
||||
return FilesDefs::getIconFromQtResourcePath(":/images/message-state-new.png");
|
||||
}
|
||||
|
||||
return QIcon();
|
||||
|
@ -498,6 +513,7 @@ void GxsForumThreadWidget::recursRestoreExpandedItems(const QModelIndex& /*index
|
|||
ui->threadTreeWidget->setExpanded( mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(*it)) ,true) ;
|
||||
}
|
||||
|
||||
|
||||
void GxsForumThreadWidget::updateDisplay(bool complete)
|
||||
{
|
||||
#ifdef DEBUG_FORUMS
|
||||
|
@ -560,35 +576,35 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
|
|||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Clicked on msg " << current_post.mMsgId << std::endl;
|
||||
#endif
|
||||
QAction *editAct = new QAction(QIcon(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu);
|
||||
QAction *editAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu);
|
||||
connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage()));
|
||||
|
||||
bool is_pinned = mForumGroup.mPinnedPosts.ids.find(mThreadId) != mForumGroup.mPinnedPosts.ids.end();
|
||||
QAction *pinUpPostAct = new QAction(QIcon(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu);
|
||||
QAction *pinUpPostAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu);
|
||||
connect(pinUpPostAct , SIGNAL(triggered()), this, SLOT(togglePinUpPost()));
|
||||
|
||||
QAction *replyAct = new QAction(QIcon(IMAGE_REPLY), tr("Reply"), &contextMnu);
|
||||
QAction *replyAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REPLY), tr("Reply"), &contextMnu);
|
||||
connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage()));
|
||||
|
||||
QAction *replyauthorAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu);
|
||||
QAction *replyauthorAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu);
|
||||
connect(replyauthorAct, SIGNAL(triggered()), this, SLOT(reply_with_private_message()));
|
||||
|
||||
QAction *flagaspositiveAct = new QAction(QIcon(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu);
|
||||
QAction *flagaspositiveAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu);
|
||||
flagaspositiveAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ;
|
||||
flagaspositiveAct->setData(static_cast<uint32_t>(RsOpinion::POSITIVE));
|
||||
connect(flagaspositiveAct, SIGNAL(triggered()), this, SLOT(flagperson()));
|
||||
|
||||
QAction *flagasneutralAct = new QAction(QIcon(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu);
|
||||
QAction *flagasneutralAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu);
|
||||
flagasneutralAct->setToolTip(tr("Doing this, you trust your friends to decide to forward this message or not.")) ;
|
||||
flagasneutralAct->setData(static_cast<uint32_t>(RsOpinion::NEUTRAL));
|
||||
connect(flagasneutralAct, SIGNAL(triggered()), this, SLOT(flagperson()));
|
||||
|
||||
QAction *flagasnegativeAct = new QAction(QIcon(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu);
|
||||
QAction *flagasnegativeAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu);
|
||||
flagasnegativeAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ;
|
||||
flagasnegativeAct->setData(static_cast<uint32_t>(RsOpinion::NEGATIVE));
|
||||
connect(flagasnegativeAct, SIGNAL(triggered()), this, SLOT(flagperson()));
|
||||
|
||||
QAction *newthreadAct = new QAction(QIcon(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu);
|
||||
QAction *newthreadAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu);
|
||||
newthreadAct->setEnabled (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags));
|
||||
connect(newthreadAct , SIGNAL(triggered()), this, SLOT(createthread()));
|
||||
|
||||
|
@ -598,19 +614,19 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
|
|||
QAction* collapseAll = new QAction(tr( "Collapse all"), &contextMnu);
|
||||
connect(collapseAll, SIGNAL(triggered()), ui->threadTreeWidget, SLOT(collapseAll()));
|
||||
|
||||
QAction *markMsgAsRead = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu);
|
||||
QAction *markMsgAsRead = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu);
|
||||
connect(markMsgAsRead, SIGNAL(triggered()), this, SLOT(markMsgAsRead()));
|
||||
|
||||
QAction *markMsgAsReadChildren = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu);
|
||||
QAction *markMsgAsReadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu);
|
||||
connect(markMsgAsReadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsReadChildren()));
|
||||
|
||||
QAction *markMsgAsUnread = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu);
|
||||
QAction *markMsgAsUnread = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu);
|
||||
connect(markMsgAsUnread, SIGNAL(triggered()), this, SLOT(markMsgAsUnread()));
|
||||
|
||||
QAction *markMsgAsUnreadChildren = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu);
|
||||
QAction *markMsgAsUnreadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu);
|
||||
connect(markMsgAsUnreadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsUnreadChildren()));
|
||||
|
||||
QAction *showinpeopleAct = new QAction(QIcon(":/images/info16.png"), tr("Show author in people tab"), &contextMnu);
|
||||
QAction *showinpeopleAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("Show author in people tab"), &contextMnu);
|
||||
connect(showinpeopleAct, SIGNAL(triggered()), this, SLOT(showInPeopleTab()));
|
||||
|
||||
if (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags))
|
||||
|
@ -663,7 +679,7 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
|
|||
|
||||
contextMnu.addAction(replyAct);
|
||||
contextMnu.addAction(newthreadAct);
|
||||
QAction* action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink()));
|
||||
QAction* action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink()));
|
||||
action->setEnabled(!groupId().isNull() && !mThreadId.isNull());
|
||||
contextMnu.addSeparator();
|
||||
contextMnu.addAction(markMsgAsRead);
|
||||
|
@ -755,7 +771,7 @@ void GxsForumThreadWidget::togglethreadview_internal()
|
|||
{
|
||||
// if (ui->expandButton->isChecked()) {
|
||||
ui->postText->setVisible(true);
|
||||
ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png")));
|
||||
ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/edit_remove24.png")));
|
||||
ui->expandButton->setToolTip(tr("Hide"));
|
||||
// } else {
|
||||
// ui->postText->setVisible(false);
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<file>icons/png/anonymous.png</file>
|
||||
<file>icons/png/attach-image.png</file>
|
||||
<file>icons/png/attach.png</file>
|
||||
<file>icons/png/arrow.png</file>
|
||||
<file>icons/png/cert.png</file>
|
||||
<file>icons/png/channels-notify.png</file>
|
||||
<file>icons/png/channels.png</file>
|
||||
|
|
BIN
retroshare-gui/src/gui/icons/png/arrow.png
Normal file
BIN
retroshare-gui/src/gui/icons/png/arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 935 B |
|
@ -745,12 +745,7 @@ GxsForumMsgItem QFrame#frame{
|
|||
background-color: white;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QFrame#infoFrame
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton {
|
||||
font: bold;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
|
@ -759,18 +754,18 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton {
|
|||
max-height: 27px;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton:hover {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:hover {
|
||||
background: #03b1f3;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton:pressed {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:pressed {
|
||||
background: #03b1f3;
|
||||
border-radius: 4px;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:disabled {
|
||||
background: gray;
|
||||
border-radius: 4px;
|
||||
border: 1px solid gray;
|
||||
|
@ -778,19 +773,35 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled {
|
|||
}
|
||||
|
||||
/* only for MenuButtonPopup */
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton[popupMode="1"] {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton[popupMode="1"] {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-arrow {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-arrow {
|
||||
image: none;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-button {
|
||||
GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-button {
|
||||
image: none;
|
||||
|
||||
}
|
||||
|
||||
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator {
|
||||
image: none;
|
||||
}
|
||||
|
||||
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator {
|
||||
image: none;
|
||||
}
|
||||
|
||||
GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
GxsGroupDialog QLabel#groupLogo{
|
||||
border: 2px solid #CCCCCC;
|
||||
border-radius: 3px;
|
||||
|
|
|
@ -84,7 +84,7 @@ ConfCertDialog QLabel#servicePermissionsLabel
|
|||
qproperty-fontSizeFactor: 125;
|
||||
}
|
||||
|
||||
GxsChannelPostsWidget QLabel#nameLabel
|
||||
GxsChannelPostsWidgetWithModel QLabel#channelName_LB
|
||||
{
|
||||
qproperty-fontSizeFactor: 250;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <gui/notifyqt.h>
|
||||
#include "rshare.h"
|
||||
#include "rsharesettings.h"
|
||||
#include "util/i2pcommon.h"
|
||||
#include "util/RsNetUtil.h"
|
||||
#include "util/misc.h"
|
||||
|
||||
|
@ -82,6 +83,10 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags)
|
|||
manager = NULL ;
|
||||
mOngoingConnectivityCheck = -1;
|
||||
|
||||
#ifndef RS_USE_I2P_BOB
|
||||
ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important.
|
||||
#endif
|
||||
|
||||
if(RsAccounts::isHiddenNode())
|
||||
{
|
||||
if(RsAccounts::isTorAuto())
|
||||
|
@ -1352,7 +1357,7 @@ void ServerPage::updateInProxyIndicator()
|
|||
ui.iconlabel_service_incoming->setMovie(movie);
|
||||
movie->start();
|
||||
|
||||
if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enableBob) {
|
||||
if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enable) {
|
||||
|
||||
QTcpSocket tcpSocket;
|
||||
|
||||
|
@ -1439,15 +1444,16 @@ void ServerPage::getNewKey()
|
|||
|
||||
void ServerPage::loadKey()
|
||||
{
|
||||
mBobSettings.keys = ui.pteBobServerKey->toPlainText().toStdString();
|
||||
mBobSettings.addr = p3I2pBob::keyToBase32Addr(mBobSettings.keys);
|
||||
mBobSettings.address.privateKey = ui.pteBobServerKey->toPlainText().toStdString();
|
||||
mBobSettings.address.publicKey = i2p::publicKeyFromPrivate(mBobSettings.address.privateKey);
|
||||
mBobSettings.address.base32 = i2p::keyToBase32Addr(mBobSettings.address.publicKey);
|
||||
|
||||
rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings);
|
||||
}
|
||||
|
||||
void ServerPage::enableBob(bool checked)
|
||||
{
|
||||
mBobSettings.enableBob = checked;
|
||||
mBobSettings.enable = checked;
|
||||
|
||||
rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings);
|
||||
|
||||
|
@ -1487,7 +1493,7 @@ void ServerPage::toggleBobAdvancedSettings(bool checked)
|
|||
{
|
||||
ui.swBobAdvanced->setCurrentIndex(checked ? 1 : 0);
|
||||
|
||||
if (!mBobSettings.keys.empty()) {
|
||||
if (!mBobSettings.address.privateKey.empty()) {
|
||||
if (checked) {
|
||||
ui.pbBobGenAddr->show();
|
||||
} else {
|
||||
|
@ -1578,9 +1584,9 @@ void ServerPage::loadCommon()
|
|||
whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for bob tab
|
||||
|
||||
// don't use whileBlocking here
|
||||
ui.cb_enableBob->setChecked(mBobSettings.enableBob);
|
||||
ui.cb_enableBob->setChecked(mBobSettings.enable);
|
||||
|
||||
if (!mBobSettings.keys.empty()) {
|
||||
if (!mBobSettings.address.privateKey.empty()) {
|
||||
ui.lBobB32Addr->show();
|
||||
ui.leBobB32Addr->show();
|
||||
}
|
||||
|
@ -1623,13 +1629,13 @@ void ServerPage::saveBob()
|
|||
|
||||
void ServerPage::updateStatusBob()
|
||||
{
|
||||
QString addr = QString::fromStdString(mBobSettings.addr);
|
||||
QString addr = QString::fromStdString(mBobSettings.address.base32);
|
||||
if (ui.leBobB32Addr->text() != addr) {
|
||||
ui.leBobB32Addr->setText(addr);
|
||||
ui.hiddenpage_serviceAddress->setText(addr);
|
||||
ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.keys));
|
||||
ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey));
|
||||
|
||||
if (!mBobSettings.keys.empty()) {
|
||||
if (!mBobSettings.address.privateKey.empty()) {
|
||||
// we have an addr -> show fields
|
||||
ui.lBobB32Addr->show();
|
||||
ui.leBobB32Addr->show();
|
||||
|
@ -1655,7 +1661,7 @@ void ServerPage::updateStatusBob()
|
|||
QString bobSimpleText = QString();
|
||||
bobSimpleText.append(tr("RetroShare uses BOB to set up a %1 tunnel at %2:%3 (named %4)\n\n"
|
||||
"When changing options (e.g. port) use the buttons at the bottom to restart BOB.\n\n").
|
||||
arg(mBobSettings.keys.empty() ? tr("client") : tr("server"),
|
||||
arg(mBobSettings.address.privateKey.empty() ? tr("client") : tr("server"),
|
||||
ui.hiddenpage_proxyAddress_i2p_2->text(),
|
||||
ui.hiddenpage_proxyPort_i2p_2->text(),
|
||||
bs.tunnelName.empty() ? tr("unknown") :
|
||||
|
@ -1777,15 +1783,15 @@ void ServerPage::updateStatusBob()
|
|||
|
||||
void ServerPage::setUpBobElements()
|
||||
{
|
||||
ui.gbBob->setEnabled(mBobSettings.enableBob);
|
||||
if (mBobSettings.enableBob) {
|
||||
ui.gbBob->setEnabled(mBobSettings.enable);
|
||||
if (mBobSettings.enable) {
|
||||
ui.hiddenpage_proxyAddress_i2p->setEnabled(false);
|
||||
ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P/BOB settings to change this value");
|
||||
ui.hiddenpage_proxyPort_i2p->setEnabled(false);
|
||||
ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P/BOB settings to change this value");
|
||||
|
||||
ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.addr));
|
||||
ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.keys));
|
||||
ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.address.base32));
|
||||
ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey));
|
||||
|
||||
// cast to int to avoid problems
|
||||
int li, lo, qi, qo, vi, vo;
|
||||
|
|
588
retroshare-gui/src/gui/statistics/GxsIdStatistics.cpp
Normal file
588
retroshare-gui/src/gui/statistics/GxsIdStatistics.cpp
Normal file
|
@ -0,0 +1,588 @@
|
|||
/*******************************************************************************
|
||||
* gui/statistics/GlobalRouterStatistics.cpp *
|
||||
* *
|
||||
* Copyright (c) 2011 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QFontMetrics>
|
||||
#include <QWheelEvent>
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QStylePainter>
|
||||
#include <QLayout>
|
||||
#include <QHeaderView>
|
||||
|
||||
#include <retroshare/rsidentity.h>
|
||||
#include <retroshare/rsservicecontrol.h>
|
||||
|
||||
#include "GxsIdStatistics.h"
|
||||
|
||||
#include "util/DateTime.h"
|
||||
#include "util/QtVersion.h"
|
||||
#include "util/misc.h"
|
||||
#include "util/qtthreadsutils.h"
|
||||
|
||||
static QColor colorScale(float f)
|
||||
{
|
||||
if(f == 0)
|
||||
return QColor::fromHsv(0,0,192) ;
|
||||
else
|
||||
return QColor::fromHsv((int)((1.0-f)*280),200,255) ;
|
||||
}
|
||||
|
||||
GxsIdStatistics::GxsIdStatistics(QWidget *parent)
|
||||
: RsAutoUpdatePage(4000,parent)
|
||||
{
|
||||
setupUi(this) ;
|
||||
|
||||
_stats_F->setWidget(_tst_CW = new GxsIdStatisticsWidget);
|
||||
m_bProcessSettings = false;
|
||||
|
||||
// load settings
|
||||
processSettings(true);
|
||||
}
|
||||
|
||||
GxsIdStatistics::~GxsIdStatistics()
|
||||
{
|
||||
// save settings
|
||||
processSettings(false);
|
||||
}
|
||||
|
||||
void GxsIdStatistics::processSettings(bool bLoad)
|
||||
{
|
||||
m_bProcessSettings = true;
|
||||
|
||||
Settings->beginGroup(QString("GlobalRouterStatistics"));
|
||||
|
||||
if (bLoad) {
|
||||
// load settings
|
||||
|
||||
// state of splitter
|
||||
//splitter->restoreState(Settings->value("Splitter").toByteArray());
|
||||
} else {
|
||||
// save settings
|
||||
|
||||
// state of splitter
|
||||
//Settings->setValue("Splitter", splitter->saveState());
|
||||
|
||||
}
|
||||
|
||||
Settings->endGroup();
|
||||
|
||||
m_bProcessSettings = false;
|
||||
}
|
||||
|
||||
void GxsIdStatistics::updateDisplay()
|
||||
{
|
||||
_tst_CW->updateContent() ;
|
||||
|
||||
static rstime_t last_data_update_time = 0;
|
||||
rstime_t now = time(NULL);
|
||||
|
||||
if(now > last_data_update_time + 60)
|
||||
{
|
||||
last_data_update_time = now;
|
||||
_tst_CW->updateData();
|
||||
}
|
||||
}
|
||||
|
||||
static QString getServiceName(uint32_t s)
|
||||
{
|
||||
switch(s)
|
||||
{
|
||||
default:
|
||||
case 0x0011 /* GOSSIP_DISCOVERY */ : return QObject::tr("Discovery");
|
||||
case 0x0012 /* CHAT */ : return QObject::tr("Chat");
|
||||
case 0x0013 /* MSG */ : return QObject::tr("Messages");
|
||||
case 0x0014 /* TURTLE */ : return QObject::tr("Turtle");
|
||||
case 0x0016 /* HEARTBEAT */ : return QObject::tr("Heartbeat");
|
||||
case 0x0017 /* FILE_TRANSFER */ : return QObject::tr("File transfer");
|
||||
case 0x0018 /* GROUTER */ : return QObject::tr("Global router");
|
||||
case 0x0019 /* FILE_DATABASE */ : return QObject::tr("File database");
|
||||
case 0x0020 /* SERVICEINFO */ : return QObject::tr("Service info");
|
||||
case 0x0021 /* BANDWIDTH_CONTROL */ : return QObject::tr("Bandwidth control");
|
||||
case 0x0022 /* MAIL */ : return QObject::tr("Mail");
|
||||
case 0x0023 /* DIRECT_MAIL */ : return QObject::tr("Mail");
|
||||
case 0x0024 /* DISTANT_MAIL */ : return QObject::tr("Distant mail");
|
||||
case 0x0026 /* SERVICE_CONTROL */ : return QObject::tr("Service control");
|
||||
case 0x0027 /* DISTANT_CHAT */ : return QObject::tr("Distant chat");
|
||||
case 0x0028 /* GXS_TUNNEL */ : return QObject::tr("GXS Tunnel");
|
||||
case 0x0101 /* BANLIST */ : return QObject::tr("Ban list");
|
||||
case 0x0102 /* STATUS */ : return QObject::tr("Status");
|
||||
case 0x0200 /* NXS */ : return QObject::tr("NXS");
|
||||
case 0x0211 /* GXSID */ : return QObject::tr("Identities");
|
||||
case 0x0212 /* PHOTO */ : return QObject::tr("GXS Photo");
|
||||
case 0x0213 /* WIKI */ : return QObject::tr("GXS Wiki");
|
||||
case 0x0214 /* WIRE */ : return QObject::tr("GXS TheWire");
|
||||
case 0x0215 /* FORUMS */ : return QObject::tr("Forums");
|
||||
case 0x0216 /* POSTED */ : return QObject::tr("Boards");
|
||||
case 0x0217 /* CHANNELS */ : return QObject::tr("Channels");
|
||||
case 0x0218 /* GXSCIRCLE */ : return QObject::tr("Circles");
|
||||
/// entiti not gxs, but used with :dentities.
|
||||
case 0x0219 /* REPUTATION */ : return QObject::tr("Reputation");
|
||||
case 0x0220 /* GXS_RECOGN */ : return QObject::tr("Recogn");
|
||||
case 0x0230 /* GXS_TRANS */ : return QObject::tr("GXS Transport");
|
||||
case 0x0240 /* JSONAPI */ : return QObject::tr("JSon API");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static QString getUsageStatisticsName(RsIdentityUsage::UsageCode code)
|
||||
{
|
||||
switch(code)
|
||||
{
|
||||
default:
|
||||
case RsIdentityUsage::UNKNOWN_USAGE : return QObject::tr("Unknown");
|
||||
case RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION : return QObject::tr("Group admin signature creation");
|
||||
case RsIdentityUsage::GROUP_ADMIN_SIGNATURE_VALIDATION : return QObject::tr("Group admin signature validation");
|
||||
case RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION : return QObject::tr("Group author signature creation");
|
||||
case RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION : return QObject::tr("Group author signature validation");
|
||||
case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION : return QObject::tr("Message author signature creation");
|
||||
case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION : return QObject::tr("Message author signature validation");
|
||||
case RsIdentityUsage::GROUP_AUTHOR_KEEP_ALIVE : return QObject::tr("Routine group author signature check.");
|
||||
case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE : return QObject::tr("Routine message author signature check");
|
||||
case RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION : return QObject::tr("Chat room signature validation");
|
||||
case RsIdentityUsage::GLOBAL_ROUTER_SIGNATURE_CHECK : return QObject::tr("Global router message validation");
|
||||
case RsIdentityUsage::GLOBAL_ROUTER_SIGNATURE_CREATION : return QObject::tr("Global router message creation");
|
||||
case RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK : return QObject::tr("DH Key exchange validation for GXS tunnel");
|
||||
case RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CREATION : return QObject::tr("DH Key exchange creation for GXS tunnel");
|
||||
case RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC : return QObject::tr("New identity from GXS sync");
|
||||
case RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY : return QObject::tr("New friend identity from discovery");
|
||||
case RsIdentityUsage::IDENTITY_NEW_FROM_EXPLICIT_REQUEST : return QObject::tr("New identity requested from friend node");
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CHECK : return QObject::tr("Generic signature validation");
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION : return QObject::tr("Generic signature creation");
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION : return QObject::tr("Generic data decryption");
|
||||
case RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION : return QObject::tr("Generic data encryption");
|
||||
case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK : return QObject::tr("Circle membership checking");
|
||||
}
|
||||
}
|
||||
|
||||
void GxsIdStatisticsWidget::updateData()
|
||||
{
|
||||
// get the info, stats, histograms and pass them
|
||||
|
||||
RsThread::async([this]()
|
||||
{
|
||||
// 1 - get group data
|
||||
|
||||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Retrieving post data for post " << mThreadId << std::endl;
|
||||
#endif
|
||||
|
||||
auto pids = new std::list<RsGroupMetaData>() ;
|
||||
rsIdentity->getIdentitiesSummaries(*pids) ;
|
||||
|
||||
RsQThreadUtils::postToObject( [pids,this]()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete */
|
||||
|
||||
const auto& ids(*pids);
|
||||
|
||||
time_t now = time(NULL);
|
||||
mPublishDateHist = Histogram(now - mNbWeeks*7*86400,now,mNbWeeks);
|
||||
mLastUsedHist = Histogram(now - 3600*mNbHours,now,mNbHours);
|
||||
mTotalIdentities = 0;
|
||||
mUsageMap.clear();
|
||||
mPerServiceUsageMap.clear();
|
||||
|
||||
for(auto& meta:ids)
|
||||
{
|
||||
RsIdentityDetails det;
|
||||
|
||||
if(!rsIdentity->getIdDetails(RsGxsId(meta.mGroupId),det))
|
||||
continue;
|
||||
|
||||
mPublishDateHist.insert((double)meta.mPublishTs);
|
||||
mLastUsedHist.insert((double)det.mLastUsageTS);
|
||||
|
||||
for(auto it:det.mUseCases)
|
||||
{
|
||||
auto it2 = mUsageMap.find(it.first.mUsageCode);
|
||||
if(it2 == mUsageMap.end())
|
||||
mUsageMap[it.first.mUsageCode] = 0 ;
|
||||
|
||||
++mUsageMap[it.first.mUsageCode];
|
||||
|
||||
uint32_t s = static_cast<uint32_t>(it.first.mServiceId);
|
||||
auto it3 = mPerServiceUsageMap.find(s);
|
||||
if(it3 == mPerServiceUsageMap.end())
|
||||
mPerServiceUsageMap[s] = 0;
|
||||
|
||||
++mPerServiceUsageMap[s];
|
||||
}
|
||||
|
||||
++mTotalIdentities;
|
||||
}
|
||||
|
||||
delete pids;
|
||||
|
||||
}, this );
|
||||
});
|
||||
}
|
||||
|
||||
void GxsIdStatisticsWidget::updateContent()
|
||||
{
|
||||
// Now draw the info int the widget's pixmap
|
||||
|
||||
float size = QFontMetricsF(font()).height() ;
|
||||
float fact = size/14.0 ;
|
||||
|
||||
QPixmap tmppixmap(mMaxWidth, mMaxHeight);
|
||||
tmppixmap.fill(Qt::transparent);
|
||||
setFixedHeight(mMaxHeight);
|
||||
|
||||
QPainter painter(&tmppixmap);
|
||||
painter.initFrom(this);
|
||||
painter.setPen(QColor::fromRgb(0,0,0)) ;
|
||||
|
||||
QFont times_f(font());//"Times") ;
|
||||
QFont monospace_f("Monospace") ;
|
||||
monospace_f.setStyleHint(QFont::TypeWriter) ;
|
||||
monospace_f.setPointSize(font().pointSize()) ;
|
||||
|
||||
QFontMetricsF fm_monospace(monospace_f) ;
|
||||
QFontMetricsF fm_times(times_f) ;
|
||||
|
||||
int cellx = fm_monospace.width(QString(" ")) ;
|
||||
int celly = fm_monospace.height() ;
|
||||
|
||||
// Display general statistics
|
||||
|
||||
int ox=5*fact,oy=15*fact ;
|
||||
|
||||
painter.setFont(times_f) ;
|
||||
painter.drawText(ox,oy,tr("Total identities: ")+QString::number(mTotalIdentities)) ; oy += celly*2 ;
|
||||
|
||||
uint32_t total_per_type = 0;
|
||||
for(auto it:mUsageMap)
|
||||
total_per_type += it.second;
|
||||
|
||||
painter.setFont(times_f) ;
|
||||
painter.drawText(ox,oy,tr("Usage types") + "(" + QString::number(total_per_type) + " hits): ") ; oy += 2*celly;
|
||||
|
||||
for(auto it:mUsageMap)
|
||||
{
|
||||
painter.drawText(ox+2*cellx,oy, getUsageStatisticsName(it.first) + ": " + QString::number(it.second)) ;
|
||||
oy += celly ;
|
||||
}
|
||||
oy += celly ;
|
||||
|
||||
// Display per-service statistics
|
||||
|
||||
uint32_t total_per_service = 0;
|
||||
for(auto it:mPerServiceUsageMap)
|
||||
total_per_service += it.second;
|
||||
|
||||
painter.setFont(times_f) ;
|
||||
painter.drawText(ox,oy,tr("Usage per service") + "(" + QString::number(total_per_service) + " hits): ") ; oy += 2*celly;
|
||||
|
||||
for(auto it:mPerServiceUsageMap)
|
||||
{
|
||||
painter.drawText(ox+2*cellx,oy, getServiceName(it.first) + ": " + QString::number(it.second)) ;
|
||||
oy += celly ;
|
||||
}
|
||||
oy += celly ;
|
||||
|
||||
// Draw the creation time histogram
|
||||
|
||||
painter.setFont(times_f) ;
|
||||
painter.drawText(ox,oy,tr("Identity age (in weeks):")) ; oy += celly ;
|
||||
|
||||
uint32_t hist_height = 10;
|
||||
oy += hist_height*celly;
|
||||
|
||||
painter.drawLine(QPoint(ox+4*cellx,oy),QPoint(ox+4*cellx+cellx*mNbWeeks*2,oy));
|
||||
painter.drawLine(QPoint(ox+4*cellx,oy),QPoint(ox+4*cellx,oy-celly*hist_height));
|
||||
|
||||
uint32_t max_entry=0;
|
||||
for(int i=0;i<mPublishDateHist.entries().size();++i)
|
||||
max_entry = std::max(max_entry,mPublishDateHist.entries()[i]);
|
||||
|
||||
for(int i=0;i<mPublishDateHist.entries().size();++i)
|
||||
{
|
||||
float h = floor(celly*mPublishDateHist.entries()[i]/(float)max_entry*hist_height);
|
||||
int I = mPublishDateHist.entries().size() - 1 - i;
|
||||
|
||||
painter.fillRect(ox+4*cellx+I*2*cellx+cellx, oy-h, cellx, h,QColor::fromRgbF(0.9,0.6,0.2));
|
||||
painter.setPen(QColor::fromRgb(0,0,0));
|
||||
painter.drawRect(ox+4*cellx+I*2*cellx+cellx, oy-h, cellx, h);
|
||||
}
|
||||
for(int i=0;i<mPublishDateHist.entries().size();++i)
|
||||
{
|
||||
QString txt = QString::number(i);
|
||||
painter.drawText(ox+4*cellx+i*2*cellx+cellx*1.5 - 0.5*fm_times.width(txt),oy+celly,txt);
|
||||
}
|
||||
|
||||
for(int i=0;i<5;++i)
|
||||
{
|
||||
QString txt = QString::number((int)rint(max_entry*i/5.0));
|
||||
painter.drawText(ox + 4*cellx - cellx - fm_times.width(txt),oy - i*hist_height/5.0 * celly,txt );
|
||||
}
|
||||
|
||||
oy += 2*celly;
|
||||
oy += celly;
|
||||
|
||||
// Last used histogram
|
||||
|
||||
painter.setFont(times_f) ;
|
||||
painter.drawText(ox,oy,tr("Last used (hours ago): ")) ; oy += celly ;
|
||||
|
||||
oy += hist_height*celly;
|
||||
|
||||
painter.drawLine(QPoint(ox+4*cellx,oy),QPoint(ox+4*cellx+cellx*mNbHours*2,oy));
|
||||
painter.drawLine(QPoint(ox+4*cellx,oy),QPoint(ox+4*cellx,oy-celly*hist_height));
|
||||
|
||||
max_entry=0;
|
||||
for(int i=0;i<mLastUsedHist.entries().size();++i)
|
||||
max_entry = std::max(max_entry,mLastUsedHist.entries()[i]);
|
||||
|
||||
for(int i=0;i<mLastUsedHist.entries().size();++i)
|
||||
{
|
||||
float h = floor(celly*mLastUsedHist.entries()[i]/(float)max_entry*hist_height);
|
||||
int I = mLastUsedHist.entries().size() - 1 - i;
|
||||
|
||||
painter.fillRect(ox+4*cellx+I*2*cellx+cellx, oy-h, cellx, h,QColor::fromRgbF(0.6,0.9,0.4));
|
||||
painter.setPen(QColor::fromRgb(0,0,0));
|
||||
painter.drawRect(ox+4*cellx+I*2*cellx+cellx, oy-h, cellx, h);
|
||||
}
|
||||
for(int i=0;i<mLastUsedHist.entries().size();++i)
|
||||
{
|
||||
QString txt = QString::number(i);
|
||||
painter.drawText(ox+4*cellx+i*2*cellx+cellx*1.5 - 0.5*fm_times.width(txt),oy+celly,txt);
|
||||
}
|
||||
|
||||
for(int i=0;i<5;++i)
|
||||
{
|
||||
QString txt = QString::number((int)rint(max_entry*i/5.0));
|
||||
painter.drawText(ox + 4*cellx - cellx - fm_times.width(txt),oy - i*hist_height/5.0 * celly,txt );
|
||||
}
|
||||
|
||||
oy += 2*celly;
|
||||
|
||||
|
||||
// set the pixmap
|
||||
|
||||
pixmap = tmppixmap;
|
||||
mMaxHeight = oy;
|
||||
}
|
||||
|
||||
|
||||
GxsIdStatisticsWidget::GxsIdStatisticsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
float size = QFontMetricsF(font()).height() ;
|
||||
float fact = size/14.0 ;
|
||||
|
||||
mNbWeeks = 52;
|
||||
mNbHours = 52;
|
||||
|
||||
mMaxWidth = 400*fact ;
|
||||
mMaxHeight = 0 ;
|
||||
//mCurrentN = PARTIAL_VIEW_SIZE/2+1 ;
|
||||
}
|
||||
|
||||
#ifdef TODO
|
||||
void GxsIdStatisticsWidget::updateContent()
|
||||
{
|
||||
// 1 - get info
|
||||
|
||||
// 2 - draw histograms
|
||||
|
||||
float size = QFontMetricsF(font()).height() ;
|
||||
float fact = size/14.0 ;
|
||||
|
||||
// What do we need to draw?
|
||||
//
|
||||
// Routing matrix
|
||||
// Key [][][][][][][][][][]
|
||||
//
|
||||
// -> each [] shows a square (one per friend node) that is the routing probabilities for all connected friends
|
||||
// computed using the "computeRoutingProbabilitites()" method.
|
||||
//
|
||||
// Own key ids
|
||||
// key service id description
|
||||
//
|
||||
// Data items
|
||||
// Msg id Local origin Destination Time Status
|
||||
//
|
||||
QPixmap tmppixmap(maxWidth, maxHeight);
|
||||
tmppixmap.fill(Qt::transparent);
|
||||
setFixedHeight(maxHeight);
|
||||
|
||||
QPainter painter(&tmppixmap);
|
||||
painter.initFrom(this);
|
||||
painter.setPen(QColor::fromRgb(0,0,0)) ;
|
||||
|
||||
QFont times_f(font());//"Times") ;
|
||||
QFont monospace_f("Monospace") ;
|
||||
monospace_f.setStyleHint(QFont::TypeWriter) ;
|
||||
monospace_f.setPointSize(font().pointSize()) ;
|
||||
|
||||
QFontMetricsF fm_monospace(monospace_f) ;
|
||||
QFontMetricsF fm_times(times_f) ;
|
||||
|
||||
static const int cellx = fm_monospace.width(QString(" ")) ;
|
||||
static const int celly = fm_monospace.height() ;
|
||||
|
||||
maxHeight = 500*fact ;
|
||||
|
||||
// std::cerr << "Drawing into pixmap of size " << maxWidth << "x" << maxHeight << std::endl;
|
||||
// draw...
|
||||
int ox=5*fact,oy=5*fact ;
|
||||
|
||||
|
||||
painter.setFont(times_f) ;
|
||||
painter.drawText(ox,oy+celly,tr("Managed keys")+":" + QString::number(matrix_info.published_keys.size())) ; oy += celly*2 ;
|
||||
|
||||
painter.setFont(monospace_f) ;
|
||||
for(std::map<Sha1CheckSum,RsGRouter::GRouterPublishedKeyInfo>::const_iterator it(matrix_info.published_keys.begin());it!=matrix_info.published_keys.end();++it)
|
||||
{
|
||||
QString packet_string ;
|
||||
packet_string += QString::fromStdString(it->second.authentication_key.toStdString()) ;
|
||||
packet_string += tr(" : Service ID =")+" "+QString::number(it->second.service_id,16) ;
|
||||
packet_string += " \""+QString::fromUtf8(it->second.description_string.c_str()) + "\"" ;
|
||||
|
||||
painter.drawText(ox+2*cellx,oy+celly,packet_string ) ; oy += celly ;
|
||||
}
|
||||
oy += celly ;
|
||||
|
||||
|
||||
std::map<QString, std::vector<QString> > tos ;
|
||||
|
||||
// Now draw the matrix
|
||||
|
||||
QString prob_string ;
|
||||
painter.setFont(times_f) ;
|
||||
QString Q = tr("Routing matrix (") ;
|
||||
|
||||
painter.drawText(ox+0*cellx,oy+fm_times.height(),Q) ;
|
||||
|
||||
// draw scale
|
||||
|
||||
for(int i=0;i<100*fact;++i)
|
||||
{
|
||||
painter.setPen(colorScale(i/100.0/fact)) ;
|
||||
painter.drawLine(ox+fm_times.width(Q)+i,oy+fm_times.height()*0.5,ox+fm_times.width(Q)+i,oy+fm_times.height()) ;
|
||||
}
|
||||
painter.setPen(QColor::fromRgb(0,0,0)) ;
|
||||
|
||||
painter.drawText(ox+fm_times.width(Q) + 102*fact,oy+celly,")") ;
|
||||
|
||||
oy += celly ;
|
||||
oy += celly ;
|
||||
|
||||
//static const int MaxKeySize = 20*fact ;
|
||||
painter.setFont(monospace_f) ;
|
||||
|
||||
int n=0;
|
||||
QString ids;
|
||||
std::vector<float> current_probs ;
|
||||
int current_oy = 0 ;
|
||||
|
||||
mMinWheelZoneX = ox+2*cellx ;
|
||||
mMinWheelZoneY = oy ;
|
||||
|
||||
RsGxsId current_id ;
|
||||
float current_width=0 ;
|
||||
|
||||
for(std::map<GRouterKeyId,std::vector<float> >::const_iterator it(matrix_info.per_friend_probabilities.begin());it!=matrix_info.per_friend_probabilities.end();++it,++n)
|
||||
if(n >= mCurrentN-PARTIAL_VIEW_SIZE/2 && n <= mCurrentN+PARTIAL_VIEW_SIZE/2)
|
||||
{
|
||||
ids = QString::fromStdString(it->first.toStdString())+" : " ;
|
||||
painter.drawText(ox+2*cellx,oy+celly,ids) ;
|
||||
|
||||
for(uint32_t i=0;i<matrix_info.friend_ids.size();++i)
|
||||
painter.fillRect(ox+i*cellx+fm_monospace.width(ids),oy+0.15*celly,cellx,celly,colorScale(it->second[i])) ;
|
||||
|
||||
if(n == mCurrentN)
|
||||
{
|
||||
current_probs = it->second ;
|
||||
current_oy = oy ;
|
||||
current_id = it->first ;
|
||||
current_width = ox+matrix_info.friend_ids.size()*cellx+fm_monospace.width(ids);
|
||||
}
|
||||
|
||||
oy += celly ;
|
||||
}
|
||||
mMaxWheelZoneX = ox+matrix_info.friend_ids.size()*cellx + fm_monospace.width(ids);
|
||||
|
||||
RsIdentityDetails iddetails ;
|
||||
if(rsIdentity->getIdDetails(current_id,iddetails))
|
||||
painter.drawText(current_width+cellx, current_oy+celly, QString::fromUtf8(iddetails.mNickname.c_str())) ;
|
||||
else
|
||||
painter.drawText(current_width+cellx, current_oy+celly, tr("[Unknown identity]")) ;
|
||||
|
||||
mMaxWheelZoneY = oy+celly ;
|
||||
|
||||
painter.setPen(QColor::fromRgb(0,0,0)) ;
|
||||
|
||||
painter.setPen(QColor::fromRgb(127,127,127));
|
||||
painter.drawRect(ox+2*cellx,current_oy+0.15*celly,fm_monospace.width(ids)+cellx*matrix_info.friend_ids.size()- 2*cellx,celly) ;
|
||||
|
||||
float total_length = (matrix_info.friend_ids.size()+2)*cellx ;
|
||||
|
||||
if(!current_probs.empty())
|
||||
for(uint32_t i=0;i<matrix_info.friend_ids.size();++i)
|
||||
{
|
||||
float x1 = ox+(i+0.5)*cellx+fm_monospace.width(ids) ;
|
||||
float y1 = oy+0.15*celly ;
|
||||
float y2 = y1+(matrix_info.friend_ids.size()-1-i+1)*celly;
|
||||
|
||||
RsPeerDetails peer_ssl_details;
|
||||
rsPeers->getPeerDetails(matrix_info.friend_ids[i], peer_ssl_details);
|
||||
|
||||
painter.drawLine(x1,y1,x1,y2);
|
||||
painter.drawLine(x1,y2,x1 + total_length - i*cellx,y2) ;
|
||||
painter.drawText(cellx+ x1 + total_length - i*cellx,y2+(0.35)*celly, QString::fromUtf8(peer_ssl_details.name.c_str()) + " - " + QString::fromUtf8(peer_ssl_details.location.c_str()) + " ("+QString::number(current_probs[i])+")");
|
||||
}
|
||||
oy += celly * (2+matrix_info.friend_ids.size());
|
||||
|
||||
oy += celly ;
|
||||
oy += celly ;
|
||||
|
||||
// update the pixmap
|
||||
//
|
||||
pixmap = tmppixmap;
|
||||
maxHeight = oy ;
|
||||
}
|
||||
#endif
|
||||
|
||||
void GxsIdStatisticsWidget::paintEvent(QPaintEvent */*event*/)
|
||||
{
|
||||
QStylePainter(this).drawPixmap(0, 0, pixmap);
|
||||
}
|
||||
|
||||
void GxsIdStatisticsWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QRect rect = geometry();
|
||||
|
||||
mMaxWidth = rect.width();
|
||||
mMaxHeight = rect.height() ;
|
||||
|
||||
QWidget::resizeEvent(event);
|
||||
updateContent();
|
||||
}
|
94
retroshare-gui/src/gui/statistics/GxsIdStatistics.h
Normal file
94
retroshare-gui/src/gui/statistics/GxsIdStatistics.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*******************************************************************************
|
||||
* gui/statistics/GxsIdStatistics.h *
|
||||
* *
|
||||
* Copyright (c) 2011 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPoint>
|
||||
#include <retroshare/rsidentity.h>
|
||||
#include <retroshare/rstypes.h>
|
||||
|
||||
#include "RsAutoUpdatePage.h"
|
||||
#include "Histogram.h"
|
||||
#include "ui_GxsIdStatistics.h"
|
||||
|
||||
// In this statistics panel we show:
|
||||
//
|
||||
// - histograms
|
||||
// * age histogram of GXS ids (creation time)
|
||||
// * last usage histogram
|
||||
// * number of IDs used in each service as reported by UsageStatistics
|
||||
//
|
||||
// (note: we could use that histogram class for packets statistics, so we made a separate class)
|
||||
//
|
||||
// And general statistics:
|
||||
//
|
||||
// - total number of IDs
|
||||
// - total number of signed IDs
|
||||
// - total number of own IDs
|
||||
//
|
||||
class GxsIdStatisticsWidget ;
|
||||
|
||||
class GxsIdStatistics: public RsAutoUpdatePage, public Ui::GxsIdStatistics
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GxsIdStatistics(QWidget *parent = NULL) ;
|
||||
~GxsIdStatistics();
|
||||
|
||||
void updateContent() ;
|
||||
|
||||
private:
|
||||
|
||||
void processSettings(bool bLoad);
|
||||
bool m_bProcessSettings;
|
||||
|
||||
virtual void updateDisplay() ;
|
||||
|
||||
GxsIdStatisticsWidget *_tst_CW ;
|
||||
} ;
|
||||
|
||||
class GxsIdStatisticsWidget: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GxsIdStatisticsWidget(QWidget *parent = NULL) ;
|
||||
|
||||
virtual void paintEvent(QPaintEvent *event) ;
|
||||
virtual void resizeEvent(QResizeEvent *event);
|
||||
|
||||
void updateContent() ;
|
||||
void updateData();
|
||||
private:
|
||||
static QString speedString(float f) ;
|
||||
|
||||
QPixmap pixmap ;
|
||||
int mMaxWidth,mMaxHeight ;
|
||||
uint32_t mNbWeeks;
|
||||
uint32_t mNbHours;
|
||||
uint32_t mTotalIdentities;
|
||||
Histogram mPublishDateHist ;
|
||||
Histogram mLastUsedHist ;
|
||||
|
||||
std::map<RsIdentityUsage::UsageCode,int> mUsageMap;
|
||||
std::map<uint32_t,int> mPerServiceUsageMap;
|
||||
};
|
||||
|
49
retroshare-gui/src/gui/statistics/GxsIdStatistics.ui
Normal file
49
retroshare-gui/src/gui/statistics/GxsIdStatistics.ui
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GxsIdStatistics</class>
|
||||
<widget class="QWidget" name="GxsIdStatistics">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1468</width>
|
||||
<height>659</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Router Statistics</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QScrollArea" name="_stats_F">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1450</width>
|
||||
<height>641</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <time.h>
|
||||
#include <memory>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFontMetrics>
|
||||
|
@ -49,6 +50,9 @@
|
|||
#include "gui/gxs/GxsIdLabel.h"
|
||||
#include "gui/gxs/GxsIdDetails.h"
|
||||
#include "gui/gxs/GxsIdTreeWidgetItem.h"
|
||||
#include "gui/Identity/IdDialog.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/common/FilesDefs.h"
|
||||
|
||||
#define COL_PENDING_ID 0
|
||||
#define COL_PENDING_DESTINATION 1
|
||||
|
@ -61,11 +65,13 @@
|
|||
#define COL_PENDING_DESTINATION_ID 8
|
||||
|
||||
#define COL_GROUP_GRP_ID 0
|
||||
#define COL_GROUP_NUM_MSGS 1
|
||||
#define COL_GROUP_SIZE_MSGS 2
|
||||
#define COL_GROUP_SUBSCRIBED 3
|
||||
#define COL_GROUP_POPULARITY 4
|
||||
#define COL_GROUP_UNIQUE_ID 5
|
||||
#define COL_GROUP_PUBLISHTS 1
|
||||
#define COL_GROUP_NUM_MSGS 2
|
||||
#define COL_GROUP_SIZE_MSGS 3
|
||||
#define COL_GROUP_SUBSCRIBED 4
|
||||
#define COL_GROUP_POPULARITY 5
|
||||
#define COL_GROUP_UNIQUE_ID 6
|
||||
#define COL_GROUP_AUTHOR_ID 7
|
||||
|
||||
//static const int PARTIAL_VIEW_SIZE = 9 ;
|
||||
//static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ;
|
||||
|
@ -94,8 +100,10 @@ GxsTransportStatistics::GxsTransportStatistics(QWidget *parent)
|
|||
QHeaderView_setSectionResizeMode(groupTreeWidget->header(), QHeaderView::ResizeToContents);
|
||||
|
||||
connect(treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CustomPopupMenu(QPoint)));
|
||||
|
||||
treeWidget->setColumnHidden(COL_PENDING_DESTINATION_ID,true);
|
||||
connect(groupTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(CustomPopupMenuGroups(QPoint)));
|
||||
|
||||
treeWidget->setColumnHidden(COL_PENDING_DESTINATION_ID,true);
|
||||
groupTreeWidget->setColumnHidden(COL_GROUP_AUTHOR_ID,true);
|
||||
|
||||
// load settings
|
||||
processSettings(true);
|
||||
|
@ -139,7 +147,20 @@ void GxsTransportStatistics::CustomPopupMenu( QPoint )
|
|||
|
||||
QTreeWidgetItem *item = treeWidget->currentItem();
|
||||
if (item) {
|
||||
contextMnu.addAction(QIcon(":/images/info16.png"), tr("Details"), this, SLOT(personDetails()));
|
||||
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("View details"), this, SLOT(personDetails()));
|
||||
|
||||
}
|
||||
|
||||
contextMnu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void GxsTransportStatistics::CustomPopupMenuGroups( QPoint )
|
||||
{
|
||||
QMenu contextMnu( this );
|
||||
|
||||
QTreeWidgetItem *item = groupTreeWidget->currentItem();
|
||||
if (item) {
|
||||
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("View details"), this, SLOT(showAuthorInPeople()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -275,9 +296,10 @@ void GxsTransportStatistics::updateContent()
|
|||
groupTreeWidget->addTopLevelItem(item);
|
||||
groupTreeWidget->setItemExpanded(item,openned_groups.find(it->first) != openned_groups.end());
|
||||
|
||||
QString msg_time_string = (stat.last_publish_TS>0)?QString(" (Last msg: %1)").arg(QDateTime::fromTime_t((uint)stat.last_publish_TS).toString()):"" ;
|
||||
QString msg_time_string = (stat.last_publish_TS>0)?QString("(Last msg: %1)").arg(QDateTime::fromTime_t((uint)stat.last_publish_TS).toString()):"" ;
|
||||
|
||||
item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs) + msg_time_string) ;
|
||||
item->setData(COL_GROUP_PUBLISHTS, Qt::DisplayRole, msg_time_string) ;
|
||||
item->setData(COL_GROUP_NUM_MSGS, Qt::DisplayRole, QString::number(stat.mNumMsgs) ) ;
|
||||
item->setData(COL_GROUP_GRP_ID, Qt::DisplayRole, QString::fromStdString(it->first.toStdString())) ;
|
||||
item->setData(COL_GROUP_SIZE_MSGS, Qt::DisplayRole, QString::number(stat.mTotalSizeOfMsgs)) ;
|
||||
item->setData(COL_GROUP_SUBSCRIBED,Qt::DisplayRole, stat.subscribed?tr("Yes"):tr("No")) ;
|
||||
|
@ -308,6 +330,8 @@ void GxsTransportStatistics::updateContent()
|
|||
rsIdentity->getIdDetails(meta.mAuthorId,idDetails);
|
||||
|
||||
QPixmap pixmap ;
|
||||
QDateTime qdatetime;
|
||||
qdatetime.setTime_t(meta.mPublishTs);
|
||||
|
||||
if(idDetails.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idDetails.mAvatar.mData, idDetails.mAvatar.mSize, pixmap,GxsIdDetails::SMALL))
|
||||
pixmap = GxsIdDetails::makeDefaultIcon(meta.mAuthorId,GxsIdDetails::SMALL);
|
||||
|
@ -315,7 +339,9 @@ void GxsTransportStatistics::updateContent()
|
|||
sitem->setIcon(COL_GROUP_GRP_ID, QIcon(pixmap));
|
||||
|
||||
sitem->setData(COL_GROUP_UNIQUE_ID, Qt::DisplayRole,QString::fromStdString(meta.mMsgId.toStdString()));
|
||||
sitem->setData(COL_GROUP_NUM_MSGS,Qt::DisplayRole, QDateTime::fromTime_t(meta.mPublishTs).toString());
|
||||
sitem->setData(COL_GROUP_AUTHOR_ID, Qt::DisplayRole, QString::fromStdString(meta.mAuthorId.toStdString())) ;
|
||||
sitem->setText(COL_GROUP_PUBLISHTS, QDateTime::fromTime_t(meta.mPublishTs).toString());
|
||||
sitem->setData(COL_GROUP_PUBLISHTS, Qt::UserRole, qdatetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,6 +359,25 @@ void GxsTransportStatistics::personDetails()
|
|||
dialog->show();
|
||||
}
|
||||
|
||||
void GxsTransportStatistics::showAuthorInPeople()
|
||||
{
|
||||
QTreeWidgetItem *item = groupTreeWidget->currentItem();
|
||||
std::string id = item->text(COL_GROUP_AUTHOR_ID).toStdString();
|
||||
|
||||
if (id.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* window will destroy itself! */
|
||||
IdDialog *idDialog = dynamic_cast<IdDialog*>(MainWindow::getPage(MainWindow::People));
|
||||
|
||||
if (!idDialog)
|
||||
return ;
|
||||
|
||||
MainWindow::showWindow(MainWindow::People);
|
||||
idDialog->navigate(RsGxsId(id));
|
||||
}
|
||||
|
||||
#ifdef TO_REMOVE
|
||||
void GxsTransportStatistics::loadGroupMeta(const std::vector<RsGroupMetaData>& groupInfo)
|
||||
{
|
||||
|
@ -419,24 +464,25 @@ void GxsTransportStatistics::loadGroups()
|
|||
#ifdef DEBUG_FORUMS
|
||||
std::cerr << "Retrieving post data for post " << mThreadId << std::endl;
|
||||
#endif
|
||||
std::map<RsGxsGroupId,RsGxsTransGroupStatistics> stats;
|
||||
auto stats = std::make_unique<
|
||||
std::map<RsGxsGroupId,RsGxsTransGroupStatistics> >();
|
||||
|
||||
if(!rsGxsTrans->getGroupStatistics(stats))
|
||||
{
|
||||
RsErr() << "Cannot retrieve group statistics in GxsTransportStatistics" << std::endl;
|
||||
return;
|
||||
}
|
||||
if(!rsGxsTrans->getGroupStatistics(*stats))
|
||||
{
|
||||
RS_ERR("Cannot retrieve group statistics in GxsTransportStatistics");
|
||||
return;
|
||||
}
|
||||
|
||||
RsQThreadUtils::postToObject( [stats,this]()
|
||||
RsQThreadUtils::postToObject(
|
||||
[stats = std::move(stats), this]()
|
||||
{
|
||||
/* Here it goes any code you want to be executed on the Qt Gui
|
||||
* thread, for example to update the data model with new information
|
||||
* after a blocking call to RetroShare API complete */
|
||||
|
||||
mGroupStats = stats;
|
||||
|
||||
updateContent();
|
||||
|
||||
// TODO: consider making mGroupStats an unique_ptr to avoid copying
|
||||
mGroupStats = *stats;
|
||||
updateContent();
|
||||
mStateHelper->setLoading(GXSTRANS_GROUP_META, false);
|
||||
|
||||
}, this );
|
||||
|
|
|
@ -51,7 +51,10 @@ public:
|
|||
private slots:
|
||||
/** Create the context popup menu and it's submenus */
|
||||
void CustomPopupMenu( QPoint point );
|
||||
void CustomPopupMenuGroups( QPoint point ) ;
|
||||
|
||||
void personDetails();
|
||||
void showAuthorInPeople();
|
||||
|
||||
private:
|
||||
void updateDisplay(bool complete) ;
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerShowSortIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
|
@ -108,6 +108,12 @@
|
|||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerShowSortIndicator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Group ID / Author</string>
|
||||
|
@ -115,7 +121,12 @@
|
|||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Number of messages / Publish TS</string>
|
||||
<string>Publish TS</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Number of messages </string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
|
|
55
retroshare-gui/src/gui/statistics/Histogram.cpp
Normal file
55
retroshare-gui/src/gui/statistics/Histogram.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*******************************************************************************
|
||||
* gui/statistics/Histogram.cpp *
|
||||
* *
|
||||
* Copyright (c) 2020 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "Histogram.h"
|
||||
|
||||
Histogram::Histogram()
|
||||
: mStart(0),mEnd(1.0),mBins(10,0)
|
||||
{}
|
||||
|
||||
Histogram::Histogram(double start, double end, int bins)
|
||||
: mStart(start),mEnd(end),mBins(bins,0)
|
||||
{
|
||||
if(mEnd <= mStart)
|
||||
std::cerr << "Null histogram created! Please check your parameters" << std::endl;
|
||||
}
|
||||
|
||||
void Histogram::draw(QPainter *painter) const
|
||||
{
|
||||
}
|
||||
|
||||
void Histogram::insert(double val)
|
||||
{
|
||||
long int bin = (uint32_t)floor((val - mStart)/(mEnd - mStart) * mBins.size());
|
||||
|
||||
if(bin >= 0 && bin < mBins.size())
|
||||
++mBins[bin];
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o,const Histogram& h)
|
||||
{
|
||||
o << "Histogram: [" << h.mStart << "..." << h.mEnd << "] " << h.mBins.size() << " bins." << std::endl;
|
||||
for(uint32_t i=0;i<h.mBins.size();++i)
|
||||
o << " " << h.mStart + i*(double)(h.mEnd - h.mStart)/(double)h.mBins.size() << " : " << h.mBins[i] << std::endl;
|
||||
|
||||
return o;
|
||||
}
|
45
retroshare-gui/src/gui/statistics/Histogram.h
Normal file
45
retroshare-gui/src/gui/statistics/Histogram.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*******************************************************************************
|
||||
* gui/statistics/Histogram.h *
|
||||
* *
|
||||
* Copyright (c) 2020 Retroshare Team <retroshare.project@gmail.com> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
class QPainter;
|
||||
|
||||
class Histogram
|
||||
{
|
||||
public:
|
||||
Histogram();
|
||||
Histogram(double start, double end, int bins);
|
||||
|
||||
void draw(QPainter *painter) const ;
|
||||
void insert(double val);
|
||||
|
||||
const std::vector<uint32_t>& entries() const { return mBins; }
|
||||
|
||||
private:
|
||||
double mStart;
|
||||
double mEnd;
|
||||
|
||||
std::vector<uint32_t> mBins;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o,const Histogram& h);
|
||||
};
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include <gui/statistics/TurtleRouterStatistics.h>
|
||||
#include <gui/statistics/GlobalRouterStatistics.h>
|
||||
#include <gui/statistics/GxsIdStatistics.h>
|
||||
#include <gui/statistics/GxsTransportStatistics.h>
|
||||
#include <gui/statistics/BwCtrlWindow.h>
|
||||
#include <gui/statistics/DhtWindow.h>
|
||||
|
@ -52,6 +53,7 @@
|
|||
|
||||
#define IMAGE_DHT ":/icons/DHT128.png"
|
||||
#define IMAGE_TURTLE ":/icons/turtle128.png"
|
||||
#define IMAGE_IDENTITIES ":/icons/avatar_128.png"
|
||||
#define IMAGE_BWGRAPH ":/icons/bandwidth128.png"
|
||||
#define IMAGE_GLOBALROUTER ":/icons/GRouter128.png"
|
||||
#define IMAGE_GXSTRANSPORT ":/icons/transport128.png"
|
||||
|
@ -143,6 +145,9 @@ void StatisticsWindow::initStackedPage()
|
|||
ui->stackPages->add(trsdlg = new TurtleRouterStatistics(ui->stackPages),
|
||||
action = createPageAction(QIcon(IMAGE_TURTLE), tr("Turtle Router"), grp));
|
||||
|
||||
ui->stackPages->add(gxsiddlg = new GxsIdStatistics(ui->stackPages),
|
||||
action = createPageAction(QIcon(IMAGE_IDENTITIES), tr("Identities"), grp));
|
||||
|
||||
ui->stackPages->add(grsdlg = new GlobalRouterStatistics(ui->stackPages),
|
||||
action = createPageAction(QIcon(IMAGE_GLOBALROUTER), tr("Global Router"), grp));
|
||||
|
||||
|
@ -150,7 +155,7 @@ void StatisticsWindow::initStackedPage()
|
|||
action = createPageAction(QIcon(IMAGE_GXSTRANSPORT), tr("Gxs Transport"), grp));
|
||||
|
||||
ui->stackPages->add(rttdlg = new RttStatistics(ui->stackPages),
|
||||
action = createPageAction(QIcon(IMAGE_RTT), tr("RTT Statistics"), grp));
|
||||
action = createPageAction(QIcon(IMAGE_RTT), tr("RTT Statistics"), grp));
|
||||
|
||||
bool showdht = true;
|
||||
RsPeerDetails detail;
|
||||
|
|
|
@ -38,6 +38,7 @@ class TurtleRouterStatistics;
|
|||
class GlobalRouterStatistics;
|
||||
class GxsTransportStatistics;
|
||||
class RttStatistics;
|
||||
class GxsIdStatistics;
|
||||
|
||||
class StatisticsWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
@ -57,6 +58,7 @@ public:
|
|||
BwCtrlWindow *bwdlg;
|
||||
TurtleRouterStatistics *trsdlg;
|
||||
RttStatistics *rttdlg;
|
||||
GxsIdStatistics *gxsiddlg;
|
||||
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -344,12 +344,16 @@ openbsd-* {
|
|||
LIBS *= -rdynamic
|
||||
}
|
||||
|
||||
################################### COMMON stuff ##################################
|
||||
|
||||
wikipoos {
|
||||
PRE_TARGETDEPS *= $$OUT_PWD/../../supportlibs/pegmarkdown/lib/libpegmarkdown.a
|
||||
LIBS *= $$OUT_PWD/../../supportlibs/pegmarkdown/lib/libpegmarkdown.a
|
||||
LIBS *= -lglib-2.0
|
||||
}
|
||||
|
||||
################################### HEADERS & SOURCES #############################
|
||||
|
||||
# Tor controller
|
||||
|
||||
HEADERS += TorControl/AddOnionCommand.h \
|
||||
|
@ -433,7 +437,9 @@ HEADERS += rshare.h \
|
|||
gui/FileTransfer/BannedFilesDialog.h \
|
||||
gui/statistics/TurtleRouterDialog.h \
|
||||
gui/statistics/TurtleRouterStatistics.h \
|
||||
gui/statistics/GxsIdStatistics.h \
|
||||
gui/statistics/dhtgraph.h \
|
||||
gui/statistics/Histogram.h \
|
||||
gui/statistics/BandwidthGraphWindow.h \
|
||||
gui/statistics/turtlegraph.h \
|
||||
gui/statistics/BandwidthStatsWidget.h \
|
||||
|
@ -751,6 +757,7 @@ FORMS += gui/StartDialog.ui \
|
|||
gui/statistics/DhtWindow.ui \
|
||||
gui/statistics/TurtleRouterDialog.ui \
|
||||
gui/statistics/TurtleRouterStatistics.ui \
|
||||
gui/statistics/GxsIdStatistics.ui \
|
||||
gui/statistics/GlobalRouterStatistics.ui \
|
||||
gui/statistics/GxsTransportStatistics.ui \
|
||||
gui/statistics/StatisticsWindow.ui \
|
||||
|
@ -991,8 +998,10 @@ SOURCES += main.cpp \
|
|||
gui/statistics/BandwidthGraphWindow.cpp \
|
||||
gui/statistics/BandwidthStatsWidget.cpp \
|
||||
gui/statistics/DhtWindow.cpp \
|
||||
gui/statistics/Histogram.cpp \
|
||||
gui/statistics/TurtleRouterDialog.cpp \
|
||||
gui/statistics/TurtleRouterStatistics.cpp \
|
||||
gui/statistics/GxsIdStatistics.cpp \
|
||||
gui/statistics/GlobalRouterStatistics.cpp \
|
||||
gui/statistics/GxsTransportStatistics.cpp \
|
||||
gui/statistics/StatisticsWindow.cpp \
|
||||
|
@ -1353,13 +1362,19 @@ gxschannels {
|
|||
gui/gxschannels/GxsChannelGroupDialog.h \
|
||||
gui/gxschannels/CreateGxsChannelMsg.h \
|
||||
gui/gxschannels/GxsChannelPostsWidget.h \
|
||||
gui/gxschannels/GxsChannelPostsWidgetWithModel.h \
|
||||
gui/gxschannels/GxsChannelPostsModel.h \
|
||||
gui/gxschannels/GxsChannelPostFilesModel.h \
|
||||
gui/gxschannels/GxsChannelFilesWidget.h \
|
||||
gui/gxschannels/GxsChannelPostThumbnail.h \
|
||||
gui/gxschannels/GxsChannelFilesStatusWidget.h \
|
||||
gui/feeds/GxsChannelGroupItem.h \
|
||||
gui/feeds/GxsChannelPostItem.h \
|
||||
gui/gxschannels/GxsChannelUserNotify.h
|
||||
|
||||
FORMS += gui/gxschannels/GxsChannelPostsWidget.ui \
|
||||
FORMS += \
|
||||
gui/gxschannels/GxsChannelPostsWidgetWithModel.ui \
|
||||
gui/gxschannels/GxsChannelPostsWidget.ui \
|
||||
gui/gxschannels/GxsChannelFilesWidget.ui \
|
||||
gui/gxschannels/GxsChannelFilesStatusWidget.ui \
|
||||
gui/gxschannels/CreateGxsChannelMsg.ui \
|
||||
|
@ -1368,6 +1383,9 @@ gxschannels {
|
|||
|
||||
SOURCES += gui/gxschannels/GxsChannelDialog.cpp \
|
||||
gui/gxschannels/GxsChannelPostsWidget.cpp \
|
||||
gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \
|
||||
gui/gxschannels/GxsChannelPostsModel.cpp \
|
||||
gui/gxschannels/GxsChannelPostFilesModel.cpp \
|
||||
gui/gxschannels/GxsChannelFilesWidget.cpp \
|
||||
gui/gxschannels/GxsChannelFilesStatusWidget.cpp \
|
||||
gui/gxschannels/GxsChannelGroupDialog.cpp \
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
* util/qthreadutils.h *
|
||||
* *
|
||||
* Copyright (C) 2018 Gioacchino Mazzurco <gio@eigenlab.org> *
|
||||
* Copyright (C) 2018-2020 Gioacchino Mazzurco <gio@eigenlab.org> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
|
@ -27,7 +27,9 @@
|
|||
|
||||
#include <QtGlobal>
|
||||
#include <QtCore>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace RsQThreadUtils {
|
||||
|
||||
|
@ -44,7 +46,7 @@ void postToObject(F &&fun, QObject *obj = qApp)
|
|||
QObject src;
|
||||
auto type = obj->metaObject();
|
||||
QObject::connect( &src, &QObject::destroyed, obj,
|
||||
[fun, type, obj]
|
||||
[fun = std::move(fun), type, obj]
|
||||
{
|
||||
// ensure that the object is not being destructed
|
||||
if (obj->metaObject()->inherits(type)) fun();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue