/** * Copyright (C) 2021 Saturneric * * 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 . * * The initial version of the source code is inherited from * the gpg4usb project, which is under GPL-3.0-or-later. * * The source code version of this software was modified and released * by Saturneric starting on May 12, 2021. * */ #include "ui/widgets/PlainTextEditorPage.h" #include #include #include #include "ui/encoding/TextEncodingDetect.h" #include "ui/thread/FileReadThread.h" #include "ui_PlainTextEditor.h" namespace GpgFrontend::UI { PlainTextEditorPage::PlainTextEditorPage(QString filePath, QWidget* parent) : QWidget(parent), ui_(std::make_shared()), full_file_path_(std::move(filePath)) { ui_->setupUi(this); if (full_file_path_.isEmpty()) read_done_ = true; ui_->textPage->setFocus(); ui_->loadingLabel->setHidden(true); // Front in same width this->setFont({"Courier"}); this->setAttribute(Qt::WA_DeleteOnClose); this->ui_->characterLabel->setText(_("0 character")); this->ui_->lfLabel->setText(_("None")); this->ui_->encodingLabel->setText(_("Binary")); connect(ui_->textPage, &QPlainTextEdit::textChanged, this, [=]() { if (!read_done_) return; auto text = ui_->textPage->document()->toPlainText(); auto str = boost::format(_("%1% character(s)")) % text.size(); this->ui_->characterLabel->setText(str.str().c_str()); detect_cr_lf(text); detect_encoding(text.toStdString()); }); ui_->loadingLabel->setText(_("Loading...")); } const QString& PlainTextEditorPage::GetFilePath() const { return full_file_path_; } QPlainTextEdit* PlainTextEditorPage::GetTextPage() { return ui_->textPage; } void PlainTextEditorPage::SetFilePath(const QString& filePath) { full_file_path_ = filePath; } void PlainTextEditorPage::ShowNotificationWidget(QWidget* widget, const char* className) { widget->setProperty(className, true); ui_->verticalLayout->addWidget(widget); } void PlainTextEditorPage::CloseNoteByClass(const char* className) { QList widgets = findChildren(); for (QWidget* widget : widgets) { if (widget->property(className) == true) { widget->close(); } } } void PlainTextEditorPage::slot_format_gpg_header() { QString content = ui_->textPage->toPlainText(); // Get positions of the gpg-headers, if they exist int start = content.indexOf(GpgFrontend::GpgConstants::PGP_SIGNED_BEGIN); int startSig = content.indexOf(GpgFrontend::GpgConstants::PGP_SIGNATURE_BEGIN); int endSig = content.indexOf(GpgFrontend::GpgConstants::PGP_SIGNATURE_END); if (start < 0 || startSig < 0 || endSig < 0 || sign_marked_) { return; } sign_marked_ = true; // Set the fontstyle for the header QTextCharFormat signFormat; signFormat.setForeground(QBrush(QColor::fromRgb(80, 80, 80))); signFormat.setFontPointSize(9); // set font style for the signature QTextCursor cursor(ui_->textPage->document()); cursor.setPosition(startSig, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, endSig); cursor.setCharFormat(signFormat); // set the font style for the header int headEnd = content.indexOf("\n\n", start); cursor.setPosition(start, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, headEnd); cursor.setCharFormat(signFormat); } void PlainTextEditorPage::ReadFile() { LOG(INFO) << "called"; read_done_ = false; read_bytes_ = 0; ui_->textPage->setEnabled(false); ui_->textPage->setReadOnly(true); ui_->textPage->blockSignals(true); ui_->loadingLabel->setHidden(false); ui_->textPage->document()->blockSignals(true); auto text_page = this->GetTextPage(); text_page->setReadOnly(true); auto thread = new FileReadThread(this->full_file_path_.toStdString()); connect(thread, &FileReadThread::SignalSendReadBlock, this, &PlainTextEditorPage::slot_insert_text); connect(thread, &FileReadThread::SignalReadDone, this, [=]() { LOG(INFO) << "thread read done"; if (!binary_mode_) { text_page->setReadOnly(false); } }); connect(thread, &FileReadThread::finished, this, [=]() { LOG(INFO) << "thread finished"; thread->deleteLater(); read_done_ = true; read_thread_ = nullptr; ui_->textPage->setEnabled(true); text_page->document()->setModified(false); ui_->textPage->blockSignals(false); ui_->textPage->document()->blockSignals(false); ui_->loadingLabel->setHidden(true); }); connect(this, &PlainTextEditorPage::destroyed, [=]() { LOG(INFO) << "request interruption for read thread"; thread->requestInterruption(); read_thread_ = nullptr; }); this->read_thread_ = thread; thread->start(); } std::string binary_to_string(const std::string& source) { static char syms[] = "0123456789ABCDEF"; std::stringstream ss; for (unsigned char c : source) ss << syms[((c >> 4) & 0xf)] << syms[c & 0xf] << " "; return ss.str(); } void PlainTextEditorPage::slot_insert_text(const std::string& data) { LOG(INFO) << "data size" << data.size(); read_bytes_ += data.size(); // If binary format is detected, the entire file is converted to binary format // for display bool if_last_binary_mode = binary_mode_; if (!binary_mode_) { detect_encoding(data); } if (binary_mode_) { if (if_last_binary_mode != binary_mode_) { auto text_buffer = ui_->textPage->document()->toRawText().toLocal8Bit().toStdString(); ui_->textPage->clear(); this->GetTextPage()->insertPlainText( binary_to_string(text_buffer).c_str()); this->ui_->lfLabel->setText("None"); } this->GetTextPage()->insertPlainText(binary_to_string(data).c_str()); auto str = boost::format(_("%1% byte(s)")) % read_bytes_; this->ui_->characterLabel->setText(str.str().c_str()); } else { this->GetTextPage()->insertPlainText(data.c_str()); auto text = this->GetTextPage()->toPlainText(); auto str = boost::format(_("%1% character(s)")) % text.size(); this->ui_->characterLabel->setText(str.str().c_str()); detect_cr_lf(text); } } void PlainTextEditorPage::PrepareToDestroy() { if (read_thread_) { read_thread_->requestInterruption(); read_thread_ = nullptr; } } void PlainTextEditorPage::detect_encoding(const std::string& data) { AutoIt::Common::TextEncodingDetect text_detect; AutoIt::Common::TextEncodingDetect::Encoding encoding = text_detect.DetectEncoding((unsigned char*)(data.data()), data.size()); if (encoding == AutoIt::Common::TextEncodingDetect::None) { binary_mode_ = true; ui_->encodingLabel->setText(_("Binary")); } else if (encoding == AutoIt::Common::TextEncodingDetect::ASCII) { ui_->encodingLabel->setText(_("ASCII(7 bits)")); } else if (encoding == AutoIt::Common::TextEncodingDetect::ANSI) { ui_->encodingLabel->setText(_("ASCII(8 bits)")); } else if (encoding == AutoIt::Common::TextEncodingDetect::UTF8_BOM || encoding == AutoIt::Common::TextEncodingDetect::UTF8_NOBOM) { ui_->encodingLabel->setText(_("UTF-8")); } else if (encoding == AutoIt::Common::TextEncodingDetect::UTF16_LE_BOM || encoding == AutoIt::Common::TextEncodingDetect::UTF16_LE_NOBOM) { ui_->encodingLabel->setText(_("UTF-16")); } else if (encoding == AutoIt::Common::TextEncodingDetect::UTF16_BE_BOM || encoding == AutoIt::Common::TextEncodingDetect::UTF16_BE_NOBOM) { ui_->encodingLabel->setText(_("UTF-16(BE)")); } } void PlainTextEditorPage::detect_cr_lf(const QString& data) { if (binary_mode_) { this->ui_->lfLabel->setText("None"); return; } if (data.contains("\r\n")) { this->ui_->lfLabel->setText("CRLF"); } else { this->ui_->lfLabel->setText("LF"); } } } // namespace GpgFrontend::UI