mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-23 10:22:52 +00:00
kdeplasma-addons: rework the dictionary runner
requires the following commit from kde-workspace: 6cfe505af4ede2843d7faa3d302e09cb171d823d and because the runner is running in its own thread it is still not thread-safe, that's something to fix Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
698d7f49c7
commit
9b90a8609f
5 changed files with 78 additions and 181 deletions
|
@ -1,4 +1,4 @@
|
|||
set(dictionaryrunner_SRCS dictionaryrunner.cpp dictionarymatchengine.cpp)
|
||||
set(dictionaryrunner_SRCS dictionaryrunner.cpp)
|
||||
set(kcm_dictionaryrunner_SRCS dictionaryrunner_config.cpp)
|
||||
|
||||
kde4_add_plugin(krunner_dictionary ${dictionaryrunner_SRCS})
|
||||
|
@ -7,5 +7,13 @@ kde4_add_plugin(kcm_krunner_dictionary ${kcm_dictionaryrunner_SRCS})
|
|||
target_link_libraries(krunner_dictionary KDE4::plasma)
|
||||
target_link_libraries(kcm_krunner_dictionary KDE4::kcmutils)
|
||||
|
||||
install(TARGETS krunner_dictionary kcm_krunner_dictionary DESTINATION ${KDE4_PLUGIN_INSTALL_DIR})
|
||||
install(FILES plasma-runner-dictionary.desktop plasma-runner-dictionary_config.desktop DESTINATION ${KDE4_SERVICES_INSTALL_DIR})
|
||||
install(
|
||||
TARGETS krunner_dictionary kcm_krunner_dictionary
|
||||
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
plasma-runner-dictionary.desktop
|
||||
plasma-runner-dictionary_config.desktop
|
||||
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
|
||||
)
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010, 2012 Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
*/
|
||||
|
||||
#include "dictionarymatchengine.h"
|
||||
#include <Plasma/AbstractRunner>
|
||||
#include <QThread>
|
||||
#include <QtCore/qmetaobject.h>
|
||||
#include <KDebug>
|
||||
|
||||
DictionaryMatchEngine::DictionaryMatchEngine(Plasma::DataEngine *dictionaryEngine, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_dictionaryEngine(dictionaryEngine)
|
||||
{
|
||||
/* We have to connect source in two different places, due to the difference in
|
||||
* how the connection is made based on data availability. There are two cases,
|
||||
* and this extra connection handles the second case. */
|
||||
connect(m_dictionaryEngine, SIGNAL(sourceAdded(QString)), this, SLOT(sourceAdded(QString)));
|
||||
}
|
||||
|
||||
/* This function should be called from a different thread. */
|
||||
QString DictionaryMatchEngine::lookupWord(const QString &word)
|
||||
{
|
||||
if (!m_dictionaryEngine) {
|
||||
kDebug() << "Could not find dictionary data engine.";
|
||||
return QString();
|
||||
}
|
||||
if (thread() == QThread::currentThread()) {
|
||||
kDebug() << "DictionaryMatchEngine::lookupWord is only meant to be called from non-primary threads.";
|
||||
return QString();
|
||||
}
|
||||
|
||||
ThreadData data;
|
||||
|
||||
QMutexLocker locker(&m_wordLock);
|
||||
m_lockers.insert(word, &data);
|
||||
locker.unlock();
|
||||
|
||||
/* We lock it in this thread. Then we try to lock it again, which we cannot do, until the other thread
|
||||
* unlocks it for us first. We time-out after 30 seconds. */
|
||||
data.mutex.lock();
|
||||
QMetaObject::invokeMethod(this, "sourceAdded", Qt::QueuedConnection, Q_ARG(const QString&, QLatin1Char(':') + word));
|
||||
if (!data.mutex.tryLock(30 * 1000))
|
||||
kDebug() << "The dictionary data engine timed out.";
|
||||
|
||||
locker.relock();
|
||||
m_lockers.remove(word, &data);
|
||||
|
||||
return data.definition;
|
||||
}
|
||||
|
||||
void DictionaryMatchEngine::sourceAdded(const QString &source)
|
||||
{
|
||||
m_dictionaryEngine->connectSource(source, this);
|
||||
}
|
||||
|
||||
void DictionaryMatchEngine::dataUpdated(const QString &source, const Plasma::DataEngine::Data &result)
|
||||
{
|
||||
if (!result.contains(QLatin1String("text")))
|
||||
return;
|
||||
|
||||
QString definition(result[QLatin1String("text")].toString());
|
||||
|
||||
QMutexLocker locker(&m_wordLock);
|
||||
foreach (ThreadData *data, m_lockers.values(source)) {
|
||||
/* Because of QString's CoW semantics, we don't have to worry about
|
||||
* the overhead of assigning this to every item. */
|
||||
data->definition = definition;
|
||||
data->mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_dictionarymatchengine.cpp"
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010, 2012 Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
*/
|
||||
|
||||
#ifndef DICTIONARYMATCHENGINE_H
|
||||
#define DICTIONARYMATCHENGINE_H
|
||||
|
||||
#include <Plasma/DataEngine>
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
#include <QtCore/qmap.h>
|
||||
|
||||
namespace Plasma
|
||||
{
|
||||
class RunnerContext;
|
||||
}
|
||||
class DictionaryMatchEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DictionaryMatchEngine(Plasma::DataEngine *dictionaryEngine, QObject *parent = 0);
|
||||
QString lookupWord(const QString &word);
|
||||
|
||||
private:
|
||||
struct ThreadData {
|
||||
QMutex mutex;
|
||||
QString definition;
|
||||
};
|
||||
QMultiMap <QString, ThreadData*> m_lockers;
|
||||
QMutex m_wordLock;
|
||||
Plasma::DataEngine *m_dictionaryEngine;
|
||||
|
||||
private slots:
|
||||
void dataUpdated(const QString &name, const Plasma::DataEngine::Data &data);
|
||||
void sourceAdded(const QString &source);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,78 +4,80 @@
|
|||
|
||||
#include "dictionaryrunner.h"
|
||||
#include "dictionaryrunner_config.h"
|
||||
#include <QStringList>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <KIcon>
|
||||
#include <KDebug>
|
||||
|
||||
DictionaryRunner::DictionaryRunner(QObject *parent, const QVariantList &args)
|
||||
: AbstractRunner(parent, args)
|
||||
: AbstractRunner(parent, args),
|
||||
m_engine(nullptr)
|
||||
{
|
||||
m_engine = new DictionaryMatchEngine(dataEngine("dict"), this);
|
||||
|
||||
setSpeed(SlowSpeed);
|
||||
setPriority(LowPriority);
|
||||
setObjectName(QLatin1String("Dictionary"));
|
||||
setIgnoredTypes(Plasma::RunnerContext::Directory | Plasma::RunnerContext::File |
|
||||
Plasma::RunnerContext::NetworkLocation | Plasma::RunnerContext::Executable |
|
||||
Plasma::RunnerContext::ShellCommand);
|
||||
setSpeed(SlowSpeed);
|
||||
setPriority(LowPriority);
|
||||
setObjectName(QLatin1String("Dictionary"));
|
||||
setIgnoredTypes(
|
||||
Plasma::RunnerContext::Directory | Plasma::RunnerContext::File |
|
||||
Plasma::RunnerContext::NetworkLocation | Plasma::RunnerContext::Executable |
|
||||
Plasma::RunnerContext::ShellCommand
|
||||
);
|
||||
}
|
||||
|
||||
void DictionaryRunner::init()
|
||||
{
|
||||
reloadConfiguration();
|
||||
reloadConfiguration();
|
||||
m_engine = dataEngine("dict");
|
||||
}
|
||||
|
||||
void DictionaryRunner::reloadConfiguration()
|
||||
{
|
||||
KConfigGroup c = config();
|
||||
m_triggerWord = c.readEntry(CONFIG_TRIGGERWORD, i18nc("Trigger word before word to define", "define"));
|
||||
if (!m_triggerWord.isEmpty())
|
||||
m_triggerWord.append(QLatin1Char(' '));
|
||||
setSyntaxes(QList<Plasma::RunnerSyntax>() << Plasma::RunnerSyntax(Plasma::RunnerSyntax(i18nc("Dictionary keyword", "%1:q:", m_triggerWord), i18n("Finds the definition of :q:."))));
|
||||
KConfigGroup c = config();
|
||||
m_triggerWord = c.readEntry(CONFIG_TRIGGERWORD, i18nc("Trigger word before word to define", "define"));
|
||||
if (!m_triggerWord.isEmpty()) {
|
||||
m_triggerWord.append(QLatin1Char(' '));
|
||||
}
|
||||
setSyntaxes(
|
||||
QList<Plasma::RunnerSyntax>()
|
||||
<< Plasma::RunnerSyntax(Plasma::RunnerSyntax(i18nc("Dictionary keyword", "%1:q:", m_triggerWord), i18n("Finds the definition of :q:."))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void DictionaryRunner::match(Plasma::RunnerContext &context)
|
||||
{
|
||||
QString query = context.query();
|
||||
if (!query.startsWith(m_triggerWord, Qt::CaseInsensitive))
|
||||
return;
|
||||
query.remove(0, m_triggerWord.length());
|
||||
if (query.isEmpty())
|
||||
return;
|
||||
QString returnedQuery = m_engine->lookupWord(query);
|
||||
if (!context.isValid()) {
|
||||
return;
|
||||
}
|
||||
QString query = context.query();
|
||||
if (!query.startsWith(m_triggerWord, Qt::CaseInsensitive)) {
|
||||
return;
|
||||
}
|
||||
query.remove(0, m_triggerWord.length());
|
||||
if (query.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.isValid())
|
||||
return;
|
||||
Plasma::DataEngine::Data enginedata = m_engine->query(query);
|
||||
// qDebug() << Q_FUNC_INFO << query << enginedata;
|
||||
const QString enginedefinition = enginedata.value("definition").toString();
|
||||
if (!enginedefinition.isEmpty()) {
|
||||
const QString engineexample = enginedata.value("example").toString();
|
||||
Plasma::QueryMatch match(this);
|
||||
match.setText(enginedefinition);
|
||||
match.setSubtext(engineexample);
|
||||
match.setData(enginedefinition);
|
||||
match.setType(Plasma::QueryMatch::InformationalMatch);
|
||||
match.setIcon(KIcon(QLatin1String("accessories-dictionary")));
|
||||
context.addMatch(context.query(), match);
|
||||
}
|
||||
}
|
||||
|
||||
static const QRegExp removeHtml(QLatin1String("<[^>]*>"));
|
||||
QString definitions(returnedQuery);
|
||||
definitions.remove(QLatin1Char('\r')).remove(removeHtml);
|
||||
while (definitions.contains(QLatin1String(" ")))
|
||||
definitions.replace(QLatin1String(" "), QLatin1String(" "));
|
||||
QStringList lines = definitions.split(QLatin1Char('\n'), QString::SkipEmptyParts);
|
||||
if (lines.length() < 2)
|
||||
return;
|
||||
lines.removeFirst();
|
||||
|
||||
QList<Plasma::QueryMatch> matches;
|
||||
int item = 0;
|
||||
static const QRegExp partOfSpeech(QLatin1String("(?: ([a-z]{1,5})){0,1} [0-9]{1,2}: (.*)"));
|
||||
QString lastPartOfSpeech;
|
||||
static const KIcon icon(QLatin1String("accessories-dictionary"));
|
||||
foreach (const QString &line, lines) {
|
||||
if (partOfSpeech.indexIn(line) == -1)
|
||||
continue;
|
||||
if (!partOfSpeech.cap(1).isEmpty())
|
||||
lastPartOfSpeech = partOfSpeech.cap(1);
|
||||
Plasma::QueryMatch match(this);
|
||||
match.setText(QString(QLatin1String("%1: %2")).arg(query, lastPartOfSpeech));
|
||||
match.setRelevance(1 - (static_cast<double>(++item) / static_cast<double>(lines.length())));
|
||||
match.setType(Plasma::QueryMatch::InformationalMatch);
|
||||
match.setIcon(icon);
|
||||
match.setSubtext(partOfSpeech.cap(2));
|
||||
matches.append(match);
|
||||
}
|
||||
context.addMatches(context.query(), matches);
|
||||
void DictionaryRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
|
||||
{
|
||||
Q_UNUSED(context)
|
||||
const QString data = match.data().toString();
|
||||
QApplication::clipboard()->setText(data);
|
||||
}
|
||||
|
||||
K_EXPORT_PLASMA_RUNNER(krunner_dictionary, DictionaryRunner)
|
||||
|
|
|
@ -6,24 +6,24 @@
|
|||
#define DICTIONARYRUNNER_H
|
||||
|
||||
#include <Plasma/AbstractRunner>
|
||||
#include "dictionarymatchengine.h"
|
||||
#include <Plasma/DataEngine>
|
||||
|
||||
class DictionaryRunner : public Plasma::AbstractRunner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
DictionaryRunner(QObject *parent, const QVariantList &args);
|
||||
void match(Plasma::RunnerContext &context);
|
||||
void reloadConfiguration();
|
||||
DictionaryRunner(QObject *parent, const QVariantList &args);
|
||||
|
||||
void match(Plasma::RunnerContext &context);
|
||||
void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match);
|
||||
void reloadConfiguration();
|
||||
|
||||
private:
|
||||
QString m_triggerWord;
|
||||
DictionaryMatchEngine *m_engine;
|
||||
QString m_triggerWord;
|
||||
Plasma::DataEngine *m_engine;
|
||||
|
||||
protected slots:
|
||||
void init();
|
||||
|
||||
void init();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // DICTIONARYRUNNER_H
|
||||
|
|
Loading…
Add table
Reference in a new issue