39#include <QApplication>
43#include <QFontDatabase>
44#include <QImageReader>
45#include <QInputDialog>
55#include "moc_qgsrichtexteditor.cpp"
57using namespace Qt::StringLiterals;
66 QVBoxLayout *sourceLayout =
new QVBoxLayout();
67 sourceLayout->setContentsMargins( 0, 0, 0, 0 );
70 sourceLayout->addWidget( codeEditorWidget );
71 mPageSourceEdit->setLayout( sourceLayout );
75 connect( mTextEdit, &QTextEdit::currentCharFormatChanged,
this, &QgsRichTextEditor::slotCurrentCharFormatChanged );
76 connect( mTextEdit, &QTextEdit::cursorPositionChanged,
this, &QgsRichTextEditor::slotCursorPositionChanged );
79 mActionUndo->setShortcut( QKeySequence::Undo );
80 mActionRedo->setShortcut( QKeySequence::Redo );
82 connect( mTextEdit->document(), &QTextDocument::undoAvailable, mActionUndo, &QAction::setEnabled );
83 connect( mTextEdit->document(), &QTextDocument::redoAvailable, mActionRedo, &QAction::setEnabled );
85 mActionUndo->setEnabled( mTextEdit->document()->isUndoAvailable() );
86 mActionRedo->setEnabled( mTextEdit->document()->isRedoAvailable() );
88 connect( mActionUndo, &QAction::triggered, mTextEdit, &QTextEdit::undo );
89 connect( mActionRedo, &QAction::triggered, mTextEdit, &QTextEdit::redo );
92 mActionCut->setShortcut( QKeySequence::Cut );
93 mActionCopy->setShortcut( QKeySequence::Copy );
94 mActionPaste->setShortcut( QKeySequence::Paste );
96 mActionCut->setEnabled(
false );
97 mActionCopy->setEnabled(
false );
99 connect( mActionCut, &QAction::triggered, mTextEdit, &QTextEdit::cut );
100 connect( mActionCopy, &QAction::triggered, mTextEdit, &QTextEdit::copy );
101 connect( mActionPaste, &QAction::triggered, mTextEdit, &QTextEdit::paste );
103 connect( mTextEdit, &QTextEdit::copyAvailable, mActionCut, &QAction::setEnabled );
104 connect( mTextEdit, &QTextEdit::copyAvailable, mActionCopy, &QAction::setEnabled );
106#ifndef QT_NO_CLIPBOARD
107 connect( QApplication::clipboard(), &QClipboard::dataChanged,
this, &QgsRichTextEditor::slotClipboardDataChanged );
111 mActionInsertLink->setShortcut( QKeySequence( u
"CTRL+L"_s ) );
112 connect( mActionInsertLink, &QAction::triggered,
this, &QgsRichTextEditor::textLink );
115 mActionBold->setShortcut( QKeySequence( u
"CTRL+B"_s ) );
116 mActionItalic->setShortcut( QKeySequence( u
"CTRL+I"_s ) );
117 mActionUnderline->setShortcut( QKeySequence( u
"CTRL+U"_s ) );
119 connect( mActionBold, &QAction::triggered,
this, &QgsRichTextEditor::textBold );
120 connect( mActionItalic, &QAction::triggered,
this, &QgsRichTextEditor::textItalic );
121 connect( mActionUnderline, &QAction::triggered,
this, &QgsRichTextEditor::textUnderline );
122 connect( mActionStrikeOut, &QAction::triggered,
this, &QgsRichTextEditor::textStrikeout );
125 mActionBulletList->setShortcut( QKeySequence( u
"CTRL+-"_s ) );
126 mActionOrderedList->setShortcut( QKeySequence( u
"CTRL+="_s ) );
127 connect( mActionBulletList, &QAction::triggered,
this, &QgsRichTextEditor::listBullet );
128 connect( mActionOrderedList, &QAction::triggered,
this, &QgsRichTextEditor::listOrdered );
131 mActionDecreaseIndent->setShortcut( QKeySequence( u
"CTRL+,"_s ) );
132 mActionIncreaseIndent->setShortcut( QKeySequence( u
"CTRL+."_s ) );
133 connect( mActionIncreaseIndent, &QAction::triggered,
this, &QgsRichTextEditor::increaseIndentation );
134 connect( mActionDecreaseIndent, &QAction::triggered,
this, &QgsRichTextEditor::decreaseIndentation );
136 connect( mActionEditSource, &QAction::toggled,
this, &QgsRichTextEditor::editSource );
139 connect( mActionInsertImage, &QAction::triggered,
this, &QgsRichTextEditor::insertImage );
143 fontChanged( mTextEdit->font() );
153 delete mFontSizeCombo;
154 mFontSizeCombo =
nullptr;
155 delete mParagraphStyleCombo;
156 mParagraphStyleCombo =
nullptr;
157 delete mForeColorButton;
158 mForeColorButton =
nullptr;
159 delete mBackColorButton;
160 mBackColorButton =
nullptr;
165 mParagraphStyleCombo =
new QComboBox();
166 mParagraphStyleCombo->addItem( tr(
"Standard" ), ParagraphStandard );
167 mParagraphStyleCombo->addItem( tr(
"Heading 1" ), ParagraphHeading1 );
168 mParagraphStyleCombo->addItem( tr(
"Heading 2" ), ParagraphHeading2 );
169 mParagraphStyleCombo->addItem( tr(
"Heading 3" ), ParagraphHeading3 );
170 mParagraphStyleCombo->addItem( tr(
"Heading 4" ), ParagraphHeading4 );
171 mParagraphStyleCombo->addItem( tr(
"Monospace" ), ParagraphMonospace );
173 connect( mParagraphStyleCombo, qOverload<int>( &QComboBox::activated ),
this, &QgsRichTextEditor::textStyle );
176 mToolBar->addWidget( mParagraphStyleCombo );
179 mToolBar->addAction( mActionUndo );
180 mToolBar->addAction( mActionRedo );
181 mToolBar->addAction( mActionCut );
182 mToolBar->addAction( mActionCopy );
183 mToolBar->addAction( mActionPaste );
186 mFontSizeCombo =
new QComboBox();
187 mFontSizeCombo->setEditable(
true );
189 const QList<int> sizes = QFontDatabase::standardSizes();
190 for (
const int size : sizes )
191 mFontSizeCombo->addItem( QString::number( size ), size );
193 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( QApplication::font().pointSize() ) );
194 connect( mFontSizeCombo, &QComboBox::textActivated,
this, &QgsRichTextEditor::textSize );
198 mToolBar->addSeparator();
199 mToolBar->addWidget( mFontSizeCombo );
200 mToolBar->addAction( mActionBold );
201 mToolBar->addAction( mActionItalic );
202 mToolBar->addAction( mActionUnderline );
203 mToolBar->addAction( mActionStrikeOut );
205 mToolBar->addSeparator();
210 mForeColorButton->setAllowOpacity(
false );
211 mForeColorButton->setColorDialogTitle( tr(
"Foreground Color" ) );
212 mForeColorButton->setColor( palette().windowText().color() );
213 mForeColorButton->setShowNoColor(
false );
214 mForeColorButton->setToolTip( tr(
"Foreground color" ) );
215 mForeColorButton->setMinimumWidth( QFontMetrics( font() ).horizontalAdvance( u
"x"_s ) * 10 );
216 mForeColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
222 mToolBar->addWidget( mForeColorButton );
227 mBackColorButton->setAllowOpacity(
false );
228 mBackColorButton->setColorDialogTitle( tr(
"Background Color" ) );
229 mBackColorButton->setToolTip( tr(
"Background color" ) );
230 mBackColorButton->setShowNull(
true, tr(
"No Background Color" ) );
231 mBackColorButton->setToNull();
232 mBackColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
233 mBackColorButton->setMinimumWidth( QFontMetrics( font() ).horizontalAdvance( u
"x"_s ) * 10 );
238 mToolBar->addWidget( mBackColorButton );
239 mToolBar->addAction( mActionBulletList );
240 mToolBar->addAction( mActionOrderedList );
241 mToolBar->addAction( mActionDecreaseIndent );
242 mToolBar->addAction( mActionIncreaseIndent );
243 mToolBar->addSeparator();
244 mToolBar->addAction( mActionInsertLink );
245 mToolBar->addAction( mActionInsertImage );
249 mToolBar->addAction( mActionEditSource );
252 QMenu *menu =
new QMenu(
this );
255 QAction *removeFormat =
new QAction( tr(
"Remove Character Formatting" ), menu );
256 removeFormat->setShortcut( QKeySequence( u
"CTRL+M"_s ) );
257 connect( removeFormat, &QAction::triggered,
this, &QgsRichTextEditor::textRemoveFormat );
258 mTextEdit->addAction( removeFormat );
260 QAction *removeAllFormat =
new QAction( tr(
"Remove all Formatting" ), menu );
261 connect( removeAllFormat, &QAction::triggered,
this, &QgsRichTextEditor::textRemoveAllFormat );
262 mTextEdit->addAction( removeAllFormat );
263 menu->addAction( removeAllFormat );
264 menu->addAction( removeFormat );
267 QAction *clearText =
new QAction( tr(
"Clear all Content" ), menu );
269 mTextEdit->addAction( clearText );
271 menu->addAction( clearText );
273 QToolButton *menuButton =
new QToolButton( mToolBar );
274 menuButton->setMenu( menu );
275 menuButton->setPopupMode( QToolButton::InstantPopup );
276 menuButton->setToolTip( tr(
"Advanced Options" ) );
277 menuButton->setText( u
"…"_s );
279 QWidget *spacer =
new QWidget();
280 spacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
281 mToolBar->addWidget( spacer );
282 mToolBar->addWidget( menuButton );
292 switch ( mStackedWidget->currentIndex() )
295 return mTextEdit->toPlainText();
299 mTextEdit->setText( mSourceEdit->text() );
300 return mTextEdit->toPlainText();
310 switch ( mStackedWidget->currentIndex() )
313 return mTextEdit->toHtml();
316 return mSourceEdit->text();
324void QgsRichTextEditor::editSource(
bool enabled )
326 if ( enabled && mStackedWidget->currentIndex() == 0 )
328 mSourceEdit->
setText( mTextEdit->toHtml() );
329 mStackedWidget->setCurrentIndex( 1 );
331 else if ( !enabled && mStackedWidget->currentIndex() == 1 )
333 mTextEdit->setHtml( mSourceEdit->text() );
334 mStackedWidget->setCurrentIndex( 0 );
335 mSourceEdit->clear();
339 mFontSizeCombo->setEnabled( !enabled );
340 mParagraphStyleCombo->setEnabled( !enabled );
341 mForeColorButton->setEnabled( !enabled );
342 mBackColorButton->setEnabled( !enabled );
343 mActionUndo->setEnabled( !enabled );
344 mActionRedo->setEnabled( !enabled );
345 mActionCut->setEnabled( !enabled );
346 mActionCopy->setEnabled( !enabled );
347 mActionPaste->setEnabled( !enabled );
348 mActionInsertLink->setEnabled( !enabled );
349 mActionBold->setEnabled( !enabled );
350 mActionItalic->setEnabled( !enabled );
351 mActionUnderline->setEnabled( !enabled );
352 mActionStrikeOut->setEnabled( !enabled );
353 mActionBulletList->setEnabled( !enabled );
354 mActionOrderedList->setEnabled( !enabled );
355 mActionDecreaseIndent->setEnabled( !enabled );
356 mActionIncreaseIndent->setEnabled( !enabled );
357 mActionInsertImage->setEnabled( !enabled );
365void QgsRichTextEditor::textRemoveFormat()
367 QTextCharFormat format;
368 format.setFontWeight( QFont::Normal );
369 format.setFontUnderline(
false );
370 format.setFontStrikeOut(
false );
371 format.setFontItalic(
false );
372 format.setFontPointSize( 9 );
374 mActionBold->setChecked(
false );
375 mActionUnderline->setChecked(
false );
376 mActionItalic->setChecked(
false );
377 mActionStrikeOut->setChecked(
false );
378 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( 9 ) );
380 format.clearBackground();
382 mergeFormatOnWordOrSelection( format );
385void QgsRichTextEditor::textRemoveAllFormat()
387 mActionBold->setChecked(
false );
388 mActionUnderline->setChecked(
false );
389 mActionItalic->setChecked(
false );
390 mActionStrikeOut->setChecked(
false );
391 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( 9 ) );
392 const QString text = mTextEdit->toPlainText();
393 mTextEdit->setPlainText( text );
396void QgsRichTextEditor::textBold()
398 QTextCharFormat format;
399 format.setFontWeight( mActionBold->isChecked() ? QFont::Bold : QFont::Normal );
400 mergeFormatOnWordOrSelection( format );
405 mTextEdit->setFocus( Qt::TabFocusReason );
408void QgsRichTextEditor::textUnderline()
410 QTextCharFormat format;
411 format.setFontUnderline( mActionUnderline->isChecked() );
412 mergeFormatOnWordOrSelection( format );
415void QgsRichTextEditor::textItalic()
417 QTextCharFormat format;
418 format.setFontItalic( mActionItalic->isChecked() );
419 mergeFormatOnWordOrSelection( format );
422void QgsRichTextEditor::textStrikeout()
424 QTextCharFormat format;
425 format.setFontStrikeOut( mActionStrikeOut->isChecked() );
426 mergeFormatOnWordOrSelection( format );
429void QgsRichTextEditor::textSize(
const QString &p )
431 const qreal pointSize = p.toDouble();
432 if ( p.toFloat() > 0 )
434 QTextCharFormat format;
435 format.setFontPointSize( pointSize );
436 mergeFormatOnWordOrSelection( format );
440void QgsRichTextEditor::textLink(
bool checked )
443 QTextCharFormat format;
446 const QString url = mTextEdit->currentCharFormat().anchorHref();
448 const QString newUrl = QInputDialog::getText(
this, tr(
"Create a Link" ), tr(
"Link URL:" ), QLineEdit::Normal, url, &ok );
451 format.setAnchor(
true );
452 format.setAnchorHref( newUrl );
453 format.setForeground( palette().color( QPalette::Link ) );
454 format.setFontUnderline(
true );
467 format.setAnchor(
false );
468 format.setForeground( palette().color( QPalette::Text ) );
469 format.setFontUnderline(
false );
471 mergeFormatOnWordOrSelection( format );
474void QgsRichTextEditor::textStyle(
int index )
476 QTextCursor cursor = mTextEdit->textCursor();
477 cursor.beginEditBlock();
480 if ( !cursor.hasSelection() )
482 cursor.select( QTextCursor::BlockUnderCursor );
484 QTextCharFormat format;
485 cursor.setCharFormat( format );
486 mTextEdit->setCurrentCharFormat( format );
488 const ParagraphItems style =
static_cast<ParagraphItems
>( mParagraphStyleCombo->itemData( index ).toInt() );
492 case QgsRichTextEditor::ParagraphStandard:
495 case QgsRichTextEditor::ParagraphHeading1:
496 format.setFontPointSize( mFontSizeH1 );
497 format.setFontWeight( QFont::Bold );
500 case QgsRichTextEditor::ParagraphHeading2:
501 format.setFontPointSize( mFontSizeH2 );
502 format.setFontWeight( QFont::Bold );
503 format.setFontItalic(
true );
506 case QgsRichTextEditor::ParagraphHeading3:
507 format.setFontPointSize( mFontSizeH3 );
508 format.setFontWeight( QFont::Bold );
511 case QgsRichTextEditor::ParagraphHeading4:
512 format.setFontPointSize( mFontSizeH4 );
513 format.setFontWeight( QFont::Bold );
514 format.setFontItalic(
true );
517 case QgsRichTextEditor::ParagraphMonospace:
519 format = cursor.charFormat();
520 format.setFontFamily( mMonospaceFontFamily );
521 format.setFontStyleHint( QFont::Monospace );
522 format.setFontFixedPitch(
true );
527 cursor.setCharFormat( format );
528 mTextEdit->setCurrentCharFormat( format );
530 cursor.endEditBlock();
533void QgsRichTextEditor::textFgColor()
535 QTextCharFormat format;
536 format.setForeground( mForeColorButton->color() );
537 mergeFormatOnWordOrSelection( format );
540void QgsRichTextEditor::textBgColor()
542 QTextCharFormat format;
543 const QColor col = mBackColorButton->color();
546 format.setBackground( col );
550 format.clearBackground();
552 mergeFormatOnWordOrSelection( format );
555void QgsRichTextEditor::listBullet(
bool checked )
559 mActionOrderedList->setChecked(
false );
561 list( checked, QTextListFormat::ListDisc );
564void QgsRichTextEditor::listOrdered(
bool checked )
568 mActionBulletList->setChecked(
false );
570 list( checked, QTextListFormat::ListDecimal );
573void QgsRichTextEditor::list(
bool checked, QTextListFormat::Style style )
575 QTextCursor cursor = mTextEdit->textCursor();
576 cursor.beginEditBlock();
579 const QTextBlockFormat originalFormat = cursor.blockFormat();
580 QTextBlockFormat format;
581 format.setIndent( originalFormat.indent() );
582 cursor.setBlockFormat( format );
586 QTextListFormat listFormat;
587 if ( cursor.currentList() )
589 listFormat = cursor.currentList()->format();
591 listFormat.setStyle( style );
592 cursor.createList( listFormat );
594 cursor.endEditBlock();
597void QgsRichTextEditor::mergeFormatOnWordOrSelection(
const QTextCharFormat &format )
599 QTextCursor cursor = mTextEdit->textCursor();
600 if ( !cursor.hasSelection() )
602 cursor.select( QTextCursor::WordUnderCursor );
604 cursor.mergeCharFormat( format );
605 mTextEdit->mergeCurrentCharFormat( format );
606 mTextEdit->setFocus( Qt::TabFocusReason );
609void QgsRichTextEditor::slotCursorPositionChanged()
611 QTextList *l = mTextEdit->textCursor().currentList();
612 if ( mLastBlockList && ( l == mLastBlockList || ( l && mLastBlockList && l->format().style() == mLastBlockList->format().style() ) ) )
619 const QTextListFormat listFormat = l->format();
620 if ( listFormat.style() == QTextListFormat::ListDisc )
622 mActionBulletList->setChecked(
true );
623 mActionOrderedList->setChecked(
false );
625 else if ( listFormat.style() == QTextListFormat::ListDecimal )
627 mActionBulletList->setChecked(
false );
628 mActionOrderedList->setChecked(
true );
632 mActionBulletList->setChecked(
false );
633 mActionOrderedList->setChecked(
false );
638 mActionBulletList->setChecked(
false );
639 mActionOrderedList->setChecked(
false );
643void QgsRichTextEditor::fontChanged(
const QFont &f )
645 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( f.pointSize() ) );
646 mActionBold->setChecked( f.bold() );
647 mActionItalic->setChecked( f.italic() );
648 mActionUnderline->setChecked( f.underline() );
649 mActionStrikeOut->setChecked( f.strikeOut() );
650 if ( f.bold() && f.pointSize() == mFontSizeH1 )
652 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading1 );
654 else if ( f.bold() && f.pointSize() == mFontSizeH2 )
656 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading2 );
658 else if ( f.bold() && f.pointSize() == mFontSizeH3 )
660 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading3 );
662 else if ( f.bold() && f.pointSize() == mFontSizeH4 )
664 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading4 );
668 if ( f.fixedPitch() && f.family() == mMonospaceFontFamily )
670 mParagraphStyleCombo->setCurrentIndex( ParagraphMonospace );
674 mParagraphStyleCombo->setCurrentIndex( ParagraphStandard );
677 if ( mTextEdit->textCursor().currentList() )
679 const QTextListFormat listFormat = mTextEdit->textCursor().currentList()->format();
680 if ( listFormat.style() == QTextListFormat::ListDisc )
682 mActionBulletList->setChecked(
true );
683 mActionOrderedList->setChecked(
false );
685 else if ( listFormat.style() == QTextListFormat::ListDecimal )
687 mActionBulletList->setChecked(
false );
688 mActionOrderedList->setChecked(
true );
692 mActionBulletList->setChecked(
false );
693 mActionOrderedList->setChecked(
false );
698 mActionBulletList->setChecked(
false );
699 mActionOrderedList->setChecked(
false );
703void QgsRichTextEditor::fgColorChanged(
const QColor &
c )
708void QgsRichTextEditor::bgColorChanged(
const QColor &
c )
716void QgsRichTextEditor::slotCurrentCharFormatChanged(
const QTextCharFormat &format )
718 fontChanged( format.font() );
719 bgColorChanged( ( format.background().isOpaque() ) ? format.background().color() : QColor() );
720 fgColorChanged( ( format.foreground().isOpaque() ) ? format.foreground().color() : palette().windowText().color() );
721 mActionInsertLink->setChecked( format.isAnchor() );
724void QgsRichTextEditor::slotClipboardDataChanged()
726#ifndef QT_NO_CLIPBOARD
727 if (
const QMimeData *md = QApplication::clipboard()->mimeData() )
728 mActionPaste->setEnabled( md->hasText() );
732void QgsRichTextEditor::increaseIndentation()
737void QgsRichTextEditor::decreaseIndentation()
742void QgsRichTextEditor::indent(
int delta )
744 QTextCursor cursor = mTextEdit->textCursor();
745 cursor.beginEditBlock();
746 QTextBlockFormat format = cursor.blockFormat();
747 const int indent = format.indent();
748 if ( indent + delta >= 0 )
750 format.setIndent( indent + delta );
752 cursor.setBlockFormat( format );
753 cursor.endEditBlock();
758 if ( text.isEmpty() )
760 mTextEdit->setPlainText( text );
761 mSourceEdit->clear();
765 const thread_local QRegularExpression sIsHtmlRx( u
"^\\s*<"_s );
766 if ( sIsHtmlRx.match( text ).hasMatch() )
768 mTextEdit->setHtml( text );
769 mSourceEdit->setText( text );
773 mTextEdit->setPlainText( text );
774 mSourceEdit->setText( text );
778void QgsRichTextEditor::insertImage()
781 const QString attdir = s.value( u
"general/filedialog-path"_s ).toString();
782 const QString file = QFileDialog::getOpenFileName(
this, tr(
"Select an image" ), attdir, tr(
"JPEG (*.jpg);; GIF (*.gif);; PNG (*.png);; BMP (*.bmp);; All (*)" ) );
783 if ( file.isEmpty() )
786 const QImage image = QImageReader( file ).read();
788 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.