File indexing completed on 2024-05-05 17:34:05
0001 /* 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * SPDX-FileCopyrightText: 2010, 2012 Jason A. Donenfeld <Jason@zx2c4.com> 0004 */ 0005 0006 #include "dictionaryrunner.h" 0007 0008 #include <KConfigGroup> 0009 #include <KLocalizedString> 0010 #include <KNotifications/KNotification> 0011 #include <QClipboard> 0012 #include <QEventLoop> 0013 #include <QGuiApplication> 0014 #include <QIcon> 0015 #include <QMutex> 0016 #include <QMutexLocker> 0017 #include <QStringList> 0018 #include <QTimer> 0019 0020 namespace 0021 { 0022 const char CONFIG_TRIGGERWORD[] = "triggerWord"; 0023 QMutex s_initMutex; 0024 } 0025 0026 DictionaryRunner::DictionaryRunner(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) 0027 : AbstractRunner(parent, metaData, args) 0028 { 0029 setPriority(LowPriority); 0030 setObjectName(QLatin1String("Dictionary")); 0031 } 0032 0033 void DictionaryRunner::reloadConfiguration() 0034 { 0035 KConfigGroup c = config(); 0036 m_triggerWord = c.readEntry(CONFIG_TRIGGERWORD, i18nc("Trigger word before word to define", "define")); 0037 if (!m_triggerWord.isEmpty()) { 0038 m_triggerWord.append(QLatin1Char(' ')); 0039 setTriggerWords({m_triggerWord}); 0040 } else { 0041 setMatchRegex(QRegularExpression()); 0042 } 0043 setSyntaxes({RunnerSyntax(i18nc("Dictionary keyword", "%1:q:", m_triggerWord), i18n("Finds the definition of :q:."))}); 0044 } 0045 0046 void DictionaryRunner::match(RunnerContext &context) 0047 { 0048 QString query = context.query(); 0049 if (query.startsWith(m_triggerWord, Qt::CaseInsensitive)) { 0050 query.remove(0, m_triggerWord.length()); 0051 } 0052 if (query.isEmpty()) { 0053 return; 0054 } 0055 0056 { 0057 QEventLoop loop; 0058 QTimer::singleShot(400, &loop, [&loop]() { 0059 loop.quit(); 0060 }); 0061 loop.exec(); 0062 } 0063 if (!context.isValid()) { 0064 return; 0065 } 0066 QString returnedQuery; 0067 QMetaObject::invokeMethod(&m_dictEngine, "requestDefinition", Qt::QueuedConnection, Q_ARG(const QString &, query)); 0068 QEventLoop loop; 0069 connect(&m_dictEngine, &DictEngine::definitionRecieved, &loop, [&loop, &query, &returnedQuery, &context](const QString &html) { 0070 returnedQuery = html; 0071 loop.quit(); 0072 }); 0073 loop.exec(); 0074 if (!context.isValid() || returnedQuery.isEmpty()) { 0075 return; 0076 } 0077 0078 static const QRegularExpression removeHtml(QLatin1String("<[^>]*>")); 0079 QString definitions(returnedQuery); 0080 definitions.remove(QLatin1Char('\r')).remove(removeHtml); 0081 while (definitions.contains(QLatin1String(" "))) { 0082 definitions.replace(QLatin1String(" "), QLatin1String(" ")); 0083 } 0084 QStringList lines = definitions.split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0085 if (lines.length() < 2) { 0086 return; 0087 } 0088 lines.removeFirst(); 0089 0090 QList<QueryMatch> matches; 0091 int item = 0; 0092 static const QRegularExpression partOfSpeech(QLatin1String("(?: ([a-z]{1,5})){0,1} [0-9]{1,2}: (.*)")); 0093 QString lastPartOfSpeech; 0094 for (const QString &line : std::as_const(lines)) { 0095 const auto reMatch = partOfSpeech.match(line); 0096 if (!reMatch.hasMatch()) { 0097 continue; 0098 } 0099 if (!reMatch.capturedView(1).isEmpty()) { 0100 lastPartOfSpeech = reMatch.captured(1); 0101 } 0102 QueryMatch match(this); 0103 match.setMultiLine(true); 0104 match.setText(lastPartOfSpeech + QLatin1String(": ") + reMatch.captured(2)); 0105 match.setRelevance(1 - (static_cast<double>(++item) / static_cast<double>(lines.length()))); 0106 match.setType(QueryMatch::HelperMatch); 0107 match.setIconName(QStringLiteral("accessories-dictionary")); 0108 matches.append(match); 0109 } 0110 context.addMatches(matches); 0111 } 0112 0113 void DictionaryRunner::run(const RunnerContext &context, const QueryMatch &match) 0114 { 0115 QString query = context.query(); 0116 if (query.startsWith(m_triggerWord, Qt::CaseInsensitive)) { 0117 query.remove(0, m_triggerWord.length()); 0118 } 0119 QGuiApplication::clipboard()->setText(query + QLatin1Char(' ') + match.text()); 0120 KNotification::event(KNotification::Notification, name(), i18n("Definition for \"%1\" has been copied to clipboard", query), icon().name()); 0121 } 0122 0123 K_PLUGIN_CLASS_WITH_JSON(DictionaryRunner, "plasma-runner-dictionary.json") 0124 0125 #include "dictionaryrunner.moc"