lib

kkbdaccessextensions.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005, Gary Cramblitt <garycramblitt@comcast.net>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 
00020 // Qt includes
00021 #include <qsplitter.h>
00022 #include <qdockwindow.h>
00023 #include <qdockarea.h>
00024 #include <qevent.h>
00025 #include <qcursor.h>
00026 #include <qobjectlist.h>
00027 #include <qwidgetlist.h>
00028 #include <qlabel.h>
00029 #include <qtooltip.h>
00030 
00031 // KDE includes
00032 #include <klocale.h>
00033 #include <kglobal.h>
00034 #include <kapplication.h>
00035 #include <kmainwindow.h>
00036 #include <kaction.h>
00037 #include <kdebug.h>
00038 
00039 // KKbdAccessExtensions includes
00040 #include "kkbdaccessextensions.h"
00041 // TODO: See eventFilter method.
00042 //#include "kkbdaccessextensions.moc"
00043 
00044 class KPanelKbdSizerIcon : public QCursor
00045 {
00046     public:
00047         KPanelKbdSizerIcon() :
00048             QCursor(Qt::SizeAllCursor),
00049             isActive(false)
00050         {
00051             currentPos = QPoint(-1, -1);
00052         }
00053 
00054         ~KPanelKbdSizerIcon()
00055         {
00056             hide();
00057         }
00058 
00059         void show(const QPoint p) {
00060             if (!isActive) {
00061                 originalPos = QCursor::pos();
00062                 kapp->setOverrideCursor(*this);
00063                 isActive = true;
00064             }
00065             if (p != pos())
00066                 setPos(p);
00067             currentPos = p;
00068         }
00069 
00070         void hide() {
00071             if (isActive) {
00072                 kapp->restoreOverrideCursor();
00073                 QCursor::setPos(originalPos);
00074             }
00075             isActive = false;
00076         }
00077 
00078         void setShape(int shayp)
00079         {
00080             if (shayp != shape()) {
00081                 // Must restore and override to get the icon to refresh.
00082                 if (isActive) kapp->restoreOverrideCursor();
00083                 QCursor::setShape(shayp);
00084                 if (isActive) kapp->setOverrideCursor(*this);
00085             }
00086         }
00087 
00088         // Return the difference between a position and where icon is supposed to be.
00089         QSize delta(const QPoint p)
00090         {
00091             QPoint d = p - currentPos;
00092             return QSize(d.x(), d.y());
00093         }
00094 
00095         // Return the difference between where the icon is currently positioned and where
00096         // it is supposed to be.
00097         QSize delta() { return delta(pos()); }
00098 
00099         // True if the sizing icon is visible.
00100         bool isActive;
00101 
00102     private:
00103         // Icon's current position.
00104         QPoint currentPos;
00105         // Mouse cursor's original position when icon is shown.
00106         QPoint originalPos;
00107 };
00108 
00109 class KKbdAccessExtensionsPrivate
00110 {
00111     public:
00112         KKbdAccessExtensionsPrivate() :
00113             fwdAction(0),
00114             revAction(0),
00115             accessKeysAction(0),
00116             panel(0),
00117             handleNdx(0),
00118             icon(0),
00119             stepSize(10),
00120             accessKeyLabels(0) {};
00121 
00122         ~KKbdAccessExtensionsPrivate()
00123         {
00124             delete icon;
00125             // TODO: This crashes, but should delete in the event that KMainWindow is not deleted.
00126             if (accessKeyLabels) {
00127                 accessKeyLabels->setAutoDelete(false);
00128                 delete accessKeyLabels;
00129             }
00130         }
00131 
00132         // Action that starts panel sizing (defaults to F8), forward and reverse;
00133         KAction* fwdAction;
00134         KAction* revAction;
00135 
00136         // Action that starts access keys.
00137         KAction* accessKeysAction;
00138 
00139         // The splitter or dockwindow currently being sized.  If 0, sizing is not in progress.
00140         QWidget* panel;
00141 
00142         // Index of current handle of the panel.  When panel is a QDockWindow:
00143         //      1 = size horizontally
00144         //      2 = size vertically
00145         uint handleNdx;
00146 
00147         // Sizing icon.
00148         KPanelKbdSizerIcon* icon;
00149 
00150         // Sizing increment.
00151         int stepSize;
00152 
00153         // List of the access key QLabels.  If not 0, access keys are onscreen.
00154         QPtrList<QLabel>* accessKeyLabels;
00155 
00156         // Pointer to the KMainWindow.
00157         KMainWindow* mainWindow;
00158 };
00159 
00160 KKbdAccessExtensions::KKbdAccessExtensions(KMainWindow* parent, const char* name) :
00161     QObject(parent, name)
00162 {
00163     // kdDebug() << "KKbdAccessExtensions::KKbdAccessExtensions: running." << endl;
00164     d = new KKbdAccessExtensionsPrivate;
00165     d->mainWindow = parent;
00166     d->fwdAction = new KAction(i18n("Resize Panel Forward"), KShortcut("F8"),
00167         0, 0, parent->actionCollection(), "resize_panel_forward");
00168     d->revAction = new KAction(i18n("Resize Panel Reverse"), KShortcut("Shift+F8"),
00169         0, 0, parent->actionCollection(), "resize_panel_reverse");
00170     d->accessKeysAction = new KAction(i18n("Access Keys"), KShortcut("Alt+F8"),
00171         0, 0, parent->actionCollection(), "access_keys");
00172     // "Disable" the shortcuts so we can see them in eventFilter.
00173     d->fwdAction->setEnabled(false);
00174     d->revAction->setEnabled(false);
00175     d->accessKeysAction->setEnabled(false);
00176     d->icon = new KPanelKbdSizerIcon();
00177     kapp->installEventFilter(this);
00178 }
00179 
00180 KKbdAccessExtensions::~KKbdAccessExtensions()
00181 {
00182     kapp->removeEventFilter(this);
00183     if (d->panel) exitSizing();
00184     delete d;
00185 }
00186 
00187 int KKbdAccessExtensions::stepSize() const { return d->stepSize; }
00188 
00189 void KKbdAccessExtensions::setStepSize(int s) { d->stepSize = s; }
00190 
00191 bool KKbdAccessExtensions::eventFilter( QObject *o, QEvent *e )
00192 {
00193     if ( e->type() == QEvent::KeyPress ) {
00194         // TODO: This permits only a single-key shortcut.  For example, Alt+S,R would not work.
00195         // If user configures a multi-key shortcut, it is undefined what will happen here.
00196         // It would be better to handle these as KShortcut activate() signals, but the problem
00197         // is that once a QDockWindow is undocked and has focus, the KShortcut activate() signals
00198         // don't fire anymore.
00199         KShortcut fwdSc = d->fwdAction->shortcut();
00200         KShortcut revSc = d->revAction->shortcut();
00201         KShortcut accessKeysSc = d->accessKeysAction->shortcut();
00202         QKeyEvent* kev = dynamic_cast<QKeyEvent *>(e);
00203         KKey k = KKey(kev);
00204         KShortcut sc = KShortcut(k);
00205         // kdDebug() << "KKbdAccessExtensions::eventFilter: Key press " << sc << endl;
00206         if (!d->accessKeyLabels) {
00207             if (sc == fwdSc) {
00208                 nextHandle();
00209                 return true;
00210             }
00211             if (sc == revSc) {
00212                 prevHandle();
00213                 return true;
00214             }
00215         }
00216         if (d->panel) {
00217             if (k == KKey(Key_Escape))
00218                 exitSizing();
00219             else
00220                 resizePanelFromKey(kev->key(), kev->state());
00221             // Eat the key.
00222             return true;
00223         }
00224         if (sc == accessKeysSc && !d->panel) {
00225             if (d->accessKeyLabels) {
00226                 delete d->accessKeyLabels;
00227                 d->accessKeyLabels = 0;
00228             } else
00229                 displayAccessKeys();
00230             return true;
00231         }
00232         if (d->accessKeyLabels) {
00233             if (k == KKey(Key_Escape)) {
00234                 delete d->accessKeyLabels;
00235                 d->accessKeyLabels = 0;
00236             } else
00237                 handleAccessKey(kev);
00238             return true;
00239         }
00240         return false;
00241     }
00242     else if (d->icon->isActive && e->type() == QEvent::MouseButtonPress) {
00243         exitSizing();
00244         return true;
00245     }
00246     else if (d->accessKeyLabels && e->type() == QEvent::MouseButtonPress) {
00247         delete d->accessKeyLabels;
00248         d->accessKeyLabels = 0;
00249         return true;
00250     }
00251 /*    else if (e->type() == QEvent::MouseMove && d->icon->isActive) {
00252         // Lock mouse cursor down.
00253         showIcon();
00254         dynamic_cast<QMouseEvent *>(e)->accept();
00255         return true;
00256     }*/
00257     else if (e->type() == QEvent::MouseMove && d->icon->isActive && d->panel) {
00258         // Resize according to mouse movement.
00259         QMouseEvent* me = dynamic_cast<QMouseEvent *>(e);
00260         QSize s = d->icon->delta();
00261         int dx = s.width();
00262         int dy = s.height();
00263         resizePanel(dx, dy, me->state());
00264         me->accept();
00265         showIcon();
00266         return true;
00267     }
00268     else if (e->type() == QEvent::Resize && d->panel && o == d->panel) {
00269         // TODO: This doesn't always work.
00270         showIcon();
00271     }
00272     return false;
00273 }
00274 
00275 QWidgetList* KKbdAccessExtensions::getAllPanels()
00276 {
00277     QWidgetList* allWidgets = kapp->allWidgets();
00278     QWidgetList* allPanels = new QWidgetList;
00279     QWidget* widget = allWidgets->first();
00280     while (widget) {
00281         if (widget->isVisible()) {
00282             if (::qt_cast<QSplitter*>( widget )) {
00283                 // Only size QSplitters with at least two handles (there is always one hidden).
00284                 if (dynamic_cast<QSplitter *>(widget)->sizes().count() >= 2)
00285                     allPanels->append(widget);
00286             } else if (::qt_cast<QDockWindow*>( widget )) {
00287                 if (dynamic_cast<QDockWindow *>(widget)->isResizeEnabled()) {
00288                     // kdDebug() << "KKbdAccessExtensions::getAllPanels: QDockWindow = " << widget->name() << endl;
00289                     allPanels->append(widget);
00290                 }
00291             }
00292         }
00293         widget = allWidgets->next();
00294     }
00295     delete allWidgets;
00296     return allPanels;
00297 }
00298 
00299 void KKbdAccessExtensions::nextHandle()
00300 {
00301     QWidget* panel = d->panel;
00302     // See if current panel has another handle.  If not, find next panel.
00303     if (panel) {
00304         bool advance = true;
00305         d->handleNdx++;
00306         if (::qt_cast<QSplitter*>( panel ))
00307             advance = (d->handleNdx >= dynamic_cast<QSplitter *>(panel)->sizes().count());
00308         else
00309             // Undocked windows have only one "handle" (center).
00310             advance = (d->handleNdx > 2 || !dynamic_cast<QDockWindow *>(panel)->area());
00311         if (advance) {
00312             QWidgetList* allWidgets = getAllPanels();
00313             allWidgets->findRef(panel);
00314             panel = 0;
00315             if (allWidgets->current()) panel = allWidgets->next();
00316             delete allWidgets;
00317             d->handleNdx = 1;
00318         }
00319     } else {
00320         // Find first panel.
00321         QWidgetList* allWidgets = getAllPanels();
00322         panel = allWidgets->first();
00323         delete allWidgets;
00324         d->handleNdx = 1;
00325     }
00326     d->panel = panel;
00327     if (panel)
00328         showIcon();
00329     else
00330         exitSizing();
00331 }
00332 
00333 void KKbdAccessExtensions::prevHandle()
00334 {
00335     QWidget* panel = d->panel;
00336     // See if current panel has another handle.  If not, find previous panel.
00337     if (panel) {
00338         bool rewind = true;
00339         d->handleNdx--;
00340         rewind = (d->handleNdx < 1);
00341         if (rewind) {
00342             QWidgetList* allWidgets = getAllPanels();
00343             allWidgets->findRef(panel);
00344             panel = 0;
00345             if (allWidgets->current()) panel = allWidgets->prev();
00346             delete allWidgets;
00347             if (panel) {
00348                 if (::qt_cast<QSplitter*>( panel ))
00349                     d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
00350                 else {
00351                     if (dynamic_cast<QDockWindow *>(panel)->area())
00352                         d->handleNdx = 2;
00353                     else
00354                         d->handleNdx = 1;
00355                 }
00356             }
00357         }
00358     } else {
00359         // Find last panel.
00360         QWidgetList* allWidgets = getAllPanels();
00361         panel = allWidgets->last();
00362         delete allWidgets;
00363         if (panel) {
00364             if (::qt_cast<QSplitter*>( panel ))
00365                 d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
00366             else {
00367                 if (dynamic_cast<QDockWindow *>(panel)->area())
00368                     d->handleNdx = 2;
00369                 else
00370                     d->handleNdx = 1;
00371             }
00372         }
00373     }
00374     d->panel = panel;
00375     if (panel)
00376         showIcon();
00377     else
00378         exitSizing();
00379 }
00380 
00381 void KKbdAccessExtensions::exitSizing()
00382 {
00383     // kdDebug() << "KKbdAccessExtensions::exiting sizing mode." << endl;
00384     hideIcon();
00385     d->handleNdx = 0;
00386     d->panel = 0;
00387 }
00388 
00389 void KKbdAccessExtensions::showIcon()
00390 {
00391     if (!d->panel) return;
00392     QPoint p;
00393     // kdDebug() << "KKbdAccessExtensions::showIcon: topLevelWidget = " << d->panel->topLevelWidget()->name() << endl;
00394     if (::qt_cast<QSplitter*>( d->panel )) {
00395         QSplitter* splitter = dynamic_cast<QSplitter *>(d->panel);
00396         int handleNdx = d->handleNdx - 1;
00397         QValueList<int> sizes = splitter->sizes();
00398         // kdDebug() << "KKbdAccessExtensions::showIcon: sizes = " << sizes << endl;
00399         if (splitter->orientation() == Qt::Horizontal) {
00400             d->icon->setShape(Qt::SizeHorCursor);
00401             p.setX(sizes[handleNdx] + (splitter->handleWidth() / 2));
00402             p.setY(splitter->height() / 2);
00403         } else {
00404             d->icon->setShape(Qt::SizeVerCursor);
00405             p.setX(splitter->width() / 2);
00406             p.setY(sizes[handleNdx] + (splitter->handleWidth() / 2));
00407         }
00408         // kdDebug() << "KKbdAccessExtensions::showIcon: p = " << p << endl;
00409         p = splitter->mapToGlobal(p);
00410         // kdDebug() << "KKbdAccessExtensions::showIcon: mapToGlobal = " << p << endl;
00411     } else {
00412         QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
00413         p = dockWindow->pos();
00414         if (dockWindow->area()) {
00415             // kdDebug() << "KKbdAccessExtensions::showIcon: pos = " << p << " of window = " << dockWindow->parentWidget()->name() << endl;
00416             p = dockWindow->parentWidget()->mapTo(dockWindow->topLevelWidget(), p);
00417             // kdDebug() << "KKbdAccessExtensions::showIcon: mapTo = " << p << " of window = " << dockWindow->topLevelWidget()->name() << endl;
00418             // TODO: How to get the handle width?
00419             if (d->handleNdx == 1) {
00420                 d->icon->setShape(Qt::SizeHorCursor);
00421                 if (dockWindow->area()->orientation() == Qt::Vertical) {
00422                     if (dockWindow->area()->handlePosition() == QDockArea::Normal)
00423                         // Handle is to the right of the dock window.
00424                         p.setX(p.x() + dockWindow->width());
00425                         // else Handle is to the left of the dock window.
00426                 } else
00427                     // Handle is to the right of the dock window.
00428                     p.setX(p.x() + dockWindow->width());
00429                 p.setY(p.y() + (dockWindow->height() / 2));
00430             } else {
00431                 d->icon->setShape(Qt::SizeVerCursor);
00432                 p.setX(p.x() + (dockWindow->width() / 2));
00433                 if (dockWindow->area()->orientation() == Qt::Vertical)
00434                     // Handle is below the dock window.
00435                     p.setY(p.y() + dockWindow->height());
00436                 else {
00437                     if (dockWindow->area()->handlePosition() == QDockArea::Normal)
00438                         // Handle is below the dock window.
00439                         p.setY(p.y() + dockWindow->height());
00440                         // else Handle is above the dock window.
00441                 }
00442             }
00443             p = dockWindow->topLevelWidget()->mapToGlobal(p);
00444         } else {
00445             d->icon->setShape(Qt::SizeAllCursor);
00446             p = QPoint(dockWindow->width() / 2, dockWindow->height() / 2);
00447             p = dockWindow->mapToGlobal(p);       // Undocked.  Position in center of window.
00448         }
00449     }
00450     // kdDebug() << "KKbdAccessExtensions::showIcon: show(p) = " << p << endl;
00451     d->icon->show(p);
00452 }
00453 
00454 void KKbdAccessExtensions::hideIcon()
00455 {
00456     d->icon->hide();
00457 }
00458 
00459 void KKbdAccessExtensions::resizePanel(int dx, int dy, int state)
00460 {
00461     int adj = dx + dy;
00462     if (adj == 0) return;
00463     // kdDebug() << "KKbdAccessExtensions::resizePanel: panel = " << d->panel->name() << endl;
00464     if (::qt_cast<QSplitter*>( d->panel )) {
00465         QSplitter* splitter = dynamic_cast<QSplitter *>(d->panel);
00466         int handleNdx = d->handleNdx - 1;
00467         QValueList<int> sizes = splitter->sizes();
00468         // kdDebug() << "KKbdAccessExtensions::resizePanel: before sizes = " << sizes << endl;
00469         sizes[handleNdx] = sizes[handleNdx] + adj;
00470         // kdDebug() << "KKbdAccessExtensions::resizePanel: setSizes = " << sizes << endl;
00471         splitter->setSizes(sizes);
00472         QApplication::postEvent(splitter, new QEvent(QEvent::LayoutHint));
00473     } else {
00474         // TODO: How to get the handle width?
00475         QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
00476         if (dockWindow->area()) {
00477             // kdDebug() << "KKbdAccessExtensions::resizePanel: fixedExtent = " << dockWindow->fixedExtent() << endl;
00478             QSize fe = dockWindow->fixedExtent();
00479             if (d->handleNdx == 1) {
00480                 // When vertically oriented and dock area is on right side of screen, pressing
00481                 // left arrow increases size.
00482                 if (dockWindow->area()->orientation() == Qt::Vertical &&
00483                     dockWindow->area()->handlePosition() == QDockArea::Reverse) adj = -adj;
00484                 int w = fe.width();
00485                 if (w < 0) w = dockWindow->width();
00486                 w = w + adj;
00487                 if (w > 0 ) dockWindow->setFixedExtentWidth(w);
00488             } else {
00489                 // When horizontally oriented and dock area is at bottom of screen,
00490                 // pressing up arrow increases size.
00491                 if (dockWindow->area()->orientation() == Qt::Horizontal &&
00492                     dockWindow->area()->handlePosition() == QDockArea::Reverse) adj = -adj;
00493                 int h = fe.height();
00494                 if (h < 0) h = dockWindow->height();
00495                 h = h + adj;
00496                 if (h > 0) dockWindow->setFixedExtentHeight(h);
00497             }
00498             dockWindow->updateGeometry();
00499             QApplication::postEvent(dockWindow->area(), new QEvent(QEvent::LayoutHint));
00500             // kdDebug() << "KKbdAccessExtensions::resizePanel: fixedExtent = " << dockWindow->fixedExtent() << endl;
00501         } else {
00502             if (state == Qt::ShiftButton) {
00503                 QSize s = dockWindow->size();
00504                 s.setWidth(s.width() + dx);
00505                 s.setHeight(s.height() + dy);
00506                 dockWindow->resize(s);
00507             } else {
00508                 QPoint p = dockWindow->pos();
00509                 p.setX(p.x() + dx);
00510                 p.setY(p.y() + dy);
00511                 dockWindow->move(p);
00512             }
00513         }
00514     }
00515 }
00516 
00517 void KKbdAccessExtensions::resizePanelFromKey(int key, int state)
00518 {
00519     // kdDebug() << "KPanelKdbSizer::resizePanelFromKey: key = " << key << " state = " << state << endl;
00520     if (!d->panel) return;
00521     int dx = 0;
00522     int dy = 0;
00523     int stepSize = d->stepSize;
00524     switch (key) {
00525         case Qt::Key_Left:      dx = -stepSize;     break;
00526         case Qt::Key_Right:     dx = stepSize;      break;
00527         case Qt::Key_Up:        dy = -stepSize;     break;
00528         case Qt::Key_Down:      dy = stepSize;      break;
00529         case Qt::Key_Prior:     dy = -5 * stepSize; break;
00530         case Qt::Key_Next:      dy = 5 * stepSize;  break;
00531     }
00532     int adj = dx + dy;
00533     // kdDebug() << "KKbdAccessExtensions::resizePanelFromKey: adj = " << adj << endl;
00534     if (adj != 0)
00535         resizePanel(dx, dy, state);
00536     else {
00537         if (key == Qt::Key_Enter && ::qt_cast<QDockWindow*>( d->panel )) {
00538             QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
00539             if (dockWindow->area())
00540                 dockWindow->undock();
00541             else
00542                 dockWindow->dock();
00543         }
00544     }
00545     showIcon();
00546 }
00547 
00548 void KKbdAccessExtensions::displayAccessKeys()
00549 {
00550     // Build a list of valid access keys that don't collide with shortcuts.
00551     QString availableAccessKeys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
00552     QPtrList<KXMLGUIClient> allClients = d->mainWindow->factory()->clients();
00553     QPtrListIterator<KXMLGUIClient> it( allClients );
00554     KXMLGUIClient *client;
00555     while( (client=it.current()) !=0 )
00556     {
00557         ++it;
00558         KActionPtrList actions = client->actionCollection()->actions();
00559         for (int j = 0; j < (int)actions.count(); j++) {
00560             KAction* action = actions[j];
00561             KShortcut sc = action->shortcut();
00562             for (int i = 0; i < (int)sc.count(); i++) {
00563                 KKeySequence seq = sc.seq(i);
00564                 if (seq.count() == 1) {
00565                     QString s = seq.toString();
00566                     if (availableAccessKeys.contains(s))
00567                         availableAccessKeys.remove(s);
00568                 }
00569             }
00570         }
00571     }
00572     // Find all visible, focusable widgets and create a QLabel for each.  Don't exceed
00573     // available list of access keys.
00574     QWidgetList* allWidgets = kapp->allWidgets();
00575     QWidget* widget = allWidgets->first();
00576     int accessCount = 0;
00577     int maxAccessCount = availableAccessKeys.length();
00578     int overlap = 20;
00579     QPoint prevGlobalPos = QPoint(-overlap, -overlap);
00580     while (widget && (accessCount < maxAccessCount)) {
00581         if (widget->isVisible() && widget->isFocusEnabled() ) {
00582             QRect r = widget->rect();
00583             QPoint p(r.x(), r.y());
00584             // Don't display an access key if within overlap pixels of previous one.
00585             QPoint globalPos = widget->mapToGlobal(p);
00586             QPoint diffPos = globalPos - prevGlobalPos;
00587             if (diffPos.manhattanLength() > overlap) {
00588                 accessCount++;
00589                 QLabel* lab=new QLabel(widget, "", widget, 0, Qt::WDestructiveClose);
00590                 lab->setPalette(QToolTip::palette());
00591                 lab->setLineWidth(2);
00592                 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
00593                 lab->setMargin(3);
00594                 lab->adjustSize();
00595                 lab->move(p);
00596                 if (!d->accessKeyLabels) {
00597                     d->accessKeyLabels = new QPtrList<QLabel>;
00598                     d->accessKeyLabels->setAutoDelete(true);
00599                 }
00600                 d->accessKeyLabels->append(lab);
00601                 prevGlobalPos = globalPos;
00602             }
00603         }
00604         widget = allWidgets->next();
00605     }
00606     if (accessCount > 0) {
00607         // Sort the access keys from left to right and down the screen.
00608         QValueList<KSortedLabel> sortedLabels;
00609         for (int i = 0; i < accessCount; i++)
00610             sortedLabels.append(KSortedLabel(d->accessKeyLabels->at(i)));
00611         qHeapSort( sortedLabels );
00612         // Assign access key labels.
00613         for (int i = 0; i < accessCount; i++) {
00614             QLabel* lab = sortedLabels[i].label();
00615             QChar s = availableAccessKeys[i];
00616             lab->setText(s);
00617             lab->adjustSize();
00618             lab->show();
00619         }
00620     }
00621 }
00622 
00623 // Handling of the HTML accesskey attribute.
00624 bool KKbdAccessExtensions::handleAccessKey( const QKeyEvent* ev )
00625 {
00626 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that,
00627 // but this code must act as if the modifiers weren't pressed
00628     if (!d->accessKeyLabels) return false;
00629     QChar c;
00630     if( ev->key() >= Key_A && ev->key() <= Key_Z )
00631         c = 'A' + ev->key() - Key_A;
00632     else if( ev->key() >= Key_0 && ev->key() <= Key_9 )
00633         c = '0' + ev->key() - Key_0;
00634     else {
00635         // TODO fake XKeyEvent and XLookupString ?
00636         // This below seems to work e.g. for eacute though.
00637         if( ev->text().length() == 1 )
00638             c = ev->text()[ 0 ];
00639     }
00640     if( c.isNull())
00641         return false;
00642 
00643     QLabel* lab = d->accessKeyLabels->first();
00644     while (lab) {
00645         if (lab->text() == c) {
00646             lab->buddy()->setFocus();
00647             delete d->accessKeyLabels;
00648             d->accessKeyLabels = 0;
00649             return true;
00650         }
00651         lab = d->accessKeyLabels->next();
00652     }
00653     return false;
00654 }
00655 
00656 KSortedLabel::KSortedLabel(QLabel* l) :
00657     m_l(l) { }
00658 
00659 KSortedLabel::KSortedLabel() :
00660     m_l(0) { }
00661 
00662 bool KSortedLabel::operator<( KSortedLabel l )
00663 {
00664     QPoint p1 = m_l->mapToGlobal(m_l->pos());
00665     QPoint p2 = l.label()->mapToGlobal(l.label()->pos());
00666     return (p1.y() < p2.y() || (p1.y() == p2.y() && p1.x() < p2.x()));
00667 }
KDE Home | KDE Accessibility Home | Description of Access Keys