diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/ui/FindWidget.cpp | 52 | ||||
-rw-r--r-- | src/ui/FindWidget.h | 8 | ||||
-rw-r--r-- | src/ui/details/VerifyDetailsDialog.h | 2 | ||||
-rw-r--r-- | src/ui/encoding/TextEncodingDetect.cpp | 313 | ||||
-rw-r--r-- | src/ui/encoding/TextEncodingDetect.h | 85 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowSlotFunction.cpp | 33 | ||||
-rw-r--r-- | src/ui/main_window/MainWindowSlotUI.cpp | 8 | ||||
-rw-r--r-- | src/ui/thread/FileReadThread.cpp | 24 | ||||
-rw-r--r-- | src/ui/thread/FileReadThread.h | 2 | ||||
-rw-r--r-- | src/ui/widgets/EditorPage.cpp | 154 | ||||
-rw-r--r-- | src/ui/widgets/InfoBoardWidget.h | 2 | ||||
-rw-r--r-- | src/ui/widgets/PlainTextEditorPage.cpp | 257 | ||||
-rw-r--r-- | src/ui/widgets/PlainTextEditorPage.h (renamed from src/ui/widgets/EditorPage.h) | 27 | ||||
-rw-r--r-- | src/ui/widgets/TextEdit.cpp | 60 | ||||
-rw-r--r-- | src/ui/widgets/TextEdit.h | 6 |
16 files changed, 775 insertions, 259 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 59cc99bf..32edb262 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -10,6 +10,7 @@ aux_source_directory(./settings UI_SOURCE) aux_source_directory(./thread UI_SOURCE) aux_source_directory(./details UI_SOURCE) aux_source_directory(./data_struct UI_SOURCE) +aux_source_directory(./encoding UI_SOURCE) if (SMTP_SUPPORT) aux_source_directory(./smtp UI_SOURCE) diff --git a/src/ui/FindWidget.cpp b/src/ui/FindWidget.cpp index 6326b119..b95859a1 100644 --- a/src/ui/FindWidget.cpp +++ b/src/ui/FindWidget.cpp @@ -26,8 +26,8 @@ namespace GpgFrontend::UI { -FindWidget::FindWidget(QWidget* parent, QTextEdit* edit) : QWidget(parent) { - mTextpage = edit; +FindWidget::FindWidget(QWidget* parent, PlainTextEditorPage* edit) + : QWidget(parent), mTextpage(edit) { findEdit = new QLineEdit(this); auto* closeButton = new QPushButton( this->style()->standardIcon(QStyle::SP_TitleBarCloseButton), QString(), @@ -55,12 +55,13 @@ FindWidget::FindWidget(QWidget* parent, QTextEdit* edit) : QWidget(parent) { } void FindWidget::setBackground() { - QTextCursor cursor = mTextpage->textCursor(); + auto cursor = mTextpage->getTextPage()->textCursor(); // if match is found set background of QLineEdit to white, otherwise to red QPalette bgPalette(findEdit->palette()); if (!findEdit->text().isEmpty() && - mTextpage->document()->find(findEdit->text()).position() < 0) { + mTextpage->getTextPage()->document()->find(findEdit->text()).position() < + 0) { bgPalette.setColor(QPalette::Base, "#ececba"); } else { bgPalette.setColor(QPalette::Base, Qt::white); @@ -69,45 +70,45 @@ void FindWidget::setBackground() { } void FindWidget::slotFindNext() { - QTextCursor cursor = mTextpage->textCursor(); - cursor = mTextpage->document()->find(findEdit->text(), cursor, - QTextDocument::FindCaseSensitively); + QTextCursor cursor = mTextpage->getTextPage()->textCursor(); + cursor = mTextpage->getTextPage()->document()->find( + findEdit->text(), cursor, QTextDocument::FindCaseSensitively); // if end of document is reached, restart search from beginning if (cursor.position() == -1) { - cursor = mTextpage->document()->find(findEdit->text(), cursor, - QTextDocument::FindCaseSensitively); + cursor = mTextpage->getTextPage()->document()->find( + findEdit->text(), cursor, QTextDocument::FindCaseSensitively); } // cursor should not stay at -1, otherwise text is not editable // todo: check how gedit handles this if (cursor.position() != -1) { - mTextpage->setTextCursor(cursor); + mTextpage->getTextPage()->setTextCursor(cursor); } this->setBackground(); } void FindWidget::slotFind() { - QTextCursor cursor = mTextpage->textCursor(); + QTextCursor cursor = mTextpage->getTextPage()->textCursor(); if (cursor.anchor() == -1) { - cursor = mTextpage->document()->find(findEdit->text(), cursor, - QTextDocument::FindCaseSensitively); + cursor = mTextpage->getTextPage()->document()->find( + findEdit->text(), cursor, QTextDocument::FindCaseSensitively); } else { - cursor = mTextpage->document()->find(findEdit->text(), cursor.anchor(), - QTextDocument::FindCaseSensitively); + cursor = mTextpage->getTextPage()->document()->find( + findEdit->text(), cursor.anchor(), QTextDocument::FindCaseSensitively); } // if end of document is reached, restart search from beginning if (cursor.position() == -1) { - cursor = mTextpage->document()->find(findEdit->text(), cursor, - QTextDocument::FindCaseSensitively); + cursor = mTextpage->getTextPage()->document()->find( + findEdit->text(), cursor, QTextDocument::FindCaseSensitively); } // cursor should not stay at -1, otherwise text is not editable // todo: check how gedit handles this if (cursor.position() != -1) { - mTextpage->setTextCursor(cursor); + mTextpage->getTextPage()->setTextCursor(cursor); } this->setBackground(); } @@ -117,19 +118,20 @@ void FindWidget::slotFindPrevious() { flags |= QTextDocument::FindBackward; flags |= QTextDocument::FindCaseSensitively; - QTextCursor cursor = mTextpage->textCursor(); - cursor = mTextpage->document()->find(findEdit->text(), cursor, flags); + QTextCursor cursor = mTextpage->getTextPage()->textCursor(); + cursor = mTextpage->getTextPage()->document()->find(findEdit->text(), cursor, + flags); // if begin of document is reached, restart search from end if (cursor.position() == -1) { - cursor = - mTextpage->document()->find(findEdit->text(), QTextCursor::End, flags); + cursor = mTextpage->getTextPage()->document()->find( + findEdit->text(), QTextCursor::End, flags); } // cursor should not stay at -1, otherwise text is not editable // todo: check how gedit handles this if (cursor.position() != -1) { - mTextpage->setTextCursor(cursor); + mTextpage->getTextPage()->setTextCursor(cursor); } this->setBackground(); } @@ -150,11 +152,11 @@ void FindWidget::keyPressEvent(QKeyEvent* e) { } void FindWidget::slotClose() { - QTextCursor cursor = mTextpage->textCursor(); + QTextCursor cursor = mTextpage->getTextPage()->textCursor(); if (cursor.position() == -1) { cursor.setPosition(0); - mTextpage->setTextCursor(cursor); + mTextpage->getTextPage()->setTextCursor(cursor); } mTextpage->setFocus(); close(); diff --git a/src/ui/FindWidget.h b/src/ui/FindWidget.h index e4cbdaab..bc412012 100644 --- a/src/ui/FindWidget.h +++ b/src/ui/FindWidget.h @@ -26,7 +26,7 @@ #define FINDWIDGET_H #include "ui/GpgFrontendUI.h" -#include "ui/widgets/EditorPage.h" +#include "ui/widgets/PlainTextEditorPage.h" namespace GpgFrontend::UI { @@ -42,7 +42,7 @@ class FindWidget : public QWidget { * * @param parent The parent widget */ - explicit FindWidget(QWidget* parent, QTextEdit* edit); + explicit FindWidget(QWidget* parent, PlainTextEditorPage* edit); private: void keyPressEvent(QKeyEvent* e) override; @@ -53,8 +53,8 @@ class FindWidget : public QWidget { */ void setBackground(); - QTextEdit* mTextpage; /** Textedit associated to the notification */ - QLineEdit* findEdit; /** Label holding the text shown in infoBoard */ + PlainTextEditorPage* mTextpage; /** Textedit associated to the notification */ + QLineEdit* findEdit; /** Label holding the text shown in infoBoard */ private slots: diff --git a/src/ui/details/VerifyDetailsDialog.h b/src/ui/details/VerifyDetailsDialog.h index 5de648f6..21affdb4 100644 --- a/src/ui/details/VerifyDetailsDialog.h +++ b/src/ui/details/VerifyDetailsDialog.h @@ -26,7 +26,7 @@ #define __VERIFYDETAILSDIALOG_H__ #include "ui/GpgFrontendUI.h" -#include "ui/widgets/EditorPage.h" +#include "ui/widgets/PlainTextEditorPage.h" #include "ui/widgets/VerifyKeyDetailBox.h" namespace GpgFrontend::UI { diff --git a/src/ui/encoding/TextEncodingDetect.cpp b/src/ui/encoding/TextEncodingDetect.cpp new file mode 100644 index 00000000..22ae5897 --- /dev/null +++ b/src/ui/encoding/TextEncodingDetect.cpp @@ -0,0 +1,313 @@ +// +// Copyright 2015-2016 Jonathan Bennett <[email protected]> +// +// https://www.autoitscript.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Includes +#include "TextEncodingDetect.h" + +using namespace AutoIt::Common; + +static const unsigned char TextEncodingDetect_UTF16_BOM_LE[] = { + (unsigned char)(0xFF), (unsigned char)(0xFE)}; +static const unsigned char TextEncodingDetect_UTF16_BOM_BE[] = { + (unsigned char)(0xFE), (unsigned char)(0xFF)}; +static const unsigned char TextEncodingDetect_UTF8_BOM[] = { + (unsigned char)(0xEF), (unsigned char)(0xBB), (unsigned char)(0xBF)}; + +const unsigned char *TextEncodingDetect::utf16_bom_le_ = + TextEncodingDetect_UTF16_BOM_LE; +const unsigned char *TextEncodingDetect::utf16_bom_be_ = + TextEncodingDetect_UTF16_BOM_BE; +const unsigned char *TextEncodingDetect::utf8_bom_ = + TextEncodingDetect_UTF8_BOM; + +/////////////////////////////////////////////////////////////////////////////// +// Constructor() +// Default constructor +/////////////////////////////////////////////////////////////////////////////// + +TextEncodingDetect::TextEncodingDetect() { + // By default, assume nulls can't appear in ANSI/ASCII/UTF8 text files + null_suggests_binary_ = true; + + // Set defaults for utf16 detection based the use of odd/even nulls + utf16_expected_null_percent_ = 70; + utf16_unexpected_null_percent_ = 10; +} + +/////////////////////////////////////////////////////////////////////////////// +// Set the percentages used in utf16 detection using nulls. +/////////////////////////////////////////////////////////////////////////////// + +void TextEncodingDetect::SetUtf16UnexpectedNullPercent(int percent) { + if (percent > 0 && percent < 100) utf16_expected_null_percent_ = percent; +} + +void TextEncodingDetect::SetUtf16ExpectedNullPercent(int percent) { + if (percent > 0 && percent < 100) utf16_unexpected_null_percent_ = percent; +} + +/////////////////////////////////////////////////////////////////////////////// +// Simple function to return the length of the BOM for a particular encoding +// mode. +/////////////////////////////////////////////////////////////////////////////// + +int TextEncodingDetect::GetBOMLengthFromEncodingMode(Encoding encoding) { + int length = 0; + + if (encoding == UTF16_BE_BOM || encoding == UTF16_LE_BOM) + length = 2; + else if (encoding == UTF8_BOM) + length = 3; + + return length; +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks if a buffer contains a valid BOM and returns the encoding based on it. +// Returns encoding "None" if there is no BOM. +/////////////////////////////////////////////////////////////////////////////// + +TextEncodingDetect::Encoding TextEncodingDetect::CheckBOM( + const unsigned char *pBuffer, size_t size) { + // Check for BOM + if (size >= 2 && pBuffer[0] == utf16_bom_le_[0] && + pBuffer[1] == utf16_bom_le_[1]) { + return UTF16_LE_BOM; + } else if (size >= 2 && pBuffer[0] == utf16_bom_be_[0] && + pBuffer[1] == utf16_bom_be_[1]) { + return UTF16_BE_BOM; + } else if (size >= 3 && pBuffer[0] == utf8_bom_[0] && + pBuffer[1] == utf8_bom_[1] && pBuffer[2] == utf8_bom_[2]) { + return UTF8_BOM; + } else { + return None; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks if a buffer contains a valid BOM and returns the encoding based on it. +// If it doesn't contain a BOM it tries to guess what the encoding is or +// "None" if it just looks like binary data. +/////////////////////////////////////////////////////////////////////////////// + +TextEncodingDetect::Encoding TextEncodingDetect::DetectEncoding( + const unsigned char *pBuffer, size_t size) const { + // First check if we have a BOM and return that if so + Encoding encoding = CheckBOM(pBuffer, size); + if (encoding != None) return encoding; + + // Now check for valid UTF8 + encoding = CheckUTF8(pBuffer, size); + if (encoding != None) return encoding; + + // Now try UTF16 + encoding = CheckUTF16NewlineChars(pBuffer, size); + if (encoding != None) return encoding; + + encoding = CheckUTF16ASCII(pBuffer, size); + if (encoding != None) return encoding; + + // ANSI or None (binary) then + if (!DoesContainNulls(pBuffer, size)) + return ANSI; + else { + // Found a null, return based on the preference in null_suggests_binary_ + if (null_suggests_binary_) + return None; + else + return ANSI; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks if a buffer contains valid utf8. Returns: +// None - not valid utf8 +// UTF8_NOBOM - valid utf8 encodings and multibyte sequences +// ASCII - Only data in the 0-127 range. +/////////////////////////////////////////////////////////////////////////////// + +TextEncodingDetect::Encoding TextEncodingDetect::CheckUTF8( + const unsigned char *pBuffer, size_t size) const { + // UTF8 Valid sequences + // 0xxxxxxx ASCII + // 110xxxxx 10xxxxxx 2-byte + // 1110xxxx 10xxxxxx 10xxxxxx 3-byte + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4-byte + // + // Width in UTF8 + // Decimal Width + // 0-127 1 byte + // 194-223 2 bytes + // 224-239 3 bytes + // 240-244 4 bytes + // + // Subsequent chars are in the range 128-191 + + bool only_saw_ascii_range = true; + size_t pos = 0; + int more_chars; + + while (pos < size) { + unsigned char ch = pBuffer[pos++]; + + if (ch == 0 && null_suggests_binary_) { + return None; + } else if (ch <= 127) { + // 1 byte + more_chars = 0; + } else if (ch >= 194 && ch <= 223) { + // 2 Byte + more_chars = 1; + } else if (ch >= 224 && ch <= 239) { + // 3 Byte + more_chars = 2; + } else if (ch >= 240 && ch <= 244) { + // 4 Byte + more_chars = 3; + } else { + return None; // Not utf8 + } + + // Check secondary chars are in range if we are expecting any + while (more_chars && pos < size) { + only_saw_ascii_range = false; // Seen non-ascii chars now + + ch = pBuffer[pos++]; + if (ch < 128 || ch > 191) return None; // Not utf8 + + --more_chars; + } + } + + // If we get to here then only valid UTF-8 sequences have been processed + + // If we only saw chars in the range 0-127 then we can't assume UTF8 (the + // caller will need to decide) + if (only_saw_ascii_range) + return ASCII; + else + return UTF8_NOBOM; +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks if a buffer contains text that looks like utf16 by scanning for +// newline chars that would be present even in non-english text. +// Returns: +// None - not valid utf16 +// UTF16_LE_NOBOM - looks like utf16 le +// UTF16_BE_NOBOM - looks like utf16 be +/////////////////////////////////////////////////////////////////////////////// + +TextEncodingDetect::Encoding TextEncodingDetect::CheckUTF16NewlineChars( + const unsigned char *pBuffer, size_t size) { + if (size < 2) return None; + + // Reduce size by 1 so we don't need to worry about bounds checking for pairs + // of bytes + size--; + + int le_control_chars = 0; + int be_control_chars = 0; + unsigned char ch1, ch2; + + size_t pos = 0; + while (pos < size) { + ch1 = pBuffer[pos++]; + ch2 = pBuffer[pos++]; + + if (ch1 == 0) { + if (ch2 == 0x0a || ch2 == 0x0d) ++be_control_chars; + } else if (ch2 == 0) { + if (ch1 == 0x0a || ch1 == 0x0d) ++le_control_chars; + } + + // If we are getting both LE and BE control chars then this file is not + // utf16 + if (le_control_chars && be_control_chars) return None; + } + + if (le_control_chars) + return UTF16_LE_NOBOM; + else if (be_control_chars) + return UTF16_BE_NOBOM; + else + return None; +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks if a buffer contains text that looks like utf16. This is done based +// the use of nulls which in ASCII/script like text can be useful to identify. +// Returns: +// None - not valid utf16 +// UTF16_LE_NOBOM - looks like utf16 le +// UTF16_BE_NOBOM - looks like utf16 be +/////////////////////////////////////////////////////////////////////////////// + +TextEncodingDetect::Encoding TextEncodingDetect::CheckUTF16ASCII( + const unsigned char *pBuffer, size_t size) const { + int num_odd_nulls = 0; + int num_even_nulls = 0; + + // Get even nulls + size_t pos = 0; + while (pos < size) { + if (pBuffer[pos] == 0) num_even_nulls++; + + pos += 2; + } + + // Get odd nulls + pos = 1; + while (pos < size) { + if (pBuffer[pos] == 0) num_odd_nulls++; + + pos += 2; + } + + double even_null_threshold = (num_even_nulls * 2.0) / size; + double odd_null_threshold = (num_odd_nulls * 2.0) / size; + double expected_null_threshold = utf16_expected_null_percent_ / 100.0; + double unexpected_null_threshold = utf16_unexpected_null_percent_ / 100.0; + + // Lots of odd nulls, low number of even nulls + if (even_null_threshold < unexpected_null_threshold && + odd_null_threshold > expected_null_threshold) + return UTF16_LE_NOBOM; + + // Lots of even nulls, low number of odd nulls + if (odd_null_threshold < unexpected_null_threshold && + even_null_threshold > expected_null_threshold) + return UTF16_BE_NOBOM; + + // Don't know + return None; +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks if a buffer contains any nulls. Used to check for binary vs text data. +/////////////////////////////////////////////////////////////////////////////// + +bool TextEncodingDetect::DoesContainNulls(const unsigned char *pBuffer, + size_t size) { + size_t pos = 0; + while (pos < size) { + if (pBuffer[pos++] == 0) return true; + } + + return false; +} diff --git a/src/ui/encoding/TextEncodingDetect.h b/src/ui/encoding/TextEncodingDetect.h new file mode 100644 index 00000000..6d861716 --- /dev/null +++ b/src/ui/encoding/TextEncodingDetect.h @@ -0,0 +1,85 @@ +#pragma once +#ifndef TEXT_ENCODING_DETECT_H_ +#define TEXT_ENCODING_DETECT_H_ + +// +// Copyright 2015 Jonathan Bennett <[email protected]> +// +// https://www.autoitscript.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// Includes +#include <stddef.h> + +namespace AutoIt::Common { +class TextEncodingDetect { + public: + enum Encoding { + None, // Unknown or binary + ANSI, // 0-255 + ASCII, // 0-127 + UTF8_BOM, // UTF8 with BOM + UTF8_NOBOM, // UTF8 without BOM + UTF16_LE_BOM, // UTF16 LE with BOM + UTF16_LE_NOBOM, // UTF16 LE without BOM + UTF16_BE_BOM, // UTF16-BE with BOM + UTF16_BE_NOBOM, // UTF16-BE without BOM + }; + + TextEncodingDetect(); + ~TextEncodingDetect() = default; + + static Encoding CheckBOM( + const unsigned char *pBuffer, + size_t size); // Just check if there is a BOM and return + Encoding DetectEncoding(const unsigned char *pBuffer, size_t size) + const; // Check BOM and also guess if there is no BOM + static int GetBOMLengthFromEncodingMode( + Encoding encoding); // Just return the BOM length of a given mode + + void SetNullSuggestsBinary(bool null_suggests_binary) { + null_suggests_binary_ = null_suggests_binary; + } + void SetUtf16UnexpectedNullPercent(int percent); + void SetUtf16ExpectedNullPercent(int percent); + + private: + TextEncodingDetect(const TextEncodingDetect &); + const TextEncodingDetect &operator=(const TextEncodingDetect &); + + static const unsigned char *utf16_bom_le_; + static const unsigned char *utf16_bom_be_; + static const unsigned char *utf8_bom_; + + bool null_suggests_binary_; + int utf16_expected_null_percent_; + int utf16_unexpected_null_percent_; + + Encoding CheckUTF8(const unsigned char *pBuffer, + size_t size) const; // Check for valid UTF8 with no BOM + static Encoding CheckUTF16NewlineChars( + const unsigned char *pBuffer, + size_t size); // Check for valid UTF16 with no BOM via control chars + Encoding CheckUTF16ASCII(const unsigned char *pBuffer, size_t size) + const; // Check for valid UTF16 with no BOM via null distribution + static bool DoesContainNulls(const unsigned char *pBuffer, + size_t size); // Check for nulls +}; + +} // namespace AutoIt::Common + +////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/ui/main_window/MainWindowSlotFunction.cpp b/src/ui/main_window/MainWindowSlotFunction.cpp index d2ad177a..593fe6fe 100644 --- a/src/ui/main_window/MainWindowSlotFunction.cpp +++ b/src/ui/main_window/MainWindowSlotFunction.cpp @@ -69,7 +69,8 @@ void MainWindow::slotEncrypt() { process_operation(this, _("Symmetrically Encrypting"), [&]() { try { - auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + auto buffer = + edit->curTextPage()->getTextPage()->toPlainText().toStdString(); error = GpgFrontend::BasicOperator::GetInstance().EncryptSymmetric( buffer, tmp, result); } catch (const std::runtime_error& e) { @@ -94,7 +95,8 @@ void MainWindow::slotEncrypt() { process_operation(this, _("Encrypting"), [&]() { try { - auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + auto buffer = + edit->curTextPage()->getTextPage()->toPlainText().toStdString(); error = GpgFrontend::BasicOperator::GetInstance().Encrypt( std::move(keys), buffer, tmp, result); } catch (const std::runtime_error& e) { @@ -114,7 +116,8 @@ void MainWindow::slotEncrypt() { infoBoard->resetOptionActionsMenu(); #ifdef SMTP_SUPPORT if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) - send_an_email(this, infoBoard, edit->curTextPage()->toPlainText()); + send_an_email(this, infoBoard, + edit->curTextPage()->getTextPage()->toPlainText()); #endif } else { QMessageBox::critical(this, _("Error"), @@ -157,7 +160,11 @@ void MainWindow::slotSign() { process_operation(this, _("Signing"), [&]() { try { - auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + auto buffer = edit->curTextPage() + ->getTextPage() + ->toPlainText() + .toUtf8() + .toStdString(); error = GpgFrontend::BasicOperator::GetInstance().Sign( std::move(keys), buffer, tmp, GPGME_SIG_MODE_CLEAR, result); } catch (const std::runtime_error& e) { @@ -183,7 +190,7 @@ void MainWindow::slotDecrypt() { if (edit->tabCount() == 0 || edit->slotCurPageTextEdit() == nullptr) return; auto decrypted = std::make_unique<ByteArray>(); - QByteArray text = edit->curTextPage()->toPlainText().toUtf8(); + QByteArray text = edit->curTextPage()->getTextPage()->toPlainText().toUtf8(); if (text.trimmed().startsWith(GpgConstants::GPG_FRONTEND_SHORT_CRYPTO_HEAD)) { QMessageBox::critical( @@ -234,7 +241,7 @@ void MainWindow::slotFind() { void MainWindow::slotVerify() { if (edit->tabCount() == 0 || edit->slotCurPageTextEdit() == nullptr) return; - auto text = edit->curTextPage()->toPlainText().toUtf8(); + auto text = edit->curTextPage()->getTextPage()->toPlainText().toUtf8(); // TODO(Saturneric) PreventNoDataErr auto sig_buffer = std::make_unique<ByteArray>(); @@ -317,7 +324,11 @@ void MainWindow::slotEncryptSign() { auto tmp = std::make_unique<ByteArray>(); process_operation(this, _("Encrypting and Signing"), [&]() { try { - auto buffer = edit->curTextPage()->toPlainText().toUtf8().toStdString(); + auto buffer = edit->curTextPage() + ->getTextPage() + ->toPlainText() + .toUtf8() + .toStdString(); error = GpgFrontend::BasicOperator::GetInstance().EncryptSign( std::move(keys), std::move(signer_keys), buffer, tmp, encr_result, sign_result); @@ -352,7 +363,8 @@ void MainWindow::slotEncryptSign() { infoBoard->resetOptionActionsMenu(); #ifdef SMTP_SUPPORT if (check_gpg_error_2_err_code(error) == GPG_ERR_NO_ERROR) - send_an_email(this, infoBoard, edit->curTextPage()->toPlainText(), false); + send_an_email(this, infoBoard, + edit->curTextPage()->getTextPage()->toPlainText(), false); #endif #ifdef ADVANCE_SUPPORT @@ -376,7 +388,7 @@ void MainWindow::slotEncryptSign() { void MainWindow::slotDecryptVerify() { if (edit->tabCount() == 0 || edit->slotCurPageTextEdit() == nullptr) return; - QString plainText = edit->curTextPage()->toPlainText(); + QString plainText = edit->curTextPage()->getTextPage()->toPlainText(); #ifdef ADVANCE_SUPPORT if (plainText.trimmed().startsWith( @@ -453,7 +465,8 @@ void MainWindow::slotAppendSelectedKeys() { auto key_ids = mKeyList->getSelected(); GpgKeyImportExporter::GetInstance().ExportKeys(key_ids, exported); - edit->curTextPage()->append(QString::fromStdString(*exported)); + edit->curTextPage()->getTextPage()->appendPlainText( + QString::fromStdString(*exported)); } void MainWindow::slotCopyMailAddressToClipboard() { diff --git a/src/ui/main_window/MainWindowSlotUI.cpp b/src/ui/main_window/MainWindowSlotUI.cpp index 0c0204f4..9f40ec9d 100644 --- a/src/ui/main_window/MainWindowSlotUI.cpp +++ b/src/ui/main_window/MainWindowSlotUI.cpp @@ -41,7 +41,7 @@ void MainWindow::slotStartWizard() { void MainWindow::slotImportKeyFromEdit() { if (edit->tabCount() == 0 || edit->slotCurPageTextEdit() == nullptr) return; CommonUtils::GetInstance()->slotImportKeys( - this, edit->curTextPage()->toPlainText().toStdString()); + this, edit->curTextPage()->getTextPage()->toPlainText().toStdString()); } void MainWindow::slotOpenKeyManagement() { @@ -139,7 +139,7 @@ void MainWindow::slotCleanDoubleLinebreaks() { return; } - QString content = edit->curTextPage()->toPlainText(); + QString content = edit->curTextPage()->getTextPage()->toPlainText(); content.replace("\n\n", "\n"); edit->slotFillTextEditWithText(content); } @@ -149,7 +149,7 @@ void MainWindow::slotAddPgpHeader() { return; } - QString content = edit->curTextPage()->toPlainText().trimmed(); + QString content = edit->curTextPage()->getTextPage()->toPlainText().trimmed(); content.prepend("\n\n").prepend(GpgConstants::PGP_CRYPT_BEGIN); content.append("\n").append(GpgConstants::PGP_CRYPT_END); @@ -162,7 +162,7 @@ void MainWindow::slotCutPgpHeader() { return; } - QString content = edit->curTextPage()->toPlainText(); + QString content = edit->curTextPage()->getTextPage()->toPlainText(); int start = content.indexOf(GpgConstants::PGP_CRYPT_BEGIN); int end = content.indexOf(GpgConstants::PGP_CRYPT_END); diff --git a/src/ui/thread/FileReadThread.cpp b/src/ui/thread/FileReadThread.cpp index 270f50e7..04f713bd 100644 --- a/src/ui/thread/FileReadThread.cpp +++ b/src/ui/thread/FileReadThread.cpp @@ -29,39 +29,41 @@ namespace GpgFrontend::UI { -FileReadThread::FileReadThread(std::string path) : path(std::move(path)) {} +FileReadThread::FileReadThread(std::string path) : path(std::move(path)) { + qRegisterMetaType<std::string>("std::string"); +} void FileReadThread::run() { - LOG(INFO) << "Started"; + LOG(INFO) << "started"; boost::filesystem::path read_file_path(this->path); if (is_regular_file(read_file_path)) { - LOG(INFO) << "Read Open"; + LOG(INFO) << "read open"; - auto fp = fopen(read_file_path.string().c_str(), "r"); + auto fp = fopen(read_file_path.string().c_str(), "rb"); size_t read_size; - LOG(INFO) << "Thread Start Reading"; + LOG(INFO) << "thread start reading"; - char buffer[8192]; + char buffer[4096]; while ((read_size = fread(buffer, sizeof(char), sizeof buffer, fp)) > 0) { // Check isInterruptionRequested if (QThread::currentThread()->isInterruptionRequested()) { - LOG(INFO) << "Read Thread isInterruptionRequested "; + LOG(INFO) << "thread is interruption requested "; fclose(fp); return; } - LOG(INFO) << "Read Thread Read block size " << read_size; + LOG(INFO) << "block size " << read_size; std::string buffer_str(buffer, read_size); - emit sendReadBlock(QString::fromStdString(buffer_str)); + emit sendReadBlock(buffer_str); #ifdef RELEASE QThread::msleep(32); #else - QThread::msleep(48); + QThread::msleep(128); #endif } fclose(fp); emit readDone(); - LOG(INFO) << "Thread End Reading"; + LOG(INFO) << "thread end reading"; } } diff --git a/src/ui/thread/FileReadThread.h b/src/ui/thread/FileReadThread.h index 46ed6cbc..65982848 100644 --- a/src/ui/thread/FileReadThread.h +++ b/src/ui/thread/FileReadThread.h @@ -37,7 +37,7 @@ class FileReadThread : public QThread { signals: - void sendReadBlock(const QString& block); + void sendReadBlock(const std::string& block); void readDone(); diff --git a/src/ui/widgets/EditorPage.cpp b/src/ui/widgets/EditorPage.cpp deleted file mode 100644 index 8c26fe71..00000000 --- a/src/ui/widgets/EditorPage.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/** - * 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. - * - * Foobar 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 Foobar. If not, see <https://www.gnu.org/licenses/>. - * - * The initial version of the source code is inherited from gpg4usb-team. - * Their source code version also complies with GNU General Public License. - * - * The source code version of this software was modified and released - * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021. - * - */ - -#include "ui/widgets/EditorPage.h" - -#include <boost/filesystem.hpp> -#include <utility> - -#include "ui/thread/FileReadThread.h" - -namespace GpgFrontend::UI { - -EditorPage::EditorPage(QString filePath, QWidget* parent) - : QWidget(parent), full_file_path_(std::move(filePath)) { - // Set the Textedit properties - textPage = new QTextEdit(); - textPage->setAcceptRichText(false); - - // Set the layout style - mainLayout = new QVBoxLayout(); - mainLayout->setSpacing(0); - mainLayout->addWidget(textPage); - mainLayout->setContentsMargins(0, 0, 0, 0); - setLayout(mainLayout); - - textPage->setFocus(); - - // Front in same width - this->setFont({"Courier"}); - this->setAttribute(Qt::WA_DeleteOnClose); -} - -const QString& EditorPage::getFilePath() const { return full_file_path_; } - -QTextEdit* EditorPage::getTextPage() { return textPage; } - -void EditorPage::setFilePath(const QString& filePath) { - full_file_path_ = filePath; -} - -void EditorPage::showNotificationWidget(QWidget* widget, - const char* className) { - widget->setProperty(className, true); - mainLayout->addWidget(widget); -} - -void EditorPage::closeNoteByClass(const char* className) { - QList<QWidget*> widgets = findChildren<QWidget*>(); - for (QWidget* widget : widgets) { - if (widget->property(className) == true) { - widget->close(); - } - } -} - -void EditorPage::slotFormatGpgHeader() { - QString content = 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 || signMarked) { - return; - } - - signMarked = 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(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 EditorPage::ReadFile() { - LOG(INFO) << "Called"; - - read_done_ = false; - - auto text_page = this->getTextPage(); - text_page->setReadOnly(true); - auto thread = new FileReadThread(this->full_file_path_.toStdString()); - - connect(thread, &FileReadThread::sendReadBlock, this, - &EditorPage::slotInsertText); - - connect(thread, &FileReadThread::readDone, this, [=]() { - LOG(INFO) << "thread read done"; - text_page->document()->setModified(false); - text_page->setReadOnly(false); - }); - - connect(thread, &FileReadThread::finished, this, [=]() { - LOG(INFO) << "thread finished"; - thread->deleteLater(); - read_done_ = true; - read_hread_ = nullptr; - }); - - connect(this, &EditorPage::destroyed, [=]() { - LOG(INFO) << "request interruption for read thread"; - thread->requestInterruption(); - read_hread_ = nullptr; - }); - this->read_hread_ = thread; - thread->start(); -} - -void EditorPage::slotInsertText(const QString& text) { - this->getTextPage()->insertPlainText(text); -} -void EditorPage::PrepareToDestroy() { - if (read_hread_) { - read_hread_->requestInterruption(); - read_hread_ = nullptr; - } -} - -} // namespace GpgFrontend::UI diff --git a/src/ui/widgets/InfoBoardWidget.h b/src/ui/widgets/InfoBoardWidget.h index 8d37be6c..816da849 100644 --- a/src/ui/widgets/InfoBoardWidget.h +++ b/src/ui/widgets/InfoBoardWidget.h @@ -25,7 +25,7 @@ #ifndef __VERIFYNOTIFICATION_H__ #define __VERIFYNOTIFICATION_H__ -#include "EditorPage.h" +#include "PlainTextEditorPage.h" #include "gpg/result_analyse/VerifyResultAnalyse.h" #include "ui/details/VerifyDetailsDialog.h" diff --git a/src/ui/widgets/PlainTextEditorPage.cpp b/src/ui/widgets/PlainTextEditorPage.cpp new file mode 100644 index 00000000..1a99c2a6 --- /dev/null +++ b/src/ui/widgets/PlainTextEditorPage.cpp @@ -0,0 +1,257 @@ +/** + * 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. + * + * Foobar 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 Foobar. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from gpg4usb-team. + * Their source code version also complies with GNU General Public License. + * + * The source code version of this software was modified and released + * by Saturneric<[email protected]><[email protected]> starting on May 12, 2021. + * + */ + +#include "ui/widgets/PlainTextEditorPage.h" + +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <utility> + +#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<Ui_PlainTextEditor>()), + 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_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<QWidget*> widgets = findChildren<QWidget*>(); + for (QWidget* widget : widgets) { + if (widget->property(className) == true) { + widget->close(); + } + } +} + +void PlainTextEditorPage::slotFormatGpgHeader() { + 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 || signMarked) { + return; + } + + signMarked = 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::sendReadBlock, this, + &PlainTextEditorPage::slotInsertText); + + connect(thread, &FileReadThread::readDone, 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::slotInsertText(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 diff --git a/src/ui/widgets/EditorPage.h b/src/ui/widgets/PlainTextEditorPage.h index d1bc1ac2..24823c06 100644 --- a/src/ui/widgets/EditorPage.h +++ b/src/ui/widgets/PlainTextEditorPage.h @@ -28,12 +28,7 @@ #include "gpg/GpgConstants.h" #include "ui/GpgFrontendUI.h" -QT_BEGIN_NAMESPACE -class QVBoxLayout; -class QHBoxLayout; -class QString; -class QLabel; -QT_END_NAMESPACE +class Ui_PlainTextEditor; namespace GpgFrontend::UI { @@ -41,7 +36,7 @@ namespace GpgFrontend::UI { * @brief Class for handling a single tab of the tabwidget * */ -class EditorPage : public QWidget { +class PlainTextEditorPage : public QWidget { Q_OBJECT public: /** @@ -50,7 +45,8 @@ class EditorPage : public QWidget { * @param filePath Path of the file handled in this tab * @param parent Pointer to the parent widget */ - explicit EditorPage(QString filePath = "", QWidget* parent = nullptr); + explicit PlainTextEditorPage(QString filePath = "", + QWidget* parent = nullptr); /** * @details Get the filepath of the currently activated tab. @@ -67,7 +63,7 @@ class EditorPage : public QWidget { /** * @details Return pointer tp the textedit of the currently activated tab. */ - QTextEdit* getTextPage(); + QPlainTextEdit* getTextPage(); /** * @details Show additional widget at buttom of currently active tab @@ -91,12 +87,17 @@ class EditorPage : public QWidget { void PrepareToDestroy(); private: - QTextEdit* textPage; /** The textedit of the tab */ - QVBoxLayout* mainLayout; /** The layout for the tab */ + std::shared_ptr<Ui_PlainTextEditor> ui; QString full_file_path_; /** The path to the file handled in the tab */ bool signMarked{}; /** true, if the signed header is marked, false if not */ bool read_done_ = false; - QThread* read_hread_ = nullptr; + QThread* read_thread_ = nullptr; + bool binary_mode_ = false; + size_t read_bytes_ = 0; + + void detect_encoding(const std::string& data); + + void detect_cr_lf(const QString& data); private slots: @@ -105,7 +106,7 @@ class EditorPage : public QWidget { */ void slotFormatGpgHeader(); - void slotInsertText(const QString& text); + void slotInsertText(const std::string& data); }; } // namespace GpgFrontend::UI diff --git a/src/ui/widgets/TextEdit.cpp b/src/ui/widgets/TextEdit.cpp index be6ec181..a8ff8d73 100644 --- a/src/ui/widgets/TextEdit.cpp +++ b/src/ui/widgets/TextEdit.cpp @@ -50,7 +50,7 @@ TextEdit::TextEdit(QWidget* parent) : QWidget(parent) { void TextEdit::slotNewTab() { QString header = _("untitled") + QString::number(++countPage) + ".txt"; - auto* page = new EditorPage(); + auto* page = new PlainTextEditorPage(); auto index = tabWidget->addTab(page, header); tabWidget->setTabIcon(index, QIcon(":file.png")); tabWidget->setCurrentIndex(tabWidget->count() - 1); @@ -80,11 +80,11 @@ void TextEdit::slotOpenFile(QString& path) { LOG(INFO) << "path" << path.toStdString(); auto result = file.open(QIODevice::ReadOnly | QIODevice::Text); if (result) { - auto* page = new EditorPage(path); + auto* page = new PlainTextEditorPage(path); connect(page->getTextPage()->document(), &QTextDocument::modificationChanged, this, &TextEdit::slotShowModified); - + QApplication::setOverrideCursor(Qt::WaitCursor); auto index = tabWidget->addTab(page, strippedName(path)); tabWidget->setTabIcon(index, QIcon(":file.png")); @@ -111,7 +111,7 @@ void TextEdit::slotOpen() { QFile file(fileName); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - auto* page = new EditorPage(fileName); + auto* page = new PlainTextEditorPage(fileName); QTextStream in(&file); QApplication::setOverrideCursor(Qt::WaitCursor); @@ -165,7 +165,7 @@ bool TextEdit::saveFile(const QString& fileName) { QFile file(fileName); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { - EditorPage* page = slotCurPageTextEdit(); + PlainTextEditorPage* page = slotCurPageTextEdit(); QTextStream outputStream(&file); QApplication::setOverrideCursor(Qt::WaitCursor); @@ -197,7 +197,7 @@ bool TextEdit::slotSaveAs() { return true; } - EditorPage* page = slotCurPageTextEdit(); + PlainTextEditorPage* page = slotCurPageTextEdit(); QString path; if (!page->getFilePath().isEmpty()) { path = page->getFilePath(); @@ -250,7 +250,7 @@ void TextEdit::removeTab(int index) { * If it returns false, the close event should be aborted. */ bool TextEdit::maybeSaveCurrentTab(bool askToSave) { - EditorPage* page = slotCurPageTextEdit(); + PlainTextEditorPage* page = slotCurPageTextEdit(); // if this page is no textedit, there should be nothing to save if (page == nullptr) { return true; @@ -352,13 +352,8 @@ bool TextEdit::maybeSaveAnyTab() { return false; } -QTextEdit* TextEdit::curTextPage() const { - auto* curTextPage = qobject_cast<EditorPage*>(tabWidget->currentWidget()); - if (curTextPage != nullptr) { - return curTextPage->getTextPage(); - } else { - return nullptr; - } +PlainTextEditorPage* TextEdit::curTextPage() const { + return qobject_cast<PlainTextEditorPage*>(tabWidget->currentWidget()); } FilePage* TextEdit::curFilePage() const { @@ -372,8 +367,9 @@ FilePage* TextEdit::curFilePage() const { int TextEdit::tabCount() const { return tabWidget->count(); } -EditorPage* TextEdit::slotCurPageTextEdit() const { - auto* curPage = qobject_cast<EditorPage*>(tabWidget->currentWidget()); +PlainTextEditorPage* TextEdit::slotCurPageTextEdit() const { + auto* curPage = + qobject_cast<PlainTextEditorPage*>(tabWidget->currentWidget()); return curPage; } @@ -387,7 +383,7 @@ void TextEdit::slotQuote() const { return; } - QTextCursor cursor(curTextPage()->document()); + QTextCursor cursor(curTextPage()->getTextPage()->document()); // beginEditBlock and endEditBlock() let operation look like single undo/redo // operation @@ -405,10 +401,10 @@ void TextEdit::slotQuote() const { } void TextEdit::slotFillTextEditWithText(const QString& text) const { - QTextCursor cursor(curTextPage()->document()); + QTextCursor cursor(curTextPage()->getTextPage()->document()); cursor.beginEditBlock(); - this->curTextPage()->selectAll(); - this->curTextPage()->insertPlainText(text); + this->curTextPage()->getTextPage()->selectAll(); + this->curTextPage()->getTextPage()->insertPlainText(text); cursor.endEditBlock(); } @@ -425,7 +421,7 @@ void TextEdit::loadFile(const QString& fileName) { } QTextStream in(&file); QApplication::setOverrideCursor(Qt::WaitCursor); - curTextPage()->setPlainText(in.readAll()); + curTextPage()->getTextPage()->setPlainText(in.readAll()); QApplication::restoreOverrideCursor(); slotCurPageTextEdit()->setFilePath(fileName); tabWidget->setTabText(tabWidget->currentIndex(), strippedName(fileName)); @@ -445,7 +441,7 @@ void TextEdit::slotPrint() { #ifndef QT_NO_PRINTER QTextDocument* document; if (curTextPage() != nullptr) { - document = curTextPage()->document(); + document = curTextPage()->getTextPage()->document(); } QPrinter printer; @@ -464,7 +460,7 @@ void TextEdit::slotShowModified() const { QString title = tabWidget->tabText(index); // if doc is modified now, add leading * to title, // otherwise remove the leading * from the title - if (curTextPage()->document()->isModified()) { + if (curTextPage()->getTextPage()->document()->isModified()) { tabWidget->setTabText(index, title.prepend("* ")); } else { tabWidget->setTabText(index, title.remove(0, 2)); @@ -494,7 +490,7 @@ QHash<int, QString> TextEdit::unsavedDocuments() const { // gedit like "unsaved changed"-dialog for (int i = 0; i < tabWidget->count(); i++) { - auto* ep = qobject_cast<EditorPage*>(tabWidget->widget(i)); + auto* ep = qobject_cast<PlainTextEditorPage*>(tabWidget->widget(i)); if (ep != nullptr && ep->ReadDone() && ep->getTextPage()->document()->isModified()) { QString doc_name = tabWidget->tabText(i); @@ -513,7 +509,7 @@ void TextEdit::slotCut() const { return; } - curTextPage()->cut(); + curTextPage()->getTextPage()->cut(); } void TextEdit::slotCopy() const { @@ -522,7 +518,7 @@ void TextEdit::slotCopy() const { } if (curTextPage() != nullptr) { - curTextPage()->copy(); + curTextPage()->getTextPage()->copy(); } } @@ -531,7 +527,7 @@ void TextEdit::slotPaste() const { return; } - curTextPage()->paste(); + curTextPage()->getTextPage()->paste(); } void TextEdit::slotUndo() const { @@ -539,7 +535,7 @@ void TextEdit::slotUndo() const { return; } - curTextPage()->undo(); + curTextPage()->getTextPage()->undo(); } void TextEdit::slotRedo() const { @@ -547,7 +543,7 @@ void TextEdit::slotRedo() const { return; } - curTextPage()->redo(); + curTextPage()->getTextPage()->redo(); } void TextEdit::slotZoomIn() const { @@ -556,7 +552,7 @@ void TextEdit::slotZoomIn() const { } if (curTextPage() != nullptr) { - curTextPage()->zoomIn(); + curTextPage()->getTextPage()->zoomIn(); } } @@ -566,7 +562,7 @@ void TextEdit::slotZoomOut() const { } if (curTextPage() != nullptr) { - curTextPage()->zoomOut(); + curTextPage()->getTextPage()->zoomOut(); } } @@ -574,7 +570,7 @@ void TextEdit::slotSelectAll() const { if (tabWidget->count() == 0 || curTextPage() == nullptr) { return; } - curTextPage()->selectAll(); + curTextPage()->getTextPage()->selectAll(); } void TextEdit::slotFilePagePathChanged(const QString& path) const { diff --git a/src/ui/widgets/TextEdit.h b/src/ui/widgets/TextEdit.h index e877ccc1..c1f44969 100644 --- a/src/ui/widgets/TextEdit.h +++ b/src/ui/widgets/TextEdit.h @@ -26,9 +26,9 @@ #define __TEXTEDIT_H__ #include "ui/QuitDialog.h" -#include "ui/widgets/EditorPage.h" #include "ui/widgets/FilePage.h" #include "ui/widgets/HelpPage.h" +#include "ui/widgets/PlainTextEditorPage.h" namespace GpgFrontend::UI { /** @@ -66,7 +66,7 @@ class TextEdit : public QWidget { * @return \li reference to QTextEdit if tab has one * \li 0 otherwise (e.g. if helppage) */ - [[nodiscard]] QTextEdit* curTextPage() const; + [[nodiscard]] PlainTextEditorPage* curTextPage() const; [[nodiscard]] FilePage* curFilePage() const; @@ -84,7 +84,7 @@ class TextEdit : public QWidget { * @details Return pointer to the currently activated text edit tab page. * */ - EditorPage* slotCurPageTextEdit() const; + PlainTextEditorPage* slotCurPageTextEdit() const; /** * @details Return pointer to the currently activated file treeview tab page. |