Configurable hotkey, remember window geometry and splitter state

This commit is contained in:
Jerzy Kozera 2013-02-07 20:31:45 +00:00
parent 3dcc45cb0e
commit 4210b1d53b
15 changed files with 745 additions and 45 deletions

View File

@ -33,11 +33,10 @@ Currently Zeal requires Qt 5.0. To compile it, run `qmake` and `make` in the `ze
## Windows binary
A 64-bit Windows binary with all dependencies is available to download from Dropbox - [zeal.zip](https://www.dropbox.com/s/fbi5pr1gg706gvm/zeal.zip) (24M).
A 64-bit Windows binary with all dependencies is available to download from Dropbox - [zeal.zip](https://www.dropbox.com/s/odiu8vqvnec2jre/zeal.zip) (24M).
## TODO
* Configuration (customisable hotkey instead of hardcoded Alt+Space, remember window size, etc.)
* Support for global hotkeys under platforms other than Linux/X11 and Windows (OSX)
* Search enhancements - some ideas:
1. Allow selecting subset of docsets to search in.

View File

@ -2,7 +2,6 @@
#include "ui_mainwindow.h"
#include "zeallistmodel.h"
#include "zealsearchmodel.h"
#include "zealnativeeventfilter.h"
#include "zealdocsetsregistry.h"
#include "zealsearchitemdelegate.h"
#include "zealsettingsdialog.h"
@ -14,6 +13,7 @@
#include <QSystemTrayIcon>
#include <QLocalSocket>
#include <QDir>
#include <QSettings>
#include <QTimer>
#ifdef WIN32
@ -27,13 +27,15 @@
#include <xcb/xcb.h>
#include <xcb/xcb_keysyms.h>
#include <X11/keysym.h>
#include "xcb_keysym.h"
#endif
const QString serverName = "zeal_process_running";
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
ui(new Ui::MainWindow),
settings("Zeal", "Zeal")
{
// server for detecting already running instances
localServer = new QLocalServer(this);
@ -52,39 +54,23 @@ MainWindow::MainWindow(QWidget *parent) :
setWindowIcon(icon);
createTrayIcon();
QKeySequence keySequence;
if(settings.value("hotkey").isNull()) {
keySequence = QKeySequence("Alt+Space");
} else {
keySequence = settings.value("hotkey").value<QKeySequence>();
}
// initialise key grabber
auto filter = new ZealNativeEventFilter();
connect(filter, &ZealNativeEventFilter::gotHotKey, [&]() {
connect(&nativeFilter, &ZealNativeEventFilter::gotHotKey, [&]() {
if(isVisible()) hide();
else {
bringToFront(true);
}
});
qApp->eventDispatcher()->installNativeEventFilter(filter);
qApp->eventDispatcher()->installNativeEventFilter(&nativeFilter);
setHotKey(keySequence);
// platform-specific code for global key grabbing
#ifdef WIN32
RegisterHotKey(NULL, 10, MOD_ALT, VK_SPACE);
#else
auto platform = qApp->platformNativeInterface();
xcb_connection_t *c = static_cast<xcb_connection_t*>(platform->nativeResourceForWindow("connection", 0));
xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(c);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, XK_space), keycode;
// add bindings for all screens
xcb_screen_iterator_t iter;
iter = xcb_setup_roots_iterator (xcb_get_setup (c));
for (; iter.rem; xcb_screen_next (&iter)) {
int i = 0;
while(keycodes[i] != XCB_NO_SYMBOL) {
keycode = keycodes[i];
xcb_grab_key(c, true, iter.data->root, XCB_MOD_MASK_ANY, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
i += 1;
}
}
#endif // WIN32
// initialise docsets
auto dataLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
auto dataDir = QDir(dataLocation);
@ -102,15 +88,26 @@ MainWindow::MainWindow(QWidget *parent) :
// initialise ui
ui->setupUi(this);
restoreGeometry(settings.value("geometry").toByteArray());
ui->splitter->restoreState(settings.value("splitter").toByteArray());
connect(ui->splitter, &QSplitter::splitterMoved, [=](int, int) {
settings.setValue("splitter", ui->splitter->saveState());
});
// menu
auto quitAction = ui->menuBar->addAction("&Quit");
quitAction->setShortcut(QKeySequence::Quit);
connect(quitAction, &QAction::triggered, [=]() { settings.setValue("geometry", saveGeometry()); });
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
auto settingsAction = ui->menuBar->addAction("&Options");
connect(settingsAction, &QAction::triggered, [&]() {
ZealSettingsDialog settings;
settings.exec();
connect(settingsAction, &QAction::triggered, [=]() {
ZealSettingsDialog settingsDialog;
settingsDialog.setHotKey(hotKey);
nativeFilter.setEnabled(false);
if(settingsDialog.exec()) {
setHotKey(settingsDialog.hotKey());
}
nativeFilter.setEnabled(true);
});
auto helpMenu = new QMenu("&Help");
auto aboutAction = helpMenu->addAction("&About");
@ -195,3 +192,145 @@ void MainWindow::bringToFront(bool withHack)
}
#endif
}
void MainWindow::setHotKey(const QKeySequence& hotKey_) {
// platform-specific code for global key grabbing
#ifdef WIN32
UINT i_vk, i_mod = 0;
if(!hotKey.isEmpty()) {
// disable previous hotkey
UnregisterHotKey(NULL, 10);
}
hotKey = hotKey_;
nativeFilter.setHotKey(hotKey);
settings.setValue("hotkey", hotKey);
if(hotKey.isEmpty()) return;
int key = hotKey[hotKey.count()-1];
if(key & Qt::ALT) i_mod |= MOD_ALT;
if(key & Qt::CTRL) i_mod |= MOD_CONTROL;
if(key & Qt::SHIFT) i_mod |= MOD_SHIFT;
key = key & ~(Qt::ALT | Qt::CTRL | Qt::SHIFT | Qt::META);
#ifndef VK_VOLUME_DOWN
#define VK_VOLUME_DOWN 0xAE
#define VK_VOLUME_UP 0xAF
#endif
#ifndef VK_MEDIA_NEXT_TRACK
#define VK_MEDIA_NEXT_TRACK 0xB0
#define VK_MEDIA_PREV_TRACK 0xB1
#define VK_MEDIA_STOP 0xB2
#define VK_MEDIA_PLAY_PAUSE 0xB3
#endif
#ifndef VK_PAGEUP
#define VK_PAGEUP 0x21
#define VK_PAGEDOWN 0x22
#endif
switch(key) {
case Qt::Key_Left: i_vk = VK_LEFT; break;
case Qt::Key_Right: i_vk = VK_RIGHT; break;
case Qt::Key_Up: i_vk = VK_UP; break;
case Qt::Key_Down: i_vk = VK_DOWN; break;
case Qt::Key_Space: i_vk = VK_SPACE; break;
case Qt::Key_Escape: i_vk = VK_ESCAPE; break;
case Qt::Key_Enter: i_vk = VK_RETURN; break;
case Qt::Key_Return: i_vk = VK_RETURN; break;
case Qt::Key_F1: i_vk = VK_F1; break;
case Qt::Key_F2: i_vk = VK_F2; break;
case Qt::Key_F3: i_vk = VK_F3; break;
case Qt::Key_F4: i_vk = VK_F4; break;
case Qt::Key_F5: i_vk = VK_F5; break;
case Qt::Key_F6: i_vk = VK_F6; break;
case Qt::Key_F7: i_vk = VK_F7; break;
case Qt::Key_F8: i_vk = VK_F8; break;
case Qt::Key_F9: i_vk = VK_F9; break;
case Qt::Key_F10: i_vk = VK_F10; break;
case Qt::Key_F11: i_vk = VK_F11; break;
case Qt::Key_F12: i_vk = VK_F12; break;
case Qt::Key_PageUp: i_vk = VK_PAGEUP; break;
case Qt::Key_PageDown: i_vk = VK_PAGEDOWN; break;
case Qt::Key_Home: i_vk = VK_HOME; break;
case Qt::Key_End: i_vk = VK_END; break;
case Qt::Key_Insert: i_vk = VK_INSERT; break;
case Qt::Key_Delete: i_vk = VK_DELETE; break;
case Qt::Key_VolumeDown: i_vk = VK_VOLUME_DOWN; break;
case Qt::Key_VolumeUp: i_vk = VK_VOLUME_UP; break;
case Qt::Key_MediaTogglePlayPause: i_vk = VK_MEDIA_PLAY_PAUSE; break;
case Qt::Key_MediaStop: i_vk = VK_MEDIA_STOP; break;
case Qt::Key_MediaPrevious: i_vk = VK_MEDIA_PREV_TRACK; break;
case Qt::Key_MediaNext: i_vk = VK_MEDIA_NEXT_TRACK; break;
default:
i_vk = toupper( key );
break;
}
if(!RegisterHotKey(NULL, 10, i_mod, i_vk)) {
hotKey = QKeySequence();
nativeFilter.setHotKey(hotKey);
settings.setValue("hotkey", hotKey);
QMessageBox::warning(this, "Key binding failed", "Binding global hotkey failed.");
}
#else
auto platform = qApp->platformNativeInterface();
xcb_connection_t *c = static_cast<xcb_connection_t*>(platform->nativeResourceForWindow("connection", 0));
xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(c);
if(!hotKey.isEmpty()) {
// disable previous hotkey
xcb_keysym_t keysym = GetX11Key(hotKey[hotKey.count()-1]);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, keysym), keycode;
// remove bindings from all screens
xcb_screen_iterator_t iter;
iter = xcb_setup_roots_iterator (xcb_get_setup (c));
for (; iter.rem; xcb_screen_next (&iter)) {
int i = 0;
while(keycodes[i] != XCB_NO_SYMBOL) {
keycode = keycodes[i];
xcb_ungrab_key(c, keycode, iter.data->root, XCB_MOD_MASK_ANY);
i += 1;
}
}
free(keycodes);
}
hotKey = hotKey_;
nativeFilter.setHotKey(hotKey);
settings.setValue("hotkey", hotKey);
if(hotKey.isEmpty()) return;
xcb_keysym_t keysym = GetX11Key(hotKey[hotKey.count()-1]);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, keysym), keycode;
if(!keycodes) {
hotKey = QKeySequence();
nativeFilter.setHotKey(hotKey);
settings.setValue("hotkey", hotKey);
QMessageBox::warning(this, "Key binding failed", "Binding global hotkey failed.");
return;
}
// add bindings for all screens
xcb_screen_iterator_t iter;
iter = xcb_setup_roots_iterator (xcb_get_setup (c));
for (; iter.rem; xcb_screen_next (&iter)) {
int i = 0;
while(keycodes[i] != XCB_NO_SYMBOL) {
keycode = keycodes[i];
xcb_void_cookie_t cookie = xcb_grab_key_checked(c, true, iter.data->root, XCB_MOD_MASK_ANY, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
if(xcb_request_check(c, cookie)) {
hotKey = QKeySequence();
nativeFilter.setHotKey(hotKey);
settings.setValue("hotkey", hotKey);
QMessageBox::warning(this, "Key binding failed", "Binding global hotkey failed.");
return;
}
i += 1;
}
}
free(keysyms);
free(keycodes);
#endif // WIN32
}

View File

@ -5,8 +5,10 @@
#include <QProcess>
#include <QLocalServer>
#include <QDialog>
#include <QSettings>
#include "zeallistmodel.h"
#include "zealsearchmodel.h"
#include "zealnativeeventfilter.h"
namespace Ui {
class MainWindow;
@ -31,6 +33,16 @@ private:
QLocalServer *localServer;
QDialog hackDialog;
void createTrayIcon();
void setHotKey(const QKeySequence& hotKey);
QKeySequence hotKey;
QSettings settings;
ZealNativeEventFilter nativeFilter;
protected:
void closeEvent(QCloseEvent *event) {
settings.setValue("geometry", saveGeometry());
QMainWindow::closeEvent(event);
}
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,226 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* Razor - a lightweight, Qt based, desktop toolset
* http://razor-qt.org
*
* Copyright: 2010-2011 Razor team
* Authors:
* Alexander Sokoloff <sokoloff.a@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#include "razorshortcutbutton.h"
#include "razorshortcutbutton_p.h"
#include <QtGui/QKeyEvent>
#include <QtCore/QDebug>
/************************************************
************************************************/
RazorShortcutButton::RazorShortcutButton(QWidget *parent) :
QToolButton(parent),
d_ptr(new RazorShortcutButtonPrivate(this))
{
setFocusPolicy(Qt::StrongFocus);
setPopupMode(QToolButton::MenuButtonPopup);
setCheckable(true);
Q_D(RazorShortcutButton);
QAction *a = d->mMenu.addAction("Clear");
connect(a, SIGNAL(triggered()), d, SLOT(clear()));
QAction *a_def = d->mMenu.addAction("Default");
connect(a_def, &QAction::triggered, [=]() {
setKeySequence(QKeySequence("Alt+Space"));
});
setMenu(&d->mMenu);
connect(this, SIGNAL(toggled(bool)), d, SLOT(activate(bool)));
setKeySequence("");
}
/************************************************
************************************************/
RazorShortcutButtonPrivate::RazorShortcutButtonPrivate(RazorShortcutButton *parent):
q_ptr(parent),
mKeysCount(0)
{
}
/************************************************
************************************************/
void RazorShortcutButtonPrivate::activate(bool active)
{
Q_Q(RazorShortcutButton);
mKeysCount = 0;
if (active)
q->grabKeyboard();
else
q->releaseKeyboard();
}
/************************************************
************************************************/
RazorShortcutButton::~RazorShortcutButton()
{
releaseKeyboard();
Q_D(RazorShortcutButton);
delete d;
}
/************************************************
************************************************/
bool RazorShortcutButton::event(QEvent *event)
{
Q_D(RazorShortcutButton);
if (isChecked())
{
if (event->type() == QEvent::KeyPress)
return d->keyPressEvent(static_cast<QKeyEvent*>(event));
if (event->type() == QEvent::KeyRelease)
return d->keyReleaseEvent(static_cast<QKeyEvent*>(event));
if (event->type() == QEvent::FocusOut)
setChecked(false);
}
return QToolButton::event(event);
}
/************************************************
************************************************/
bool RazorShortcutButtonPrivate::keyPressEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
return true;
mKeysCount++;
Q_Q(RazorShortcutButton);
int key = 0;
switch (event->key())
{
case Qt::Key_Escape:
return false;
case Qt::Key_AltGr: //or else we get unicode salad
return false;
case Qt::Key_Shift:
case Qt::Key_Control:
case Qt::Key_Alt:
case Qt::Key_Meta:
case Qt::Key_Menu:
break;
default:
key = event->key();
break;
}
if (key)
{
QKeySequence seq(key + event->modifiers());
q->setKeySequence(seq);
return true;
}
return false;
}
/************************************************
************************************************/
bool RazorShortcutButtonPrivate::keyReleaseEvent(QKeyEvent *event)
{
if (event->isAutoRepeat())
return true;
Q_Q(RazorShortcutButton);
mKeysCount--;
if (mKeysCount<1)
{
q->setChecked(false);
}
return false;
}
/************************************************
************************************************/
QKeySequence RazorShortcutButton::keySequence() const
{
Q_D(const RazorShortcutButton);
return d->mSequence;
}
/************************************************
************************************************/
void RazorShortcutButton::setKeySequence(const QKeySequence &sequence)
{
Q_D(RazorShortcutButton);
d->mSequence = QKeySequence(sequence);
QString s = d->mSequence.toString();
if (s.isEmpty())
setText("None");
else
setText(s);
emit keySequenceChanged(d->mSequence);
emit keySequenceChanged(s);
}
/************************************************
************************************************/
void RazorShortcutButton::setKeySequence(const QString &sequence)
{
setKeySequence(QKeySequence(sequence));
}
/************************************************
************************************************/
void RazorShortcutButtonPrivate::clear()
{
Q_Q(RazorShortcutButton);
q->setKeySequence("");
}

View File

@ -0,0 +1,87 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* Razor - a lightweight, Qt based, desktop toolset
* http://razor-qt.org
*
* Copyright: 2010-2011 Razor team
* Authors:
* Alexander Sokoloff <sokoloff.a@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#ifndef RAZORSHORTCUTBUTTON_H
#define RAZORSHORTCUTBUTTON_H
#include <QToolButton>
#include <QKeySequence>
class RazorShortcutButtonPrivate;
/**
* @short A widget to input a QKeySequence.
*
* This widget lets the user choose a QKeySequence, which is usually used as a
* shortcut key. The recording is initiated by the user clicking into the widget.
*
*/
class RazorShortcutButton : public QToolButton
{
Q_OBJECT
public:
/// Constructor.
explicit RazorShortcutButton(QWidget *parent = 0);
/// Destructs the widget.
virtual ~RazorShortcutButton();
/// Return the currently selected key sequence.
QKeySequence keySequence() const;
public slots:
/// Set the key sequence.
void setKeySequence(const QKeySequence &sequence);
/// This function is provided for convenience. It's equivalent to calling setKeySequence(QKeySequence(sequence)).
void setKeySequence(const QString &sequence);
signals:
/**
* This signal is emitted when the current key sequence has changed, be it by user
* input or programmatically.
*/
void keySequenceChanged(const QKeySequence &sequence);
/**
* This signal is emitted when the current key sequence has changed, be it by user
* input or programmatically.
*/
void keySequenceChanged(const QString &sequence);
protected:
/// @reimp
bool event(QEvent *event);
private:
RazorShortcutButtonPrivate* const d_ptr;
Q_DECLARE_PRIVATE(RazorShortcutButton)
};
#endif // RAZORSHORTCUTBUTTON_H

View File

@ -0,0 +1,59 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* Razor - a lightweight, Qt based, desktop toolset
* http://razor-qt.org
*
* Copyright: 2010-2011 Razor team
* Authors:
* Alexander Sokoloff <sokoloff.a@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#ifndef RAZORSHORTCUTBUTTON_P_H
#define RAZORSHORTCUTBUTTON_P_H
#include "razorshortcutbutton.h"
#include <QMenu>
class QKeyEvent;
class RazorShortcutButtonPrivate: public QObject
{
Q_OBJECT
public:
explicit RazorShortcutButtonPrivate(RazorShortcutButton *parent);
bool keyPressEvent(QKeyEvent *event);
bool keyReleaseEvent(QKeyEvent *event);
public slots:
void clear();
void activate(bool active);
private:
RazorShortcutButton* const q_ptr;
Q_DECLARE_PUBLIC(RazorShortcutButton);
QKeySequence mSequence;
QMenu mMenu;
int mKeysCount;
};
#endif // RAZORSHORTCUTBUTTON_P_H

View File

@ -1,5 +1,8 @@
HEADERS += widgets/searchablewebview.h \
widgets/zealsearchedit.h
widgets/zealsearchedit.h \
widgets/razorshortcutbutton.h \
widgets/razorshortcutbutton_p.h
SOURCES += widgets/searchablewebview.cpp \
widgets/zealsearchedit.cpp
widgets/zealsearchedit.cpp \
widgets/razorshortcutbutton.cpp

108
zeal/xcb_keysym.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "xcb_keysym.h"
#include <Qt>
const x11_to_qt x11keys_to_qtkeys[] =
{
{ XK_BackSpace, Qt::Key_Backspace, },
{ XK_Tab, Qt::Key_Tab, },
{ XK_Return, Qt::Key_Enter, },
{ XK_Escape, Qt::Key_Escape, },
{ XK_Home, Qt::Key_Home, },
{ XK_Left, Qt::Key_Left, },
{ XK_Up, Qt::Key_Up, },
{ XK_Right, Qt::Key_Right, },
{ XK_Down, Qt::Key_Down, },
{ XK_Page_Up, Qt::Key_PageUp, },
{ XK_Page_Down, Qt::Key_PageDown, },
{ XK_End, Qt::Key_End, },
{ XK_Begin, Qt::Key_Home, },
{ XK_Insert, Qt::Key_Insert, },
{ XK_Menu, Qt::Key_Menu },
/* Numeric pad keys - this breaks binding, so commented out for now */
/* { XK_KP_Space, ' ', },
{ XK_KP_Tab, Qt::Key_Tab, },
{ XK_KP_Enter, Qt::Key_Enter, },
{ XK_KP_F1, Qt::Key_F1, },
{ XK_KP_F2, Qt::Key_F2, },
{ XK_KP_F3, Qt::Key_F3, },
{ XK_KP_F4, Qt::Key_F4, },
{ XK_KP_Home, Qt::Key_Home, },
{ XK_KP_Left, Qt::Key_Left, },
{ XK_KP_Up, Qt::Key_Up, },
{ XK_KP_Right, Qt::Key_Right, },
{ XK_KP_Down, Qt::Key_Down, },
{ XK_KP_Page_Up, Qt::Key_PageUp, },
{ XK_KP_Page_Down, Qt::Key_PageDown, },
{ XK_KP_End, Qt::Key_End, },
{ XK_KP_Begin, Qt::Key_Home, },
{ XK_KP_Insert, Qt::Key_Insert, },
{ XK_KP_Delete, Qt::Key_Delete, },
{ XK_KP_Equal, Qt::Key_Equal, },
{ XK_KP_Multiply, Qt::Key_multiply, },
{ XK_KP_Add, Qt::Key_Plus, },
{ XK_KP_Separator, Qt::Key_Comma, },
{ XK_KP_Subtract, Qt::Key_hyphen, },
{ XK_KP_Decimal, Qt::Key_Comma, },
{ XK_KP_Divide, Qt::Key_division, },
{ XK_KP_0, Qt::Key_0, },
{ XK_KP_1, Qt::Key_1, },
{ XK_KP_2, Qt::Key_2, },
{ XK_KP_3, Qt::Key_3, },
{ XK_KP_4, Qt::Key_4, },
{ XK_KP_5, Qt::Key_5, },
{ XK_KP_6, Qt::Key_6, },
{ XK_KP_7, Qt::Key_7, },
{ XK_KP_8, Qt::Key_8, },
{ XK_KP_9, Qt::Key_9, }, */
{ XK_F1, Qt::Key_F1, },
{ XK_F2, Qt::Key_F2, },
{ XK_F3, Qt::Key_F3, },
{ XK_F4, Qt::Key_F4, },
{ XK_F5, Qt::Key_F5, },
{ XK_F6, Qt::Key_F6, },
{ XK_F7, Qt::Key_F7, },
{ XK_F8, Qt::Key_F8, },
{ XK_F9, Qt::Key_F9, },
{ XK_F10, Qt::Key_F10, },
{ XK_F11, Qt::Key_F11, },
{ XK_F12, Qt::Key_F12, },
{ XK_Delete, Qt::Key_Delete, },
/* XFree86 extensions */
{ XF86XK_AudioLowerVolume, Qt::Key_VolumeDown, },
{ XF86XK_AudioMute, Qt::Key_VolumeMute, },
{ XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp, },
{ XF86XK_AudioPlay, Qt::Key_MediaTogglePlayPause, },
{ XF86XK_AudioStop, Qt::Key_MediaStop, },
{ XF86XK_AudioPrev, Qt::Key_MediaPrevious, },
{ XF86XK_AudioNext, Qt::Key_MediaNext, },
{ XF86XK_HomePage, Qt::Key_HomePage, },
{ XF86XK_Search, Qt::Key_Search, },
{ XF86XK_Back, Qt::Key_Back, },
{ XF86XK_Forward, Qt::Key_Forward, },
{ XF86XK_Stop, Qt::Key_Stop, },
{ XF86XK_Refresh, Qt::Key_Refresh, },
{ XF86XK_Favorites, Qt::Key_Favorites, },
{ XF86XK_AudioPause, Qt::Key_MediaTogglePlayPause, },
{ XF86XK_Reload, Qt::Key_Reload, },
{ 0, 0 }
};
xcb_keysym_t GetX11Key( unsigned i_qt )
{
i_qt = i_qt & ~(Qt::ALT | Qt::CTRL | Qt::SHIFT | Qt::META);
/* X11 and Qt use ASCII for printable ASCII characters */
if( i_qt >= 32 && i_qt <= 127 )
return i_qt;
for( int i = 0; x11keys_to_qtkeys[i].i_qt != 0; i++ )
{
if( x11keys_to_qtkeys[i].i_qt == i_qt )
return x11keys_to_qtkeys[i].i_x11;
}
return XK_VoidSymbol;
}

20
zeal/xcb_keysym.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef XCB_KEYSYM_H
#define XCB_KEYSYM_H
#include <xcb/xcb.h>
#include <xcb/xcb_keysyms.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
typedef struct
{
xcb_keysym_t i_x11;
unsigned i_qt;
} x11_to_qt;
extern const x11_to_qt x11keys_to_qtkeys[];
extern xcb_keysym_t GetX11Key( unsigned i_qt );
#endif

View File

@ -32,7 +32,8 @@ HEADERS += mainwindow.h \
lineedit.h \
zealsearchitemdelegate.h \
zealsearchitemstyle.h \
zealsettingsdialog.h
zealsettingsdialog.h \
xcb_keysym.h
FORMS += mainwindow.ui \
zealsettingsdialog.ui
@ -43,5 +44,6 @@ QMAKE_CXXFLAGS += -std=c++11
win32:DEFINES += WIN32
unix:!macx: LIBS += -lxcb-keysyms
unix:!macx: SOURCES += xcb_keysym.cpp
include (widgets/widgets.pri)

View File

@ -7,6 +7,7 @@
#include <xcb/xcb_keysyms.h>
#include <X11/keysym.h>
#include <QGuiApplication>
#include "xcb_keysym.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
#include <QtGui/5.1.0/QtGui/qpa/qplatformnativeinterface.h>
#else
@ -100,21 +101,41 @@ bool ZealNativeEventFilter::nativeEventFilter(const QByteArray &eventType, void
return true;
}
#else // WIN32
xcb_generic_event_t* ev = static_cast<xcb_generic_event_t*>(message);
if((ev->response_type&127) == XCB_KEY_PRESS) {
if((ev->response_type&127) == XCB_KEY_PRESS && !hotKey.isEmpty()) {
xcb_connection_t *c = static_cast<xcb_connection_t*>(
((QGuiApplication*)QGuiApplication::instance())->
platformNativeInterface()->nativeResourceForWindow("connection", 0));
xcb_key_press_event_t *event = (xcb_key_press_event_t *)ev;
xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(c);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, XK_space);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, GetX11Key(hotKey[hotKey.count()-1]));
int i = 0;
bool found = false;
while(keycodes[i] != XCB_NO_SYMBOL) {
if(event->detail == keycodes[i]) {
if(event->state & GetModifier(c, keysyms, XK_Alt_L) || event->state & GetModifier(c, keysyms, XK_Alt_R)) {
bool modifiers_present = true;
if(hotKey[hotKey.count()-1] & Qt::ALT) {
if(!(event->state & GetModifier(c, keysyms, XK_Alt_L) || event->state & GetModifier(c, keysyms, XK_Alt_R))) {
modifiers_present = false;
}
}
if(hotKey[hotKey.count()-1] & Qt::CTRL) {
if(!(event->state & GetModifier(c, keysyms, XK_Control_L) || event->state & GetModifier(c, keysyms, XK_Control_R))) {
modifiers_present = false;
}
}
if(hotKey[hotKey.count()-1] & Qt::META) {
if(!(event->state & GetModifier(c, keysyms, XK_Meta_L) || event->state & GetModifier(c, keysyms, XK_Meta_R))) {
modifiers_present = false;
}
}
if(hotKey[hotKey.count()-1] & Qt::SHIFT) {
if(!(event->state & GetModifier(c, keysyms, XK_Shift_L) || event->state & GetModifier(c, keysyms, XK_Shift_R))) {
modifiers_present = false;
}
}
if(enabled && modifiers_present) {
xcb_allow_events(c, XCB_ALLOW_ASYNC_KEYBOARD, event->time);
emit gotHotKey();
found = true;

View File

@ -3,6 +3,7 @@
#include <QObject>
#include <QAbstractNativeEventFilter>
#include <QKeySequence>
class ZealNativeEventFilter : public QObject, public QAbstractNativeEventFilter
{
@ -10,10 +11,14 @@ class ZealNativeEventFilter : public QObject, public QAbstractNativeEventFilter
public:
explicit ZealNativeEventFilter(QObject *parent = 0);
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
void setEnabled(bool enabled_) { enabled = enabled_; }
void setHotKey(const QKeySequence& hotKey_) { hotKey = hotKey_; }
signals:
void gotHotKey();
public slots:
private:
bool enabled = true;
QKeySequence hotKey;
};
#endif // ZEALNATIVEEVENTFILTER_H

View File

@ -12,3 +12,13 @@ ZealSettingsDialog::~ZealSettingsDialog()
{
delete ui;
}
void ZealSettingsDialog::setHotKey(const QKeySequence &keySequence)
{
ui->toolButton->setKeySequence(keySequence);
}
QKeySequence ZealSettingsDialog::hotKey()
{
return ui->toolButton->keySequence();
}

View File

@ -14,6 +14,8 @@ class ZealSettingsDialog : public QDialog
public:
explicit ZealSettingsDialog(QWidget *parent = 0);
~ZealSettingsDialog();
void setHotKey(const QKeySequence &keySequence);
QKeySequence hotKey();
private:
Ui::ZealSettingsDialog *ui;

View File

@ -6,12 +6,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>236</width>
<height>101</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>Zeal Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@ -24,7 +24,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="toolButton">
<widget class="RazorShortcutButton" name="toolButton">
<property name="text">
<string>...</string>
</property>
@ -44,6 +44,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RazorShortcutButton</class>
<extends>QToolButton</extends>
<header>widgets/razorshortcutbutton.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>