File indexing completed on 2024-04-21 05:43:43

0001 /***************************************************************************
0002  *   Copyright (C) 2006 by David Saxton - david@bluehaze.org               *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  ***************************************************************************/
0009 
0010 #include "richtexteditor.h"
0011 
0012 #include <KToggleAction>
0013 #include <KToolBarPopupAction>
0014 
0015 #include <KLocalizedString>
0016 // #include <k3popupmenu.h>
0017 #include <KActionCollection>
0018 #include <KTextEdit>
0019 #include <KToolBar>
0020 
0021 #include <QFont>
0022 #include <QIcon>
0023 #include <QTextEdit>
0024 //#include <qmime.h>
0025 #include <QColorDialog>
0026 #include <QDialogButtonBox>
0027 #include <QDir>
0028 #include <QMenu>
0029 #include <QPushButton>
0030 #include <QRegExp>
0031 #include <QTextList>
0032 #include <QTextListFormat>
0033 #include <QVBoxLayout>
0034 
0035 #include <ktechlab_debug.h>
0036 
0037 // #include <q3vbox.h>
0038 // #include <q3textedit.h>
0039 // #include <q3stylesheet.h>
0040 
0041 // BEGIN class RichTextEditor
0042 RichTextEditor::RichTextEditor(QWidget *parent)
0043     : QWidget(parent)
0044 {
0045     QVBoxLayout *layout = new QVBoxLayout(this /*, 0, 6 */);
0046     layout->setMargin(0);
0047     layout->setSpacing(6);
0048     m_pEditor = new QTextEdit(this); //, "RichTextEdit" );
0049     m_pEditor->setObjectName("RichTextEdit");
0050     layout->addWidget(m_pEditor);
0051 
0052     // m_pEditor->setTextFormat( Qt::RichText ); // 2018.12.07 - just use toHtml() and html()
0053 
0054     connect(m_pEditor, &QTextEdit::textChanged, this, &RichTextEditor::textChanged);
0055     connect(m_pEditor, &QTextEdit::currentCharFormatChanged, this, &RichTextEditor::slotCurrentCharFormatChanged);
0056     // connect( m_pEditor, SIGNAL( currentFontChanged( const QFont & ) ), this, SLOT( fontChanged( const QFont & ) ) ); // 2018.01.03 - use slotCurrentCharFormatChanged
0057     // connect( m_pEditor, SIGNAL( currentColorChanged( const QColor & ) ), this, SLOT( colorChanged( const QColor & ) ) ); // 2018.01.03 - use slotCurrentCharFormatChanged
0058     // connect( m_pEditor, SIGNAL( currentAlignmentChanged( int ) ), this, SLOT( alignmentChanged( int ) ) ); // 2018.01.03 - use slotCurrentCharFormatChanged
0059     // connect( m_pEditor, SIGNAL( currentVerticalAlignmentChanged( Q3TextEdit::VerticalAlignment ) ), this, SLOT(verticalAlignmentChanged()) ); // 2018.01.03 - use slotCurrentCharFormatChanged
0060 
0061     KToolBar *tools = new KToolBar(QStringLiteral("RichTextEditorToops"), this);
0062     layout->addWidget(tools);
0063     KActionCollection *ac = new KActionCollection(m_pEditor);
0064 
0065     // m_pTextBold = new KToggleAction( i18n("Bold"), "format-text-bold", Qt::CTRL + Qt::Key_B, 0, 0, ac, "format_bold" );
0066     m_pTextBold = new KToggleAction(i18n("Bold"), ac);
0067     m_pTextBold->setObjectName("text_bold");
0068     m_pTextBold->setShortcut(Qt::CTRL + Qt::Key_B);
0069     m_pTextBold->setIcon(QIcon::fromTheme("format-text-bold"));
0070     connect(m_pTextBold, &KToggleAction::toggled, this, &RichTextEditor::slotSetBold);
0071     // m_pTextBold->plug( tools );
0072     tools->addAction(m_pTextBold);
0073 
0074     // m_pTextItalic = new KToggleAction( i18n("Italic"), "format-text-italic", Qt::CTRL + Qt::Key_I, 0, 0, ac, "format_italic" );
0075     m_pTextItalic = new KToggleAction(i18n("Italic"), ac);
0076     m_pTextItalic->setObjectName("text_italic");
0077     m_pTextItalic->setShortcut(Qt::CTRL + Qt::Key_I);
0078     m_pTextItalic->setIcon(QIcon::fromTheme("format-text-italic"));
0079     connect(m_pTextItalic, &KToggleAction::toggled, this, &RichTextEditor::slotSetItalic);
0080     // m_pTextItalic->plug( tools );
0081     tools->addAction(m_pTextItalic);
0082 
0083     // m_pTextUnderline = new KToggleAction( i18n("Underline"), "format-text-underline", Qt::CTRL + Qt::Key_U, 0, 0, ac, "format_underline" );
0084     m_pTextUnderline = new KToggleAction(i18n("Underline"), ac);
0085     m_pTextUnderline->setObjectName("text_under");
0086     m_pTextUnderline->setShortcut(Qt::CTRL + Qt::Key_U);
0087     m_pTextItalic->setIcon(QIcon::fromTheme("format-text-underline"));
0088     connect(m_pTextUnderline, &KToggleAction::toggled, this, &RichTextEditor::slotSetUnderline);
0089     // m_pTextUnderline->plug( tools );
0090     tools->addAction(m_pTextUnderline);
0091 
0092     // m_pTextList = new KToggleAction( i18n("List"), "unsorted_list", Qt::CTRL + Qt::Key_L, 0, 0, ac, "format_list" );
0093     m_pTextList = new KToggleAction(i18n("List"), ac);
0094     m_pTextList->setObjectName("unsorted_list");
0095     m_pTextList->setShortcut(Qt::CTRL + Qt::Key_L);
0096     m_pTextItalic->setIcon(QIcon::fromTheme("format-list-unordered"));
0097     connect(m_pTextList, &KToggleAction::toggled, this, &RichTextEditor::slotSetList);
0098     // m_pTextList->plug( tools );
0099     tools->addAction(m_pTextList);
0100 
0101     // BEGIN Text horizontal-alignment actions
0102     // m_pTextAlignment = new KToolBarPopupAction( i18n("Text Alignment"), "format-justify-left", 0, 0, 0, ac, "text_alignment" );
0103     m_pTextAlignment = new KToolBarPopupAction(QIcon::fromTheme("format-justify-left"), i18n("Text Alignment"), ac);
0104     m_pTextAlignment->setObjectName("text_left");
0105     // m_pTextAlignment->plug( tools );
0106     tools->addAction(m_pTextAlignment);
0107     m_pTextAlignment->setDelayed(false);
0108 
0109     // K3PopupMenu * m = m_pTextAlignment->menu();
0110     QMenu *m = m_pTextAlignment->menu();
0111     // m->insertTitle( i18n("Text Alignment") );
0112     m->setTitle(i18n("Text Alignment"));
0113     // m->setCheckable( true ); // 2018.12.07
0114 
0115     // m->insertItem( QIcon::fromTheme( "format-justify-left" ), i18n("Align Left"),        Qt::AlignLeft );
0116     m->addAction(QIcon::fromTheme("format-justify-left"), i18n("Align Left"))->setData(Qt::AlignLeft);
0117     // m->insertItem( QIcon::fromTheme( "format-justify-center"), i18n("Align Center"), Qt::AlignHCenter );
0118     m->addAction(QIcon::fromTheme("format-justify-center"), i18n("Align Center"))->setData(Qt::AlignHCenter);
0119     // m->insertItem( QIcon::fromTheme( "format-justify-right" ), i18n("Align Right"),  Qt::AlignRight );
0120     m->addAction(QIcon::fromTheme("format-justify-right"), i18n("Align Right"))->setData(Qt::AlignRight);
0121     // m->insertItem( QIcon::fromTheme( "format-justify-fill" ), i18n("Align Block"),   Qt::AlignJustify );
0122     m->addAction(QIcon::fromTheme("format-justify-fill"), i18n("Align Block"))->setData(Qt::AlignJustify);
0123     connect(m, &QMenu::triggered, this, &RichTextEditor::slotSetAlignment);
0124     // END Text horizontal-alignment actions
0125 
0126     // BEGIN Text vertical-alignment actions
0127     // m_pTextVerticalAlignment = new KToolBarPopupAction( i18n("Text Vertical Alignment"), "text", 0, 0, 0, ac, "text_vertical_alignment" );
0128     m_pTextVerticalAlignment = new KToolBarPopupAction(QIcon::fromTheme(QString("text_vertical_alignment")), i18n("Text Vertical Alignment"), ac);
0129     m_pTextVerticalAlignment->setObjectName("text");
0130     // m_pTextVerticalAlignment->plug( tools );
0131     tools->addAction(m_pTextVerticalAlignment);
0132     m_pTextVerticalAlignment->setDelayed(false);
0133 
0134     m = m_pTextVerticalAlignment->menu();
0135     // m->insertTitle( i18n("Text Vertical Alignment") );
0136     m->setTitle(i18n("Text Vertical Alignment"));
0137     // m->setCheckable( true ); // 2018.12.07
0138 
0139     m->addAction(QIcon::fromTheme("format-text-superscript"), i18n("Superscript"))->setData(QTextCharFormat::AlignSuperScript);
0140     // m->insertItem( QIcon::fromTheme( "format-text-superscript" ), i18n("Superscript"),   QTextCharFormat::AlignSuperScript );
0141     m->addAction(i18n("Normal"))->setData(QTextCharFormat::AlignNormal);
0142     // m->insertItem(                       i18n("Normal"),         QTextCharFormat::AlignNormal );
0143     m->addAction(QIcon::fromTheme("format-text-subscript"), i18n("Subscript"))->setData(QTextCharFormat::AlignSubScript);
0144     // m->insertItem( QIcon::fromTheme( "format-text-subscript" ), i18n("Subscript"),       QTextCharFormat::AlignSubScript );
0145     connect(m, &QMenu::triggered, this, &RichTextEditor::slotSetVerticalAlignment);
0146     // END Text vertical-alignment actions
0147 
0148     QPixmap pm(16, 16);
0149     pm.fill(Qt::black);
0150     // m_pTextColor = new KAction( i18n("Text Color..."), pm, 0, this, SLOT(textColor()), ac, "format_color" );
0151     m_pTextColor = new QAction(i18n("Text Color..."), this);
0152     m_pTextColor->setIcon(pm);
0153     m_pTextColor->setObjectName("format_color");
0154     connect(m_pTextColor, &QAction::triggered, this, &RichTextEditor::textColor);
0155     // m_pTextColor->plug( tools );
0156     ac->addAction("format_color", m_pTextColor);
0157     tools->addAction(m_pTextColor);
0158 }
0159 
0160 RichTextEditor::~RichTextEditor()
0161 {
0162 }
0163 
0164 void RichTextEditor::makeUseStandardFont(QString *html)
0165 {
0166     if (!html)
0167         return;
0168 
0169     QFont f;
0170     QString bodyString = QString("<body style=\"font-size:%1pt;font-family:%2\">").arg(f.pointSize()).arg(f.family());
0171 
0172     if (html->contains("<body>")) {
0173         // Set the correct font size
0174         QFont f;
0175         html->replace("<body>", bodyString);
0176     }
0177 
0178     else if (!html->startsWith("<html>")) {
0179         html->prepend("<html>" + bodyString);
0180         html->append("</body></html>");
0181     }
0182 }
0183 
0184 QWidget *RichTextEditor::editorViewport() const
0185 {
0186     return m_pEditor->viewport();
0187 }
0188 
0189 void RichTextEditor::setText(QString text)
0190 {
0191     if (!Qt::mightBeRichText(text)) {
0192         // Format the text to be HTML
0193         text.replace('\n', "<br>");
0194     }
0195 
0196     m_pEditor->setText(text);
0197 }
0198 
0199 QString RichTextEditor::text() const
0200 {
0201     QString text = m_pEditor->toHtml().trimmed();
0202 
0203     // Remove the style info (e.g. style="font-size:8pt;font-family:DejaVu Sans") inserted into the body tag.
0204     text.replace(QRegExp("<body style=\"[^\"]*\">"), "<body>");
0205 
0206     // Replace all non-latin1 characters with HTML codes to represent them
0207     QString nonAsciiChars;
0208     for (int i = 0; i < text.length(); ++i) {
0209         QChar current = text[i];
0210         if ((current.toLatin1() == 0) && (current.unicode() != 0)) {
0211             // A non-latin1 character
0212             if (!nonAsciiChars.contains(current))
0213                 nonAsciiChars.append(current);
0214         }
0215     }
0216     for (int i = 0; i < nonAsciiChars.length(); ++i) {
0217         text.replace(nonAsciiChars[i], QString("&#%1;").arg(nonAsciiChars[i].unicode()));
0218     }
0219 
0220     return text;
0221 }
0222 
0223 void RichTextEditor::insertURL(const QString &url, const QString &text)
0224 {
0225     insertHTML(QString("<a href=\"%1\">%2</a>").arg(url).arg(text));
0226 }
0227 
0228 void RichTextEditor::insertHTML(const QString &html)
0229 {
0230     // 2018.12.07
0231     //  // Save cursor position
0232     //  //int cursorPara, cursorIndex;
0233     //  //m_pEditor->getCursorPosition( & cursorPara, & cursorIndex );
0234     //     QPoint cursorPos;
0235     //     cursorPos = m_pEditor->cursor().pos();
0236     //
0237     //  // replaceString is used so that the inserted text is at the cursor position.
0238     //  // it's just a random set of characters, so that the chance of them actually being
0239     //  // used is about zero.
0240     //  QString replaceString = "SXbCk2CtqJ83";
0241     //
0242     //  m_pEditor->insert( replaceString );
0243     //  QString editorText = m_pEditor->text();
0244     //  //editorText.replace( replaceString, html, (uint)0 ); // 2018.12.07
0245     //     editorText.replace( replaceString, html, Qt::CaseInsensitive );
0246     //  m_pEditor->setText( editorText );
0247     //
0248     //  // Restore cursor position
0249     //  //m_pEditor->setCursorPosition( cursorPara, cursorIndex );
0250     //     m_pEditor->cursor().setPos(cursorPos);
0251 
0252     m_pEditor->insertHtml(html);
0253 }
0254 
0255 void RichTextEditor::slotSetBold(bool isBold)
0256 {
0257     QTextCharFormat format;
0258     if (isBold) {
0259         format.setFontWeight(QFont::Bold);
0260     } else {
0261         format.setFontWeight(QFont::Normal);
0262     }
0263     m_pEditor->textCursor().mergeCharFormat(format);
0264 }
0265 void RichTextEditor::slotSetItalic(bool isItalic)
0266 {
0267     QTextCharFormat format;
0268     format.setFontItalic(isItalic);
0269     m_pEditor->textCursor().mergeCharFormat(format);
0270 }
0271 void RichTextEditor::slotSetUnderline(bool isUnderline)
0272 {
0273     QTextCharFormat format;
0274     format.setFontUnderline(isUnderline);
0275     m_pEditor->textCursor().mergeCharFormat(format);
0276 }
0277 void RichTextEditor::slotSetAlignment(QAction *act)
0278 {
0279     int alignment = act->data().toInt();
0280     QTextBlockFormat format = m_pEditor->textCursor().blockFormat();
0281     format.setAlignment(static_cast<Qt::AlignmentFlag>(alignment));
0282     m_pEditor->textCursor().mergeBlockFormat(format);
0283 }
0284 
0285 void RichTextEditor::slotSetVerticalAlignment(QAction *action)
0286 {
0287     int a = action->data().toInt();
0288     // m_pEditor->setVerticalAlignment( (Q3TextEdit::VerticalAlignment)a );
0289     // m_pEditor->setAlignment(a);
0290     QTextCharFormat format;
0291     format.setVerticalAlignment(static_cast<QTextCharFormat::VerticalAlignment>(a));
0292     m_pEditor->mergeCurrentCharFormat(format);
0293 }
0294 
0295 void RichTextEditor::slotSetList(bool set)
0296 {
0297     // m_pEditor->setParagType( set ? Q3StyleSheetItem::DisplayListItem : Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc );
0298     if (set) {
0299         m_pEditor->textCursor().createList(QTextListFormat::ListDisc);
0300     } else {
0301         QTextCursor cursor = m_pEditor->textCursor();
0302         QTextList *list = cursor.currentList();
0303         if (list) {
0304             QTextListFormat::Style style = QTextListFormat::ListStyleUndefined;
0305             QTextListFormat listFormat;
0306             listFormat.setIndent(0);
0307             listFormat.setStyle(style);
0308             list->setFormat(listFormat);
0309 
0310             for (int i = list->count() - 1; i >= 0; --i)
0311                 list->removeItem(i);
0312         }
0313     }
0314 }
0315 
0316 void RichTextEditor::slotCurrentCharFormatChanged(const QTextCharFormat &f)
0317 {
0318     fontChanged(f.font());
0319     // colorChanged( m_pEditor->foregroundColor() ); // 2018.12.07
0320     colorChanged(m_pEditor->palette().color(m_pEditor->foregroundRole()));
0321     alignmentChanged(m_pEditor->alignment());
0322     verticalAlignmentChanged(); // note: consider removing this method
0323 }
0324 
0325 void RichTextEditor::fontChanged(const QFont &f)
0326 {
0327     m_pTextBold->setChecked(f.bold());
0328     m_pTextItalic->setChecked(f.italic());
0329     m_pTextUnderline->setChecked(f.underline());
0330 }
0331 
0332 void RichTextEditor::textColor()
0333 {
0334     const QColor c = QColorDialog::getColor(m_pEditor->textColor(), this);
0335     if (c.isValid()) {
0336         m_pEditor->setTextColor(c);
0337     }
0338 }
0339 
0340 void RichTextEditor::colorChanged(const QColor &c)
0341 {
0342     QPixmap pix(16, 16);
0343     pix.fill(c);
0344     m_pTextColor->setIcon(pix);
0345 }
0346 
0347 void RichTextEditor::alignmentChanged(int a)
0348 {
0349     if (/*( a == Qt::AlignAuto ) || */ (a & Qt::AlignLeft))
0350         m_pTextAlignment->setIcon(QIcon::fromTheme("format-justify-left"));
0351     else if ((a & Qt::AlignHCenter))
0352         m_pTextAlignment->setIcon(QIcon::fromTheme("format-justify-center"));
0353     else if ((a & Qt::AlignRight))
0354         m_pTextAlignment->setIcon(QIcon::fromTheme("format-justify-right"));
0355     else if ((a & Qt::AlignJustify))
0356         m_pTextAlignment->setIcon(QIcon::fromTheme("format-justify-fill"));
0357 }
0358 
0359 void RichTextEditor::verticalAlignmentChanged()
0360 {
0361     //  QTextEdit::VerticalAlignment a =
0362     //  if ( a == KTextEdit::AlignNormal )
0363     //      m_pTextVerticalAlignment->setIcon( "text" );
0364     //  else if ( a == KTextEdit::AlignSuperScript )
0365     //      m_pTextVerticalAlignment->setIcon( "format-text-superscript" );
0366     //  else if ( a == KTextEdit::AlignSubScript )
0367     //      m_pTextVerticalAlignment->setIcon( "format-text-subscript" );
0368 }
0369 
0370 void RichTextEditor::setResourcePaths(const QStringList &paths)
0371 {
0372     // m_pEditor->mimeSourceFactory()->setFilePath( paths );
0373 
0374     for (QStringList::const_iterator itStr = paths.begin(); itStr != paths.end(); ++itStr) {
0375         QString dirName = *itStr;
0376         QDir dir(dirName);
0377         dir.setFilter(QDir::Files);
0378         QStringList l;
0379         l << "*.png";
0380         dir.setNameFilters(l);
0381         QFileInfoList fileInfoList = dir.entryInfoList();
0382         qCDebug(KTL_LOG) << " list size " << fileInfoList.size();
0383         for (QFileInfoList::iterator itFile = fileInfoList.begin(); itFile != fileInfoList.end(); ++itFile) {
0384             QFileInfo &fi = *itFile;
0385 
0386             QString fullPath = fi.path() + "/" + fi.fileName();
0387             QPixmap img(fullPath);
0388             if (img.isNull()) {
0389                 qCWarning(KTL_LOG) << " img is null " << fullPath;
0390             }
0391 
0392             m_pEditor->document()->addResource(QTextDocument::ImageResource, QUrl(fi.fileName()), QVariant(img));
0393             qCDebug(KTL_LOG) << " added resource: " << fi.fileName() << " to " << fullPath;
0394         }
0395     }
0396 }
0397 // END class RichTextEditor
0398 
0399 // BEGIN class RichTextEditorDlg
0400 RichTextEditorDlg::RichTextEditorDlg(QWidget *parent, const QString &caption)
0401     : QDialog(parent)
0402 {
0403     setObjectName("RichTextEditorDlg");
0404     setModal(true);
0405     setWindowTitle(caption);
0406 
0407     QVBoxLayout *mainLayout = new QVBoxLayout;
0408     setLayout(mainLayout);
0409 
0410     m_pEditor = new RichTextEditor(this);
0411     mainLayout->addWidget(m_pEditor);
0412 
0413     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0414     mainLayout->addWidget(buttonBox);
0415 
0416     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0417     okButton->setDefault(true);
0418     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0419     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0420     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0421 }
0422 // END class RichTextEditorDlg
0423 
0424 #include "moc_richtexteditor.cpp"