39#include <QApplication>
43#include <QFontDatabase>
44#include <QImageReader>
45#include <QInputDialog>
54#include "moc_qgsrichtexteditor.cpp"
63 QVBoxLayout *sourceLayout =
new QVBoxLayout();
64 sourceLayout->setContentsMargins( 0, 0, 0, 0 );
67 sourceLayout->addWidget( codeEditorWidget );
68 mPageSourceEdit->setLayout( sourceLayout );
72 connect( mTextEdit, &QTextEdit::currentCharFormatChanged,
this, &QgsRichTextEditor::slotCurrentCharFormatChanged );
73 connect( mTextEdit, &QTextEdit::cursorPositionChanged,
this, &QgsRichTextEditor::slotCursorPositionChanged );
76 mActionUndo->setShortcut( QKeySequence::Undo );
77 mActionRedo->setShortcut( QKeySequence::Redo );
79 connect( mTextEdit->document(), &QTextDocument::undoAvailable, mActionUndo, &QAction::setEnabled );
80 connect( mTextEdit->document(), &QTextDocument::redoAvailable, mActionRedo, &QAction::setEnabled );
82 mActionUndo->setEnabled( mTextEdit->document()->isUndoAvailable() );
83 mActionRedo->setEnabled( mTextEdit->document()->isRedoAvailable() );
85 connect( mActionUndo, &QAction::triggered, mTextEdit, &QTextEdit::undo );
86 connect( mActionRedo, &QAction::triggered, mTextEdit, &QTextEdit::redo );
89 mActionCut->setShortcut( QKeySequence::Cut );
90 mActionCopy->setShortcut( QKeySequence::Copy );
91 mActionPaste->setShortcut( QKeySequence::Paste );
93 mActionCut->setEnabled(
false );
94 mActionCopy->setEnabled(
false );
96 connect( mActionCut, &QAction::triggered, mTextEdit, &QTextEdit::cut );
97 connect( mActionCopy, &QAction::triggered, mTextEdit, &QTextEdit::copy );
98 connect( mActionPaste, &QAction::triggered, mTextEdit, &QTextEdit::paste );
100 connect( mTextEdit, &QTextEdit::copyAvailable, mActionCut, &QAction::setEnabled );
101 connect( mTextEdit, &QTextEdit::copyAvailable, mActionCopy, &QAction::setEnabled );
103#ifndef QT_NO_CLIPBOARD
104 connect( QApplication::clipboard(), &QClipboard::dataChanged,
this, &QgsRichTextEditor::slotClipboardDataChanged );
108 mActionInsertLink->setShortcut( QKeySequence( QStringLiteral(
"CTRL+L" ) ) );
109 connect( mActionInsertLink, &QAction::triggered,
this, &QgsRichTextEditor::textLink );
112 mActionBold->setShortcut( QKeySequence( QStringLiteral(
"CTRL+B" ) ) );
113 mActionItalic->setShortcut( QKeySequence( QStringLiteral(
"CTRL+I" ) ) );
114 mActionUnderline->setShortcut( QKeySequence( QStringLiteral(
"CTRL+U" ) ) );
116 connect( mActionBold, &QAction::triggered,
this, &QgsRichTextEditor::textBold );
117 connect( mActionItalic, &QAction::triggered,
this, &QgsRichTextEditor::textItalic );
118 connect( mActionUnderline, &QAction::triggered,
this, &QgsRichTextEditor::textUnderline );
119 connect( mActionStrikeOut, &QAction::triggered,
this, &QgsRichTextEditor::textStrikeout );
122 mActionBulletList->setShortcut( QKeySequence( QStringLiteral(
"CTRL+-" ) ) );
123 mActionOrderedList->setShortcut( QKeySequence( QStringLiteral(
"CTRL+=" ) ) );
124 connect( mActionBulletList, &QAction::triggered,
this, &QgsRichTextEditor::listBullet );
125 connect( mActionOrderedList, &QAction::triggered,
this, &QgsRichTextEditor::listOrdered );
128 mActionDecreaseIndent->setShortcut( QKeySequence( QStringLiteral(
"CTRL+," ) ) );
129 mActionIncreaseIndent->setShortcut( QKeySequence( QStringLiteral(
"CTRL+." ) ) );
130 connect( mActionIncreaseIndent, &QAction::triggered,
this, &QgsRichTextEditor::increaseIndentation );
131 connect( mActionDecreaseIndent, &QAction::triggered,
this, &QgsRichTextEditor::decreaseIndentation );
133 connect( mActionEditSource, &QAction::toggled,
this, &QgsRichTextEditor::editSource );
136 connect( mActionInsertImage, &QAction::triggered,
this, &QgsRichTextEditor::insertImage );
140 fontChanged( mTextEdit->font() );
150 delete mFontSizeCombo;
151 mFontSizeCombo =
nullptr;
152 delete mParagraphStyleCombo;
153 mParagraphStyleCombo =
nullptr;
154 delete mForeColorButton;
155 mForeColorButton =
nullptr;
156 delete mBackColorButton;
157 mBackColorButton =
nullptr;
162 mParagraphStyleCombo =
new QComboBox();
163 mParagraphStyleCombo->addItem( tr(
"Standard" ), ParagraphStandard );
164 mParagraphStyleCombo->addItem( tr(
"Heading 1" ), ParagraphHeading1 );
165 mParagraphStyleCombo->addItem( tr(
"Heading 2" ), ParagraphHeading2 );
166 mParagraphStyleCombo->addItem( tr(
"Heading 3" ), ParagraphHeading3 );
167 mParagraphStyleCombo->addItem( tr(
"Heading 4" ), ParagraphHeading4 );
168 mParagraphStyleCombo->addItem( tr(
"Monospace" ), ParagraphMonospace );
170 connect( mParagraphStyleCombo, qOverload<int>( &QComboBox::activated ),
this, &QgsRichTextEditor::textStyle );
173 mToolBar->addWidget( mParagraphStyleCombo );
176 mToolBar->addAction( mActionUndo );
177 mToolBar->addAction( mActionRedo );
178 mToolBar->addAction( mActionCut );
179 mToolBar->addAction( mActionCopy );
180 mToolBar->addAction( mActionPaste );
183 mFontSizeCombo =
new QComboBox();
184 mFontSizeCombo->setEditable(
true );
186 const QList<int> sizes = QFontDatabase::standardSizes();
187 for (
const int size : sizes )
188 mFontSizeCombo->addItem( QString::number( size ), size );
190 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( QApplication::font().pointSize() ) );
191 connect( mFontSizeCombo, &QComboBox::textActivated,
this, &QgsRichTextEditor::textSize );
195 mToolBar->addSeparator();
196 mToolBar->addWidget( mFontSizeCombo );
197 mToolBar->addAction( mActionBold );
198 mToolBar->addAction( mActionItalic );
199 mToolBar->addAction( mActionUnderline );
200 mToolBar->addAction( mActionStrikeOut );
202 mToolBar->addSeparator();
207 mForeColorButton->setAllowOpacity(
false );
208 mForeColorButton->setColorDialogTitle( tr(
"Foreground Color" ) );
209 mForeColorButton->setColor( palette().windowText().color() );
210 mForeColorButton->setShowNoColor(
false );
211 mForeColorButton->setToolTip( tr(
"Foreground color" ) );
212 mForeColorButton->setMinimumWidth( QFontMetrics( font() ).horizontalAdvance( QStringLiteral(
"x" ) ) * 10 );
213 mForeColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
219 mToolBar->addWidget( mForeColorButton );
224 mBackColorButton->setAllowOpacity(
false );
225 mBackColorButton->setColorDialogTitle( tr(
"Background Color" ) );
226 mBackColorButton->setToolTip( tr(
"Background color" ) );
227 mBackColorButton->setShowNull(
true, tr(
"No Background Color" ) );
228 mBackColorButton->setToNull();
229 mBackColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
230 mBackColorButton->setMinimumWidth( QFontMetrics( font() ).horizontalAdvance( QStringLiteral(
"x" ) ) * 10 );
235 mToolBar->addWidget( mBackColorButton );
236 mToolBar->addAction( mActionBulletList );
237 mToolBar->addAction( mActionOrderedList );
238 mToolBar->addAction( mActionDecreaseIndent );
239 mToolBar->addAction( mActionIncreaseIndent );
240 mToolBar->addSeparator();
241 mToolBar->addAction( mActionInsertLink );
242 mToolBar->addAction( mActionInsertImage );
246 mToolBar->addAction( mActionEditSource );
249 QMenu *menu =
new QMenu(
this );
252 QAction *removeFormat =
new QAction( tr(
"Remove Character Formatting" ), menu );
253 removeFormat->setShortcut( QKeySequence( QStringLiteral(
"CTRL+M" ) ) );
254 connect( removeFormat, &QAction::triggered,
this, &QgsRichTextEditor::textRemoveFormat );
255 mTextEdit->addAction( removeFormat );
257 QAction *removeAllFormat =
new QAction( tr(
"Remove all Formatting" ), menu );
258 connect( removeAllFormat, &QAction::triggered,
this, &QgsRichTextEditor::textRemoveAllFormat );
259 mTextEdit->addAction( removeAllFormat );
260 menu->addAction( removeAllFormat );
261 menu->addAction( removeFormat );
264 QAction *clearText =
new QAction( tr(
"Clear all Content" ), menu );
266 mTextEdit->addAction( clearText );
268 menu->addAction( clearText );
270 QToolButton *menuButton =
new QToolButton( mToolBar );
271 menuButton->setMenu( menu );
272 menuButton->setPopupMode( QToolButton::InstantPopup );
273 menuButton->setToolTip( tr(
"Advanced Options" ) );
274 menuButton->setText( QStringLiteral(
"…" ) );
276 QWidget *spacer =
new QWidget();
277 spacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
278 mToolBar->addWidget( spacer );
279 mToolBar->addWidget( menuButton );
289 switch ( mStackedWidget->currentIndex() )
292 return mTextEdit->toPlainText();
296 mTextEdit->setText( mSourceEdit->text() );
297 return mTextEdit->toPlainText();
307 switch ( mStackedWidget->currentIndex() )
310 return mTextEdit->toHtml();
313 return mSourceEdit->text();
321void QgsRichTextEditor::editSource(
bool enabled )
323 if ( enabled && mStackedWidget->currentIndex() == 0 )
325 mSourceEdit->
setText( mTextEdit->toHtml() );
326 mStackedWidget->setCurrentIndex( 1 );
328 else if ( !enabled && mStackedWidget->currentIndex() == 1 )
330 mTextEdit->setHtml( mSourceEdit->text() );
331 mStackedWidget->setCurrentIndex( 0 );
332 mSourceEdit->clear();
336 mFontSizeCombo->setEnabled( !enabled );
337 mParagraphStyleCombo->setEnabled( !enabled );
338 mForeColorButton->setEnabled( !enabled );
339 mBackColorButton->setEnabled( !enabled );
340 mActionUndo->setEnabled( !enabled );
341 mActionRedo->setEnabled( !enabled );
342 mActionCut->setEnabled( !enabled );
343 mActionCopy->setEnabled( !enabled );
344 mActionPaste->setEnabled( !enabled );
345 mActionInsertLink->setEnabled( !enabled );
346 mActionBold->setEnabled( !enabled );
347 mActionItalic->setEnabled( !enabled );
348 mActionUnderline->setEnabled( !enabled );
349 mActionStrikeOut->setEnabled( !enabled );
350 mActionBulletList->setEnabled( !enabled );
351 mActionOrderedList->setEnabled( !enabled );
352 mActionDecreaseIndent->setEnabled( !enabled );
353 mActionIncreaseIndent->setEnabled( !enabled );
354 mActionInsertImage->setEnabled( !enabled );
362void QgsRichTextEditor::textRemoveFormat()
364 QTextCharFormat format;
365 format.setFontWeight( QFont::Normal );
366 format.setFontUnderline(
false );
367 format.setFontStrikeOut(
false );
368 format.setFontItalic(
false );
369 format.setFontPointSize( 9 );
371 mActionBold->setChecked(
false );
372 mActionUnderline->setChecked(
false );
373 mActionItalic->setChecked(
false );
374 mActionStrikeOut->setChecked(
false );
375 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( 9 ) );
377 format.clearBackground();
379 mergeFormatOnWordOrSelection( format );
382void QgsRichTextEditor::textRemoveAllFormat()
384 mActionBold->setChecked(
false );
385 mActionUnderline->setChecked(
false );
386 mActionItalic->setChecked(
false );
387 mActionStrikeOut->setChecked(
false );
388 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( 9 ) );
389 const QString text = mTextEdit->toPlainText();
390 mTextEdit->setPlainText( text );
393void QgsRichTextEditor::textBold()
395 QTextCharFormat format;
396 format.setFontWeight( mActionBold->isChecked() ? QFont::Bold : QFont::Normal );
397 mergeFormatOnWordOrSelection( format );
402 mTextEdit->setFocus( Qt::TabFocusReason );
405void QgsRichTextEditor::textUnderline()
407 QTextCharFormat format;
408 format.setFontUnderline( mActionUnderline->isChecked() );
409 mergeFormatOnWordOrSelection( format );
412void QgsRichTextEditor::textItalic()
414 QTextCharFormat format;
415 format.setFontItalic( mActionItalic->isChecked() );
416 mergeFormatOnWordOrSelection( format );
419void QgsRichTextEditor::textStrikeout()
421 QTextCharFormat format;
422 format.setFontStrikeOut( mActionStrikeOut->isChecked() );
423 mergeFormatOnWordOrSelection( format );
426void QgsRichTextEditor::textSize(
const QString &p )
428 const qreal pointSize = p.toDouble();
429 if ( p.toFloat() > 0 )
431 QTextCharFormat format;
432 format.setFontPointSize( pointSize );
433 mergeFormatOnWordOrSelection( format );
437void QgsRichTextEditor::textLink(
bool checked )
440 QTextCharFormat format;
443 const QString url = mTextEdit->currentCharFormat().anchorHref();
445 const QString newUrl = QInputDialog::getText(
this, tr(
"Create a Link" ), tr(
"Link URL:" ), QLineEdit::Normal, url, &ok );
448 format.setAnchor(
true );
449 format.setAnchorHref( newUrl );
450 format.setForeground( palette().color( QPalette::Link ) );
451 format.setFontUnderline(
true );
464 format.setAnchor(
false );
465 format.setForeground( palette().color( QPalette::Text ) );
466 format.setFontUnderline(
false );
468 mergeFormatOnWordOrSelection( format );
471void QgsRichTextEditor::textStyle(
int index )
473 QTextCursor cursor = mTextEdit->textCursor();
474 cursor.beginEditBlock();
477 if ( !cursor.hasSelection() )
479 cursor.select( QTextCursor::BlockUnderCursor );
481 QTextCharFormat format;
482 cursor.setCharFormat( format );
483 mTextEdit->setCurrentCharFormat( format );
485 const ParagraphItems style =
static_cast<ParagraphItems
>( mParagraphStyleCombo->itemData( index ).toInt() );
489 case QgsRichTextEditor::ParagraphStandard:
492 case QgsRichTextEditor::ParagraphHeading1:
493 format.setFontPointSize( mFontSizeH1 );
494 format.setFontWeight( QFont::Bold );
497 case QgsRichTextEditor::ParagraphHeading2:
498 format.setFontPointSize( mFontSizeH2 );
499 format.setFontWeight( QFont::Bold );
500 format.setFontItalic(
true );
503 case QgsRichTextEditor::ParagraphHeading3:
504 format.setFontPointSize( mFontSizeH3 );
505 format.setFontWeight( QFont::Bold );
508 case QgsRichTextEditor::ParagraphHeading4:
509 format.setFontPointSize( mFontSizeH4 );
510 format.setFontWeight( QFont::Bold );
511 format.setFontItalic(
true );
514 case QgsRichTextEditor::ParagraphMonospace:
516 format = cursor.charFormat();
517 format.setFontFamily( mMonospaceFontFamily );
518 format.setFontStyleHint( QFont::Monospace );
519 format.setFontFixedPitch(
true );
524 cursor.setCharFormat( format );
525 mTextEdit->setCurrentCharFormat( format );
527 cursor.endEditBlock();
530void QgsRichTextEditor::textFgColor()
532 QTextCharFormat format;
533 format.setForeground( mForeColorButton->color() );
534 mergeFormatOnWordOrSelection( format );
537void QgsRichTextEditor::textBgColor()
539 QTextCharFormat format;
540 const QColor col = mBackColorButton->color();
543 format.setBackground( col );
547 format.clearBackground();
549 mergeFormatOnWordOrSelection( format );
552void QgsRichTextEditor::listBullet(
bool checked )
556 mActionOrderedList->setChecked(
false );
558 list( checked, QTextListFormat::ListDisc );
561void QgsRichTextEditor::listOrdered(
bool checked )
565 mActionBulletList->setChecked(
false );
567 list( checked, QTextListFormat::ListDecimal );
570void QgsRichTextEditor::list(
bool checked, QTextListFormat::Style style )
572 QTextCursor cursor = mTextEdit->textCursor();
573 cursor.beginEditBlock();
576 const QTextBlockFormat originalFormat = cursor.blockFormat();
577 QTextBlockFormat format;
578 format.setIndent( originalFormat.indent() );
579 cursor.setBlockFormat( format );
583 QTextListFormat listFormat;
584 if ( cursor.currentList() )
586 listFormat = cursor.currentList()->format();
588 listFormat.setStyle( style );
589 cursor.createList( listFormat );
591 cursor.endEditBlock();
594void QgsRichTextEditor::mergeFormatOnWordOrSelection(
const QTextCharFormat &format )
596 QTextCursor cursor = mTextEdit->textCursor();
597 if ( !cursor.hasSelection() )
599 cursor.select( QTextCursor::WordUnderCursor );
601 cursor.mergeCharFormat( format );
602 mTextEdit->mergeCurrentCharFormat( format );
603 mTextEdit->setFocus( Qt::TabFocusReason );
606void QgsRichTextEditor::slotCursorPositionChanged()
608 QTextList *l = mTextEdit->textCursor().currentList();
609 if ( mLastBlockList && ( l == mLastBlockList || ( l && mLastBlockList && l->format().style() == mLastBlockList->format().style() ) ) )
616 const QTextListFormat listFormat = l->format();
617 if ( listFormat.style() == QTextListFormat::ListDisc )
619 mActionBulletList->setChecked(
true );
620 mActionOrderedList->setChecked(
false );
622 else if ( listFormat.style() == QTextListFormat::ListDecimal )
624 mActionBulletList->setChecked(
false );
625 mActionOrderedList->setChecked(
true );
629 mActionBulletList->setChecked(
false );
630 mActionOrderedList->setChecked(
false );
635 mActionBulletList->setChecked(
false );
636 mActionOrderedList->setChecked(
false );
640void QgsRichTextEditor::fontChanged(
const QFont &f )
642 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( f.pointSize() ) );
643 mActionBold->setChecked( f.bold() );
644 mActionItalic->setChecked( f.italic() );
645 mActionUnderline->setChecked( f.underline() );
646 mActionStrikeOut->setChecked( f.strikeOut() );
647 if ( f.bold() && f.pointSize() == mFontSizeH1 )
649 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading1 );
651 else if ( f.bold() && f.pointSize() == mFontSizeH2 )
653 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading2 );
655 else if ( f.bold() && f.pointSize() == mFontSizeH3 )
657 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading3 );
659 else if ( f.bold() && f.pointSize() == mFontSizeH4 )
661 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading4 );
665 if ( f.fixedPitch() && f.family() == mMonospaceFontFamily )
667 mParagraphStyleCombo->setCurrentIndex( ParagraphMonospace );
671 mParagraphStyleCombo->setCurrentIndex( ParagraphStandard );
674 if ( mTextEdit->textCursor().currentList() )
676 const QTextListFormat listFormat = mTextEdit->textCursor().currentList()->format();
677 if ( listFormat.style() == QTextListFormat::ListDisc )
679 mActionBulletList->setChecked(
true );
680 mActionOrderedList->setChecked(
false );
682 else if ( listFormat.style() == QTextListFormat::ListDecimal )
684 mActionBulletList->setChecked(
false );
685 mActionOrderedList->setChecked(
true );
689 mActionBulletList->setChecked(
false );
690 mActionOrderedList->setChecked(
false );
695 mActionBulletList->setChecked(
false );
696 mActionOrderedList->setChecked(
false );
700void QgsRichTextEditor::fgColorChanged(
const QColor &
c )
705void QgsRichTextEditor::bgColorChanged(
const QColor &
c )
713void QgsRichTextEditor::slotCurrentCharFormatChanged(
const QTextCharFormat &format )
715 fontChanged( format.font() );
716 bgColorChanged( ( format.background().isOpaque() ) ? format.background().color() : QColor() );
717 fgColorChanged( ( format.foreground().isOpaque() ) ? format.foreground().color() : palette().windowText().color() );
718 mActionInsertLink->setChecked( format.isAnchor() );
721void QgsRichTextEditor::slotClipboardDataChanged()
723#ifndef QT_NO_CLIPBOARD
724 if (
const QMimeData *md = QApplication::clipboard()->mimeData() )
725 mActionPaste->setEnabled( md->hasText() );
729void QgsRichTextEditor::increaseIndentation()
734void QgsRichTextEditor::decreaseIndentation()
739void QgsRichTextEditor::indent(
int delta )
741 QTextCursor cursor = mTextEdit->textCursor();
742 cursor.beginEditBlock();
743 QTextBlockFormat format = cursor.blockFormat();
744 const int indent = format.indent();
745 if ( indent + delta >= 0 )
747 format.setIndent( indent + delta );
749 cursor.setBlockFormat( format );
750 cursor.endEditBlock();
755 if ( text.isEmpty() )
757 mTextEdit->setPlainText( text );
758 mSourceEdit->clear();
762 const thread_local QRegularExpression sIsHtmlRx( QStringLiteral(
"^\\s*<" ) );
763 if ( sIsHtmlRx.match( text ).hasMatch() )
765 mTextEdit->setHtml( text );
766 mSourceEdit->setText( text );
770 mTextEdit->setPlainText( text );
771 mSourceEdit->setText( text );
775void QgsRichTextEditor::insertImage()
778 const QString attdir = s.value( QStringLiteral(
"general/filedialog-path" ) ).toString();
779 const QString file = QFileDialog::getOpenFileName(
this, tr(
"Select an image" ), attdir, tr(
"JPEG (*.jpg);; GIF (*.gif);; PNG (*.png);; BMP (*.bmp);; All (*)" ) );
780 if ( file.isEmpty() )
783 const QImage image = QImageReader( file ).read();
785 mTextEdit->dropImage( image, QFileInfo( file ).suffix().toUpper().toLocal8Bit().data() );
A HTML editor based on QScintilla2.
void setText(const QString &text) override
static QFont getMonospaceFont()
Returns the monospaced font to use for code editors.
QgsRichTextEditor(QWidget *parent=nullptr)
Constructor for QgsRichTextEditor, with the specified parent widget.
QString toPlainText() const
Returns the widget's content as a plain text string.
Mode mode() const
Returns the widget's mode, which defines which formatting options are exposed in the widget.
void setMode(Mode mode)
Sets the widget's mode, which defines which formatting options are exposed in the widget.
@ QTextDocument
Default mode, exposes the Qt supported HTML/CSS subset.
@ PlainText
Plain text mode.
QString toHtml() const
Returns the widget's content as a HTML string.
void textChanged()
Emitted when the text contents are changed.
void clearSource()
Clears the current text from the widget.
void focusInEvent(QFocusEvent *event) override
void setText(const QString &text)
Sets the text to show in the widget.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.