/* ** Copyright (C) 2013 Jiří Procházka (Hobrasoft) ** Contact: http://www.hobrasoft.cz/ ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file is under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing ** in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the ** GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ */ #include "mrichtextedit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "widgets/arrowrectangle.h" #include "widgets/colorpanel.h" #include "widgets/toolbutton.h" #include MRichTextEdit::MRichTextEdit(QWidget* parent) : QWidget(parent) { setupUi(this); m_lastBlockList = 0; f_textedit->setTabStopWidth(40); connect(f_textedit, SIGNAL(currentCharFormatChanged(QTextCharFormat)), this, SLOT(slotCurrentCharFormatChanged(QTextCharFormat))); connect(f_textedit, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChanged())); fontChanged(f_textedit->font()); bgColorChanged(f_textedit->textColor()); /* m_fontsize_h1 = 18; m_fontsize_h2 = 16; m_fontsize_h3 = 14; m_fontsize_h4 = 12; */ // paragraph formatting /* m_paragraphItems << tr("Standard") << tr("Heading 1") << tr("Heading 2") << tr("Heading 3") << tr("Heading 4") << tr("Monospace"); f_paragraph->addItems(m_paragraphItems); connect(f_paragraph, SIGNAL(activated(int)), this, SLOT(textStyle(int))); */ // undo & redo buttonUndo->setShortcut(QKeySequence::Undo); buttonRedo->setShortcut(QKeySequence::Redo); connect(f_textedit->document(), SIGNAL(undoAvailable(bool)), buttonUndo, SLOT(setEnabled(bool))); connect(f_textedit->document(), SIGNAL(redoAvailable(bool)), buttonRedo, SLOT(setEnabled(bool))); buttonUndo->setEnabled(f_textedit->document()->isUndoAvailable()); buttonRedo->setEnabled(f_textedit->document()->isRedoAvailable()); connect(buttonUndo, SIGNAL(clicked()), f_textedit, SLOT(undo())); connect(buttonRedo, SIGNAL(clicked()), f_textedit, SLOT(redo())); buttonUndo->hide(); buttonRedo->hide(); // cut, copy & paste buttonCut->setShortcut(QKeySequence::Cut); buttonCopy->setShortcut(QKeySequence::Copy); buttonPaste->setShortcut(QKeySequence::Paste); buttonCut->setEnabled(false); buttonCopy->setEnabled(false); connect(buttonCut, SIGNAL(clicked()), f_textedit, SLOT(cut())); connect(buttonCopy, SIGNAL(clicked()), f_textedit, SLOT(copy())); connect(buttonPaste, SIGNAL(clicked()), f_textedit, SLOT(paste())); connect(f_textedit, SIGNAL(copyAvailable(bool)), buttonCut, SLOT(setEnabled(bool))); connect(f_textedit, SIGNAL(copyAvailable(bool)), buttonCopy, SLOT(setEnabled(bool))); buttonCut->hide(); buttonCopy->hide(); buttonPaste->hide(); #ifndef QT_NO_CLIPBOARD connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(slotClipboardDataChanged())); #endif // link buttonLink->setShortcut(Qt::CTRL + Qt::Key_L); connect(buttonLink, SIGNAL(clicked(bool)), this, SLOT(textLink(bool))); // bold, italic & underline buttonBold->setShortcut(Qt::CTRL + Qt::Key_B); buttonItalic->setShortcut(Qt::CTRL + Qt::Key_I); buttonUnderline->setShortcut(Qt::CTRL + Qt::Key_U); connect(buttonBold, SIGNAL(clicked()), this, SLOT(textBold())); connect(buttonItalic, SIGNAL(clicked()), this, SLOT(textItalic())); connect(buttonUnderline, SIGNAL(clicked()), this, SLOT(textUnderline())); connect(buttonStrikeout, SIGNAL(clicked()), this, SLOT(textStrikeout())); QAction* removeFormat = new QAction(tr("Remove character formatting"), this); removeFormat->setShortcut(QKeySequence("CTRL+M")); connect(removeFormat, SIGNAL(triggered()), this, SLOT(textRemoveFormat())); f_textedit->addAction(removeFormat); QAction* removeAllFormat = new QAction(tr("Remove all formatting"), this); connect(removeAllFormat, SIGNAL(triggered()), this, SLOT(textRemoveAllFormat())); f_textedit->addAction(removeAllFormat); QAction* textsource = new QAction(tr("Edit document source"), this); textsource->setShortcut(QKeySequence("CTRL+O")); connect(textsource, SIGNAL(triggered()), this, SLOT(textSource())); f_textedit->addAction(textsource); QMenu* menu = new QMenu(this); menu->addAction(removeAllFormat); menu->addAction(removeFormat); menu->addAction(textsource); buttonMenu->setMenu(menu); buttonMenu->setPopupMode(QToolButton::InstantPopup); // lists buttonListBullet->setShortcut(Qt::CTRL + Qt::Key_Minus); buttonListOrdered->setShortcut(Qt::CTRL + Qt::Key_Equal); connect(buttonListBullet, SIGNAL(clicked(bool)), this, SLOT(listBullet(bool))); connect(buttonListOrdered, SIGNAL(clicked(bool)), this, SLOT(listOrdered(bool))); // indentation buttonIndentDec->setShortcut(Qt::CTRL + Qt::Key_Comma); buttonIndentInc->setShortcut(Qt::CTRL + Qt::Key_Period); connect(buttonIndentInc, SIGNAL(clicked()), this, SLOT(increaseIndentation())); connect(buttonIndentDec, SIGNAL(clicked()), this, SLOT(decreaseIndentation())); // font family QFont font; font.setPointSize(8); fontComboBox->setFont(font); connect(fontComboBox, SIGNAL(activated(QString)), this, SLOT(textFontFamily(QString))); fontComboBox->setCurrentFont(QApplication::font().family());// >setCurrentIndex(fontComboBox->findText(QString::number(QApplication::font().pointSize()))); //<- cambiar // font size QFontDatabase db; foreach (int size, db.standardSizes()) comboFontSize->addItem(QString::number(size)); connect(comboFontSize, SIGNAL(activated(QString)), this, SLOT(textSize(QString))); comboFontSize->setCurrentIndex(comboFontSize->findText(QString::number(QApplication::font().pointSize()))); // text foreground color QPixmap pix(16, 16); pix.fill(QApplication::palette().foreground().color()); buttonFGColor->setIcon(pix); connect(buttonFGColor, SIGNAL(clicked()), this, SLOT(textFgColor())); // text background color pix.fill(QApplication::palette().background().color()); buttonBGColor->setIcon(pix); connect(buttonBGColor, SIGNAL(clicked()), this, SLOT(textBgColor())); // images connect(buttonImage, SIGNAL(clicked()), this, SLOT(insertImage())); } void MRichTextEdit::textSource() { QDialog* dialog = new QDialog(this); QPlainTextEdit* pte = new QPlainTextEdit(dialog); pte->setPlainText(f_textedit->toHtml()); QGridLayout* gl = new QGridLayout(dialog); gl->addWidget(pte, 0, 0, 1, 1); dialog->setWindowTitle(tr("Document source")); dialog->setMinimumWidth(400); dialog->setMinimumHeight(600); dialog->exec(); f_textedit->setHtml(pte->toPlainText()); delete dialog; } void MRichTextEdit::textRemoveFormat() { QTextCharFormat fmt; fmt.setFontWeight(QFont::Normal); fmt.setFontUnderline(false); fmt.setFontStrikeOut(false); fmt.setFontItalic(false); fmt.setFontPointSize(9); // fmt.setFontFamily ("Helvetica"); // fmt.setFontStyleHint (QFont::SansSerif); // fmt.setFontFixedPitch (true); buttonBold->setChecked(false); buttonUnderline->setChecked(false); buttonItalic->setChecked(false); buttonStrikeout->setChecked(false); comboFontSize->setCurrentIndex(comboFontSize->findText("9")); // QTextBlockFormat bfmt = cursor.blockFormat(); // bfmt->setIndent(0); fmt.clearBackground(); mergeFormatOnWordOrSelection(fmt); } void MRichTextEdit::textRemoveAllFormat() { buttonBold->setChecked(false); buttonUnderline->setChecked(false); buttonItalic->setChecked(false); buttonStrikeout->setChecked(false); comboFontSize->setCurrentIndex(comboFontSize->findText("9")); QString text = f_textedit->toPlainText(); f_textedit->setPlainText(text); } void MRichTextEdit::textBold() { QTextCharFormat fmt; fmt.setFontWeight(buttonBold->isChecked() ? QFont::Bold : QFont::Normal); mergeFormatOnWordOrSelection(fmt); } void MRichTextEdit::focusInEvent(QFocusEvent*) { f_textedit->setFocus(Qt::TabFocusReason); } void MRichTextEdit::textUnderline() { QTextCharFormat fmt; fmt.setFontUnderline(buttonUnderline->isChecked()); mergeFormatOnWordOrSelection(fmt); } void MRichTextEdit::textItalic() { QTextCharFormat fmt; fmt.setFontItalic(buttonItalic->isChecked()); mergeFormatOnWordOrSelection(fmt); } void MRichTextEdit::textStrikeout() { QTextCharFormat fmt; fmt.setFontStrikeOut(buttonStrikeout->isChecked()); mergeFormatOnWordOrSelection(fmt); } void MRichTextEdit::textFontFamily(const QString& p) { QTextCharFormat fmt; fmt.setFontFamily(this->fontComboBox->currentFont().family()); mergeFormatOnWordOrSelection(fmt); } void MRichTextEdit::textSize(const QString& p) { qreal pointSize = p.toFloat(); if (p.toFloat() > 0) { QTextCharFormat fmt; fmt.setFontPointSize(pointSize); mergeFormatOnWordOrSelection(fmt); } } void MRichTextEdit::textLink(bool checked) { bool unlink = false; QTextCharFormat fmt; if (checked) { QString url = f_textedit->currentCharFormat().anchorHref(); bool ok; QString newUrl = DInputDialog::getText(NULL, tr("Create a link"), tr("Link URL:"), QLineEdit::Normal, url, &ok); if (ok) { fmt.setAnchor(true); fmt.setAnchorHref(newUrl); fmt.setForeground(QApplication::palette().color(QPalette::Link)); fmt.setFontUnderline(true); } else { unlink = true; } } else { unlink = true; } if (unlink) { fmt.setAnchor(false); fmt.setForeground(QApplication::palette().color(QPalette::Text)); fmt.setFontUnderline(false); } mergeFormatOnWordOrSelection(fmt); } /* void MRichTextEdit::textStyle(int index) { QTextCursor cursor = f_textedit->textCursor(); cursor.beginEditBlock(); // standard if (!cursor.hasSelection()) { cursor.select(QTextCursor::BlockUnderCursor); } QTextCharFormat fmt; cursor.setCharFormat(fmt); f_textedit->setCurrentCharFormat(fmt); if (index == ParagraphHeading1 || index == ParagraphHeading2 || index == ParagraphHeading3 || index == ParagraphHeading4) { if (index == ParagraphHeading1) { fmt.setFontPointSize(m_fontsize_h1); } if (index == ParagraphHeading2) { fmt.setFontPointSize(m_fontsize_h2); } if (index == ParagraphHeading3) { fmt.setFontPointSize(m_fontsize_h3); } if (index == ParagraphHeading4) { fmt.setFontPointSize(m_fontsize_h4); } if (index == ParagraphHeading2 || index == ParagraphHeading4) { fmt.setFontItalic(true); } fmt.setFontWeight(QFont::Bold); } if (index == ParagraphMonospace) { fmt = cursor.charFormat(); fmt.setFontFamily("Monospace"); fmt.setFontStyleHint(QFont::Monospace); fmt.setFontFixedPitch(true); } cursor.setCharFormat(fmt); f_textedit->setCurrentCharFormat(fmt); cursor.endEditBlock(); } */ void MRichTextEdit::textFgColor() { auto* m_colorPanel = new ColorPanel(this); qApp->setProperty("_d_isDxcb", false); auto* m_colorARect = new ArrowRectangle(DArrowRectangle::ArrowTop); qApp->setProperty("_d_isDxcb", true); m_colorARect->setWindowFlags(Qt::Widget); //m_colorARect->setAttribute(Qt::WA_TranslucentBackground, true); m_colorARect->setArrowWidth(18); m_colorARect->setArrowHeight(10); m_colorARect->setContent(m_colorPanel); m_colorARect->setBackgroundColor(QColor(255, 255, 255, 255)); connect(m_colorPanel, &ColorPanel::updateHeight, this, [=] { m_colorARect->setContent(m_colorPanel); }); m_colorARect->raise(); m_colorARect->show(buttonFGColor->mapToGlobal(buttonFGColor->rect().bottomLeft()).x() + buttonFGColor->width() / 2, buttonFGColor->mapToGlobal(buttonFGColor->rect().bottomLeft()).y() + 2); connect(m_colorARect, &ArrowRectangle::hideWindow, this, [=] { QColor col = m_colorPanel->getColor(); QTextCursor cursor = f_textedit->textCursor(); if (!cursor.hasSelection()) { cursor.select(QTextCursor::WordUnderCursor); } QTextCharFormat fmt = cursor.charFormat(); if (col.isValid()) { fmt.setForeground(col); } else { fmt.clearForeground(); } cursor.setCharFormat(fmt); f_textedit->setCurrentCharFormat(fmt); fgColorChanged(col); }); } void MRichTextEdit::textBgColor() { auto* m_colorPanel = new ColorPanel(this); qApp->setProperty("_d_isDxcb", false); auto* m_colorARect = new ArrowRectangle(DArrowRectangle::ArrowTop); qApp->setProperty("_d_isDxcb", true); m_colorARect->setWindowFlags(Qt::Widget); m_colorARect->setArrowWidth(18); m_colorARect->setArrowHeight(10); m_colorARect->setContent(m_colorPanel); m_colorARect->setBackgroundColor(QColor(255, 255, 255, 255)); connect(m_colorPanel, &ColorPanel::updateHeight, this, [=] { m_colorARect->setContent(m_colorPanel); }); m_colorARect->raise(); m_colorARect->show(buttonBGColor->mapToGlobal(buttonBGColor->rect().bottomLeft()).x() + buttonBGColor->width() / 2, buttonBGColor->mapToGlobal(buttonBGColor->rect().bottomLeft()).y() + 2); connect(m_colorARect, &ArrowRectangle::hideWindow, this, [=] { QColor col = m_colorPanel->getColor(); QTextCursor cursor = f_textedit->textCursor(); if (!cursor.hasSelection()) { cursor.select(QTextCursor::WordUnderCursor); } QTextCharFormat fmt = cursor.charFormat(); if (col.isValid()) { fmt.setBackground(col); } else { fmt.clearBackground(); } cursor.setCharFormat(fmt); f_textedit->setCurrentCharFormat(fmt); bgColorChanged(col); }); /*QColor col = QColorDialog::getColor(f_textedit->textBackgroundColor(), this); QTextCursor cursor = f_textedit->textCursor(); if (!cursor.hasSelection()) { cursor.select(QTextCursor::WordUnderCursor); } QTextCharFormat fmt = cursor.charFormat(); if (col.isValid()) { fmt.setBackground(col); } else { fmt.clearBackground(); } cursor.setCharFormat(fmt); f_textedit->setCurrentCharFormat(fmt); bgColorChanged(col);*/ } void MRichTextEdit::listBullet(bool checked) { if (checked) { buttonListOrdered->setChecked(false); } list(checked, QTextListFormat::ListDisc); } void MRichTextEdit::listOrdered(bool checked) { if (checked) { buttonListBullet->setChecked(false); } list(checked, QTextListFormat::ListDecimal); } void MRichTextEdit::list(bool checked, QTextListFormat::Style style) { QTextCursor cursor = f_textedit->textCursor(); cursor.beginEditBlock(); if (!checked) { QTextBlockFormat obfmt = cursor.blockFormat(); QTextBlockFormat bfmt; bfmt.setIndent(obfmt.indent()); cursor.setBlockFormat(bfmt); } else { QTextListFormat listFmt; if (cursor.currentList()) { listFmt = cursor.currentList()->format(); } listFmt.setStyle(style); cursor.createList(listFmt); } cursor.endEditBlock(); } void MRichTextEdit::mergeFormatOnWordOrSelection(const QTextCharFormat& format) { QTextCursor cursor = f_textedit->textCursor(); if (!cursor.hasSelection()) { cursor.select(QTextCursor::WordUnderCursor); } cursor.mergeCharFormat(format); f_textedit->mergeCurrentCharFormat(format); f_textedit->setFocus(Qt::TabFocusReason); } void MRichTextEdit::slotCursorPositionChanged() { QTextList* l = f_textedit->textCursor().currentList(); if (m_lastBlockList && (l == m_lastBlockList || (l != 0 && m_lastBlockList != 0 && l->format().style() == m_lastBlockList->format().style()))) { return; } m_lastBlockList = l; if (l) { QTextListFormat lfmt = l->format(); if (lfmt.style() == QTextListFormat::ListDisc) { buttonListBullet->setChecked(true); buttonListOrdered->setChecked(false); } else if (lfmt.style() == QTextListFormat::ListDecimal) { buttonListBullet->setChecked(false); buttonListOrdered->setChecked(true); } else { buttonListBullet->setChecked(false); buttonListOrdered->setChecked(false); } } else { buttonListBullet->setChecked(false); buttonListOrdered->setChecked(false); } } void MRichTextEdit::fontChanged(const QFont& f) { fontComboBox->setCurrentFont(QFont(f.family()) ); comboFontSize->setCurrentIndex(comboFontSize->findText(QString::number(f.pointSize()))); buttonBold->setChecked(f.bold()); buttonItalic->setChecked(f.italic()); buttonUnderline->setChecked(f.underline()); buttonStrikeout->setChecked(f.strikeOut()); /* if (f.pointSize() == m_fontsize_h1) { f_paragraph->setCurrentIndex(ParagraphHeading1); } else if (f.pointSize() == m_fontsize_h2) { f_paragraph->setCurrentIndex(ParagraphHeading2); } else if (f.pointSize() == m_fontsize_h3) { f_paragraph->setCurrentIndex(ParagraphHeading3); } else if (f.pointSize() == m_fontsize_h4) { f_paragraph->setCurrentIndex(ParagraphHeading4); } else { if (f.fixedPitch() && f.family() == "Monospace") { f_paragraph->setCurrentIndex(ParagraphMonospace); } else { f_paragraph->setCurrentIndex(ParagraphStandard); } } */ if (f_textedit->textCursor().currentList()) { QTextListFormat lfmt = f_textedit->textCursor().currentList()->format(); if (lfmt.style() == QTextListFormat::ListDisc) { buttonListBullet->setChecked(true); buttonListOrdered->setChecked(false); } else if (lfmt.style() == QTextListFormat::ListDecimal) { buttonListBullet->setChecked(false); buttonListOrdered->setChecked(true); } else { buttonListBullet->setChecked(false); buttonListOrdered->setChecked(false); } } else { buttonListBullet->setChecked(false); buttonListOrdered->setChecked(false); } } void MRichTextEdit::fgColorChanged(const QColor& c) { QPixmap pix(16, 16); if (c.isValid()) { pix.fill(c); } else { pix.fill(QApplication::palette().foreground().color()); } buttonFGColor->setIcon(pix); } void MRichTextEdit::bgColorChanged(const QColor& c) { QPixmap pix(16, 16); if (c.isValid()) { pix.fill(c); } else { pix.fill(QApplication::palette().background().color()); } buttonBGColor->setIcon(pix); } void MRichTextEdit::slotCurrentCharFormatChanged(const QTextCharFormat& format) { fontChanged(format.font()); bgColorChanged((format.background().isOpaque()) ? format.background().color() : QColor()); fgColorChanged((format.foreground().isOpaque()) ? format.foreground().color() : QColor()); buttonLink->setChecked(format.isAnchor()); } void MRichTextEdit::slotClipboardDataChanged() { #ifndef QT_NO_CLIPBOARD if (const QMimeData* md = QApplication::clipboard()->mimeData()) buttonPaste->setEnabled(md->hasText()); #endif } QString MRichTextEdit::toHtml() const { QString s = f_textedit->toHtml(); // convert emails to links s = s.replace(QRegExp("(<[^a][^>]+>(?:]+>)?|\\s)([a-zA-Z\\d]+@[a-zA-Z\\d]+\\.[a-zA-Z]+)"), "\\1\\2"); // convert links s = s.replace(QRegExp("(<[^a][^>]+>(?:]+>)?|\\s)((?:https?|ftp|file)://[^\\s'\"<>]+)"), "\\1\\2"); // see also: Utils::linkify() return s; } void MRichTextEdit::increaseIndentation() { indent(+1); } void MRichTextEdit::decreaseIndentation() { indent(-1); } void MRichTextEdit::indent(int delta) { QTextCursor cursor = f_textedit->textCursor(); cursor.beginEditBlock(); QTextBlockFormat bfmt = cursor.blockFormat(); int ind = bfmt.indent(); if (ind + delta >= 0) { bfmt.setIndent(ind + delta); } cursor.setBlockFormat(bfmt); cursor.endEditBlock(); } void MRichTextEdit::setText(const QString& text) { if (text.isEmpty()) { setPlainText(text); return; } /* if (text[0] == '<') { setHtml(text); } else { setPlainText(text); }*/ setHtml(text); f_textedit->moveCursor(QTextCursor::End); } void MRichTextEdit::insertHtml(const QString &text) { f_textedit->insertHtml(text); } void MRichTextEdit::insertImage() { QSettings s; QString attdir = s.value("general/filedialog-path").toString(); QString file = QFileDialog::getOpenFileName(this, tr("Select an image"), attdir, tr("JPEG (*.jpg);; GIF (*.gif);; PNG (*.png);; BMP (*.bmp);; All (*)")); QImage image = QImageReader(file).read(); f_textedit->dropImage(image, QFileInfo(file).suffix().toUpper().toLocal8Bit().data()); } void MRichTextEdit::setDefaultFont(QFont fontFamily) { fontComboBox->setCurrentFont(fontFamily.family()); textFontFamily(fontFamily.family()); } void MRichTextEdit::setDefaultFontSize(int size) { QString sSize = QString("%i").arg(size); comboFontSize->setCurrentText(sSize); textSize(sSize); }