38 #include <QApplication>
40 #include <QFontDatabase>
41 #include <QInputDialog>
44 #include <QFileDialog>
45 #include <QImageReader>
50 #include <QToolButton>
59 QVBoxLayout *sourceLayout =
new QVBoxLayout();
60 sourceLayout->setContentsMargins( 0, 0, 0, 0 );
62 sourceLayout->addWidget( mSourceEdit );
63 mPageSourceEdit->setLayout( sourceLayout );
67 connect( mTextEdit, &QTextEdit::currentCharFormatChanged,
this, &QgsRichTextEditor::slotCurrentCharFormatChanged );
68 connect( mTextEdit, &QTextEdit::cursorPositionChanged,
this, &QgsRichTextEditor::slotCursorPositionChanged );
71 mParagraphStyleCombo =
new QComboBox();
72 mParagraphStyleCombo->addItem( tr(
"Standard" ), ParagraphStandard );
73 mParagraphStyleCombo->addItem( tr(
"Heading 1" ), ParagraphHeading1 );
74 mParagraphStyleCombo->addItem( tr(
"Heading 2" ), ParagraphHeading2 );
75 mParagraphStyleCombo->addItem( tr(
"Heading 3" ), ParagraphHeading3 );
76 mParagraphStyleCombo->addItem( tr(
"Heading 4" ), ParagraphHeading4 );
77 mParagraphStyleCombo->addItem( tr(
"Monospace" ), ParagraphMonospace );
79 connect( mParagraphStyleCombo, qOverload< int >( &QComboBox::activated ),
this, &QgsRichTextEditor::textStyle );
80 mToolBar->insertWidget( mToolBar->actions().at( 0 ), mParagraphStyleCombo );
82 mFontSizeCombo =
new QComboBox();
83 mFontSizeCombo->setEditable(
true );
84 mToolBar->insertWidget( mActionBold, mFontSizeCombo );
87 mActionUndo->setShortcut( QKeySequence::Undo );
88 mActionRedo->setShortcut( QKeySequence::Redo );
90 connect( mTextEdit->document(), &QTextDocument::undoAvailable, mActionUndo, &QAction::setEnabled );
91 connect( mTextEdit->document(), &QTextDocument::redoAvailable, mActionRedo, &QAction::setEnabled );
93 mActionUndo->setEnabled( mTextEdit->document()->isUndoAvailable() );
94 mActionRedo->setEnabled( mTextEdit->document()->isRedoAvailable() );
96 connect( mActionUndo, &QAction::triggered, mTextEdit, &QTextEdit::undo );
97 connect( mActionRedo, &QAction::triggered, mTextEdit, &QTextEdit::redo );
100 mActionCut->setShortcut( QKeySequence::Cut );
101 mActionCopy->setShortcut( QKeySequence::Copy );
102 mActionPaste->setShortcut( QKeySequence::Paste );
104 mActionCut->setEnabled(
false );
105 mActionCopy->setEnabled(
false );
107 connect( mActionCut, &QAction::triggered, mTextEdit, &QTextEdit::cut );
108 connect( mActionCopy, &QAction::triggered, mTextEdit, &QTextEdit::copy );
109 connect( mActionPaste, &QAction::triggered, mTextEdit, &QTextEdit::paste );
111 connect( mTextEdit, &QTextEdit::copyAvailable, mActionCut, &QAction::setEnabled );
112 connect( mTextEdit, &QTextEdit::copyAvailable, mActionCopy, &QAction::setEnabled );
114 #ifndef QT_NO_CLIPBOARD
115 connect( QApplication::clipboard(), &QClipboard::dataChanged,
this, &QgsRichTextEditor::slotClipboardDataChanged );
119 mActionInsertLink->setShortcut( QKeySequence( QStringLiteral(
"CTRL+L" ) ) );
120 connect( mActionInsertLink, &QAction::triggered,
this, &QgsRichTextEditor::textLink );
123 mActionBold->setShortcut( QKeySequence( QStringLiteral(
"CTRL+B" ) ) );
124 mActionItalic->setShortcut( QKeySequence( QStringLiteral(
"CTRL+I" ) ) );
125 mActionUnderline->setShortcut( QKeySequence( QStringLiteral(
"CTRL+U" ) ) );
127 connect( mActionBold, &QAction::triggered,
this, &QgsRichTextEditor::textBold );
128 connect( mActionItalic, &QAction::triggered,
this, &QgsRichTextEditor::textItalic );
129 connect( mActionUnderline, &QAction::triggered,
this, &QgsRichTextEditor::textUnderline );
130 connect( mActionStrikeOut, &QAction::triggered,
this, &QgsRichTextEditor::textStrikeout );
132 QAction *removeFormat =
new QAction( tr(
"Remove Character Formatting" ),
this );
133 removeFormat->setShortcut( QKeySequence( QStringLiteral(
"CTRL+M" ) ) );
134 connect( removeFormat, &QAction::triggered,
this, &QgsRichTextEditor::textRemoveFormat );
135 mTextEdit->addAction( removeFormat );
137 QAction *removeAllFormat =
new QAction( tr(
"Remove all Formatting" ),
this );
138 connect( removeAllFormat, &QAction::triggered,
this, &QgsRichTextEditor::textRemoveAllFormat );
139 mTextEdit->addAction( removeAllFormat );
141 QAction *clearText =
new QAction( tr(
"Clear all Content" ),
this );
143 mTextEdit->addAction( clearText );
145 QMenu *menu =
new QMenu(
this );
146 menu->addAction( removeAllFormat );
147 menu->addAction( removeFormat );
148 menu->addAction( clearText );
150 QToolButton *menuButton =
new QToolButton();
151 menuButton->setMenu( menu );
152 menuButton->setPopupMode( QToolButton::InstantPopup );
153 menuButton->setToolTip( tr(
"Advanced Options" ) );
154 menuButton->setText( QStringLiteral(
"…" ) );
155 QWidget *spacer =
new QWidget();
156 spacer->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
157 mToolBar->addWidget( spacer );
158 mToolBar->addWidget( menuButton );
161 mActionBulletList->setShortcut( QKeySequence( QStringLiteral(
"CTRL+-" ) ) );
162 mActionOrderedList->setShortcut( QKeySequence( QStringLiteral(
"CTRL+=" ) ) );
163 connect( mActionBulletList, &QAction::triggered,
this, &QgsRichTextEditor::listBullet );
164 connect( mActionOrderedList, &QAction::triggered,
this, &QgsRichTextEditor::listOrdered );
167 mActionDecreaseIndent->setShortcut( QKeySequence( QStringLiteral(
"CTRL+," ) ) );
168 mActionIncreaseIndent->setShortcut( QKeySequence( QStringLiteral(
"CTRL+." ) ) );
169 connect( mActionIncreaseIndent, &QAction::triggered,
this, &QgsRichTextEditor::increaseIndentation );
170 connect( mActionDecreaseIndent, &QAction::triggered,
this, &QgsRichTextEditor::decreaseIndentation );
173 const QList< int > sizes = QFontDatabase::standardSizes();
174 for (
const int size : sizes )
175 mFontSizeCombo->addItem( QString::number( size ), size );
177 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( QApplication::font().pointSize() ) );
183 mForeColorButton->
setColor( palette().windowText().color() );
185 mForeColorButton->setToolTip( tr(
"Foreground color" ) );
186 mForeColorButton->setMinimumWidth( QFontMetrics( font() ).horizontalAdvance( QStringLiteral(
"x" ) ) * 10 );
187 mForeColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
189 QAction *listSeparator = mToolBar->insertSeparator( mActionBulletList );
192 mToolBar->insertWidget( listSeparator, mForeColorButton );
198 mBackColorButton->setToolTip( tr(
"Background color" ) );
199 mBackColorButton->
setShowNull(
true, tr(
"No Background Color" ) );
201 mBackColorButton->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
202 mBackColorButton->setMinimumWidth( QFontMetrics( font() ).horizontalAdvance( QStringLiteral(
"x" ) ) * 10 );
204 mToolBar->insertWidget( listSeparator, mBackColorButton );
206 connect( mActionEditSource, &QAction::toggled,
this, &QgsRichTextEditor::editSource );
209 connect( mActionInsertImage, &QAction::triggered,
this, &QgsRichTextEditor::insertImage );
210 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
211 connect( mFontSizeCombo, qOverload< const QString &>( &QComboBox::activated ),
this, &QgsRichTextEditor::textSize );
213 connect( mFontSizeCombo, &QComboBox::textActivated,
this, &QgsRichTextEditor::textSize );
216 fontChanged( mTextEdit->font() );
224 switch ( mStackedWidget->currentIndex() )
227 return mTextEdit->toPlainText();
231 mTextEdit->setText( mSourceEdit->text() );
232 return mTextEdit->toPlainText();
239 switch ( mStackedWidget->currentIndex() )
242 return mTextEdit->toHtml();
245 return mSourceEdit->text();
250 void QgsRichTextEditor::editSource(
bool enabled )
254 mSourceEdit->setText( mTextEdit->toHtml() );
255 mStackedWidget->setCurrentIndex( 1 );
259 mTextEdit->setHtml( mSourceEdit->text() );
260 mStackedWidget->setCurrentIndex( 0 );
261 mSourceEdit->clear();
265 mFontSizeCombo->setEnabled( !enabled );
266 mParagraphStyleCombo->setEnabled( !enabled );
267 mForeColorButton->setEnabled( !enabled );
268 mBackColorButton->setEnabled( !enabled );
269 mActionUndo->setEnabled( !enabled );
270 mActionRedo->setEnabled( !enabled );
271 mActionCut->setEnabled( !enabled );
272 mActionCopy->setEnabled( !enabled );
273 mActionPaste->setEnabled( !enabled );
274 mActionInsertLink->setEnabled( !enabled );
275 mActionBold->setEnabled( !enabled );
276 mActionItalic->setEnabled( !enabled );
277 mActionUnderline->setEnabled( !enabled );
278 mActionStrikeOut->setEnabled( !enabled );
279 mActionBulletList->setEnabled( !enabled );
280 mActionOrderedList->setEnabled( !enabled );
281 mActionDecreaseIndent->setEnabled( !enabled );
282 mActionIncreaseIndent->setEnabled( !enabled );
283 mActionInsertImage->setEnabled( !enabled );
291 void QgsRichTextEditor::textRemoveFormat()
293 QTextCharFormat format;
294 format.setFontWeight( QFont::Normal );
295 format.setFontUnderline(
false );
296 format.setFontStrikeOut(
false );
297 format.setFontItalic(
false );
298 format.setFontPointSize( 9 );
300 mActionBold->setChecked(
false );
301 mActionUnderline->setChecked(
false );
302 mActionItalic->setChecked(
false );
303 mActionStrikeOut->setChecked(
false );
304 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( 9 ) );
306 format.clearBackground();
308 mergeFormatOnWordOrSelection( format );
311 void QgsRichTextEditor::textRemoveAllFormat()
313 mActionBold->setChecked(
false );
314 mActionUnderline->setChecked(
false );
315 mActionItalic->setChecked(
false );
316 mActionStrikeOut->setChecked(
false );
317 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( 9 ) );
318 const QString text = mTextEdit->toPlainText();
319 mTextEdit->setPlainText( text );
322 void QgsRichTextEditor::textBold()
324 QTextCharFormat format;
325 format.setFontWeight( mActionBold->isChecked() ? QFont::Bold : QFont::Normal );
326 mergeFormatOnWordOrSelection( format );
331 mTextEdit->setFocus( Qt::TabFocusReason );
334 void QgsRichTextEditor::textUnderline()
336 QTextCharFormat format;
337 format.setFontUnderline( mActionUnderline->isChecked() );
338 mergeFormatOnWordOrSelection( format );
341 void QgsRichTextEditor::textItalic()
343 QTextCharFormat format;
344 format.setFontItalic( mActionItalic->isChecked() );
345 mergeFormatOnWordOrSelection( format );
348 void QgsRichTextEditor::textStrikeout()
350 QTextCharFormat format;
351 format.setFontStrikeOut( mActionStrikeOut->isChecked() );
352 mergeFormatOnWordOrSelection( format );
355 void QgsRichTextEditor::textSize(
const QString &p )
357 const qreal pointSize = p.toDouble();
358 if ( p.toFloat() > 0 )
360 QTextCharFormat format;
361 format.setFontPointSize( pointSize );
362 mergeFormatOnWordOrSelection( format );
366 void QgsRichTextEditor::textLink(
bool checked )
369 QTextCharFormat format;
372 const QString url = mTextEdit->currentCharFormat().anchorHref();
374 const QString newUrl = QInputDialog::getText(
this, tr(
"Create a Link" ),
375 tr(
"Link URL:" ), QLineEdit::Normal,
380 format.setAnchor(
true );
381 format.setAnchorHref( newUrl );
382 format.setForeground( palette().color( QPalette::Link ) );
383 format.setFontUnderline(
true );
396 format.setAnchor(
false );
397 format.setForeground( palette().color( QPalette::Text ) );
398 format.setFontUnderline(
false );
400 mergeFormatOnWordOrSelection( format );
403 void QgsRichTextEditor::textStyle(
int )
405 QTextCursor cursor = mTextEdit->textCursor();
406 cursor.beginEditBlock();
409 if ( !cursor.hasSelection() )
411 cursor.select( QTextCursor::BlockUnderCursor );
413 QTextCharFormat format;
414 cursor.setCharFormat( format );
415 mTextEdit->setCurrentCharFormat( format );
417 const ParagraphItems style =
static_cast< ParagraphItems
>( mParagraphStyleCombo->currentData().toInt() );
421 case QgsRichTextEditor::ParagraphStandard:
424 case QgsRichTextEditor::ParagraphHeading1:
425 format.setFontPointSize( mFontSizeH1 );
426 format.setFontWeight( QFont::Bold );
429 case QgsRichTextEditor::ParagraphHeading2:
430 format.setFontPointSize( mFontSizeH2 );
431 format.setFontWeight( QFont::Bold );
432 format.setFontItalic(
true );
435 case QgsRichTextEditor::ParagraphHeading3:
436 format.setFontPointSize( mFontSizeH3 );
437 format.setFontWeight( QFont::Bold );
440 case QgsRichTextEditor::ParagraphHeading4:
441 format.setFontPointSize( mFontSizeH4 );
442 format.setFontWeight( QFont::Bold );
443 format.setFontItalic(
true );
446 case QgsRichTextEditor::ParagraphMonospace:
448 format = cursor.charFormat();
449 format.setFontFamily( mMonospaceFontFamily );
450 format.setFontStyleHint( QFont::Monospace );
451 format.setFontFixedPitch(
true );
456 cursor.setCharFormat( format );
457 mTextEdit->setCurrentCharFormat( format );
459 cursor.endEditBlock();
462 void QgsRichTextEditor::textFgColor()
464 QTextCharFormat format;
465 format.setForeground( mForeColorButton->
color() );
466 mergeFormatOnWordOrSelection( format );
469 void QgsRichTextEditor::textBgColor()
471 QTextCharFormat format;
472 const QColor col = mBackColorButton->
color();
475 format.setBackground( col );
479 format.clearBackground();
481 mergeFormatOnWordOrSelection( format );
484 void QgsRichTextEditor::listBullet(
bool checked )
488 mActionOrderedList->setChecked(
false );
490 list( checked, QTextListFormat::ListDisc );
493 void QgsRichTextEditor::listOrdered(
bool checked )
497 mActionBulletList->setChecked(
false );
499 list( checked, QTextListFormat::ListDecimal );
502 void QgsRichTextEditor::list(
bool checked, QTextListFormat::Style style )
504 QTextCursor cursor = mTextEdit->textCursor();
505 cursor.beginEditBlock();
508 const QTextBlockFormat originalFormat = cursor.blockFormat();
509 QTextBlockFormat format;
510 format.setIndent( originalFormat.indent() );
511 cursor.setBlockFormat( format );
515 QTextListFormat listFormat;
516 if ( cursor.currentList() )
518 listFormat = cursor.currentList()->format();
520 listFormat.setStyle( style );
521 cursor.createList( listFormat );
523 cursor.endEditBlock();
526 void QgsRichTextEditor::mergeFormatOnWordOrSelection(
const QTextCharFormat &format )
528 QTextCursor cursor = mTextEdit->textCursor();
529 if ( !cursor.hasSelection() )
531 cursor.select( QTextCursor::WordUnderCursor );
533 cursor.mergeCharFormat( format );
534 mTextEdit->mergeCurrentCharFormat( format );
535 mTextEdit->setFocus( Qt::TabFocusReason );
538 void QgsRichTextEditor::slotCursorPositionChanged()
540 QTextList *l = mTextEdit->textCursor().currentList();
541 if ( mLastBlockList && ( l == mLastBlockList || ( l !=
nullptr && mLastBlockList !=
nullptr
542 && l->format().style() == mLastBlockList->format().style() ) ) )
549 const QTextListFormat listFormat = l->format();
550 if ( listFormat.style() == QTextListFormat::ListDisc )
552 mActionBulletList->setChecked(
true );
553 mActionOrderedList->setChecked(
false );
555 else if ( listFormat.style() == QTextListFormat::ListDecimal )
557 mActionBulletList->setChecked(
false );
558 mActionOrderedList->setChecked(
true );
562 mActionBulletList->setChecked(
false );
563 mActionOrderedList->setChecked(
false );
568 mActionBulletList->setChecked(
false );
569 mActionOrderedList->setChecked(
false );
573 void QgsRichTextEditor::fontChanged(
const QFont &f )
575 mFontSizeCombo->setCurrentIndex( mFontSizeCombo->findData( f.pointSize() ) );
576 mActionBold->setChecked( f.bold() );
577 mActionItalic->setChecked( f.italic() );
578 mActionUnderline->setChecked( f.underline() );
579 mActionStrikeOut->setChecked( f.strikeOut() );
580 if ( f.pointSize() == mFontSizeH1 )
582 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading1 );
584 else if ( f.pointSize() == mFontSizeH2 )
586 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading2 );
588 else if ( f.pointSize() == mFontSizeH3 )
590 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading3 );
592 else if ( f.pointSize() == mFontSizeH4 )
594 mParagraphStyleCombo->setCurrentIndex( ParagraphHeading4 );
598 if ( f.fixedPitch() && f.family() == mMonospaceFontFamily )
600 mParagraphStyleCombo->setCurrentIndex( ParagraphMonospace );
604 mParagraphStyleCombo->setCurrentIndex( ParagraphStandard );
607 if ( mTextEdit->textCursor().currentList() )
609 const QTextListFormat listFormat = mTextEdit->textCursor().currentList()->format();
610 if ( listFormat.style() == QTextListFormat::ListDisc )
612 mActionBulletList->setChecked(
true );
613 mActionOrderedList->setChecked(
false );
615 else if ( listFormat.style() == QTextListFormat::ListDecimal )
617 mActionBulletList->setChecked(
false );
618 mActionOrderedList->setChecked(
true );
622 mActionBulletList->setChecked(
false );
623 mActionOrderedList->setChecked(
false );
628 mActionBulletList->setChecked(
false );
629 mActionOrderedList->setChecked(
false );
633 void QgsRichTextEditor::fgColorChanged(
const QColor &
c )
638 void QgsRichTextEditor::bgColorChanged(
const QColor &
c )
646 void QgsRichTextEditor::slotCurrentCharFormatChanged(
const QTextCharFormat &format )
648 fontChanged( format.font() );
649 bgColorChanged( ( format.background().isOpaque() ) ? format.background().color() : QColor() );
650 fgColorChanged( ( format.foreground().isOpaque() ) ? format.foreground().color() : palette().windowText().color() );
651 mActionInsertLink->setChecked( format.isAnchor() );
654 void QgsRichTextEditor::slotClipboardDataChanged()
656 #ifndef QT_NO_CLIPBOARD
657 if (
const QMimeData *md = QApplication::clipboard()->mimeData() )
658 mActionPaste->setEnabled( md->hasText() );
662 void QgsRichTextEditor::increaseIndentation()
667 void QgsRichTextEditor::decreaseIndentation()
672 void QgsRichTextEditor::indent(
int delta )
674 QTextCursor cursor = mTextEdit->textCursor();
675 cursor.beginEditBlock();
676 QTextBlockFormat format = cursor.blockFormat();
677 const int indent = format.indent();
678 if ( indent + delta >= 0 )
680 format.setIndent( indent + delta );
682 cursor.setBlockFormat( format );
683 cursor.endEditBlock();
688 if ( text.isEmpty() )
690 setPlainText( text );
693 if ( text[0] ==
'<' )
699 setPlainText( text );
703 void QgsRichTextEditor::insertImage()
706 const QString attdir = s.value( QStringLiteral(
"general/filedialog-path" ) ).toString();
707 const QString file = QFileDialog::getOpenFileName(
this,
708 tr(
"Select an image" ),
710 tr(
"JPEG (*.jpg);; GIF (*.gif);; PNG (*.png);; BMP (*.bmp);; All (*)" ) );
711 if ( file.isEmpty() )
714 const QImage image = QImageReader( file ).read();
716 mTextEdit->dropImage( image, QFileInfo( file ).suffix().toUpper().toLocal8Bit().data() );