File indexing completed on 2024-04-28 04:55:40

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "choqoktextedit.h"
0010 
0011 #include <QAction>
0012 #include <QGridLayout>
0013 #include <QKeyEvent>
0014 #include <QLabel>
0015 #include <QMenu>
0016 #include <QMimeData>
0017 #include <QTimer>
0018 
0019 #include <KLocalizedString>
0020 #include <sonnet/speller.h>
0021 
0022 #include "choqokbehaviorsettings.h"
0023 #include "libchoqokdebug.h"
0024 #include "shortenmanager.h"
0025 
0026 namespace Choqok
0027 {
0028 namespace UI
0029 {
0030 
0031 class TextEdit::Private
0032 {
0033 public:
0034     Private(uint charLmt)
0035         : langActions(new QMenu), charLimit(charLmt)
0036     {}
0037     QMenu *langActions;
0038     QMap<QString, QAction *> langActionMap;
0039     uint charLimit;
0040     QString prevStr;
0041     QChar firstChar;
0042     QString curLang;
0043 };
0044 
0045 TextEdit::TextEdit(uint charLimit /*= 0*/, QWidget *parent /*= 0*/)
0046     : KTextEdit(parent), d(new Private(charLimit))
0047 {
0048     qCDebug(CHOQOK) << charLimit;
0049     connect(this, &TextEdit::textChanged, this, &TextEdit::updateRemainingCharsCount);
0050     setAcceptRichText(false);
0051     this->setToolTip(i18n("<b>Note:</b><br/><i>Ctrl+S</i> to enable/disable auto spell checker."));
0052 
0053     enableFindReplace(false);
0054     QFont counterF;
0055     counterF.setBold(true);
0056     counterF.setPointSize(10);
0057     lblRemainChar = new QLabel(this);
0058     lblRemainChar->resize(50, 50);
0059     lblRemainChar->setFont(counterF);
0060     QGridLayout *layout = new QGridLayout(this);
0061     layout->setRowStretch(0, 100);
0062     layout->setColumnStretch(5, 100);
0063     layout->setMargin(0);
0064     layout->setSpacing(0);
0065     layout->addWidget(lblRemainChar, 1, 0);
0066     this->setLayout(layout);
0067     setTabChangesFocus(true);
0068     settingsChanged();
0069     connect(BehaviorSettings::self(), &BehaviorSettings::configChanged,
0070             this, &TextEdit::settingsChanged);
0071 
0072     QTimer::singleShot(1000, this, SLOT(setupSpeller()));
0073     connect(this, &TextEdit::aboutToShowContextMenu, this,
0074             &TextEdit::slotAboutToShowContextMenu);
0075 }
0076 
0077 TextEdit::~TextEdit()
0078 {
0079     disconnect(this, &TextEdit::textChanged, this,
0080             &TextEdit::updateRemainingCharsCount);
0081     disconnect(this, &TextEdit::aboutToShowContextMenu, this,
0082             &TextEdit::slotAboutToShowContextMenu);
0083     disconnect(BehaviorSettings::self(), &BehaviorSettings::configChanged,
0084                this, &TextEdit::settingsChanged);
0085 
0086     BehaviorSettings::setSpellerLanguage(d->curLang);
0087     d->langActions->deleteLater();
0088     delete d;
0089 }
0090 
0091 void TextEdit::keyPressEvent(QKeyEvent *e)
0092 {
0093     if ((e->key() == Qt::Key_Return) || (e->key() == Qt::Key_Enter)) {
0094         if (e->modifiers() == Qt::ShiftModifier) {
0095             KTextEdit::keyPressEvent(e);
0096         } else {
0097             QString txt = toPlainText();
0098             Q_EMIT returnPressed(txt);
0099         }
0100         e->accept();
0101     } else if (e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_S) {
0102         this->setCheckSpellingEnabled(!this->checkSpellingEnabled());
0103         e->accept();
0104     } else if (e->key() == Qt::Key_Escape) {
0105         if (!this->toPlainText().isEmpty()) {
0106             this->clear();
0107             Q_EMIT cleared();
0108             e->accept();
0109         } else {
0110             KTextEdit::keyPressEvent(e);
0111         }
0112     } else {
0113         KTextEdit::keyPressEvent(e);
0114     }
0115 }
0116 
0117 void TextEdit::clear()
0118 {
0119     if (toPlainText().isEmpty()) {
0120         return;
0121     } else {
0122         undoableClear();
0123         Q_EMIT cleared();
0124     }
0125 }
0126 
0127 void TextEdit::undoableClear()
0128 {
0129     QTextCursor cursor = textCursor();
0130     cursor.beginEditBlock();
0131     cursor.movePosition(QTextCursor::Start);
0132     cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
0133     cursor.removeSelectedText();
0134     cursor.endEditBlock();
0135 }
0136 
0137 void TextEdit::insertFromMimeData(const QMimeData *source)
0138 {
0139     if (Choqok::BehaviorSettings::shortenOnPaste()) {
0140         KTextEdit::insertPlainText(ShortenManager::self()->parseText(source->text()));
0141     } else {
0142         KTextEdit::insertPlainText(source->text());
0143     }
0144 }
0145 
0146 void TextEdit::updateRemainingCharsCount()
0147 {
0148     QString txt = this->toPlainText();
0149     int count = txt.count();
0150     if (count) {
0151         lblRemainChar->show();
0152         if (d->charLimit) {
0153             int remain = d->charLimit - count;
0154             if (remain < 0) {
0155                 lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: red;}"));
0156             } else if (remain < 30) {
0157                 lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: rgb(242, 179, 19);}"));
0158             } else {
0159                 lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: green;}"));
0160             }
0161             lblRemainChar->setText(QString::number(remain));
0162         } else {
0163             lblRemainChar->setText(QString::number(count));
0164             lblRemainChar->setStyleSheet(QLatin1String("QLabel {color: blue;}"));
0165         }
0166         txt.remove(QRegExp(QLatin1String("@([^\\s\\W]+)")));
0167         txt = txt.trimmed();
0168         if (d->firstChar != txt[0]) {
0169             d->firstChar = txt[0];
0170             txt.prepend(QLatin1Char(' '));
0171             QTextBlockFormat f;
0172             f.setLayoutDirection((Qt::LayoutDirection) txt.isRightToLeft());
0173             textCursor().mergeBlockFormat(f);
0174         }
0175     } else {
0176         lblRemainChar->hide();
0177     }
0178 }
0179 
0180 void TextEdit::slotAboutToShowContextMenu(QMenu *menu)
0181 {
0182     if (menu) {
0183         qCDebug(CHOQOK);
0184         QAction *act = new QAction(i18n("Set spell check language"), menu);
0185         act->setMenu(d->langActions);
0186         menu->addAction(act);
0187 
0188         QAction *shorten = new QAction(i18nc("Replace URLs by a shortened URL", "Shorten URLs"), menu);
0189         connect(shorten, &QAction::triggered, this, &TextEdit::shortenUrls);
0190         menu->addAction(shorten);
0191     }
0192 }
0193 
0194 void TextEdit::shortenUrls()
0195 {
0196     qCDebug(CHOQOK);
0197     QTextCursor cur = textCursor();
0198     if (!cur.hasSelection()) {
0199         cur.select(QTextCursor::BlockUnderCursor);
0200     }
0201     QString shortened = ShortenManager::self()->parseText(cur.selectedText());
0202     cur.removeSelectedText();
0203     cur.insertText(shortened);
0204     setTextCursor(cur);
0205 }
0206 
0207 void TextEdit::slotChangeSpellerLanguage()
0208 {
0209     QAction *act = qobject_cast<QAction *>(sender());
0210     if (act) {
0211         QString lang = act->data().toString();
0212         setSpellCheckingLanguage(lang);
0213         QAction *actOld = d->langActionMap.value(d->curLang);
0214         if (actOld) {
0215             actOld->setChecked(false);
0216         }
0217         d->curLang = lang;
0218     }
0219 }
0220 
0221 uint TextEdit::charLimit()
0222 {
0223     return d->charLimit;
0224 }
0225 
0226 QChar TextEdit::firstChar()
0227 {
0228     return d->firstChar;
0229 }
0230 
0231 void TextEdit::setFirstChar(const QChar &firstChar)
0232 {
0233     d->firstChar = firstChar;
0234 }
0235 
0236 void TextEdit::setCharLimit(uint charLimit /*= 0*/)
0237 {
0238     d->charLimit = charLimit;
0239     updateRemainingCharsCount();
0240 }
0241 
0242 void TextEdit::setPlainText(const QString &text)
0243 {
0244     if (Choqok::BehaviorSettings::shortenOnPaste()) {
0245         KTextEdit::setPlainText(ShortenManager::self()->parseText(text));
0246     } else {
0247         KTextEdit::setPlainText(text);
0248     }
0249     moveCursor(QTextCursor::End);
0250     setEnabled(true);
0251 }
0252 
0253 void TextEdit::setText(const QString &text)
0254 {
0255     KTextEdit::setPlainText(text);
0256     moveCursor(QTextCursor::End);
0257     setEnabled(true);
0258 }
0259 
0260 void TextEdit::prependText(const QString &text)
0261 {
0262     QString tmp = text;
0263     tmp.append(QLatin1Char(' ') + toPlainText());
0264     setPlainText(tmp);
0265 }
0266 
0267 void TextEdit::appendText(const QString &text)
0268 {
0269     QString tmp = toPlainText();
0270     if (tmp.isEmpty()) {
0271         tmp = text + QLatin1Char(' ');
0272     } else {
0273         tmp.append(QLatin1Char(' ') + text);
0274     }
0275     setPlainText(tmp);
0276 }
0277 
0278 void TextEdit::settingsChanged()
0279 {
0280     setCheckSpellingEnabled(BehaviorSettings::enableSpellChecker());
0281 }
0282 
0283 void TextEdit::setupSpeller()
0284 {
0285     BehaviorSettings::self()->load();
0286     d->curLang = BehaviorSettings::spellerLanguage();
0287     Sonnet::Speller s;
0288     if (d->curLang.isEmpty()) {
0289         d->curLang = s.defaultLanguage();
0290     }
0291     qCDebug(CHOQOK) << "Current LANG:" << d->curLang;
0292     for (const QString &dict: s.availableDictionaries().keys()) {
0293         const QString value = s.availableDictionaries().value(dict);
0294         QAction *act = new QAction(dict, d->langActions);
0295         act->setData(value);
0296         act->setCheckable(true);
0297         if (d->curLang == value) {
0298             act->setChecked(true);
0299         }
0300         connect(act, &QAction::triggered, this, &TextEdit::slotChangeSpellerLanguage);
0301         d->langActions->addAction(act);
0302         d->langActionMap.insert(value, act);
0303     }
0304 }
0305 
0306 QSize TextEdit::minimumSizeHint() const
0307 {
0308     const QSize size = KTextEdit::minimumSizeHint();
0309     return QSize(size.width(), qMax(fontMetrics().height() * 3, size.height()));
0310 }
0311 
0312 }
0313 }
0314 
0315 #include "moc_choqoktextedit.cpp"