diff --git a/3rdparty/interface/archiveinterface/archiveinterface.cpp b/3rdparty/interface/archiveinterface/archiveinterface.cpp index e953a4bb..0f856ed2 100644 --- a/3rdparty/interface/archiveinterface/archiveinterface.cpp +++ b/3rdparty/interface/archiveinterface/archiveinterface.cpp @@ -76,6 +76,11 @@ ErrorType ReadOnlyArchiveInterface::errorType() return m_eErrorType; } +void ReadOnlyArchiveInterface::resetResult() +{ + m_eErrorType = ET_NoError; +} + bool ReadOnlyArchiveInterface::doKill() { return false; // 修改默认为未取消 diff --git a/3rdparty/interface/archiveinterface/archiveinterface.h b/3rdparty/interface/archiveinterface/archiveinterface.h index 6721fc70..8445eab6 100644 --- a/3rdparty/interface/archiveinterface/archiveinterface.h +++ b/3rdparty/interface/archiveinterface/archiveinterface.h @@ -116,6 +116,12 @@ class ReadOnlyArchiveInterface : public QObject */ ErrorType errorType(); + /** + * @brief resetResult forget the result of the previous operation; the interface is reused for + * later operations on the same archive, so a job starts with this + */ + virtual void resetResult(); + /** * @brief pauseOperation 暂停操作 */ diff --git a/3rdparty/interface/archiveinterface/cliinterface.cpp b/3rdparty/interface/archiveinterface/cliinterface.cpp index 0db6cc57..0ce4dec1 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.cpp +++ b/3rdparty/interface/archiveinterface/cliinterface.cpp @@ -66,6 +66,12 @@ CliInterface::~CliInterface() deleteProcess(); } +void CliInterface::resetResult() +{ + ReadOnlyArchiveInterface::resetResult(); + m_finishType = PFT_Nomral; +} + PluginFinishType CliInterface::list() { setPassword(QString()); @@ -784,6 +790,9 @@ bool CliInterface::runProcess(const QString &programName, const QStringList &arg [=](int exitCode, QProcess::ExitStatus) { if (m_process) { if (exitCode != 0) { + // Release the failed listing process: the interface is used again (e.g. to + // extract after asking for the password again), and runProcess() needs none + deleteProcess(); emit signalprogress(100); emit signalFinished(PFT_Error); return; diff --git a/3rdparty/interface/archiveinterface/cliinterface.h b/3rdparty/interface/archiveinterface/cliinterface.h index 64913354..d6441cbf 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.h +++ b/3rdparty/interface/archiveinterface/cliinterface.h @@ -66,6 +66,12 @@ class CliInterface : public ReadWriteArchiveInterface explicit CliInterface(QObject *parent, const QVariantList &args); ~CliInterface() override; + /** + * @brief resetResult also forget the previous finish type: an operation that succeeds after a + * failed one on the same archive must not end as an error + */ + void resetResult() override; + // ReadOnlyArchiveInterface interface public: PluginFinishType list() override; diff --git a/3rdparty/interface/queries.cpp b/3rdparty/interface/queries.cpp index d2ab9e38..d4244605 100644 --- a/3rdparty/interface/queries.cpp +++ b/3rdparty/interface/queries.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -459,6 +460,18 @@ void ExtractToQuery::setWidgetType(QWidget *pWgt, DPalette::ColorType ct, double +bool PasswordNeededQuery::s_bPreviousPasswordWrong = false; + +void PasswordNeededQuery::setPreviousPasswordWrong(bool bWrong) +{ + s_bPreviousPasswordWrong = bWrong; +} + +bool PasswordNeededQuery::previousPasswordWrong() +{ + return s_bPreviousPasswordWrong; +} + PasswordNeededQuery::PasswordNeededQuery(const QString &strFileName, QObject *parent) : Query(parent) { @@ -514,6 +527,17 @@ void PasswordNeededQuery::execute() passwordedit->setMinimumHeight(36); passwordedit->setFixedWidth(280); + // The previous password was wrong: say so and mark the field until the user types + if (s_bPreviousPasswordWrong) { + s_bPreviousPasswordWrong = false; + m_strDesText = QCoreApplication::translate("MainWindow", "Wrong password"); + pTipLbl->setText(m_strDesText); + passwordedit->setAlert(true); + connect(passwordedit, &DPasswordEdit::textChanged, passwordedit, [passwordedit]() { + passwordedit->setAlert(false); + }); + } + dialog->addButton(QObject::tr("Cancel", "button"), true, DDialog::ButtonNormal); dialog->addButton(QObject::tr("OK", "button"), true, DDialog::ButtonRecommend); dialog->getButton(1)->setEnabled(false); diff --git a/3rdparty/interface/queries.h b/3rdparty/interface/queries.h index df56165e..4b46c5b5 100644 --- a/3rdparty/interface/queries.h +++ b/3rdparty/interface/queries.h @@ -249,7 +249,19 @@ class PasswordNeededQuery : public Query */ QString password(); + /** + * @brief setPreviousPasswordWrong the next password dialog says the previous password was wrong + * (set before an extraction or load is restarted after a wrong password) + */ + static void setPreviousPasswordWrong(bool bWrong); + + /** + * @brief previousPasswordWrong true until a password dialog has shown the hint + */ + static bool previousPasswordWrong(); + private: + static bool s_bPreviousPasswordWrong; QString m_strDesText; QString m_strFileName; int m_iLabelOldHeight = 0; diff --git a/src/source/archivemanager/singlejob.cpp b/src/source/archivemanager/singlejob.cpp index 9cb2a0e6..0b92ffef 100644 --- a/src/source/archivemanager/singlejob.cpp +++ b/src/source/archivemanager/singlejob.cpp @@ -56,6 +56,10 @@ void SingleJob::start() return; } + // The interface is reused for later operations on the same archive: start without the result of + // the previous one (errors found during this job, e.g. while listing first, are kept) + m_pInterface->resetResult(); + // 判断是否通过线程的方式调用 if (m_pInterface->waitForFinished()) { qCDebug(appLog) << "Executing work directly"; @@ -532,6 +536,10 @@ void UpdateJob::start() return; } + // The interface is reused for later operations on the same archive: start without the result of + // the previous one (errors found during this job, e.g. while listing first, are kept) + m_pInterface->resetResult(); + d->start(); // 开启线程,执行操作 } diff --git a/src/source/mainwindow.cpp b/src/source/mainwindow.cpp index 4491aa88..ce0dba8c 100644 --- a/src/source/mainwindow.cpp +++ b/src/source/mainwindow.cpp @@ -1323,6 +1323,11 @@ void MainWindow::slotJobFinished(ArchiveJob::JobType eJobType, PluginFinishType { qCInfo(appLog) << "操作类型:" << eJobType << "****结束类型:" << eFinishType << "****错误类型" << eErrorType; + // A retry after a wrong password asks for the password again; if the job ended without showing the + // dialog, the hint is still pending: do not retry again, and do not leave the hint for a later dialog. + m_bWrongPasswordUnanswered = PasswordNeededQuery::previousPasswordWrong(); + PasswordNeededQuery::setPreviousPasswordWrong(false); + switch (eFinishType) { case PFT_Nomral: handleJobNormalFinished(eJobType, eErrorType); // 处理job正常结束 @@ -1362,8 +1367,54 @@ void MainWindow::slotJobFinished(ArchiveJob::JobType eJobType, PluginFinishType PERF_PRINT_END("POINT-05"); } +bool MainWindow::retryAfterWrongPassword(ArchiveJob::JobType eJobType) +{ + if (m_bWrongPasswordUnanswered || m_stUnCompressParameter.bBatch) { + return false; + } + + const QString strArchive = m_stUnCompressParameter.strFullPath; + if (strArchive.isEmpty()) { + return false; + } + + if (ArchiveJob::JT_Load == eJobType) { + PasswordNeededQuery::setPreviousPasswordWrong(true); + QTimer::singleShot(0, this, [this, strArchive]() { + loadArchive(strArchive); + refreshPage(); + }); + return true; + } + + if (StartupType::ST_ExtractHere == m_eStartupType || StartupType::ST_Extractto == m_eStartupType) { + // Started from the file manager: extract the same archive the same way again + PasswordNeededQuery::setPreviousPasswordWrong(true); + const StartupType eType = m_eStartupType; + QTimer::singleShot(0, this, [this, eType, strArchive]() { + rightExtract2Path(eType, QStringList() << strArchive); + refreshPage(); + }); + return true; + } + + // Opened in the window: extract again to the destination the user chose (strExtractPath may already + // include the folder named after the archive, which would be added a second time) + const QString strPath = m_strUncompressBasePath; + if (strPath.isEmpty() || m_pUnCompressPage->archiveFullPath().isEmpty()) { + return false; + } + PasswordNeededQuery::setPreviousPasswordWrong(true); + m_ePageID = PI_UnCompress; + QTimer::singleShot(0, this, [this, strPath]() { + slotUncompressClicked(strPath); + }); + return true; +} + void MainWindow::slotUncompressClicked(const QString &strUncompressPath) { + m_strUncompressBasePath = strUncompressPath; qCDebug(appLog) << "Starting extraction to path:" << strUncompressPath; m_operationtype = Operation_Extract; //解压操作 @@ -1934,8 +1985,11 @@ void MainWindow::handleJobErrorFinished(ArchiveJob::JobType eJobType, ErrorType case ET_ArchiveDamaged: showErrorMessage(FI_Load, EI_ArchiveDamaged); break; - // 密码错误 + // 密码错误:重新询问密码,不显示失败界面 case ET_WrongPassword: + if (retryAfterWrongPassword(eJobType)) { + break; + } showErrorMessage(FI_Load, EI_WrongPassword, true); break; // ftp目录不支持seek操作 @@ -2004,8 +2058,11 @@ void MainWindow::handleJobErrorFinished(ArchiveJob::JobType eJobType, ErrorType showErrorMessage(FI_Uncompress, EI_ArchiveDamaged, !(StartupType::ST_ExtractHere == m_eStartupType || StartupType::ST_Extractto == m_eStartupType)); break; - // 密码错误 + // 密码错误:重新询问密码,不显示失败界面 case ET_WrongPassword: + if (retryAfterWrongPassword(eJobType)) { + break; + } showErrorMessage(FI_Uncompress, EI_WrongPassword, !(StartupType::ST_ExtractHere == m_eStartupType || StartupType::ST_Extractto == m_eStartupType)); break; @@ -3549,6 +3606,13 @@ void MainWindow::slotFailureReturn() } break; case FI_Uncompress: { + // An archive opened in the window: go back to its file list instead of an empty home page + if (!(StartupType::ST_ExtractHere == m_eStartupType || StartupType::ST_Extractto == m_eStartupType) + && !m_stUnCompressParameter.bBatch && !m_pUnCompressPage->archiveFullPath().isEmpty()) { + m_ePageID = PI_UnCompress; + refreshPage(); + return; + } m_pUnCompressPage->clear(); // 清空解压界面 } break; diff --git a/src/source/mainwindow.h b/src/source/mainwindow.h index 8f0d39cc..4041090d 100644 --- a/src/source/mainwindow.h +++ b/src/source/mainwindow.h @@ -185,6 +185,13 @@ public : */ void handleJobErrorFinished(ArchiveJob::JobType eJobType, ErrorType eErrorType); + /** + * @brief retryAfterWrongPassword after a wrong password, restart the same load or extraction so the + * password dialog asks again (with a "Wrong password" hint) instead of showing the failure page + * @return false when it does not apply (batch extraction, no dialog was shown since the last retry, ...) + */ + bool retryAfterWrongPassword(ArchiveJob::JobType eJobType); + /** * @brief addFiles2Archive 向压缩包中添加文件 * @param listFiles 需要添加的文件 @@ -579,6 +586,8 @@ private Q_SLOTS: DFileWatcher *m_pFileWatcher = nullptr; // 文件监控 QString m_strFinalConvertFile; // 格式转换最终的文件全路径 QString m_fileWriteErrorName; // 创建失败的文件名 + bool m_bWrongPasswordUnanswered = false; // a retry after a wrong password ended before any password dialog + QString m_strUncompressBasePath; // the destination chosen on the file list page (before any folder is added) StartupType m_eStartupType = StartupType::ST_Normal; // 操作方式 CalculateSizeThread *m_mywork = nullptr; // 计算文件(夹)大小线程