--- /dev/null
+#include <Plasma/AbstractRunner>
+#include <QHash>
+#include <KRun>
+#include <QMutex>
+#include <QThread>
+#include <LocalMyList/MyList>
+#include <LocalMyList/Database>
+
+using namespace LocalMyList;
+
+
+// LML set up can not be in init or prepare. The database can only be used from
+// one thread and all matches can run from different threads.
+class RaiiLml
+{
+public:
+ RaiiLml() : mutex{}, locker{&mutex}
+ {
+ if (MyList::instance()->thread() != QThread::currentThread())
+ {
+ qWarning("MyList instance created from a different thread");
+ ok = false;
+ return;
+ }
+ MyList::instance()->loadLocalSettings();
+ connected_ = MyList::instance()->database()->connect();
+ }
+ ~RaiiLml()
+ {
+ if (!ok) return;
+
+ MyList::instance()->database()->disconnect();
+ MyList::instance()->destroy();
+ }
+
+ operator bool () const { return ok; }
+ bool connected() const { return connected_; }
+
+private:
+ QMutex mutex;
+ QMutexLocker locker;
+ bool ok;
+ bool connected_;
+};
+
+class LocalMyListRunner : public Plasma::AbstractRunner
+{
+ Q_OBJECT
+
+public:
+ LocalMyListRunner(QObject *parent, const QVariantList &args);
+
+ void match(Plasma::RunnerContext &context);
+ void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match);
+ void reloadConfiguration();
+
+protected Q_SLOTS:
+ void init();
+ void prepareForMatchSession();
+ void matchSessionFinished();
+
+private:
+ QString m_path;
+ QString m_triggerWord;
+};
+
+
+
+LocalMyListRunner::LocalMyListRunner(QObject *parent, const QVariantList &args)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(args);
+ setSpeed(SlowSpeed);
+ setPriority(LowPriority);
+ setHasRunOptions(false);
+}
+
+void LocalMyListRunner::match(Plasma::RunnerContext &context)
+{
+ QString query = context.query();
+
+ if (!query.startsWith(m_triggerWord))
+ return;
+
+ query.remove(0, m_triggerWord.length());
+ QString searchQuery = toSearchQuery(query);
+
+ qDebug("Initialize LML");
+ RaiiLml lml;
+ if (!lml)
+ return;
+
+ if (!lml.connected())
+ {
+ Plasma::QueryMatch match(this);
+ match.setText(i18n("Could not connect to the database"));
+ match.setSubtext(i18n("Make sure you have configured LocalMyList correctly."));
+ match.setType(Plasma::QueryMatch::InformationalMatch);
+ match.setRelevance(0.0);
+ context.addMatch(context.query(), match);
+ return;
+ }
+
+ QList<AnimeTitle> titles = MyList::instance()->database()->myListSearch(searchQuery, 10);
+
+ if (titles.isEmpty())
+ titles = MyList::instance()->database()->myListSearchSimmilar(searchQuery);
+
+ QList<Plasma::QueryMatch> matches;
+
+ QSet<int> fids;
+ for (const AnimeTitle &title : titles)
+ {
+ if (!context.isValid())
+ break;
+
+ OpenFileData ofd = MyList::instance()->database()->firstUnwatchedByAid(title.aid);
+
+ Plasma::QueryMatch match(this);
+ if (ofd.fid)
+ {
+ if (fids.contains(ofd.fid))
+ continue;
+
+ match.setText(i18n("Play %1 - %2").arg(ofd.animeTitle).arg(ofd.epno));
+ match.setSubtext(QString("%1")
+ .arg(ofd.episodeTitle));
+
+ match.setData(ofd.localPath);
+ match.setType(Plasma::QueryMatch::CompletionMatch);
+
+ if (title.title.compare(query.trimmed(), Qt::CaseInsensitive))
+ {
+ match.setRelevance(1.0);
+ match.setType(Plasma::QueryMatch::ExactMatch);
+ }
+ else
+ {
+ match.setRelevance(0.8);
+ }
+
+ fids << ofd.fid;
+ }
+ else
+ {
+ match.setText(i18n("Nothing to watch for %1").arg(title.title));
+ match.setType(Plasma::QueryMatch::InformationalMatch);
+ match.setRelevance(0.0);
+ }
+ matches << match;
+ }
+
+ context.addMatches(context.query(), matches);
+}
+
+void LocalMyListRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
+{
+ Q_UNUSED(context);
+ KRun *opener = new KRun(match.data().toString(), 0);
+ opener->setRunExecutables(false);
+}
+
+void LocalMyListRunner::reloadConfiguration()
+{
+ m_triggerWord = "lml";
+
+ Plasma::RunnerSyntax syntax(QString("%1:q:").arg(m_triggerWord),
+ i18n("Play the first unwatched episode of :q:"));
+
+ setDefaultSyntax(syntax);
+
+}
+
+void LocalMyListRunner::init()
+{
+ reloadConfiguration();
+ connect(this, SIGNAL(prepare()), this, SLOT(prepareForMatchSession()));
+ connect(this, SIGNAL(teardown()), this, SLOT(matchSessionFinished()));
+}
+
+void LocalMyListRunner::prepareForMatchSession()
+{
+}
+
+void LocalMyListRunner::matchSessionFinished()
+{
+}
+
+K_EXPORT_PLASMA_RUNNER(localmylist, LocalMyListRunner)
+
+#include "main.moc"