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"