diff options
author | saturneric <[email protected]> | 2024-11-24 20:17:43 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2024-11-24 20:17:43 +0000 |
commit | b4e556d834a0c1e1cedbbda0a91cd892f9105d20 (patch) | |
tree | 6fe99ac300a8643daa7177b8f83820acb097f9aa | |
parent | fix: solve key list drag and drop refreshing issue (diff) | |
download | GpgFrontend-b4e556d834a0c1e1cedbbda0a91cd892f9105d20.tar.gz GpgFrontend-b4e556d834a0c1e1cedbbda0a91cd892f9105d20.zip |
feat: allow drag and drop to open text file
-rw-r--r-- | src/core/function/basic/ChannelObject.cpp | 2 | ||||
-rw-r--r-- | src/ui/main_window/MainWindow.cpp | 3 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowSlotFunction.cpp | 17 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.cpp | 3 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.h | 4 | ||||
-rw-r--r-- | src/ui/widgets/PlainTextEditorPage.cpp | 3 | ||||
-rw-r--r-- | src/ui/widgets/TextEdit.cpp | 204 | ||||
-rw-r--r-- | src/ui/widgets/TextEdit.h | 99 | ||||
-rw-r--r-- | src/ui/widgets/TextEditTabWidget.cpp | 242 | ||||
-rw-r--r-- | src/ui/widgets/TextEditTabWidget.h | 118 |
10 files changed, 474 insertions, 221 deletions
diff --git a/src/core/function/basic/ChannelObject.cpp b/src/core/function/basic/ChannelObject.cpp index 39a5568b..27999f5e 100644 --- a/src/core/function/basic/ChannelObject.cpp +++ b/src/core/function/basic/ChannelObject.cpp @@ -28,8 +28,6 @@ #include "ChannelObject.h" -#include <iostream> - namespace GpgFrontend { ChannelObject::ChannelObject() noexcept = default; diff --git a/src/ui/main_window/MainWindow.cpp b/src/ui/main_window/MainWindow.cpp index 8e5e1d03..597697f4 100644 --- a/src/ui/main_window/MainWindow.cpp +++ b/src/ui/main_window/MainWindow.cpp @@ -39,6 +39,7 @@ #include "ui/struct/settings_object/KeyServerSO.h" #include "ui/widgets/KeyList.h" #include "ui/widgets/TextEdit.h" +#include "ui/widgets/TextEditTabWidget.h" namespace GpgFrontend::UI { @@ -97,7 +98,7 @@ void MainWindow::Init() noexcept { UISignalStation::GetInstance(), &UISignalStation::SignalKeyDatabaseRefresh); - connect(edit_->tab_widget_, &QTabWidget::currentChanged, this, + connect(edit_->tab_widget_, &TextEditTabWidget::currentChanged, this, &MainWindow::slot_disable_tab_actions); connect(UISignalStation::GetInstance(), &UISignalStation::SignalRefreshStatusBar, this, diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index cea105b7..9a8e57d0 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -276,23 +276,6 @@ void MainWindow::upload_key_to_server() { } void MainWindow::SlotOpenFile(const QString& path) { - QFileInfo const info(path); - if (!info.isFile() || !info.isReadable()) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot open this file. Please make sure that this " - "is a regular file and it's readable.")); - return; - } - - if (info.size() > static_cast<qint64>(1024 * 1024)) { - QMessageBox::critical( - this, tr("Error"), - tr("Cannot open this file. The file is TOO LARGE (>1MB) for " - "GpgFrontend Text Editor.")); - return; - } - edit_->SlotOpenFile(path); } diff --git a/src/ui/widgets/InfoBoardWidget.cpp b/src/ui/widgets/InfoBoardWidget.cpp index 21a6c08a..92b22452 100644 --- a/src/ui/widgets/InfoBoardWidget.cpp +++ b/src/ui/widgets/InfoBoardWidget.cpp @@ -32,6 +32,7 @@ #include "core/model/SettingsObject.h" #include "ui/UISignalStation.h" #include "ui/struct/settings_object/AppearanceSO.h" +#include "ui/widgets/TextEditTabWidget.h" #include "ui_InfoBoard.h" namespace GpgFrontend::UI { @@ -103,7 +104,7 @@ void InfoBoardWidget::AssociateTextEdit(QTextEdit* edit) { connect(edit, &QTextEdit::textChanged, this, &InfoBoardWidget::SlotReset); } -void InfoBoardWidget::AssociateTabWidget(QTabWidget* tab) { +void InfoBoardWidget::AssociateTabWidget(TextEditTabWidget* tab) { m_text_page_ = nullptr; m_tab_widget_ = tab; connect(tab, &QTabWidget::tabBarClicked, this, &InfoBoardWidget::SlotReset); diff --git a/src/ui/widgets/InfoBoardWidget.h b/src/ui/widgets/InfoBoardWidget.h index b124cea2..1dfe7a52 100644 --- a/src/ui/widgets/InfoBoardWidget.h +++ b/src/ui/widgets/InfoBoardWidget.h @@ -46,6 +46,8 @@ typedef enum { INFO_ERROR_NEUTRAL = 3, } InfoBoardStatus; +class TextEditTabWidget; + /** * @brief Class for handling the verify label shown at bottom of a textedit-page */ @@ -72,7 +74,7 @@ class InfoBoardWidget : public QWidget { * * @param tab */ - void AssociateTabWidget(QTabWidget* tab); + void AssociateTabWidget(TextEditTabWidget* tab); /** * @brief diff --git a/src/ui/widgets/PlainTextEditorPage.cpp b/src/ui/widgets/PlainTextEditorPage.cpp index 6e92b5b6..bd1c9837 100644 --- a/src/ui/widgets/PlainTextEditorPage.cpp +++ b/src/ui/widgets/PlainTextEditorPage.cpp @@ -54,6 +54,9 @@ PlainTextEditorPage::PlainTextEditorPage(QString file_path, QWidget *parent) this->ui_->characterLabel->setText(tr("0 character")); this->ui_->lfLabel->setHidden(true); this->ui_->encodingLabel->setText("Unicode"); + this->ui_->textPage->setAcceptDrops(false); + + setAcceptDrops(false); connect(ui_->textPage, &QPlainTextEdit::textChanged, this, [=]() { // if file is loading diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index 4d83cff3..a742d49d 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -26,22 +26,23 @@ * */ -#include "ui/widgets/TextEdit.h" +#include "TextEdit.h" #include <QtPrintSupport> +#include <cstddef> #include <tuple> +#include <utility> #include <vector> #include "core/GpgModel.h" -#include "core/function/GlobalSettingStation.h" -#include "core/model/CacheObject.h" #include "ui/UISignalStation.h" +#include "ui/widgets/HelpPage.h" +#include "ui/widgets/TextEditTabWidget.h" namespace GpgFrontend::UI { TextEdit::TextEdit(QWidget* parent) : QWidget(parent) { - count_page_ = 0; - tab_widget_ = new QTabWidget(this); + tab_widget_ = new TextEditTabWidget(this); tab_widget_->setMovable(true); tab_widget_->setTabsClosable(true); tab_widget_->setDocumentMode(true); @@ -58,46 +59,13 @@ TextEdit::TextEdit(QWidget* parent) : QWidget(parent) { setAcceptDrops(false); } -void TextEdit::SlotNewTab() { - QString header = tr("untitled") + QString::number(++count_page_) + ".txt"; - - auto* page = new PlainTextEditorPage(); - auto index = tab_widget_->addTab(page, header); - tab_widget_->setTabIcon(index, QIcon(":/icons/file.png")); - tab_widget_->setCurrentIndex(tab_widget_->count() - 1); - page->GetTextPage()->setFocus(); - connect(page->GetTextPage()->document(), &QTextDocument::modificationChanged, - this, &TextEdit::SlotShowModified); - connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, - this, &TextEdit::slot_save_status_to_cache_for_revovery); -} +void TextEdit::SlotNewTab() { tab_widget_->SlotNewTab(); } void TextEdit::SlotNewTabWithContent(QString title, const QString& content) { - QString header = tr("untitled") + QString::number(++count_page_) + ".txt"; - if (!title.isEmpty()) { - // modify title - if (!title.isEmpty() && title[0] == '*') { - title.remove(0, 1); - } - // set title - header = title; - } - - auto* page = new PlainTextEditorPage(); - auto index = tab_widget_->addTab(page, header); - tab_widget_->setTabIcon(index, QIcon(":/icons/file.png")); - tab_widget_->setCurrentIndex(tab_widget_->count() - 1); - page->GetTextPage()->setFocus(); - connect(page->GetTextPage()->document(), &QTextDocument::modificationChanged, - this, &TextEdit::SlotShowModified); - connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, - this, &TextEdit::slot_save_status_to_cache_for_revovery); - - // set content with modified status - page->GetTextPage()->document()->setPlainText(content); + tab_widget_->SlotNewTabWithContent(std::move(title), content); } -void TextEdit::slotNewHelpTab(const QString& title, const QString& path) const { +void TextEdit::SlotNewHelpTab(const QString& title, const QString& path) const { auto* page = new HelpPage(path); tab_widget_->addTab(page, title); tab_widget_->setCurrentIndex(tab_widget_->count() - 1); @@ -119,31 +87,42 @@ void TextEdit::SlotNewFileTab() { } void TextEdit::SlotOpenFile(const QString& path) { - QFile file(path); - auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); - if (result) { - auto* page = new PlainTextEditorPage(path); - connect(page->GetTextPage()->document(), - &QTextDocument::modificationChanged, this, - &TextEdit::SlotShowModified); - // connect to cache recovery fucntion - connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, - this, &TextEdit::slot_save_status_to_cache_for_revovery); + QFileInfo const info(path); + if (!info.isFile() || !info.isReadable()) { + QMessageBox::critical( + this, tr("Error"), + tr("Cannot open this file. Please make sure that this " + "is a regular file and it's readable.")); + return; + } - QApplication::setOverrideCursor(Qt::WaitCursor); - auto index = tab_widget_->addTab(page, stripped_name(path)); - tab_widget_->setTabIcon(index, QIcon(":/icons/file.png")); - tab_widget_->setCurrentIndex(tab_widget_->count() - 1); - QApplication::restoreOverrideCursor(); - page->GetTextPage()->setFocus(); - page->ReadFile(); - } else { + if (info.size() > static_cast<qint64>(1024 * 1024)) { + QMessageBox::critical( + this, tr("Error"), + tr("Cannot open this file. The file is TOO LARGE (>1MB) for " + "GpgFrontend Text Editor.")); + return; + } + + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { QMessageBox::warning( - this, tr("Warning"), - tr("Cannot read file %1:\n%2.").arg(path).arg(file.errorString())); + this, tr("File Open Error"), + tr("The file \"%1\" could not be opened.").arg(info.fileName())); + return; } + QByteArray file_data = file.read(1024); file.close(); + + if (file_data.contains('\0')) { + QMessageBox::warning(this, tr("Binary File Detected"), + tr("The file \"%1\" appears to be a binary file " + "and will not be opened.") + .arg(info.fileName())); + return; + } + tab_widget_->SlotOpenFile(path); } void TextEdit::SlotOpen() { @@ -168,19 +147,17 @@ void TextEdit::SlotSave() { // docname.remove(0,2); SlotSaveAs(); } else { - save_file(file_name); + saveFile(file_name); } } -bool TextEdit::save_file(const QString& fileName) { - if (fileName.isEmpty()) { - return false; - } +auto TextEdit::saveFile(const QString& file_name) -> bool { + if (file_name.isEmpty()) return false; PlainTextEditorPage* page = SlotCurPageTextEdit(); if (page == nullptr) return false; - QFile file(fileName); + QFile file(file_name); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream output_stream(&file); QApplication::setOverrideCursor(Qt::WaitCursor); @@ -191,8 +168,8 @@ bool TextEdit::save_file(const QString& fileName) { document->setModified(false); int cur_index = tab_widget_->currentIndex(); - tab_widget_->setTabText(cur_index, stripped_name(fileName)); - page->SetFilePath(fileName); + tab_widget_->setTabText(cur_index, stripped_name(file_name)); + page->SetFilePath(file_name); page->NotifyFileSaved(); file.close(); @@ -200,7 +177,7 @@ bool TextEdit::save_file(const QString& fileName) { } QMessageBox::warning( this, tr("Warning"), - tr("Cannot read file %1:\n%2.").arg(fileName).arg(file.errorString())); + tr("Cannot read file %1:\n%2.").arg(file_name).arg(file.errorString())); return false; } @@ -217,7 +194,7 @@ auto TextEdit::SlotSaveAs() -> bool { path = tab_widget_->tabText(tab_widget_->currentIndex()).remove(0, 2); } - return save_file(QFileDialog::getSaveFileName(this, tr("Save file"), path)); + return saveFile(QFileDialog::getSaveFileName(this, tr("Save file"), path)); } void TextEdit::SlotCloseTab() { @@ -294,7 +271,7 @@ auto TextEdit::maybe_save_current_tab(bool askToSave) -> bool { // docname.remove(0,2); return SlotSaveAs(); } - return save_file(file_path); + return saveFile(file_path); } return result == QMessageBox::Discard; } @@ -354,32 +331,25 @@ auto TextEdit::MaybeSaveAnyTab() -> bool { return false; } -PlainTextEditorPage* TextEdit::CurTextPage() const { - return qobject_cast<PlainTextEditorPage*>(tab_widget_->currentWidget()); -} - void TextEdit::SlotAppendText2CurTextPage(const QString& text) { if (CurTextPage() == nullptr) SlotNewTab(); CurTextPage()->GetTextPage()->appendPlainText(text); } -FilePage* TextEdit::CurFilePage() const { - auto* cur_file_page = qobject_cast<FilePage*>(tab_widget_->currentWidget()); - if (cur_file_page != nullptr) { - return cur_file_page; - } - return nullptr; +auto TextEdit::CurTextPage() const -> PlainTextEditorPage* { + return tab_widget_->CurTextPage(); } -int TextEdit::TabCount() const { return tab_widget_->count(); } - -PlainTextEditorPage* TextEdit::SlotCurPageTextEdit() const { - auto* cur_page = - qobject_cast<PlainTextEditorPage*>(tab_widget_->currentWidget()); - return cur_page; +auto TextEdit::SlotCurPageTextEdit() const -> PlainTextEditorPage* { + return tab_widget_->SlotCurPageTextEdit(); } +auto TextEdit::CurFilePage() const -> FilePage* { + return tab_widget_->CurFilePage(); +} + +auto TextEdit::TabCount() const -> int { return tab_widget_->count(); } -FilePage* TextEdit::SlotCurPageFileTreeView() const { +auto TextEdit::SlotCurPageFileTreeView() const -> FilePage* { auto* cur_page = qobject_cast<FilePage*>(tab_widget_->currentWidget()); return cur_page; } @@ -432,7 +402,7 @@ void TextEdit::LoadFile(const QString& fileName) { // statusBar()->showMessage(tr("File loaded"), 2000); } -QString TextEdit::stripped_name(const QString& full_file_name) { +auto TextEdit::stripped_name(const QString& full_file_name) -> QString { return QFileInfo(full_file_name).fileName(); } @@ -476,7 +446,7 @@ void TextEdit::SlotShowModified(bool changed) const { // if doc is modified now, add leading * to title, // otherwise remove the leading * from the title if (CurTextPage()->GetTextPage()->document()->isModified()) { - tab_widget_->setTabText(index, title.prepend("* ")); + tab_widget_->setTabText(index, title.trimmed().prepend("* ")); } else { tab_widget_->setTabText(index, title.remove(0, 2)); } @@ -602,56 +572,4 @@ void TextEdit::slot_file_page_path_changed(const QString& path) const { emit UISignalStation::GetInstance() -> SignalMainWindowlUpdateBasicalOperaMenu(0); } - -void TextEdit::slot_save_status_to_cache_for_revovery() { - if (this->text_page_data_modified_count_++ % 8 != 0) return; - - auto settings = - GpgFrontend::GlobalSettingStation::GetInstance().GetSettings(); - - bool restore_text_editor_page = - settings.value("basic/restore_text_editor_page", false).toBool(); - if (!restore_text_editor_page) { - FLOG_D("restore_text_editor_page is false, ignoring..."); - return; - } - - int tab_count = tab_widget_->count(); - std::vector<std::tuple<int, QString, QString>> unsaved_pages; - - for (int i = 0; i < tab_count; i++) { - auto* target_page = - qobject_cast<PlainTextEditorPage*>(tab_widget_->widget(i)); - - // if this page is no textedit, there should be nothing to save - if (target_page == nullptr) { - continue; - } - - auto* document = target_page->GetTextPage()->document(); - auto tab_title = tab_widget_->tabText(i); - if (!target_page->ReadDone() || !target_page->isEnabled() || - !document->isModified()) { - continue; - } - - unsaved_pages.emplace_back(i, tab_title, document->toRawText()); - } - - CacheObject cache("editor_unsaved_pages"); - QJsonArray unsaved_page_array; - for (const auto& page : unsaved_pages) { - const auto [index, title, content] = page; - - QJsonObject page_json; - page_json["index"] = index; - page_json["title"] = title; - page_json["content"] = content; - - unsaved_page_array.push_back(page_json); - } - - cache.setArray(unsaved_page_array); -} - } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEdit.h b/src/ui/widgets/TextEdit.h index 50939d0e..4b893a3a 100644 --- a/src/ui/widgets/TextEdit.h +++ b/src/ui/widgets/TextEdit.h @@ -30,20 +30,19 @@ #include "ui/dialog/QuitDialog.h" #include "ui/widgets/FilePage.h" -#include "ui/widgets/HelpPage.h" #include "ui/widgets/PlainTextEditorPage.h" namespace GpgFrontend::UI { -/** - * @brief TextEdit class - */ + +class TextEditTabWidget; + class TextEdit : public QWidget { Q_OBJECT public: /** * @brief */ - TextEdit(QWidget* parent); + explicit TextEdit(QWidget* parent); /** * @details Load the content of file into the current textpage @@ -60,36 +59,37 @@ class TextEdit : public QWidget { * @return \li false, if the close event should be aborted. * \li true, otherwise */ - bool MaybeSaveAnyTab(); + auto MaybeSaveAnyTab() -> bool; /** * @brief * * @return int */ - [[nodiscard]] int TabCount() const; + [[nodiscard]] auto TabCount() const -> int; /** * @details textpage of the currently activated tab * @return \li reference to QTextEdit if tab has one * \li 0 otherwise (e.g. if helppage) */ - [[nodiscard]] PlainTextEditorPage* CurTextPage() const; + [[nodiscard]] auto CurTextPage() const -> PlainTextEditorPage*; /** * @brief * * @return FilePage* */ - [[nodiscard]] FilePage* CurFilePage() const; + [[nodiscard]] auto CurFilePage() const -> FilePage*; /** * @details List of currently unsaved tabs. * @returns QHash<int, QString> Hash of tab indexes and title of unsaved tabs. */ - [[nodiscard]] QHash<int, QString> UnsavedDocuments() const; + [[nodiscard]] auto UnsavedDocuments() const -> QHash<int, QString>; - QTabWidget* tab_widget_; /** Widget containing the tabs of the editor */ + TextEditTabWidget* + tab_widget_; /** Widget containing the tabs of the editor */ public slots: @@ -97,13 +97,13 @@ class TextEdit : public QWidget { * @details Return pointer to the currently activated text edit tab page. * */ - PlainTextEditorPage* SlotCurPageTextEdit() const; + [[nodiscard]] auto SlotCurPageTextEdit() const -> PlainTextEditorPage*; /** * @details Return pointer to the currently activated file tree view tab page. * */ - FilePage* SlotCurPageFileTreeView() const; + [[nodiscard]] auto SlotCurPageFileTreeView() const -> FilePage*; /** * @details Insert a ">" at the beginning of every line of current textedit. @@ -128,7 +128,7 @@ class TextEdit : public QWidget { * * @return Return the return value of the savefile method */ - bool SlotSaveAs(); + auto SlotSaveAs() -> bool; /** * @details Show an OpenFileDoalog and open the file in a new tab. @@ -166,7 +166,7 @@ class TextEdit : public QWidget { * @param title title for the tab * @param path path for html file to show */ - void slotNewHelpTab(const QString& title, const QString& path) const; + void SlotNewHelpTab(const QString& title, const QString& path) const; /** * New File Tab to do file operation @@ -197,46 +197,6 @@ class TextEdit : public QWidget { */ void SlotSwitchTabDown() const; - private: - uint text_page_data_modified_count_ = 0; ///< - - /** - * @details return just a filename stripped of a whole path - * - * @param a filename path - * @return QString containing the filename - */ - static QString stripped_name(const QString& full_file_name); - - /** - * @brief - * - * @param askToSave - */ - bool maybe_save_current_tab(bool askToSave); - - int count_page_; ///< int containing the number of added tabs - - private slots: - - void slot_file_page_path_changed(const QString& path) const; - - /** - * @details Remove the tab with given index - * - * @param index Tab-number to remove - */ - void slot_remove_tab(int index); - - /** - * @brief - * - * @param index - */ - void slot_save_status_to_cache_for_revovery(); - - public slots: - /** * @details Cut selected text in current text page. */ @@ -295,7 +255,34 @@ class TextEdit : public QWidget { * * @param fileName */ - bool save_file(const QString& fileName); + auto saveFile(const QString& file_name) -> bool; + + private: + /** + * @details return just a filename stripped of a whole path + * + * @param a filename path + * @return QString containing the filename + */ + static auto stripped_name(const QString& full_file_name) -> QString; + + /** + * @brief + * + * @param askToSave + */ + auto maybe_save_current_tab(bool askToSave) -> bool; + + private slots: + + void slot_file_page_path_changed(const QString& path) const; + + /** + * @details Remove the tab with given index + * + * @param index Tab-number to remove + */ + void slot_remove_tab(int index); }; } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEditTabWidget.cpp b/src/ui/widgets/TextEditTabWidget.cpp new file mode 100644 index 00000000..c782e868 --- /dev/null +++ b/src/ui/widgets/TextEditTabWidget.cpp @@ -0,0 +1,242 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend 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. + * + * GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "TextEditTabWidget.h" + +#include "core/function/GlobalSettingStation.h" +#include "core/model/CacheObject.h" +#include "ui/widgets/PlainTextEditorPage.h" +#include "widgets/FilePage.h" + +namespace GpgFrontend::UI { + +TextEditTabWidget::TextEditTabWidget(QWidget* parent) : QTabWidget(parent) { + setAcceptDrops(true); +} + +void TextEditTabWidget::dragEnterEvent(QDragEnterEvent* event) { + event->acceptProposedAction(); +} + +void TextEditTabWidget::dropEvent(QDropEvent* event) { + if (!event->mimeData()->hasUrls()) return; + + auto urls = event->mimeData()->urls(); + + for (const auto& url : urls) { + QString local_file = url.toLocalFile(); + + QFileInfo file_info(local_file); + if (file_info.size() > static_cast<qint64>(1024 * 1024)) { + QMessageBox::warning( + this, tr("File Too Large"), + tr("The file \"%1\" is larger than 1MB and will not be opened.") + .arg(file_info.fileName())); + continue; + } + + QFile file(local_file); + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::warning( + this, tr("File Open Error"), + tr("The file \"%1\" could not be opened.").arg(file_info.fileName())); + continue; + } + QByteArray file_data = file.read(1024); + file.close(); + + if (file_data.contains('\0')) { + QMessageBox::warning(this, tr("Binary File Detected"), + tr("The file \"%1\" appears to be a binary file " + "and will not be opened.") + .arg(file_info.fileName())); + continue; + } + + SlotOpenFile(local_file); + } + + event->acceptProposedAction(); +} + +void TextEditTabWidget::SlotOpenFile(const QString& path) { + QFile file(path); + auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); + if (result) { + auto* page = new PlainTextEditorPage(path); + connect(page->GetTextPage()->document(), + &QTextDocument::modificationChanged, this, + &TextEditTabWidget::SlotShowModified); + // connect to cache recovery fucntion + connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, + this, &TextEditTabWidget::slot_save_status_to_cache_for_recovery); + + QApplication::setOverrideCursor(Qt::WaitCursor); + auto index = this->addTab(page, stripped_name(path)); + this->setTabIcon(index, QIcon(":/icons/file.png")); + this->setCurrentIndex(this->count() - 1); + QApplication::restoreOverrideCursor(); + page->GetTextPage()->setFocus(); + page->ReadFile(); + } else { + QMessageBox::warning( + this, tr("Warning"), + tr("Cannot read file %1:\n%2.").arg(path).arg(file.errorString())); + } + + file.close(); +} +void TextEditTabWidget::SlotShowModified(bool changed) { + // get current tab + int index = this->currentIndex(); + QString title = this->tabText(index); + + // if changed + if (!changed) { + this->setTabText(index, title.remove(0, 2)); + return; + } + + // if doc is modified now, add leading * to title, + // otherwise remove the leading * from the title + if (CurTextPage()->GetTextPage()->document()->isModified()) { + this->setTabText(index, title.trimmed().prepend("* ")); + } else { + this->setTabText(index, title.remove(0, 2)); + } +} +auto TextEditTabWidget::CurTextPage() const -> PlainTextEditorPage* { + return qobject_cast<PlainTextEditorPage*>(this->currentWidget()); +} + +auto TextEditTabWidget::SlotCurPageTextEdit() -> PlainTextEditorPage* { + auto* cur_page = qobject_cast<PlainTextEditorPage*>(this->currentWidget()); + return cur_page; +} + +auto TextEditTabWidget::CurFilePage() const -> FilePage* { + auto* cur_file_page = qobject_cast<FilePage*>(this->currentWidget()); + if (cur_file_page != nullptr) { + return cur_file_page; + } + return nullptr; +} + +auto TextEditTabWidget::stripped_name(const QString& full_file_name) + -> QString { + return QFileInfo(full_file_name).fileName(); +} + +void TextEditTabWidget::slot_save_status_to_cache_for_recovery() { + if (this->text_page_data_modified_count_++ % 8 != 0) return; + + auto settings = GlobalSettingStation::GetInstance().GetSettings(); + + bool restore_text_editor_page = + settings.value("basic/restore_text_editor_page", false).toBool(); + if (!restore_text_editor_page) { + FLOG_D("restore_text_editor_page is false, ignoring..."); + return; + } + + int tab_count = this->count(); + std::vector<std::tuple<int, QString, QString>> unsaved_pages; + + for (int i = 0; i < tab_count; i++) { + auto* target_page = qobject_cast<PlainTextEditorPage*>(this->widget(i)); + + // if this page is no textedit, there should be nothing to save + if (target_page == nullptr) { + continue; + } + + auto* document = target_page->GetTextPage()->document(); + auto tab_title = this->tabText(i); + if (!target_page->ReadDone() || !target_page->isEnabled() || + !document->isModified()) { + continue; + } + + unsaved_pages.emplace_back(i, tab_title, document->toRawText()); + } + + CacheObject cache("editor_unsaved_pages"); + QJsonArray unsaved_page_array; + for (const auto& page : unsaved_pages) { + const auto [index, title, content] = page; + + QJsonObject page_json; + page_json["index"] = index; + page_json["title"] = title; + page_json["content"] = content; + + unsaved_page_array.push_back(page_json); + } + + cache.setArray(unsaved_page_array); +} + +void TextEditTabWidget::SlotNewTab() { + QString header = tr("untitled") + QString::number(++count_page_) + ".txt"; + + auto* page = new PlainTextEditorPage(); + auto index = this->addTab(page, header); + this->setTabIcon(index, QIcon(":/icons/file.png")); + this->setCurrentIndex(this->count() - 1); + page->GetTextPage()->setFocus(); + connect(page->GetTextPage()->document(), &QTextDocument::modificationChanged, + this, &TextEditTabWidget::SlotShowModified); + connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, + this, &TextEditTabWidget::slot_save_status_to_cache_for_recovery); +} +void TextEditTabWidget::SlotNewTabWithContent(QString title, + const QString& content) { + QString header = tr("untitled") + QString::number(++count_page_) + ".txt"; + if (!title.isEmpty()) { + // modify title + if (!title.isEmpty() && title[0] == '*') { + title.remove(0, 1); + } + // set title + header = title; + } + + auto* page = new PlainTextEditorPage(); + auto index = this->addTab(page, header); + this->setTabIcon(index, QIcon(":/icons/file.png")); + this->setCurrentIndex(this->count() - 1); + page->GetTextPage()->setFocus(); + connect(page->GetTextPage()->document(), &QTextDocument::modificationChanged, + this, &TextEditTabWidget::SlotShowModified); + connect(page->GetTextPage()->document(), &QTextDocument::contentsChanged, + this, &TextEditTabWidget::slot_save_status_to_cache_for_recovery); + + // set content with modified status + page->GetTextPage()->document()->setPlainText(content); +} +} // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEditTabWidget.h b/src/ui/widgets/TextEditTabWidget.h new file mode 100644 index 00000000..7c83a721 --- /dev/null +++ b/src/ui/widgets/TextEditTabWidget.h @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend 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. + * + * GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include "widgets/FilePage.h" +namespace GpgFrontend::UI { + +class PlainTextEditorPage; + +class TextEditTabWidget : public QTabWidget { + Q_OBJECT + public: + explicit TextEditTabWidget(QWidget* parent = nullptr); + + public slots: + + /** + * @brief + * + */ + void SlotNewTab(); + + /** + * @brief + * + * @param title + * @param content + */ + void SlotNewTabWithContent(QString title, const QString& content); + + /** + * @details Adds a new tab with opening file by path + */ + void SlotOpenFile(const QString& path); + + /** + * @details put a * in front of current tabs title, if current textedit is + * modified + */ + void SlotShowModified(bool); + + /** + * @brief + * + * @return PlainTextEditorPage* + */ + [[nodiscard]] auto CurTextPage() const -> PlainTextEditorPage*; + + /** + * @brief + * + * @return PlainTextEditorPage* + */ + auto SlotCurPageTextEdit() -> PlainTextEditorPage*; + + /** + * @brief + * + * @return FilePage* + */ + [[nodiscard]] auto CurFilePage() const -> FilePage*; + + protected: + /** + * @brief + * + * @param event + */ + void dragEnterEvent(QDragEnterEvent* event) override; + + /** + * @brief + * + * @param event + */ + void dropEvent(QDropEvent* event) override; + + private slots: + /** + * @brief + * + */ + void slot_save_status_to_cache_for_recovery(); + + private: + int count_page_ = 0; + int text_page_data_modified_count_ = 0; + + static auto stripped_name(const QString& full_file_name) -> QString; +}; + +} // namespace GpgFrontend::UI
\ No newline at end of file |