File indexing completed on 2024-05-19 09:22:09
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 <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) 0027 : AbstractRunner(parent, metaData) 0028 { 0029 } 0030 0031 void DictionaryRunner::reloadConfiguration() 0032 { 0033 KConfigGroup c = config(); 0034 m_triggerWord = c.readEntry(CONFIG_TRIGGERWORD, i18nc("Trigger word before word to define", "define")); 0035 if (!m_triggerWord.isEmpty()) { 0036 m_triggerWord.append(QLatin1Char(' ')); 0037 setTriggerWords({m_triggerWord}); 0038 } else { 0039 setMatchRegex(QRegularExpression()); 0040 } 0041 setSyntaxes({RunnerSyntax(i18nc("Dictionary keyword", "%1:q:", m_triggerWord), i18n("Finds the definition of :q:."))}); 0042 } 0043 0044 void DictionaryRunner::match(RunnerContext &context) 0045 { 0046 QString query = context.query(); 0047 if (query.startsWith(m_triggerWord, Qt::CaseInsensitive)) { 0048 query.remove(0, m_triggerWord.length()); 0049 } 0050 if (query.isEmpty()) { 0051 return; 0052 } 0053 0054 { 0055 QEventLoop loop; 0056 QTimer::singleShot(400, &loop, [&loop]() { 0057 loop.quit(); 0058 }); 0059 loop.exec(); 0060 } 0061 if (!context.isValid()) { 0062 return; 0063 } 0064 QString returnedQuery; 0065 QMetaObject::invokeMethod(&m_dictEngine, "requestDefinition", Qt::QueuedConnection, Q_ARG(const QString &, query)); 0066 QEventLoop loop; 0067 connect(&m_dictEngine, &DictEngine::definitionRecieved, &loop, [&loop, &query, &returnedQuery, &context](const QString &html) { 0068 returnedQuery = html; 0069 loop.quit(); 0070 }); 0071 loop.exec(); 0072 if (!context.isValid() || returnedQuery.isEmpty()) { 0073 return; 0074 } 0075 0076 static const QRegularExpression removeHtml(QLatin1String("<[^>]*>")); 0077 QString definitions(returnedQuery); 0078 definitions.remove(QLatin1Char('\r')).remove(removeHtml); 0079 while (definitions.contains(QLatin1String(" "))) { 0080 definitions.replace(QLatin1String(" "), QLatin1String(" ")); 0081 } 0082 QStringList lines = definitions.split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0083 if (lines.length() < 2) { 0084 return; 0085 } 0086 lines.removeFirst(); 0087 0088 QList<QueryMatch> matches; 0089 int item = 0; 0090 static const QRegularExpression partOfSpeech(QLatin1String("(?: ([a-z]{1,5})){0,1} [0-9]{1,2}: (.*)")); 0091 QString lastPartOfSpeech; 0092 for (const QString &line : std::as_const(lines)) { 0093 const auto reMatch = partOfSpeech.match(line); 0094 if (!reMatch.hasMatch()) { 0095 continue; 0096 } 0097 if (!reMatch.capturedView(1).isEmpty()) { 0098 lastPartOfSpeech = reMatch.captured(1); 0099 } 0100 QueryMatch match(this); 0101 match.setMultiLine(true); 0102 match.setText(lastPartOfSpeech + QLatin1String(": ") + reMatch.captured(2)); 0103 match.setRelevance(1 - (static_cast<double>(++item) / static_cast<double>(lines.length()))); 0104 match.setCategoryRelevance(QueryMatch::CategoryRelevance::Moderate); 0105 match.setIconName(QStringLiteral("accessories-dictionary")); 0106 matches.append(match); 0107 } 0108 context.addMatches(matches); 0109 } 0110 0111 void DictionaryRunner::run(const RunnerContext &context, const QueryMatch &match) 0112 { 0113 QString query = context.query(); 0114 if (query.startsWith(m_triggerWord, Qt::CaseInsensitive)) { 0115 query.remove(0, m_triggerWord.length()); 0116 } 0117 QGuiApplication::clipboard()->setText(query + QLatin1Char(' ') + match.text()); 0118 KNotification::event(KNotification::Notification, name(), i18n("Definition for \"%1\" has been copied to clipboard", query), metadata().iconName()); 0119 } 0120 0121 K_PLUGIN_CLASS_WITH_JSON(DictionaryRunner, "plasma-runner-dictionary.json") 0122 0123 #include "dictionaryrunner.moc"