Compare commits

...

11 Commits

Author SHA1 Message Date
Chocobo1
f68bc3fef9 Raise 'torrent share ratio' maximum limit
The default from Qt was `99.99` which could be too small for some.
The new limit is `INT_MAX` (not `DOUBLE_MAX`) as to hide the rounding/approximation errors from the user.

PR #23559.
2025-11-30 21:00:16 +08:00
Chocobo1
ed9a8687ad NSIS: set appropriate error code on error
PR #23557.
2025-11-30 19:51:17 +08:00
Chocobo1
6a4b1b9727 GHA CI: enable ccache by default on forked repository
This change only affects forked repository.
Previously ccache was only enabled on `master` branch regardless of the owner of the repository.
This is not ideal for forked repository where the owner (mostly) wants to run the GHA CI to
create binary for their own use and ccache couldn't be utilized. This change will enable
ccache by default for all non-official repositories.

PR #23558.
2025-11-30 19:37:26 +08:00
Vasiliy Kostin
f2f4676824 Add reboot option when downloads complete
This commit implements a new "Reboot System" option that allows users to automatically reboot the computer when all downloads are complete, similar to the existing shutdown, suspend, and hibernate options.

Closes #10774.
PR #23525.
2025-11-30 19:16:48 +08:00
Mark Yu
8b9064a33c WebUI: Do not hide context menu if the click target has submenu
Add check if the click event occurs in the menu item and if the menu item has a submenu, do not close the context menu.

Closes #23532.
PR #23534.
2025-11-30 19:15:23 +08:00
tehcneko
296c90d688 WebUI: Fix row selection by Shift key with virtual list enabled
Fixes #23336.
PR #23543.
2025-11-30 19:14:30 +08:00
tehcneko
564afc975f WebUI: Fix row collapsing with virtual list enabled
Fixes https://github.com/qbittorrent/qBittorrent/issues/23241#issuecomment-3295352816.
PR #23542.
2025-11-30 19:13:10 +08:00
Vladimir Golovnev
a77b17e6da Allow to configure style and color scheme on all platforms
PR #23522.
2025-11-24 09:04:28 +03:00
Hanabishi
4a3922d152 Make the active torrents filter reflect actual transfers
PR #23431.
Closes #23121.
2025-11-24 09:03:20 +03:00
Chocobo1
1b96a48266 GHA CI: test built binary
This would ensure the built binary is able to start up and rule out compiler or library linking
issues.

PR #23529.
2025-11-24 02:51:46 +08:00
Chocobo1
1f6e7519a0 Raise connection max limits
And remove a few redundant properties as they are the same as the default value (or they have the same effect as using the default value).

Closes #23465.
PR #23528.
2025-11-24 02:38:21 +08:00
21 changed files with 170 additions and 142 deletions

View File

@@ -49,7 +49,7 @@ jobs:
- name: Setup ccache
uses: Chocobo1/setup-ccache-action@v1
with:
store_cache: ${{ github.ref == 'refs/heads/master' }}
store_cache: ${{ (github.repository != 'qbittorrent/qBittorrent') || (github.ref == 'refs/heads/master') }}
update_packager_index: false
ccache_options: |
max_size=1G
@@ -119,6 +119,11 @@ jobs:
cmake --build build --target qbt_update_translations
cmake --build build
cmake --build build --target check
if [ "${{ matrix.qbt_gui }}" = "GUI=ON" ]; then
build/qbittorrent.app/Contents/MacOS/qbittorrent -v
else
build/qbittorrent-nox.app/Contents/MacOS/qbittorrent-nox -v
fi
- name: Prepare build artifacts
run: |

View File

@@ -39,12 +39,12 @@ jobs:
sudo apt update
sudo apt install \
build-essential cmake ninja-build \
libssl-dev libxkbcommon-x11-dev libxcb-cursor-dev zlib1g-dev
libssl-dev zlib1g-dev
- name: Setup ccache
uses: Chocobo1/setup-ccache-action@v1
with:
store_cache: ${{ github.ref == 'refs/heads/master' }}
store_cache: ${{ (github.repository != 'qbittorrent/qBittorrent') || (github.ref == 'refs/heads/master') }}
update_packager_index: false
ccache_options: |
max_size=1G
@@ -123,6 +123,11 @@ jobs:
cmake --build build --target qbt_update_translations
cmake --build build
cmake --build build --target check
if [ "${{ matrix.qbt_gui }}" = "GUI=ON" ]; then
QT_QPA_PLATFORM=offscreen build/qbittorrent -v
else
build/qbittorrent-nox -v
fi
DESTDIR="qbittorrent" cmake --install build
- name: Run CodeQL analysis

View File

@@ -157,16 +157,20 @@ ${Case} 0
${IfThen} $3 <> 0 ${|} ${Break} ${|} ;we are admin, let the show go on
${If} $1 = 3 ;RunAs completed successfully, but with a non-admin user
MessageBox mb_YesNo|mb_IconExclamation|mb_TopMost|mb_SetForeground "This ${thing} requires admin privileges, try again" /SD IDNO IDYES uac_tryagain IDNO 0
SetErrorLevel 1314 # WinError.h: `ERROR_PRIVILEGE_NOT_HELD`
${EndIf}
;fall-through and die
${Case} 1223
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "This ${thing} requires admin privileges, aborting!"
SetErrorLevel 1223 # WinError.h: `ERROR_CANCELLED`
Quit
${Case} 1062
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Logon service not running, aborting!"
SetErrorLevel 1062 # WinError.h: `ERROR_SERVICE_NOT_ACTIVE`
Quit
${Default}
MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Unable to elevate , error $0"
SetErrorLevel 1603 # WinError.h: `ERROR_INSTALL_FAILURE`
Quit
${EndSwitch}

View File

@@ -776,8 +776,9 @@ void Application::allTorrentsFinished()
bool isShutdown = pref->shutdownWhenDownloadsComplete();
bool isSuspend = pref->suspendWhenDownloadsComplete();
bool isHibernate = pref->hibernateWhenDownloadsComplete();
bool isReboot = pref->rebootWhenDownloadsComplete();
bool haveAction = isExit || isShutdown || isSuspend || isHibernate;
const bool haveAction = isExit || isShutdown || isSuspend || isHibernate || isReboot;
if (!haveAction) return;
ShutdownDialogAction action = ShutdownDialogAction::Exit;
@@ -787,6 +788,8 @@ void Application::allTorrentsFinished()
action = ShutdownDialogAction::Hibernate;
else if (isShutdown)
action = ShutdownDialogAction::Shutdown;
else if (isReboot)
action = ShutdownDialogAction::Reboot;
#ifndef DISABLE_GUI
// ask confirm
@@ -808,6 +811,7 @@ void Application::allTorrentsFinished()
pref->setShutdownWhenDownloadsComplete(false);
pref->setSuspendWhenDownloadsComplete(false);
pref->setHibernateWhenDownloadsComplete(false);
pref->setRebootWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting
m_shutdownAct = action;
}

View File

@@ -1186,25 +1186,7 @@ bool TorrentImpl::isCompleted() const
bool TorrentImpl::isActive() const
{
switch (m_state)
{
case TorrentState::StalledDownloading:
return (uploadPayloadRate() > 0);
case TorrentState::DownloadingMetadata:
case TorrentState::ForcedDownloadingMetadata:
case TorrentState::Downloading:
case TorrentState::ForcedDownloading:
case TorrentState::Uploading:
case TorrentState::ForcedUploading:
case TorrentState::Moving:
return true;
default:
break;
};
return false;
return ((uploadPayloadRate() > 0) || (downloadPayloadRate() > 0));
}
bool TorrentImpl::isInactive() const

View File

@@ -494,10 +494,17 @@ void Preferences::setWinStartup(const bool b)
settings.remove(profileID);
}
}
#endif // Q_OS_WIN
QString Preferences::getStyle() const
{
return value<QString>(u"Appearance/Style"_s);
#ifdef Q_OS_WIN
const QString defaultStyleName = u"Fusion"_s;
#else
const QString defaultStyleName = u"system"_s;
#endif
const auto styleName = value<QString>(u"Appearance/Style"_s);
return styleName.isEmpty() ? defaultStyleName : styleName;
}
void Preferences::setStyle(const QString &styleName)
@@ -507,7 +514,6 @@ void Preferences::setStyle(const QString &styleName)
setValue(u"Appearance/Style"_s, styleName);
}
#endif // Q_OS_WIN
// Downloads
Path Preferences::getScanDirsLastPath() const
@@ -1291,6 +1297,19 @@ void Preferences::setShutdownWhenDownloadsComplete(const bool shutdown)
setValue(u"Preferences/Downloads/AutoShutDownOnCompletion"_s, shutdown);
}
bool Preferences::rebootWhenDownloadsComplete() const
{
return value(u"Preferences/Downloads/AutoRebootOnCompletion"_s, false);
}
void Preferences::setRebootWhenDownloadsComplete(const bool reboot)
{
if (reboot == rebootWhenDownloadsComplete())
return;
setValue(u"Preferences/Downloads/AutoRebootOnCompletion"_s, reboot);
}
bool Preferences::suspendWhenDownloadsComplete() const
{
return value(u"Preferences/Downloads/AutoSuspendOnCompletion"_s, false);

View File

@@ -137,11 +137,11 @@ public:
void setPreventFromSuspendWhenDownloading(bool b);
bool preventFromSuspendWhenSeeding() const;
void setPreventFromSuspendWhenSeeding(bool b);
QString getStyle() const;
void setStyle(const QString &styleName);
#ifdef Q_OS_WIN
bool WinStartup() const;
void setWinStartup(bool b);
QString getStyle() const;
void setStyle(const QString &styleName);
#endif
// Downloads
@@ -287,6 +287,8 @@ public:
bool shutdownWhenDownloadsComplete() const;
void setShutdownWhenDownloadsComplete(bool shutdown);
bool rebootWhenDownloadsComplete() const;
void setRebootWhenDownloadsComplete(bool reboot);
bool suspendWhenDownloadsComplete() const;
void setSuspendWhenDownloadsComplete(bool suspend);
bool hibernateWhenDownloadsComplete() const;

View File

@@ -37,7 +37,8 @@ enum class ShutdownDialogAction
Exit,
Shutdown,
Suspend,
Hibernate
Hibernate,
Reboot
};
using QStringMap = QMap<QString, QString>;

View File

@@ -87,22 +87,30 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
{
::SetSuspendState(TRUE, FALSE, FALSE);
}
else
else if (action == ShutdownDialogAction::Shutdown)
{
std::wstring msg = QCoreApplication::translate("misc"
, "qBittorrent will shutdown the computer now because all downloads are complete.").toStdWString();
::InitiateSystemShutdownW(nullptr, msg.data(), 10, TRUE, FALSE);
}
else if (action == ShutdownDialogAction::Reboot)
{
std::wstring msg = QCoreApplication::translate("misc"
, "qBittorrent will reboot the computer now because all downloads are complete.").toStdWString();
::InitiateSystemShutdownW(nullptr, msg.data(), 10, TRUE, TRUE);
}
// Disable shutdown privilege.
tkp.Privileges[0].Attributes = 0;
::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
#elif defined(Q_OS_MACOS)
AEEventID EventToSend;
if (action != ShutdownDialogAction::Shutdown)
AEEventID EventToSend {};
if (action == ShutdownDialogAction::Suspend)
EventToSend = kAESleep;
else
else if (action == ShutdownDialogAction::Reboot)
EventToSend = kAERestart;
else if (action == ShutdownDialogAction::Shutdown)
EventToSend = kAEShutDown;
AEAddressDesc targetDesc;
const ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess};
@@ -133,7 +141,7 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
#elif defined(QBT_USES_DBUS)
// Use dbus to power off / suspend the system
if (action != ShutdownDialogAction::Shutdown)
if ((action == ShutdownDialogAction::Suspend) || (action == ShutdownDialogAction::Hibernate))
{
// Some recent systems use systemd's logind
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
@@ -166,7 +174,7 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
else
halIface.call(u"Hibernate"_s);
}
else
else if (action == ShutdownDialogAction::Shutdown)
{
// Some recent systems use systemd's logind
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
@@ -190,6 +198,30 @@ void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &ac
QDBusConnection::systemBus());
halIface.call(u"Shutdown"_s);
}
else if (action == ShutdownDialogAction::Reboot)
{
// Some recent systems use systemd's logind
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
u"org.freedesktop.login1.Manager"_s, QDBusConnection::systemBus());
if (login1Iface.isValid())
{
login1Iface.call(u"Reboot"_s, false);
return;
}
// Else, other recent systems use ConsoleKit
QDBusInterface consolekitIface(u"org.freedesktop.ConsoleKit"_s, u"/org/freedesktop/ConsoleKit/Manager"_s,
u"org.freedesktop.ConsoleKit.Manager"_s, QDBusConnection::systemBus());
if (consolekitIface.isValid())
{
consolekitIface.call(u"Restart"_s);
return;
}
// HAL (older systems)
QDBusInterface halIface(u"org.freedesktop.Hal"_s, u"/org/freedesktop/Hal/devices/computer"_s,
u"org.freedesktop.Hal.Device.SystemPowerManagement"_s,
QDBusConnection::systemBus());
halIface.call(u"Reboot"_s);
}
#endif
}

View File

@@ -388,10 +388,16 @@ MainWindow::MainWindow(IGUIApplication *app, const WindowState initialState, con
autoShutdownGroup->addAction(m_ui->actionAutoShutdown);
autoShutdownGroup->addAction(m_ui->actionAutoSuspend);
autoShutdownGroup->addAction(m_ui->actionAutoHibernate);
autoShutdownGroup->addAction(m_ui->actionAutoReboot);
#if (!defined(Q_OS_UNIX) || defined(Q_OS_MACOS)) || defined(QBT_USES_DBUS)
m_ui->actionAutoShutdown->setChecked(pref->shutdownWhenDownloadsComplete());
m_ui->actionAutoReboot->setChecked(pref->rebootWhenDownloadsComplete());
m_ui->actionAutoSuspend->setChecked(pref->suspendWhenDownloadsComplete());
m_ui->actionAutoHibernate->setChecked(pref->hibernateWhenDownloadsComplete());
#ifdef Q_OS_MACOS
// macOS doesn't support Hibernate via Apple Events API
m_ui->actionAutoHibernate->setDisabled(true);
#endif
#else
m_ui->actionAutoShutdown->setDisabled(true);
m_ui->actionAutoSuspend->setDisabled(true);
@@ -1790,30 +1796,31 @@ void MainWindow::on_actionCriticalMessages_triggered(const bool checked)
setExecutionLogMsgTypes(flags);
}
void MainWindow::on_actionAutoExit_toggled(bool enabled)
void MainWindow::on_actionAutoExit_toggled(const bool enabled)
{
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setShutdownqBTWhenDownloadsComplete(enabled);
}
void MainWindow::on_actionAutoSuspend_toggled(bool enabled)
void MainWindow::on_actionAutoSuspend_toggled(const bool enabled)
{
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setSuspendWhenDownloadsComplete(enabled);
}
void MainWindow::on_actionAutoHibernate_toggled(bool enabled)
void MainWindow::on_actionAutoHibernate_toggled(const bool enabled)
{
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setHibernateWhenDownloadsComplete(enabled);
}
void MainWindow::on_actionAutoShutdown_toggled(bool enabled)
void MainWindow::on_actionAutoShutdown_toggled(const bool enabled)
{
qDebug() << Q_FUNC_INFO << enabled;
Preferences::instance()->setShutdownWhenDownloadsComplete(enabled);
}
void MainWindow::on_actionAutoReboot_toggled(const bool enabled)
{
Preferences::instance()->setRebootWhenDownloadsComplete(enabled);
}
void MainWindow::updatePowerManagementState() const
{
const auto *pref = Preferences::instance();

View File

@@ -158,6 +158,7 @@ private slots:
void on_actionAutoSuspend_toggled(bool);
void on_actionAutoHibernate_toggled(bool);
void on_actionAutoShutdown_toggled(bool);
void on_actionAutoReboot_toggled(bool);
void on_actionAbout_triggered();
void on_actionStatistics_triggered();
void on_actionCreateTorrent_triggered();

View File

@@ -76,6 +76,7 @@
<addaction name="actionAutoExit"/>
<addaction name="actionAutoSuspend"/>
<addaction name="actionAutoHibernate"/>
<addaction name="actionAutoReboot"/>
<addaction name="actionAutoShutdown"/>
</widget>
<addaction name="actionCreateTorrent"/>
@@ -397,6 +398,14 @@
<string>Sh&amp;utdown System</string>
</property>
</action>
<action name="actionAutoReboot">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Reboot System</string>
</property>
</action>
<action name="actionAutoShutdownDisabled">
<property name="checkable">
<bool>true</bool>

View File

@@ -43,13 +43,10 @@
#include <QEvent>
#include <QFileDialog>
#include <QMessageBox>
#include <QStyleFactory>
#include <QSystemTrayIcon>
#include <QTranslator>
#ifdef Q_OS_WIN
#include <QStyleFactory>
#endif
#include "base/bittorrent/session.h"
#include "base/bittorrent/sharelimitaction.h"
#include "base/exceptions.h"
@@ -376,11 +373,7 @@ void OptionsDialog::loadBehaviorTabOptions()
m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled());
connect(m_ui->comboLanguage, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
#ifdef Q_OS_WIN
connect(m_ui->comboStyle, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
#endif
#ifdef QBT_HAS_COLORSCHEME_OPTION
connect(m_ui->comboColorScheme, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
#endif
@@ -488,9 +481,7 @@ void OptionsDialog::saveBehaviorTabOptions() const
}
pref->setLocale(locale);
#ifdef Q_OS_WIN
pref->setStyle(m_ui->comboStyle->currentData().toString());
#endif
#ifdef QBT_HAS_COLORSCHEME_OPTION
UIThemeManager::instance()->setColorScheme(m_ui->comboColorScheme->currentData().value<ColorScheme>());
@@ -1126,6 +1117,8 @@ void OptionsDialog::loadBittorrentTabOptions()
m_ui->spinUploadRateForSlowTorrents->setValue(session->uploadRateForSlowTorrents());
m_ui->spinSlowTorrentsInactivityTimer->setValue(session->slowTorrentsInactivityTimer());
m_ui->spinMaxRatio->setMaximum(std::numeric_limits<int>::max());
if (session->globalMaxRatio() >= 0.)
{
// Enable
@@ -1170,7 +1163,7 @@ void OptionsDialog::loadBittorrentTabOptions()
const QHash<BitTorrent::ShareLimitAction, int> actIndex =
{
{BitTorrent::ShareLimitAction::Stop, 0},
{BitTorrent::ShareLimitAction::Stop, 0},
{BitTorrent::ShareLimitAction::Remove, 1},
{BitTorrent::ShareLimitAction::RemoveWithContent, 2},
{BitTorrent::ShareLimitAction::EnableSuperSeeding, 3}
@@ -1846,6 +1839,11 @@ void OptionsDialog::initializeStyleCombo()
#ifdef Q_OS_WIN
m_ui->labelStyleHint->setText(tr("%1 is recommended for best compatibility with Windows dark mode"
, "Fusion is recommended for best compatibility with Windows dark mode").arg(u"Fusion"_s));
#else
m_ui->labelStyleHint->hide();
m_ui->layoutStyle->removeWidget(m_ui->labelStyleHint);
#endif
m_ui->comboStyle->addItem(tr("System", "System default Qt style"), u"system"_s);
m_ui->comboStyle->setItemData(0, tr("Let Qt decide the style for this system"), Qt::ToolTipRole);
m_ui->comboStyle->insertSeparator(1);
@@ -1859,14 +1857,6 @@ void OptionsDialog::initializeStyleCombo()
const QString selectedStyleName = prefStyleName.isEmpty() ? QApplication::style()->name() : prefStyleName;
const int styleIndex = m_ui->comboStyle->findData(selectedStyleName, Qt::UserRole, Qt::MatchFixedString);
m_ui->comboStyle->setCurrentIndex(std::max(0, styleIndex));
#else
m_ui->labelStyle->hide();
m_ui->comboStyle->hide();
m_ui->labelStyleHint->hide();
m_ui->layoutStyle->removeWidget(m_ui->labelStyle);
m_ui->layoutStyle->removeWidget(m_ui->comboStyle);
m_ui->layoutStyle->removeWidget(m_ui->labelStyleHint);
#endif
}
void OptionsDialog::initializeColorSchemeOptions()

View File

@@ -1908,7 +1908,7 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
<number>2</number>
</property>
<property name="maximum">
<number>2000</number>
<number>2147483647</number>
</property>
<property name="value">
<number>500</number>
@@ -1944,7 +1944,7 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
<number>2</number>
</property>
<property name="maximum">
<number>2000</number>
<number>2147483647</number>
</property>
<property name="value">
<number>100</number>
@@ -1961,7 +1961,7 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
<item row="2" column="1">
<widget class="QSpinBox" name="spinMaxUploads">
<property name="maximum">
<number>2000</number>
<number>2147483647</number>
</property>
<property name="value">
<number>8</number>
@@ -1978,7 +1978,7 @@ readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</st
<item row="3" column="1">
<widget class="QSpinBox" name="spinMaxUploadsPerTorrent">
<property name="maximum">
<number>500</number>
<number>2147483647</number>
</property>
<property name="value">
<number>4</number>
@@ -3789,9 +3789,6 @@ Specify an IPv4 or IPv6 address. You can specify &quot;0.0.0.0&quot; for any IPv
<layout class="QHBoxLayout" name="horizontalLayoutAPIKey">
<item>
<widget class="QLineEdit" name="textWebUIAPIKey">
<property name="enabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
@@ -3805,89 +3802,35 @@ Specify an IPv4 or IPv6 address. You can specify &quot;0.0.0.0&quot; for any IPv
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Copy API key</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<iconset>
<normaloff>:/icons/edit-copy.svg</normaloff>:/icons/edit-copy.svg</iconset>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnWebUIAPIKeyRotate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Generate API key</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<iconset>
<normaloff>:/icons/view-refresh.svg</normaloff>:/icons/view-refresh.svg</iconset>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnWebUIAPIKeyDelete">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Delete API key</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<iconset>
<normaloff>:/icons/list-remove.svg</normaloff>:/icons/list-remove.svg</iconset>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>

View File

@@ -134,6 +134,11 @@ void ShutdownConfirmDialog::initText()
okButton->setText(tr("&Hibernate Now"));
setWindowTitle(tr("Hibernate confirmation"));
break;
case ShutdownDialogAction::Reboot:
m_msg = tr("The computer is going to reboot.");
okButton->setText(tr("&Reboot Now"));
setWindowTitle(tr("Reboot confirmation"));
break;
}
m_msg += u'\n';

View File

@@ -28,6 +28,8 @@
#include "torrentsharelimitswidget.h"
#include <limits>
#include "base/bittorrent/torrent.h"
#include "ui_torrentsharelimitswidget.h"
@@ -59,6 +61,7 @@ TorrentShareLimitsWidget::TorrentShareLimitsWidget(QWidget *parent)
m_ui->setupUi(this);
m_ui->spinBoxRatioValue->setEnabled(false);
m_ui->spinBoxRatioValue->setMaximum(std::numeric_limits<int>::max());
m_ui->spinBoxRatioValue->setSuffix({});
m_ui->spinBoxRatioValue->clear();
m_ui->spinBoxSeedingTimeValue->setEnabled(false);

View File

@@ -76,13 +76,11 @@ UIThemeManager::UIThemeManager()
, m_useSystemIcons {Preferences::instance()->useSystemIcons()}
#endif
{
#ifdef Q_OS_WIN
if (const QString styleName = Preferences::instance()->getStyle(); styleName.compare(u"system", Qt::CaseInsensitive) != 0)
{
if (!QApplication::setStyle(styleName.isEmpty() ? u"Fusion"_s : styleName))
if (!QApplication::setStyle(styleName))
LogMsg(tr("Set app style failed. Unknown style: \"%1\"").arg(styleName), Log::WARNING);
}
#endif
#ifdef QBT_HAS_COLORSCHEME_OPTION
applyColorScheme();

View File

@@ -41,7 +41,7 @@
#include "uithemesource.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) && defined(Q_OS_WIN)
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
#define QBT_HAS_COLORSCHEME_OPTION
#endif

View File

@@ -211,6 +211,18 @@ window.qBittorrent.ContextMenu ??= (() => {
// hide on body click
document.body.addEventListener("click", (event) => {
const parentNode = event.target.parentNode;
// make sure the click was on a context menu item
if ((parentNode !== null) && (parentNode.tagName.toLowerCase() === "li")) {
const grandParentNode = parentNode.parentNode;
if ((grandParentNode !== null) && (grandParentNode.classList.contains("contextMenu"))) {
const submenuNodes = parentNode.getElementsByTagName("ul");
if (submenuNodes.length > 0)
return;
}
}
this.hide();
this.options.element = null;
});

View File

@@ -761,13 +761,13 @@ window.qBittorrent.DynamicTable ??= (() => {
}
let select = false;
for (const tr of this.getTrs()) {
if ((tr.rowId === rowId1) || (tr.rowId === rowId2)) {
for (const row of this.getFilteredAndSortedRows()) {
if ((row.rowId === rowId1) || (row.rowId === rowId2)) {
select = !select;
this.selectedRows.push(tr.rowId);
this.selectedRows.push(row.rowId);
}
else if (select) {
this.selectedRows.push(tr.rowId);
this.selectedRows.push(row.rowId);
}
}
this.setRowClass();
@@ -1552,7 +1552,7 @@ window.qBittorrent.DynamicTable ??= (() => {
}
applyFilter(row, filterName, category, tag, trackerHost, filterTerms) {
const state = row["full_data"].state;
const { state, upspeed, dlspeed } = row["full_data"];
let inactive = false;
switch (filterName) {
@@ -1592,11 +1592,7 @@ window.qBittorrent.DynamicTable ??= (() => {
inactive = true;
// fallthrough
case "active": {
let r;
if (state === "stalledDL")
r = (row["full_data"].upspeed > 0);
else
r = (state === "metaDL") || (state === "forcedMetaDL") || (state === "downloading") || (state === "forcedDL") || (state === "uploading") || (state === "forcedUP");
const r = (upspeed > 0) || (dlspeed > 0);
if (r === inactive)
return false;
break;
@@ -2363,6 +2359,9 @@ window.qBittorrent.DynamicTable ??= (() => {
for (const [key, _] of this.collapseState)
this.expandNode(key);
if (this.useVirtualList)
this.rerender();
}
collapseAllNodes() {
@@ -2374,6 +2373,9 @@ window.qBittorrent.DynamicTable ??= (() => {
if (state.depth >= 1)
this.collapseNode(key);
}
if (this.useVirtualList)
this.rerender();
}
#updateNodeVisibility(node, shouldHide) {
@@ -2745,8 +2747,10 @@ window.qBittorrent.DynamicTable ??= (() => {
generateRowsSignature() {
const rowsData = [];
for (const { rowId } of this.getRowValues())
rowsData.push({ ...this.getNode(rowId).serialize(), collapsed: this.isCollapsed(rowId) });
for (const { rowId } of this.getRowValues()) {
const node = this.getNode(rowId);
rowsData.push({ ...node.serialize(), collapsed: this.isCollapsed(node.rowId) });
}
return JSON.stringify(rowsData);
}
@@ -2775,7 +2779,7 @@ window.qBittorrent.DynamicTable ??= (() => {
// sort, then filter
this.#sortNodesByColumn(root, this.columns[this.sortedColumn]);
const rows = (() => {
if (this.filterTerms.length === 0) {
if (!this.useVirtualList && (this.filterTerms.length === 0)) {
const nodeArray = this.fileTree.toArray();
const filteredRows = nodeArray.map(node => this.getRow(node));
return filteredRows;

View File

@@ -74,6 +74,7 @@ window.qBittorrent.PropFiles ??= (() => {
const new_hash = torrentsTable.getCurrentTorrentID();
if (new_hash === "") {
torrentFilesTable.clear();
current_hash = "";
clearTimeout(loadTorrentFilesDataTimer);
return;
}
@@ -176,6 +177,7 @@ window.qBittorrent.PropFiles ??= (() => {
const clear = () => {
torrentFilesTable.clear();
current_hash = "";
};
return exports();