Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 75 additions & 12 deletions src/gui/statusbar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@
#include <QDebug>
#include <QFrame>
#include <QHBoxLayout>
#include <QHostAddress>
#include <QLabel>
#include <QPushButton>
#include <QStyle>

#include "base/bittorrent/session.h"
#include "base/bittorrent/sessionstatus.h"
#include "base/net/geoipmanager.h"
#include "base/preferences.h"
#include "base/utils/misc.h"
#include "speedlimitdialog.h"
Expand Down Expand Up @@ -104,9 +106,13 @@ StatusBar::StatusBar(QWidget *parent)
m_freeDiskSpaceLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
m_freeDiskSpaceSeparator = createSeparator(m_freeDiskSpaceLbl);

m_lastExternalIPsLbl = new QLabel(tr("External IP: N/A"));
m_lastExternalIPsLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
m_lastExternalIPsSeparator = createSeparator(m_lastExternalIPsLbl);
m_externalIPsContainer = new QWidget(this);
m_externalIPsContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
auto *externalIPsLayout = new QHBoxLayout(m_externalIPsContainer);
externalIPsLayout->setContentsMargins(0, 0, 0, 0);
externalIPsLayout->setSpacing(0);
externalIPsLayout->addWidget(new QLabel(tr("External IP: N/A"), m_externalIPsContainer));
m_lastExternalIPsSeparator = createSeparator(m_externalIPsContainer);

m_DHTLbl = new QLabel(tr("DHT: %1 nodes").arg(0), this);
m_DHTLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
Expand Down Expand Up @@ -135,7 +141,7 @@ StatusBar::StatusBar(QWidget *parent)
layout->addWidget(m_freeDiskSpaceLbl);
layout->addWidget(m_freeDiskSpaceSeparator);

layout->addWidget(m_lastExternalIPsLbl);
layout->addWidget(m_externalIPsContainer);
layout->addWidget(m_lastExternalIPsSeparator);

layout->addWidget(m_DHTLbl);
Expand Down Expand Up @@ -249,25 +255,82 @@ void StatusBar::updateFreeDiskSpaceVisibility()

void StatusBar::updateExternalAddressesLabel()
{
QHBoxLayout *const externalIPsLayout = qobject_cast<QHBoxLayout *>(m_externalIPsContainer->layout());
Q_ASSERT(externalIPsLayout);

while (QLayoutItem *item = externalIPsLayout->takeAt(0))
{
if (QWidget *w = item->widget())
w->deleteLater();
delete item;
}

const QString lastExternalIPv4Address = BitTorrent::Session::instance()->lastExternalIPv4Address();
const QString lastExternalIPv6Address = BitTorrent::Session::instance()->lastExternalIPv6Address();
QString addressText = tr("External IP: N/A");

const bool hasIPv4Address = !lastExternalIPv4Address.isEmpty();
const bool hasIPv6Address = !lastExternalIPv6Address.isEmpty();

if (hasIPv4Address && hasIPv6Address)
addressText = tr("External IPs: %1, %2").arg(lastExternalIPv4Address, lastExternalIPv6Address);
else if (hasIPv4Address || hasIPv6Address)
addressText = tr("External IP: %1%2").arg(lastExternalIPv4Address, lastExternalIPv6Address);
const bool resolveCountries = Preferences::instance()->resolvePeerCountries();
Net::GeoIPManager *const geoIP = Net::GeoIPManager::instance();

QString prefix;
if (!hasIPv4Address && !hasIPv6Address)
prefix = tr("External IP: N/A");
else if (hasIPv4Address && hasIPv6Address)
prefix = tr("External IPs: ");
else
prefix = tr("External IP: ");

m_lastExternalIPsLbl->setText(addressText);
externalIPsLayout->addWidget(new QLabel(prefix, m_externalIPsContainer));

const auto addIPWithOptionalFlag = [this, externalIPsLayout, resolveCountries, geoIP]
(const QString &address)
{
if (address.isEmpty())
return;
QString countryCode;
QString countryName;
if (resolveCountries && geoIP)
{
const QHostAddress hostAddr(address);
if (!hostAddr.isNull())
{
countryCode = geoIP->lookup(hostAddr);
if (!countryCode.isEmpty())
{
countryCode = countryCode.toLower();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When hovering over the flag in the GUI, N/A appears briefly and then disappears.

The "N/A" part is because of this.
The duration of the tooltip appears to be affected by Refresh interval in advanced settings or when some item in the toolbar is updated e.g. DHT nodes.

countryName = Net::GeoIPManager::CountryName(countryCode);
}
}
}
if (!countryCode.isEmpty())
{
const QIcon flagIcon = UIThemeManager::instance()->getFlagIcon(countryCode);
if (!flagIcon.isNull())
{
QLabel *flagLbl = new QLabel(m_externalIPsContainer);
flagLbl->setPixmap(flagIcon.pixmap(Utils::Gui::smallIconSize()));
flagLbl->setToolTip(countryName);
flagLbl->setContentsMargins(0, 0, 3, 0);
externalIPsLayout->addWidget(flagLbl);
}
}
auto *ipLbl = new QLabel(address, m_externalIPsContainer);
externalIPsLayout->addWidget(ipLbl);
};

if (hasIPv4Address)
addIPWithOptionalFlag(lastExternalIPv4Address);
if (hasIPv4Address && hasIPv6Address)
externalIPsLayout->addWidget(new QLabel(u", "_s, m_externalIPsContainer));
if (hasIPv6Address)
addIPWithOptionalFlag(lastExternalIPv6Address);
}

void StatusBar::updateExternalAddressesVisibility()
{
const bool isVisible = Preferences::instance()->isStatusbarExternalIPDisplayed();
m_lastExternalIPsLbl->setVisible(isVisible);
m_externalIPsContainer->setVisible(isVisible);
m_lastExternalIPsSeparator->setVisible(isVisible);
}

Expand Down
2 changes: 1 addition & 1 deletion src/gui/statusbar.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private slots:
QPushButton *m_upSpeedLbl = nullptr;
QLabel *m_freeDiskSpaceLbl = nullptr;
QWidget *m_freeDiskSpaceSeparator = nullptr;
QLabel *m_lastExternalIPsLbl = nullptr;
QWidget *m_externalIPsContainer = nullptr;
QWidget *m_lastExternalIPsSeparator = nullptr;
QLabel *m_DHTLbl = nullptr;
QWidget *m_DHTSeparator = nullptr;
Expand Down
24 changes: 22 additions & 2 deletions src/webui/api/synccontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "synccontroller.h"

#include <QFuture>
#include <QHostAddress>
#include <QJsonArray>
#include <QJsonObject>
#include <QMetaObject>
Expand Down Expand Up @@ -88,6 +89,10 @@ namespace
const QString KEY_TRANSFER_FREESPACEONDISK = u"free_space_on_disk"_s;
const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4 = u"last_external_address_v4"_s;
const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6 = u"last_external_address_v6"_s;
const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4_COUNTRY_CODE = u"last_external_address_v4_country_code"_s;
const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6_COUNTRY_CODE = u"last_external_address_v6_country_code"_s;
const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4_COUNTRY = u"last_external_address_v4_country"_s;
const QString KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6_COUNTRY = u"last_external_address_v6_country"_s;
const QString KEY_TRANSFER_UPDATA = u"up_info_data"_s;
const QString KEY_TRANSFER_UPRATELIMIT = u"up_rate_limit"_s;
const QString KEY_TRANSFER_UPSPEED = u"up_info_speed"_s;
Expand Down Expand Up @@ -184,8 +189,23 @@ namespace
map[KEY_TRANSFER_AVERAGE_TIME_QUEUE] = cacheStatus.averageJobTime;
map[KEY_TRANSFER_TOTAL_QUEUED_SIZE] = cacheStatus.queuedBytes;

map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4] = session->lastExternalIPv4Address();
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6] = session->lastExternalIPv6Address();
const QString lastExternalIPv4Address = session->lastExternalIPv4Address();
const QString lastExternalIPv6Address = session->lastExternalIPv6Address();
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4] = lastExternalIPv4Address;
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6] = lastExternalIPv6Address;

const bool resolvePeerCountries = Preferences::instance()->resolvePeerCountries();
if (resolvePeerCountries)
{
const QString ip4CountryCode = Net::GeoIPManager::instance()->lookup(QHostAddress(lastExternalIPv4Address));
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4_COUNTRY_CODE] = ip4CountryCode.toLower();
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V4_COUNTRY] = Net::GeoIPManager::CountryName(ip4CountryCode);

const QString ip6CountryCode = Net::GeoIPManager::instance()->lookup(QHostAddress(lastExternalIPv6Address));
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6_COUNTRY_CODE] = ip6CountryCode.toLower();
map[KEY_TRANSFER_LAST_EXTERNAL_ADDRESS_V6_COUNTRY] = Net::GeoIPManager::CountryName(ip6CountryCode);
}

map[KEY_TRANSFER_DHT_NODES] = sessionStatus.dhtNodes;
map[KEY_TRANSFER_CONNECTION_STATUS] = session->isListening()
? (sessionStatus.hasIncomingConnections ? u"connected"_s : u"firewalled"_s)
Expand Down
6 changes: 6 additions & 0 deletions src/webui/www/private/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,12 @@ td.statusBarSeparator {
width: 24px;
}

span.flags {
background: left center / contain no-repeat;
margin-left: 2px;
padding-left: 25px;
}

/* Statistics window */
#statisticsContent {
& table {
Expand Down
69 changes: 63 additions & 6 deletions src/webui/www/private/scripts/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1135,16 +1135,73 @@ window.addEventListener("DOMContentLoaded", async (event) => {
if (window.qBittorrent.Cache.preferences.get().status_bar_external_ip) {
const lastExternalAddressV4 = serverState.last_external_address_v4;
const lastExternalAddressV6 = serverState.last_external_address_v6;
const lastExternalAddressV4CountryCode = serverState.last_external_address_v4_country_code;
const lastExternalAddressV6CountryCode = serverState.last_external_address_v6_country_code;
const lastExternalAddressV4Country = serverState.last_external_address_v4_country;
const lastExternalAddressV6Country = serverState.last_external_address_v6_country;
const hasIPv4Address = lastExternalAddressV4 !== "";
const hasIPv6Address = lastExternalAddressV6 !== "";
let lastExternalAddressLabel = "QBT_TR(External IP: N/A)QBT_TR[CONTEXT=HttpServer]";

// Clear previous content
externalIPsElement.textContent = "";

let labelPrefix = "QBT_TR(External IP: N/A)QBT_TR[CONTEXT=HttpServer]";
if (hasIPv4Address && hasIPv6Address)
lastExternalAddressLabel = "QBT_TR(External IPs: %1, %2)QBT_TR[CONTEXT=HttpServer]";
labelPrefix = "QBT_TR(External IPs: )QBT_TR[CONTEXT=HttpServer]";
else if (hasIPv4Address || hasIPv6Address)
lastExternalAddressLabel = "QBT_TR(External IP: %1%2)QBT_TR[CONTEXT=HttpServer]";
// https://en.wikipedia.org/wiki/IPv6_address#Scoped_literal_IPv6_addresses_(with_zone_index)
lastExternalAddressLabel = lastExternalAddressLabel.replace("%1", lastExternalAddressV4).replace("%2", lastExternalAddressV6);
externalIPsElement.textContent = lastExternalAddressLabel;
labelPrefix = "QBT_TR(External IP: )QBT_TR[CONTEXT=HttpServer]";

if (hasIPv4Address || hasIPv6Address) {
// Add label prefix
const labelSpan = document.createElement("span");
labelSpan.textContent = labelPrefix;
externalIPsElement.appendChild(labelSpan);

// Add IPv4 with flag if available
if (hasIPv4Address) {
if (lastExternalAddressV4CountryCode) {
const flagSpan = document.createElement("span");
flagSpan.classList.add("flags");
flagSpan.style.backgroundImage = `url('images/flags/${lastExternalAddressV4CountryCode || "xx"}.svg')`;
flagSpan.textContent = lastExternalAddressV4;
flagSpan.title = lastExternalAddressV4Country || lastExternalAddressV4;
externalIPsElement.appendChild(flagSpan);
}
else {
const ipSpan = document.createElement("span");
ipSpan.textContent = lastExternalAddressV4;
externalIPsElement.appendChild(ipSpan);
}

// Add separator if both addresses exist
if (hasIPv6Address) {
const separatorSpan = document.createElement("span");
separatorSpan.textContent = ", ";
externalIPsElement.appendChild(separatorSpan);
}
}

// Add IPv6 with flag if available
if (hasIPv6Address) {
if (lastExternalAddressV6CountryCode) {
const flagSpan = document.createElement("span");
flagSpan.classList.add("flags");
flagSpan.style.backgroundImage = `url('images/flags/${lastExternalAddressV6CountryCode || "xx"}.svg')`;
flagSpan.textContent = lastExternalAddressV6;
flagSpan.title = lastExternalAddressV6Country || lastExternalAddressV6;
externalIPsElement.appendChild(flagSpan);
}
else {
const ipSpan = document.createElement("span");
ipSpan.textContent = lastExternalAddressV6;
externalIPsElement.appendChild(ipSpan);
}
}
}
else {
externalIPsElement.textContent = labelPrefix;
}

externalIPsElement.classList.remove("invisible");
externalIPsElement.previousElementSibling.classList.remove("invisible");
}
Expand Down
Loading