From baa0ca79673aa250fb1bc2fe9ae627c38cda04fa Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 8 Jul 2025 21:55:11 +0800 Subject: [PATCH 01/10] =?UTF-8?q?chore:=E5=88=A0=E9=99=A4=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 80 ++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 1a1c3e98..34d13d5b 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -7,6 +7,9 @@ #include #include #include +#include // 确保 QProcess 头文件已包含 +#include // 确保 QFile 头文件已包含 +#include // 确保 QQueue 头文件已包含 AppDelegate::AppDelegate(QObject *parent) : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) { @@ -54,7 +57,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QString newVersion = index.data(Qt::UserRole + 3).toString(); QString iconPath = index.data(Qt::UserRole + 4).toString(); QString size = index.data(Qt::UserRole + 5).toString(); - QString description = index.data(Qt::UserRole + 6).toString(); + // QString description = index.data(Qt::UserRole + 6).toString(); // description 未使用 QRect rect = option.rect; int margin = 10, spacing = 6, iconSize = 40; @@ -84,7 +87,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QString packageName = index.data(Qt::UserRole + 1).toString(); bool isDownloading = m_downloads.contains(packageName) && m_downloads[packageName].isDownloading; - int progress = m_downloads.value(packageName, DownloadInfo{0, false}).progress; + int progress = m_downloads.value(packageName, DownloadInfo{0, false, false, false}).progress; // 确保 DownloadInfo 有默认构造或匹配的初始化 bool isInstalled = m_downloads.value(packageName).isInstalled; bool isInstalling = m_downloads.value(packageName).isInstalling; @@ -171,14 +174,17 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); if (buttonRect.contains(mouseEvent->pos())) { // 判断是否为“下载完成”状态,如果是则不响应点击 - if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { - // “下载完成”状态,按钮失效,点击无效 + // 同时也要判断是否为“已安装”状态,如果是也不响应点击 + if ((m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) || + (m_downloads.contains(packageName) && m_downloads[packageName].isInstalled)) { + // “下载完成”或“已安装”状态,按钮失效,点击无效 return false; } QString downloadUrl = index.data(Qt::UserRole + 7).toString(); QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - m_downloads[packageName] = {0, true}; + // 假设 DownloadInfo 结构体的构造函数或聚合初始化方式支持四个成员 + m_downloads[packageName] = {0, true, false, false}; // progress, isDownloading, isInstalling, isInstalled m_downloadManager->startDownload(packageName, downloadUrl, outputPath); emit updateDisplay(packageName); return true; @@ -194,11 +200,16 @@ void AppDelegate::startDownloadForAll() { for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); QString packageName = index.data(Qt::UserRole + 1).toString(); - if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled)) - continue; // 跳过正在下载或已安装的 + // 确保跳过正在下载、正在安装或已安装的 + if (m_downloads.contains(packageName) && + (m_downloads[packageName].isDownloading || + m_downloads[packageName].isInstalling || + m_downloads[packageName].isInstalled)) { + continue; + } QString downloadUrl = index.data(Qt::UserRole + 7).toString(); QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - m_downloads[packageName] = {0, true, false}; + m_downloads[packageName] = {0, true, false, false}; // progress, isDownloading, isInstalling, isInstalled m_downloadManager->startDownload(packageName, downloadUrl, outputPath); emit updateDisplay(packageName); } @@ -240,6 +251,9 @@ void AppDelegate::startNextInstall() { if (debPath.isEmpty()) { qWarning() << "未找到deb文件,包名:" << packageName; m_downloads[packageName].isInstalling = false; + // 如果找不到deb文件,也应该根据情况处理是已安装还是未安装 + // 这里只是简单地设置为未安装,并更新显示 + m_downloads[packageName].isInstalled = false; // 确保如果未找到文件则不显示已安装 emit updateDisplay(packageName); m_installingPackage.clear(); startNextInstall(); @@ -262,12 +276,7 @@ void AppDelegate::startNextInstall() { logFile->flush(); QString text = QString::fromLocal8Bit(out); qDebug().noquote() << text; - // 检查“软件包已安装”关键字 - if (text.contains(QStringLiteral("软件包已安装"))) { - m_downloads[packageName].isInstalling = false; - m_downloads[packageName].isInstalled = true; - emit updateDisplay(packageName); - } + // 尽管此处可以检查关键字,但最终的安装状态应由进程退出码决定 }); connect(m_installProcess, &QProcess::readyReadStandardError, this, [this, logFile]() { QByteArray err = m_installProcess->readAllStandardError(); @@ -276,17 +285,28 @@ void AppDelegate::startNextInstall() { qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName, logFile](int /*exitCode*/, QProcess::ExitStatus /*status*/) { + this, [this, packageName, logFile](int exitCode, QProcess::ExitStatus exitStatus) { if (logFile) logFile->close(); - // 若未检测到“软件包已安装”,此处兜底 - if (!m_downloads[packageName].isInstalled) { - m_downloads[packageName].isInstalling = false; + + // === 关键修改 === + // 检查进程是否正常退出且退出码为0,这通常表示成功 + if (exitStatus == QProcess::NormalExit && exitCode == 0) { + m_downloads[packageName].isInstalled = true; // 安装成功,设置为已安装 + qDebug() << "安装成功:" << packageName; + } else { + // 安装失败或异常退出 + m_downloads[packageName].isInstalled = false; // 确保不是已安装状态 + qWarning() << "安装失败或异常退出:" << packageName << "Exit Code:" << exitCode << "Exit Status:" << exitStatus; } - emit updateDisplay(packageName); + // === 关键修改结束 === + + m_downloads[packageName].isInstalling = false; // 安装过程结束 + emit updateDisplay(packageName); // 更新UI显示 + m_installProcess->deleteLater(); m_installProcess = nullptr; m_installingPackage.clear(); - startNextInstall(); + startNextInstall(); // 处理队列中的下一个安装任务 }); } else { // 日志文件无法打开时,仍然要连接原有信号 @@ -294,18 +314,25 @@ void AppDelegate::startNextInstall() { QByteArray out = m_installProcess->readAllStandardOutput(); QString text = QString::fromLocal8Bit(out); qDebug().noquote() << text; - if (text.contains(QStringLiteral("软件包已安装"))) { - m_downloads[packageName].isInstalling = false; - m_downloads[packageName].isInstalled = true; - emit updateDisplay(packageName); - } + // 同样,这里的关键字检查不是最可靠的 }); connect(m_installProcess, &QProcess::readyReadStandardError, this, [this]() { QByteArray err = m_installProcess->readAllStandardError(); qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName](int /*exitCode*/, QProcess::ExitStatus /*status*/) { + this, [this, packageName](int exitCode, QProcess::ExitStatus exitStatus) { + // === 关键修改 (与上面相同) === + if (exitStatus == QProcess::NormalExit && exitCode == 0) { + m_downloads[packageName].isInstalled = true; + qDebug() << "安装成功 (无日志文件):" << packageName; + } else { + m_downloads[packageName].isInstalled = false; + qWarning() << "安装失败或异常退出 (无日志文件):" << packageName << "Exit Code:" << exitCode << "Exit Status:" << exitStatus; + } + // === 关键修改结束 === + + m_downloads[packageName].isInstalling = false; emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; @@ -319,3 +346,4 @@ void AppDelegate::startNextInstall() { args << debPath << "--no-create-desktop-entry"; m_installProcess->start("ssinstall", args); } + -- Gitee From fa815b8df3d49e3852017ec29412670d5ae1833c Mon Sep 17 00:00:00 2001 From: momen Date: Wed, 9 Jul 2025 11:04:07 +0800 Subject: [PATCH 02/10] =?UTF-8?q?fix:=E5=9B=9E=E9=80=80=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 80 +++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 34d13d5b..1a1c3e98 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -7,9 +7,6 @@ #include #include #include -#include // 确保 QProcess 头文件已包含 -#include // 确保 QFile 头文件已包含 -#include // 确保 QQueue 头文件已包含 AppDelegate::AppDelegate(QObject *parent) : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) { @@ -57,7 +54,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QString newVersion = index.data(Qt::UserRole + 3).toString(); QString iconPath = index.data(Qt::UserRole + 4).toString(); QString size = index.data(Qt::UserRole + 5).toString(); - // QString description = index.data(Qt::UserRole + 6).toString(); // description 未使用 + QString description = index.data(Qt::UserRole + 6).toString(); QRect rect = option.rect; int margin = 10, spacing = 6, iconSize = 40; @@ -87,7 +84,7 @@ void AppDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, c QString packageName = index.data(Qt::UserRole + 1).toString(); bool isDownloading = m_downloads.contains(packageName) && m_downloads[packageName].isDownloading; - int progress = m_downloads.value(packageName, DownloadInfo{0, false, false, false}).progress; // 确保 DownloadInfo 有默认构造或匹配的初始化 + int progress = m_downloads.value(packageName, DownloadInfo{0, false}).progress; bool isInstalled = m_downloads.value(packageName).isInstalled; bool isInstalling = m_downloads.value(packageName).isInstalling; @@ -174,17 +171,14 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); if (buttonRect.contains(mouseEvent->pos())) { // 判断是否为“下载完成”状态,如果是则不响应点击 - // 同时也要判断是否为“已安装”状态,如果是也不响应点击 - if ((m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) || - (m_downloads.contains(packageName) && m_downloads[packageName].isInstalled)) { - // “下载完成”或“已安装”状态,按钮失效,点击无效 + if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { + // “下载完成”状态,按钮失效,点击无效 return false; } QString downloadUrl = index.data(Qt::UserRole + 7).toString(); QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - // 假设 DownloadInfo 结构体的构造函数或聚合初始化方式支持四个成员 - m_downloads[packageName] = {0, true, false, false}; // progress, isDownloading, isInstalling, isInstalled + m_downloads[packageName] = {0, true}; m_downloadManager->startDownload(packageName, downloadUrl, outputPath); emit updateDisplay(packageName); return true; @@ -200,16 +194,11 @@ void AppDelegate::startDownloadForAll() { for (int row = 0; row < m_model->rowCount(); ++row) { QModelIndex index = m_model->index(row, 0); QString packageName = index.data(Qt::UserRole + 1).toString(); - // 确保跳过正在下载、正在安装或已安装的 - if (m_downloads.contains(packageName) && - (m_downloads[packageName].isDownloading || - m_downloads[packageName].isInstalling || - m_downloads[packageName].isInstalled)) { - continue; - } + if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled)) + continue; // 跳过正在下载或已安装的 QString downloadUrl = index.data(Qt::UserRole + 7).toString(); QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - m_downloads[packageName] = {0, true, false, false}; // progress, isDownloading, isInstalling, isInstalled + m_downloads[packageName] = {0, true, false}; m_downloadManager->startDownload(packageName, downloadUrl, outputPath); emit updateDisplay(packageName); } @@ -251,9 +240,6 @@ void AppDelegate::startNextInstall() { if (debPath.isEmpty()) { qWarning() << "未找到deb文件,包名:" << packageName; m_downloads[packageName].isInstalling = false; - // 如果找不到deb文件,也应该根据情况处理是已安装还是未安装 - // 这里只是简单地设置为未安装,并更新显示 - m_downloads[packageName].isInstalled = false; // 确保如果未找到文件则不显示已安装 emit updateDisplay(packageName); m_installingPackage.clear(); startNextInstall(); @@ -276,7 +262,12 @@ void AppDelegate::startNextInstall() { logFile->flush(); QString text = QString::fromLocal8Bit(out); qDebug().noquote() << text; - // 尽管此处可以检查关键字,但最终的安装状态应由进程退出码决定 + // 检查“软件包已安装”关键字 + if (text.contains(QStringLiteral("软件包已安装"))) { + m_downloads[packageName].isInstalling = false; + m_downloads[packageName].isInstalled = true; + emit updateDisplay(packageName); + } }); connect(m_installProcess, &QProcess::readyReadStandardError, this, [this, logFile]() { QByteArray err = m_installProcess->readAllStandardError(); @@ -285,28 +276,17 @@ void AppDelegate::startNextInstall() { qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName, logFile](int exitCode, QProcess::ExitStatus exitStatus) { + this, [this, packageName, logFile](int /*exitCode*/, QProcess::ExitStatus /*status*/) { if (logFile) logFile->close(); - - // === 关键修改 === - // 检查进程是否正常退出且退出码为0,这通常表示成功 - if (exitStatus == QProcess::NormalExit && exitCode == 0) { - m_downloads[packageName].isInstalled = true; // 安装成功,设置为已安装 - qDebug() << "安装成功:" << packageName; - } else { - // 安装失败或异常退出 - m_downloads[packageName].isInstalled = false; // 确保不是已安装状态 - qWarning() << "安装失败或异常退出:" << packageName << "Exit Code:" << exitCode << "Exit Status:" << exitStatus; + // 若未检测到“软件包已安装”,此处兜底 + if (!m_downloads[packageName].isInstalled) { + m_downloads[packageName].isInstalling = false; } - // === 关键修改结束 === - - m_downloads[packageName].isInstalling = false; // 安装过程结束 - emit updateDisplay(packageName); // 更新UI显示 - + emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; m_installingPackage.clear(); - startNextInstall(); // 处理队列中的下一个安装任务 + startNextInstall(); }); } else { // 日志文件无法打开时,仍然要连接原有信号 @@ -314,25 +294,18 @@ void AppDelegate::startNextInstall() { QByteArray out = m_installProcess->readAllStandardOutput(); QString text = QString::fromLocal8Bit(out); qDebug().noquote() << text; - // 同样,这里的关键字检查不是最可靠的 + if (text.contains(QStringLiteral("软件包已安装"))) { + m_downloads[packageName].isInstalling = false; + m_downloads[packageName].isInstalled = true; + emit updateDisplay(packageName); + } }); connect(m_installProcess, &QProcess::readyReadStandardError, this, [this]() { QByteArray err = m_installProcess->readAllStandardError(); qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName](int exitCode, QProcess::ExitStatus exitStatus) { - // === 关键修改 (与上面相同) === - if (exitStatus == QProcess::NormalExit && exitCode == 0) { - m_downloads[packageName].isInstalled = true; - qDebug() << "安装成功 (无日志文件):" << packageName; - } else { - m_downloads[packageName].isInstalled = false; - qWarning() << "安装失败或异常退出 (无日志文件):" << packageName << "Exit Code:" << exitCode << "Exit Status:" << exitStatus; - } - // === 关键修改结束 === - - m_downloads[packageName].isInstalling = false; + this, [this, packageName](int /*exitCode*/, QProcess::ExitStatus /*status*/) { emit updateDisplay(packageName); m_installProcess->deleteLater(); m_installProcess = nullptr; @@ -346,4 +319,3 @@ void AppDelegate::startNextInstall() { args << debPath << "--no-create-desktop-entry"; m_installProcess->start("ssinstall", args); } - -- Gitee From fed8829a47330548413c4c0e4e47e473abebad78 Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 18 Jul 2025 00:03:34 +0800 Subject: [PATCH 03/10] =?UTF-8?q?update:=E6=9B=B4=E6=96=B0readme=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ae2026a3..3e30de32 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ | 支持 ACE 兼容环境| | | 多线程下载 | 基于 aptss 方案 | +如您已安装星火应用商店,则会附带本程序。 #### 联系与反馈 如有问题或建议,欢迎联系:momen@momen.world \ No newline at end of file -- Gitee From ef480a1e060fc04b0dcf19e34d44c11773cdea5c Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 24 Jul 2025 14:08:05 +0800 Subject: [PATCH 04/10] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0=E4=B8=AD?= =?UTF-8?q?=E6=96=87readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README copy.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 README copy.md diff --git a/README copy.md b/README copy.md new file mode 100644 index 00000000..3e30de32 --- /dev/null +++ b/README copy.md @@ -0,0 +1,30 @@ +### 星火软件更新器 + +#### 简介 + +欢迎使用星火软件更新器!本工具可帮助您便捷地更新 Linux 计算机上的各类程序。 + +本版本专为仅包含 Qt5 的 Linux 发行版设计。 +**请在 root 权限下运行本程序。** + +#### 当前支持的 Linux 发行版 + +- [x] GXDE OS +- [x] Ubuntu +- [x] deepin +- [ ] Kylin + +#### 功能清单 + +| 功能模块 | 描述 | +|------------------|--------------------------------------| +| 应用名识别 | 基于 `ss-do-upgrade.sh` 部分代码实现 | +| 应用包大小识别 | 通过 dpkg 获取包大小信息 | +| 获取应用图标 | 利用 QDesktopServices 实现 | +| 支持 ACE 兼容环境| | +| 多线程下载 | 基于 aptss 方案 | + +如您已安装星火应用商店,则会附带本程序。 +#### 联系与反馈 + +如有问题或建议,欢迎联系:momen@momen.world \ No newline at end of file -- Gitee From 5470014ee2b3960791d81b5e899727207ad1cc93 Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 25 Jul 2025 14:11:35 +0800 Subject: [PATCH 05/10] =?UTF-8?q?chore:=E6=9B=B4=E6=96=B0=E8=8B=B1?= =?UTF-8?q?=E6=96=87=E7=89=88readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3e30de32..e5728804 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,21 @@ -### 星火软件更新器 +### Spark Software Updater -#### 简介 +#### Introduction -欢迎使用星火软件更新器!本工具可帮助您便捷地更新 Linux 计算机上的各类程序。 +Welcome to Spark Software Updater! This tool helps you conveniently update various applications on your Linux system. -本版本专为仅包含 Qt5 的 Linux 发行版设计。 -**请在 root 权限下运行本程序。** +This version is specifically designed for Linux distributions with Qt5 support. +**Please run with root privileges (recommended to use `sudo`).** -#### 当前支持的 Linux 发行版 +#### Currently Supported Linux Distributions - [x] GXDE OS - [x] Ubuntu - [x] deepin - [ ] Kylin -#### 功能清单 +// ... existing Chinese feature table ... -| 功能模块 | 描述 | -|------------------|--------------------------------------| -| 应用名识别 | 基于 `ss-do-upgrade.sh` 部分代码实现 | -| 应用包大小识别 | 通过 dpkg 获取包大小信息 | -| 获取应用图标 | 利用 QDesktopServices 实现 | -| 支持 ACE 兼容环境| | -| 多线程下载 | 基于 aptss 方案 | +#### Contact & Feedback -如您已安装星火应用商店,则会附带本程序。 -#### 联系与反馈 - -如有问题或建议,欢迎联系:momen@momen.world \ No newline at end of file +For any issues or suggestions, please contact: momen@momen.world -- Gitee From d765a83fa34902739edb4a4579a0d9dc5b0f3b1a Mon Sep 17 00:00:00 2001 From: momen Date: Sat, 26 Jul 2025 14:11:34 +0800 Subject: [PATCH 06/10] =?UTF-8?q?update:=E6=9B=B4=E6=96=B0=E4=B8=AD?= =?UTF-8?q?=E6=96=87readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README copy.md => README.zh.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README copy.md => README.zh.md (100%) diff --git a/README copy.md b/README.zh.md similarity index 100% rename from README copy.md rename to README.zh.md -- Gitee From 0360e8f3c9955fbd5eaef77b34e9d796ff0ece62 Mon Sep 17 00:00:00 2001 From: momen Date: Fri, 15 Aug 2025 20:56:13 +0800 Subject: [PATCH 07/10] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E5=90=8E=E6=98=BE=E7=A4=BA=E4=B8=BA=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E5=AE=8C=E6=88=90=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/appdelegate.cpp | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/appdelegate.cpp b/src/appdelegate.cpp index 1a1c3e98..4efbc467 100644 --- a/src/appdelegate.cpp +++ b/src/appdelegate.cpp @@ -12,14 +12,14 @@ AppDelegate::AppDelegate(QObject *parent) : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) { connect(m_downloadManager, &DownloadManager::downloadFinished, this, [this](const QString &packageName, bool success) { - if (m_downloads.contains(packageName)) { - m_downloads[packageName].isDownloading = false; - emit updateDisplay(packageName); - qDebug() << (success ? "下载完成:" : "下载失败:") << packageName; - if (success) { - enqueueInstall(packageName); - } - } + if (m_downloads.contains(packageName)) { + m_downloads[packageName].isDownloading = false; + emit updateDisplay(packageName); + qDebug() << (success ? "下载完成:" : "下载失败:") << packageName; + if (success) { + enqueueInstall(packageName); + } + } }); connect(m_downloadManager, &DownloadManager::downloadProgress, this, @@ -276,18 +276,19 @@ void AppDelegate::startNextInstall() { qDebug().noquote() << QString::fromLocal8Bit(err); }); connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName, logFile](int /*exitCode*/, QProcess::ExitStatus /*status*/) { - if (logFile) logFile->close(); - // 若未检测到“软件包已安装”,此处兜底 - if (!m_downloads[packageName].isInstalled) { - m_downloads[packageName].isInstalling = false; - } - emit updateDisplay(packageName); - m_installProcess->deleteLater(); - m_installProcess = nullptr; - m_installingPackage.clear(); - startNextInstall(); - }); + this, [this, packageName, logFile](int exitCode, QProcess::ExitStatus status) { + if (logFile) logFile->close(); + m_downloads[packageName].isInstalling = false; + if (exitCode == 0) { + m_downloads[packageName].isInstalled = true; // 安装成功 + } + emit updateDisplay(packageName); + m_installProcess->deleteLater(); + m_installProcess = nullptr; + m_installingPackage.clear(); + startNextInstall(); + }); + } else { // 日志文件无法打开时,仍然要连接原有信号 connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName]() { -- Gitee From 35cf0a188b792d1069bd9c124bc2f1b068000ba1 Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 28 Aug 2025 21:09:29 +0800 Subject: [PATCH 08/10] =?UTF-8?q?chore:=E4=B8=A2=E5=BC=83=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spark-update-tool/.gitignore | 62 -- spark-update-tool/CMakeLists.txt | 58 -- spark-update-tool/README.md | 29 - spark-update-tool/debian/changelog | 5 - spark-update-tool/debian/compat | 1 - spark-update-tool/debian/control | 13 - spark-update-tool/debian/copyright | 7 - spark-update-tool/debian/install | 3 - spark-update-tool/debian/postrm | 11 - spark-update-tool/debian/rules | 7 - spark-update-tool/debian/source/format | 1 - .../debian/spark-update-tool.desktop | 9 - .../resources/128*128/spark-update-tool.png | Bin 9142 -> 0 bytes spark-update-tool/resources/default_icon.svg | 1 - spark-update-tool/resources/down_arrow.svg | 1 - .../resources/spark-update-tool.svg | 1 - spark-update-tool/src/appdelegate.cpp | 321 --------- spark-update-tool/src/appdelegate.h | 46 -- spark-update-tool/src/applistmodel.cpp | 62 -- spark-update-tool/src/applistmodel.h | 26 - spark-update-tool/src/aptssupdater.cpp | 398 ----------- spark-update-tool/src/aptssupdater.h | 28 - spark-update-tool/src/downloadmanager.cpp | 117 --- spark-update-tool/src/downloadmanager.h | 28 - spark-update-tool/src/icons.qrc | 8 - spark-update-tool/src/main.cpp | 86 --- spark-update-tool/src/mainwindow.cpp | 229 ------ spark-update-tool/src/mainwindow.h | 33 - spark-update-tool/src/mainwindow.ui | 256 ------- src/spark-update-tool/.gitignore | 62 -- src/spark-update-tool/CMakeLists.txt | 58 -- src/spark-update-tool/LICENSE | 674 ------------------ src/spark-update-tool/README.md | 29 - src/spark-update-tool/debian/changelog | 5 - src/spark-update-tool/debian/compat | 1 - src/spark-update-tool/debian/control | 13 - src/spark-update-tool/debian/copyright | 7 - src/spark-update-tool/debian/install | 3 - src/spark-update-tool/debian/postrm | 11 - src/spark-update-tool/debian/rules | 7 - src/spark-update-tool/debian/source/format | 1 - .../debian/spark-update-tool.desktop | 9 - .../resources/128*128/spark-update-tool.png | Bin 9142 -> 0 bytes .../resources/default_icon.svg | 1 - .../resources/down_arrow.svg | 1 - .../resources/spark-update-tool.svg | 1 - src/spark-update-tool/spark-update-tool.pro | 48 -- src/spark-update-tool/src/appdelegate.cpp | 321 --------- src/spark-update-tool/src/appdelegate.h | 46 -- src/spark-update-tool/src/applistmodel.cpp | 62 -- src/spark-update-tool/src/applistmodel.h | 26 - src/spark-update-tool/src/aptssupdater.cpp | 398 ----------- src/spark-update-tool/src/aptssupdater.h | 28 - src/spark-update-tool/src/downloadmanager.cpp | 117 --- src/spark-update-tool/src/downloadmanager.h | 28 - src/spark-update-tool/src/icons.qrc | 8 - src/spark-update-tool/src/main.cpp | 86 --- src/spark-update-tool/src/mainwindow.cpp | 262 ------- src/spark-update-tool/src/mainwindow.h | 36 - src/spark-update-tool/src/mainwindow.ui | 256 ------- 60 files changed, 4452 deletions(-) delete mode 100644 spark-update-tool/.gitignore delete mode 100644 spark-update-tool/CMakeLists.txt delete mode 100644 spark-update-tool/README.md delete mode 100644 spark-update-tool/debian/changelog delete mode 100644 spark-update-tool/debian/compat delete mode 100644 spark-update-tool/debian/control delete mode 100644 spark-update-tool/debian/copyright delete mode 100644 spark-update-tool/debian/install delete mode 100644 spark-update-tool/debian/postrm delete mode 100755 spark-update-tool/debian/rules delete mode 100644 spark-update-tool/debian/source/format delete mode 100644 spark-update-tool/debian/spark-update-tool.desktop delete mode 100644 spark-update-tool/resources/128*128/spark-update-tool.png delete mode 100644 spark-update-tool/resources/default_icon.svg delete mode 100644 spark-update-tool/resources/down_arrow.svg delete mode 100644 spark-update-tool/resources/spark-update-tool.svg delete mode 100644 spark-update-tool/src/appdelegate.cpp delete mode 100644 spark-update-tool/src/appdelegate.h delete mode 100644 spark-update-tool/src/applistmodel.cpp delete mode 100644 spark-update-tool/src/applistmodel.h delete mode 100644 spark-update-tool/src/aptssupdater.cpp delete mode 100644 spark-update-tool/src/aptssupdater.h delete mode 100644 spark-update-tool/src/downloadmanager.cpp delete mode 100644 spark-update-tool/src/downloadmanager.h delete mode 100644 spark-update-tool/src/icons.qrc delete mode 100644 spark-update-tool/src/main.cpp delete mode 100644 spark-update-tool/src/mainwindow.cpp delete mode 100644 spark-update-tool/src/mainwindow.h delete mode 100644 spark-update-tool/src/mainwindow.ui delete mode 100644 src/spark-update-tool/.gitignore delete mode 100644 src/spark-update-tool/CMakeLists.txt delete mode 100644 src/spark-update-tool/LICENSE delete mode 100644 src/spark-update-tool/README.md delete mode 100644 src/spark-update-tool/debian/changelog delete mode 100644 src/spark-update-tool/debian/compat delete mode 100644 src/spark-update-tool/debian/control delete mode 100644 src/spark-update-tool/debian/copyright delete mode 100644 src/spark-update-tool/debian/install delete mode 100644 src/spark-update-tool/debian/postrm delete mode 100755 src/spark-update-tool/debian/rules delete mode 100644 src/spark-update-tool/debian/source/format delete mode 100644 src/spark-update-tool/debian/spark-update-tool.desktop delete mode 100644 src/spark-update-tool/resources/128*128/spark-update-tool.png delete mode 100644 src/spark-update-tool/resources/default_icon.svg delete mode 100644 src/spark-update-tool/resources/down_arrow.svg delete mode 100644 src/spark-update-tool/resources/spark-update-tool.svg delete mode 100644 src/spark-update-tool/spark-update-tool.pro delete mode 100644 src/spark-update-tool/src/appdelegate.cpp delete mode 100644 src/spark-update-tool/src/appdelegate.h delete mode 100644 src/spark-update-tool/src/applistmodel.cpp delete mode 100644 src/spark-update-tool/src/applistmodel.h delete mode 100644 src/spark-update-tool/src/aptssupdater.cpp delete mode 100644 src/spark-update-tool/src/aptssupdater.h delete mode 100644 src/spark-update-tool/src/downloadmanager.cpp delete mode 100644 src/spark-update-tool/src/downloadmanager.h delete mode 100644 src/spark-update-tool/src/icons.qrc delete mode 100644 src/spark-update-tool/src/main.cpp delete mode 100644 src/spark-update-tool/src/mainwindow.cpp delete mode 100644 src/spark-update-tool/src/mainwindow.h delete mode 100644 src/spark-update-tool/src/mainwindow.ui diff --git a/spark-update-tool/.gitignore b/spark-update-tool/.gitignore deleted file mode 100644 index 79c9ac74..00000000 --- a/spark-update-tool/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -build -.vscode -.cache -CMakeLists.txt.user -CMakeLists.txt.user.* -obj-x86_64-linux-gnu -# C++ objects and libs -*.slo -*.lo -*.o -*.a -*.la -*.lai -*.so -*.dll -*.dylib - -# Qt-es -object_script.*.Release -object_script.*.Debug -*_plugin_import.cpp -/.qmake.cache -/.qmake.stash -*.pro.user -*.pro.user.* -*.qbs.user -*.qbs.user.* -*.moc -moc_*.cpp -moc_*.h -qrc_*.cpp -ui_*.h -*.qmlc -*.jsc -Makefile* -*build-* - -# Qt unit tests -target_wrapper.* - -# Qt qm files -translations/*.qm - -# QtCreator -*.autosave - -# QtCreator Qml -*.qmlproject.user -*.qmlproject.user.* - -# QtCreator CMake -CMakeLists.txt.user* -build - -# Debian dpkg-buildpackage -debian/*.debhelper* -debian/files -debian/*.substvars -debian/spark-update-tool - -.vscode/* -src/spark-update-tool diff --git a/spark-update-tool/CMakeLists.txt b/spark-update-tool/CMakeLists.txt deleted file mode 100644 index 49a4d5eb..00000000 --- a/spark-update-tool/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(spark-update-tool VERSION 0.1 LANGUAGES CXX) - -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - - -find_package(Qt5 REQUIRED COMPONENTS Widgets Network Concurrent) - - -find_package(Qt5 REQUIRED COMPONENTS Widgets Network Concurrent Core Gui) - -# 定义所有项目源文件,现在无需条件判断 -set(PROJECT_SOURCES - src/main.cpp - src/mainwindow.cpp - src/mainwindow.h - src/mainwindow.ui - src/aptssupdater.h src/aptssupdater.cpp - src/icons.qrc - src/appdelegate.h src/appdelegate.cpp - src/applistmodel.h src/applistmodel.cpp - src/downloadmanager.h src/downloadmanager.cpp -) - - -add_executable(spark-update-tool - ${PROJECT_SOURCES} -) - -target_link_libraries(spark-update-tool PRIVATE - Qt5::Widgets - Qt5::Network - Qt5::Concurrent - Qt5::Core - Qt5::Gui -) - - -set_target_properties(spark-update-tool PROPERTIES - # ${BUNDLE_ID_OPTION} # 如果上面取消注释,这里也取消注释 - MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} - -) - -include(GNUInstallDirs) -install(TARGETS spark-update-tool - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - diff --git a/spark-update-tool/README.md b/spark-update-tool/README.md deleted file mode 100644 index ae2026a3..00000000 --- a/spark-update-tool/README.md +++ /dev/null @@ -1,29 +0,0 @@ -### 星火软件更新器 - -#### 简介 - -欢迎使用星火软件更新器!本工具可帮助您便捷地更新 Linux 计算机上的各类程序。 - -本版本专为仅包含 Qt5 的 Linux 发行版设计。 -**请在 root 权限下运行本程序。** - -#### 当前支持的 Linux 发行版 - -- [x] GXDE OS -- [x] Ubuntu -- [x] deepin -- [ ] Kylin - -#### 功能清单 - -| 功能模块 | 描述 | -|------------------|--------------------------------------| -| 应用名识别 | 基于 `ss-do-upgrade.sh` 部分代码实现 | -| 应用包大小识别 | 通过 dpkg 获取包大小信息 | -| 获取应用图标 | 利用 QDesktopServices 实现 | -| 支持 ACE 兼容环境| | -| 多线程下载 | 基于 aptss 方案 | - -#### 联系与反馈 - -如有问题或建议,欢迎联系:momen@momen.world \ No newline at end of file diff --git a/spark-update-tool/debian/changelog b/spark-update-tool/debian/changelog deleted file mode 100644 index b414feaa..00000000 --- a/spark-update-tool/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -spark-update-tool (1.0.0) unstable; urgency=low - - * Initial release. - - -- momen Wed, 18 Jun 2025 00:00:00 +0000 \ No newline at end of file diff --git a/spark-update-tool/debian/compat b/spark-update-tool/debian/compat deleted file mode 100644 index f11c82a4..00000000 --- a/spark-update-tool/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 \ No newline at end of file diff --git a/spark-update-tool/debian/control b/spark-update-tool/debian/control deleted file mode 100644 index 19c9d624..00000000 --- a/spark-update-tool/debian/control +++ /dev/null @@ -1,13 +0,0 @@ -Source: spark-update-tool -Section: utils -Priority: optional -Maintainer: momen -Build-Depends: debhelper (>= 9) -Standards-Version: 3.9.6 -Homepage: https://gitee.com/spark-store-project/Spark-Update-Tool - -Package: spark-update-tool -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Spark Update Tool - 星火应用商店更新组件。This package provides the Spark Update Tool. It includes features for checking for updates, downloading, and applying them seamlessly. \ No newline at end of file diff --git a/spark-update-tool/debian/copyright b/spark-update-tool/debian/copyright deleted file mode 100644 index 163489ed..00000000 --- a/spark-update-tool/debian/copyright +++ /dev/null @@ -1,7 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: spark-update-tool -Source: https://gitee.com/spark-store-project/Spark-Update-Tool - -Files: * -Copyright: 2025, momen -License: GPL-3.0+ \ No newline at end of file diff --git a/spark-update-tool/debian/install b/spark-update-tool/debian/install deleted file mode 100644 index ed44f2cf..00000000 --- a/spark-update-tool/debian/install +++ /dev/null @@ -1,3 +0,0 @@ -build/spark-update-tool /usr/bin/ -debian/spark-update-tool.desktop /usr/share/applications -resources/128*128/spark-update-tool.png /usr/share/icons/hicolor/128x128/apps diff --git a/spark-update-tool/debian/postrm b/spark-update-tool/debian/postrm deleted file mode 100644 index 263e9c42..00000000 --- a/spark-update-tool/debian/postrm +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - purge) - - rm -rf /usr/share/spark-update-tool - ;; -esac - -exit 0 \ No newline at end of file diff --git a/spark-update-tool/debian/rules b/spark-update-tool/debian/rules deleted file mode 100755 index 1159ac4f..00000000 --- a/spark-update-tool/debian/rules +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/make -f - -%: - dh $@ - -override_dh_auto_configure: - dh_auto_configure -- -DCMAKE_INSTALL_PREFIX=/usr diff --git a/spark-update-tool/debian/source/format b/spark-update-tool/debian/source/format deleted file mode 100644 index 9f674278..00000000 --- a/spark-update-tool/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) \ No newline at end of file diff --git a/spark-update-tool/debian/spark-update-tool.desktop b/spark-update-tool/debian/spark-update-tool.desktop deleted file mode 100644 index 688ba0cd..00000000 --- a/spark-update-tool/debian/spark-update-tool.desktop +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=Spark Update Tool -Comment=A Qt-based application for managing and updating software -Exec=spark-update-tool -Icon=spark-update-tool -Terminal=false -Type=Application -Categories=Utility; diff --git a/spark-update-tool/resources/128*128/spark-update-tool.png b/spark-update-tool/resources/128*128/spark-update-tool.png deleted file mode 100644 index 138ed83770cf4cce8fdead9c11908a0446e8a135..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9142 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 AL;wH) diff --git a/spark-update-tool/resources/default_icon.svg b/spark-update-tool/resources/default_icon.svg deleted file mode 100644 index 7c9cb42d..00000000 --- a/spark-update-tool/resources/default_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/spark-update-tool/resources/down_arrow.svg b/spark-update-tool/resources/down_arrow.svg deleted file mode 100644 index 4989662c..00000000 --- a/spark-update-tool/resources/down_arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/spark-update-tool/resources/spark-update-tool.svg b/spark-update-tool/resources/spark-update-tool.svg deleted file mode 100644 index 3d21d816..00000000 --- a/spark-update-tool/resources/spark-update-tool.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/spark-update-tool/src/appdelegate.cpp b/spark-update-tool/src/appdelegate.cpp deleted file mode 100644 index 1a1c3e98..00000000 --- a/spark-update-tool/src/appdelegate.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "appdelegate.h" -#include -#include -#include -#include -#include -#include -#include -#include - -AppDelegate::AppDelegate(QObject *parent) - : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) { - connect(m_downloadManager, &DownloadManager::downloadFinished, this, - [this](const QString &packageName, bool success) { - if (m_downloads.contains(packageName)) { - m_downloads[packageName].isDownloading = false; - emit updateDisplay(packageName); - qDebug() << (success ? "下载完成:" : "下载失败:") << packageName; - if (success) { - enqueueInstall(packageName); - } - } - }); - - connect(m_downloadManager, &DownloadManager::downloadProgress, this, - [this](const QString &packageName, int progress) { - if (m_downloads.contains(packageName)) { - m_downloads[packageName].progress = progress; - qDebug()<save(); - - if (option.state & QStyle::State_Selected) - painter->fillRect(option.rect, option.palette.highlight()); - else - painter->fillRect(option.rect, QColor("#F3F4F6")); - - QFont boldFont = option.font; - boldFont.setBold(true); - QFont normalFont = option.font; - - QString name = index.data(Qt::DisplayRole).toString(); - QString currentVersion = index.data(Qt::UserRole + 2).toString(); - QString newVersion = index.data(Qt::UserRole + 3).toString(); - QString iconPath = index.data(Qt::UserRole + 4).toString(); - QString size = index.data(Qt::UserRole + 5).toString(); - QString description = index.data(Qt::UserRole + 6).toString(); - - QRect rect = option.rect; - int margin = 10, spacing = 6, iconSize = 40; - - QRect iconRect(rect.left() + margin, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); - QIcon(iconPath).paint(painter, iconRect); - - int textX = iconRect.right() + margin; - int textWidth = rect.width() - textX - 100; - - QRect nameRect(textX, rect.top() + margin, textWidth, 20); - painter->setFont(boldFont); - painter->setPen(QColor("#333333")); - painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name); - - QRect versionRect(textX, nameRect.bottom() + spacing, textWidth, 20); - painter->setFont(normalFont); - painter->setPen(QColor("#888888")); - painter->drawText(versionRect, Qt::AlignLeft | Qt::AlignVCenter, - QString("当前版本: %1 → 新版本: %2").arg(currentVersion, newVersion)); - - QRect descRect(textX, versionRect.bottom() + spacing, textWidth, 40); - painter->setFont(normalFont); - painter->setPen(QColor("#AAAAAA")); - painter->drawText(descRect, Qt::TextWordWrap, - QString("包大小:%1 MB").arg(QString::number(size.toDouble() / (1024 * 1024), 'f', 2))); - - QString packageName = index.data(Qt::UserRole + 1).toString(); - bool isDownloading = m_downloads.contains(packageName) && m_downloads[packageName].isDownloading; - int progress = m_downloads.value(packageName, DownloadInfo{0, false}).progress; - bool isInstalled = m_downloads.value(packageName).isInstalled; - bool isInstalling = m_downloads.value(packageName).isInstalling; - - if (isDownloading) { - QRect progressRect(rect.right() - 270, rect.top() + (rect.height() - 20) / 2, 150, 20); - QStyleOptionProgressBar progressBarOption; - progressBarOption.rect = progressRect; - progressBarOption.minimum = 0; - progressBarOption.maximum = 100; - progressBarOption.progress = progress; - progressBarOption.text = QString("%1%").arg(progress); - progressBarOption.textVisible = true; - QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); - - // 修改后的取消按钮绘制代码 - QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); - painter->setPen(Qt::NoPen); - painter->setBrush(QColor("#ff4444")); // 红色背景 - painter->drawRoundedRect(buttonRect, 4, 4); // 圆角矩形 - - painter->setPen(Qt::white); // 白色文字 - painter->setFont(option.font); - painter->drawText(buttonRect, Qt::AlignCenter, "取消"); - } else if (isInstalling) { - // 安装中:显示转圈和文字 - QRect spinnerRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 30, 30); - int angle = (m_spinnerTimer.elapsed() / 10) % 360; - QPen pen(QColor("#2563EB"), 3); - painter->setPen(pen); - painter->setRenderHint(QPainter::Antialiasing, true); - QRectF arcRect = spinnerRect.adjusted(3, 3, -3, -3); - painter->drawArc(arcRect, angle * 16, 120 * 16); // 120度弧 - - QRect textRect(option.rect.right() - 120, option.rect.top() + (option.rect.height() - 30) / 2, 110, 30); - painter->setPen(QColor("#2563EB")); - painter->setFont(option.font); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, "正在安装中"); - } else { - QRect buttonRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 70, 30); - painter->setPen(Qt::NoPen); - if (isInstalled) { - painter->setBrush(QColor("#10B981")); - painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(Qt::white); - painter->drawText(buttonRect, Qt::AlignCenter, "已安装"); - } else if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { - // 下载完成,按钮绿色,样式不变 - painter->setBrush(QColor("#10B981")); - painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(Qt::white); - painter->drawText(buttonRect, Qt::AlignCenter, "下载完成"); - // 不需要特殊处理样式,交互在 editorEvent 控制 - } else { - painter->setBrush(QColor("#e9effd")); - painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(QColor("#2563EB")); - painter->drawText(buttonRect, Qt::AlignCenter, "更新"); - } - } - - painter->restore(); -} - -QSize AppDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - return QSize(option.rect.width(), 110); -} - -bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index) { - if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *mouseEvent = static_cast(event); - QRect rect = option.rect; - QString packageName = index.data(Qt::UserRole + 1).toString(); - - if (m_downloads.contains(packageName) && m_downloads[packageName].isDownloading) { - QRect cancelButtonRect(rect.right() - 70, rect.top() + (rect.height() - 20) / 2, 60, 20); - if (cancelButtonRect.contains(mouseEvent->pos())) { - m_downloadManager->cancelDownload(packageName); - m_downloads.remove(packageName); - emit updateDisplay(packageName); - return true; - } - } else { - QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); - if (buttonRect.contains(mouseEvent->pos())) { - // 判断是否为“下载完成”状态,如果是则不响应点击 - if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { - // “下载完成”状态,按钮失效,点击无效 - return false; - } - QString downloadUrl = index.data(Qt::UserRole + 7).toString(); - QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - - m_downloads[packageName] = {0, true}; - m_downloadManager->startDownload(packageName, downloadUrl, outputPath); - emit updateDisplay(packageName); - return true; - } - } - } - - return QStyledItemDelegate::editorEvent(event, model, option, index); -} - -void AppDelegate::startDownloadForAll() { - if (!m_model) return; - for (int row = 0; row < m_model->rowCount(); ++row) { - QModelIndex index = m_model->index(row, 0); - QString packageName = index.data(Qt::UserRole + 1).toString(); - if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled)) - continue; // 跳过正在下载或已安装的 - QString downloadUrl = index.data(Qt::UserRole + 7).toString(); - QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - m_downloads[packageName] = {0, true, false}; - m_downloadManager->startDownload(packageName, downloadUrl, outputPath); - emit updateDisplay(packageName); - } -} - -// 新增:安装队列相关实现 -void AppDelegate::enqueueInstall(const QString &packageName) { - m_installQueue.enqueue(packageName); - if (!m_isInstalling) { - startNextInstall(); - } -} - -void AppDelegate::startNextInstall() { - if (m_installQueue.isEmpty()) { - m_isInstalling = false; - m_installingPackage.clear(); - return; - } - m_isInstalling = true; - QString packageName = m_installQueue.dequeue(); - m_installingPackage = packageName; - m_downloads[packageName].isInstalling = true; - emit updateDisplay(packageName); - - // 查找 /tmp 下以包名开头的 .deb 文件 - QDir tempDir(QDir::tempPath()); - QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName), QDir::Files); - QString debPath; - if (!debs.isEmpty()) { - debPath = tempDir.absoluteFilePath(debs.first()); - } else { - debs = tempDir.entryList(QStringList() << QString("%1*.deb").arg(packageName), QDir::Files); - if (!debs.isEmpty()) { - debPath = tempDir.absoluteFilePath(debs.first()); - } - } - - if (debPath.isEmpty()) { - qWarning() << "未找到deb文件,包名:" << packageName; - m_downloads[packageName].isInstalling = false; - emit updateDisplay(packageName); - m_installingPackage.clear(); - startNextInstall(); - return; - } - - m_installProcess = new QProcess(this); - - // 新增:准备安装日志文件 - QString logPath = QString("/tmp/%1_install.log").arg(packageName); - QFile *logFile = new QFile(logPath, m_installProcess); - if (logFile->open(QIODevice::Append | QIODevice::Text)) { - // 设置权限为777 - QFile::setPermissions(logPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | - QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | - QFile::ReadOther | QFile::WriteOther | QFile::ExeOther); - connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName, logFile]() { - QByteArray out = m_installProcess->readAllStandardOutput(); - logFile->write(out); - logFile->flush(); - QString text = QString::fromLocal8Bit(out); - qDebug().noquote() << text; - // 检查“软件包已安装”关键字 - if (text.contains(QStringLiteral("软件包已安装"))) { - m_downloads[packageName].isInstalling = false; - m_downloads[packageName].isInstalled = true; - emit updateDisplay(packageName); - } - }); - connect(m_installProcess, &QProcess::readyReadStandardError, this, [this, logFile]() { - QByteArray err = m_installProcess->readAllStandardError(); - logFile->write(err); - logFile->flush(); - qDebug().noquote() << QString::fromLocal8Bit(err); - }); - connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName, logFile](int /*exitCode*/, QProcess::ExitStatus /*status*/) { - if (logFile) logFile->close(); - // 若未检测到“软件包已安装”,此处兜底 - if (!m_downloads[packageName].isInstalled) { - m_downloads[packageName].isInstalling = false; - } - emit updateDisplay(packageName); - m_installProcess->deleteLater(); - m_installProcess = nullptr; - m_installingPackage.clear(); - startNextInstall(); - }); - } else { - // 日志文件无法打开时,仍然要连接原有信号 - connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName]() { - QByteArray out = m_installProcess->readAllStandardOutput(); - QString text = QString::fromLocal8Bit(out); - qDebug().noquote() << text; - if (text.contains(QStringLiteral("软件包已安装"))) { - m_downloads[packageName].isInstalling = false; - m_downloads[packageName].isInstalled = true; - emit updateDisplay(packageName); - } - }); - connect(m_installProcess, &QProcess::readyReadStandardError, this, [this]() { - QByteArray err = m_installProcess->readAllStandardError(); - qDebug().noquote() << QString::fromLocal8Bit(err); - }); - connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName](int /*exitCode*/, QProcess::ExitStatus /*status*/) { - emit updateDisplay(packageName); - m_installProcess->deleteLater(); - m_installProcess = nullptr; - m_installingPackage.clear(); - startNextInstall(); - }); - } - - // 注意参数顺序:deb路径在前,--no-create-desktop-entry在后 - QStringList args; - args << debPath << "--no-create-desktop-entry"; - m_installProcess->start("ssinstall", args); -} diff --git a/spark-update-tool/src/appdelegate.h b/spark-update-tool/src/appdelegate.h deleted file mode 100644 index 0545155e..00000000 --- a/spark-update-tool/src/appdelegate.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "downloadmanager.h" - -struct DownloadInfo { - int progress = 0; - bool isDownloading = false; - bool isInstalled = false; - bool isInstalling = false; // 新增:标记是否正在安装 -}; - -class AppDelegate : public QStyledItemDelegate { - Q_OBJECT -public: - explicit AppDelegate(QObject *parent = nullptr); - - void setModel(QAbstractItemModel *model); - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - bool editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index) override; - void startDownloadForAll(); - -signals: - void updateDisplay(const QString &packageName); - -private: - DownloadManager *m_downloadManager; - QHash m_downloads; - QAbstractItemModel *m_model = nullptr; - - QQueue m_installQueue; - bool m_isInstalling = false; - QProcess *m_installProcess = nullptr; - QString m_installingPackage; // 当前正在安装的包名 - QElapsedTimer m_spinnerTimer; // 用于转圈动画 - - void enqueueInstall(const QString &packageName); - void startNextInstall(); -}; diff --git a/spark-update-tool/src/applistmodel.cpp b/spark-update-tool/src/applistmodel.cpp deleted file mode 100644 index 31e244a9..00000000 --- a/spark-update-tool/src/applistmodel.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "applistmodel.h" - -AppListModel::AppListModel(QObject *parent) : QAbstractListModel(parent) {} - -int AppListModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - return m_data.size(); -} - -QVariant AppListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() >= m_data.size()) - return QVariant(); - - const QVariantMap &map = m_data.at(index.row()); // 直接访问 QVariantMap - switch (role) { - case Qt::DisplayRole: - return map.value("name"); - case Qt::UserRole + 1: // 包名 - return map.value("package"); - case Qt::UserRole + 2: // 当前版本 - return map.value("current_version"); - case Qt::UserRole + 3: // 新版本 - return map.value("new_version"); - case Qt::UserRole + 4: // 图标路径 - return map.value("icon"); - case Qt::UserRole + 5: // 文件大小 - return map.value("size"); - case Qt::UserRole + 6: // 描述 - return map.value("description"); - case Qt::UserRole + 7: // 下载 URL - return map.value("download_url"); // 返回下载 URL - default: - return QVariant(); - } -} - -void AppListModel::setUpdateData(const QJsonArray &updateInfo) -{ - beginResetModel(); - m_data.clear(); // 清空 QList - - for (const auto &item : updateInfo) { - QJsonObject obj = item.toObject(); - QVariantMap map; - map["package"] = obj["package"].toString(); - map["name"] = obj["name"].toString(); - map["current_version"] = obj["current_version"].toString(); - map["new_version"] = obj["new_version"].toString(); - map["icon"] = obj["icon"].toString(); - map["size"] = obj["size"].toString(); - map["download_url"] = obj["download_url"].toString(); // 确保设置下载 URL - m_data.append(map); // 添加到 QList - - qDebug() << "设置到模型的包名:" << map["package"].toString(); - qDebug() << "设置到模型的下载 URL:" << map["download_url"].toString(); // 检查设置的数据 - } - - endResetModel(); -} diff --git a/spark-update-tool/src/applistmodel.h b/spark-update-tool/src/applistmodel.h deleted file mode 100644 index 2fb36c0d..00000000 --- a/spark-update-tool/src/applistmodel.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef APPLISTMODEL_H -#define APPLISTMODEL_H - -#include -#include -// 添加 QJsonObject 头文件 -#include - -class AppListModel : public QAbstractListModel -{ - Q_OBJECT -public: - explicit AppListModel(QObject *parent = nullptr); - - // 重写方法 - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - // 设置更新数据 - void setUpdateData(const QJsonArray &data); - -private: - QList m_data; // 修改类型为 QList -}; - -#endif // APPLISTMODEL_H diff --git a/spark-update-tool/src/aptssupdater.cpp b/spark-update-tool/src/aptssupdater.cpp deleted file mode 100644 index c4bd9060..00000000 --- a/spark-update-tool/src/aptssupdater.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include "aptssupdater.h" -#include -#include -#include -#include -#include - -aptssUpdater::aptssUpdater(QWidget *parent) - : QWidget(parent) -{ - packageName = getUpdateablePackages(); -} - -QStringList aptssUpdater::getUpdateablePackages() -{ - QStringList packageDetails; - QProcess process; - QString command = R"(env LANGUAGE=en_US /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf list --upgradable -o Dir::Etc::sourcelist="/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list" -o Dir::Etc::sourceparts="/dev/null" -o APT::Get::List-Cleanup="0" | awk 'NR>1')"; - - process.start("bash", QStringList() << "-c" << command); - if (!process.waitForFinished()) { - qWarning() << "Process failed to finish."; - return packageDetails; - } - - QString output = process.readAllStandardOutput(); - QStringList lines = output.split('\n', Qt::SkipEmptyParts); - - // 创建临时文件 - QTemporaryFile tempFile; - tempFile.setAutoRemove(false); - if (tempFile.open()) { - QTextStream stream(&tempFile); - - for (const QString &line : lines) { - QRegularExpression regex(R"(([\w\-\+\.]+)/\S+\s+([^\s]+)\s+\S+\s+\[upgradable from: ([^\]]+)\])"); - QRegularExpressionMatch match = regex.match(line); - if (match.hasMatch()) { - QString name = match.captured(1); - QString newVersion = match.captured(2); - QString oldVersion = match.captured(3); - - // 检查版本是否相同,相同则跳过 - if (newVersion == oldVersion) { - qDebug() << "跳过版本相同的包:" << name << "(" << oldVersion << "→" << newVersion << ")"; - continue; - } - - // 写入内存列表 - packageDetails << QString("%1: %2 → %3").arg(name, oldVersion, newVersion); - - // 写入临时文件(原始数据) - stream << name << "|" << oldVersion << "|" << newVersion << "\n"; - } - } - tempFile.close(); - m_tempFilePath = tempFile.fileName(); - qDebug()<< "临时文件路径:" << m_tempFilePath; - - } else { - qWarning() << "无法创建临时文件"; - } - - return packageDetails; -} - - -QStringList aptssUpdater::getPackageSizes() -{ - QStringList packageDetails; - QProcess process; - - // 获取可更新包名列表 - QStringList updateablePackages; - for (const QString &pkgInfo : packageName) { - updateablePackages << pkgInfo.section(":", 0, 0).trimmed(); - } - - foreach (const QString &packageName, updateablePackages) { - // 构建新命令(包含包名参数) - QString command = QString("apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " - "-o Dir::Etc::sourcelist=\"/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list\" " - "-o Dir::Etc::sourceparts=\"/dev/null\"").arg(packageName); - - process.start("bash", QStringList() << "-c" << command); - if (!process.waitForFinished()) { - qWarning() << "获取包信息失败:" << packageName; - continue; - } - - QString output = process.readAllStandardOutput(); - // 使用正则匹配所有信息 - // 调整正则表达式匹配分组 - QRegularExpression regex(R"('([^']+)'\s+(\S+)\s+(\d+)\s+SHA512:([^\s]+))"); // 分组1:URL 分组2:文件名 分组3:大小 分组4:SHA512 - QRegularExpressionMatch match = regex.match(output); - - if (match.hasMatch()) { - QString url = match.captured(1); - QString fileName = match.captured(2); - QString size = match.captured(3); - QString sha512 = match.captured(4); - - // 调整字段顺序:包名 | 大小 | URL | SHA512 - packageDetails << QString("%1: %2|%3|%4").arg(packageName, size, url, sha512); - } - } - - qDebug() << "完整包信息:" << packageDetails; - return packageDetails; -} - - - - - -QStringList aptssUpdater::getDesktopAppNames() -{ - QStringList appNames; - QProcess dpkgProcess; - - // 获取当前系统语言环境 - QString lang = QLocale().name().replace("_", "-"); - - // 遍历所有可更新包(复用已有的临时文件数据) - QStringList packages = packageName; - - foreach (const QString &package, packages) { - QString packageName = package.split(":")[0]; - QString finalName = packageName; // 默认使用包名 - - // 获取包文件列表 - dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); - dpkgProcess.waitForFinished(); - QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); - - // 先检查常规应用目录 - QStringList regularDesktopFiles = files.filter("/usr/share/applications/"); - QString regularAppName; - if (!regularDesktopFiles.isEmpty()) { - checkDesktopFiles(regularDesktopFiles, regularAppName, lang, packageName); - } - - // 如果常规目录没有找到,再检查特殊目录 - if (regularAppName.isEmpty()) { - QStringList specialDesktopFiles = files.filter(QRegularExpression(QString("/opt/apps/%1/entries/applications").arg(packageName))); - QString specialAppName; - if (!specialDesktopFiles.isEmpty()) { - checkDesktopFiles(specialDesktopFiles, specialAppName, lang, packageName); - if (!specialAppName.isEmpty()) { - finalName = specialAppName; - } - } - } else { - finalName = regularAppName; - } - - // 输出格式为[软件名|包名] - appNames << QString("[%1|%2]").arg(finalName, packageName); - } - qDebug()<< "应用名称列表:" << appNames; - return appNames; -} - - -bool aptssUpdater::checkDesktopFiles(const QStringList &desktopFiles, QString &appName, const QString &lang, const QString &packageName) -{ - QString lastValidName; - QRegularExpression noDisplayRe("^NoDisplay=(true|True)"); - QRegularExpression nameRe("^Name\\[?" + lang + "?\\]?=(.*)"); - QRegularExpression nameOrigRe("^Name=(.*)"); - - foreach (const QString &filePath, desktopFiles) { - if (!filePath.endsWith(".desktop")) continue; - - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) continue; - - bool skip = false; - QString currentName; - - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - - // 检查NoDisplay属性 - if (line.startsWith("NoDisplay=")) { - if (noDisplayRe.match(line).hasMatch()) { - skip = true; - break; - } - } - - // 优先匹配本地化名称 - if (currentName.isEmpty()) { - QRegularExpressionMatch match = nameRe.match(line); - if (match.hasMatch()) { - currentName = match.captured(1); - continue; - } - - // 匹配原始名称 - match = nameOrigRe.match(line); - if (match.hasMatch()) { - currentName = match.captured(1); - } - } - } - - if (!skip && !currentName.isEmpty()) { - lastValidName = currentName; - } - } - - // 处理最终的有效名称 - if (!lastValidName.isEmpty()) { - appName = lastValidName; // 直接赋值而不是使用<< - return true; - } - - // 回退到包名 - appName = packageName; - return false; -} - -QStringList aptssUpdater::getPackageIcons() -{ - QStringList packageIcons; - QProcess dpkgProcess; - - // 遍历所有可更新包 - QStringList packages = packageName; - - foreach (const QString &package, packages) { - QString packageName = package.split(":")[0]; - QString iconPath = ":/resources/default_icon.svg"; // 默认图标 - - // 获取包文件列表 - dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); - dpkgProcess.waitForFinished(); - QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); - - // 查找.desktop文件 - QStringList desktopFiles = files.filter(QRegularExpression("/(usr/share|opt/apps)/.*\\.desktop$")); - - // 从.desktop文件中提取图标 - foreach (const QString &desktopFile, desktopFiles) { - QFile file(desktopFile); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - if (line.startsWith("Icon=")) { - QString iconName = line.mid(5).trimmed(); - - // 处理相对图标名(如Icon=vscode) - if (!iconName.contains('/')) { - // 查找标准图标路径 - QStringList iconPaths = { - QString("/usr/share/pixmaps/%1.png").arg(iconName), - QString("/usr/share/icons/hicolor/48x48/apps/%1.png").arg(iconName), - QString("/usr/share/icons/hicolor/scalable/apps/%1.svg").arg(iconName), - QString("/opt/apps/%1/entries/icons/hicolor/48x48/apps/%2.png").arg(packageName, iconName) - }; - - foreach (const QString &path, iconPaths) { - if (QFile::exists(path)) { - iconPath = path; - break; - } - } - } else { - // 已经是绝对路径 - if (QFile::exists(iconName)) { - iconPath = iconName; - } - } - break; - } - } - file.close(); - } - } - - // 如果.desktop中没有找到图标,尝试直接查找包中的图标文件 - if (iconPath == ":/resources/default_icon.svg") { - QStringList iconFiles = files.filter(QRegularExpression("/(usr/share/pixmaps|usr/share/icons|opt/apps/.*/entries/icons)/.*\\.(png|svg)$")); - if (!iconFiles.isEmpty()) { - iconPath = iconFiles.first(); - } - } - - qDebug() << "包名:" << packageName << "图标路径:" << iconPath; - packageIcons << QString("%1: %2").arg(packageName, iconPath); - } - - return packageIcons; -} - - -QJsonArray aptssUpdater::getUpdateInfoAsJson() -{ - QJsonArray jsonArray; - - // 获取所有需要的信息 - QStringList sizes = getPackageSizes(); - QStringList names = getDesktopAppNames(); - QStringList icons = getPackageIcons(); - - // 创建包名到各种信息的映射 - QHash> packageInfo; - - // 解析包版本信息 - for (const QString &pkg : packageName) { - QStringList parts = pkg.split(": "); - if (parts.size() >= 2) { - QString packageName = parts[0]; - QStringList versions = parts[1].split(" → "); - if (versions.size() == 2) { - packageInfo[packageName]["current_version"] = versions[0]; - packageInfo[packageName]["new_version"] = versions[1]; - } - } - } - - // 解析包详细信息(新增部分) - for (const QString &sizeInfo : sizes) { - QStringList parts = sizeInfo.split(": "); - if (parts.size() == 2) { - QString packageName = parts[0]; - QStringList details = parts[1].split("|"); - if (details.size() == 3) { // 现在包含大小|URL|SHA512 - packageInfo[packageName]["size"] = details[0]; - packageInfo[packageName]["url"] = details[1]; - packageInfo[packageName]["sha512"] = details[2]; - } - } - } - - // 解析应用名称信息 - for (const QString &nameInfo : names) { - if (nameInfo.startsWith("[") && nameInfo.endsWith("]")) { - QString content = nameInfo.mid(1, nameInfo.length() - 2); - QStringList parts = content.split("|"); - if (parts.size() == 2) { - QString displayName = parts[0]; - QString packageName = parts[1]; - packageInfo[packageName]["display_name"] = displayName; - } - } - } - - // 解析图标信息 - for (const QString &iconInfo : icons) { - QStringList parts = iconInfo.split(": "); - if (parts.size() == 2) { - QString packageName = parts[0]; - packageInfo[packageName]["icon"] = parts[1].trimmed(); - } - } - - // 构建JSON数组 - for (const QString &packageName : packageInfo.keys()) { - QJsonObject jsonObj; - jsonObj["package"] = packageName; - - // 使用显示名称(如果有),否则使用包名 - if (packageInfo[packageName].contains("display_name")) { - jsonObj["name"] = packageInfo[packageName]["display_name"]; - } else { - jsonObj["name"] = packageName; - } - - jsonObj["current_version"] = packageInfo[packageName]["current_version"]; - jsonObj["new_version"] = packageInfo[packageName]["new_version"]; - jsonObj["icon"] = packageInfo[packageName]["icon"]; - jsonObj["ignored"] = false; // 默认不忽略 - - // 如果有大小信息也加入 - if (packageInfo[packageName].contains("size")) { - jsonObj["size"] = packageInfo[packageName]["size"]; - } - - // 在构建JSON对象时添加新字段(在jsonObj中添加): - if (packageInfo[packageName].contains("url")) { - jsonObj["download_url"] = packageInfo[packageName]["url"]; - qDebug() << "生成的下载 URL:" << packageInfo[packageName]["url"]; // 检查生成的 URL - } else { - qWarning() << "未找到下载 URL,包名:" << packageName; - jsonObj["download_url"] = ""; // 设置为空字符串以避免崩溃 - } - jsonObj["sha512"] = packageInfo[packageName]["sha512"]; - jsonArray.append(jsonObj); - } - qDebug()< -#include -#include -#include -#include -#include -class aptssUpdater : public QWidget -{ - Q_OBJECT -public: - explicit aptssUpdater(QWidget *parent = nullptr); - - QStringList getUpdateablePackages(); // 查询可更新包列表及更新内容 - QStringList getPackageSizes(); // 获取每个包的大小 - QStringList getDesktopAppNames(); // 获取桌面应用名称列表 - QStringList getPackageIcons(); // 获取包图标列表 - QJsonArray getUpdateInfoAsJson(); // 获取更新信息的 JSON 格式 - QString m_tempFilePath; -signals: -private: - bool checkDesktopFiles(const QStringList &desktopFiles, QString &appName, const QString &lang, const QString &packageName); - QStringList packageName; -}; - -#endif // APTSSUPDATER_H \ No newline at end of file diff --git a/spark-update-tool/src/downloadmanager.cpp b/spark-update-tool/src/downloadmanager.cpp deleted file mode 100644 index 97803249..00000000 --- a/spark-update-tool/src/downloadmanager.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "downloadmanager.h" -#include -#include -#include -#include -#include - -DownloadManager::DownloadManager(QObject *parent) : QObject(parent) -{ - cleanupTempFiles(); -} - -void DownloadManager::startDownload(const QString &packageName, const QString &url, const QString &outputPath) -{ - if (m_processes.contains(packageName)) { - qWarning() << packageName << " is already downloading."; - return; - } - - QString metalinkUrl = url + ".metalink"; - QFileInfo fileInfo(outputPath); - - QStringList arguments = { - "--enable-rpc=false", - "--console-log-level=warn", - "--summary-interval=1", - "--allow-overwrite=true", - "--dir=" + fileInfo.absolutePath(), - "--out=" + fileInfo.fileName(), - metalinkUrl - }; - - QProcess *process = new QProcess(this); - m_processes.insert(packageName, process); - - // 新增:准备日志文件 - QString logPath = QString("/tmp/%1_download.log").arg(packageName); - QFile *logFile = new QFile(logPath, process); - if (logFile->open(QIODevice::Append | QIODevice::Text)) { - // 设置权限为777 - QFile::setPermissions(logPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | - QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | - QFile::ReadOther | QFile::WriteOther | QFile::ExeOther); - connect(process, &QProcess::readyReadStandardOutput, this, [this, packageName, process, logFile]() { - while (process->canReadLine()) { - QString line = QString::fromUtf8(process->readLine()).trimmed(); - // 写入日志 - logFile->write(line.toUtf8() + '\n'); - logFile->flush(); - QRegularExpression regex(R"(\((\d+)%\))"); - QRegularExpressionMatch match = regex.match(line); - if (match.hasMatch()) { - int progress = match.captured(1).toInt(); - emit downloadProgress(packageName, progress); - } - } - }); - connect(process, &QProcess::readyReadStandardError, this, [process, logFile]() { - QByteArray err = process->readAllStandardError(); - logFile->write(err); - logFile->flush(); - }); - } - - connect(process, QOverload::of(&QProcess::finished), - this, [this, packageName, outputPath, logFile](int exitCode, QProcess::ExitStatus status) { - bool success = (exitCode == 0 && status == QProcess::NormalExit); - if (!success) { - qWarning() << "Download failed for" << packageName << "exit code:" << exitCode; - } - - removeAria2Files(outputPath); // 清理残留 .aria2 - emit downloadFinished(packageName, success); - - if (logFile) logFile->close(); - - QProcess *proc = m_processes.take(packageName); - if (proc) proc->deleteLater(); - }); - - process->start("aria2c", arguments); -} - -void DownloadManager::cancelDownload(const QString &packageName) -{ - if (!m_processes.contains(packageName)) return; - - QProcess *process = m_processes.take(packageName); - if (process) { - process->kill(); // 立即终止 - process->waitForFinished(3000); // 最多等待3秒 - process->deleteLater(); - } - - emit downloadFinished(packageName, false); // 显式通知取消 - -} - -void DownloadManager::removeAria2Files(const QString &filePath) -{ - QString ariaFile = filePath + ".aria2"; - QFile::remove(ariaFile); -} - -bool DownloadManager::isDownloading(const QString &packageName) const -{ - return m_processes.contains(packageName); -} - -void DownloadManager::cleanupTempFiles() -{ - QDir tempDir(QDir::tempPath()); - QStringList leftovers = tempDir.entryList(QStringList() << "*.aria2", QDir::Files); - for (const QString &f : leftovers) { - tempDir.remove(f); - } -} diff --git a/spark-update-tool/src/downloadmanager.h b/spark-update-tool/src/downloadmanager.h deleted file mode 100644 index 0614368a..00000000 --- a/spark-update-tool/src/downloadmanager.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef DOWNLOADMANAGER_H -#define DOWNLOADMANAGER_H - -#include -#include -#include - -class DownloadManager : public QObject -{ - Q_OBJECT -public: - explicit DownloadManager(QObject *parent = nullptr); - void startDownload(const QString &packageName, const QString &url, const QString &outputPath); - void cancelDownload(const QString &packageName); - bool isDownloading(const QString &packageName) const; - -signals: - void downloadProgress(const QString &packageName, int progress); - void downloadFinished(const QString &packageName, bool success); - -private: - void cleanupTempFiles(); - void removeAria2Files(const QString &filePath); - - QMap m_processes; -}; - -#endif // DOWNLOADMANAGER_H diff --git a/spark-update-tool/src/icons.qrc b/spark-update-tool/src/icons.qrc deleted file mode 100644 index f81e1f88..00000000 --- a/spark-update-tool/src/icons.qrc +++ /dev/null @@ -1,8 +0,0 @@ - - - ../resources/down_arrow.svg - ../resources/default_icon.svg - ../resources/spark-update-tool.svg - ../resources/128*128/spark-update-tool.png - - diff --git a/spark-update-tool/src/main.cpp b/spark-update-tool/src/main.cpp deleted file mode 100644 index a3df5a43..00000000 --- a/spark-update-tool/src/main.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "mainwindow.h" -#include -#include -#include -#include // for geteuid -#include // for getenv -#include // For debugging output - -bool isRoot() { - return geteuid() == 0; -} - -bool elevateToRoot() { - QString program = QCoreApplication::applicationFilePath(); - qDebug() << "Current application path:" << program; - - QByteArray display = qgetenv("DISPLAY"); - QByteArray xauthority = qgetenv("XAUTHORITY"); - - QStringList args; - args << "env" - << "DISPLAY=" + display - << "XAUTHORITY=" + xauthority - << program; - - QProcess process; - process.setProgram("pkexec"); - process.setArguments(args); - - qDebug() << "Attempting to elevate using pkexec with arguments:" << args; - - process.start(); - if (!process.waitForStarted(5000)) { - qDebug() << "Failed to start pkexec."; - return false; - } - - // 阻塞等待提权进程退出(比如主程序窗口关闭) - if (!process.waitForFinished(-1)) { // 等待直到新进程退出 - qDebug() << "pkexec process waitForFinished failed."; - return false; - } - - int exitCode = process.exitCode(); - QProcess::ExitStatus exitStatus = process.exitStatus(); - - qDebug() << "pkexec exit code:" << exitCode; - qDebug() << "pkexec exit status:" << exitStatus; - qDebug() << "pkexec stderr:" << process.readAllStandardError(); - - return (exitStatus == QProcess::NormalExit && exitCode == 0); -} - -int main(int argc, char *argv[]) -{ - // 必须在 QGuiApplication 实例创建之前调用 - // QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); - - QApplication a(argc, argv); - a.setWindowIcon(QIcon(":/resources/128*128/spark-update-tool.png")); - if (!isRoot()) { - qDebug() << "Not running as root. Attempting to elevate..."; - if (!elevateToRoot()) { - qDebug() << "Elevation failed or pkexec command was not executed successfully."; - QMessageBox::critical(nullptr, - "权限不足", - "提权失败!\n\n您的系统可能不支持 `pkexec` 或 `polkit` 配置不正确," - "或者您取消了授权。\n\n请尝试使用 `sudo` 命令运行此程序:" - "\n\n在终端中输入:\n`sudo " + QCoreApplication::applicationName() + "`"); - return 0; // 提权失败,退出程序 - } else { - // 如果 elevateToRoot 返回 true,说明 pkexec 命令本身执行成功 - // 但这并不意味着原始程序以 root 权限启动了 - // 因为 elevateToRoot 启动的是一个新的进程,当前进程应该退出 - // 否则会并行运行两个程序实例 - qDebug() << "pkexec command executed successfully (new process likely started). Exiting current process."; - return 0; // 当前非root进程退出 - } - } else { - qDebug() << "Running as root."; - } - - MainWindow w; - w.show(); - return a.exec(); -} \ No newline at end of file diff --git a/spark-update-tool/src/mainwindow.cpp b/spark-update-tool/src/mainwindow.cpp deleted file mode 100644 index dde2a4c5..00000000 --- a/spark-update-tool/src/mainwindow.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "mainwindow.h" -#include "./ui_mainwindow.h" -#include -#include -#include -#include // 新增 -#include // 新增 -#include -#include -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) - , m_model(new AppListModel(this)) - , m_delegate(new AppDelegate(this)) -{ - QIcon icon(":/resources/128*128/spark-update-tool.png"); - setWindowIcon(icon); - QProgressDialog *progressDialog = new QProgressDialog("正在与服务器通信,获取更新信息中...", QString(), 0, 0, this); - progressDialog->setWindowModality(Qt::ApplicationModal); - progressDialog->setCancelButton(nullptr); - progressDialog->setWindowTitle("请稍候"); - progressDialog->setMinimumDuration(0); - progressDialog->setWindowFlags(progressDialog->windowFlags() & ~Qt::WindowCloseButtonHint); // 禁用关闭按钮 - progressDialog->show(); - //异步执行runAptssUpgrade - QFutureWatcher *watcher = new QFutureWatcher(this); - connect(watcher, &QFutureWatcher::finished, this, [=]() { - progressDialog->close(); - progressDialog->deleteLater(); - watcher->deleteLater(); - ui->setupUi(this); - QIcon icon(":/resources/128*128/spark-update-tool.png"); - setWindowIcon(icon); - // 创建 QListView 并设置父控件为 ui->appWidget - listView = new QListView(ui->appWidget); - listView->setModel(m_model); - listView->setItemDelegate(m_delegate); - - // 新增:确保 delegate 拥有 model 指针 - m_delegate->setModel(m_model); - - // 设置 QListView 填充 ui->appWidget - QVBoxLayout *layout = new QVBoxLayout(ui->appWidget); - layout->addWidget(listView); - layout->setContentsMargins(0, 0, 0, 0); - connect(m_delegate, &AppDelegate::updateDisplay, this, [=](const QString &packageName) { - for (int i = 0; i < m_model->rowCount(); ++i) { - QModelIndex index = m_model->index(i); - if (index.data(Qt::UserRole + 1).toString() == packageName) { - m_model->dataChanged(index, index); // 刷新该行 - break; - } - } - }); - - // 新增:点击“更新全部”按钮批量下载 - connect(ui->updatePushButton, &QPushButton::clicked, this, [=](){ - qDebug()<<"更新全部按钮被点击"; - m_delegate->startDownloadForAll(); - }); - - checkUpdates(); - initStyle(); - }); - - // 启动异步任务 - watcher->setFuture(QtConcurrent::run([this](){ - runAptssUpgrade(); - })); -} -//初始化控件样式 -void MainWindow::initStyle() -{ - //设置窗口标题 - this->setWindowTitle("软件更新中心"); - - //查询框样式 - ui->plainTextEdit->setStyleSheet(R"( - QPlainTextEdit { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - border-radius: 4px; - padding-top: 8px; - padding-bottom: 8px; - font-size: 9px; - line-height: 1.4; - color: #9CA3AF; - } - )"); - - ui->plainTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - ui->plainTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - //筛选框样式 - ui->FilterComboBox->setStyleSheet(R"( - QComboBox { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - border-radius: 4px; - color: #4B5563; - padding: 4px 8px; - } - - QComboBox::drop-down { - border: none; - width: 20px; - } - - QComboBox::down-arrow { - image: url(:/resources/down_arrow.svg); - width: 12px; - height: 16px; - } - QComboBox QAbstractItemView { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - color: #4B5563; - selection-background-color: #F3F4F6; - selection-color: #111827; - } - )"); - - //更新软件按钮样式 - ui->updatePushButton->setStyleSheet(R"( - QPushButton { - background-color: #2563EB; - color: #FFFFFF; - border: none; - border-radius: 4px; - font-size: 14px; - padding: 6px 12px; - text-align: center; - } - - QPushButton:hover { - background-color: #1D4ED8; /* 深一点的 hover 效果,可选 */ - } - - QPushButton:pressed { - background-color: #1E40AF; /* 按下效果,可选 */ - } - - QPushButton:disabled { - background-color: #A5B4FC; - color: #F9FAFB; - } - )"); - - //设置背景填充颜色 - ui->backgroundWidget->setStyleSheet(R"( - QWidget { - background-color: #FFFFFF; - border-radius: 12px; - } - )"); - - //设置主背景颜色 - this->setStyleSheet("background-color: #F8FAFC;"); - - // 添加滚动条样式 - this->setStyleSheet(R"( - QScrollBar:vertical { - background: #F3F4F6; - width: 8px; - margin: 0px; - } - QScrollBar::handle:vertical { - background: #D1D5DB; - border-radius: 4px; - min-height: 30px; - } - QScrollBar::handle:vertical:hover { - background: #9CA3AF; - } - QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - background: none; - height: 0px; - } - - QScrollBar:horizontal { - background: #F3F4F6; - height: 8px; - margin: 0px; - } - QScrollBar::handle:horizontal { - background: #D1D5DB; - border-radius: 4px; - min-width: 30px; - } - QScrollBar::handle:horizontal:hover { - background: #9CA3AF; - } - )"); -} -void MainWindow::checkUpdates() -{ - aptssUpdater updater; - QJsonArray updateInfo = updater.getUpdateInfoAsJson(); - m_model->setUpdateData(updateInfo); - - for (const auto &item : updateInfo) { - QJsonObject obj = item.toObject(); - qDebug() << "模型设置的包名:" << obj["package"].toString(); - qDebug() << "模型设置的下载 URL:" << obj["download_url"].toString(); // 检查模型数据 - } -} - -void MainWindow::runAptssUpgrade() -{ - QProcess process; - QStringList args; - args << "sudo" <<"aptss" << "ssupdate"; - process.start("sudo", args); - if (!process.waitForStarted(5000)) { - QMessageBox::warning(this, "升级失败", "无法启动 sudo aptss ssupdate"); - return; - } - process.write("n\n"); - process.closeWriteChannel(); - process.waitForFinished(-1); - if (process.exitCode() != 0) { - QMessageBox::warning(this, "升级失败", "执行 sudo aptss ssupdate 失败,请检查系统环境。"); - } -} - -MainWindow::~MainWindow() -{ - delete ui; -} diff --git a/spark-update-tool/src/mainwindow.h b/spark-update-tool/src/mainwindow.h deleted file mode 100644 index e36db88a..00000000 --- a/spark-update-tool/src/mainwindow.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include "aptssupdater.h" -#include "applistmodel.h" -#include "appdelegate.h" -#include - -QT_BEGIN_NAMESPACE -namespace Ui { -class MainWindow; -} -QT_END_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - -private: - Ui::MainWindow *ui; - void checkUpdates(); - void initStyle(); - void runAptssUpgrade(); - AppListModel *m_model; - AppDelegate *m_delegate; - QListView *listView; // 声明 QListView 指针 -}; -#endif // MAINWINDOW_H diff --git a/spark-update-tool/src/mainwindow.ui b/spark-update-tool/src/mainwindow.ui deleted file mode 100644 index 2a582878..00000000 --- a/spark-update-tool/src/mainwindow.ui +++ /dev/null @@ -1,256 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 1440 - 858 - - - - MainWindow - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - Noto Sans Vai - 22 - - - - 软件更新中心 - - - - - - - Qt::Horizontal - - - - 1245 - 20 - - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 320 - 38 - - - - - 320 - 38 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 200 - 38 - - - - - - - 81.000000000000000 - - - 搜索软件... - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 214 - 40 - - - - - 214 - 40 - - - - - - 0 - 0 - 102 - 38 - - - - - 102 - 38 - - - - - 102 - 38 - - - - - - - Qt::LeftToRight - - - QComboBox::AdjustToContentsOnFirstShow - - - - 按名称 - - - - - - - 118 - 0 - 96 - 40 - - - - 更新全部 - - - - - - - - - - - true - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - 1440 - 23 - - - - - - - diff --git a/src/spark-update-tool/.gitignore b/src/spark-update-tool/.gitignore deleted file mode 100644 index 79c9ac74..00000000 --- a/src/spark-update-tool/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -build -.vscode -.cache -CMakeLists.txt.user -CMakeLists.txt.user.* -obj-x86_64-linux-gnu -# C++ objects and libs -*.slo -*.lo -*.o -*.a -*.la -*.lai -*.so -*.dll -*.dylib - -# Qt-es -object_script.*.Release -object_script.*.Debug -*_plugin_import.cpp -/.qmake.cache -/.qmake.stash -*.pro.user -*.pro.user.* -*.qbs.user -*.qbs.user.* -*.moc -moc_*.cpp -moc_*.h -qrc_*.cpp -ui_*.h -*.qmlc -*.jsc -Makefile* -*build-* - -# Qt unit tests -target_wrapper.* - -# Qt qm files -translations/*.qm - -# QtCreator -*.autosave - -# QtCreator Qml -*.qmlproject.user -*.qmlproject.user.* - -# QtCreator CMake -CMakeLists.txt.user* -build - -# Debian dpkg-buildpackage -debian/*.debhelper* -debian/files -debian/*.substvars -debian/spark-update-tool - -.vscode/* -src/spark-update-tool diff --git a/src/spark-update-tool/CMakeLists.txt b/src/spark-update-tool/CMakeLists.txt deleted file mode 100644 index 49a4d5eb..00000000 --- a/src/spark-update-tool/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(spark-update-tool VERSION 0.1 LANGUAGES CXX) - -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - - -find_package(Qt5 REQUIRED COMPONENTS Widgets Network Concurrent) - - -find_package(Qt5 REQUIRED COMPONENTS Widgets Network Concurrent Core Gui) - -# 定义所有项目源文件,现在无需条件判断 -set(PROJECT_SOURCES - src/main.cpp - src/mainwindow.cpp - src/mainwindow.h - src/mainwindow.ui - src/aptssupdater.h src/aptssupdater.cpp - src/icons.qrc - src/appdelegate.h src/appdelegate.cpp - src/applistmodel.h src/applistmodel.cpp - src/downloadmanager.h src/downloadmanager.cpp -) - - -add_executable(spark-update-tool - ${PROJECT_SOURCES} -) - -target_link_libraries(spark-update-tool PRIVATE - Qt5::Widgets - Qt5::Network - Qt5::Concurrent - Qt5::Core - Qt5::Gui -) - - -set_target_properties(spark-update-tool PROPERTIES - # ${BUNDLE_ID_OPTION} # 如果上面取消注释,这里也取消注释 - MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} - -) - -include(GNUInstallDirs) -install(TARGETS spark-update-tool - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) - diff --git a/src/spark-update-tool/LICENSE b/src/spark-update-tool/LICENSE deleted file mode 100644 index 3877ae0a..00000000 --- a/src/spark-update-tool/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/src/spark-update-tool/README.md b/src/spark-update-tool/README.md deleted file mode 100644 index ae2026a3..00000000 --- a/src/spark-update-tool/README.md +++ /dev/null @@ -1,29 +0,0 @@ -### 星火软件更新器 - -#### 简介 - -欢迎使用星火软件更新器!本工具可帮助您便捷地更新 Linux 计算机上的各类程序。 - -本版本专为仅包含 Qt5 的 Linux 发行版设计。 -**请在 root 权限下运行本程序。** - -#### 当前支持的 Linux 发行版 - -- [x] GXDE OS -- [x] Ubuntu -- [x] deepin -- [ ] Kylin - -#### 功能清单 - -| 功能模块 | 描述 | -|------------------|--------------------------------------| -| 应用名识别 | 基于 `ss-do-upgrade.sh` 部分代码实现 | -| 应用包大小识别 | 通过 dpkg 获取包大小信息 | -| 获取应用图标 | 利用 QDesktopServices 实现 | -| 支持 ACE 兼容环境| | -| 多线程下载 | 基于 aptss 方案 | - -#### 联系与反馈 - -如有问题或建议,欢迎联系:momen@momen.world \ No newline at end of file diff --git a/src/spark-update-tool/debian/changelog b/src/spark-update-tool/debian/changelog deleted file mode 100644 index b414feaa..00000000 --- a/src/spark-update-tool/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -spark-update-tool (1.0.0) unstable; urgency=low - - * Initial release. - - -- momen Wed, 18 Jun 2025 00:00:00 +0000 \ No newline at end of file diff --git a/src/spark-update-tool/debian/compat b/src/spark-update-tool/debian/compat deleted file mode 100644 index f11c82a4..00000000 --- a/src/spark-update-tool/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 \ No newline at end of file diff --git a/src/spark-update-tool/debian/control b/src/spark-update-tool/debian/control deleted file mode 100644 index 19c9d624..00000000 --- a/src/spark-update-tool/debian/control +++ /dev/null @@ -1,13 +0,0 @@ -Source: spark-update-tool -Section: utils -Priority: optional -Maintainer: momen -Build-Depends: debhelper (>= 9) -Standards-Version: 3.9.6 -Homepage: https://gitee.com/spark-store-project/Spark-Update-Tool - -Package: spark-update-tool -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Spark Update Tool - 星火应用商店更新组件。This package provides the Spark Update Tool. It includes features for checking for updates, downloading, and applying them seamlessly. \ No newline at end of file diff --git a/src/spark-update-tool/debian/copyright b/src/spark-update-tool/debian/copyright deleted file mode 100644 index 163489ed..00000000 --- a/src/spark-update-tool/debian/copyright +++ /dev/null @@ -1,7 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: spark-update-tool -Source: https://gitee.com/spark-store-project/Spark-Update-Tool - -Files: * -Copyright: 2025, momen -License: GPL-3.0+ \ No newline at end of file diff --git a/src/spark-update-tool/debian/install b/src/spark-update-tool/debian/install deleted file mode 100644 index ed44f2cf..00000000 --- a/src/spark-update-tool/debian/install +++ /dev/null @@ -1,3 +0,0 @@ -build/spark-update-tool /usr/bin/ -debian/spark-update-tool.desktop /usr/share/applications -resources/128*128/spark-update-tool.png /usr/share/icons/hicolor/128x128/apps diff --git a/src/spark-update-tool/debian/postrm b/src/spark-update-tool/debian/postrm deleted file mode 100644 index 263e9c42..00000000 --- a/src/spark-update-tool/debian/postrm +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - purge) - - rm -rf /usr/share/spark-update-tool - ;; -esac - -exit 0 \ No newline at end of file diff --git a/src/spark-update-tool/debian/rules b/src/spark-update-tool/debian/rules deleted file mode 100755 index 1159ac4f..00000000 --- a/src/spark-update-tool/debian/rules +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/make -f - -%: - dh $@ - -override_dh_auto_configure: - dh_auto_configure -- -DCMAKE_INSTALL_PREFIX=/usr diff --git a/src/spark-update-tool/debian/source/format b/src/spark-update-tool/debian/source/format deleted file mode 100644 index 9f674278..00000000 --- a/src/spark-update-tool/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) \ No newline at end of file diff --git a/src/spark-update-tool/debian/spark-update-tool.desktop b/src/spark-update-tool/debian/spark-update-tool.desktop deleted file mode 100644 index 688ba0cd..00000000 --- a/src/spark-update-tool/debian/spark-update-tool.desktop +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=Spark Update Tool -Comment=A Qt-based application for managing and updating software -Exec=spark-update-tool -Icon=spark-update-tool -Terminal=false -Type=Application -Categories=Utility; diff --git a/src/spark-update-tool/resources/128*128/spark-update-tool.png b/src/spark-update-tool/resources/128*128/spark-update-tool.png deleted file mode 100644 index 138ed83770cf4cce8fdead9c11908a0446e8a135..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9142 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 AL;wH) diff --git a/src/spark-update-tool/resources/default_icon.svg b/src/spark-update-tool/resources/default_icon.svg deleted file mode 100644 index 7c9cb42d..00000000 --- a/src/spark-update-tool/resources/default_icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/spark-update-tool/resources/down_arrow.svg b/src/spark-update-tool/resources/down_arrow.svg deleted file mode 100644 index 4989662c..00000000 --- a/src/spark-update-tool/resources/down_arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/spark-update-tool/resources/spark-update-tool.svg b/src/spark-update-tool/resources/spark-update-tool.svg deleted file mode 100644 index 3d21d816..00000000 --- a/src/spark-update-tool/resources/spark-update-tool.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/spark-update-tool/spark-update-tool.pro b/src/spark-update-tool/spark-update-tool.pro deleted file mode 100644 index 69dc5007..00000000 --- a/src/spark-update-tool/spark-update-tool.pro +++ /dev/null @@ -1,48 +0,0 @@ -# spark-update-tool.pro -QT += core gui widgets network concurrent -TARGET = spark-update-tool -TEMPLATE = app - -# Set C++ standard to C++17 -CONFIG += c++17 - -# Enable auto features (uic, moc, rcc) -CONFIG += qt warn_on release -QT_CONFIG += no-pkg-config - -# Version info (replace with your actual version) -VERSION = 0.1.0 -DEFINES += APP_VERSION=\\\"$$VERSION\\\" - -# Source files -SOURCES += \ - src/main.cpp \ - src/mainwindow.cpp \ - src/aptssupdater.cpp \ - src/appdelegate.cpp \ - src/applistmodel.cpp \ - src/downloadmanager.cpp - -HEADERS += \ - src/mainwindow.h \ - src/aptssupdater.h \ - src/appdelegate.h \ - src/applistmodel.h \ - src/downloadmanager.h - -FORMS += \ - src/mainwindow.ui - -RESOURCES += \ - src/icons.qrc - -# macOS bundle properties (optional) -macx { - QMAKE_INFO_PLIST = Info.plist - ICON = resources/spark-update-tool.icns - BUNDLE_IDENTIFIER = org.spark.store.update-tool -} - -# Installation paths (matches CMake install) -target.path = $$[QT_INSTALL_BINS] -INSTALLS += target \ No newline at end of file diff --git a/src/spark-update-tool/src/appdelegate.cpp b/src/spark-update-tool/src/appdelegate.cpp deleted file mode 100644 index 1a1c3e98..00000000 --- a/src/spark-update-tool/src/appdelegate.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "appdelegate.h" -#include -#include -#include -#include -#include -#include -#include -#include - -AppDelegate::AppDelegate(QObject *parent) - : QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) { - connect(m_downloadManager, &DownloadManager::downloadFinished, this, - [this](const QString &packageName, bool success) { - if (m_downloads.contains(packageName)) { - m_downloads[packageName].isDownloading = false; - emit updateDisplay(packageName); - qDebug() << (success ? "下载完成:" : "下载失败:") << packageName; - if (success) { - enqueueInstall(packageName); - } - } - }); - - connect(m_downloadManager, &DownloadManager::downloadProgress, this, - [this](const QString &packageName, int progress) { - if (m_downloads.contains(packageName)) { - m_downloads[packageName].progress = progress; - qDebug()<save(); - - if (option.state & QStyle::State_Selected) - painter->fillRect(option.rect, option.palette.highlight()); - else - painter->fillRect(option.rect, QColor("#F3F4F6")); - - QFont boldFont = option.font; - boldFont.setBold(true); - QFont normalFont = option.font; - - QString name = index.data(Qt::DisplayRole).toString(); - QString currentVersion = index.data(Qt::UserRole + 2).toString(); - QString newVersion = index.data(Qt::UserRole + 3).toString(); - QString iconPath = index.data(Qt::UserRole + 4).toString(); - QString size = index.data(Qt::UserRole + 5).toString(); - QString description = index.data(Qt::UserRole + 6).toString(); - - QRect rect = option.rect; - int margin = 10, spacing = 6, iconSize = 40; - - QRect iconRect(rect.left() + margin, rect.top() + (rect.height() - iconSize) / 2, iconSize, iconSize); - QIcon(iconPath).paint(painter, iconRect); - - int textX = iconRect.right() + margin; - int textWidth = rect.width() - textX - 100; - - QRect nameRect(textX, rect.top() + margin, textWidth, 20); - painter->setFont(boldFont); - painter->setPen(QColor("#333333")); - painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, name); - - QRect versionRect(textX, nameRect.bottom() + spacing, textWidth, 20); - painter->setFont(normalFont); - painter->setPen(QColor("#888888")); - painter->drawText(versionRect, Qt::AlignLeft | Qt::AlignVCenter, - QString("当前版本: %1 → 新版本: %2").arg(currentVersion, newVersion)); - - QRect descRect(textX, versionRect.bottom() + spacing, textWidth, 40); - painter->setFont(normalFont); - painter->setPen(QColor("#AAAAAA")); - painter->drawText(descRect, Qt::TextWordWrap, - QString("包大小:%1 MB").arg(QString::number(size.toDouble() / (1024 * 1024), 'f', 2))); - - QString packageName = index.data(Qt::UserRole + 1).toString(); - bool isDownloading = m_downloads.contains(packageName) && m_downloads[packageName].isDownloading; - int progress = m_downloads.value(packageName, DownloadInfo{0, false}).progress; - bool isInstalled = m_downloads.value(packageName).isInstalled; - bool isInstalling = m_downloads.value(packageName).isInstalling; - - if (isDownloading) { - QRect progressRect(rect.right() - 270, rect.top() + (rect.height() - 20) / 2, 150, 20); - QStyleOptionProgressBar progressBarOption; - progressBarOption.rect = progressRect; - progressBarOption.minimum = 0; - progressBarOption.maximum = 100; - progressBarOption.progress = progress; - progressBarOption.text = QString("%1%").arg(progress); - progressBarOption.textVisible = true; - QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); - - // 修改后的取消按钮绘制代码 - QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); - painter->setPen(Qt::NoPen); - painter->setBrush(QColor("#ff4444")); // 红色背景 - painter->drawRoundedRect(buttonRect, 4, 4); // 圆角矩形 - - painter->setPen(Qt::white); // 白色文字 - painter->setFont(option.font); - painter->drawText(buttonRect, Qt::AlignCenter, "取消"); - } else if (isInstalling) { - // 安装中:显示转圈和文字 - QRect spinnerRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 30, 30); - int angle = (m_spinnerTimer.elapsed() / 10) % 360; - QPen pen(QColor("#2563EB"), 3); - painter->setPen(pen); - painter->setRenderHint(QPainter::Antialiasing, true); - QRectF arcRect = spinnerRect.adjusted(3, 3, -3, -3); - painter->drawArc(arcRect, angle * 16, 120 * 16); // 120度弧 - - QRect textRect(option.rect.right() - 120, option.rect.top() + (option.rect.height() - 30) / 2, 110, 30); - painter->setPen(QColor("#2563EB")); - painter->setFont(option.font); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, "正在安装中"); - } else { - QRect buttonRect(option.rect.right() - 80, option.rect.top() + (option.rect.height() - 30) / 2, 70, 30); - painter->setPen(Qt::NoPen); - if (isInstalled) { - painter->setBrush(QColor("#10B981")); - painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(Qt::white); - painter->drawText(buttonRect, Qt::AlignCenter, "已安装"); - } else if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { - // 下载完成,按钮绿色,样式不变 - painter->setBrush(QColor("#10B981")); - painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(Qt::white); - painter->drawText(buttonRect, Qt::AlignCenter, "下载完成"); - // 不需要特殊处理样式,交互在 editorEvent 控制 - } else { - painter->setBrush(QColor("#e9effd")); - painter->drawRoundedRect(buttonRect, 4, 4); - painter->setPen(QColor("#2563EB")); - painter->drawText(buttonRect, Qt::AlignCenter, "更新"); - } - } - - painter->restore(); -} - -QSize AppDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - return QSize(option.rect.width(), 110); -} - -bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index) { - if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *mouseEvent = static_cast(event); - QRect rect = option.rect; - QString packageName = index.data(Qt::UserRole + 1).toString(); - - if (m_downloads.contains(packageName) && m_downloads[packageName].isDownloading) { - QRect cancelButtonRect(rect.right() - 70, rect.top() + (rect.height() - 20) / 2, 60, 20); - if (cancelButtonRect.contains(mouseEvent->pos())) { - m_downloadManager->cancelDownload(packageName); - m_downloads.remove(packageName); - emit updateDisplay(packageName); - return true; - } - } else { - QRect buttonRect(rect.right() - 80, rect.top() + (rect.height() - 30) / 2, 70, 30); - if (buttonRect.contains(mouseEvent->pos())) { - // 判断是否为“下载完成”状态,如果是则不响应点击 - if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) { - // “下载完成”状态,按钮失效,点击无效 - return false; - } - QString downloadUrl = index.data(Qt::UserRole + 7).toString(); - QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - - m_downloads[packageName] = {0, true}; - m_downloadManager->startDownload(packageName, downloadUrl, outputPath); - emit updateDisplay(packageName); - return true; - } - } - } - - return QStyledItemDelegate::editorEvent(event, model, option, index); -} - -void AppDelegate::startDownloadForAll() { - if (!m_model) return; - for (int row = 0; row < m_model->rowCount(); ++row) { - QModelIndex index = m_model->index(row, 0); - QString packageName = index.data(Qt::UserRole + 1).toString(); - if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled)) - continue; // 跳过正在下载或已安装的 - QString downloadUrl = index.data(Qt::UserRole + 7).toString(); - QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName); - m_downloads[packageName] = {0, true, false}; - m_downloadManager->startDownload(packageName, downloadUrl, outputPath); - emit updateDisplay(packageName); - } -} - -// 新增:安装队列相关实现 -void AppDelegate::enqueueInstall(const QString &packageName) { - m_installQueue.enqueue(packageName); - if (!m_isInstalling) { - startNextInstall(); - } -} - -void AppDelegate::startNextInstall() { - if (m_installQueue.isEmpty()) { - m_isInstalling = false; - m_installingPackage.clear(); - return; - } - m_isInstalling = true; - QString packageName = m_installQueue.dequeue(); - m_installingPackage = packageName; - m_downloads[packageName].isInstalling = true; - emit updateDisplay(packageName); - - // 查找 /tmp 下以包名开头的 .deb 文件 - QDir tempDir(QDir::tempPath()); - QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName), QDir::Files); - QString debPath; - if (!debs.isEmpty()) { - debPath = tempDir.absoluteFilePath(debs.first()); - } else { - debs = tempDir.entryList(QStringList() << QString("%1*.deb").arg(packageName), QDir::Files); - if (!debs.isEmpty()) { - debPath = tempDir.absoluteFilePath(debs.first()); - } - } - - if (debPath.isEmpty()) { - qWarning() << "未找到deb文件,包名:" << packageName; - m_downloads[packageName].isInstalling = false; - emit updateDisplay(packageName); - m_installingPackage.clear(); - startNextInstall(); - return; - } - - m_installProcess = new QProcess(this); - - // 新增:准备安装日志文件 - QString logPath = QString("/tmp/%1_install.log").arg(packageName); - QFile *logFile = new QFile(logPath, m_installProcess); - if (logFile->open(QIODevice::Append | QIODevice::Text)) { - // 设置权限为777 - QFile::setPermissions(logPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | - QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | - QFile::ReadOther | QFile::WriteOther | QFile::ExeOther); - connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName, logFile]() { - QByteArray out = m_installProcess->readAllStandardOutput(); - logFile->write(out); - logFile->flush(); - QString text = QString::fromLocal8Bit(out); - qDebug().noquote() << text; - // 检查“软件包已安装”关键字 - if (text.contains(QStringLiteral("软件包已安装"))) { - m_downloads[packageName].isInstalling = false; - m_downloads[packageName].isInstalled = true; - emit updateDisplay(packageName); - } - }); - connect(m_installProcess, &QProcess::readyReadStandardError, this, [this, logFile]() { - QByteArray err = m_installProcess->readAllStandardError(); - logFile->write(err); - logFile->flush(); - qDebug().noquote() << QString::fromLocal8Bit(err); - }); - connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName, logFile](int /*exitCode*/, QProcess::ExitStatus /*status*/) { - if (logFile) logFile->close(); - // 若未检测到“软件包已安装”,此处兜底 - if (!m_downloads[packageName].isInstalled) { - m_downloads[packageName].isInstalling = false; - } - emit updateDisplay(packageName); - m_installProcess->deleteLater(); - m_installProcess = nullptr; - m_installingPackage.clear(); - startNextInstall(); - }); - } else { - // 日志文件无法打开时,仍然要连接原有信号 - connect(m_installProcess, &QProcess::readyReadStandardOutput, this, [this, packageName]() { - QByteArray out = m_installProcess->readAllStandardOutput(); - QString text = QString::fromLocal8Bit(out); - qDebug().noquote() << text; - if (text.contains(QStringLiteral("软件包已安装"))) { - m_downloads[packageName].isInstalling = false; - m_downloads[packageName].isInstalled = true; - emit updateDisplay(packageName); - } - }); - connect(m_installProcess, &QProcess::readyReadStandardError, this, [this]() { - QByteArray err = m_installProcess->readAllStandardError(); - qDebug().noquote() << QString::fromLocal8Bit(err); - }); - connect(m_installProcess, QOverload::of(&QProcess::finished), - this, [this, packageName](int /*exitCode*/, QProcess::ExitStatus /*status*/) { - emit updateDisplay(packageName); - m_installProcess->deleteLater(); - m_installProcess = nullptr; - m_installingPackage.clear(); - startNextInstall(); - }); - } - - // 注意参数顺序:deb路径在前,--no-create-desktop-entry在后 - QStringList args; - args << debPath << "--no-create-desktop-entry"; - m_installProcess->start("ssinstall", args); -} diff --git a/src/spark-update-tool/src/appdelegate.h b/src/spark-update-tool/src/appdelegate.h deleted file mode 100644 index 0545155e..00000000 --- a/src/spark-update-tool/src/appdelegate.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "downloadmanager.h" - -struct DownloadInfo { - int progress = 0; - bool isDownloading = false; - bool isInstalled = false; - bool isInstalling = false; // 新增:标记是否正在安装 -}; - -class AppDelegate : public QStyledItemDelegate { - Q_OBJECT -public: - explicit AppDelegate(QObject *parent = nullptr); - - void setModel(QAbstractItemModel *model); - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - bool editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, const QModelIndex &index) override; - void startDownloadForAll(); - -signals: - void updateDisplay(const QString &packageName); - -private: - DownloadManager *m_downloadManager; - QHash m_downloads; - QAbstractItemModel *m_model = nullptr; - - QQueue m_installQueue; - bool m_isInstalling = false; - QProcess *m_installProcess = nullptr; - QString m_installingPackage; // 当前正在安装的包名 - QElapsedTimer m_spinnerTimer; // 用于转圈动画 - - void enqueueInstall(const QString &packageName); - void startNextInstall(); -}; diff --git a/src/spark-update-tool/src/applistmodel.cpp b/src/spark-update-tool/src/applistmodel.cpp deleted file mode 100644 index 31e244a9..00000000 --- a/src/spark-update-tool/src/applistmodel.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "applistmodel.h" - -AppListModel::AppListModel(QObject *parent) : QAbstractListModel(parent) {} - -int AppListModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - return m_data.size(); -} - -QVariant AppListModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() >= m_data.size()) - return QVariant(); - - const QVariantMap &map = m_data.at(index.row()); // 直接访问 QVariantMap - switch (role) { - case Qt::DisplayRole: - return map.value("name"); - case Qt::UserRole + 1: // 包名 - return map.value("package"); - case Qt::UserRole + 2: // 当前版本 - return map.value("current_version"); - case Qt::UserRole + 3: // 新版本 - return map.value("new_version"); - case Qt::UserRole + 4: // 图标路径 - return map.value("icon"); - case Qt::UserRole + 5: // 文件大小 - return map.value("size"); - case Qt::UserRole + 6: // 描述 - return map.value("description"); - case Qt::UserRole + 7: // 下载 URL - return map.value("download_url"); // 返回下载 URL - default: - return QVariant(); - } -} - -void AppListModel::setUpdateData(const QJsonArray &updateInfo) -{ - beginResetModel(); - m_data.clear(); // 清空 QList - - for (const auto &item : updateInfo) { - QJsonObject obj = item.toObject(); - QVariantMap map; - map["package"] = obj["package"].toString(); - map["name"] = obj["name"].toString(); - map["current_version"] = obj["current_version"].toString(); - map["new_version"] = obj["new_version"].toString(); - map["icon"] = obj["icon"].toString(); - map["size"] = obj["size"].toString(); - map["download_url"] = obj["download_url"].toString(); // 确保设置下载 URL - m_data.append(map); // 添加到 QList - - qDebug() << "设置到模型的包名:" << map["package"].toString(); - qDebug() << "设置到模型的下载 URL:" << map["download_url"].toString(); // 检查设置的数据 - } - - endResetModel(); -} diff --git a/src/spark-update-tool/src/applistmodel.h b/src/spark-update-tool/src/applistmodel.h deleted file mode 100644 index 2fb36c0d..00000000 --- a/src/spark-update-tool/src/applistmodel.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef APPLISTMODEL_H -#define APPLISTMODEL_H - -#include -#include -// 添加 QJsonObject 头文件 -#include - -class AppListModel : public QAbstractListModel -{ - Q_OBJECT -public: - explicit AppListModel(QObject *parent = nullptr); - - // 重写方法 - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - // 设置更新数据 - void setUpdateData(const QJsonArray &data); - -private: - QList m_data; // 修改类型为 QList -}; - -#endif // APPLISTMODEL_H diff --git a/src/spark-update-tool/src/aptssupdater.cpp b/src/spark-update-tool/src/aptssupdater.cpp deleted file mode 100644 index c4bd9060..00000000 --- a/src/spark-update-tool/src/aptssupdater.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include "aptssupdater.h" -#include -#include -#include -#include -#include - -aptssUpdater::aptssUpdater(QWidget *parent) - : QWidget(parent) -{ - packageName = getUpdateablePackages(); -} - -QStringList aptssUpdater::getUpdateablePackages() -{ - QStringList packageDetails; - QProcess process; - QString command = R"(env LANGUAGE=en_US /usr/bin/apt -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf list --upgradable -o Dir::Etc::sourcelist="/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list" -o Dir::Etc::sourceparts="/dev/null" -o APT::Get::List-Cleanup="0" | awk 'NR>1')"; - - process.start("bash", QStringList() << "-c" << command); - if (!process.waitForFinished()) { - qWarning() << "Process failed to finish."; - return packageDetails; - } - - QString output = process.readAllStandardOutput(); - QStringList lines = output.split('\n', Qt::SkipEmptyParts); - - // 创建临时文件 - QTemporaryFile tempFile; - tempFile.setAutoRemove(false); - if (tempFile.open()) { - QTextStream stream(&tempFile); - - for (const QString &line : lines) { - QRegularExpression regex(R"(([\w\-\+\.]+)/\S+\s+([^\s]+)\s+\S+\s+\[upgradable from: ([^\]]+)\])"); - QRegularExpressionMatch match = regex.match(line); - if (match.hasMatch()) { - QString name = match.captured(1); - QString newVersion = match.captured(2); - QString oldVersion = match.captured(3); - - // 检查版本是否相同,相同则跳过 - if (newVersion == oldVersion) { - qDebug() << "跳过版本相同的包:" << name << "(" << oldVersion << "→" << newVersion << ")"; - continue; - } - - // 写入内存列表 - packageDetails << QString("%1: %2 → %3").arg(name, oldVersion, newVersion); - - // 写入临时文件(原始数据) - stream << name << "|" << oldVersion << "|" << newVersion << "\n"; - } - } - tempFile.close(); - m_tempFilePath = tempFile.fileName(); - qDebug()<< "临时文件路径:" << m_tempFilePath; - - } else { - qWarning() << "无法创建临时文件"; - } - - return packageDetails; -} - - -QStringList aptssUpdater::getPackageSizes() -{ - QStringList packageDetails; - QProcess process; - - // 获取可更新包名列表 - QStringList updateablePackages; - for (const QString &pkgInfo : packageName) { - updateablePackages << pkgInfo.section(":", 0, 0).trimmed(); - } - - foreach (const QString &packageName, updateablePackages) { - // 构建新命令(包含包名参数) - QString command = QString("apt download %1 --print-uris -c /opt/durapps/spark-store/bin/apt-fast-conf/aptss-apt.conf " - "-o Dir::Etc::sourcelist=\"/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/sparkstore.list\" " - "-o Dir::Etc::sourceparts=\"/dev/null\"").arg(packageName); - - process.start("bash", QStringList() << "-c" << command); - if (!process.waitForFinished()) { - qWarning() << "获取包信息失败:" << packageName; - continue; - } - - QString output = process.readAllStandardOutput(); - // 使用正则匹配所有信息 - // 调整正则表达式匹配分组 - QRegularExpression regex(R"('([^']+)'\s+(\S+)\s+(\d+)\s+SHA512:([^\s]+))"); // 分组1:URL 分组2:文件名 分组3:大小 分组4:SHA512 - QRegularExpressionMatch match = regex.match(output); - - if (match.hasMatch()) { - QString url = match.captured(1); - QString fileName = match.captured(2); - QString size = match.captured(3); - QString sha512 = match.captured(4); - - // 调整字段顺序:包名 | 大小 | URL | SHA512 - packageDetails << QString("%1: %2|%3|%4").arg(packageName, size, url, sha512); - } - } - - qDebug() << "完整包信息:" << packageDetails; - return packageDetails; -} - - - - - -QStringList aptssUpdater::getDesktopAppNames() -{ - QStringList appNames; - QProcess dpkgProcess; - - // 获取当前系统语言环境 - QString lang = QLocale().name().replace("_", "-"); - - // 遍历所有可更新包(复用已有的临时文件数据) - QStringList packages = packageName; - - foreach (const QString &package, packages) { - QString packageName = package.split(":")[0]; - QString finalName = packageName; // 默认使用包名 - - // 获取包文件列表 - dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); - dpkgProcess.waitForFinished(); - QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); - - // 先检查常规应用目录 - QStringList regularDesktopFiles = files.filter("/usr/share/applications/"); - QString regularAppName; - if (!regularDesktopFiles.isEmpty()) { - checkDesktopFiles(regularDesktopFiles, regularAppName, lang, packageName); - } - - // 如果常规目录没有找到,再检查特殊目录 - if (regularAppName.isEmpty()) { - QStringList specialDesktopFiles = files.filter(QRegularExpression(QString("/opt/apps/%1/entries/applications").arg(packageName))); - QString specialAppName; - if (!specialDesktopFiles.isEmpty()) { - checkDesktopFiles(specialDesktopFiles, specialAppName, lang, packageName); - if (!specialAppName.isEmpty()) { - finalName = specialAppName; - } - } - } else { - finalName = regularAppName; - } - - // 输出格式为[软件名|包名] - appNames << QString("[%1|%2]").arg(finalName, packageName); - } - qDebug()<< "应用名称列表:" << appNames; - return appNames; -} - - -bool aptssUpdater::checkDesktopFiles(const QStringList &desktopFiles, QString &appName, const QString &lang, const QString &packageName) -{ - QString lastValidName; - QRegularExpression noDisplayRe("^NoDisplay=(true|True)"); - QRegularExpression nameRe("^Name\\[?" + lang + "?\\]?=(.*)"); - QRegularExpression nameOrigRe("^Name=(.*)"); - - foreach (const QString &filePath, desktopFiles) { - if (!filePath.endsWith(".desktop")) continue; - - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) continue; - - bool skip = false; - QString currentName; - - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - - // 检查NoDisplay属性 - if (line.startsWith("NoDisplay=")) { - if (noDisplayRe.match(line).hasMatch()) { - skip = true; - break; - } - } - - // 优先匹配本地化名称 - if (currentName.isEmpty()) { - QRegularExpressionMatch match = nameRe.match(line); - if (match.hasMatch()) { - currentName = match.captured(1); - continue; - } - - // 匹配原始名称 - match = nameOrigRe.match(line); - if (match.hasMatch()) { - currentName = match.captured(1); - } - } - } - - if (!skip && !currentName.isEmpty()) { - lastValidName = currentName; - } - } - - // 处理最终的有效名称 - if (!lastValidName.isEmpty()) { - appName = lastValidName; // 直接赋值而不是使用<< - return true; - } - - // 回退到包名 - appName = packageName; - return false; -} - -QStringList aptssUpdater::getPackageIcons() -{ - QStringList packageIcons; - QProcess dpkgProcess; - - // 遍历所有可更新包 - QStringList packages = packageName; - - foreach (const QString &package, packages) { - QString packageName = package.split(":")[0]; - QString iconPath = ":/resources/default_icon.svg"; // 默认图标 - - // 获取包文件列表 - dpkgProcess.start("dpkg", QStringList() << "-L" << packageName); - dpkgProcess.waitForFinished(); - QStringList files = QString(dpkgProcess.readAllStandardOutput()).split('\n', Qt::SkipEmptyParts); - - // 查找.desktop文件 - QStringList desktopFiles = files.filter(QRegularExpression("/(usr/share|opt/apps)/.*\\.desktop$")); - - // 从.desktop文件中提取图标 - foreach (const QString &desktopFile, desktopFiles) { - QFile file(desktopFile); - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - if (line.startsWith("Icon=")) { - QString iconName = line.mid(5).trimmed(); - - // 处理相对图标名(如Icon=vscode) - if (!iconName.contains('/')) { - // 查找标准图标路径 - QStringList iconPaths = { - QString("/usr/share/pixmaps/%1.png").arg(iconName), - QString("/usr/share/icons/hicolor/48x48/apps/%1.png").arg(iconName), - QString("/usr/share/icons/hicolor/scalable/apps/%1.svg").arg(iconName), - QString("/opt/apps/%1/entries/icons/hicolor/48x48/apps/%2.png").arg(packageName, iconName) - }; - - foreach (const QString &path, iconPaths) { - if (QFile::exists(path)) { - iconPath = path; - break; - } - } - } else { - // 已经是绝对路径 - if (QFile::exists(iconName)) { - iconPath = iconName; - } - } - break; - } - } - file.close(); - } - } - - // 如果.desktop中没有找到图标,尝试直接查找包中的图标文件 - if (iconPath == ":/resources/default_icon.svg") { - QStringList iconFiles = files.filter(QRegularExpression("/(usr/share/pixmaps|usr/share/icons|opt/apps/.*/entries/icons)/.*\\.(png|svg)$")); - if (!iconFiles.isEmpty()) { - iconPath = iconFiles.first(); - } - } - - qDebug() << "包名:" << packageName << "图标路径:" << iconPath; - packageIcons << QString("%1: %2").arg(packageName, iconPath); - } - - return packageIcons; -} - - -QJsonArray aptssUpdater::getUpdateInfoAsJson() -{ - QJsonArray jsonArray; - - // 获取所有需要的信息 - QStringList sizes = getPackageSizes(); - QStringList names = getDesktopAppNames(); - QStringList icons = getPackageIcons(); - - // 创建包名到各种信息的映射 - QHash> packageInfo; - - // 解析包版本信息 - for (const QString &pkg : packageName) { - QStringList parts = pkg.split(": "); - if (parts.size() >= 2) { - QString packageName = parts[0]; - QStringList versions = parts[1].split(" → "); - if (versions.size() == 2) { - packageInfo[packageName]["current_version"] = versions[0]; - packageInfo[packageName]["new_version"] = versions[1]; - } - } - } - - // 解析包详细信息(新增部分) - for (const QString &sizeInfo : sizes) { - QStringList parts = sizeInfo.split(": "); - if (parts.size() == 2) { - QString packageName = parts[0]; - QStringList details = parts[1].split("|"); - if (details.size() == 3) { // 现在包含大小|URL|SHA512 - packageInfo[packageName]["size"] = details[0]; - packageInfo[packageName]["url"] = details[1]; - packageInfo[packageName]["sha512"] = details[2]; - } - } - } - - // 解析应用名称信息 - for (const QString &nameInfo : names) { - if (nameInfo.startsWith("[") && nameInfo.endsWith("]")) { - QString content = nameInfo.mid(1, nameInfo.length() - 2); - QStringList parts = content.split("|"); - if (parts.size() == 2) { - QString displayName = parts[0]; - QString packageName = parts[1]; - packageInfo[packageName]["display_name"] = displayName; - } - } - } - - // 解析图标信息 - for (const QString &iconInfo : icons) { - QStringList parts = iconInfo.split(": "); - if (parts.size() == 2) { - QString packageName = parts[0]; - packageInfo[packageName]["icon"] = parts[1].trimmed(); - } - } - - // 构建JSON数组 - for (const QString &packageName : packageInfo.keys()) { - QJsonObject jsonObj; - jsonObj["package"] = packageName; - - // 使用显示名称(如果有),否则使用包名 - if (packageInfo[packageName].contains("display_name")) { - jsonObj["name"] = packageInfo[packageName]["display_name"]; - } else { - jsonObj["name"] = packageName; - } - - jsonObj["current_version"] = packageInfo[packageName]["current_version"]; - jsonObj["new_version"] = packageInfo[packageName]["new_version"]; - jsonObj["icon"] = packageInfo[packageName]["icon"]; - jsonObj["ignored"] = false; // 默认不忽略 - - // 如果有大小信息也加入 - if (packageInfo[packageName].contains("size")) { - jsonObj["size"] = packageInfo[packageName]["size"]; - } - - // 在构建JSON对象时添加新字段(在jsonObj中添加): - if (packageInfo[packageName].contains("url")) { - jsonObj["download_url"] = packageInfo[packageName]["url"]; - qDebug() << "生成的下载 URL:" << packageInfo[packageName]["url"]; // 检查生成的 URL - } else { - qWarning() << "未找到下载 URL,包名:" << packageName; - jsonObj["download_url"] = ""; // 设置为空字符串以避免崩溃 - } - jsonObj["sha512"] = packageInfo[packageName]["sha512"]; - jsonArray.append(jsonObj); - } - qDebug()< -#include -#include -#include -#include -#include -class aptssUpdater : public QWidget -{ - Q_OBJECT -public: - explicit aptssUpdater(QWidget *parent = nullptr); - - QStringList getUpdateablePackages(); // 查询可更新包列表及更新内容 - QStringList getPackageSizes(); // 获取每个包的大小 - QStringList getDesktopAppNames(); // 获取桌面应用名称列表 - QStringList getPackageIcons(); // 获取包图标列表 - QJsonArray getUpdateInfoAsJson(); // 获取更新信息的 JSON 格式 - QString m_tempFilePath; -signals: -private: - bool checkDesktopFiles(const QStringList &desktopFiles, QString &appName, const QString &lang, const QString &packageName); - QStringList packageName; -}; - -#endif // APTSSUPDATER_H \ No newline at end of file diff --git a/src/spark-update-tool/src/downloadmanager.cpp b/src/spark-update-tool/src/downloadmanager.cpp deleted file mode 100644 index 97803249..00000000 --- a/src/spark-update-tool/src/downloadmanager.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "downloadmanager.h" -#include -#include -#include -#include -#include - -DownloadManager::DownloadManager(QObject *parent) : QObject(parent) -{ - cleanupTempFiles(); -} - -void DownloadManager::startDownload(const QString &packageName, const QString &url, const QString &outputPath) -{ - if (m_processes.contains(packageName)) { - qWarning() << packageName << " is already downloading."; - return; - } - - QString metalinkUrl = url + ".metalink"; - QFileInfo fileInfo(outputPath); - - QStringList arguments = { - "--enable-rpc=false", - "--console-log-level=warn", - "--summary-interval=1", - "--allow-overwrite=true", - "--dir=" + fileInfo.absolutePath(), - "--out=" + fileInfo.fileName(), - metalinkUrl - }; - - QProcess *process = new QProcess(this); - m_processes.insert(packageName, process); - - // 新增:准备日志文件 - QString logPath = QString("/tmp/%1_download.log").arg(packageName); - QFile *logFile = new QFile(logPath, process); - if (logFile->open(QIODevice::Append | QIODevice::Text)) { - // 设置权限为777 - QFile::setPermissions(logPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | - QFile::ReadGroup | QFile::WriteGroup | QFile::ExeGroup | - QFile::ReadOther | QFile::WriteOther | QFile::ExeOther); - connect(process, &QProcess::readyReadStandardOutput, this, [this, packageName, process, logFile]() { - while (process->canReadLine()) { - QString line = QString::fromUtf8(process->readLine()).trimmed(); - // 写入日志 - logFile->write(line.toUtf8() + '\n'); - logFile->flush(); - QRegularExpression regex(R"(\((\d+)%\))"); - QRegularExpressionMatch match = regex.match(line); - if (match.hasMatch()) { - int progress = match.captured(1).toInt(); - emit downloadProgress(packageName, progress); - } - } - }); - connect(process, &QProcess::readyReadStandardError, this, [process, logFile]() { - QByteArray err = process->readAllStandardError(); - logFile->write(err); - logFile->flush(); - }); - } - - connect(process, QOverload::of(&QProcess::finished), - this, [this, packageName, outputPath, logFile](int exitCode, QProcess::ExitStatus status) { - bool success = (exitCode == 0 && status == QProcess::NormalExit); - if (!success) { - qWarning() << "Download failed for" << packageName << "exit code:" << exitCode; - } - - removeAria2Files(outputPath); // 清理残留 .aria2 - emit downloadFinished(packageName, success); - - if (logFile) logFile->close(); - - QProcess *proc = m_processes.take(packageName); - if (proc) proc->deleteLater(); - }); - - process->start("aria2c", arguments); -} - -void DownloadManager::cancelDownload(const QString &packageName) -{ - if (!m_processes.contains(packageName)) return; - - QProcess *process = m_processes.take(packageName); - if (process) { - process->kill(); // 立即终止 - process->waitForFinished(3000); // 最多等待3秒 - process->deleteLater(); - } - - emit downloadFinished(packageName, false); // 显式通知取消 - -} - -void DownloadManager::removeAria2Files(const QString &filePath) -{ - QString ariaFile = filePath + ".aria2"; - QFile::remove(ariaFile); -} - -bool DownloadManager::isDownloading(const QString &packageName) const -{ - return m_processes.contains(packageName); -} - -void DownloadManager::cleanupTempFiles() -{ - QDir tempDir(QDir::tempPath()); - QStringList leftovers = tempDir.entryList(QStringList() << "*.aria2", QDir::Files); - for (const QString &f : leftovers) { - tempDir.remove(f); - } -} diff --git a/src/spark-update-tool/src/downloadmanager.h b/src/spark-update-tool/src/downloadmanager.h deleted file mode 100644 index 0614368a..00000000 --- a/src/spark-update-tool/src/downloadmanager.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef DOWNLOADMANAGER_H -#define DOWNLOADMANAGER_H - -#include -#include -#include - -class DownloadManager : public QObject -{ - Q_OBJECT -public: - explicit DownloadManager(QObject *parent = nullptr); - void startDownload(const QString &packageName, const QString &url, const QString &outputPath); - void cancelDownload(const QString &packageName); - bool isDownloading(const QString &packageName) const; - -signals: - void downloadProgress(const QString &packageName, int progress); - void downloadFinished(const QString &packageName, bool success); - -private: - void cleanupTempFiles(); - void removeAria2Files(const QString &filePath); - - QMap m_processes; -}; - -#endif // DOWNLOADMANAGER_H diff --git a/src/spark-update-tool/src/icons.qrc b/src/spark-update-tool/src/icons.qrc deleted file mode 100644 index f81e1f88..00000000 --- a/src/spark-update-tool/src/icons.qrc +++ /dev/null @@ -1,8 +0,0 @@ - - - ../resources/down_arrow.svg - ../resources/default_icon.svg - ../resources/spark-update-tool.svg - ../resources/128*128/spark-update-tool.png - - diff --git a/src/spark-update-tool/src/main.cpp b/src/spark-update-tool/src/main.cpp deleted file mode 100644 index a3df5a43..00000000 --- a/src/spark-update-tool/src/main.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "mainwindow.h" -#include -#include -#include -#include // for geteuid -#include // for getenv -#include // For debugging output - -bool isRoot() { - return geteuid() == 0; -} - -bool elevateToRoot() { - QString program = QCoreApplication::applicationFilePath(); - qDebug() << "Current application path:" << program; - - QByteArray display = qgetenv("DISPLAY"); - QByteArray xauthority = qgetenv("XAUTHORITY"); - - QStringList args; - args << "env" - << "DISPLAY=" + display - << "XAUTHORITY=" + xauthority - << program; - - QProcess process; - process.setProgram("pkexec"); - process.setArguments(args); - - qDebug() << "Attempting to elevate using pkexec with arguments:" << args; - - process.start(); - if (!process.waitForStarted(5000)) { - qDebug() << "Failed to start pkexec."; - return false; - } - - // 阻塞等待提权进程退出(比如主程序窗口关闭) - if (!process.waitForFinished(-1)) { // 等待直到新进程退出 - qDebug() << "pkexec process waitForFinished failed."; - return false; - } - - int exitCode = process.exitCode(); - QProcess::ExitStatus exitStatus = process.exitStatus(); - - qDebug() << "pkexec exit code:" << exitCode; - qDebug() << "pkexec exit status:" << exitStatus; - qDebug() << "pkexec stderr:" << process.readAllStandardError(); - - return (exitStatus == QProcess::NormalExit && exitCode == 0); -} - -int main(int argc, char *argv[]) -{ - // 必须在 QGuiApplication 实例创建之前调用 - // QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); - - QApplication a(argc, argv); - a.setWindowIcon(QIcon(":/resources/128*128/spark-update-tool.png")); - if (!isRoot()) { - qDebug() << "Not running as root. Attempting to elevate..."; - if (!elevateToRoot()) { - qDebug() << "Elevation failed or pkexec command was not executed successfully."; - QMessageBox::critical(nullptr, - "权限不足", - "提权失败!\n\n您的系统可能不支持 `pkexec` 或 `polkit` 配置不正确," - "或者您取消了授权。\n\n请尝试使用 `sudo` 命令运行此程序:" - "\n\n在终端中输入:\n`sudo " + QCoreApplication::applicationName() + "`"); - return 0; // 提权失败,退出程序 - } else { - // 如果 elevateToRoot 返回 true,说明 pkexec 命令本身执行成功 - // 但这并不意味着原始程序以 root 权限启动了 - // 因为 elevateToRoot 启动的是一个新的进程,当前进程应该退出 - // 否则会并行运行两个程序实例 - qDebug() << "pkexec command executed successfully (new process likely started). Exiting current process."; - return 0; // 当前非root进程退出 - } - } else { - qDebug() << "Running as root."; - } - - MainWindow w; - w.show(); - return a.exec(); -} \ No newline at end of file diff --git a/src/spark-update-tool/src/mainwindow.cpp b/src/spark-update-tool/src/mainwindow.cpp deleted file mode 100644 index 54f064ca..00000000 --- a/src/spark-update-tool/src/mainwindow.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "mainwindow.h" -#include "./ui_mainwindow.h" -#include -#include -#include -#include // 新增 -#include // 新增 -#include -#include -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) - , m_model(new AppListModel(this)) - , m_delegate(new AppDelegate(this)) -{ - QIcon icon(":/resources/128*128/spark-update-tool.png"); - setWindowIcon(icon); - QProgressDialog *progressDialog = new QProgressDialog("正在与服务器通信,获取更新信息中...", QString(), 0, 0, this); - progressDialog->setWindowModality(Qt::ApplicationModal); - progressDialog->setCancelButton(nullptr); - progressDialog->setWindowTitle("请稍候"); - progressDialog->setMinimumDuration(0); - progressDialog->setWindowFlags(progressDialog->windowFlags() & ~Qt::WindowCloseButtonHint); // 禁用关闭按钮 - progressDialog->show(); - //异步执行runAptssUpgrade - QFutureWatcher *watcher = new QFutureWatcher(this); - connect(watcher, &QFutureWatcher::finished, this, [=]() { - progressDialog->close(); - progressDialog->deleteLater(); - watcher->deleteLater(); - ui->setupUi(this); - QIcon icon(":/resources/128*128/spark-update-tool.png"); - setWindowIcon(icon); - // 创建 QListView 并设置父控件为 ui->appWidget - listView = new QListView(ui->appWidget); - listView->setModel(m_model); - listView->setItemDelegate(m_delegate); - - // 新增:确保 delegate 拥有 model 指针 - m_delegate->setModel(m_model); - - // 设置 QListView 填充 ui->appWidget - QVBoxLayout *layout = new QVBoxLayout(ui->appWidget); - layout->addWidget(listView); - layout->setContentsMargins(0, 0, 0, 0); - connect(m_delegate, &AppDelegate::updateDisplay, this, [=](const QString &packageName) { - for (int i = 0; i < m_model->rowCount(); ++i) { - QModelIndex index = m_model->index(i); - if (index.data(Qt::UserRole + 1).toString() == packageName) { - m_model->dataChanged(index, index); // 刷新该行 - break; - } - } - }); - - // 新增:点击“更新全部”按钮批量下载 - connect(ui->updatePushButton, &QPushButton::clicked, this, [=](){ - qDebug()<<"更新全部按钮被点击"; - m_delegate->startDownloadForAll(); - }); - - checkUpdates(); - // 新增:监听搜索框文本变化 - connect(ui->searchPlainTextEdit, &QPlainTextEdit::textChanged, this, [=]() { - QString keyword = ui->searchPlainTextEdit->toPlainText(); - filterAppsByKeyword(keyword); - }); - initStyle(); - - // 确保搜索框内容为空,placeholder 能显示 - ui->searchPlainTextEdit->clear(); - }); - - // 启动异步任务 - watcher->setFuture(QtConcurrent::run([this](){ - runAptssUpgrade(); - })); -} -//初始化控件样式 -void MainWindow::initStyle() -{ - //设置窗口标题 - this->setWindowTitle("软件更新中心"); - - //查询框样式 - ui->searchPlainTextEdit->setStyleSheet(R"( - QPlainTextEdit { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - border-radius: 4px; - padding-top: 8px; - padding-bottom: 8px; - font-size: 9px; - line-height: 1.4; - color: #9CA3AF; - } - QPlainTextEdit[placeholderText]:empty { - color: #9CA3AF; - } - )"); - - ui->searchPlainTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - ui->searchPlainTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - //筛选框样式 - ui->FilterComboBox->setStyleSheet(R"( - QComboBox { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - border-radius: 4px; - color: #4B5563; - padding: 4px 8px; - } - - QComboBox::drop-down { - border: none; - width: 20px; - } - - QComboBox::down-arrow { - image: url(:/resources/down_arrow.svg); - width: 12px; - height: 16px; - } - QComboBox QAbstractItemView { - background-color: #FFFFFF; - border: 1px solid #E5E7EB; - color: #4B5563; - selection-background-color: #F3F4F6; - selection-color: #111827; - } - )"); - - //更新软件按钮样式 - ui->updatePushButton->setStyleSheet(R"( - QPushButton { - background-color: #2563EB; - color: #FFFFFF; - border: none; - border-radius: 4px; - font-size: 14px; - padding: 6px 12px; - text-align: center; - } - - QPushButton:hover { - background-color: #1D4ED8; /* 深一点的 hover 效果,可选 */ - } - - QPushButton:pressed { - background-color: #1E40AF; /* 按下效果,可选 */ - } - - QPushButton:disabled { - background-color: #A5B4FC; - color: #F9FAFB; - } - )"); - - //设置背景填充颜色 - ui->backgroundWidget->setStyleSheet(R"( - QWidget { - background-color: #FFFFFF; - border-radius: 12px; - } - )"); - - //设置主背景颜色 - this->setStyleSheet("background-color: #F8FAFC;"); - - // 添加滚动条样式 - this->setStyleSheet(R"( - QScrollBar:vertical { - background: #F3F4F6; - width: 8px; - margin: 0px; - } - QScrollBar::handle:vertical { - background: #D1D5DB; - border-radius: 4px; - min-height: 30px; - } - QScrollBar::handle:vertical:hover { - background: #9CA3AF; - } - QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - background: none; - height: 0px; - } - - QScrollBar:horizontal { - background: #F3F4F6; - height: 8px; - margin: 0px; - } - QScrollBar::handle:horizontal { - background: #D1D5DB; - border-radius: 4px; - min-width: 30px; - } - QScrollBar::handle:horizontal:hover { - background: #9CA3AF; - } - )"); -} -void MainWindow::checkUpdates() -{ - aptssUpdater updater; - QJsonArray updateInfo = updater.getUpdateInfoAsJson(); - m_allApps = updateInfo; // 保存所有应用数据 - m_model->setUpdateData(updateInfo); - - for (const auto &item : updateInfo) { - QJsonObject obj = item.toObject(); - qDebug() << "模型设置的包名:" << obj["package"].toString(); - qDebug() << "模型设置的下载 URL:" << obj["download_url"].toString(); // 检查模型数据 - } -} - -// 新增:根据关键字过滤应用 -void MainWindow::filterAppsByKeyword(const QString &keyword) -{ - if (keyword.trimmed().isEmpty()) { - m_model->setUpdateData(m_allApps); - return; - } - QJsonArray filtered; - for (const auto &item : m_allApps) { - QJsonObject obj = item.toObject(); - // 可根据需要匹配更多字段 - QString name = obj.value("name").toString(); - QString package = obj.value("package").toString(); - if (name.contains(keyword, Qt::CaseInsensitive) || - package.contains(keyword, Qt::CaseInsensitive)) { - filtered.append(item); - } - } - m_model->setUpdateData(filtered); -} - -void MainWindow::runAptssUpgrade() -{ - QProcess process; - QStringList args; - args << "sudo" <<"aptss" << "ssupdate"; - process.start("sudo", args); - if (!process.waitForStarted(5000)) { - QMessageBox::warning(this, "升级失败", "无法启动 sudo aptss ssupdate"); - return; - } - process.write("n\n"); - process.closeWriteChannel(); - process.waitForFinished(-1); - if (process.exitCode() != 0) { - QMessageBox::warning(this, "升级失败", "执行 sudo aptss ssupdate 失败,请检查系统环境。"); - } -} - -MainWindow::~MainWindow() -{ - delete ui; -} diff --git a/src/spark-update-tool/src/mainwindow.h b/src/spark-update-tool/src/mainwindow.h deleted file mode 100644 index bc7f9d72..00000000 --- a/src/spark-update-tool/src/mainwindow.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include "aptssupdater.h" -#include "applistmodel.h" -#include "appdelegate.h" -#include -#include // 添加头文件 - -QT_BEGIN_NAMESPACE -namespace Ui { -class MainWindow; -} -QT_END_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); - -private: - Ui::MainWindow *ui; - void checkUpdates(); - void initStyle(); - void runAptssUpgrade(); - AppListModel *m_model; - AppDelegate *m_delegate; - QListView *listView; // 声明 QListView 指针 - QJsonArray m_allApps; // 新增:保存所有应用数据 - void filterAppsByKeyword(const QString &keyword); // 新增:搜索过滤函数声明 -}; -#endif // MAINWINDOW_H diff --git a/src/spark-update-tool/src/mainwindow.ui b/src/spark-update-tool/src/mainwindow.ui deleted file mode 100644 index a53a4b88..00000000 --- a/src/spark-update-tool/src/mainwindow.ui +++ /dev/null @@ -1,256 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 1440 - 858 - - - - MainWindow - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - Noto Sans Vai - 22 - - - - 软件更新中心 - - - - - - - Qt::Orientation::Horizontal - - - - 1245 - 20 - - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 320 - 38 - - - - - 320 - 38 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 200 - 38 - - - - - - - 81.000000000000000 - - - 搜索软件... - - - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - 214 - 40 - - - - - 214 - 40 - - - - - - 0 - 0 - 102 - 38 - - - - - 102 - 38 - - - - - 102 - 38 - - - - - - - Qt::LayoutDirection::LeftToRight - - - QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow - - - - 按名称 - - - - - - - 118 - 0 - 96 - 40 - - - - 更新全部 - - - - - - - - - - - true - - - - 0 - 0 - - - - - - - - - - - - - 0 - 0 - 1440 - 23 - - - - - - - -- Gitee From 30570b759bfff3f62e493b032d7909c8bb4cb5ed Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 28 Aug 2025 21:17:32 +0800 Subject: [PATCH 09/10] =?UTF-8?q?chore:=E5=8A=A0=E5=85=A5pro=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spark-update-tool.pro | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 spark-update-tool.pro diff --git a/spark-update-tool.pro b/spark-update-tool.pro new file mode 100644 index 00000000..69dc5007 --- /dev/null +++ b/spark-update-tool.pro @@ -0,0 +1,48 @@ +# spark-update-tool.pro +QT += core gui widgets network concurrent +TARGET = spark-update-tool +TEMPLATE = app + +# Set C++ standard to C++17 +CONFIG += c++17 + +# Enable auto features (uic, moc, rcc) +CONFIG += qt warn_on release +QT_CONFIG += no-pkg-config + +# Version info (replace with your actual version) +VERSION = 0.1.0 +DEFINES += APP_VERSION=\\\"$$VERSION\\\" + +# Source files +SOURCES += \ + src/main.cpp \ + src/mainwindow.cpp \ + src/aptssupdater.cpp \ + src/appdelegate.cpp \ + src/applistmodel.cpp \ + src/downloadmanager.cpp + +HEADERS += \ + src/mainwindow.h \ + src/aptssupdater.h \ + src/appdelegate.h \ + src/applistmodel.h \ + src/downloadmanager.h + +FORMS += \ + src/mainwindow.ui + +RESOURCES += \ + src/icons.qrc + +# macOS bundle properties (optional) +macx { + QMAKE_INFO_PLIST = Info.plist + ICON = resources/spark-update-tool.icns + BUNDLE_IDENTIFIER = org.spark.store.update-tool +} + +# Installation paths (matches CMake install) +target.path = $$[QT_INSTALL_BINS] +INSTALLS += target \ No newline at end of file -- Gitee From 4b8145f3edc46ea66878f43421b7b441fdb459b1 Mon Sep 17 00:00:00 2001 From: momen Date: Thu, 28 Aug 2025 21:25:59 +0800 Subject: [PATCH 10/10] =?UTF-8?q?update:=E6=9B=B4=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7=E4=B8=BA4.8.1~test2,=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=99=A8=E5=AE=89=E8=A3=85=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=90=8E=E4=BB=8D=E7=84=B6=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=AE=8C=E6=88=90=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 0d5f275f..71001573 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +spark-store (4.8.1~test2) UNRELEASED; urgency=medium + + * 修复更新器安装软件完成后仍然显示下载完成的问题 + + -- momen Tue, 28 Aug 2025 01:03:08 +0800 + spark-store (4.8.1~test1) UNRELEASED; urgency=medium * 添加全新的更新器 -- Gitee