merged upstream/master

This commit is contained in:
csoler 2019-09-08 20:35:23 +02:00
commit 6419b03a2a
No known key found for this signature in database
GPG key ID: 7BCA522266C0804C
131 changed files with 6081 additions and 1473 deletions

View file

@ -41,11 +41,11 @@ void AvatarDefs::getOwnAvatar(QPixmap &avatar, const QString& defaultImage)
}
/* load image */
avatar.loadFromData(data, size, "PNG") ;
GxsIdDetails::loadPixmapFromData(data, size, avatar,GxsIdDetails::ORIGINAL) ;
free(data);
}
void AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, const QString& defaultImage)
bool AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, const QString& defaultImage)
{
unsigned char *data = NULL;
int size = 0;
@ -54,15 +54,16 @@ void AvatarDefs::getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, cons
rsMsgs->getAvatarData(RsPeerId(sslId), data, size);
if (size == 0) {
avatar = QPixmap(defaultImage);
return;
return false;
}
/* load image */
avatar.loadFromData(data, size, "PNG") ;
GxsIdDetails::loadPixmapFromData(data, size, avatar, GxsIdDetails::LARGE) ;
free(data);
return true;
}
void AvatarDefs::getAvatarFromGxsId(const RsGxsId& gxsId, QPixmap &avatar, const QString& defaultImage)
bool AvatarDefs::getAvatarFromGxsId(const RsGxsId& gxsId, QPixmap &avatar, const QString& defaultImage)
{
//int size = 0;
@ -72,16 +73,18 @@ void AvatarDefs::getAvatarFromGxsId(const RsGxsId& gxsId, QPixmap &avatar, const
if(!rsIdentity->getIdDetails(gxsId, details))
{
avatar = QPixmap(defaultImage);
return ;
return false;
}
/* load image */
if(details.mAvatar.mSize == 0 || !avatar.loadFromData(details.mAvatar.mData, details.mAvatar.mSize, "PNG"))
avatar = QPixmap::fromImage(GxsIdDetails::makeDefaultIcon(gxsId));
if(details.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(details.mAvatar.mData, details.mAvatar.mSize, avatar,GxsIdDetails::LARGE))
avatar = GxsIdDetails::makeDefaultIcon(gxsId,GxsIdDetails::LARGE);
return true;
}
void AvatarDefs::getAvatarFromGpgId(const RsPgpId& gpgId, QPixmap &avatar, const QString& defaultImage)
bool AvatarDefs::getAvatarFromGpgId(const RsPgpId& gpgId, QPixmap &avatar, const QString& defaultImage)
{
unsigned char *data = NULL;
int size = 0;
@ -105,11 +108,13 @@ void AvatarDefs::getAvatarFromGpgId(const RsPgpId& gpgId, QPixmap &avatar, const
if (size == 0) {
avatar = QPixmap(defaultImage);
return;
return false;
}
/* load image */
avatar.loadFromData(data, size, "PNG") ;
GxsIdDetails::loadPixmapFromData(data, size, avatar);
free(data);
return true;
}

View file

@ -35,9 +35,9 @@ class AvatarDefs
public:
static void getOwnAvatar(QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
static void getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
static void getAvatarFromGpgId(const RsPgpId & gpgId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
static void getAvatarFromGxsId(const RsGxsId & gxsId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
static bool getAvatarFromSslId(const RsPeerId& sslId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
static bool getAvatarFromGpgId(const RsPgpId & gpgId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
static bool getAvatarFromGxsId(const RsGxsId & gxsId, QPixmap &avatar, const QString& defaultImage = AVATAR_DEFAULT_IMAGE);
};
#endif

View file

@ -45,6 +45,9 @@ AvatarDialog::AvatarDialog(QWidget *parent) :
updateInterface();
}
const int AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_W = 64;
const int AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_H = 64;
AvatarDialog::~AvatarDialog()
{
delete(ui);
@ -52,7 +55,7 @@ AvatarDialog::~AvatarDialog()
void AvatarDialog::changeAvatar()
{
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load Avatar"), 128, 128);
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load Avatar"), RS_AVATAR_DEFAULT_IMAGE_W,RS_AVATAR_DEFAULT_IMAGE_H);
if (img.isNull())
return;

View file

@ -35,6 +35,9 @@ class AvatarDialog : public QDialog
Q_OBJECT
public:
static const int RS_AVATAR_DEFAULT_IMAGE_W ;
static const int RS_AVATAR_DEFAULT_IMAGE_H ;
AvatarDialog(QWidget *parent = 0);
~AvatarDialog();

View file

@ -18,12 +18,15 @@
* *
*******************************************************************************/
#include <QBuffer>
#include <rshare.h>
#include <retroshare/rsstatus.h>
#include <retroshare/rspeers.h>
#include <retroshare/rsmsgs.h>
#include "gui/notifyqt.h"
#include "util/misc.h"
#include "gui/common/AvatarDefs.h"
#include "gui/common/AvatarDialog.h"
@ -85,19 +88,33 @@ void AvatarWidget::mouseReleaseEvent(QMouseEvent */*event*/)
if (!mFlag.isOwnId) {
return;
}
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Choose avatar"), AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_W,AvatarDialog::RS_AVATAR_DEFAULT_IMAGE_H);
AvatarDialog dialog(this);
if (img.isNull())
return;
QPixmap avatar;
AvatarDefs::getOwnAvatar(avatar, "");
setPixmap(img);
dialog.setAvatar(avatar);
if (dialog.exec() == QDialog::Accepted) {
QByteArray newAvatar;
dialog.getAvatar(newAvatar);
QByteArray data;
QBuffer buffer(&data);
rsMsgs->setOwnAvatarData((unsigned char *)(newAvatar.data()), newAvatar.size()) ; // last char 0 included.
}
buffer.open(QIODevice::WriteOnly);
img.save(&buffer, "PNG"); // writes image into a in PNG format
rsMsgs->setOwnAvatarData((unsigned char *)(data.data()), data.size()) ; // last char 0 included.
// AvatarDialog dialog(this);
//
// QPixmap avatar;
// AvatarDefs::getOwnAvatar(avatar, "");
//
// dialog.setAvatar(avatar);
// if (dialog.exec() == QDialog::Accepted) {
// QByteArray newAvatar;
// dialog.getAvatar(newAvatar);
//
// rsMsgs->setOwnAvatarData((unsigned char *)(newAvatar.data()), newAvatar.size()) ; // last char 0 included.
// }
}
void AvatarWidget::setFrameType(FrameType type)

View file

@ -21,23 +21,40 @@
#include <QApplication>
#include <QDesktopWidget>
#include <QFile>
#include <QDir>
#include <QGridLayout>
#include <QHash>
#include <QIcon>
#include <QPushButton>
#include <QTabWidget>
#include <QWidget>
#include <QMessageBox>
#include <iostream>
#include <math.h>
#include "Emoticons.h"
#include "util/HandleRichText.h"
#include "retroshare/rsinit.h"
#define ICONNAME "groupicon.png"
static QHash<QString, QPair<QVector<QString>, QHash<QString, QString> > > Smileys;
static QVector<QString> grpOrdered;
static QVector<QString > StickerGroups;
static QStringList filters;
static QHash<QString, QString> tooltipcache;
void Emoticons::load()
{
loadSmiley();
filters << "*.png" << "*.jpg" << "*.gif";
loadSticker(QString::fromStdString(RsAccounts::ConfigDirectory()) + "/stickers"); //under .retroshare, shared between users
loadSticker(QString::fromStdString(RsAccounts::AccountDirectory()) + "/stickers"); //under account, unique for user
loadSticker(QString::fromStdString(RsAccounts::systemDataDirectory()) + "/stickers"); //exe's folder, shipped with RS
}
void Emoticons::loadSmiley()
{
QString sm_AllLines;
bool internalFiles = true;
@ -267,3 +284,184 @@ void Emoticons::showSmileyWidget(QWidget *parent, QWidget *button, const char *s
smWidget->move(x, y) ;
smWidget->show() ;
}
void Emoticons::loadSticker(QString foldername)
{
QDir dir(foldername);
if(!dir.exists()) return;
//If it contains at a least one png then add it as a group
QStringList files = dir.entryList(filters, QDir::Files);
if(files.count() > 0)
StickerGroups.append(foldername);
//Check subfolders
QFileInfoList subfolders = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot);
for(int i = 0; i<subfolders.length(); i++)
loadSticker(subfolders[i].filePath());
}
void Emoticons::showStickerWidget(QWidget *parent, QWidget *button, const char *slotAddMethod, bool above)
{
QWidget *smWidget = new QWidget(parent, Qt::Popup) ;
smWidget->setAttribute(Qt::WA_DeleteOnClose) ;
smWidget->setWindowTitle("Stickers") ;
if(StickerGroups.count() == 0) {
QMessageBox::warning(parent, "Stickers", "No stickers installed");
return;
}
bool bOnlyOneGroup = (StickerGroups.count() == 1);
QTabWidget *smTab = nullptr;
if (! bOnlyOneGroup)
{
smTab = new QTabWidget(smWidget);
QGridLayout *smGLayout = new QGridLayout(smWidget);
smGLayout->setContentsMargins(0,0,0,0);
smGLayout->addWidget(smTab);
}
const int buttonWidth = QFontMetricsF(smWidget->font()).height()*5;
const int buttonHeight = QFontMetricsF(smWidget->font()).height()*5;
int maxRowCount = 0;
int maxCountPerLine = 0;
QVectorIterator<QString> grp(StickerGroups);
while(grp.hasNext())
{
QDir groupDir = QDir(grp.next());
QString groupName = groupDir.dirName();
groupDir.setNameFilters(filters);
QWidget *tabGrpWidget = nullptr;
if (! bOnlyOneGroup)
{
//Lazy load tooltips for the current tab
QObject::connect(smTab, &QTabWidget::currentChanged, [=](int index){
QWidget* current = smTab->widget(index);
loadToolTips(current);
});
tabGrpWidget = new QWidget(smTab);
// (Cyril) Never use an absolute size. It needs to be scaled to the actual font size on the screen.
//
QFontMetricsF fm(parent->font()) ;
smTab->setIconSize(QSize(28*fm.height()/14.0,28*fm.height()/14.0));
smTab->setMinimumWidth(400);
smTab->setTabPosition(QTabWidget::South);
smTab->setStyleSheet("QTabBar::tab { height: 44px; width: 44px; }");
int index;
if (groupDir.exists(ICONNAME)) //use groupicon.png if exists, else the first png as a group icon
index = smTab->addTab( tabGrpWidget, QIcon(groupDir.absoluteFilePath(ICONNAME)), "");
else
index = smTab->addTab( tabGrpWidget, QIcon(groupDir.entryInfoList(QDir::Files)[0].canonicalFilePath()), "");
smTab->setTabToolTip(index, groupName);
} else {
tabGrpWidget = smWidget;
}
QGridLayout *tabGLayout = new QGridLayout(tabGrpWidget);
tabGLayout->setContentsMargins(0,0,0,0);
tabGLayout->setSpacing(0);
QFileInfoList group = groupDir.entryInfoList(QDir::Files, QDir::Name);
int rowCount = (int)sqrt((double)group.size());
int countPerLine = (group.size()/rowCount) + ((group.size() % rowCount) ? 1 : 0);
maxRowCount = qMax(maxRowCount, rowCount);
maxCountPerLine = qMax(maxCountPerLine, countPerLine);
int lin = 0;
int col = 0;
for(int i = 0; i < group.length(); ++i)
{
QFileInfo fi = group[i];
if(fi.fileName().compare(ICONNAME, Qt::CaseInsensitive) == 0)
continue;
QPushButton *button = new QPushButton("", tabGrpWidget);
button->setIconSize(QSize(buttonWidth, buttonHeight));
button->setFixedSize(QSize(buttonWidth, buttonHeight));
button->setIcon(QPixmap(fi.absoluteFilePath()));
button->setToolTip(fi.fileName());
button->setStatusTip(fi.absoluteFilePath());
button->setStyleSheet("QPushButton:hover {border: 3px solid #0099cc; border-radius: 3px;}");
button->setFlat(true);
tabGLayout->addWidget(button,col,lin);
++lin;
if(lin >= countPerLine)
{
lin = 0;
++col;
}
QObject::connect(button, SIGNAL(clicked()), parent, slotAddMethod);
QObject::connect(button, SIGNAL(clicked()), smWidget, SLOT(close()));
}
}
//Load tooltips for the first page
QWidget * firstpage;
if(bOnlyOneGroup) {
firstpage = smWidget;
} else {
firstpage = smTab->currentWidget();
}
loadToolTips(firstpage);
//Get left up pos of button
QPoint butTopLeft = button->mapToGlobal(QPoint(0,0));
//Get widget's size
QSize sizeWidget = smWidget->sizeHint();
//Get screen's size
QSize sizeScreen = QApplication::desktop()->size();
//Calculate left distance to screen start
int distToScreenLeft = butTopLeft.x();
//Calculate right distance to screen end
int distToRightScreen = sizeScreen.width() - (butTopLeft.x() + button->width());
//Calculate left position
int x;
if (distToScreenLeft >= distToRightScreen) //More distance in left than right in screen
x = butTopLeft.x() - sizeWidget.width(); //Place widget on left of button
else
x = butTopLeft.x() + button->width(); //Place widget on right of button
//Calculate top position
int y;
if (above) //Widget must be above the button
y = butTopLeft.y() + button->height() - sizeWidget.height();
else
y = butTopLeft.y() + button->height()/2 - sizeWidget.height()/2; //Centered on button height
if (y + sizeWidget.height() > sizeScreen.height()) //Widget will be too low
y = sizeScreen.height() - sizeWidget.height(); //Place widget bottom at screen bottom
if (y < 0) //Widget will be too high
y = 0; //Place widget top at screen top
smWidget->move(x, y);
smWidget->show();
}
void Emoticons::loadToolTips(QWidget *container)
{
QList<QPushButton *> children = container->findChildren<QPushButton *>();
for(int i = 0; i < children.length(); ++i) {
if(!children[i]->toolTip().contains('<')) {
if(tooltipcache.contains(children[i]->statusTip())) {
children[i]->setToolTip(tooltipcache[children[i]->statusTip()]);
} else {
QString tooltip;
if(RsHtml::makeEmbeddedImage(children[i]->statusTip(), tooltip, 300*300)) {
tooltipcache.insert(children[i]->statusTip(), tooltip);
children[i]->setToolTip(tooltip);
}
}
}
}
}

View file

@ -28,10 +28,14 @@ class Emoticons
{
public:
static void load();
static void loadSmiley();
static void loadSticker(QString foldername);
static void showSmileyWidget(QWidget *parent, QWidget *button, const char *slotAddMethod, bool above);
static void showStickerWidget(QWidget *parent, QWidget *button, const char *slotAddMethod, bool above);
// static void formatText(QString &text);
private:
static void loadToolTips(QWidget *container);
};
#endif

View file

@ -126,8 +126,7 @@ FriendList::FriendList(QWidget *parent) :
#ifdef RS_DIRECT_CHAT
connect(ui->peerTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(chatfriend(QTreeWidgetItem *)));
#else
connect( ui->peerTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
this, SLOT(expandItem(QTreeWidgetItem *)) );
connect( ui->peerTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(expandItem(QTreeWidgetItem *)) );
#endif
connect(NotifyQt::getInstance(), SIGNAL(groupsChanged(int)), this, SLOT(groupsChanged()));
@ -553,275 +552,6 @@ static void getNameWidget(QTreeWidget *treeWidget, QTreeWidgetItem *item, Elided
}
}
struct PgpItemInfo
{
PgpItemInfo() : gpg_connected(false),gpg_online(false),gpg_hasPrivateChat(false),bestPeerState(0),bestRSState(0) {}
bool gpg_connected ;
bool gpg_online ;
bool gpg_hasPrivateChat ;
int bestPeerState ;
unsigned int bestRSState ;
QString bestCustomStateString;// for gpg item
std::list<RsPeerId> sslContacts;
QDateTime bestLastContact;
QString bestIP;
QPixmap bestAvatar;
};
void FriendList::manageProfileLocations(QTreeWidgetItem *gpgItem,const RsPgpId& gpg_id,PgpItemInfo& info)
{
std::vector<RsPeerId> privateChatIds;
ChatUserNotify::getPeersWithWaitingChat(privateChatIds);
int columnCount = ui->peerTreeWidget->columnCount();
for (auto sslIt ( info.sslContacts.begin()); sslIt != info.sslContacts.end(); ++sslIt)
{
QTreeWidgetItem *sslItem = NULL;
RsPeerId sslId = *sslIt;
// find the corresponding sslItem child item of the gpg item
bool newChild = true;
int childCount = gpgItem->childCount();
for (int childIndex = 0; childIndex < childCount; ++childIndex) {
// we assume, that only ssl items are child of the gpg item, so we don't need to test the type
if (getRsId(gpgItem->child(childIndex)) == sslId.toStdString()) {
sslItem = gpgItem->child(childIndex);
newChild = false;
break;
}
}
RsPeerDetails sslDetail;
if (!rsPeers->getPeerDetails(sslId, sslDetail) || !rsPeers->isFriend(sslId)) {
#ifdef FRIENDS_DEBUG
std::cerr << "Removing widget from the view : id : " << sslId << std::endl;
#endif
// child has disappeared, remove it from the gpg_item
if (sslItem) {
gpgItem->removeChild(sslItem);
delete(sslItem);
}
continue;
}
if (newChild) {
sslItem = new RSTreeWidgetItem(mCompareRole, TYPE_SSL); //set type to 1 for custom popup menu
#ifdef FRIENDS_DEBUG
std::cerr << "FriendList::insertPeers() inserting sslItem." << std::endl;
#endif
/* Add ssl child to the list. Add here, because for setHidden the item must be added */
gpgItem->addChild(sslItem);
/* Sort data */
for (int i = 0; i < columnCount; ++i) {
sslItem->setData(i, ROLE_SORT_GROUP, 2);
sslItem->setData(i, ROLE_SORT_STANDARD_GROUP, 1);
}
}
/* not displayed, used to find back the item */
QString strID = QString::fromStdString(sslDetail.id.toStdString());
sslItem->setData(COLUMN_DATA, ROLE_ID, strID);
sslItem->setText(COLUMN_ID, strID);
sslItem->setData(COLUMN_ID, ROLE_SORT_NAME, strID);
sslItem->setData(COLUMN_ID, ROLE_FILTER, strID);
/* Custom state string */
QString customStateString;
if (sslDetail.state & RS_PEER_STATE_CONNECTED) {
customStateString = QString::fromUtf8(rsMsgs->getCustomStateString(sslDetail.id).c_str());
}
QPixmap sslAvatar;
AvatarDefs::getAvatarFromSslId(RsPeerId(sslDetail.id.toStdString()), sslAvatar);
/* last contact */
QDateTime sslLastContact = QDateTime::fromTime_t(sslDetail.lastConnect);
sslItem->setData(COLUMN_LAST_CONTACT, Qt::DisplayRole, QVariant(sslLastContact));
sslItem->setData(COLUMN_LAST_CONTACT, ROLE_SORT_NAME, QVariant(sslLastContact));
if (sslLastContact > info.bestLastContact) {
info.bestLastContact = sslLastContact;
}
/* IP */
QString sslIP = (sslDetail.state & RS_PEER_STATE_CONNECTED) ? StatusDefs::connectStateIpString(sslDetail) : QString("---");
sslItem->setText(COLUMN_IP, sslIP);
sslItem->setData(COLUMN_IP, ROLE_SORT_NAME, sslIP);
/* change color and icon */
QPixmap sslOverlayIcon;
QFont sslFont;
QColor sslColor;
int peerState = 0;
QString connectStateString;
if (sslDetail.state & RS_PEER_STATE_CONNECTED)
{
// get the status info for this ssl id
int rsState = 0;
StatusInfo status_info ;
rsStatus->getStatus(sslDetail.id,status_info);
rsState = status_info.status;
switch (rsState) {
case RS_STATUS_INACTIVE:
peerState = PEER_STATE_INACTIVE;
break;
case RS_STATUS_ONLINE:
peerState = PEER_STATE_ONLINE;
break;
case RS_STATUS_AWAY:
peerState = PEER_STATE_AWAY;
break;
case RS_STATUS_BUSY:
peerState = PEER_STATE_BUSY;
break;
}
/* find the best ssl contact for the gpg item */
if (info.bestPeerState == 0 || peerState < info.bestPeerState) {
/* first ssl contact or higher state */
info.bestPeerState = peerState;
info.bestRSState = rsState;
info.bestCustomStateString = customStateString;
info.bestIP = sslIP;
if (!sslAvatar.isNull()) {
info.bestAvatar = sslAvatar;
}
} else if (peerState == info.bestPeerState) {
/* equal state */
if (info.bestCustomStateString.isEmpty() && !customStateString.isEmpty()) {
/* when customStateString is shown in name item, use sslId with customStateString.
second with a custom state string ... use second */
info.bestPeerState = peerState;
info.bestRSState = rsState;
info.bestCustomStateString = customStateString;
}
if (info.bestAvatar.isNull() && !sslAvatar.isNull()) {
/* Use available avatar */
info.bestAvatar = sslAvatar;
}
}
sslItem->setHidden(false);
info.gpg_connected = true;
sslOverlayIcon = QPixmap(StatusDefs::imageStatus(info.bestRSState));
connectStateString = StatusDefs::name(rsState);
if (rsState == 0) {
sslFont.setBold(true);
sslColor = mTextColorStatus[RS_STATUS_ONLINE];
} else {
sslFont = StatusDefs::font(rsState);
sslColor = mTextColorStatus[rsState];
}
} else if (sslDetail.state & RS_PEER_STATE_ONLINE) {
sslItem->setHidden(mHideUnconnected);
info.gpg_online = true;
peerState = PEER_STATE_AVAILABLE;
if (sslDetail.connectState) {
sslOverlayIcon = QPixmap(":/images/connect_creating.png");
} else {
sslOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_ONLINE));
}
connectStateString = StatusDefs::name(RS_STATUS_ONLINE);
sslFont.setBold(true);
sslColor = mTextColorStatus[RS_STATUS_ONLINE];
} else {
peerState = PEER_STATE_OFFLINE;
sslItem->setHidden(mHideUnconnected);
if (sslDetail.connectState) {
sslOverlayIcon = QPixmap(":/images/connect_creating.png");
} else {
sslOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_OFFLINE));
}
connectStateString = StatusDefs::connectStateWithoutTransportTypeString(sslDetail);
sslFont.setBold(false);
sslColor = mTextColorStatus[RS_STATUS_OFFLINE];
}
/* Location */
QString sslName = QString::fromUtf8(sslDetail.location.c_str());
QString sslText;
if (mShowState) {
if (!connectStateString.isEmpty()) {
sslText = connectStateString;
if (!customStateString.isEmpty()) {
sslText += " [" + customStateString + "]";
}
} else {
if (!customStateString.isEmpty()) {
sslText = customStateString;
}
}
sslItem->setToolTip(COLUMN_NAME, "");
} else {
if (!customStateString.isEmpty()) {
sslText = customStateString;
}
/* Show the state as tooltip */
sslItem->setToolTip(COLUMN_NAME, connectStateString);
}
/* Create or get ssl label */
ElidedLabel *sslNameLabel = NULL;
ElidedLabel *sslTextLabel = NULL;
getNameWidget(ui->peerTreeWidget, sslItem, sslNameLabel, sslTextLabel);
if (sslNameLabel) {
sslNameLabel->setText(sslName);
sslNameLabel->setFont(sslFont);
QPalette palette = sslNameLabel->palette();
palette.setColor(sslNameLabel->foregroundRole(), sslColor);
sslNameLabel->setPalette(palette);
}
if (sslTextLabel) {
sslTextLabel->setText(sslText);
sslTextLabel->setVisible(!sslText.isEmpty());
}
// Filter
sslItem->setData(COLUMN_NAME, ROLE_FILTER, sslName);
if (std::find(privateChatIds.begin(), privateChatIds.end(), sslDetail.id) != privateChatIds.end()) {
// private chat is available
sslOverlayIcon = QPixmap(":/images/chat.png");
info.gpg_hasPrivateChat = true;
}
sslItem->setIcon(COLUMN_NAME, createAvatar(sslAvatar, sslOverlayIcon));
/* Sort data */
sslItem->setData(COLUMN_NAME, ROLE_SORT_NAME, sslName);
for (int i = 0; i < columnCount; ++i) {
sslItem->setData(i, ROLE_SORT_STATE, peerState);
sslItem->setTextColor(i, sslColor);
sslItem->setFont(i, sslFont);
}
}
}
/**
* Get the list of peers from the RsIface.
* Adds all friend gpg ids, with their nodes as children to the peerTreeWidget.
@ -839,8 +569,8 @@ void FriendList::insertPeers()
int columnCount = ui->peerTreeWidget->columnCount();
//std::list<StatusInfo> statusInfo;
//rsStatus->getStatusList(statusInfo);
std::list<StatusInfo> statusInfo;
rsStatus->getStatusList(statusInfo);
if (!rsPeers) {
/* not ready yet! */
@ -849,6 +579,9 @@ void FriendList::insertPeers()
}
// get peers with waiting incoming chats
std::vector<RsPeerId> privateChatIds;
ChatUserNotify::getPeersWithWaitingChat(privateChatIds);
// get existing groups
std::list<RsGroupInfo> groupInfoList;
std::list<RsGroupInfo>::iterator groupIt;
@ -858,8 +591,7 @@ void FriendList::insertPeers()
std::list<RsPgpId>::iterator gpgIt;
rsPeers->getGPGAcceptedList(gpgFriends);
// add own gpg id, if we have more than on node (ssl client)
//add own gpg id, if we have more than on node (ssl client)
std::list<RsPeerId> ownSslContacts;
RsPgpId ownId = rsPeers->getGPGOwnId();
rsPeers->getAssociatedSSLIds(ownId, ownSslContacts);
@ -867,24 +599,6 @@ void FriendList::insertPeers()
gpgFriends.push_back(ownId);
}
// Also add SSL peers which PGP key is not available yet.
std::list<RsPeerId> ssl_friends ;
rsPeers->getFriendList(ssl_friends);
std::map<RsPgpId,std::list<RsPeerDetails> > pgp_friends_without_validation;
std::cerr << "List of accepted ssl peers: " << std::endl;
for(auto it(ssl_friends.begin());it!=ssl_friends.end();++it)
{
RsPeerDetails pd;
if(rsPeers->getPeerDetails(*it,pd) && pd.skip_signature_validation)
{
std::cerr << " adding " << *it << " - " << pd.gpg_id << std::endl;
gpgFriends.push_back(pd.gpg_id);
pgp_friends_without_validation[pd.gpg_id].push_back(pd);
}
}
/* get a link to the table */
QTreeWidget *peerTreeWidget = ui->peerTreeWidget;
@ -1086,8 +800,7 @@ void FriendList::insertPeers()
}
RsPeerDetails detail;
if (pgp_friends_without_validation.find(gpgId) == pgp_friends_without_validation.end() &&
(!rsPeers->getGPGDetails(gpgId, detail) || !detail.accept_connection) && detail.gpg_id != ownId) {
if ((!rsPeers->getGPGDetails(gpgId, detail) || !detail.accept_connection) && detail.gpg_id != ownId) {
// don't accept anymore connection, remove from the view
if (gpgItem) {
if (groupItem) {
@ -1096,13 +809,10 @@ void FriendList::insertPeers()
delete (peerTreeWidget->takeTopLevelItem(peerTreeWidget->indexOfTopLevelItem(gpgItem)));
}
}
// We still want to add the item if it is unvalidated, in which case getGPGDetails returns false.
continue;
continue;
}
if (gpgItem == NULL)
{
if (gpgItem == NULL) {
// create gpg item and add it to tree
gpgItem = new RSTreeWidgetItem(mCompareRole, TYPE_GPG); //set type to 0 for custom popup menu
@ -1117,7 +827,7 @@ void FriendList::insertPeers()
gpgItem->setTextAlignment(COLUMN_NAME, Qt::AlignLeft | Qt::AlignVCenter);
/* not displayed, used to find back the item */
QString strID = QString::fromStdString(gpgId.toStdString());
QString strID = QString::fromStdString(detail.gpg_id.toStdString());
gpgItem->setData(COLUMN_DATA, ROLE_ID, strID);
gpgItem->setText(COLUMN_ID, strID);
gpgItem->setData(COLUMN_ID, ROLE_SORT_NAME, strID);
@ -1147,22 +857,263 @@ void FriendList::insertPeers()
}
// update the childs (ssl certs)
bool gpg_connected = false;
bool gpg_online = false;
bool gpg_hasPrivateChat = false;
int bestPeerState = 0; // for gpg item
unsigned int bestRSState = 0; // for gpg item
QString bestCustomStateString;// for gpg item
std::list<RsPeerId> sslContacts;
QDateTime bestLastContact;
QString bestIP;
QPixmap bestAvatar;
PgpItemInfo info;
rsPeers->getAssociatedSSLIds(detail.gpg_id, sslContacts);
for (std::list<RsPeerId>::iterator sslIt = sslContacts.begin(); sslIt != sslContacts.end(); ++sslIt) {
QTreeWidgetItem *sslItem = NULL;
RsPeerId sslId = *sslIt;
auto itt = pgp_friends_without_validation.find(gpgId);
if(itt != pgp_friends_without_validation.end())
{
info.sslContacts.clear();
for(auto itt2(itt->second.begin());itt2!=itt->second.end();++itt2)
info.sslContacts.push_back(itt2->id);
// find the corresponding sslItem child item of the gpg item
bool newChild = true;
childCount = gpgItem->childCount();
for (int childIndex = 0; childIndex < childCount; ++childIndex) {
// we assume, that only ssl items are child of the gpg item, so we don't need to test the type
if (getRsId(gpgItem->child(childIndex)) == sslId.toStdString()) {
sslItem = gpgItem->child(childIndex);
newChild = false;
break;
}
}
detail.name = gpgId.toStdString() + tr(" (Unverified yet)").toStdString();
RsPeerDetails sslDetail;
if (!rsPeers->getPeerDetails(sslId, sslDetail) || !rsPeers->isFriend(sslId)) {
#ifdef FRIENDS_DEBUG
std::cerr << "Removing widget from the view : id : " << sslId << std::endl;
#endif
// child has disappeared, remove it from the gpg_item
if (sslItem) {
gpgItem->removeChild(sslItem);
delete(sslItem);
}
continue;
}
if (newChild) {
sslItem = new RSTreeWidgetItem(mCompareRole, TYPE_SSL); //set type to 1 for custom popup menu
#ifdef FRIENDS_DEBUG
std::cerr << "FriendList::insertPeers() inserting sslItem." << std::endl;
#endif
/* Add ssl child to the list. Add here, because for setHidden the item must be added */
gpgItem->addChild(sslItem);
/* Sort data */
for (int i = 0; i < columnCount; ++i) {
sslItem->setData(i, ROLE_SORT_GROUP, 2);
sslItem->setData(i, ROLE_SORT_STANDARD_GROUP, 1);
}
}
/* not displayed, used to find back the item */
QString strID = QString::fromStdString(sslDetail.id.toStdString());
sslItem->setData(COLUMN_DATA, ROLE_ID, strID);
sslItem->setText(COLUMN_ID, strID);
sslItem->setData(COLUMN_ID, ROLE_SORT_NAME, strID);
sslItem->setData(COLUMN_ID, ROLE_FILTER, strID);
/* Custom state string */
QString customStateString;
if (sslDetail.state & RS_PEER_STATE_CONNECTED) {
customStateString = QString::fromUtf8(rsMsgs->getCustomStateString(sslDetail.id).c_str());
}
QPixmap sslAvatar;
AvatarDefs::getAvatarFromSslId(RsPeerId(sslDetail.id.toStdString()), sslAvatar);
/* last contact */
QDateTime sslLastContact = QDateTime::fromTime_t(sslDetail.lastConnect);
sslItem->setData(COLUMN_LAST_CONTACT, Qt::DisplayRole, QVariant(sslLastContact));
sslItem->setData(COLUMN_LAST_CONTACT, ROLE_SORT_NAME, QVariant(sslLastContact));
if (sslLastContact > bestLastContact) {
bestLastContact = sslLastContact;
}
/* IP */
QString sslIP = (sslDetail.state & RS_PEER_STATE_CONNECTED) ? StatusDefs::connectStateIpString(sslDetail) : QString("---");
sslItem->setText(COLUMN_IP, sslIP);
sslItem->setData(COLUMN_IP, ROLE_SORT_NAME, sslIP);
/* change color and icon */
QPixmap sslOverlayIcon;
QFont sslFont;
QColor sslColor;
int peerState = 0;
QString connectStateString;
if (sslDetail.state & RS_PEER_STATE_CONNECTED) {
// get the status info for this ssl id
int rsState = 0;
std::list<StatusInfo>::iterator it;
for (it = statusInfo.begin(); it != statusInfo.end(); ++it) {
if (it->id == sslId) {
rsState = it->status;
switch (rsState) {
case RS_STATUS_INACTIVE:
peerState = PEER_STATE_INACTIVE;
break;
case RS_STATUS_ONLINE:
peerState = PEER_STATE_ONLINE;
break;
case RS_STATUS_AWAY:
peerState = PEER_STATE_AWAY;
break;
case RS_STATUS_BUSY:
peerState = PEER_STATE_BUSY;
break;
}
/* find the best ssl contact for the gpg item */
if (bestPeerState == 0 || peerState < bestPeerState) {
/* first ssl contact or higher state */
bestPeerState = peerState;
bestRSState = rsState;
bestCustomStateString = customStateString;
bestIP = sslIP;
if (!sslAvatar.isNull()) {
bestAvatar = sslAvatar;
}
} else if (peerState == bestPeerState) {
/* equal state */
if (bestCustomStateString.isEmpty() && !customStateString.isEmpty()) {
/* when customStateString is shown in name item, use sslId with customStateString.
second with a custom state string ... use second */
bestPeerState = peerState;
bestRSState = rsState;
bestCustomStateString = customStateString;
}
if (bestAvatar.isNull() && !sslAvatar.isNull()) {
/* Use available avatar */
bestAvatar = sslAvatar;
}
}
break;
}
}
sslItem->setHidden(false);
gpg_connected = true;
sslOverlayIcon = QPixmap(StatusDefs::imageStatus(bestRSState));
connectStateString = StatusDefs::name(rsState);
if (rsState == 0) {
sslFont.setBold(true);
sslColor = mTextColorStatus[RS_STATUS_ONLINE];
} else {
sslFont = StatusDefs::font(rsState);
sslColor = mTextColorStatus[rsState];
}
} else if (sslDetail.state & RS_PEER_STATE_ONLINE) {
sslItem->setHidden(mHideUnconnected);
gpg_online = true;
peerState = PEER_STATE_AVAILABLE;
if (sslDetail.connectState) {
sslOverlayIcon = QPixmap(":/images/connect_creating.png");
} else {
sslOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_ONLINE));
}
connectStateString = StatusDefs::name(RS_STATUS_ONLINE);
sslFont.setBold(true);
sslColor = mTextColorStatus[RS_STATUS_ONLINE];
} else {
peerState = PEER_STATE_OFFLINE;
sslItem->setHidden(mHideUnconnected);
if (sslDetail.connectState) {
sslOverlayIcon = QPixmap(":/images/connect_creating.png");
} else {
sslOverlayIcon = QPixmap(StatusDefs::imageStatus(RS_STATUS_OFFLINE));
}
connectStateString = StatusDefs::connectStateWithoutTransportTypeString(sslDetail);
sslFont.setBold(false);
sslColor = mTextColorStatus[RS_STATUS_OFFLINE];
}
/* Location */
QString sslName = QString::fromUtf8(sslDetail.location.c_str());
QString sslText;
if (mShowState) {
if (!connectStateString.isEmpty()) {
sslText = connectStateString;
if (!customStateString.isEmpty()) {
sslText += " [" + customStateString + "]";
}
} else {
if (!customStateString.isEmpty()) {
sslText = customStateString;
}
}
sslItem->setToolTip(COLUMN_NAME, "");
} else {
if (!customStateString.isEmpty()) {
sslText = customStateString;
}
/* Show the state as tooltip */
sslItem->setToolTip(COLUMN_NAME, connectStateString);
}
/* Create or get ssl label */
ElidedLabel *sslNameLabel = NULL;
ElidedLabel *sslTextLabel = NULL;
getNameWidget(ui->peerTreeWidget, sslItem, sslNameLabel, sslTextLabel);
if (sslNameLabel) {
sslNameLabel->setText(sslName);
sslNameLabel->setFont(sslFont);
QPalette palette = sslNameLabel->palette();
palette.setColor(sslNameLabel->foregroundRole(), sslColor);
sslNameLabel->setPalette(palette);
}
if (sslTextLabel) {
sslTextLabel->setText(sslText);
sslTextLabel->setVisible(!sslText.isEmpty());
}
// Filter
sslItem->setData(COLUMN_NAME, ROLE_FILTER, sslName);
if (std::find(privateChatIds.begin(), privateChatIds.end(), sslDetail.id) != privateChatIds.end()) {
// private chat is available
sslOverlayIcon = QPixmap(":/images/chat.png");
gpg_hasPrivateChat = true;
}
sslItem->setIcon(COLUMN_NAME, createAvatar(sslAvatar, sslOverlayIcon));
/* Sort data */
sslItem->setData(COLUMN_NAME, ROLE_SORT_NAME, sslName);
for (int i = 0; i < columnCount; ++i) {
sslItem->setData(i, ROLE_SORT_STATE, peerState);
sslItem->setTextColor(i, sslColor);
sslItem->setFont(i, sslFont);
}
}
else
rsPeers->getAssociatedSSLIds(gpgId, info.sslContacts);
manageProfileLocations(gpgItem,gpgId,info);
QString gpgName = QString::fromUtf8(detail.name.c_str());
QString gpgText;
@ -1172,38 +1123,38 @@ void FriendList::insertPeers()
bool showInfoAtGpgItem = !gpgItem->isExpanded();
QPixmap gpgOverlayIcon;
if (info.gpg_connected) {
if (gpg_connected) {
gpgItem->setHidden(false);
++onlineCount;
if (info.bestPeerState == 0) {
if (bestPeerState == 0) {
// show as online
info.bestPeerState = PEER_STATE_ONLINE;
info.bestRSState = RS_STATUS_ONLINE;
bestPeerState = PEER_STATE_ONLINE;
bestRSState = RS_STATUS_ONLINE;
}
gpgColor = mTextColorStatus[info.bestRSState];
gpgFont = StatusDefs::font(info.bestRSState);
gpgColor = mTextColorStatus[bestRSState];
gpgFont = StatusDefs::font(bestRSState);
if (showInfoAtGpgItem) {
gpgOverlayIcon = QPixmap(StatusDefs::imageStatus(info.bestRSState));
gpgOverlayIcon = QPixmap(StatusDefs::imageStatus(bestRSState));
if (mShowState) {
gpgText = StatusDefs::name(info.bestRSState);
if (!info.bestCustomStateString.isEmpty()) {
gpgText += " [" + info.bestCustomStateString + "]";
gpgText = StatusDefs::name(bestRSState);
if (!bestCustomStateString.isEmpty()) {
gpgText += " [" + bestCustomStateString + "]";
}
} else {
if (!info.bestCustomStateString.isEmpty()) {
gpgText = info.bestCustomStateString;
if (!bestCustomStateString.isEmpty()) {
gpgText = bestCustomStateString;
}
}
}
} else if (info.gpg_online) {
} else if (gpg_online) {
gpgItem->setHidden(mHideUnconnected);
++onlineCount;
info.bestPeerState = PEER_STATE_AVAILABLE;
bestPeerState = PEER_STATE_AVAILABLE;
gpgFont.setBold(true);
gpgColor = mTextColorStatus[RS_STATUS_ONLINE];
@ -1216,7 +1167,7 @@ void FriendList::insertPeers()
gpgOverlayIcon = QPixmap(IMAGE_AVAILABLE);
}
} else {
info.bestPeerState = PEER_STATE_OFFLINE;
bestPeerState = PEER_STATE_OFFLINE;
gpgItem->setHidden(mHideUnconnected);
gpgFont = StatusDefs::font(RS_STATUS_OFFLINE);
@ -1231,11 +1182,11 @@ void FriendList::insertPeers()
}
}
if (info.gpg_hasPrivateChat) {
if (gpg_hasPrivateChat) {
gpgOverlayIcon = QPixmap(":/images/chat.png");
}
gpgItem->setIcon(COLUMN_NAME, createAvatar(info.bestAvatar.isNull() ? QPixmap(AVATAR_DEFAULT_IMAGE) : info.bestAvatar, gpgOverlayIcon));
gpgItem->setIcon(COLUMN_NAME, createAvatar(bestAvatar.isNull() ? QPixmap(AVATAR_DEFAULT_IMAGE) : bestAvatar, gpgOverlayIcon));
/* Create or get gpg label */
ElidedLabel *gpgNameLabel = NULL;
@ -1260,16 +1211,16 @@ void FriendList::insertPeers()
// Filter
gpgItem->setData(COLUMN_NAME, ROLE_FILTER, gpgName);
gpgItem->setData(COLUMN_LAST_CONTACT, Qt::DisplayRole, showInfoAtGpgItem ? QVariant(info.bestLastContact) : "");
gpgItem->setData(COLUMN_LAST_CONTACT, ROLE_SORT_NAME, QVariant(info.bestLastContact));
gpgItem->setText(COLUMN_IP, showInfoAtGpgItem ? info.bestIP : "");
gpgItem->setData(COLUMN_IP, ROLE_SORT_NAME, info.bestIP);
gpgItem->setData(COLUMN_LAST_CONTACT, Qt::DisplayRole, showInfoAtGpgItem ? QVariant(bestLastContact) : "");
gpgItem->setData(COLUMN_LAST_CONTACT, ROLE_SORT_NAME, QVariant(bestLastContact));
gpgItem->setText(COLUMN_IP, showInfoAtGpgItem ? bestIP : "");
gpgItem->setData(COLUMN_IP, ROLE_SORT_NAME, bestIP);
/* Sort data */
gpgItem->setData(COLUMN_NAME, ROLE_SORT_NAME, gpgName);
for (int i = 0; i < columnCount; ++i) {
gpgItem->setData(i, ROLE_SORT_STATE, info.bestPeerState);
gpgItem->setData(i, ROLE_SORT_STATE, bestPeerState);
gpgItem->setTextColor(i, gpgColor);
gpgItem->setFont(i, gpgFont);
@ -2014,7 +1965,7 @@ bool FriendList::exportFriendlist(QString &fileName)
/**
* @brief helper function to show a message box
*/
void showXMLParsingError()
static void showXMLParsingError()
{
// show error to user
QMessageBox mbox;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
/*******************************************************************************
* retroshare-gui/src/gui/msgs/RsFriendListModel.h *
* *
* Copyright 2019 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 <QModelIndex>
#include <QColor>
#include "retroshare/rsstatus.h"
#include "retroshare/rsmsgs.h"
#include "retroshare/rspeers.h"
typedef uint32_t ForumModelIndex;
// This class is the item model used by Qt to display the information
class RsFriendListModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit RsFriendListModel(QObject *parent = NULL);
~RsFriendListModel(){}
class RsNodeDetails: public RsPeerDetails {};// in the near future, there will be a specific class for Profile/Node details in replacement of RsPeerDetails
class RsProfileDetails: public RsPeerDetails {};
struct HierarchicalGroupInformation
{
RsGroupInfo group_info;
std::vector<uint32_t> child_profile_indices; // index in the array of hierarchical profiles
};
struct HierarchicalProfileInformation
{
RsProfileDetails profile_info;
std::vector<uint32_t> child_node_indices; // indices of nodes in the array of nodes.
};
struct HierarchicalNodeInformation
{
HierarchicalNodeInformation() : last_update_ts(0) {}
rstime_t last_update_ts;
RsNodeDetails node_info;
};
enum Columns {
COLUMN_THREAD_NAME = 0x00,
COLUMN_THREAD_LAST_CONTACT = 0x01,
COLUMN_THREAD_IP = 0x02,
COLUMN_THREAD_ID = 0x03,
COLUMN_THREAD_NB_COLUMNS = 0x04
};
enum Roles{ SortRole = Qt::UserRole+1,
StatusRole = Qt::UserRole+2,
UnreadRole = Qt::UserRole+3,
FilterRole = Qt::UserRole+4,
OnlineRole = Qt::UserRole+5,
TypeRole = Qt::UserRole+6
};
enum FilterType{ FILTER_TYPE_NONE = 0x00,
FILTER_TYPE_ID = 0x01,
FILTER_TYPE_NAME = 0x02
};
enum EntryType{ ENTRY_TYPE_UNKNOWN = 0x00,
ENTRY_TYPE_GROUP = 0x01,
ENTRY_TYPE_PROFILE = 0x02,
ENTRY_TYPE_NODE = 0x03
};
// This structure encodes the position of a node in the hierarchy. The type tells which of the index fields are valid.
struct EntryIndex
{
public:
EntryIndex();
EntryType type; // type of the entry (group,profile,location)
// Indices w.r.t. parent. The set of indices entirely determines the position of the entry in the hierarchy.
// An index of 0xff means "undefined"
uint16_t group_index; // index of the group in mGroups tab
uint16_t profile_index; // index of the child profile in its own group if group_index < 0xff, or in the mProfiles tab otherwise.
uint16_t node_index; // index of the child node in its own profile
EntryIndex parent() const;
EntryIndex child(int row,const std::vector<EntryIndex>& top_level) const;
uint32_t parentRow(uint32_t nb_groups) const;
};
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
QModelIndex getIndexOfGroup(const RsNodeGroupId& mid) const;
static const QString FilterString ;
// This method will asynchroneously update the data
void setDisplayGroups(bool b);
bool getDisplayGroups() const { return mDisplayGroups; }
void setDisplayStatusString(bool b);
bool getDisplayStatusString() const { return mDisplayStatusString; }
EntryType getType(const QModelIndex&) const;
bool getGroupData (const QModelIndex&,RsGroupInfo &) const;
bool getProfileData(const QModelIndex&,RsProfileDetails&) const;
bool getNodeData (const QModelIndex&,RsNodeDetails &) const;
void setFilter(FilterType filter_type, const QStringList& strings) ;
void expandItem(const QModelIndex&) ;
void collapseItem(const QModelIndex&) ;
// Overloaded methods from QAbstractItemModel
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 headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void clear() ;
/* Color definitions (for standard see qss.default) */
QColor mTextColorGroup;
QColor mTextColorStatus[RS_STATUS_COUNT];
private:
const HierarchicalGroupInformation *getGroupInfo (const EntryIndex&) const;
const HierarchicalProfileInformation *getProfileInfo(const EntryIndex&)const;
const HierarchicalNodeInformation *getNodeInfo(const EntryIndex&) const;
bool isProfileExpanded(const EntryIndex& e) const;
void checkNode(HierarchicalNodeInformation& node);
bool getPeerOnlineStatus(const EntryIndex& e) const;
std::map<RsPgpId,uint32_t>::const_iterator checkProfileIndex(const RsPgpId& pgp_id,
std::map<RsPgpId,uint32_t>& pgp_indices,
std::vector<HierarchicalProfileInformation>& mProfiles,
bool create);
QVariant sizeHintRole (const EntryIndex& e, int col) const;
QVariant displayRole (const EntryIndex& e, int col) const;
QVariant decorationRole(const EntryIndex& e, int col) const;
QVariant toolTipRole (const EntryIndex& e, int col) const;
QVariant statusRole (const EntryIndex& e, int col) const;
QVariant sortRole (const EntryIndex& e, int col) const;
QVariant fontRole (const EntryIndex& e, int col) const;
QVariant textColorRole (const EntryIndex& e, int col) const;
QVariant onlineRole (const EntryIndex& e, int col) const;
QVariant filterRole (const EntryIndex& e, int col) const;
/*!
* \brief debug_dump
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
*/
public slots:
void checkInternalData(bool force);
void debug_dump() const;
signals:
void dataLoaded(); // emitted after the messages have been set. Can be used to updated the UI.
void friendListChanged(); // emitted after the messages have been set. Can be used to updated the UI.
void dataAboutToLoad();
private:
void updateInternalData();
bool passesFilter(const EntryIndex &e, int column) const;
void preMods() ;
void postMods() ;
void *getParentRef(void *ref,int& row) const;
void *getChildRef(void *ref,int row) const;
int getChildrenCount(void *ref) const;
template<uint8_t> static bool convertIndexToInternalId(const EntryIndex& e,quintptr& ref);
template<uint8_t> static bool convertInternalIdToIndex(quintptr ref, EntryIndex& e);
uint32_t updateFilterStatus(ForumModelIndex i,int column,const QStringList& strings);
QStringList mFilterStrings;
FilterType mFilterType;
bool mDisplayGroups ;
bool mDisplayStatusString ;
rstime_t mLastInternalDataUpdate;
rstime_t mLastNodeUpdate;;
// The 3 vectors below store thehierarchical information for groups, profiles and locations,
// meaning which is the child/parent of which. The actual group/profile/node data are also stored in the
// structure.
mutable std::vector<HierarchicalGroupInformation> mGroups;
mutable std::vector<HierarchicalProfileInformation> mProfiles;
mutable std::vector<HierarchicalNodeInformation> mLocations;
// The top level list contains all nodes to display, which type depends on the option to display groups or not.
// Idices in the list may be profile indices or group indices. In the former case the profile child index refers to
// the inde in the mProfiles tab, whereas in the the later case, the child index refers to the index of the profile in the
// group it belows to.
std::vector<EntryIndex> mTopLevel;
// keeps track of expanded/collapsed items, so as to only show icon for collapsed profiles
std::set<std::string> mExpandedProfiles;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,158 @@
/*******************************************************************************
* gui/common/NewFriendList.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 <set>
#include <QWidget>
#include <QTreeView>
#include "FriendListModel.h"
#include "RsAutoUpdatePage.h"
#include "retroshare/rsstatus.h"
namespace Ui {
class NewFriendList;
}
class RSTreeWidgetItemCompareRole;
class QTreeWidgetItem;
class QToolButton;
class FriendListSortFilterProxyModel;
class NewFriendList: public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor textColorGroup READ textColorGroup WRITE setTextColorGroup)
Q_PROPERTY(QColor textColorStatusOffline READ textColorStatusOffline WRITE setTextColorStatusOffline)
Q_PROPERTY(QColor textColorStatusAway READ textColorStatusAway WRITE setTextColorStatusAway)
Q_PROPERTY(QColor textColorStatusBusy READ textColorStatusBusy WRITE setTextColorStatusBusy)
Q_PROPERTY(QColor textColorStatusOnline READ textColorStatusOnline WRITE setTextColorStatusOnline)
Q_PROPERTY(QColor textColorStatusInactive READ textColorStatusInactive WRITE setTextColorStatusInactive)
public:
explicit NewFriendList(QWidget *parent = 0);
~NewFriendList();
// Add a tool button to the tool area
void addToolButton(QToolButton *toolButton);
void processSettings(bool load);
void setColumnVisible(int col,bool visible);
bool isColumnVisible(int col) const;
std::string getSelectedGroupId() const;
//void updateDisplay() override;
QColor textColorGroup() const { return mModel->mTextColorGroup; }
QColor textColorStatusOffline() const { return mModel->mTextColorStatus[RS_STATUS_OFFLINE ]; }
QColor textColorStatusAway() const { return mModel->mTextColorStatus[RS_STATUS_AWAY ]; }
QColor textColorStatusBusy() const { return mModel->mTextColorStatus[RS_STATUS_BUSY ]; }
QColor textColorStatusOnline() const { return mModel->mTextColorStatus[RS_STATUS_ONLINE ]; }
QColor textColorStatusInactive() const { return mModel->mTextColorStatus[RS_STATUS_INACTIVE]; }
void setTextColorGroup(QColor color) { mModel->mTextColorGroup = color; }
void setTextColorStatusOffline(QColor color) { mModel->mTextColorStatus[RS_STATUS_OFFLINE ] = color; }
void setTextColorStatusAway(QColor color) { mModel->mTextColorStatus[RS_STATUS_AWAY ] = color; }
void setTextColorStatusBusy(QColor color) { mModel->mTextColorStatus[RS_STATUS_BUSY ] = color; }
void setTextColorStatusOnline(QColor color) { mModel->mTextColorStatus[RS_STATUS_ONLINE ] = color; }
void setTextColorStatusInactive(QColor color) { mModel->mTextColorStatus[RS_STATUS_INACTIVE] = color; }
public slots:
void filterItems(const QString &text);
void toggleSortByState(bool sort);
void forceUpdateDisplay();
void toggleColumnVisible();
void setShowGroups(bool show);
void setShowUnconnected(bool hidden);
void setShowState(bool show);
void headerContextMenuRequested(QPoint);
private slots:
void sortColumn(int col,Qt::SortOrder so);
void itemExpanded(const QModelIndex&);
void itemCollapsed(const QModelIndex&);
protected:
void changeEvent(QEvent *e);
private:
Ui::NewFriendList *ui;
RsFriendListModel *mModel;
QAction *mActionSortByState;
void expandGroup(const RsNodeGroupId& gid);
void recursRestoreExpandedItems(const QModelIndex& index, const QString& parent_path, const std::set<QString>& exp, const std::set<QString> &sel);
void recursSaveExpandedItems(const QModelIndex& index,const QString& parent_path,std::set<QString>& exp, std::set<QString>& sel);
void saveExpandedPathsAndSelection(std::set<QString>& expanded_indexes, std::set<QString>& selected_indexes);
void restoreExpandedPathsAndSelection(const std::set<QString>& expanded_indexes, const std::set<QString>& selected_indexes);
void checkInternalData(bool force);
QModelIndex getCurrentSourceIndex() const;
bool getCurrentNode(RsFriendListModel::RsNodeDetails& prof) const;
bool getCurrentGroup(RsGroupInfo& prof) const;
bool getCurrentProfile(RsFriendListModel::RsProfileDetails& prof) const;
// Settings for peer list display
bool mShowState;
std::set<RsNodeGroupId> openGroups;
std::set<RsPgpId> openPeers;
bool getOrCreateGroup(const std::string& name, uint flag, RsNodeGroupId& id);
bool getGroupIdByName(const std::string& name, RsNodeGroupId& id);
bool importExportFriendlistFileDialog(QString &fileName, bool import);
bool exportFriendlist(QString &fileName);
bool importFriendlist(QString &fileName, bool &errorPeers, bool &errorGroups);
FriendListSortFilterProxyModel *mProxyModel ;
private slots:
void peerTreeWidgetCustomPopupMenu();
void pastePerson();
void connectNode();
void configureNode();
void configureProfile();
void chatNode();
void copyFullCertificate();
void addFriend();
void msgNode();
void msgGroup();
void msgProfile();
void recommendNode();
void removeNode();
void removeProfile();
void createNewGroup() ;
void addToGroup();
void moveToGroup();
void removeFromGroup();
void editGroup();
void removeGroup();
void exportFriendlistClicked();
void importFriendlistClicked();
};

View file

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewFriendList</class>
<widget class="QWidget" name="NewFriendList">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>475</width>
<height>292</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</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="titleBarFrame">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="LineEditClear" name="filterLineEdit"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeView" name="peerTreeWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
<action name="actionShowOfflineFriends">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Offline Friends</string>
</property>
<property name="toolTip">
<string>Show Offline Friends</string>
</property>
</action>
<action name="actionShowState">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Status</string>
</property>
<property name="toolTip">
<string>Show status</string>
</property>
</action>
<action name="actionShowGroups">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Groups</string>
</property>
<property name="toolTip">
<string>Show groups</string>
</property>
</action>
<action name="actionExportFriendlist">
<property name="text">
<string>export friendlist</string>
</property>
<property name="toolTip">
<string>export your friendlist including groups</string>
</property>
</action>
<action name="actionImportFriendlist">
<property name="text">
<string>import friendlist</string>
</property>
<property name="toolTip">
<string>import your friendlist including groups</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>LineEditClear</class>
<extends>QLineEdit</extends>
<header location="global">gui/common/LineEditClear.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -243,7 +243,7 @@ QString StatusDefs::connectStateWithoutTransportTypeString(RsPeerDetails &detail
return stateString;
}
QString StatusDefs::connectStateIpString(RsPeerDetails &details)
QString StatusDefs::connectStateIpString(const RsPeerDetails &details)
{
QString stateString = QString("");

View file

@ -40,7 +40,7 @@ public:
static QString peerStateString(int peerState);
static QString connectStateString(RsPeerDetails &details);
static QString connectStateWithoutTransportTypeString(RsPeerDetails &details);
static QString connectStateIpString(RsPeerDetails &details);
static QString connectStateIpString(const RsPeerDetails &details);
};
#endif