From 234a574868089583ff7684f9cf5777e661786b37 Mon Sep 17 00:00:00 2001 From: dengzhongyuan365-dev Date: Thu, 23 Jul 2026 11:00:00 +0000 Subject: [PATCH] feat: add safe preview and controlled save for files containing NUL bytes Cherry-pick of PR #489 (merge commit b5df59d3) from master to release/eagle. Conflicts resolved preferring release/eagle base while preserving all NUL safety preview functionality. When a file contains \x00 NUL bytes, the editor enters a read-only preview mode that displays NUL as visible text \00 with red highlight, shows a warning bar with an "Edit Anyway" button, and enforces a three-button confirmation dialog (Don't Save / Save As / Save Anyway) for both Ctrl+S and close-tab flows. Save As to the original path is rejected in preview mode. Auto-backup is short-circuited during preview to prevent persisting unsaved preview edits across sessions. PMS: bug-371363 Multica Issue: https://agent-dev.uniontech.com/issues/14dc6e9a-d392-4a6a-b8a9-f295ea204c96 --- src/common/CSyntaxHighlighter.cpp | 26 ++++++- src/common/CSyntaxHighlighter.h | 7 +- src/common/fileloadthread.cpp | 27 +++++-- src/common/fileloadthread.h | 4 +- src/controls/warningnotices.cpp | 20 +++++- src/controls/warningnotices.h | 6 +- src/editor/editwrapper.cpp | 81 ++++++++++++++++++++- src/editor/editwrapper.h | 20 +++++- src/widgets/window.cpp | 107 +++++++++++++++++++++++++++- src/widgets/window.h | 3 +- translations/deepin-editor.ts | 40 +++++++++++ translations/deepin-editor_zh_CN.ts | 40 +++++++++++ translations/deepin-editor_zh_HK.ts | 42 ++++++++++- translations/deepin-editor_zh_TW.ts | 42 ++++++++++- 14 files changed, 444 insertions(+), 21 deletions(-) diff --git a/src/common/CSyntaxHighlighter.cpp b/src/common/CSyntaxHighlighter.cpp index d2e62ce86..ee7340a4f 100644 --- a/src/common/CSyntaxHighlighter.cpp +++ b/src/common/CSyntaxHighlighter.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2017-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -23,6 +23,16 @@ void CSyntaxHighlighter::setEnableHighlight(bool isEnable) m_bHighlight = isEnable; } +void CSyntaxHighlighter::setInvalidCharHighlight(bool enable) +{ + qDebug() << "CSyntaxHighlighter::setInvalidCharHighlight()" << enable; + m_bInvalidCharHighlight = enable; + if (enable) { + setEnableHighlight(true); + } + rehighlight(); +} + void CSyntaxHighlighter::highlightBlock(const QString &text) { if (!m_bHighlight) { @@ -30,4 +40,18 @@ void CSyntaxHighlighter::highlightBlock(const QString &text) } KSyntaxHighlighting::SyntaxHighlighter::highlightBlock(text); + + // 叠加 \00 无效字符高亮:红色背景 + 白色文字 + if (m_bInvalidCharHighlight) { + QTextCharFormat fmt; + fmt.setBackground(QColor("#FF0000")); + fmt.setForeground(QColor("#FFFFFF")); + // 匹配字面量 \00(反斜杠 + 两个零) + static const QRegularExpression re("\\\\00"); + QRegularExpressionMatchIterator it = re.globalMatch(text); + while (it.hasNext()) { + QRegularExpressionMatch match = it.next(); + setFormat(match.capturedStart(), match.capturedLength(), fmt); + } + } } diff --git a/src/common/CSyntaxHighlighter.h b/src/common/CSyntaxHighlighter.h index 4ff05468a..45c9c259c 100644 --- a/src/common/CSyntaxHighlighter.h +++ b/src/common/CSyntaxHighlighter.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2017-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include using namespace KSyntaxHighlighting; class CSyntaxHighlighter : public SyntaxHighlighter @@ -15,10 +18,12 @@ class CSyntaxHighlighter : public SyntaxHighlighter explicit CSyntaxHighlighter(QObject *parent = nullptr); explicit CSyntaxHighlighter(QTextDocument *pDocument); void setEnableHighlight(bool isEnable); + void setInvalidCharHighlight(bool enable); protected: virtual void highlightBlock(const QString & text) override; private: bool m_bHighlight = false; + bool m_bInvalidCharHighlight = false; }; diff --git a/src/common/fileloadthread.cpp b/src/common/fileloadthread.cpp index ed0b1902d..519cc8a87 100644 --- a/src/common/fileloadthread.cpp +++ b/src/common/fileloadthread.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2017-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -36,11 +36,20 @@ void FileLoadThread::run() // 发送文件头信息,用于预先加载数据 QString textEncode = QString::fromLocal8Bit(encode); - if (textEncode.contains("ASCII", Qt::CaseInsensitive) || textEncode.contains("UTF-8", Qt::CaseInsensitive)) { - emit sigPreProcess(encode, indata); + if (textEncode.contains("ASCII", Qt::CaseInsensitive) || textEncode.contains("UTF-8", Qt::CaseInsensitive)) { + if (indata.contains('\x00')) { + QByteArray headData = indata; + headData.replace('\x00', "\\00"); + emit sigPreProcess(encode, headData); + } else { + emit sigPreProcess(encode, indata); + } } else { QByteArray outHeadData; DetectCode::ChangeFileEncodingFormat(indata, outHeadData, textEncode, QString("UTF-8")); + if (outHeadData.contains('\x00')) { + outHeadData.replace('\x00', "\\00"); + } emit sigPreProcess(encode, outHeadData); } } @@ -54,10 +63,16 @@ void FileLoadThread::run() qWarning() << Q_FUNC_INFO << "Read file data error, " << QString(e.what()); file.close(); - emit sigLoadFinished(encode, indata, true); + emit sigLoadFinished(encode, indata, true, false); return; } + // NUL 字节检测与转义:将每个 \x00 替换为三个 ASCII 字符 \00 + bool hasNul = indata.contains('\x00'); + if (hasNul) { + indata.replace('\x00', "\\00"); + } + if (encode.isEmpty()) { //编码识别,如果文件数据大于1M,则只裁剪出1M文件数据去做编码探测 QByteArray dateUsedForCodeIdentify; @@ -71,11 +86,11 @@ void FileLoadThread::run() QString textEncode = QString::fromLocal8Bit(encode); if (textEncode.contains("ASCII", Qt::CaseInsensitive) || textEncode.contains("UTF-8", Qt::CaseInsensitive)) { - emit sigLoadFinished(encode, indata, false); + emit sigLoadFinished(encode, indata, false, hasNul); } else { QByteArray outData; DetectCode::ChangeFileEncodingFormat(indata, outData, textEncode, QString("UTF-8")); - emit sigLoadFinished(encode, outData, false); + emit sigLoadFinished(encode, outData, false, hasNul); } } diff --git a/src/common/fileloadthread.h b/src/common/fileloadthread.h index f6a0996ae..9dd5c86da 100644 --- a/src/common/fileloadthread.h +++ b/src/common/fileloadthread.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2017-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -19,7 +19,7 @@ class FileLoadThread : public QThread signals: // 预处理信号,优先处理文件头,防止出现加载时间过长的情况 void sigPreProcess(const QByteArray &encode, const QByteArray &content); - void sigLoadFinished(const QByteArray &encode, const QByteArray &content, bool error = false); + void sigLoadFinished(const QByteArray &encode, const QByteArray &content, bool error = false, bool hasNul = false); private: QString m_strFilePath; diff --git a/src/controls/warningnotices.cpp b/src/controls/warningnotices.cpp index ef9e4ab35..ea22db5df 100644 --- a/src/controls/warningnotices.cpp +++ b/src/controls/warningnotices.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2019-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -22,11 +22,15 @@ WarningNotices::WarningNotices(MessageType notifyType, QWidget *parent) setIcon(QIcon(":/images/warning.svg")); m_reloadBtn = new QPushButton(tr("Reload"), this); m_saveAsBtn = new QPushButton(qApp->translate("Window", "Save as"), this); + m_editAnywayBtn = new QPushButton(tr("Edit Anyway"), this); m_reloadBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_saveAsBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_editAnywayBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_editAnywayBtn->setVisible(false); connect(m_reloadBtn, &QPushButton::clicked, this, &WarningNotices::slotreloadBtnClicked); connect(m_saveAsBtn, &QPushButton::clicked, this, &WarningNotices::slotsaveAsBtnClicked); + connect(m_editAnywayBtn, &QPushButton::clicked, this, &WarningNotices::slotEditAnywayBtnClicked); #ifdef DTKWIDGET_CLASS_DSizeMode DDialogCloseButton *closeBtn = findChild(); @@ -83,3 +87,17 @@ void WarningNotices::slotsaveAsBtnClicked() this->hide(); emit saveAsBtnClicked(); } + +void WarningNotices::setEditAnywayBtn() +{ + m_reloadBtn->setVisible(false); + m_saveAsBtn->setVisible(false); + m_editAnywayBtn->setVisible(true); + setWidget(m_editAnywayBtn); +} + +void WarningNotices::slotEditAnywayBtnClicked() +{ + this->hide(); + emit editAnywayBtnClicked(); +} diff --git a/src/controls/warningnotices.h b/src/controls/warningnotices.h index 3ef6db0f5..4efc419e2 100644 --- a/src/controls/warningnotices.h +++ b/src/controls/warningnotices.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2019-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -23,19 +23,23 @@ class WarningNotices : public DFloatingMessage void setReloadBtn(); void setSaveAsBtn(); void clearBtn(); + void setEditAnywayBtn(); signals: void reloadBtnClicked(); void saveAsBtnClicked(); void closeBtnClicked(); + void editAnywayBtnClicked(); public slots: void slotreloadBtnClicked(); void slotsaveAsBtnClicked(); + void slotEditAnywayBtnClicked(); private: QPushButton *m_reloadBtn; QPushButton *m_saveAsBtn; + QPushButton *m_editAnywayBtn; QHBoxLayout *m_pLayout; }; diff --git a/src/editor/editwrapper.cpp b/src/editor/editwrapper.cpp index ea7cd6fc0..b8acff1d3 100644 --- a/src/editor/editwrapper.cpp +++ b/src/editor/editwrapper.cpp @@ -114,6 +114,7 @@ EditWrapper::EditWrapper(Window *window, QWidget *parent) connect(m_pTextEdit, &TextEdit::cursorModeChanged, this, &EditWrapper::handleCursorModeChanged); connect(m_pWaringNotices, &WarningNotices::reloadBtnClicked, this, &EditWrapper::reloadModifyFile); connect(m_pWaringNotices, &WarningNotices::saveAsBtnClicked, m_pWindow, &Window::saveAsFile); + connect(m_pWaringNotices, &WarningNotices::editAnywayBtnClicked, this, &EditWrapper::onEditAnyway); // NOTE: 文本高亮会触发重新布局,与界面布局(拖拽、放大窗口)变更时的布局操作冲突,因此调整更新顺序,在布局后刷新高亮 connect(m_pTextEdit->verticalScrollBar(), &QScrollBar::valueChanged, this, [this](int) { OnUpdateHighlighter(); @@ -507,6 +508,11 @@ QString EditWrapper::getTextEncode() */ bool EditWrapper::saveFile(QByteArray encode) { + // 预览模式下不允许直接静默保存,交由 Window 层确认弹窗 + if (m_bInvalidCharPreview) { + return false; + } + QString qstrFilePath = m_pTextEdit->getTruePath(); hideWarningNotices(); @@ -541,6 +547,47 @@ bool EditWrapper::saveFile(QByteArray encode) return ok; } +void EditWrapper::onEditAnyway() +{ + m_bInvalidCharEditAllowed = true; + m_pTextEdit->setReadOnly(false); + m_pWaringNotices->hide(); + // 保持 m_bInvalidCharPreview = true,直到 Save As 或 Save Anyway 成功 +} + +void EditWrapper::exitInvalidCharPreview() +{ + m_bInvalidCharPreview = false; + m_bInvalidCharEditAllowed = false; + m_sInvalidCharOriginalPath.clear(); + m_pTextEdit->setReadOnly(false); + m_pWaringNotices->hide(); + if (m_pSyntaxHighlighter) { + m_pSyntaxHighlighter->setInvalidCharHighlight(false); + } + updateModifyStatus(false); +} + +bool EditWrapper::forceSaveInvalidCharFile() +{ + QString savePath = m_sInvalidCharOriginalPath; + TextFileSaver saver(m_pTextEdit->document()); + if (BottomBar::EndlineFormat::Windows == m_pBottomBar->getEndlineFormat()) + saver.setWindowsEndlineFormat(true); + saver.setFilePath(savePath); + saver.setEncoding(m_sCurEncode.toUtf8()); + + bool ok = saver.save(); + if (ok) { + m_sFirstEncode = m_sCurEncode; + QFileInfo fi(savePath); + m_tModifiedDateTime = fi.lastModified(); + m_bIsTemFile = false; + exitInvalidCharPreview(); + } + return ok; +} + void EditWrapper::getPlainTextContent(QByteArray &plainTextContent) { QString strPlainText = m_pTextEdit->toPlainText(); @@ -823,7 +870,7 @@ void EditWrapper::handleFilePreProcess(const QByteArray &encode, const QByteArra * @param encode 文件编码 * @param content 完整文件内容 */ -void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteArray &content, bool error) +void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteArray &content, bool error, bool hasNul) { // 判断是否预加载,若已预加载,则无需重新初始化 if (!m_bHasPreProcess) { @@ -857,6 +904,31 @@ void EditWrapper::handleFileLoadFinished(const QByteArray &encode, const QByteAr m_pTextEdit->setReadOnly(true); } + // 无效字符(NUL)预览模式初始化 + if (hasNul) { + m_bInvalidCharPreview = true; + m_bInvalidCharEditAllowed = false; + m_sInvalidCharOriginalPath = m_pTextEdit->getTruePath(); + m_pTextEdit->setReadOnly(true); + m_pWaringNotices->setMessage(tr("The file contains invalid characters (NUL). Preview mode is read-only.")); + m_pWaringNotices->setEditAnywayBtn(); + m_pWaringNotices->show(); + DMessageManager::instance()->sendMessage(m_pTextEdit, m_pWaringNotices); + // 确保 CSyntaxHighlighter 存在(无语法定义的文件如 .txt 不会在 reinitOnFileLoad 中创建) + if (!m_pSyntaxHighlighter) { + m_pSyntaxHighlighter = new CSyntaxHighlighter(m_pTextEdit->document()); + QString themePath = Settings::instance()->settings->option("advance.editor.theme")->value().toString(); + if (themePath.contains("dark")) { + m_pSyntaxHighlighter->setTheme(m_Repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme)); + } else { + m_pSyntaxHighlighter->setTheme(m_Repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme)); + } + } + if (m_pSyntaxHighlighter) { + m_pSyntaxHighlighter->setInvalidCharHighlight(true); + } + } + if (m_bQuit) { return; } @@ -1023,7 +1095,12 @@ void EditWrapper::OnUpdateHighlighter() auto rehighlightBlock = [this](const QTextBlock &block) { m_pSyntaxHighlighter->setEnableHighlight(true); m_pSyntaxHighlighter->rehighlightBlock(block); - m_pSyntaxHighlighter->setEnableHighlight(false); + // NUL 预览模式下保持 m_bHighlight=true,使 loadContent 的 insertText 触发的 + // 延迟重排(contentsChange → _q_reformatBlocks)能正常重新应用 \00 高亮, + // 首屏立即可见无需滚动(与 master 行为一致);普通文件仍重置 false 保留性能优化。 + if (!m_bInvalidCharPreview) { + m_pSyntaxHighlighter->setEnableHighlight(false); + } }; if (foundBlock.isValid() && foundBlock < beginBlock) { diff --git a/src/editor/editwrapper.h b/src/editor/editwrapper.h index 18137200d..b0ba0e912 100644 --- a/src/editor/editwrapper.h +++ b/src/editor/editwrapper.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2017-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -119,6 +119,16 @@ class EditWrapper : public QWidget inline CSyntaxHighlighter *getSyntaxHighlighter() const { return m_pSyntaxHighlighter; } + // 无效字符预览模式访问器 + inline bool isInvalidCharPreview() const + { return m_bInvalidCharPreview; } + inline bool isInvalidCharEditAllowed() const + { return m_bInvalidCharEditAllowed; } + inline QString invalidCharOriginalPath() const + { return m_sInvalidCharOriginalPath; } + void exitInvalidCharPreview(); + bool forceSaveInvalidCharFile(); + signals: void sigClearDoubleCharaterEncode(); @@ -139,12 +149,13 @@ class EditWrapper : public QWidget public slots: // 处理文档预加载数据 void handleFilePreProcess(const QByteArray &encode, const QByteArray &content); - void handleFileLoadFinished(const QByteArray &encode, const QByteArray &content, bool error); + void handleFileLoadFinished(const QByteArray &encode, const QByteArray &content, bool error, bool hasNul = false); void OnThemeChangeSlot(QString theme); void UpdateBottomBarWordCnt(int cnt); void OnUpdateHighlighter(); //set the value of m_bIsTemFile void setTemFile(bool value); + void onEditAnyway(); private: //第一次打开文件编码 @@ -180,6 +191,11 @@ public slots: bool m_bAsyncReadFileFinished = false; bool m_bHasPreProcess = false; // 预处理标识 + + // 无效字符(NUL)预览模式状态 + bool m_bInvalidCharPreview = false; + bool m_bInvalidCharEditAllowed = false; + QString m_sInvalidCharOriginalPath; }; #endif diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 848246f7b..c6c3942da 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -862,6 +862,44 @@ bool Window::closeTab(const QString &filePath) if (m_tabbar->textAt(m_tabbar->currentIndex()).front() == "*") { isModified = true; } + + // 无效字符预览模式拦截:复用与 Ctrl+S 相同的三按钮确认弹窗 + if (wrapper->isInvalidCharPreview()) { + if (wrapper->isInvalidCharEditAllowed() && isModified) { + QString fileName = QFileInfo(filePath).fileName(); + int res = confirmInvalidCharSave(fileName); + switch (res) { + case 0: // Don't Save + removeWrapper(filePath, true); + m_tabbar->closeCurrentTab(filePath); + return true; + case 1: // Save As + { + QString newPath = saveAsFileToDisk(); + if (!newPath.isEmpty()) { + removeWrapper(newPath, true); + m_tabbar->closeCurrentTab(newPath); + return true; + } + return false; + } + case 2: // Save Anyway + if (wrapper->forceSaveInvalidCharFile()) { + removeWrapper(filePath, true); + m_tabbar->closeCurrentTab(filePath); + return true; + } + return false; + default: // 取消 + return false; + } + } else { + removeWrapper(filePath, true); + m_tabbar->closeCurrentTab(filePath); + return true; + } + } + if (isModified) { DDialog *dialog = createDialog(tr("Do you want to save this file?"), ""); int res = dialog->exec(); @@ -1127,6 +1165,23 @@ void Window::openFile() } } +int Window::confirmInvalidCharSave(const QString &fileName) +{ + DDialog *dialog = new DDialog( + tr("Invalid characters detected while saving \"%1\"").arg(fileName), + tr("If you force save this file, it may cause file corruption. Still want to save?"), + this); + dialog->setIcon(QIcon::fromTheme("dialog-warning")); + dialog->addButton(tr("Don't Save"), false, DDialog::ButtonNormal); + dialog->addButton(tr("Save As"), true, DDialog::ButtonRecommend); + dialog->addButton(tr("Save Anyway"), false, DDialog::ButtonWarning); + dialog->setCloseButtonVisible(false); + int res = dialog->exec(); + dialog->deleteLater(); + // 0=Don't Save, 1=Save As, 2=Save Anyway, -1=取消 + return res; +} + bool Window::saveFile() { EditWrapper *wrapperEdit = currentWrapper(); @@ -1134,6 +1189,30 @@ bool Window::saveFile() //大文本加载过程不允许保存 if (!wrapperEdit || wrapperEdit->getFileLoading()) return false; + // 无效字符预览模式拦截:弹出三按钮确认框 + if (wrapperEdit->isInvalidCharPreview()) { + if (!wrapperEdit->isInvalidCharEditAllowed()) { + return false; + } + QString filePath = wrapperEdit->textEditor()->getTruePath(); + QString fileName = QFileInfo(filePath).fileName(); + int res = confirmInvalidCharSave(fileName); + switch (res) { + case 0: // Don't Save + return false; + case 1: // Save As + return saveAsFile(); + case 2: // Save Anyway + if (wrapperEdit->forceSaveInvalidCharFile()) { + showNotify(tr("Saved successfully")); + return true; + } + return false; + default: // 取消 + return false; + } + } + bool isDraftFile = wrapperEdit->isDraftFile(); //bool isEmpty = wrapperEdit->isPlainTextEmpty(); QString filePath = wrapperEdit->textEditor()->getTruePath(); @@ -1243,6 +1322,18 @@ QString Window::saveAsFileToDisk() Settings::instance()->setSavePath(PathSettingWgt::LastOptBox, QFileInfo(newFilePath).absolutePath()); Settings::instance()->setSavePath(PathSettingWgt::CurFileBox, QFileInfo(newFilePath).absolutePath()); + // 预览模式下拒绝另存为原文件路径 + if (wrapper->isInvalidCharPreview()) { + QString originalPath = wrapper->invalidCharOriginalPath(); + if (QFileInfo(originalPath).absoluteFilePath() == QFileInfo(newFilePath).absoluteFilePath()) { + DMessageManager::instance()->sendMessage( + m_editorWidget->currentWidget(), + QIcon(":/images/warning.svg"), + tr("Cannot save as the original file in preview mode. Please choose a different path.")); + return QString(); + } + } + wrapper->updatePath(wrapper->filePath(), newFilePath); bool needChangeEncode = (encode != wrapper->getTextEncode().toUtf8()); @@ -1263,6 +1354,11 @@ QString Window::saveAsFileToDisk() // 更新文件编码 wrapper->bottomBar()->setEncodeName(encode); + // 预览模式 Save As 成功后退出预览模式 + if (wrapper->isInvalidCharPreview()) { + wrapper->exitInvalidCharPreview(); + } + // 若编码变更,保存完成后,重新加载文件 if (needChangeEncode) { wrapper->readFile(encode); @@ -2533,7 +2629,12 @@ void Window::backupFile() QJsonDocument document; jsonObject.insert("localPath", localPath); jsonObject.insert("cursorPosition", QString::number(wrapper->textEditor()->textCursor().position())); - jsonObject.insert("modify", wrapper->isModified()); + // 预览模式下不持久化修改状态 + if (wrapper->isInvalidCharPreview()) { + jsonObject.insert("modify", false); + } else { + jsonObject.insert("modify", wrapper->isModified()); + } jsonObject.insert("lastModifiedTime", wrapper->getLastModifiedTime().toString()); QList bookmarkList = wrapper->textEditor()->getBookmarkInfo(); if (!bookmarkList.isEmpty()) { @@ -2557,7 +2658,9 @@ void Window::backupFile() } //保存备份文件 - if (Utils::isDraftFile(filePath)) { + if (wrapper->isInvalidCharPreview()) { + // 预览模式跳过备份,不写 temFilePath,不保存临时文件 + } else if (Utils::isDraftFile(filePath)) { wrapper->saveTemFile(filePath); } else { if (wrapper->isModified()) { diff --git a/src/widgets/window.h b/src/widgets/window.h index 7fdbe6da5..e804e107e 100644 --- a/src/widgets/window.h +++ b/src/widgets/window.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2011-2023 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2011-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -194,6 +194,7 @@ public Q_SLOTS: int getBlankFileIndex(); DDialog *createDialog(const QString &title, const QString &content); + int confirmInvalidCharSave(const QString &fileName); void slotLoadContentTheme(DGuiApplicationHelper::ColorType themeType); void slotSettingResetTheme(const QString &path); diff --git a/translations/deepin-editor.ts b/translations/deepin-editor.ts index cef056303..56c885d4f 100644 --- a/translations/deepin-editor.ts +++ b/translations/deepin-editor.ts @@ -102,6 +102,11 @@ The file cannot be read, which may be too large or has been damaged! + + + The file contains invalid characters (NUL). Preview mode is read-only. + The file contains invalid characters (NUL). Preview mode is read-only. + FindBar @@ -1174,6 +1179,11 @@ Reload Reload + + + Edit Anyway + Edit Anyway + Window @@ -1305,5 +1315,35 @@ Discard Discard + + + Invalid characters detected while saving "%1" + Invalid characters detected while saving "%1" + + + + If you force save this file, it may cause file corruption. Still want to save? + If you force save this file, it may cause file corruption. Still want to save? + + + + Don't Save + Don't Save + + + + Save As + Save As + + + + Save Anyway + Save Anyway + + + + Cannot save as the original file in preview mode. Please choose a different path. + Cannot save as the original file in preview mode. Please choose a different path. + diff --git a/translations/deepin-editor_zh_CN.ts b/translations/deepin-editor_zh_CN.ts index 145e86203..51aa88ddc 100644 --- a/translations/deepin-editor_zh_CN.ts +++ b/translations/deepin-editor_zh_CN.ts @@ -100,6 +100,11 @@ The file cannot be read, which may be too large or has been damaged! 无法读取该文件,文件可能过大或损坏 + + + The file contains invalid characters (NUL). Preview mode is read-only. + 文件包含无效字符(NUL)。预览模式为只读。 + FindBar @@ -1194,6 +1199,11 @@ Reload 重新载入 + + + Edit Anyway + 仍然编辑 + Window @@ -1325,5 +1335,35 @@ Discard 不保存 + + + Invalid characters detected while saving "%1" + 保存"%1"时检测到无效字符 + + + + If you force save this file, it may cause file corruption. Still want to save? + 强制保存此文件可能导致文件损坏。仍然要保存吗? + + + + Don't Save + 不保存 + + + + Save As + 另存为 + + + + Save Anyway + 强制保存 + + + + Cannot save as the original file in preview mode. Please choose a different path. + 预览模式下不能另存为原文件。请选择其他路径。 + diff --git a/translations/deepin-editor_zh_HK.ts b/translations/deepin-editor_zh_HK.ts index 1b50b84c2..00c9b7740 100644 --- a/translations/deepin-editor_zh_HK.ts +++ b/translations/deepin-editor_zh_HK.ts @@ -100,6 +100,11 @@ The file cannot be read, which may be too large or has been damaged! 無法讀取該文件,文件可能過大或損壞 + + + The file contains invalid characters (NUL). Preview mode is read-only. + 文件包含無效字元(NUL)。預覽模式為唯讀。 + FindBar @@ -1172,6 +1177,11 @@ Reload 重新載入 + + + Edit Anyway + 仍然編輯 + Window @@ -1303,5 +1313,35 @@ Discard 不保存 + + + Invalid characters detected while saving "%1" + 保存「%1」時偵測到無效字元 + + + + If you force save this file, it may cause file corruption. Still want to save? + 強制保存此文件可能導致文件損壞。仍然要保存嗎? + + + + Don't Save + 不保存 + + + + Save As + 另存為 + + + + Save Anyway + 強制保存 + + + + Cannot save as the original file in preview mode. Please choose a different path. + 預覽模式下不能另存為原文件。請選擇其他路徑。 + - \ No newline at end of file + diff --git a/translations/deepin-editor_zh_TW.ts b/translations/deepin-editor_zh_TW.ts index 9ed2848d5..c178a0ebb 100644 --- a/translations/deepin-editor_zh_TW.ts +++ b/translations/deepin-editor_zh_TW.ts @@ -100,6 +100,11 @@ The file cannot be read, which may be too large or has been damaged! 無法讀取該文件,文件可能過大或損壞 + + + The file contains invalid characters (NUL). Preview mode is read-only. + 文件包含無效字元(NUL)。預覽模式為唯讀。 + FindBar @@ -1172,6 +1177,11 @@ Reload 重新載入 + + + Edit Anyway + 仍然編輯 + Window @@ -1303,5 +1313,35 @@ Discard 捨棄 + + + Invalid characters detected while saving "%1" + 儲存「%1」時偵測到無效字元 + + + + If you force save this file, it may cause file corruption. Still want to save? + 強制儲存此文件可能導致文件損壞。仍然要儲存嗎? + + + + Don't Save + 不儲存 + + + + Save As + 另存為 + + + + Save Anyway + 強制儲存 + + + + Cannot save as the original file in preview mode. Please choose a different path. + 預覽模式下不能另存為原文件。請選擇其他路徑。 + - \ No newline at end of file +