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() );