You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
668 lines
24 KiB
668 lines
24 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2005, Gary Cramblitt <garycramblitt@comcast.net>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
// Qt includes
|
|
#include <qsplitter.h>
|
|
#include <qdockwindow.h>
|
|
#include <qdockarea.h>
|
|
#include <qevent.h>
|
|
#include <qcursor.h>
|
|
#include <qobjectlist.h>
|
|
#include <qwidgetlist.h>
|
|
#include <qlabel.h>
|
|
#include <qtooltip.h>
|
|
|
|
// KDE includes
|
|
#include <klocale.h>
|
|
#include <kglobal.h>
|
|
#include <kapplication.h>
|
|
#include <kmainwindow.h>
|
|
#include <kaction.h>
|
|
#include <kdebug.h>
|
|
|
|
// KKbdAccessExtensions includes
|
|
#include "kkbdaccessextensions.h"
|
|
// TODO: See eventFilter method.
|
|
//#include "kkbdaccessextensions.moc"
|
|
|
|
class KPanelKbdSizerIcon : public QCursor
|
|
{
|
|
public:
|
|
KPanelKbdSizerIcon() :
|
|
QCursor(Qt::SizeAllCursor),
|
|
isActive(false)
|
|
{
|
|
currentPos = QPoint(-1, -1);
|
|
}
|
|
|
|
~KPanelKbdSizerIcon()
|
|
{
|
|
hide();
|
|
}
|
|
|
|
void show(const QPoint p) {
|
|
if (!isActive) {
|
|
originalPos = QCursor::pos();
|
|
kapp->setOverrideCursor(*this);
|
|
isActive = true;
|
|
}
|
|
if (p != pos())
|
|
setPos(p);
|
|
currentPos = p;
|
|
}
|
|
|
|
void hide() {
|
|
if (isActive) {
|
|
kapp->restoreOverrideCursor();
|
|
QCursor::setPos(originalPos);
|
|
}
|
|
isActive = false;
|
|
}
|
|
|
|
void setShape(int shayp)
|
|
{
|
|
if (shayp != shape()) {
|
|
// Must restore and override to get the icon to refresh.
|
|
if (isActive) kapp->restoreOverrideCursor();
|
|
QCursor::setShape(shayp);
|
|
if (isActive) kapp->setOverrideCursor(*this);
|
|
}
|
|
}
|
|
|
|
// Return the difference between a position and where icon is supposed to be.
|
|
QSize delta(const QPoint p)
|
|
{
|
|
QPoint d = p - currentPos;
|
|
return QSize(d.x(), d.y());
|
|
}
|
|
|
|
// Return the difference between where the icon is currently positioned and where
|
|
// it is supposed to be.
|
|
QSize delta() { return delta(pos()); }
|
|
|
|
// True if the sizing icon is visible.
|
|
bool isActive;
|
|
|
|
private:
|
|
// Icon's current position.
|
|
QPoint currentPos;
|
|
// Mouse cursor's original position when icon is shown.
|
|
QPoint originalPos;
|
|
};
|
|
|
|
class KKbdAccessExtensionsPrivate
|
|
{
|
|
public:
|
|
KKbdAccessExtensionsPrivate() :
|
|
fwdAction(0),
|
|
revAction(0),
|
|
accessKeysAction(0),
|
|
panel(0),
|
|
handleNdx(0),
|
|
icon(0),
|
|
stepSize(10),
|
|
accessKeyLabels(0) {};
|
|
|
|
~KKbdAccessExtensionsPrivate()
|
|
{
|
|
delete icon;
|
|
// TODO: This crashes, but should delete in the event that KMainWindow is not deleted.
|
|
if (accessKeyLabels) {
|
|
accessKeyLabels->setAutoDelete(false);
|
|
delete accessKeyLabels;
|
|
}
|
|
}
|
|
|
|
// Action that starts panel sizing (defaults to F8), forward and reverse;
|
|
KAction* fwdAction;
|
|
KAction* revAction;
|
|
|
|
// Action that starts access keys.
|
|
KAction* accessKeysAction;
|
|
|
|
// The splitter or dockwindow currently being sized. If 0, sizing is not in progress.
|
|
QWidget* panel;
|
|
|
|
// Index of current handle of the panel. When panel is a QDockWindow:
|
|
// 1 = size horizontally
|
|
// 2 = size vertically
|
|
uint handleNdx;
|
|
|
|
// Sizing icon.
|
|
KPanelKbdSizerIcon* icon;
|
|
|
|
// Sizing increment.
|
|
int stepSize;
|
|
|
|
// List of the access key QLabels. If not 0, access keys are onscreen.
|
|
QPtrList<QLabel>* accessKeyLabels;
|
|
|
|
// Pointer to the KMainWindow.
|
|
KMainWindow* mainWindow;
|
|
};
|
|
|
|
KKbdAccessExtensions::KKbdAccessExtensions(KMainWindow* parent, const char* name) :
|
|
QObject(parent, name)
|
|
{
|
|
// kdDebug() << "KKbdAccessExtensions::KKbdAccessExtensions: running." << endl;
|
|
d = new KKbdAccessExtensionsPrivate;
|
|
d->mainWindow = parent;
|
|
d->fwdAction = new KAction(i18n("Resize Panel Forward"), KShortcut("F8"),
|
|
0, 0, parent->actionCollection(), "resize_panel_forward");
|
|
d->revAction = new KAction(i18n("Resize Panel Reverse"), KShortcut("Shift+F8"),
|
|
0, 0, parent->actionCollection(), "resize_panel_reverse");
|
|
d->accessKeysAction = new KAction(i18n("Access Keys"), KShortcut("Alt+F8"),
|
|
0, 0, parent->actionCollection(), "access_keys");
|
|
// "Disable" the shortcuts so we can see them in eventFilter.
|
|
d->fwdAction->setEnabled(false);
|
|
d->revAction->setEnabled(false);
|
|
d->accessKeysAction->setEnabled(false);
|
|
d->icon = new KPanelKbdSizerIcon();
|
|
kapp->installEventFilter(this);
|
|
}
|
|
|
|
KKbdAccessExtensions::~KKbdAccessExtensions()
|
|
{
|
|
kapp->removeEventFilter(this);
|
|
if (d->panel) exitSizing();
|
|
delete d;
|
|
}
|
|
|
|
int KKbdAccessExtensions::stepSize() const { return d->stepSize; }
|
|
|
|
void KKbdAccessExtensions::setStepSize(int s) { d->stepSize = s; }
|
|
|
|
bool KKbdAccessExtensions::eventFilter( QObject *o, QEvent *e )
|
|
{
|
|
if ( e->type() == QEvent::KeyPress ) {
|
|
// TODO: This permits only a single-key shortcut. For example, Alt+S,R would not work.
|
|
// If user configures a multi-key shortcut, it is undefined what will happen here.
|
|
// It would be better to handle these as KShortcut activate() signals, but the problem
|
|
// is that once a QDockWindow is undocked and has focus, the KShortcut activate() signals
|
|
// don't fire anymore.
|
|
KShortcut fwdSc = d->fwdAction->shortcut();
|
|
KShortcut revSc = d->revAction->shortcut();
|
|
KShortcut accessKeysSc = d->accessKeysAction->shortcut();
|
|
QKeyEvent* kev = dynamic_cast<QKeyEvent *>(e);
|
|
KKey k = KKey(kev);
|
|
KShortcut sc = KShortcut(k);
|
|
// kdDebug() << "KKbdAccessExtensions::eventFilter: Key press " << sc << endl;
|
|
if (!d->accessKeyLabels) {
|
|
if (sc == fwdSc) {
|
|
nextHandle();
|
|
return true;
|
|
}
|
|
if (sc == revSc) {
|
|
prevHandle();
|
|
return true;
|
|
}
|
|
}
|
|
if (d->panel) {
|
|
if (k == KKey(Key_Escape))
|
|
exitSizing();
|
|
else
|
|
resizePanelFromKey(kev->key(), kev->state());
|
|
// Eat the key.
|
|
return true;
|
|
}
|
|
if (sc == accessKeysSc && !d->panel) {
|
|
if (d->accessKeyLabels) {
|
|
delete d->accessKeyLabels;
|
|
d->accessKeyLabels = 0;
|
|
} else
|
|
displayAccessKeys();
|
|
return true;
|
|
}
|
|
if (d->accessKeyLabels) {
|
|
if (k == KKey(Key_Escape)) {
|
|
delete d->accessKeyLabels;
|
|
d->accessKeyLabels = 0;
|
|
} else
|
|
handleAccessKey(kev);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else if (d->icon->isActive && e->type() == QEvent::MouseButtonPress) {
|
|
exitSizing();
|
|
return true;
|
|
}
|
|
else if (d->accessKeyLabels && e->type() == QEvent::MouseButtonPress) {
|
|
delete d->accessKeyLabels;
|
|
d->accessKeyLabels = 0;
|
|
return true;
|
|
}
|
|
/* else if (e->type() == QEvent::MouseMove && d->icon->isActive) {
|
|
// Lock mouse cursor down.
|
|
showIcon();
|
|
dynamic_cast<QMouseEvent *>(e)->accept();
|
|
return true;
|
|
}*/
|
|
else if (e->type() == QEvent::MouseMove && d->icon->isActive && d->panel) {
|
|
// Resize according to mouse movement.
|
|
QMouseEvent* me = dynamic_cast<QMouseEvent *>(e);
|
|
QSize s = d->icon->delta();
|
|
int dx = s.width();
|
|
int dy = s.height();
|
|
resizePanel(dx, dy, me->state());
|
|
me->accept();
|
|
showIcon();
|
|
return true;
|
|
}
|
|
else if (e->type() == QEvent::Resize && d->panel && o == d->panel) {
|
|
// TODO: This doesn't always work.
|
|
showIcon();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QWidgetList* KKbdAccessExtensions::getAllPanels()
|
|
{
|
|
QWidgetList* allWidgets = kapp->allWidgets();
|
|
QWidgetList* allPanels = new QWidgetList;
|
|
QWidget* widget = allWidgets->first();
|
|
while (widget) {
|
|
if (widget->isVisible()) {
|
|
if (::qt_cast<QSplitter*>( widget )) {
|
|
// Only size QSplitters with at least two handles (there is always one hidden).
|
|
if (dynamic_cast<QSplitter *>(widget)->sizes().count() >= 2)
|
|
allPanels->append(widget);
|
|
} else if (::qt_cast<QDockWindow*>( widget )) {
|
|
if (dynamic_cast<QDockWindow *>(widget)->isResizeEnabled()) {
|
|
// kdDebug() << "KKbdAccessExtensions::getAllPanels: QDockWindow = " << widget->name() << endl;
|
|
allPanels->append(widget);
|
|
}
|
|
}
|
|
}
|
|
widget = allWidgets->next();
|
|
}
|
|
delete allWidgets;
|
|
return allPanels;
|
|
}
|
|
|
|
void KKbdAccessExtensions::nextHandle()
|
|
{
|
|
QWidget* panel = d->panel;
|
|
// See if current panel has another handle. If not, find next panel.
|
|
if (panel) {
|
|
bool advance = true;
|
|
d->handleNdx++;
|
|
if (::qt_cast<QSplitter*>( panel ))
|
|
advance = (d->handleNdx >= dynamic_cast<QSplitter *>(panel)->sizes().count());
|
|
else
|
|
// Undocked windows have only one "handle" (center).
|
|
advance = (d->handleNdx > 2 || !dynamic_cast<QDockWindow *>(panel)->area());
|
|
if (advance) {
|
|
QWidgetList* allWidgets = getAllPanels();
|
|
allWidgets->findRef(panel);
|
|
panel = 0;
|
|
if (allWidgets->current()) panel = allWidgets->next();
|
|
delete allWidgets;
|
|
d->handleNdx = 1;
|
|
}
|
|
} else {
|
|
// Find first panel.
|
|
QWidgetList* allWidgets = getAllPanels();
|
|
panel = allWidgets->first();
|
|
delete allWidgets;
|
|
d->handleNdx = 1;
|
|
}
|
|
d->panel = panel;
|
|
if (panel)
|
|
showIcon();
|
|
else
|
|
exitSizing();
|
|
}
|
|
|
|
void KKbdAccessExtensions::prevHandle()
|
|
{
|
|
QWidget* panel = d->panel;
|
|
// See if current panel has another handle. If not, find previous panel.
|
|
if (panel) {
|
|
bool rewind = true;
|
|
d->handleNdx--;
|
|
rewind = (d->handleNdx < 1);
|
|
if (rewind) {
|
|
QWidgetList* allWidgets = getAllPanels();
|
|
allWidgets->findRef(panel);
|
|
panel = 0;
|
|
if (allWidgets->current()) panel = allWidgets->prev();
|
|
delete allWidgets;
|
|
if (panel) {
|
|
if (::qt_cast<QSplitter*>( panel ))
|
|
d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
|
|
else {
|
|
if (dynamic_cast<QDockWindow *>(panel)->area())
|
|
d->handleNdx = 2;
|
|
else
|
|
d->handleNdx = 1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Find last panel.
|
|
QWidgetList* allWidgets = getAllPanels();
|
|
panel = allWidgets->last();
|
|
delete allWidgets;
|
|
if (panel) {
|
|
if (::qt_cast<QSplitter*>( panel ))
|
|
d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
|
|
else {
|
|
if (dynamic_cast<QDockWindow *>(panel)->area())
|
|
d->handleNdx = 2;
|
|
else
|
|
d->handleNdx = 1;
|
|
}
|
|
}
|
|
}
|
|
d->panel = panel;
|
|
if (panel)
|
|
showIcon();
|
|
else
|
|
exitSizing();
|
|
}
|
|
|
|
void KKbdAccessExtensions::exitSizing()
|
|
{
|
|
// kdDebug() << "KKbdAccessExtensions::exiting sizing mode." << endl;
|
|
hideIcon();
|
|
d->handleNdx = 0;
|
|
d->panel = 0;
|
|
}
|
|
|
|
void KKbdAccessExtensions::showIcon()
|
|
{
|
|
if (!d->panel) return;
|
|
QPoint p;
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: topLevelWidget = " << d->panel->topLevelWidget()->name() << endl;
|
|
if (::qt_cast<QSplitter*>( d->panel )) {
|
|
QSplitter* splitter = dynamic_cast<QSplitter *>(d->panel);
|
|
int handleNdx = d->handleNdx - 1;
|
|
QValueList<int> sizes = splitter->sizes();
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: sizes = " << sizes << endl;
|
|
if (splitter->orientation() == Qt::Horizontal) {
|
|
d->icon->setShape(Qt::SizeHorCursor);
|
|
p.setX(sizes[handleNdx] + (splitter->handleWidth() / 2));
|
|
p.setY(splitter->height() / 2);
|
|
} else {
|
|
d->icon->setShape(Qt::SizeVerCursor);
|
|
p.setX(splitter->width() / 2);
|
|
p.setY(sizes[handleNdx] + (splitter->handleWidth() / 2));
|
|
}
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: p = " << p << endl;
|
|
p = splitter->mapToGlobal(p);
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: mapToGlobal = " << p << endl;
|
|
} else {
|
|
QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
|
|
p = dockWindow->pos();
|
|
if (dockWindow->area()) {
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: pos = " << p << " of window = " << dockWindow->parentWidget()->name() << endl;
|
|
p = dockWindow->parentWidget()->mapTo(dockWindow->topLevelWidget(), p);
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: mapTo = " << p << " of window = " << dockWindow->topLevelWidget()->name() << endl;
|
|
// TODO: How to get the handle width?
|
|
if (d->handleNdx == 1) {
|
|
d->icon->setShape(Qt::SizeHorCursor);
|
|
if (dockWindow->area()->orientation() == Qt::Vertical) {
|
|
if (dockWindow->area()->handlePosition() == QDockArea::Normal)
|
|
// Handle is to the right of the dock window.
|
|
p.setX(p.x() + dockWindow->width());
|
|
// else Handle is to the left of the dock window.
|
|
} else
|
|
// Handle is to the right of the dock window.
|
|
p.setX(p.x() + dockWindow->width());
|
|
p.setY(p.y() + (dockWindow->height() / 2));
|
|
} else {
|
|
d->icon->setShape(Qt::SizeVerCursor);
|
|
p.setX(p.x() + (dockWindow->width() / 2));
|
|
if (dockWindow->area()->orientation() == Qt::Vertical)
|
|
// Handle is below the dock window.
|
|
p.setY(p.y() + dockWindow->height());
|
|
else {
|
|
if (dockWindow->area()->handlePosition() == QDockArea::Normal)
|
|
// Handle is below the dock window.
|
|
p.setY(p.y() + dockWindow->height());
|
|
// else Handle is above the dock window.
|
|
}
|
|
}
|
|
p = dockWindow->topLevelWidget()->mapToGlobal(p);
|
|
} else {
|
|
d->icon->setShape(Qt::SizeAllCursor);
|
|
p = QPoint(dockWindow->width() / 2, dockWindow->height() / 2);
|
|
p = dockWindow->mapToGlobal(p); // Undocked. Position in center of window.
|
|
}
|
|
}
|
|
// kdDebug() << "KKbdAccessExtensions::showIcon: show(p) = " << p << endl;
|
|
d->icon->show(p);
|
|
}
|
|
|
|
void KKbdAccessExtensions::hideIcon()
|
|
{
|
|
d->icon->hide();
|
|
}
|
|
|
|
void KKbdAccessExtensions::resizePanel(int dx, int dy, int state)
|
|
{
|
|
int adj = dx + dy;
|
|
if (adj == 0) return;
|
|
// kdDebug() << "KKbdAccessExtensions::resizePanel: panel = " << d->panel->name() << endl;
|
|
if (::qt_cast<QSplitter*>( d->panel )) {
|
|
QSplitter* splitter = dynamic_cast<QSplitter *>(d->panel);
|
|
int handleNdx = d->handleNdx - 1;
|
|
QValueList<int> sizes = splitter->sizes();
|
|
// kdDebug() << "KKbdAccessExtensions::resizePanel: before sizes = " << sizes << endl;
|
|
sizes[handleNdx] = sizes[handleNdx] + adj;
|
|
// kdDebug() << "KKbdAccessExtensions::resizePanel: setSizes = " << sizes << endl;
|
|
splitter->setSizes(sizes);
|
|
QApplication::postEvent(splitter, new QEvent(QEvent::LayoutHint));
|
|
} else {
|
|
// TODO: How to get the handle width?
|
|
QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
|
|
if (dockWindow->area()) {
|
|
// kdDebug() << "KKbdAccessExtensions::resizePanel: fixedExtent = " << dockWindow->fixedExtent() << endl;
|
|
QSize fe = dockWindow->fixedExtent();
|
|
if (d->handleNdx == 1) {
|
|
// When vertically oriented and dock area is on right side of screen, pressing
|
|
// left arrow increases size.
|
|
if (dockWindow->area()->orientation() == Qt::Vertical &&
|
|
dockWindow->area()->handlePosition() == QDockArea::Reverse) adj = -adj;
|
|
int w = fe.width();
|
|
if (w < 0) w = dockWindow->width();
|
|
w = w + adj;
|
|
if (w > 0 ) dockWindow->setFixedExtentWidth(w);
|
|
} else {
|
|
// When horizontally oriented and dock area is at bottom of screen,
|
|
// pressing up arrow increases size.
|
|
if (dockWindow->area()->orientation() == Qt::Horizontal &&
|
|
dockWindow->area()->handlePosition() == QDockArea::Reverse) adj = -adj;
|
|
int h = fe.height();
|
|
if (h < 0) h = dockWindow->height();
|
|
h = h + adj;
|
|
if (h > 0) dockWindow->setFixedExtentHeight(h);
|
|
}
|
|
dockWindow->updateGeometry();
|
|
QApplication::postEvent(dockWindow->area(), new QEvent(QEvent::LayoutHint));
|
|
// kdDebug() << "KKbdAccessExtensions::resizePanel: fixedExtent = " << dockWindow->fixedExtent() << endl;
|
|
} else {
|
|
if (state == Qt::ShiftButton) {
|
|
QSize s = dockWindow->size();
|
|
s.setWidth(s.width() + dx);
|
|
s.setHeight(s.height() + dy);
|
|
dockWindow->resize(s);
|
|
} else {
|
|
QPoint p = dockWindow->pos();
|
|
p.setX(p.x() + dx);
|
|
p.setY(p.y() + dy);
|
|
dockWindow->move(p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void KKbdAccessExtensions::resizePanelFromKey(int key, int state)
|
|
{
|
|
// kdDebug() << "KPanelKdbSizer::resizePanelFromKey: key = " << key << " state = " << state << endl;
|
|
if (!d->panel) return;
|
|
int dx = 0;
|
|
int dy = 0;
|
|
int stepSize = d->stepSize;
|
|
switch (key) {
|
|
case Qt::Key_Left: dx = -stepSize; break;
|
|
case Qt::Key_Right: dx = stepSize; break;
|
|
case Qt::Key_Up: dy = -stepSize; break;
|
|
case Qt::Key_Down: dy = stepSize; break;
|
|
case Qt::Key_Prior: dy = -5 * stepSize; break;
|
|
case Qt::Key_Next: dy = 5 * stepSize; break;
|
|
}
|
|
int adj = dx + dy;
|
|
// kdDebug() << "KKbdAccessExtensions::resizePanelFromKey: adj = " << adj << endl;
|
|
if (adj != 0)
|
|
resizePanel(dx, dy, state);
|
|
else {
|
|
if (key == Qt::Key_Enter && ::qt_cast<QDockWindow*>( d->panel )) {
|
|
QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
|
|
if (dockWindow->area())
|
|
dockWindow->undock();
|
|
else
|
|
dockWindow->dock();
|
|
}
|
|
}
|
|
showIcon();
|
|
}
|
|
|
|
void KKbdAccessExtensions::displayAccessKeys()
|
|
{
|
|
// Build a list of valid access keys that don't collide with shortcuts.
|
|
QString availableAccessKeys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
|
|
QPtrList<KXMLGUIClient> allClients = d->mainWindow->factory()->clients();
|
|
QPtrListIterator<KXMLGUIClient> it( allClients );
|
|
KXMLGUIClient *client;
|
|
while( (client=it.current()) !=0 )
|
|
{
|
|
++it;
|
|
KActionPtrList actions = client->actionCollection()->actions();
|
|
for (int j = 0; j < (int)actions.count(); j++) {
|
|
KAction* action = actions[j];
|
|
KShortcut sc = action->shortcut();
|
|
for (int i = 0; i < (int)sc.count(); i++) {
|
|
KKeySequence seq = sc.seq(i);
|
|
if (seq.count() == 1) {
|
|
QString s = seq.toString();
|
|
if (availableAccessKeys.contains(s))
|
|
availableAccessKeys.remove(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Find all visible, focusable widgets and create a QLabel for each. Don't exceed
|
|
// available list of access keys.
|
|
QWidgetList* allWidgets = kapp->allWidgets();
|
|
QWidget* widget = allWidgets->first();
|
|
int accessCount = 0;
|
|
int maxAccessCount = availableAccessKeys.length();
|
|
int overlap = 20;
|
|
QPoint prevGlobalPos = QPoint(-overlap, -overlap);
|
|
while (widget && (accessCount < maxAccessCount)) {
|
|
if (widget->isVisible() && widget->isFocusEnabled() ) {
|
|
QRect r = widget->rect();
|
|
QPoint p(r.x(), r.y());
|
|
// Don't display an access key if within overlap pixels of previous one.
|
|
QPoint globalPos = widget->mapToGlobal(p);
|
|
QPoint diffPos = globalPos - prevGlobalPos;
|
|
if (diffPos.manhattanLength() > overlap) {
|
|
accessCount++;
|
|
QLabel* lab=new QLabel(widget, "", widget, 0, Qt::WDestructiveClose);
|
|
lab->setPalette(QToolTip::palette());
|
|
lab->setLineWidth(2);
|
|
lab->setFrameStyle(QFrame::Box | QFrame::Plain);
|
|
lab->setMargin(3);
|
|
lab->adjustSize();
|
|
lab->move(p);
|
|
if (!d->accessKeyLabels) {
|
|
d->accessKeyLabels = new QPtrList<QLabel>;
|
|
d->accessKeyLabels->setAutoDelete(true);
|
|
}
|
|
d->accessKeyLabels->append(lab);
|
|
prevGlobalPos = globalPos;
|
|
}
|
|
}
|
|
widget = allWidgets->next();
|
|
}
|
|
if (accessCount > 0) {
|
|
// Sort the access keys from left to right and down the screen.
|
|
QValueList<KSortedLabel> sortedLabels;
|
|
for (int i = 0; i < accessCount; i++)
|
|
sortedLabels.append(KSortedLabel(d->accessKeyLabels->at(i)));
|
|
qHeapSort( sortedLabels );
|
|
// Assign access key labels.
|
|
for (int i = 0; i < accessCount; i++) {
|
|
QLabel* lab = sortedLabels[i].label();
|
|
QChar s = availableAccessKeys[i];
|
|
lab->setText(s);
|
|
lab->adjustSize();
|
|
lab->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handling of the HTML accesskey attribute.
|
|
bool KKbdAccessExtensions::handleAccessKey( const QKeyEvent* ev )
|
|
{
|
|
// Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
|
|
// but this code must act as if the modifiers weren't pressed
|
|
if (!d->accessKeyLabels) return false;
|
|
QChar c;
|
|
if( ev->key() >= Key_A && ev->key() <= Key_Z )
|
|
c = 'A' + ev->key() - Key_A;
|
|
else if( ev->key() >= Key_0 && ev->key() <= Key_9 )
|
|
c = '0' + ev->key() - Key_0;
|
|
else {
|
|
// TODO fake XKeyEvent and XLookupString ?
|
|
// This below seems to work e.g. for eacute though.
|
|
if( ev->text().length() == 1 )
|
|
c = ev->text()[ 0 ];
|
|
}
|
|
if( c.isNull())
|
|
return false;
|
|
|
|
QLabel* lab = d->accessKeyLabels->first();
|
|
while (lab) {
|
|
if (lab->text() == c) {
|
|
lab->buddy()->setFocus();
|
|
delete d->accessKeyLabels;
|
|
d->accessKeyLabels = 0;
|
|
return true;
|
|
}
|
|
lab = d->accessKeyLabels->next();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
KSortedLabel::KSortedLabel(QLabel* l) :
|
|
m_l(l) { }
|
|
|
|
KSortedLabel::KSortedLabel() :
|
|
m_l(0) { }
|
|
|
|
bool KSortedLabel::operator<( KSortedLabel l )
|
|
{
|
|
QPoint p1 = m_l->mapToGlobal(m_l->pos());
|
|
QPoint p2 = l.label()->mapToGlobal(l.label()->pos());
|
|
return (p1.y() < p2.y() || (p1.y() == p2.y() && p1.x() < p2.x()));
|
|
}
|