diff options
Diffstat (limited to 'tests/manual/examples/widgets/richtext/textedit/textedit.cpp')
-rw-r--r-- | tests/manual/examples/widgets/richtext/textedit/textedit.cpp | 904 |
1 files changed, 904 insertions, 0 deletions
diff --git a/tests/manual/examples/widgets/richtext/textedit/textedit.cpp b/tests/manual/examples/widgets/richtext/textedit/textedit.cpp new file mode 100644 index 0000000000..5f9e8d87b7 --- /dev/null +++ b/tests/manual/examples/widgets/richtext/textedit/textedit.cpp @@ -0,0 +1,904 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "textedit.h" + +#include <QActionGroup> +#include <QApplication> +#include <QClipboard> +#include <QColorDialog> +#include <QComboBox> +#include <QFontComboBox> +#include <QFile> +#include <QFileDialog> +#include <QFileInfo> +#include <QFontDatabase> +#include <QMenu> +#include <QMenuBar> +#include <QTextEdit> +#include <QStatusBar> +#include <QToolBar> +#include <QTextCursor> +#include <QTextDocumentWriter> +#include <QTextList> +#include <QTimer> +#include <QtDebug> +#include <QCloseEvent> +#include <QMessageBox> +#include <QMimeData> +#include <QMimeDatabase> +#include <QStringDecoder> +#if defined(QT_PRINTSUPPORT_LIB) +#include <QtPrintSupport/qtprintsupportglobal.h> +#if QT_CONFIG(printer) +#if QT_CONFIG(printdialog) +#include <QPrintDialog> +#endif +#include <QPrinter> +#if QT_CONFIG(printpreviewdialog) +#include <QPrintPreviewDialog> +#endif +#endif +#endif + +#ifdef Q_OS_MAC +const QString rsrcPath = ":/images/mac"; +#else +const QString rsrcPath = ":/images/win"; +#endif + +TextEdit::TextEdit(QWidget *parent) + : QMainWindow(parent) +{ +#ifdef Q_OS_MACOS + setUnifiedTitleAndToolBarOnMac(true); +#endif + setWindowTitle(QCoreApplication::applicationName()); + + textEdit = new QTextEdit(this); + connect(textEdit, &QTextEdit::currentCharFormatChanged, + this, &TextEdit::currentCharFormatChanged); + connect(textEdit, &QTextEdit::cursorPositionChanged, + this, &TextEdit::cursorPositionChanged); + setCentralWidget(textEdit); + + setToolButtonStyle(Qt::ToolButtonFollowStyle); + setupFileActions(); + setupEditActions(); + setupTextActions(); + + { + QMenu *helpMenu = menuBar()->addMenu(tr("Help")); + helpMenu->addAction(tr("About"), this, &TextEdit::about); + helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); + } + + QFont textFont("Helvetica"); + textFont.setStyleHint(QFont::SansSerif); + textEdit->setFont(textFont); + fontChanged(textEdit->font()); + colorChanged(textEdit->textColor()); + alignmentChanged(textEdit->alignment()); + + auto *document = textEdit->document(); + connect(document, &QTextDocument::modificationChanged, + actionSave, &QAction::setEnabled); + connect(document, &QTextDocument::modificationChanged, + this, &QWidget::setWindowModified); + connect(document, &QTextDocument::undoAvailable, + actionUndo, &QAction::setEnabled); + connect(document, &QTextDocument::redoAvailable, + actionRedo, &QAction::setEnabled); + + setWindowModified(document->isModified()); + actionSave->setEnabled(document->isModified()); + actionUndo->setEnabled(document->isUndoAvailable()); + actionRedo->setEnabled(document->isRedoAvailable()); + +#ifndef QT_NO_CLIPBOARD + actionCut->setEnabled(false); + connect(textEdit, &QTextEdit::copyAvailable, actionCut, &QAction::setEnabled); + actionCopy->setEnabled(false); + connect(textEdit, &QTextEdit::copyAvailable, actionCopy, &QAction::setEnabled); + + connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, + this, &TextEdit::clipboardDataChanged); +#endif + + textEdit->setFocus(); + setCurrentFileName(QString()); + +#ifdef Q_OS_MACOS + // Use dark text on light background on macOS, also in dark mode. + QPalette pal = textEdit->palette(); + pal.setColor(QPalette::Base, QColor(Qt::white)); + pal.setColor(QPalette::Text, QColor(Qt::black)); + textEdit->setPalette(pal); +#endif +} + +//! [closeevent] +void TextEdit::closeEvent(QCloseEvent *e) +{ + if (closeAccepted) { + e->accept(); + return; + } + + e->ignore(); + maybeSave(SaveContinuation::Close); +} +//! [closeevent] + +void TextEdit::setupFileActions() +{ + QToolBar *tb = addToolBar(tr("File Actions")); + QMenu *menu = menuBar()->addMenu(tr("&File")); + + const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(rsrcPath + "/filenew.png")); + QAction *a = menu->addAction(newIcon, tr("&New"), this, &TextEdit::fileNew); + tb->addAction(a); + a->setPriority(QAction::LowPriority); + a->setShortcut(QKeySequence::New); + + const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(rsrcPath + "/fileopen.png")); + a = menu->addAction(openIcon, tr("&Open..."), this, &TextEdit::fileOpen); + a->setShortcut(QKeySequence::Open); + tb->addAction(a); + + menu->addSeparator(); + + const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(rsrcPath + "/filesave.png")); + actionSave = menu->addAction(saveIcon, tr("&Save"), this, + [this]() { fileSave(SaveContinuation::None); }); + actionSave->setShortcut(QKeySequence::Save); + actionSave->setEnabled(false); + tb->addAction(actionSave); + + a = menu->addAction(tr("Save &As..."), this, [this]() { fileSaveAs(SaveContinuation::None); }); + a->setPriority(QAction::LowPriority); + menu->addSeparator(); + +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) + const QIcon printIcon = QIcon::fromTheme("document-print", QIcon(rsrcPath + "/fileprint.png")); + a = menu->addAction(printIcon, tr("&Print..."), this, &TextEdit::filePrint); + a->setPriority(QAction::LowPriority); + a->setShortcut(QKeySequence::Print); + tb->addAction(a); + + const QIcon filePrintIcon = QIcon::fromTheme("fileprint", QIcon(rsrcPath + "/fileprint.png")); + menu->addAction(filePrintIcon, tr("Print Preview..."), this, &TextEdit::filePrintPreview); + + const QIcon exportPdfIcon = QIcon::fromTheme("exportpdf", QIcon(rsrcPath + "/exportpdf.png")); + a = menu->addAction(exportPdfIcon, tr("&Export PDF..."), this, &TextEdit::filePrintPdf); + a->setPriority(QAction::LowPriority); + a->setShortcut(Qt::CTRL | Qt::Key_D); + tb->addAction(a); + + menu->addSeparator(); +#endif + + a = menu->addAction(tr("&Quit"), qApp, &QCoreApplication::quit); + a->setShortcut(Qt::CTRL | Qt::Key_Q); +} + +void TextEdit::setupEditActions() +{ + QToolBar *tb = addToolBar(tr("Edit Actions")); + QMenu *menu = menuBar()->addMenu(tr("&Edit")); + + const QIcon undoIcon = QIcon::fromTheme("edit-undo", QIcon(rsrcPath + "/editundo.png")); + actionUndo = menu->addAction(undoIcon, tr("&Undo"), textEdit, &QTextEdit::undo); + actionUndo->setShortcut(QKeySequence::Undo); + tb->addAction(actionUndo); + + const QIcon redoIcon = QIcon::fromTheme("edit-redo", QIcon(rsrcPath + "/editredo.png")); + actionRedo = menu->addAction(redoIcon, tr("&Redo"), textEdit, &QTextEdit::redo); + actionRedo->setPriority(QAction::LowPriority); + actionRedo->setShortcut(QKeySequence::Redo); + tb->addAction(actionRedo); + menu->addSeparator(); + +#ifndef QT_NO_CLIPBOARD + const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(rsrcPath + "/editcut.png")); + actionCut = menu->addAction(cutIcon, tr("Cu&t"), textEdit, &QTextEdit::cut); + actionCut->setPriority(QAction::LowPriority); + actionCut->setShortcut(QKeySequence::Cut); + tb->addAction(actionCut); + + const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(rsrcPath + "/editcopy.png")); + actionCopy = menu->addAction(copyIcon, tr("&Copy"), textEdit, &QTextEdit::copy); + actionCopy->setPriority(QAction::LowPriority); + actionCopy->setShortcut(QKeySequence::Copy); + tb->addAction(actionCopy); + + const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(rsrcPath + "/editpaste.png")); + actionPaste = menu->addAction(pasteIcon, tr("&Paste"), textEdit, &QTextEdit::paste); + actionPaste->setPriority(QAction::LowPriority); + actionPaste->setShortcut(QKeySequence::Paste); + tb->addAction(actionPaste); + if (const QMimeData *md = QGuiApplication::clipboard()->mimeData()) + actionPaste->setEnabled(md->hasText()); +#endif +} + +void TextEdit::setupTextActions() +{ + QToolBar *tb = addToolBar(tr("Format Actions")); + QMenu *menu = menuBar()->addMenu(tr("F&ormat")); + + const QIcon boldIcon = QIcon::fromTheme("format-text-bold", QIcon(rsrcPath + "/textbold.png")); + actionTextBold = menu->addAction(boldIcon, tr("&Bold"), this, &TextEdit::textBold); + actionTextBold->setShortcut(Qt::CTRL | Qt::Key_B); + actionTextBold->setPriority(QAction::LowPriority); + QFont bold; + bold.setBold(true); + actionTextBold->setFont(bold); + tb->addAction(actionTextBold); + actionTextBold->setCheckable(true); + + const QIcon italicIcon = QIcon::fromTheme("format-text-italic", QIcon(rsrcPath + "/textitalic.png")); + actionTextItalic = menu->addAction(italicIcon, tr("&Italic"), this, &TextEdit::textItalic); + actionTextItalic->setPriority(QAction::LowPriority); + actionTextItalic->setShortcut(Qt::CTRL | Qt::Key_I); + QFont italic; + italic.setItalic(true); + actionTextItalic->setFont(italic); + tb->addAction(actionTextItalic); + actionTextItalic->setCheckable(true); + + const QIcon underlineIcon = QIcon::fromTheme("format-text-underline", QIcon(rsrcPath + "/textunder.png")); + actionTextUnderline = menu->addAction(underlineIcon, tr("&Underline"), this, &TextEdit::textUnderline); + actionTextUnderline->setShortcut(Qt::CTRL | Qt::Key_U); + actionTextUnderline->setPriority(QAction::LowPriority); + QFont underline; + underline.setUnderline(true); + actionTextUnderline->setFont(underline); + tb->addAction(actionTextUnderline); + actionTextUnderline->setCheckable(true); + + menu->addSeparator(); + + const QIcon leftIcon = QIcon::fromTheme("format-justify-left", QIcon(rsrcPath + "/textleft.png")); + actionAlignLeft = new QAction(leftIcon, tr("&Left"), this); + actionAlignLeft->setShortcut(Qt::CTRL | Qt::Key_L); + actionAlignLeft->setCheckable(true); + actionAlignLeft->setPriority(QAction::LowPriority); + const QIcon centerIcon = QIcon::fromTheme("format-justify-center", QIcon(rsrcPath + "/textcenter.png")); + actionAlignCenter = new QAction(centerIcon, tr("C&enter"), this); + actionAlignCenter->setShortcut(Qt::CTRL | Qt::Key_E); + actionAlignCenter->setCheckable(true); + actionAlignCenter->setPriority(QAction::LowPriority); + const QIcon rightIcon = QIcon::fromTheme("format-justify-right", QIcon(rsrcPath + "/textright.png")); + actionAlignRight = new QAction(rightIcon, tr("&Right"), this); + actionAlignRight->setShortcut(Qt::CTRL | Qt::Key_R); + actionAlignRight->setCheckable(true); + actionAlignRight->setPriority(QAction::LowPriority); + const QIcon fillIcon = QIcon::fromTheme("format-justify-fill", QIcon(rsrcPath + "/textjustify.png")); + actionAlignJustify = new QAction(fillIcon, tr("&Justify"), this); + actionAlignJustify->setShortcut(Qt::CTRL | Qt::Key_J); + actionAlignJustify->setCheckable(true); + actionAlignJustify->setPriority(QAction::LowPriority); + const QIcon indentMoreIcon = QIcon::fromTheme("format-indent-more", QIcon(rsrcPath + "/format-indent-more.png")); + actionIndentMore = menu->addAction(indentMoreIcon, tr("&Indent"), this, &TextEdit::indent); + actionIndentMore->setShortcut(Qt::CTRL | Qt::Key_BracketRight); + actionIndentMore->setPriority(QAction::LowPriority); + const QIcon indentLessIcon = QIcon::fromTheme("format-indent-less", QIcon(rsrcPath + "/format-indent-less.png")); + actionIndentLess = menu->addAction(indentLessIcon, tr("&Unindent"), this, &TextEdit::unindent); + actionIndentLess->setShortcut(Qt::CTRL | Qt::Key_BracketLeft); + actionIndentLess->setPriority(QAction::LowPriority); + + // Make sure the alignLeft is always left of the alignRight + QActionGroup *alignGroup = new QActionGroup(this); + connect(alignGroup, &QActionGroup::triggered, this, &TextEdit::textAlign); + + if (QGuiApplication::isLeftToRight()) { + alignGroup->addAction(actionAlignLeft); + alignGroup->addAction(actionAlignCenter); + alignGroup->addAction(actionAlignRight); + } else { + alignGroup->addAction(actionAlignRight); + alignGroup->addAction(actionAlignCenter); + alignGroup->addAction(actionAlignLeft); + } + alignGroup->addAction(actionAlignJustify); + + tb->addActions(alignGroup->actions()); + menu->addActions(alignGroup->actions()); + tb->addAction(actionIndentMore); + tb->addAction(actionIndentLess); + menu->addAction(actionIndentMore); + menu->addAction(actionIndentLess); + + menu->addSeparator(); + + QPixmap pix(16, 16); + pix.fill(Qt::black); + actionTextColor = menu->addAction(pix, tr("&Color..."), this, &TextEdit::textColor); + tb->addAction(actionTextColor); + + const QIcon underlineColorIcon(rsrcPath + "/textundercolor.png"); + actionUnderlineColor = menu->addAction(underlineColorIcon, tr("Underline color..."), this, &TextEdit::underlineColor); + tb->addAction(actionUnderlineColor); + + menu->addSeparator(); + + const QIcon checkboxIcon = QIcon::fromTheme("status-checkbox-checked", QIcon(rsrcPath + "/checkbox-checked.png")); + actionToggleCheckState = menu->addAction(checkboxIcon, tr("Chec&ked"), this, &TextEdit::setChecked); + actionToggleCheckState->setShortcut(Qt::CTRL | Qt::Key_K); + actionToggleCheckState->setCheckable(true); + actionToggleCheckState->setPriority(QAction::LowPriority); + tb->addAction(actionToggleCheckState); + + tb = addToolBar(tr("Format Actions")); + tb->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); + addToolBarBreak(Qt::TopToolBarArea); + addToolBar(tb); + + comboStyle = new QComboBox(tb); + tb->addWidget(comboStyle); + comboStyle->addItems({"Standard", + "Bullet List (Disc)", + "Bullet List (Circle)", + "Bullet List (Square)", + "Task List (Unchecked)", + "Task List (Checked)", + "Ordered List (Decimal)", + "Ordered List (Alpha lower)", + "Ordered List (Alpha upper)", + "Ordered List (Roman lower)", + "Ordered List (Roman upper)", + "Heading 1", + "Heading 2", + "Heading 3", + "Heading 4", + "Heading 5", + "Heading 6"}), + + connect(comboStyle, &QComboBox::activated, this, &TextEdit::textStyle); + + comboFont = new QFontComboBox(tb); + tb->addWidget(comboFont); + connect(comboFont, &QComboBox::textActivated, this, &TextEdit::textFamily); + + comboSize = new QComboBox(tb); + comboSize->setObjectName("comboSize"); + tb->addWidget(comboSize); + comboSize->setEditable(true); + + const QList<int> standardSizes = QFontDatabase::standardSizes(); + for (int size : standardSizes) + comboSize->addItem(QString::number(size)); + comboSize->setCurrentIndex(standardSizes.indexOf(QApplication::font().pointSize())); + + connect(comboSize, &QComboBox::textActivated, this, &TextEdit::textSize); +} + +bool TextEdit::load(const QString &f) +{ + if (!QFile::exists(f)) + return false; + QFile file(f); + if (!file.open(QFile::ReadOnly)) + return false; + + QByteArray data = file.readAll(); + QMimeDatabase db; + const QString &mimeTypeName = db.mimeTypeForFileNameAndData(f, data).name(); + if (mimeTypeName == u"text/html") { + auto encoding = QStringDecoder::encodingForHtml(data); + QString str = QStringDecoder(encoding ? *encoding : QStringDecoder::Utf8)(data); + QUrl fileUrl = f.startsWith(u':') ? QUrl(f) : QUrl::fromLocalFile(f); + textEdit->document()->setBaseUrl(fileUrl.adjusted(QUrl::RemoveFilename)); + textEdit->setHtml(str); +#if QT_CONFIG(textmarkdownreader) + } else if (mimeTypeName == u"text/markdown") { + textEdit->setMarkdown(QString::fromUtf8(data)); +#endif + } else { + textEdit->setPlainText(QString::fromUtf8(data)); + } + + setCurrentFileName(f); + return true; +} + +void TextEdit::maybeSave(SaveContinuation continuation) +{ + if (!textEdit->document()->isModified()) { + // Execute continuation as soon as control has returned to the event loop so that existing + // dialogs do not get in the way of closing the window. + QTimer::singleShot(0, [this, continuation]() { fileSaveComplete(continuation); }); + return; + } + + QMessageBox *msgBox = + new QMessageBox(QMessageBox::Icon::Warning, QCoreApplication::applicationName(), + tr("The document has been modified.\n" + "Do you want to save your changes?"), + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, this); + + connect(msgBox, &QMessageBox::finished, [=](int result) { + if (result == QMessageBox::Save) { + fileSave(continuation); + return; + } + fileSaveComplete(result == QMessageBox::Discard ? continuation : SaveContinuation::None); + }); + + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->open(); +} + +void TextEdit::setCurrentFileName(const QString &fileName) +{ + this->fileName = fileName; + textEdit->document()->setModified(false); + + QString shownName; + if (fileName.isEmpty()) + shownName = "untitled.txt"; + else + shownName = QFileInfo(fileName).fileName(); + + setWindowTitle(tr("%1[*] - %2").arg(shownName, QCoreApplication::applicationName())); + setWindowModified(false); +} + +void TextEdit::fileNew() +{ + maybeSave(SaveContinuation::Clear); +} + +void TextEdit::fileOpen() +{ + QFileDialog *fileDialog = new QFileDialog(this, tr("Open File...")); + fileDialog->setAcceptMode(QFileDialog::AcceptOpen); + fileDialog->setFileMode(QFileDialog::ExistingFile); + fileDialog->setMimeTypeFilters({ +#if QT_CONFIG(texthtmlparser) + "text/html", +#endif +#if QT_CONFIG(textmarkdownreader) + + "text/markdown", +#endif + "text/plain"}); + + connect(fileDialog, &QFileDialog::fileSelected, [=](const QString &file) { + statusBar()->showMessage( + load(file) ? tr(R"(Opened "%1")").arg(QDir::toNativeSeparators(file)) + : tr(R"(Could not open "%1")").arg(QDir::toNativeSeparators(file))); + }); + fileDialog->setAttribute(Qt::WA_DeleteOnClose); + fileDialog->open(); +} + +void TextEdit::fileSave(SaveContinuation continuation) +{ + if (fileName.isEmpty() || fileName.startsWith(u":/")) + return fileSaveAs(continuation); + + QTextDocumentWriter writer(fileName); + bool success = writer.write(textEdit->document()); + if (success) { + textEdit->document()->setModified(false); + statusBar()->showMessage(tr("Wrote \"%1\"").arg(QDir::toNativeSeparators(fileName))); + } else { + statusBar()->showMessage(tr("Could not write to file \"%1\"") + .arg(QDir::toNativeSeparators(fileName))); + } + fileSaveComplete(success ? continuation : SaveContinuation::None); +} + +void TextEdit::fileSaveAs(SaveContinuation continuation) +{ + QFileDialog *fileDialog = new QFileDialog(this, tr("Save as...")); + fileDialog->setAcceptMode(QFileDialog::AcceptSave); + QStringList mimeTypes{"text/plain", +#if QT_CONFIG(textodfwriter) + "application/vnd.oasis.opendocument.text", +#endif +#if QT_CONFIG(textmarkdownwriter) + "text/markdown", +#endif + "text/html"}; + fileDialog->setMimeTypeFilters(mimeTypes); +#if QT_CONFIG(textodfwriter) + fileDialog->setDefaultSuffix("odt"); +#endif + connect(fileDialog, &QFileDialog::finished, [this, continuation, fileDialog](int result) { + if (result != QDialog::Accepted) + return; + setCurrentFileName(fileDialog->selectedFiles().constFirst()); + fileSave(continuation); + }); + fileDialog->setAttribute(Qt::WA_DeleteOnClose); + fileDialog->open(); +} + +void TextEdit::fileSaveComplete(SaveContinuation continuation) +{ + switch (continuation) { + case SaveContinuation::Clear: + textEdit->clear(); + setCurrentFileName({}); + return; + case SaveContinuation::Close: + closeAccepted = true; + close(); + return; + case SaveContinuation::None: + // NOOP as promised + return; + } +} + +void TextEdit::filePrint() +{ +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) + auto printer = std::make_shared<QPrinter>(QPrinter::HighResolution); + QPrintDialog *dlg = new QPrintDialog(printer.get(), this); + if (textEdit->textCursor().hasSelection()) + dlg->setOption(QAbstractPrintDialog::PrintSelection); + dlg->setWindowTitle(tr("Print Document")); + dlg->setAttribute(Qt::WA_DeleteOnClose); + connect(dlg, qOverload<QPrinter *>(&QPrintDialog::accepted), + [this](QPrinter *printer) { textEdit->print(printer); }); + connect(dlg, &QPrintDialog::finished, [printer]() mutable { printer.reset(); }); + dlg->open(); +#endif +} + +void TextEdit::filePrintPreview() +{ +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printpreviewdialog) + auto printer = std::make_shared<QPrinter>(QPrinter::HighResolution); + QPrintPreviewDialog *preview = new QPrintPreviewDialog(printer.get(), this); + preview->setAttribute(Qt::WA_DeleteOnClose); + connect(preview, &QPrintPreviewDialog::paintRequested, textEdit, &QTextEdit::print); + connect(preview, &QPrintPreviewDialog::finished, [printer]() mutable { printer.reset(); }); + preview->open(); +#endif +} + +void TextEdit::filePrintPdf() +{ +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) + QFileDialog *fileDialog = new QFileDialog(this, tr("Export PDF")); + fileDialog->setAcceptMode(QFileDialog::AcceptSave); + fileDialog->setMimeTypeFilters(QStringList("application/pdf")); + fileDialog->setDefaultSuffix("pdf"); + fileDialog->setAttribute(Qt::WA_DeleteOnClose); + connect(fileDialog, &QFileDialog::fileSelected, [this](const QString &file) { + QPrinter printer(QPrinter::HighResolution); + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setOutputFileName(file); + textEdit->document()->print(&printer); + statusBar()->showMessage(tr("Exported \"%1\"").arg(QDir::toNativeSeparators(file))); + }); + + fileDialog->open(); +#endif +} + +void TextEdit::textBold() +{ + QTextCharFormat fmt; + fmt.setFontWeight(actionTextBold->isChecked() ? QFont::Bold : QFont::Normal); + mergeFormatOnWordOrSelection(fmt); +} + +void TextEdit::textUnderline() +{ + QTextCharFormat fmt; + fmt.setFontUnderline(actionTextUnderline->isChecked()); + mergeFormatOnWordOrSelection(fmt); +} + +void TextEdit::textItalic() +{ + QTextCharFormat fmt; + fmt.setFontItalic(actionTextItalic->isChecked()); + mergeFormatOnWordOrSelection(fmt); +} + +void TextEdit::textFamily(const QString &f) +{ + QTextCharFormat fmt; + fmt.setFontFamilies({f}); + mergeFormatOnWordOrSelection(fmt); +} + +void TextEdit::textSize(const QString &p) +{ + qreal pointSize = p.toFloat(); + if (pointSize > 0) { + QTextCharFormat fmt; + fmt.setFontPointSize(pointSize); + mergeFormatOnWordOrSelection(fmt); + } +} + +void TextEdit::textStyle(int styleIndex) +{ + QTextCursor cursor = textEdit->textCursor(); + QTextListFormat::Style style = QTextListFormat::ListStyleUndefined; + QTextBlockFormat::MarkerType marker = QTextBlockFormat::MarkerType::NoMarker; + + switch (styleIndex) { + case 1: + style = QTextListFormat::ListDisc; + break; + case 2: + style = QTextListFormat::ListCircle; + break; + case 3: + style = QTextListFormat::ListSquare; + break; + case 4: + if (cursor.currentList()) + style = cursor.currentList()->format().style(); + else + style = QTextListFormat::ListDisc; + marker = QTextBlockFormat::MarkerType::Unchecked; + break; + case 5: + if (cursor.currentList()) + style = cursor.currentList()->format().style(); + else + style = QTextListFormat::ListDisc; + marker = QTextBlockFormat::MarkerType::Checked; + break; + case 6: + style = QTextListFormat::ListDecimal; + break; + case 7: + style = QTextListFormat::ListLowerAlpha; + break; + case 8: + style = QTextListFormat::ListUpperAlpha; + break; + case 9: + style = QTextListFormat::ListLowerRoman; + break; + case 10: + style = QTextListFormat::ListUpperRoman; + break; + default: + break; + } + + cursor.beginEditBlock(); + + QTextBlockFormat blockFmt = cursor.blockFormat(); + + if (style == QTextListFormat::ListStyleUndefined) { + blockFmt.setObjectIndex(-1); + int headingLevel = styleIndex >= 11 ? styleIndex - 11 + 1 : 0; // H1 to H6, or Standard + blockFmt.setHeadingLevel(headingLevel); + cursor.setBlockFormat(blockFmt); + + int sizeAdjustment = headingLevel ? 4 - headingLevel : 0; // H1 to H6: +3 to -2 + QTextCharFormat fmt; + fmt.setFontWeight(headingLevel ? QFont::Bold : QFont::Normal); + fmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment); + cursor.select(QTextCursor::LineUnderCursor); + cursor.mergeCharFormat(fmt); + textEdit->mergeCurrentCharFormat(fmt); + } else { + blockFmt.setMarker(marker); + cursor.setBlockFormat(blockFmt); + QTextListFormat listFmt; + if (cursor.currentList()) { + listFmt = cursor.currentList()->format(); + } else { + listFmt.setIndent(blockFmt.indent() + 1); + blockFmt.setIndent(0); + cursor.setBlockFormat(blockFmt); + } + listFmt.setStyle(style); + cursor.createList(listFmt); + } + + cursor.endEditBlock(); +} + +void TextEdit::textColor() +{ + QColorDialog *dlg = new QColorDialog(this); + dlg->setCurrentColor(textEdit->textColor()); + connect(dlg, &QColorDialog::colorSelected, [this](const QColor &color) { + if (!color.isValid()) + return; + QTextCharFormat fmt; + fmt.setForeground(color); + mergeFormatOnWordOrSelection(fmt); + colorChanged(color); + }); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->open(); +} + +void TextEdit::underlineColor() +{ + QColorDialog *dlg = new QColorDialog(this); + dlg->setCurrentColor(textEdit->textColor()); + connect(dlg, &QColorDialog::colorSelected, [this](const QColor &color) { + if (!color.isValid()) + return; + QTextCharFormat fmt; + fmt.setUnderlineColor(color); + mergeFormatOnWordOrSelection(fmt); + colorChanged(color); + }); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->open(); +} + +void TextEdit::textAlign(QAction *a) +{ + if (a == actionAlignLeft) + textEdit->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); + else if (a == actionAlignCenter) + textEdit->setAlignment(Qt::AlignHCenter); + else if (a == actionAlignRight) + textEdit->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); + else if (a == actionAlignJustify) + textEdit->setAlignment(Qt::AlignJustify); +} + +void TextEdit::setChecked(bool checked) +{ + textStyle(checked ? 5 : 4); +} + +void TextEdit::indent() +{ + modifyIndentation(1); +} + +void TextEdit::unindent() +{ + modifyIndentation(-1); +} + +void TextEdit::modifyIndentation(int amount) +{ + QTextCursor cursor = textEdit->textCursor(); + cursor.beginEditBlock(); + if (cursor.currentList()) { + QTextListFormat listFmt = cursor.currentList()->format(); + // See whether the line above is the list we want to move this item into, + // or whether we need a new list. + QTextCursor above(cursor); + above.movePosition(QTextCursor::Up); + if (above.currentList() && listFmt.indent() + amount == above.currentList()->format().indent()) { + above.currentList()->add(cursor.block()); + } else { + listFmt.setIndent(listFmt.indent() + amount); + cursor.createList(listFmt); + } + } else { + QTextBlockFormat blockFmt = cursor.blockFormat(); + blockFmt.setIndent(blockFmt.indent() + amount); + cursor.setBlockFormat(blockFmt); + } + cursor.endEditBlock(); +} + +void TextEdit::currentCharFormatChanged(const QTextCharFormat &format) +{ + fontChanged(format.font()); + colorChanged(format.foreground().color()); +} + +void TextEdit::cursorPositionChanged() +{ + alignmentChanged(textEdit->alignment()); + QTextList *list = textEdit->textCursor().currentList(); + if (list) { + switch (list->format().style()) { + case QTextListFormat::ListDisc: + comboStyle->setCurrentIndex(1); + break; + case QTextListFormat::ListCircle: + comboStyle->setCurrentIndex(2); + break; + case QTextListFormat::ListSquare: + comboStyle->setCurrentIndex(3); + break; + case QTextListFormat::ListDecimal: + comboStyle->setCurrentIndex(6); + break; + case QTextListFormat::ListLowerAlpha: + comboStyle->setCurrentIndex(7); + break; + case QTextListFormat::ListUpperAlpha: + comboStyle->setCurrentIndex(8); + break; + case QTextListFormat::ListLowerRoman: + comboStyle->setCurrentIndex(9); + break; + case QTextListFormat::ListUpperRoman: + comboStyle->setCurrentIndex(10); + break; + default: + comboStyle->setCurrentIndex(-1); + break; + } + switch (textEdit->textCursor().block().blockFormat().marker()) { + case QTextBlockFormat::MarkerType::NoMarker: + actionToggleCheckState->setChecked(false); + break; + case QTextBlockFormat::MarkerType::Unchecked: + comboStyle->setCurrentIndex(4); + actionToggleCheckState->setChecked(false); + break; + case QTextBlockFormat::MarkerType::Checked: + comboStyle->setCurrentIndex(5); + actionToggleCheckState->setChecked(true); + break; + } + } else { + int headingLevel = textEdit->textCursor().blockFormat().headingLevel(); + comboStyle->setCurrentIndex(headingLevel ? headingLevel + 10 : 0); + } +} + +void TextEdit::clipboardDataChanged() +{ +#ifndef QT_NO_CLIPBOARD + if (const QMimeData *md = QGuiApplication::clipboard()->mimeData()) + actionPaste->setEnabled(md->hasText()); +#endif +} + +void TextEdit::about() +{ + QMessageBox *msgBox = + new QMessageBox(QMessageBox::Icon::Information, tr("About"), + tr("This example demonstrates Qt's rich text editing facilities in " + "action, providing an example document for you to experiment with."), + QMessageBox::NoButton, this); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->open(); +} + +void TextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat &format) +{ + QTextCursor cursor = textEdit->textCursor(); + if (!cursor.hasSelection()) + cursor.select(QTextCursor::WordUnderCursor); + cursor.mergeCharFormat(format); + textEdit->mergeCurrentCharFormat(format); +} + +void TextEdit::fontChanged(const QFont &f) +{ + comboFont->setCurrentIndex(comboFont->findText(QFontInfo(f).family())); + comboSize->setCurrentIndex(comboSize->findText(QString::number(f.pointSize()))); + actionTextBold->setChecked(f.bold()); + actionTextItalic->setChecked(f.italic()); + actionTextUnderline->setChecked(f.underline()); +} + +void TextEdit::colorChanged(const QColor &c) +{ + QPixmap pix(16, 16); + pix.fill(c); + actionTextColor->setIcon(pix); +} + +void TextEdit::alignmentChanged(Qt::Alignment a) +{ + if (a.testFlag(Qt::AlignLeft)) + actionAlignLeft->setChecked(true); + else if (a.testFlag(Qt::AlignHCenter)) + actionAlignCenter->setChecked(true); + else if (a.testFlag(Qt::AlignRight)) + actionAlignRight->setChecked(true); + else if (a.testFlag(Qt::AlignJustify)) + actionAlignJustify->setChecked(true); +} + |