--- /dev/null
+# This file is used to ignore files which are generated
+# ----------------------------------------------------------------------------
+
+*~
+*.a
+*.core
+*.moc
+*.o
+*.obj
+*.orig
+*.rej
+*.so
+*_pch.h.cpp
+*_resource.rc
+*.qm
+.#*
+*.*#
+core
+.qmake.cache
+tags
+.DS_Store
+*.debug
+Makefile*
+*.prl
+*.app
+moc_*.cpp
+ui_*.h
+qrc_*.cpp
+
+# qtcreator generated files
+*.pro.user
+*.pro.user.*
+*.autosave
+
+# xemacs temporary files
+*.flc
+
+# Vim temporary files
+.*.swp
+
+# Visual Studio generated files
+*.ib_pdb_index
+*.idb
+*.ilk
+*.pdb
+*.sln
+*.suo
+*.vcproj
+*vcproj.*.*.user
+*.ncb
+*.exp
+
+# MinGW generated files
+*.Debug
+*.Release
+
+# Directories to ignore
+# ---------------------
+
+build
+debug
+release
+lib/qtsingleapplication/lib
+lib/qtsingleapplication/examples
+lib/qtsingleapplication/doc
+.tmp
+qtc-gdbmacros
+
+# Binaries
+# --------
+build/*.dll
+build/*.lib
+build/*.exe
+build/*.so*
+
+
--- /dev/null
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += pluginapi \
+ launcher \
+ plugins
--- /dev/null
+#include "inputwidget.h"
+
+InputWidget::InputWidget(QWidget *parent) :
+ QLineEdit(parent)
+{
+
+}
+
+QSize InputWidget::minimumSizeHint()
+{
+ return QSize(400, 50);
+}
--- /dev/null
+#ifndef INPUTWIDGET_H
+#define INPUTWIDGET_H
+
+#include <QLineEdit>
+
+class InputWidget : public QLineEdit
+{
+ Q_OBJECT
+public:
+ explicit InputWidget(QWidget *parent = 0);
+
+signals:
+
+public slots:
+// void input
+protected:
+ QSize minimumSizeHint();
+};
+
+#endif // INPUTWIDGET_H
--- /dev/null
+#include "launcher.h"
+
+#include <QAction>
+#include <QMenu>
+#include <QDir>
+#include <QFileInfo>
+#include <QxtGlobalShortcut>
+
+#include "mainwindow.h"
+#include "tray.h"
+#include "plugin.h"
+#include "launcherpluginbase.h"
+#include "resultmodel.h"
+
+#include <QDebug>
+
+Launcher::Launcher(int argc, char **argv) :
+ QApplication(argc, argv)
+{
+ m_instance = this;
+ m_resultModel = new ResultModel(this);
+ m_mainWindow = new MainWindow;
+ m_tray = new Tray;
+
+ connect(this, SIGNAL(aboutToQuit()), m_tray, SLOT(hide()));
+
+ qRegisterMetaType<Reply>("Reply");
+ loadPlugins();
+
+ setWindowIcon(QIcon(":/icon.png"));
+ m_tray->setIcon(windowIcon());
+
+ showAction = new QAction("Show", this);
+ showAction->setShortcut(QKeySequence("Alt+Space"));
+ connect(showAction, SIGNAL(triggered()), m_mainWindow, SLOT(activate()));
+
+ showShortcut = new QxtGlobalShortcut(m_mainWindow);
+ connect(showShortcut, SIGNAL(activated()), showAction, SLOT(trigger()));
+ showShortcut->setShortcut(showAction->shortcut());
+
+ quitAction = new QAction("&Quit", this);
+ connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+
+
+ QMenu *menu = new QMenu;
+ menu->addAction(showAction);
+ menu->addSeparator();
+ menu->addAction(quitAction);
+ m_tray->setContextMenu(menu);
+
+ connect(m_mainWindow, SIGNAL(commandChanged(QString)), this, SLOT(queryPlugins(QString)));
+ connect(m_mainWindow, SIGNAL(commandChanged(QString)), m_resultModel, SLOT(commandChanged(QString)));
+ m_tray->show();
+
+}
+
+Launcher::~Launcher()
+{
+ unloadPlugins();
+ m_instance = 0;
+}
+
+MainWindow *Launcher::mainWindow() const
+{
+ return m_mainWindow;
+}
+
+Tray *Launcher::tray() const
+{
+ return m_tray;
+}
+
+ResultModel *Launcher::resultModel() const
+{
+ return m_resultModel;
+}
+
+void Launcher::queryPlugins(const QString &cmd)
+{
+ if (cmd.isEmpty())
+ return;
+
+ foreach (Plugin *p, plugins)
+ {
+ p->processCommand(cmd);
+ }
+}
+
+void Launcher::updateResults()
+{
+ /*
+ Plugin *p = qobject_cast<Plugin*>(sender());
+
+ foreach(const Result &r, p->latestResult().results)
+ {
+ qDebug() << r.match << r.description;
+ }
+ */
+}
+
+void Launcher::loadPlugins()
+{
+ QDir dir(QDir::current());
+ QStringList nameFilter;
+#if defined(Q_OS_WIN)
+ nameFilter << "*_plugin.dll";
+#elif defined(Q_OS_UNIX)
+ nameFilter << "*_plugin.so";
+#endif
+
+ QFileInfoList pluginFiles = dir.entryInfoList(nameFilter, QDir::Files);
+
+ foreach (const QFileInfo &file, pluginFiles)
+ {
+ qDebug() << "Loading plugin" << file.fileName();
+ Plugin *plugin = new Plugin(file.absoluteFilePath());
+ connect(plugin, SIGNAL(resultRecieved()), this, SLOT(updateResults()));
+ connect(plugin, SIGNAL(resultRecieved()), m_resultModel, SLOT(updateResults()));
+ if (!plugin->load())
+ {
+ qDebug() << "Load failed";
+ continue;
+ }
+
+ plugins.append(plugin);
+
+ }
+}
+
+void Launcher::unloadPlugins()
+{
+ while (plugins.count())
+ {
+ Plugin *p = plugins.takeLast();
+ p->unload();
+ p->deleteLater();
+ }
+ plugins.clear();
+}
+
+Launcher *Launcher::instance()
+{
+ return m_instance;
+}
+
+
+Launcher *Launcher::m_instance = 0;
--- /dev/null
+#ifndef LAUNCHER_H
+#define LAUNCHER_H
+
+#include <QApplication>
+#include "reply.h"
+
+class MainWindow;
+class Tray;
+class QAction;
+class QxtGlobalShortcut;
+
+class Plugin;
+class ResultModel;
+
+class Launcher : public QApplication
+{
+ Q_OBJECT
+public:
+ explicit Launcher(int argc, char **argv);
+ ~Launcher();
+
+ MainWindow *mainWindow() const;
+ Tray *tray() const;
+ ResultModel *resultModel() const;
+
+
+public slots:
+ void queryPlugins(const QString &cmd);
+ void updateResults();
+
+private:
+ void loadPlugins();
+ void unloadPlugins();
+
+ MainWindow *m_mainWindow;
+ Tray *m_tray;
+
+ QAction *showAction;
+ QAction *quitAction;
+ QxtGlobalShortcut *showShortcut;
+
+ QList<Plugin *> plugins;
+
+ ResultModel *m_resultModel;
+
+ static Launcher *m_instance;
+public:
+ static Launcher *instance();
+};
+
+#if defined(qApp)
+# undef qApp
+#endif
+#define qApp Launcher::instance()
+
+#endif // LAUNCHER_H
--- /dev/null
+QT += core gui declarative
+CONFIG += qxt
+QXT *= gui
+
+DESTDIR = ../build
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = launcher
+TEMPLATE = app
+
+
+SOURCES += main.cpp \
+ mainwindow.cpp \
+ inputwidget.cpp \
+ launcher.cpp \
+ tray.cpp
+
+HEADERS += mainwindow.h \
+ inputwidget.h \
+ launcher.h \
+ tray.h
+
+RESOURCES += \
+ resources.qrc
+
+include(../pluginapi/pluginapi.pri)
+
+HEADERS += \
+ pluginthread.h
+
+SOURCES += \
+ pluginthread.cpp
+
+OTHER_FILES += \
+ qml/view.qml
+
+OTHER_FILES += \
+ qml/Components/ResultDelegate.qml
+
+HEADERS += \
+ resultmodel.h
+
+SOURCES += \
+ resultmodel.cpp
+
+HEADERS += \
+ plugin.h
+
+SOURCES += \
+ plugin.cpp
--- /dev/null
+#include "mainwindow.h"
+#include "launcher.h"
+
+int main(int argc, char *argv[])
+{
+ Launcher a(argc, argv);
+
+ return a.exec();
+}
--- /dev/null
+#include "mainwindow.h"
+#include <QKeyEvent>
+#include <QFocusEvent>
+#include <QDesktopWidget>
+#include <QVBoxLayout>
+#include <QDeclarativeView>
+#include <QDeclarativeContext>
+
+#include <QProcess>
+#include <QDesktopServices>
+
+#include "launcher.h"
+#include "resultmodel.h"
+#include "inputwidget.h"
+
+#include <QDebug>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QWidget(parent)
+{
+ setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
+ setAttribute(Qt::WA_TranslucentBackground);
+ desktopWidget = new QDesktopWidget();
+ connect(desktopWidget, SIGNAL(resized(int)), this, SLOT(position()));
+ setupUi();
+}
+
+MainWindow::~MainWindow()
+{
+ delete desktopWidget;
+}
+
+void MainWindow::keyPressEvent(QKeyEvent *e)
+{
+ switch (e->key())
+ {
+ case Qt::Key_Escape:
+ hide();
+ e->accept();
+ break;
+ case Qt::Key_Down:
+ qApp->resultModel()->setSelectedIndex(qApp->resultModel()->selectedIndex() + 1);
+ e->accept();
+ break;
+ case Qt::Key_Up:
+ qApp->resultModel()->setSelectedIndex(qApp->resultModel()->selectedIndex() - 1);
+ e->accept();
+ break;
+ }
+}
+
+void MainWindow::resizeEvent(QResizeEvent *e)
+{
+ QWidget::resizeEvent(e);
+ position();
+}
+
+bool MainWindow::event(QEvent *e)
+{
+ if (e->type() == QEvent::WindowDeactivate)
+ hide();
+
+ return QWidget::event(e);
+}
+
+void MainWindow::position()
+{
+ QRect r = desktopWidget->screenGeometry();
+ move(r.center() - QPoint(width() / 2, height() / 2));
+}
+
+void MainWindow::setupUi()
+{
+ inputWidget = new InputWidget(this);
+ connect(inputWidget, SIGNAL(returnPressed()), this, SLOT(handleReturn()));
+ connect(inputWidget, SIGNAL(textEdited(QString)), this, SIGNAL(commandChanged(QString)));
+
+ view = new QDeclarativeView(this);
+ view->setMinimumHeight(60*5);
+
+ QDeclarativeContext *ctxt = view->rootContext();
+ ctxt->setContextProperty("resultModel", qApp->resultModel());
+ ctxt->setContextProperty("mainWindow", this);
+
+ view->setSource(QUrl("qrc:/qml/view.qml"));
+ view->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+
+
+ view->setAttribute(Qt::WA_OpaquePaintEvent);
+ view->setAttribute(Qt::WA_NoSystemBackground);
+ view->viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
+ view->viewport()->setAttribute(Qt::WA_NoSystemBackground);
+
+ QVBoxLayout *l = new QVBoxLayout();
+ l->setContentsMargins(0, 0, 0, 0);
+ l->addWidget(inputWidget);
+ l->addWidget(view);
+ setLayout(l);
+ resize(700, height());
+}
+
+void MainWindow::activate()
+{
+ show();
+ raise();
+ activateWindow();
+ inputWidget->setFocus();
+}
+
+void MainWindow::handleReturn()
+{
+ QString command = inputWidget->text();
+
+ if (command == "qqq")
+ {
+ qApp->quit();
+ hide();
+ return;
+ }
+
+ hide();
+
+ if (command.isEmpty())
+ return;
+
+ Result r = qApp->resultModel()->selectedResult();
+
+ if (r.command.isEmpty())
+ {
+ qDebug() << "empty command";
+ return;
+ }
+
+ bool success = false;
+ switch (r.commandType)
+ {
+ case RunCommand:
+ success = QProcess::startDetached(r.command, r.args);
+ break;
+ case OpenCommand:
+qDebug() << "opening" << ("file://" + r.command);
+ success = QDesktopServices::openUrl(QUrl::fromUserInput(r.command));
+ break;
+ }
+ qDebug() << "did it work?" << success;
+ inputWidget->clear();
+ qApp->resultModel()->clear();
+}
--- /dev/null
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QWidget>
+
+
+class QDesktopWidget;
+class QDeclarativeView;
+class InputWidget;
+
+class MainWindow : public QWidget
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = 0);
+ ~MainWindow();
+
+public slots:
+ void activate();
+ void handleReturn();
+
+protected:
+ void keyPressEvent(QKeyEvent *e);
+ void resizeEvent(QResizeEvent *e);
+ bool event(QEvent *);
+
+protected slots:
+ void position();
+
+signals:
+ void commandChanged(const QString &cmd);
+
+private:
+ void setupUi();
+
+ QDesktopWidget *desktopWidget;
+ InputWidget *inputWidget;
+ QDeclarativeView *view;
+};
+
+#endif // MAINWINDOW_H
--- /dev/null
+#include "plugin.h"
+
+#include "pluginthread.h"
+#include "launcherpluginbase.h"
+
+Plugin::Plugin(const QString &file, QObject *parent) : QObject(parent),
+ m_pluginThread(new PluginThread(file, this)), executing(false), m_status(Unloaded)
+{
+ unload();
+}
+
+bool Plugin::isCurrentResult() const
+{
+ return !executing && m_lastProcessedCommand == m_latestCommand;
+}
+
+Plugin::Status Plugin::status() const
+{
+ return m_status;
+}
+
+Reply Plugin::latestResult() const
+{
+ return m_latestResult;
+}
+
+Reply Plugin::previousResult() const
+{
+ return m_previousResult;
+}
+
+LauncherPluginBase *Plugin::pluginInstance()
+{
+ return m_pluginThread->plugin();
+}
+
+bool Plugin::load()
+{
+ if (m_status != Unloaded)
+ return false;
+
+ bool r = m_pluginThread->startThread();
+ connect(m_pluginThread->plugin(), SIGNAL(replyReady(Reply)), this, SLOT(handleReply(Reply)), Qt::QueuedConnection);
+ m_status = r ? Loaded : Error;
+
+ return r;
+}
+
+void Plugin::unload()
+{
+ if (m_status != Loaded)
+ return;
+ m_pluginThread->stopThread();
+ m_status = Unloaded;
+}
+
+void Plugin::processCommand(const QString &cmd)
+{
+ if (m_lastProcessedCommand == cmd)
+ return;
+
+ m_latestCommand = cmd;
+
+ if (executing)
+ return;
+ executing = true;
+
+ m_lastProcessedCommand = cmd;
+
+ QMetaObject::invokeMethod(m_pluginThread->plugin(), "processCommand", Qt::QueuedConnection,
+ Q_ARG(QString, cmd));
+}
+
+void Plugin::handleReply(const Reply &reply)
+{
+ m_previousResult = m_latestResult;
+ m_latestResult = reply;
+ executing = false;
+
+ if (m_lastProcessedCommand != m_latestCommand)
+ processCommand(m_latestCommand);
+
+ emit resultRecieved();
+}
--- /dev/null
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
+#include <QObject>
+
+#include "reply.h"
+
+class PluginThread;
+class LauncherPluginBase;
+
+class Plugin : public QObject
+{
+ Q_OBJECT
+public:
+ enum Status {
+ Unloaded,
+ Loaded,
+ Error
+ };
+
+ explicit Plugin(const QString &file, QObject *parent = 0);
+
+ bool isCurrentResult() const;
+ Status status() const;
+ Reply latestResult() const;
+ Reply previousResult() const;
+
+ LauncherPluginBase *pluginInstance();
+
+signals:
+ void resultRecieved();
+
+public slots:
+ bool load();
+ void unload();
+
+ void processCommand(const QString &cmd);
+
+private slots:
+ void handleReply(const Reply &reply);
+
+private:
+ bool executing;
+ Status m_status;
+
+ QString m_lastProcessedCommand;
+ QString m_latestCommand;
+
+ Reply m_previousResult;
+ Reply m_latestResult;
+
+ PluginThread *m_pluginThread;
+};
+
+#endif // PLUGIN_H
--- /dev/null
+#include "pluginthread.h"
+
+#include <QPluginLoader>
+#include "launcherpluginbase.h"
+
+#include <QDebug>
+
+PluginThread::PluginThread(const QString &file, QObject *parent) : QThread(parent),
+ loaded(false), m_file(file), m_pluginLoader(0), m_plugin(0), m_waitStarted(0)
+{
+}
+
+bool PluginThread::startThread()
+{
+ start();
+ m_waitStarted.acquire();
+ return loaded;
+}
+
+void PluginThread::stopThread()
+{
+ quit();
+ m_waitStarted.acquire();
+}
+
+LauncherPluginBase *PluginThread::plugin()
+{
+ return m_plugin;
+}
+
+void PluginThread::run()
+{
+ m_pluginLoader = new QPluginLoader(m_file);
+ Q_ASSERT(m_pluginLoader->thread() == this);
+
+ if (!m_pluginLoader->load())
+ {
+ qDebug() << "Load error:" << m_pluginLoader->errorString();
+ m_waitStarted.release();
+ delete m_pluginLoader;
+ return;
+ }
+ qDebug() << "loaded!";
+ loaded = true;
+
+ m_plugin = qobject_cast<LauncherPluginBase*>(m_pluginLoader->instance());
+ m_waitStarted.release();
+ m_plugin->init();
+
+ exec();
+
+ m_plugin->deinit();
+ bool s = m_pluginLoader->unload();
+ m_plugin = 0;
+ loaded = false;
+
+ delete m_pluginLoader;
+
+ qDebug() << "Unload succeeded?" << s;
+
+ m_waitStarted.release();
+}
+
--- /dev/null
+#ifndef PLUGINTHREAD_H
+#define PLUGINTHREAD_H
+
+#include <QThread>
+#include <QSemaphore>
+
+class QPluginLoader;
+class LauncherPluginBase;
+
+class PluginThread : public QThread
+{
+ Q_OBJECT
+public:
+ explicit PluginThread(const QString &file, QObject *parent = 0);
+
+ bool startThread();
+ void stopThread();
+ LauncherPluginBase *plugin();
+
+protected:
+ void run();
+
+
+private:
+ bool loaded;
+ QString m_file;
+ QPluginLoader *m_pluginLoader;
+ LauncherPluginBase *m_plugin;
+ QSemaphore m_waitStarted;
+};
+
+#endif // PLUGINTHREAD_H
--- /dev/null
+import QtQuick 1.0
+
+Component {
+ Item {
+ id: wrapper
+
+ property bool open: false
+
+ height: 50
+ width: listView.width - 10
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ MouseArea {
+ id: mouseArea
+ hoverEnabled: true
+ anchors.fill: parent
+
+ onClicked: {
+ resultModel.selectedIndex = index
+ mainWindow.handleReturn()
+ }
+ }
+
+ Rectangle {
+ id: bg
+ x: 2
+ y: 2
+
+ width: parent.width - 2 * x
+ height: parent.height - 2 * y
+
+ radius: 10
+
+ border.width: resultModel.selectedIndex == index || mouseArea.containsMouse ? 2 : 0
+ border.color: resultModel.selectedIndex == index ? "red" : mouseArea.containsMouse ? "yellow" : "#000000"
+ opacity: 0.90
+ color: isCurrent ? "steelblue" : "grey"
+
+ Item {
+ anchors.leftMargin: 10
+ anchors.rightMargin: 10
+ anchors.fill: parent
+
+ Text {
+ id: pluginNameText
+ font.italic: true
+ text: pluginName
+ anchors.right: parent.right
+ }
+
+ Text {
+ id: resultMatchText
+ anchors.centerIn: parent
+ text: resultMatch
+ }
+
+ Text {
+ id: resultDescritpionText
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ elide: Text.ElideRight
+ text: resultDescription
+ }
+ }
+ }
+ states: [
+ State {
+ when: mainWindow.selectedIndex == index
+ PropertyChanges {
+ target: wrapper
+ height: 100
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ PropertyAnimation {
+ properties: "x,y,height,width,opacity"; easing.type: Easing.InOutQuad; duration: 300
+ }
+ }
+ ]
+
+ ListView.onAdd: SequentialAnimation {
+ PropertyAction { target: wrapper; property: "opacity"; value: 0 }
+ PropertyAction { target: wrapper; property: "height"; value: 0 }
+ ParallelAnimation {
+ NumberAnimation { target: wrapper; property: "height"; to: 50; duration: 500; easing.type: Easing.InOutQuad }
+ NumberAnimation { target: wrapper; property: "opacity"; to: 1; duration: 500; easing.type: Easing.InOutQuad }
+ }
+ }
+
+ ListView.onRemove: SequentialAnimation {
+ PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true }
+ ParallelAnimation {
+ NumberAnimation { target: wrapper; property: "height"; to: 0; duration: 500; easing.type: Easing.InOutQuad }
+ NumberAnimation { target: wrapper; property: "opacity"; to: 0; duration: 500; easing.type: Easing.InOutQuad }
+ }
+ PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
+ }
+ }
+}
--- /dev/null
+import QtQuick 1.0
+import "Components" 1.0 as Components
+
+Item {
+ id: main
+
+ signal resultSelected
+
+// opacity: 0.1
+
+ ListModel {
+ id: dummyModel
+ ListElement
+ {
+ pluginName: "foo"
+ resultMatch: "foo"
+ }
+ }
+
+ ListView {
+ id: listView
+
+ anchors.fill: parent
+ model: resultModel
+ delegate: Components.ResultDelegate {}
+ footer: Item {
+ width: listView.width
+ height: listView.height / 2
+ }
+ }
+}
--- /dev/null
+<RCC>
+ <qresource prefix="/">
+ <file>icon.png</file>
+ <file>qml/view.qml</file>
+ <file>qml/Components/ResultDelegate.qml</file>
+ </qresource>
+</RCC>
--- /dev/null
+#include "resultmodel.h"
+
+#include "plugin.h"
+#include "launcherpluginbase.h"
+
+#include <QDebug>
+
+ResultModel::ResultModel(QObject *parent) :
+ QAbstractListModel(parent), rowCountDelta(0), emptyCommand(true)
+{
+ QHash<int, QByteArray> roles;
+ roles[PluginName] = "pluginName";
+ roles[ResultMatch] = "resultMatch";
+ roles[ResultDescription] = "resultDescription";
+ roles[ResultCommand] = "resultCommand";
+ roles[IsCurrent] = "isCurrent";
+ setRoleNames(roles);
+}
+
+ResultModel::~ResultModel()
+{
+ qDeleteAll(results);
+}
+
+int ResultModel::selectedIndex() const
+{
+ return m_selectedIndex;
+}
+
+Result ResultModel::selectedResult() const
+{
+ qDebug() << "selectedResult" << m_selectedIndex;
+ if (m_selectedIndex > rowCount())
+ return Result();
+
+ return results[m_selectedIndex]->result;
+}
+
+void ResultModel::setSelectedIndex(int idx)
+{
+ if (!rowCount() || idx >= rowCount())
+ idx = 0;
+ if (idx < 0)
+ idx = rowCount() - 1;
+
+ if (m_selectedIndex != idx)
+ {
+ m_selectedIndex = idx;
+ emit selectedIndexChanged(idx);
+ }
+}
+
+void ResultModel::clear()
+{
+ beginRemoveRows(QModelIndex(), 0, rowCount());
+ pluginOrder.clear();
+ qDeleteAll(results);
+ results.clear();
+ endRemoveRows();
+}
+
+QVariant ResultModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0)
+ return QVariant("negative index");
+
+ PluginResult *pluginResult = results[index.row()];
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ case PluginName:
+ return pluginResult->plugin->pluginInstance()->name();
+ break;
+ case ResultMatch:
+ return pluginResult->result.match;
+ case ResultDescription:
+ return pluginResult->result.description;
+ break;
+ case ResultCommand:
+ return pluginResult->result.command;
+ break;
+ case IsCurrent:
+ return pluginResult->isCurrent;
+ break;
+ default:
+ return QVariant();
+ break;
+ }
+ return QVariant();
+}
+
+int ResultModel::rowCount(const QModelIndex &) const
+{
+ return results.count();
+}
+
+void ResultModel::commandChanged(const QString &cmd)
+{
+ emptyCommand = cmd.isEmpty();
+
+ if (emptyCommand)
+ {
+ clear();
+ return;
+ }
+
+ for (int i = 0; i < results.count(); ++i)
+ results[i]->isCurrent = false;
+
+ emit dataChanged(index(0, 0), index(rowCount() - 1, 0));
+}
+
+void ResultModel::updateResults()
+{
+ if (emptyCommand)
+ return;
+
+ int oldCount = rowCount();
+
+ Plugin *plugin = qobject_cast<Plugin*>(sender());
+ int idx = pluginOrder.indexOf(plugin);
+
+ if (idx == -1)
+ {
+ if (!plugin->latestResult().results.count())
+ return;
+ pluginOrder << plugin;
+ }
+
+ beginResetModel();
+
+ qDeleteAll(results);
+ results.clear();
+
+ for (int i = 0; i < pluginOrder.count(); ++i)
+ {
+ Plugin *p = pluginOrder[i];
+
+ for (int j = 0; j < p->latestResult().results.count(); ++j)
+ {
+ Result r = p->latestResult().results[j];
+ PluginResult *pr = new PluginResult;
+ pr->isCurrent = true;
+ pr->plugin = p;
+ pr->result = r;
+ results << pr;
+ }
+ }
+ endResetModel();
+
+/* Plugin *plugin = qobject_cast<Plugin*>(sender());
+ auto it = pluginOrder.find(plugin);
+ int pluginIdx;
+ int resultIdx;
+ int oldResultCount;
+ int newResultCount;
+
+ if (it == resultMap.end())
+ {
+ if (!plugin->latestResult().results.count())
+ return;
+
+ pluginOrder.append(plugin);
+
+ pluginIdx = pluginOrder.count();
+
+ PluginResult *pr = new PluginResult;
+ pr->plugin = plugin;
+
+ results.append(pr);
+ resultMap.insert(plugin, pr);
+
+ pluginIdx = results.count() - 1;
+ resultIdx = rowCount();
+ oldResultCount = 0;
+ }
+
+ if (it != resultMap.end())
+ {
+ pluginIdx = results.indexOf(it.value());
+ resultIdx = 0;
+ for (int i = 0; i < pluginIdx; ++i)
+ resultIdx += results[i]->count();
+
+ oldResultCount = plugin->previousResult().results.count();
+ }
+
+ newResultCount = plugin->latestResult().results.count();
+
+ results[pluginIdx]->isCurrent = plugin->isCurrentResult();
+
+ if (oldResultCount == newResultCount)
+ {
+qDebug() << "Updating rows" << newResultCount;
+
+ if (newResultCount)
+ updateResultsForRange(resultIdx, newResultCount);
+ }
+ else if(oldResultCount < newResultCount)
+ {
+qDebug() << "Adding rows";
+ if (oldResultCount)
+ updateResultsForRange(resultIdx, oldResultCount);
+// rowCountDelta = -(newResultCount - oldResultCount);
+ beginInsertRows(QModelIndex(), resultIdx + oldResultCount, resultIdx + newResultCount - 1);
+ rowCountDelta = 0;
+ endInsertRows();
+ }
+ else
+ {
+qDebug() << "Removing rows";
+// rowCountDelta = oldResultCount - newResultCount;
+ beginRemoveRows(QModelIndex(), resultIdx + newResultCount, resultIdx + oldResultCount - 1);
+ rowCountDelta = 0;
+ endRemoveRows();
+ if (newResultCount)
+ updateResultsForRange(resultIdx, newResultCount);
+ }
+*/
+ if (oldCount != rowCount())
+ setSelectedIndex(0);
+}
+
+void ResultModel::updateResultsForRange(int start, int end)
+{
+qDebug() << "Data changed" << start << (start+end-1);
+ QModelIndex sidx = index(start, 0);
+ QModelIndex eidx = index(start + end - 1, 0);
+ emit dataChanged(sidx, eidx);
+}
--- /dev/null
+#ifndef RESULTMODEL_H
+#define RESULTMODEL_H
+
+#include <QAbstractListModel>
+#include "reply.h"
+
+class Plugin;
+
+class ResultModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(int selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY selectedIndexChanged())
+public:
+ struct PluginResult {
+ bool isCurrent;
+ Plugin *plugin;
+ Result result;
+ };
+
+ enum Roles
+ {
+ PluginName = Qt::UserRole + 1,
+ ResultMatch,
+ ResultDescription,
+ ResultCommand,
+ IsCurrent
+ };
+
+ explicit ResultModel(QObject *parent = 0);
+ ~ResultModel();
+
+ QVariant data(const QModelIndex &index, int role) const;
+
+ int rowCount(const QModelIndex & = QModelIndex()) const;
+
+ int selectedIndex() const;
+ Result selectedResult() const;
+
+public slots:
+ void commandChanged(const QString &cmd);
+ void updateResults();
+
+ void setSelectedIndex(int idx);
+
+ void clear();
+
+signals:
+ void selectedIndexChanged(int idx);
+
+private:
+ void updateResultsForRange(int start, int end);
+
+ QList<Plugin*> pluginOrder;
+ QList<PluginResult*> results;
+ int rowCountDelta;
+
+ int m_selectedIndex;
+
+ bool emptyCommand;
+};
+
+#endif // RESULTMODEL_H
--- /dev/null
+#include "tray.h"
+
+#include <QMenu>
+
+Tray::Tray(QObject *parent) :
+ QSystemTrayIcon(parent)
+{
+}
--- /dev/null
+#ifndef TRAY_H
+#define TRAY_H
+
+#include <QSystemTrayIcon>
+
+class Tray : public QSystemTrayIcon
+{
+ Q_OBJECT
+public:
+ explicit Tray(QObject *parent = 0);
+
+signals:
+
+public slots:
+
+};
+
+#endif // TRAY_H
--- /dev/null
+#include "launcherpluginbase.h"
+
+LauncherPluginBase::LauncherPluginBase(QObject *parent) :
+ QObject(parent)
+{
+}
+
+LauncherPluginBase *LauncherPluginBase::plugin()
+{
+ return this;
+}
+
+QStringList LauncherPluginBase::supportedPrefixes() const
+{
+ return QStringList();
+}
+
+bool LauncherPluginBase::handleNonPrefixedCommands() const
+{
+ return true;
+}
+
+bool LauncherPluginBase::init()
+{
+ return true;
+}
+
+void LauncherPluginBase::deinit()
+{
+}
+
+void LauncherPluginBase::processCommand(const QString &cmd)
+{
+ handleCommand(cmd);
+}
--- /dev/null
+#ifndef LOADERPLUGINBASE_H
+#define LOADERPLUGINBASE_H
+
+#include "pluginapi_global.h"
+#include <QObject>
+#include "launcherplugininterface.h"
+
+#include "reply.h"
+
+class PLUGINAPISHARED_EXPORT LauncherPluginBase : public QObject, public LauncherPluginInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(LauncherPluginInterface)
+public:
+ explicit LauncherPluginBase(QObject *parent = 0);
+
+
+ LauncherPluginBase *plugin();
+
+ QStringList supportedPrefixes() const;
+ bool handleNonPrefixedCommands() const;
+
+ bool init();
+ void deinit();
+
+ virtual void handleCommand(const QString &cmd) = 0;
+
+public slots:
+ void processCommand(const QString &cmd);
+
+signals:
+ void replyReady(const Reply &match);
+
+};
+
+#endif // LOADERPLUGINBASE_H
--- /dev/null
+#ifndef LAUNCHERPLUGIN_H
+#define LAUNCHERPLUGIN_H
+
+#include "pluginapi_global.h"
+#include <QStringList>
+
+class LauncherPluginBase;
+
+class PLUGINAPISHARED_EXPORT LauncherPluginInterface
+{
+public:
+ virtual ~LauncherPluginInterface() {}
+
+ virtual LauncherPluginBase *plugin() = 0;
+
+ virtual QString name() const = 0;
+
+ virtual QStringList supportedPrefixes() const = 0;
+ virtual bool handleNonPrefixedCommands() const = 0;
+
+ virtual bool init() = 0;
+ virtual void deinit() = 0;
+};
+
+Q_DECLARE_INTERFACE(LauncherPluginInterface, "org.aptx.launcher.pluginapi/1.0")
+
+#endif // LAUNCHERPLUGIN_H
--- /dev/null
+#include "loaderplugin.h"
+
+
+LoaderPluginInterface::LoaderPluginInterface()
+{
+}
--- /dev/null
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+LIBS += -lpluginapi
+LIBS += -L$$PWD/../build
--- /dev/null
+QT -= gui
+
+DESTDIR = ../build
+TARGET = pluginapi
+TEMPLATE = lib
+
+DEFINES += PLUGINAPI_LIBRARY
+
+HEADERS += launcherplugininterface.h \
+ pluginapi_global.h \
+ launcherpluginbase.h \
+ reply.h
+
+
+
+SOURCES += \
+ launcherpluginbase.cpp \
+ reply.cpp
--- /dev/null
+#ifndef PLUGINAPI_GLOBAL_H
+#define PLUGINAPI_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(PLUGINAPI_LIBRARY)
+# define PLUGINAPISHARED_EXPORT Q_DECL_EXPORT
+#else
+# define PLUGINAPISHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // PLUGINAPI_GLOBAL_H
--- /dev/null
+#include "reply.h"
+
+Result::Result()
+{
+ commandType = RunCommand;
+}
+
+Result::Result(const QString &match_, const QString &description_, CommandType commandType_,
+ const QString &command_, const QStringList &args_) :
+ match(match_), description(description_), commandType(commandType_),
+ command(command_), args(args_)
+{
+}
+
+Reply::Reply(const QVector<Result> &results_) :
+ results(results_)
+{
+}
+
+void Reply::addResult(const Result &result)
+{
+ results.append(result);
+}
+
+
--- /dev/null
+#ifndef REPLY_H
+#define REPLY_H
+
+#include "pluginapi_global.h"
+#include <QMetaType>
+#include <QVector>
+#include <QStringList>
+
+enum CommandType {
+ RunCommand,
+ OpenCommand
+};
+
+struct PLUGINAPISHARED_EXPORT Result {
+ QString match;
+ QString description;
+
+ CommandType commandType;
+ QString command;
+ QStringList args;
+
+ explicit Result();
+ Result(const QString &match_, const QString &description_ = QString(),
+ CommandType commandType_ = RunCommand, const QString &command_ = QString(), const QStringList &args_ = QStringList());
+};
+
+struct PLUGINAPISHARED_EXPORT Reply {
+ QVector<Result> results;
+
+ Reply(const QVector<Result> &results_ = QVector<Result>());
+ void addResult(const Result &result);
+};
+
+Q_DECLARE_METATYPE(Reply)
+Q_DECLARE_METATYPE(Reply*)
+
+#endif // MATCH_H
--- /dev/null
+QT -= gui
+QT += sql
+
+DESTDIR = ../../build
+CONFIG += plugin
+TARGET = localmylist_plugin
+TEMPLATE = lib
+
+DEFINES += LOCALMYLISTPLUGIN_LIBRARY
+
+SOURCES += localmylistplugin.cpp
+
+HEADERS += localmylistplugin.h\
+ localmylistplugin_global.h
+
+include(../../pluginapi/pluginapi.pri)
+
+LIBS += -llocalmylist
--- /dev/null
+#include "localmylistplugin.h"
+
+#include <LocalMyList/MyList>
+
+#include <QDebug>
+
+using namespace LocalMyList;
+
+LocalMyListPlugin::LocalMyListPlugin(QObject *parent) : LauncherPluginBase(parent)
+{
+}
+
+bool LocalMyListPlugin::init()
+{
+ MyList::MANUAL_CLEANUP = true;
+
+ LocalMyList::instance()->loadLocalSettings();
+ if (!LocalMyList::instance()->database()->connect())
+ {
+ qDebug() << "Could not connect to database.";
+ return false;
+ }
+ return true;
+}
+
+void LocalMyListPlugin::deinit()
+{
+ MyList::destroy();
+}
+
+QString LocalMyListPlugin::name() const
+{
+ return "LocalMyList";
+}
+
+void LocalMyListPlugin::handleCommand(const QString &cmd)
+{
+ Reply re;
+
+ QSqlQuery &q = LocalMyList::instance()->database()->prepare(
+ "SELECT DISTINCT a.aid, b.title AS MainTitle, a.title AS searchTitle FROM anime_title a"
+ " LEFT JOIN anime_title b on b.aid = a.aid "
+// " LEFT JOIN episode e ON e.aid = a.aid "
+// " LEFT JOIN file f ON f.aid = a.aid "
+// " LEFT JOIN file_location fl ON fl.fid = f.fid "
+ " WHERE lower(a.title) LIKE :title "
+ " AND b.type = 1 "
+ " ORDER BY a.title ASC, a.aid ASC "
+ );
+
+ q.bindValue(":title", cmd);
+ if (!LocalMyList::instance()->database()->exec(q))
+ {
+ emit replyReady(re);
+ return;
+ }
+ addResults(re, q);
+
+ q.bindValue(":title", cmd + "%");
+ if (!LocalMyList::instance()->database()->exec(q))
+ {
+ emit replyReady(re);
+ return;
+ }
+ addResults(re, q);
+
+ q.bindValue(":title", "%" + cmd + "%");
+ if (!LocalMyList::instance()->database()->exec(q))
+ {
+ emit replyReady(re);
+ return;
+ }
+ addResults(re, q);
+
+ emit replyReady(re);
+}
+
+void LocalMyListPlugin::addResults(Reply &re, QSqlQuery &q)
+{
+ while(q.next())
+ {
+ Result r;
+ r.match = q.value(2).toString();
+ r.description = QString("Main title: %1").arg(q.value(1).toString());
+// r.commandType = OpenCommand;
+// r.command = ofd.path;
+ re.addResult(r);
+ }
+ q.finish();
+}
+
+#include <QtPlugin>
+Q_EXPORT_PLUGIN2(localmylist_plugin, LocalMyListPlugin)
--- /dev/null
+#ifndef LOCALMYLISTPLUGIN_H
+#define LOCALMYLISTPLUGIN_H
+
+#include "localmylistplugin_global.h"
+#include <launcherpluginbase.h>
+
+#include <QSqlQuery>
+
+class LOCALMYLISTPLUGINSHARED_EXPORT LocalMyListPlugin : public LauncherPluginBase
+{
+public:
+ LocalMyListPlugin(QObject *parent = 0);
+
+ bool init();
+ void deinit();
+
+ QString name() const;
+ void handleCommand(const QString &cmd);
+
+private:
+ void addResults(Reply &re, QSqlQuery &q);
+};
+
+#endif // LOCALMYLIST_H
--- /dev/null
+#ifndef LOCALMYLISTPLUGIN_GLOBAL_H
+#define LOCALMYLISTPLUGIN_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(LOCALMYLISTPLUGIN_LIBRARY)
+# define LOCALMYLISTPLUGINSHARED_EXPORT Q_DECL_EXPORT
+#else
+# define LOCALMYLISTPLUGINSHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // LOCALMYLISTPLUGIN_GLOBAL_H
--- /dev/null
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += test \
+ localmylist
--- /dev/null
+#include "test.h"
+
+Test::Test(QObject *parent) : LauncherPluginBase(parent)
+{
+}
+
+QString Test::name() const
+{
+ return "Test";
+}
+
+void Test::handleCommand(const QString &cmd)
+{
+ Reply re;
+ if (cmd.isEmpty())
+ {
+ emit replyReady(re);
+ return;
+ }
+
+ int z=0;
+ for(int i =0; i<100000000;++i) z+=cmd.length();
+
+ Result r(cmd, "Echo for " + cmd + " z=" + QString::number(z), RunCommand, "cmd", QStringList("dir"));
+
+ re.addResult(r);
+ r.match += " 1";
+ re.addResult(r);
+ r.match += " 2";
+ re.addResult(r);
+ emit replyReady(re);
+}
+
+#include <QtPlugin>
+Q_EXPORT_PLUGIN2(test_plugin, Test)
--- /dev/null
+#ifndef TEST_H
+#define TEST_H
+
+#include "test_global.h"
+#include <launcherpluginbase.h>
+
+
+class TESTSHARED_EXPORT Test : public LauncherPluginBase
+{
+public:
+ Test(QObject *parent = 0);
+
+ QString name() const;
+ void handleCommand(const QString &cmd);
+};
+
+#endif // TEST_H
--- /dev/null
+QT -= gui
+
+DESTDIR = ../../build
+CONFIG += plugin
+TARGET = test_plugin
+TEMPLATE = lib
+
+DEFINES += TEST_LIBRARY
+
+SOURCES += test.cpp
+
+HEADERS += test.h\
+ test_global.h
+
+unix:!symbian {
+ maemo5 {
+ target.path = /opt/usr/lib
+ } else {
+ target.path = /usr/lib
+ }
+ INSTALLS += target
+}
+
+include(../../pluginapi/pluginapi.pri)
--- /dev/null
+#ifndef TEST_GLOBAL_H
+#define TEST_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(TEST_LIBRARY)
+# define TESTSHARED_EXPORT Q_DECL_EXPORT
+#else
+# define TESTSHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // TEST_GLOBAL_H