Alt+Space hotkey, tray icon

This commit is contained in:
Jerzy Kozera 2013-01-20 20:47:19 +00:00
parent f817f9375d
commit 88011d5009
4 changed files with 155 additions and 6 deletions

View File

@ -28,7 +28,7 @@ Do `tar -jxvf file.tar.bz2` in docsets directory to enable given docset.
## TODO
* Alt+Space / configurable hotkey for showing Zeal + tray icon
* Configuration (customisable hotkey instead of hardcoded Alt+Space, remember window size, etc.)
* Search enhancements - some ideas:
1. Allow selecting subset of docsets to search in.
2. Substring match in case current startswith matching doesn't return anything.

View File

@ -1,11 +1,84 @@
#include "mainwindow.h"
#include <QApplication>
#include <QLocalSocket>
#include <string>
#include <iostream>
#include <X11/Xlib.h>
#include <X11/keysym.h>
using namespace std;
void run_grabkey();
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
if(argc > 1 && argv[1] == string("--grabkey")) {
run_grabkey();
return -1; // infinite loop - shouldn't terminate
} else {
// detect already running instance
QLocalSocket socket;
socket.connectToServer(serverName);
if (socket.waitForConnected(500)) {
cerr << "Already running. Terminating." << endl;
return -1; // Exit already a process running
}
return a.exec();
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
}
void run_grabkey()
{
Display *display = XOpenDisplay(NULL);
unsigned int AltMask = 0;
XModifierKeymap *xmk = XGetModifierMapping(display);
if(xmk) {
KeyCode altKeyCode = XKeysymToKeycode(display, XK_Alt_L);
KeyCode *c = xmk->modifiermap;
int m, k;
for(m = 0; m < 8; m++) {
for(k = 0; k < xmk->max_keypermod; k++, c++)
{
if(*c == NoSymbol) {
continue;
}
if(*c == altKeyCode) {
AltMask = (1 << m);
}
}
}
XFreeModifiermap (xmk);
}
if(AltMask == 0) {
cout << "failed" << endl;
} else {
cout << "ok" << endl;
}
KeyCode hotKey = XKeysymToKeycode(display, XStringToKeysym("space"));
XGrabKey(display, hotKey, AnyModifier, DefaultRootWindow(display), True, GrabModeSync, GrabModeSync);
XEvent e;
for(;;)
{
XNextEvent(display, &e);
if(e.type == KeyPress){
if(e.xkey.keycode == hotKey && e.xkey.state & AltMask)
{
cout << "1" << endl;
XAllowEvents(display, AsyncKeyboard, e.xkey.time);
} else {
XAllowEvents(display, ReplayKeyboard, e.xkey.time);
XFlush(display);
}
}
}
}

View File

@ -9,12 +9,48 @@ using namespace std;
#include <QStandardPaths>
#include <QMessageBox>
#include <QSystemTrayIcon>
#include <QLocalSocket>
#include <QDir>
const QString serverName = "zeal_process_running";
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
// server for detecting already running instances
localServer = new QLocalServer(this);
connect(localServer, &QLocalServer::newConnection, [&]() {
bringToFront();
});
QLocalServer::removeServer(serverName); // remove in case previous instance crashed
localServer->listen(serverName);
// initialise icons
setWindowIcon(QIcon::fromTheme("edit-find"));
createTrayIcon();
// initialise key grabber
keyGrabber.setParent(this);
keyGrabber.start(qApp->applicationFilePath(), {"--grabkey"});
connect(&keyGrabber, &QProcess::readyRead, [&]() {
char buf[100];
keyGrabber.readLine(buf, sizeof(buf));
if(QString(buf) == "failed\n") {
QMessageBox::warning(this, "grabkey process init failed",
QString("Failed to grab keyboard - Alt+Space will not work."));
}
//first = false;
if(QString(buf) == "1\n") {
if(isVisible()) hide();
else {
bringToFront();
}
}
});
// initialise docsets
auto dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
auto dataDir = QDir(dataLocation);
if(!dataDir.cd("docsets")) {
@ -28,6 +64,8 @@ MainWindow::MainWindow(QWidget *parent) :
}
}
}
// initialise ui
ui->setupUi(this);
ui->lineEdit->setTreeView(ui->treeView);
ui->treeView->setModel(&zealList);
@ -53,4 +91,34 @@ MainWindow::MainWindow(QWidget *parent) :
MainWindow::~MainWindow()
{
delete ui;
delete localServer;
keyGrabber.terminate();
keyGrabber.waitForFinished(500);
}
void MainWindow::createTrayIcon()
{
auto trayIconMenu = new QMenu(this);
auto quitAction = trayIconMenu->addAction("&Quit");
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
auto trayIcon = new QSystemTrayIcon(this);
trayIcon->setContextMenu(trayIconMenu);
trayIcon->setIcon(QIcon::fromTheme("edit-find"));
trayIcon->setToolTip("Zeal");
connect(trayIcon, &QSystemTrayIcon::activated, [&](QSystemTrayIcon::ActivationReason reason) {
if(reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick) {
if(isVisible()) hide();
else show();
}
});
trayIcon->show();
}
void MainWindow::bringToFront()
{
show();
setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
raise();
activateWindow();
ui->lineEdit->setFocus();
}

View File

@ -2,6 +2,8 @@
#define MAINWINDOW_H
#include <QMainWindow>
#include <QProcess>
#include <QLocalServer>
#include "zeallistmodel.h"
#include "zealsearchmodel.h"
@ -9,6 +11,8 @@ namespace Ui {
class MainWindow;
}
extern const QString serverName;
class MainWindow : public QMainWindow
{
Q_OBJECT
@ -18,9 +22,13 @@ public:
~MainWindow();
private:
void bringToFront();
Ui::MainWindow *ui;
ZealListModel zealList;
ZealSearchModel zealSearch;
QProcess keyGrabber;
QLocalServer *localServer;
void createTrayIcon();
};
#endif // MAINWINDOW_H