This commit is contained in:
defnax 2026-08-31 21:49:51 +00:00 committed by GitHub
commit fd435e5f63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 2343 additions and 277 deletions

View File

@ -62,6 +62,7 @@ option( RS_GXSCHANNELS "Enable GXS channels in GUI" ON )
option( RS_GXSFORUMS "Enable GXS forums in GUI" ON )
option( RS_GXSPOSTED "Enable GXS posted in GUI" ON )
option( RS_GXSCIRCLES "Enable GXS circles in GUI" ON )
option( RS_GXSPEOPLE "Enable GXS people page in GUI" OFF )
option( RS_GUI_CMARK "Enable CommonMark support in GUI" OFF )
set(RS_GXSIDENTITIES ON CACHE BOOL "Enable GXS identities in GUI" FORCE)
set(RS_IDLE ON CACHE BOOL "Enable Idle support" FORCE)
@ -664,6 +665,10 @@ if(RS_GXSCIRCLES)
target_compile_definitions(${PROJECT_NAME} PRIVATE RS_USE_CIRCLES )
endif(RS_GXSCIRCLES)
if(RS_GXSPEOPLE)
target_compile_definitions(${PROJECT_NAME} PRIVATE RS_USE_NEW_PEOPLE )
endif(RS_GXSPEOPLE)
# NOTE: the GUI does NOT use botan, json-c, z or bz2 directly (it uses rapidjson
# for JSON). They are transitive dependencies of librnp (PGP), pulled in here only
# because the statically-linked librnp does not propagate them. Ideally librnp

View File

@ -1078,30 +1078,44 @@ if(RS_GXSCIRCLES)
APPEND RS_IMPLEMENTATION_HEADERS
src/gui/Circles/CirclesDialog.h
src/gui/Circles/CreateCircleDialog.h
src/gui/People/PeopleDialog.h
src/gui/People/CircleWidget.h
src/gui/People/IdentityWidget.h
)
list(
APPEND RS_GUI_FORMS
src/gui/Circles/CirclesDialog.ui
src/gui/Circles/CreateCircleDialog.ui
src/gui/People/PeopleDialog.ui
src/gui/People/CircleWidget.ui
src/gui/People/IdentityWidget.ui
)
list(
APPEND RS_GUI_SOURCES
src/gui/Circles/CirclesDialog.cpp
src/gui/Circles/CreateCircleDialog.cpp
src/gui/People/PeopleDialog.cpp
src/gui/People/CircleWidget.cpp
src/gui/People/IdentityWidget.cpp
)
endif(RS_GXSCIRCLES)
if(RS_GXSPEOPLE)
list(
APPEND RS_IMPLEMENTATION_HEADERS
src/gui/People/CircleWidget.h
src/gui/People/IdentityWidget.h
src/gui/People/PeopleDialog.h
)
list(
APPEND RS_GUI_FORMS
src/gui/People/CircleWidget.ui
src/gui/People/IdentityWidget.ui
src/gui/People/PeopleDialog.ui
)
list(
APPEND RS_GUI_SOURCES
src/gui/People/CircleWidget.cpp
src/gui/People/IdentityWidget.cpp
src/gui/People/PeopleDialog.cpp
)
endif(RS_GXSPEOPLE)
if(RS_GXSGUI)
list(
APPEND RS_IMPLEMENTATION_HEADERS

View File

@ -300,9 +300,7 @@ RsIdentityListModel::EntryIndex RsIdentityListModel::EntryIndex::parent() const
break;
case ENTRY_TYPE_TOP_LEVEL:
std::cerr << "ERROR: calling parent() on entryindex with no parent!" << std::endl;
default:
//Can be when request root index.
i.type = ENTRY_TYPE_TOP_LEVEL;
break;
}

View File

@ -0,0 +1,385 @@
/*******************************************************************************
* retroshare-gui/src/gui/Identity/UsageStatistics.cpp *
* *
* Copyright (C) 2026 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/>. *
* *
*******************************************************************************/
#include <unistd.h>
#include <memory>
#include "UsageStatistics.h"
#include "ui_UsageStatistics.h"
#include "gui/RetroShareLink.h"
#include "gui/gxs/GxsIdDetails.h"
#include "util/DateTime.h"
#include "util/rstime.h"
#include "retroshare/rsgxsflags.h"
#include "retroshare/rschats.h"
#include "retroshare/rspeers.h"
#include "retroshare/rsservicecontrol.h"
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsgxsforums.h"
#include "retroshare/rsposted.h"
#ifdef RS_USE_WIRE
#include "retroshare/rswire.h"
#endif
#include <iostream>
#include <algorithm>
#include <memory>
/******
* #define ID_DEBUG 1
*****/
/** Constructor */
UsageStatistics::UsageStatistics(QWidget *parent)
: QWidget(parent), ui(new Ui::UsageStatistics)
{
ui->setupUi(this);
}
/** Destructor */
UsageStatistics::~UsageStatistics()
{
delete ui;
}
static QString getHumanReadableDuration(uint32_t seconds)
{
if(seconds < 60)
return QString(QObject::tr("%1 seconds ago")).arg(seconds) ;
else if(seconds < 120)
return QString(QObject::tr("%1 minute ago")).arg(seconds/60) ;
else if(seconds < 3600)
return QString(QObject::tr("%1 minutes ago")).arg(seconds/60) ;
else if(seconds < 7200)
return QString(QObject::tr("%1 hour ago")).arg(seconds/3600) ;
else if(seconds < 24*3600)
return QString(QObject::tr("%1 hours ago")).arg(seconds/3600) ;
else if(seconds < 2*24*3600)
return QString(QObject::tr("%1 day ago")).arg(seconds/86400) ;
else
return QString(QObject::tr("%1 days ago")).arg(seconds/86400) ;
}
QString UsageStatistics::createUsageString(const RsIdentityUsage& u) const
{
QString service_name;
RetroShareLink::enumType service_type = RetroShareLink::TYPE_UNKNOWN;
switch(u.mServiceId)
{
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("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 ");
case RsServiceType::GXSCIRCLE: service_name = tr("GxsCircles"); service_type = RetroShareLink::TYPE_CIRCLES; break ;
#ifdef RS_USE_WIRE
case RsServiceType::WIRE: service_name = tr("Wire"); service_type = RetroShareLink::TYPE_WIRE; break ;
#endif
default:
service_name = tr("Unknown (service=")+QString::number((int)u.mServiceId,16)+")"; service_type = RetroShareLink::TYPE_UNKNOWN ;
}
switch(u.mUsageCode)
{
case RsIdentityUsage::UNKNOWN_USAGE:
return tr("[Unknown]") ;
case RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION: // These 2 are normally not normal GXS identities, but nothing prevents it to happen either.
return tr("Admin signature in service %1").arg(service_name);
case RsIdentityUsage::GROUP_ADMIN_SIGNATURE_VALIDATION:
return tr("Admin signature verification in service %1").arg(service_name);
case RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION: // not typically used, since most services do not require group author signatures
return tr("Creation of author signature in service %1").arg(service_name);
case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION: // most common use case. Messages are signed by authors in e.g. forums.
{
QString label = getGroupName(service_type, u.mGrpId);
RetroShareLink l = RetroShareLink::createGxsGroupLink(service_type, u.mGrpId, label);
return tr("Message signature creation in group %1 of service %2").arg(l.toHtml(), service_name);
}
case RsIdentityUsage::GROUP_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::GROUP_AUTHOR_SIGNATURE_VALIDATION:
{
// label is either the Name or the ID string (from helper)
QString label = getGroupName(service_type, u.mGrpId);
RetroShareLink l = RetroShareLink::createGxsGroupLink(service_type, u.mGrpId, label);
return tr("Group author for group %1 in service %2").arg(l.toHtml(), service_name);
}
case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION:
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;
QString title;
bool titleFound = false;
// Create a set containing only the ID we are looking for
std::set<RsGxsMessageId> msgIds;
msgIds.insert(u.mMsgId);
if (service_type == RetroShareLink::TYPE_CHANNEL && rsGxsChannels) {
std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> cmts; // Local variable to avoid rvalue error
std::vector<RsGxsVote> vots; // Local variable to avoid rvalue error
if (rsGxsChannels->getChannelContent(u.mGrpId, msgIds, posts, cmts, vots)) {
if (!posts.empty()) {
title = QString::fromUtf8(posts[0].mMeta.mMsgName.c_str());
titleFound = !title.isEmpty();
}
}
}
else if (service_type == RetroShareLink::TYPE_FORUM && rsGxsForums) {
std::vector<RsGxsForumMsg> msgs;
// getForumContent only needs 3 arguments, so no extra vectors needed
if (rsGxsForums->getForumContent(u.mGrpId, msgIds, msgs)) {
if (!msgs.empty()) {
title = QString::fromUtf8(msgs[0].mMeta.mMsgName.c_str());
titleFound = !title.isEmpty();
}
}
}
else if (service_type == RetroShareLink::TYPE_POSTED && rsPosted) {
std::vector<RsPostedPost> posts;
std::vector<RsGxsComment> cmts;
std::vector<RsGxsVote> vots;
if (rsPosted->getBoardContent(u.mGrpId, msgIds, posts, cmts, vots)) {
if (!posts.empty()) {
title = QString::fromUtf8(posts[0].mMeta.mMsgName.c_str());
titleFound = !title.isEmpty();
}
}
}
// Prepare the label
QString label;
if (titleFound) {
label = title;
} else {
// Use raw Group ID string if it's a group validation case or total failure
label = QString::fromStdString(u.mGrpId.toStdString());
}
// Generate the link
if ((service_type == RetroShareLink::TYPE_CHANNEL || service_type == RetroShareLink::TYPE_POSTED) && !u.mThreadId.isNull()) {
l = RetroShareLink::createGxsMessageLink(service_type, u.mGrpId, u.mThreadId, label);
}
else {
l = RetroShareLink::createGxsMessageLink(service_type, u.mGrpId, u.mMsgId, label);
}
// Determine the suffix based on the service type
QString suffix;
if (service_type == RetroShareLink::TYPE_CHANNEL || service_type == RetroShareLink::TYPE_POSTED) {
suffix = tr("Vote/comment");
} else {
suffix = tr("Message");
}
return tr("%2 in %3 service %1").arg(l.toHtml(), suffix, 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.
{
ChatId id = ChatId(ChatLobbyId(u.mAdditionalId));
ChatLobbyInfo linfo ;
rsChats->getChatLobbyInfo(ChatLobbyId(u.mAdditionalId),linfo);
RetroShareLink l = RetroShareLink::createChatRoom(id, QString::fromUtf8(linfo.lobby_name.c_str()));
return tr("Message in chat room %1").arg(l.toHtml()) ;
}
case RsIdentityUsage::GLOBAL_ROUTER_SIGNATURE_CHECK: // Global router message validation
{
return tr("Distant message signature validation.");
}
case RsIdentityUsage::GLOBAL_ROUTER_SIGNATURE_CREATION: // Global router message signature
{
return tr("Distant message signature creation.");
}
case RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK: //
{
return tr("Signature validation in distant tunnel system.");
}
case RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CREATION: //
{
return tr("Signature in distant tunnel system.");
}
case RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC: // Group update on that identity data. Can be avatar, name, etc.
{
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
{
return tr("Generic signature validation.");
}
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION: // Any signature made by that identity
{
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:
{
RetroShareLink l;
RsGxsCircleDetails det;
// Try to fetch circle details to get the name
if (rsGxsCircles->getCircleDetails(RsGxsCircleId(u.mGrpId), det)) {
// Prepare the label:
QString label;
if (!det.mCircleName.empty()) {
label = QString::fromUtf8(det.mCircleName.c_str()) ;
} else {
label = QString::fromStdString(u.mGrpId.toStdString());
}
// Create the RetroShareLink for the circle
l = RetroShareLink::createCircle(RsGxsCircleId(u.mGrpId), label);
// Return the formatted string with the clickable link
if (!det.mCircleName.empty()) {
return tr("Membership verification in circle %1.").arg(l.toHtml());
} else {
return tr("Membership verification in circle (ID=%1).").arg(l.toHtml());
}
}
break;
}
#warning TODO! csoler 2017-01-03: Add the different strings and translations here.
default:
return QString("Undone yet");
}
return QString("Unknown");
}
void UsageStatistics::setUsageData(RsGxsIdGroup data)
{
time_t now = time(NULL);
RsIdentityDetails det;
rsIdentity->getIdDetails(RsGxsId(data.mMeta.mGroupId), det);
QString usage_txt;
std::map<rstime_t, RsIdentityUsage> rmap;
for(auto it(det.mUseCases.begin()); it != det.mUseCases.end(); ++it)
rmap.insert(std::make_pair(it->second, it->first));
for(auto it(rmap.begin()); it != rmap.end(); ++it)
usage_txt += QString("<b>") + getHumanReadableDuration(now - data.mLastUsageTS) + "</b> \t: " + createUsageString(it->second) + "<br/>";
if(usage_txt.isEmpty()) // .isNull() can sometimes be tricky, .isEmpty() is safer here
usage_txt = tr("<b>[No record in current session]</b>");
ui->usageStatistics_TB->setText(usage_txt);
}
QString UsageStatistics::getGroupName(uint32_t service_type, const RsGxsGroupId& groupId) const
{
std::list<RsGxsGroupId> groupIds;
groupIds.push_back(groupId);
if (service_type == RetroShareLink::TYPE_CHANNEL && rsGxsChannels) {
std::vector<RsGxsChannelGroup> groups;
if (rsGxsChannels->getChannelsInfo(groupIds, groups) && !groups.empty()) {
QString name = QString::fromUtf8(groups[0].mMeta.mGroupName.c_str());
if (!name.isEmpty()) return name;
}
}
else if (service_type == RetroShareLink::TYPE_FORUM && rsGxsForums) {
std::vector<RsGxsForumGroup> groups;
if (rsGxsForums->getForumsInfo(groupIds, groups) && !groups.empty()) {
QString name = QString::fromUtf8(groups[0].mMeta.mGroupName.c_str());
if (!name.isEmpty()) return name;
}
}
else if (service_type == RetroShareLink::TYPE_POSTED && rsPosted) {
std::vector<RsPostedGroup> groups;
if (rsPosted->getBoardsInfo(groupIds, groups) && !groups.empty()) {
QString name = QString::fromUtf8(groups[0].mMeta.mGroupName.c_str());
if (!name.isEmpty()) return name;
}
}
else if (service_type == RetroShareLink::TYPE_CIRCLES && rsGxsCircles) {
RsGxsCircleDetails det;
if (rsGxsCircles->getCircleDetails(RsGxsCircleId(groupId), det)) {
QString name = QString::fromUtf8(det.mCircleName.c_str());
if (!name.isEmpty()) return name;
}
}
#ifdef RS_USE_WIRE
else if (service_type == RetroShareLink::TYPE_WIRE && rsWire) {
RsWireGroupSPtr group; // Shared pointer for the result
if (rsWire->getWireGroup(groupId, group) && group) {
QString name = QString::fromUtf8(group->mMeta.mGroupName.c_str());
if (!name.isEmpty()) return name;
}
}
#endif
// Returns the raw ID string as the label
return QString::fromStdString(groupId.toStdString());
}
QString UsageStatistics::getMessageTitle(uint32_t service_type, const RsGxsGroupId& groupId, const RsGxsMessageId& msgId) const
{
std::set<RsGxsMessageId> msgIds;
msgIds.insert(msgId);
if (service_type == RetroShareLink::TYPE_CHANNEL && rsGxsChannels) {
std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> cmts;
std::vector<RsGxsVote> vots;
if (rsGxsChannels->getChannelContent(groupId, msgIds, posts, cmts, vots) && !posts.empty()) {
return QString::fromUtf8(posts[0].mMeta.mMsgName.c_str());
}
}
else if (service_type == RetroShareLink::TYPE_FORUM && rsGxsForums) {
std::vector<RsGxsForumMsg> msgs;
if (rsGxsForums->getForumContent(groupId, msgIds, msgs) && !msgs.empty()) {
return QString::fromUtf8(msgs[0].mMeta.mMsgName.c_str());
}
}
else if (service_type == RetroShareLink::TYPE_POSTED && rsPosted) {
std::vector<RsPostedPost> posts;
std::vector<RsGxsComment> cmts;
std::vector<RsGxsVote> vots;
if (rsPosted->getBoardContent(groupId, msgIds, posts, cmts, vots) && !posts.empty()) {
return QString::fromUtf8(posts[0].mMeta.mMsgName.c_str());
}
}
else if (service_type == RetroShareLink::TYPE_CIRCLES && rsGxsCircles) {
RsGxsCircleDetails det;
if (rsGxsCircles->getCircleDetails(RsGxsCircleId(groupId), det)) {
return QString::fromUtf8(det.mCircleName.c_str());
}
}
return QString(); // Return empty if not found
}

View File

@ -0,0 +1,58 @@
/*******************************************************************************
* retroshare-gui/src/gui/Identity/UsageStatistics.h *
* *
* Copyright (C) 2026 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/>. *
* *
*******************************************************************************/
#ifndef USAGESTATISTICS_H
#define USAGESTATISTICS_H
#include "gui/gxs/RsGxsUpdateBroadcastPage.h"
#include "retroshare/rsidentity.h"
namespace Ui {
class UsageStatistics;
}
class UsageStatistics : public QWidget
{
Q_OBJECT
public:
UsageStatistics(QWidget *parent = 0);
~UsageStatistics();
void setUsageData(RsGxsIdGroup data);
protected:
private slots:
private:
QString createUsageString(const RsIdentityUsage& u) const;
QString getGroupName(uint32_t service_type, const RsGxsGroupId& groupId) const;
QString getMessageTitle(uint32_t service_type, const RsGxsGroupId& groupId, const RsGxsMessageId& msgId) const;
/* UI - Designer */
Ui::UsageStatistics *ui;
};
#endif

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UsageStatistics</class>
<widget class="QWidget" name="UsageStatistics">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<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 row="0" column="0">
<widget class="QGroupBox" name="usageStatisticsGBox">
<property name="title">
<string>Usage statistics</string>
</property>
<layout class="QHBoxLayout" name="usageStatisticsGBoxHLayout">
<item>
<widget class="RSTextBrowser" name="usageStatistics_TB"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RSTextBrowser</class>
<extends>QTextBrowser</extends>
<header>gui/common/RSTextBrowser.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -80,7 +80,7 @@
#include "gui/GetStartedDialog.h"
#endif
#ifdef RS_USE_CIRCLES
#ifdef RS_USE_NEW_PEOPLE
#include "gui/People/PeopleDialog.h"
#endif
#include "idle/idle.h"
@ -456,10 +456,10 @@ void MainWindow::initStackedPage()
addPage(gxsforumDialog = new GxsForumsDialog(ui->stackPages), grp, &notify);
addPage(postedDialog = new PostedDialog(ui->stackPages), grp, &notify);
#ifdef RS_USE_NEW_PEOPLE_DIALOG
#ifdef RS_USE_NEW_PEOPLE
PeopleDialog *peopleDialog = NULL;
addPage(peopleDialog = new PeopleDialog(ui->stackPages), grp, &notify);
#endif
#endif
#ifdef RS_USE_WIKI
wikiDialog = NULL;
addPage(wikiDialog = new WikiDialog(ui->stackPages), grp, &notify);

View File

@ -52,6 +52,12 @@ CircleWidget::CircleWidget(QString name/*=QString()*/
setAcceptDrops(true);
}
void CircleWidget::mousePressEvent(QMouseEvent *event)
{
emit clicked();
FlowLayoutItem::mousePressEvent(event);
}
CircleWidget::~CircleWidget()
{
delete _scene;

View File

@ -59,10 +59,14 @@ public:
signals:
void askForGXSIdentityWidget(RsGxsId gxs_id);
void askForPGPIdentityWidget(RsPgpId pgp_id);
void clicked(); // emitted when circle is clicked (for selection state machine)
private slots:
void updateIdImage();
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
private:
void updateScene();

View File

@ -209,8 +209,8 @@ void IdentityWidget::setIsSelected(bool value)
void IdentityWidget::setIsCurrent(bool value)
{
m_isCurrent=value;
ui->labelKeyId->setVisible(value);
ui->labelGXSId->setVisible(value && (_haveGXSId && _havePGPDetail));
ui->labelKeyId->setVisible(false); // disabled by default
ui->labelGXSId->setVisible(false); // disable by default
ui->labelPositive->setVisible(value);
ui->labelNegative->setVisible(value);
ui->label_PosIcon_2->setVisible(value);
@ -223,3 +223,22 @@ void IdentityWidget::pbAdd_clicked()
emit addButtonClicked();
}
uint32_t IdentityWidget::getReputation() const
{
RsReputationInfo info;
// Use the logic to fetch reputation
if (rsReputations->getReputationInfo(RsGxsId(_group_info.mMeta.mGroupId), _group_info.mPgpId, info)) {
return info.mFriendsPositiveVotes;
}
return 0; // Default if no info found
}
void IdentityWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
emit addButtonClicked();
} else {
emit clicked();
}
FlowLayoutItem::mousePressEvent(event);
}

View File

@ -63,9 +63,15 @@ public:
const QString nickname() const { return _nickname; }
const QString gxsId() const { return _gxsId; }
const QImage avatar() const { return _avatar; }
uint32_t getReputation() const;
signals:
void addButtonClicked();
void clicked();
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
private slots:
void pbAdd_clicked();

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@
#include "gui/People/CircleWidget.h"
#include "gui/People/IdentityWidget.h"
#include "gui/Identity/UsageStatistics.h"
#include "gui/gxs/RsGxsUpdateBroadcastPage.h"
#include "util/TokenQueue.h"
@ -59,7 +60,7 @@ class PeopleDialog : public MainPage, public Ui::PeopleDialog, public TokenRespo
void insertCircles(uint32_t token) ;
protected:
// Derives from RsGxsUpdateBroadcastPage
// Derives from MainPage
virtual void updateDisplay(bool complete);
//End RsGxsUpdateBroadcastPage
@ -88,16 +89,51 @@ private slots:
void personDetails();
void sendInvite();
void addtoContacts();
void filterChanged(const QString &text);
void sortByName();
void sortByPopularity();
void clearAllSelections();
void onIdentitySelected();
void onCircleSelected(); // circle widget clicked → circle-selected state
void onCircleTreeItemClicked(QTreeWidgetItem *item, int column);
void onCircleTreeContextMenuRequested(const QPoint &pos);
void onBackClicked(); // back button → return to no-selection state
void clearPerson();
void toggleStackedPage();
void toggledetailsStackedPage();
void onDetailsPageChanged(int index);
void modifyReputation();
void requestJoinLeaveCircle(); // join/leave button in circle details page
private:
void reloadAll();
void populatePictureFlowExt();
void populatePictureFlowInt();
void applySortAndFilter(bool);
void loadIdentityLabels(const RsGxsIdGroup& data);
void populateCirclesTree(const std::list<RsGroupMetaData>& circles);
void setVoteControlsVisible(bool visible);
// Selection state machine
enum class SelectionMode { None, Identity, Circle };
void showNoneSelected();
void showIdentitySelected(const RsGxsId& id);
void showCircleSelected(const RsGxsGroupId& circleId);
void loadCircleLabels(const CircleWidget* cw);
void loadCircleLabels(const RsGxsGroupId& circleId);
TokenQueue *mIdentityQueue;
TokenQueue *mCirclesQueue;
//RsGxsUpdateBroadcastBase *mCirclesBroadcastBase ;
RsGxsId mCurrentSelectedId; // Store the ID of the person currently clicked
QWidget *UsagePage;
// Selection state
SelectionMode _selectionMode = SelectionMode::None;
RsGxsId _selectedGxsId;
RsGxsGroupId _selectedCircleId;
bool _inSelectionUpdate = false;
std::set<RsGxsGroupId> _requestedCircles;
FlowLayout *_flowLayoutExt;
std::map<RsGxsId,IdentityWidget *> _gxs_identity_widgets ;

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>727</width>
<height>524</height>
<width>988</width>
<height>529</height>
</rect>
</property>
<property name="sizePolicy">
@ -51,6 +51,45 @@
<string>People</string>
</attribute>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="LineEditClear" name="filterLineEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="filterButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/mail/filter24.png</normaloff>:/icons/mail/filter24.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="switchButton">
<property name="toolTip">
<string>View circles</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/circles.png</normaloff>:/icons/png/circles.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitterExternal">
<property name="orientation">
@ -60,7 +99,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
<height>80</height>
</size>
</property>
<property name="widgetResizable">
@ -71,39 +110,613 @@
<rect>
<x>0</x>
<y>0</y>
<width>701</width>
<height>248</height>
<width>962</width>
<height>78</height>
</rect>
</property>
<layout class="QVBoxLayout"/>
</widget>
</widget>
<widget class="QFrame" name="widgetExternal">
<layout class="QGridLayout" name="layoutExternal">
<item row="0" column="0">
<widget class="QLabel" name="label_External">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Drag your circles or people to each other.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="PictureFlow" name="pictureFlowWidgetExternal" native="true">
<property name="widgetResizable" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<widget class="QStackedWidget" name="widgetExternal">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="widgetExternalPage">
<layout class="QGridLayout" name="layoutExternal">
<item row="0" column="0">
<widget class="QLabel" name="label_External">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Drag your circles or people to each other.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="PictureFlow" name="pictureFlowWidgetExternal" native="true">
<property name="widgetResizable" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="widgetPeoplePage">
<layout class="QGridLayout" name="gridLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QLabel" name="avatarLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="text">
<string extracomment="Click here to change your avatar">Your Avatar</string>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="editButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Edit Identity</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="inviteButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Send Invite</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="joinLeaveCircleButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-family:'ui-monospace','SFMono-Regular','Menlo','Monaco','Consolas','Liberation Mono','Courier New','monospace'; font-size:12px; color:#101010; background-color:rgba(198,236,203,0.376471);&quot;&gt;Request to join&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Request</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="avatarOpinionHLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="label_PosIcon">
<property name="maximumSize">
<size>
<width>34</width>
<height>34</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/png/thumbs-up.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_positive">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="toolTip">
<string>Positive votes</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_Opinion">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_NegIcon">
<property name="maximumSize">
<size>
<width>34</width>
<height>34</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/png/thumbs-down.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_negative">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="toolTip">
<string>Negative votes</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="avatarVSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>118</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="statsButton">
<property name="text">
<string>View Stats</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>9</number>
</property>
<item>
<widget class="ElidedLabel" name="headerTextLabel_Person">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string>People</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="backButton">
<property name="text">
<string>Back</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/arrow-left.png</normaloff>:/icons/png/arrow-left.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QStackedWidget" name="detailsStackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>299</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="detailsPage">
<layout class="QGridLayout" name="gridLayout">
<item row="7" column="0">
<widget class="QLabel" name="neighborNodesOpinion_LB">
<property name="text">
<string>Votes:</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<widget class="QLineEdit" name="neighborNodesOpinion_TF">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Average opinion of neighbor nodes about this identity. Negative is bad,&lt;/p&gt;&lt;p&gt;positive is good. Zero is neutral.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_KeyId">
<property name="text">
<string>Identity ID :</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit_LastUsed">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_GpgId">
<property name="text">
<string>Owner node ID :</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_LastUsed">
<property name="text">
<string>Last used:</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit_KeyId">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="4" rowspan="9">
<widget class="QTreeWidget" name="circlesTreeWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Circle Name</string>
</property>
</column>
<column>
<property name="text">
<string>Members</string>
</property>
</column>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_Type">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="6" column="2" colspan="2">
<widget class="QCheckBox" name="autoBanIdentities_CB">
<property name="toolTip">
<string>Auto-Ban all identities signed by the same node</string>
</property>
<property name="text">
<string>Auto-Ban profile</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit_Type">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_YourOpinion">
<property name="text">
<string>Your opinion:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="RSComboBox" name="ownOpinion_CB">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans'; font-size:9pt;&quot;&gt;Your own opinion about an identity rules the visibility of that identity for yourself and your friend nodes. Your own opinion is shared among friends and used to compute a reputation score: If your opinion about an identity is neutral, the reputation score is the difference between friend's positive and negative opinions. If not, your own opinion gives the score.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans'; font-size:9pt;&quot;&gt;The overall score is used in chat lobbies, forums and channels to decide on the actions to take for each specific identity. When the overall score is lower than -1, the identity is banned, which prevents all messages and forums/channels authored by this identity to be forwarded, both ways. Some forums also have special anti-spam flags that require a non negative reputation level, making them more sensitive to bad opinions. Banned identities gradually lose their activity and eventually disappear (after 5 days).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans'; font-size:9pt;&quot;&gt;You can change the thresholds and the time of inactivity to delete identities in preferences -&amp;gt; people. &lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<item>
<property name="text">
<string>Negative</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/thumbs-down.png</normaloff>:/icons/png/thumbs-down.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Neutral</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/thumbs-neutral.png</normaloff>:/icons/png/thumbs-neutral.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Positive</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/thumbs-up.png</normaloff>:/icons/png/thumbs-up.png</iconset>
</property>
</item>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit_GpgId">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_PublishTS">
<property name="text">
<string>Created on :</string>
</property>
</widget>
</item>
<item row="7" column="3">
<widget class="QLineEdit" name="overallOpinion_TF">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Overall reputation score, accounting for yours and your friends'.&lt;/p&gt;&lt;p&gt;Negative is bad, positive is good. Zero is neutral. If the score is too low,&lt;/p&gt;&lt;p&gt;the identity is flagged as bad, and will be filtered out in forums, chat lobbies,&lt;/p&gt;&lt;p&gt;channels, etc.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit_GpgName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_GpgName">
<property name="text">
<string>Owner node name :</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="4">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit_PublishTS">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="circleDetailsPage">
<layout class="QGridLayout" name="circleDetailsLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_CircleName">
<property name="text">
<string>Circle Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="circleNameEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_CircleId">
<property name="text">
<string>Circle ID:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="circleIdEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_CircleType">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="circleTypeEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_CircleMembers">
<property name="text">
<string>Members:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="circleMemberCountEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<spacer name="circleDetailsSpacer">
<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>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
@ -129,7 +742,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
<height>80</height>
</size>
</property>
<property name="widgetResizable">
@ -140,8 +753,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>701</width>
<height>248</height>
<width>962</width>
<height>164</height>
</rect>
</property>
<layout class="QVBoxLayout"/>
@ -183,6 +796,11 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LineEditClear</class>
<extends>QLineEdit</extends>
<header>gui/common/LineEditClear.h</header>
</customwidget>
<customwidget>
<class>PictureFlow</class>
<extends>QWidget</extends>
@ -195,9 +813,20 @@
<header location="global">gui/common/FlowLayout.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>gui/common/ElidedLabel.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -596,8 +596,10 @@ QSize FlowLayout::minimumSize() const
{
QSize size;
QLayoutItem *item;
foreach (item, m_itemList)
foreach (item, m_itemList) {
if (item->widget() && !item->widget()->isVisible()) continue;
size = size.expandedTo(item->minimumSize());
}
QMargins margins = contentsMargins();
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
@ -681,6 +683,8 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
for (int curs=0; curs<count; ++curs) {
QLayoutItem *item=m_itemList.value(curs);
QWidget *wid = item->widget();
// Skip hidden widgets so they don't leave empty gaps in the flow
if (!wid || !wid->isVisible()) continue;
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(

View File

@ -111,6 +111,7 @@ CONFIG += gxschannels
CONFIG += posted
CONFIG += gxsgui
CONFIG += gxscircles
CONFIG += gxspeople
# Other Disabled Bits.
#CONFIG += framecatcher
@ -1277,22 +1278,24 @@ identities {
gui/Identity/IdentityListModel.h \
gui/Identity/IdEditDialog.h \
gui/Identity/IdDetailsDialog.h \
gui/Identity/UsageStatistics.h \
FORMS += gui/Identity/IdDialog.ui \
gui/Identity/IdEditDialog.ui \
gui/Identity/IdDetailsDialog.ui \
gui/Identity/UsageStatistics.ui \
SOURCES += \
gui/Identity/IdDialog.cpp \
gui/Identity/IdentityListModel.cpp \
gui/Identity/IdEditDialog.cpp \
gui/Identity/IdDetailsDialog.cpp \
gui/Identity/UsageStatistics.cpp \
}
gxscircles {
DEFINES += RS_USE_CIRCLES
# DEFINES += RS_USE_NEW_PEOPLE_DIALOG
HEADERS += \
gui/Circles/CirclesDialog.h \
@ -1304,27 +1307,24 @@ gxscircles {
SOURCES += \
gui/Circles/CirclesDialog.cpp \
gui/Circles/CreateCircleDialog.cpp \
}
gxspeople {
# Enable the new PeopleDialog by adding CONFIG += rs_new_people_dialog
# on the qmake command line or at the top of this .pro file.
DEFINES += RS_USE_NEW_PEOPLE
HEADERS += gui/People/PeopleDialog.h
HEADERS += gui/People/CircleWidget.h
HEADERS += gui/People/IdentityWidget.h
FORMS += gui/People/PeopleDialog.ui
FORMS += gui/People/PeopleDialog.ui
FORMS += gui/People/CircleWidget.ui
FORMS += gui/People/IdentityWidget.ui
SOURCES += gui/People/PeopleDialog.cpp
SOURCES += gui/People/PeopleDialog.cpp
SOURCES += gui/People/CircleWidget.cpp
SOURCES += gui/People/IdentityWidget.cpp
#HEADERS += gui/People/IdentityItem.h
#HEADERS += gui/People/CircleItem.h
#HEADERS += gui/People/GroupListView.h
#SOURCES += gui/People/GroupListView.cpp
#SOURCES += gui/People/IdentityItem.cpp
#SOURCES += gui/People/CircleItem.cpp
}