00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <QSplitter>
00022 #include <q3dockwindow.h>
00023 #include <q3dockarea.h>
00024 #include <QEvent>
00025 #include <QCursor>
00026 #include <QObject>
00027 #include <QWidget>
00028 #include <QLabel>
00029 #include <QToolTip>
00030 #include <kxmlguifactory.h>
00031
00032 #include <Q3PtrList>
00033 #include <QKeyEvent>
00034 #include <QFrame>
00035 #include <Q3ValueList>
00036 #include <QMouseEvent>
00037
00038
00039 #include <klocale.h>
00040 #include <kglobal.h>
00041 #include <kapplication.h>
00042 #include <kmainwindow.h>
00043 #include <kaction.h>
00044 #include <kdebug.h>
00045 #include <q3tl.h>
00046 #include <kactioncollection.h>
00047
00048
00049 #include "kkbdaccessextensions.h"
00050
00051
00052
00053 class KPanelKbdSizerIcon : public QCursor
00054 {
00055 public:
00056 KPanelKbdSizerIcon() :
00057 QCursor(Qt::SizeAllCursor),
00058 isActive(false)
00059 {
00060 currentPos = QPoint(-1, -1);
00061 }
00062
00063 ~KPanelKbdSizerIcon()
00064 {
00065 hide();
00066 }
00067
00068 void show(const QPoint p) {
00069 if (!isActive) {
00070 originalPos = QCursor::pos();
00071 kapp->setOverrideCursor(*this);
00072 isActive = true;
00073 }
00074 if (p != pos())
00075 setPos(p);
00076 currentPos = p;
00077 }
00078
00079 void hide() {
00080 if (isActive) {
00081 kapp->restoreOverrideCursor();
00082 QCursor::setPos(originalPos);
00083 }
00084 isActive = false;
00085 }
00086
00087 void setShape(Qt::CursorShape shayp)
00088 {
00089 if (shayp != shape()) {
00090
00091 if (isActive) kapp->restoreOverrideCursor();
00092 QCursor::setShape(shayp);
00093 if (isActive) kapp->setOverrideCursor(*this);
00094 }
00095 }
00096
00097
00098 QSize delta(const QPoint p)
00099 {
00100 QPoint d = p - currentPos;
00101 return QSize(d.x(), d.y());
00102 }
00103
00104
00105
00106 QSize delta() { return delta(pos()); }
00107
00108
00109 bool isActive;
00110
00111 private:
00112
00113 QPoint currentPos;
00114
00115 QPoint originalPos;
00116 };
00117
00118 class KKbdAccessExtensionsPrivate
00119 {
00120 public:
00121 KKbdAccessExtensionsPrivate() :
00122 fwdAction(0),
00123 revAction(0),
00124 accessKeysAction(0),
00125 panel(0),
00126 handleNdx(0),
00127 icon(0),
00128 stepSize(10),
00129 accessKeyLabels(0) {};
00130
00131 ~KKbdAccessExtensionsPrivate()
00132 {
00133 delete icon;
00134
00135 if (accessKeyLabels) {
00136 accessKeyLabels->setAutoDelete(false);
00137 delete accessKeyLabels;
00138 }
00139 }
00140
00141
00142 KAction* fwdAction;
00143 KAction* revAction;
00144
00145
00146 KAction* accessKeysAction;
00147
00148
00149 QWidget* panel;
00150
00151
00152
00153
00154 int handleNdx;
00155
00156
00157 KPanelKbdSizerIcon* icon;
00158
00159
00160 int stepSize;
00161
00162
00163 Q3PtrList<QLabel>* accessKeyLabels;
00164
00165
00166 KMainWindow* mainWindow;
00167 };
00168
00169 KKbdAccessExtensions::KKbdAccessExtensions(KMainWindow* parent, const char* ) :
00170 QObject( parent )
00171 {
00172
00173 d = new KKbdAccessExtensionsPrivate;
00174 d->mainWindow = parent;
00175 d->fwdAction = new KAction(i18n("Resize Panel Forward"),
00176 parent->actionCollection(), "resize_panel_forward");
00177 d->fwdAction->setShortcut(KShortcut("F8"));
00178 d->revAction = new KAction(i18n("Resize Panel Reverse"),
00179 parent->actionCollection(), "resize_panel_reverse");
00180 d->revAction->setShortcut(KShortcut("Shift+F8"));
00181 d->accessKeysAction = new KAction(i18n("Access Keys"),
00182 parent->actionCollection(), "access_keys");
00183 d->accessKeysAction->setShortcut(KShortcut("Alt+F8"));
00184
00185 d->fwdAction->setEnabled(false);
00186 d->revAction->setEnabled(false);
00187 d->accessKeysAction->setEnabled(false);
00188 d->icon = new KPanelKbdSizerIcon();
00189 kapp->installEventFilter(this);
00190 }
00191
00192 KKbdAccessExtensions::~KKbdAccessExtensions()
00193 {
00194 kapp->removeEventFilter(this);
00195 if (d->panel) exitSizing();
00196 delete d;
00197 }
00198
00199 int KKbdAccessExtensions::stepSize() const { return d->stepSize; }
00200
00201 void KKbdAccessExtensions::setStepSize(int s) { d->stepSize = s; }
00202
00203 bool KKbdAccessExtensions::eventFilter( QObject *o, QEvent *e )
00204 {
00205 if ( e->type() == QEvent::KeyPress ) {
00206
00207
00208
00209
00210
00211 KShortcut fwdSc = d->fwdAction->shortcut();
00212 KShortcut revSc = d->revAction->shortcut();
00213 KShortcut accessKeysSc = d->accessKeysAction->shortcut();
00214 QKeyEvent* kev = static_cast<QKeyEvent *>(e);
00215 KShortcut sc = KShortcut(kev->key());
00216
00217 if (!d->accessKeyLabels) {
00218 if (sc == fwdSc) {
00219 nextHandle();
00220 return true;
00221 }
00222 if (sc == revSc) {
00223 prevHandle();
00224 return true;
00225 }
00226 }
00227 if (d->panel) {
00228 if (kev->key() == Qt::Key_Escape)
00229 exitSizing();
00230 else
00231 resizePanelFromKey( kev->key(), kev->modifiers() );
00232
00233 return true;
00234 }
00235 if (sc == accessKeysSc && !d->panel) {
00236 if (d->accessKeyLabels) {
00237 delete d->accessKeyLabels;
00238 d->accessKeyLabels = 0;
00239 } else
00240 displayAccessKeys();
00241 return true;
00242 }
00243 if (d->accessKeyLabels) {
00244 if (kev->key() == Qt::Key_Escape) {
00245 delete d->accessKeyLabels;
00246 d->accessKeyLabels = 0;
00247 } else
00248 handleAccessKey(kev);
00249 return true;
00250 }
00251 return false;
00252 }
00253 else if (d->icon->isActive && e->type() == QEvent::MouseButtonPress) {
00254 exitSizing();
00255 return true;
00256 }
00257 else if (d->accessKeyLabels && e->type() == QEvent::MouseButtonPress) {
00258 delete d->accessKeyLabels;
00259 d->accessKeyLabels = 0;
00260 return true;
00261 }
00262
00263
00264
00265
00266
00267
00268 else if (e->type() == QEvent::MouseMove && d->icon->isActive && d->panel) {
00269
00270 QMouseEvent* me = static_cast<QMouseEvent *>(e);
00271 QSize s = d->icon->delta();
00272 int dx = s.width();
00273 int dy = s.height();
00274 resizePanel( dx, dy, me->button() );
00275 me->accept();
00276 showIcon();
00277 return true;
00278 }
00279 else if (e->type() == QEvent::Resize && d->panel && o == d->panel) {
00280
00281 showIcon();
00282 }
00283 return false;
00284 }
00285
00286 QWidgetList* KKbdAccessExtensions::getAllPanels()
00287 {
00288 QWidgetList allWidgets = kapp->allWidgets();
00289 QWidgetList* allPanels = new QWidgetList;
00290 foreach ( QWidget* widget, allWidgets ) {
00291 if (widget->isVisible()) {
00292 if (qobject_cast<QSplitter*>( widget )) {
00293
00294 if (dynamic_cast<QSplitter *>(widget)->sizes().count() >= 2)
00295 allPanels->append(widget);
00296 } else if (qobject_cast<Q3DockWindow*>( widget )) {
00297 if (dynamic_cast<Q3DockWindow *>(widget)->isResizeEnabled()) {
00298
00299 allPanels->append(widget);
00300 }
00301 }
00302 }
00303 }
00304 return allPanels;
00305 }
00306
00307 void KKbdAccessExtensions::nextHandle()
00308 {
00309 QWidget* panel = d->panel;
00310
00311 if (panel) {
00312 bool advance = true;
00313 d->handleNdx++;
00314 if (qobject_cast<QSplitter*>( panel ))
00315 advance = (d->handleNdx >= dynamic_cast<QSplitter *>(panel)->sizes().count());
00316 else
00317
00318 advance = (d->handleNdx > 2 || !dynamic_cast<Q3DockWindow *>(panel)->area());
00319 if (advance) {
00320 QWidgetList* allWidgets = getAllPanels();
00321 int index = allWidgets->indexOf(panel);
00322 panel = 0;
00323 if (index > -1)
00324 panel = allWidgets->at(index+1);
00325 delete allWidgets;
00326 d->handleNdx = 1;
00327 }
00328 } else {
00329
00330 QWidgetList* allWidgets = getAllPanels();
00331 panel = allWidgets->first();
00332 delete allWidgets;
00333 d->handleNdx = 1;
00334 }
00335 d->panel = panel;
00336 if (panel)
00337 showIcon();
00338 else
00339 exitSizing();
00340 }
00341
00342 void KKbdAccessExtensions::prevHandle()
00343 {
00344 QWidget* panel = d->panel;
00345
00346 if (panel) {
00347 bool rewind = true;
00348 d->handleNdx--;
00349 rewind = (d->handleNdx < 1);
00350 if (rewind) {
00351 QWidgetList* allWidgets = getAllPanels();
00352 int index = allWidgets->indexOf(panel);
00353 panel = 0;
00354 if (index > 0)
00355 panel = allWidgets->at(index-1);
00356 delete allWidgets;
00357 if (panel) {
00358 if (qobject_cast<QSplitter*>( panel ))
00359 d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
00360 else {
00361 if (dynamic_cast<Q3DockWindow *>(panel)->area())
00362 d->handleNdx = 2;
00363 else
00364 d->handleNdx = 1;
00365 }
00366 }
00367 }
00368 } else {
00369
00370 QWidgetList* allWidgets = getAllPanels();
00371 panel = allWidgets->last();
00372 delete allWidgets;
00373 if (panel) {
00374 if (qobject_cast<QSplitter*>( panel ))
00375 d->handleNdx = dynamic_cast<QSplitter *>(panel)->sizes().count() - 1;
00376 else {
00377 if (dynamic_cast<Q3DockWindow *>(panel)->area())
00378 d->handleNdx = 2;
00379 else
00380 d->handleNdx = 1;
00381 }
00382 }
00383 }
00384 d->panel = panel;
00385 if (panel)
00386 showIcon();
00387 else
00388 exitSizing();
00389 }
00390
00391 void KKbdAccessExtensions::exitSizing()
00392 {
00393
00394 hideIcon();
00395 d->handleNdx = 0;
00396 d->panel = 0;
00397 }
00398
00399 void KKbdAccessExtensions::showIcon()
00400 {
00401 if (!d->panel) return;
00402 QPoint p;
00403
00404 QSplitter* splitter = qobject_cast<QSplitter *>(d->panel);
00405 if (splitter) {
00406 int handleNdx = d->handleNdx - 1;
00407 Q3ValueList<int> sizes = splitter->sizes();
00408
00409 if (splitter->orientation() == Qt::Horizontal) {
00410 d->icon->setShape(Qt::SizeHorCursor);
00411 p.setX(sizes[handleNdx] + (splitter->handleWidth() / 2));
00412 p.setY(splitter->height() / 2);
00413 } else {
00414 d->icon->setShape(Qt::SizeVerCursor);
00415 p.setX(splitter->width() / 2);
00416 p.setY(sizes[handleNdx] + (splitter->handleWidth() / 2));
00417 }
00418
00419 p = splitter->mapToGlobal(p);
00420
00421 } else {
00422 Q3DockWindow* dockWindow = qobject_cast<Q3DockWindow *>(d->panel);
00423 if (!dockWindow)
00424 return;
00425
00426 p = dockWindow->pos();
00427 if (dockWindow->area()) {
00428
00429 p = dockWindow->parentWidget()->mapTo(dockWindow->topLevelWidget(), p);
00430
00431
00432 if (d->handleNdx == 1) {
00433 d->icon->setShape(Qt::SizeHorCursor);
00434 if (dockWindow->area()->orientation() == Qt::Vertical) {
00435 if (dockWindow->area()->handlePosition() == Q3DockArea::Normal)
00436
00437 p.setX(p.x() + dockWindow->width());
00438
00439 } else
00440
00441 p.setX(p.x() + dockWindow->width());
00442 p.setY(p.y() + (dockWindow->height() / 2));
00443 } else {
00444 d->icon->setShape(Qt::SizeVerCursor);
00445 p.setX(p.x() + (dockWindow->width() / 2));
00446 if (dockWindow->area()->orientation() == Qt::Vertical)
00447
00448 p.setY(p.y() + dockWindow->height());
00449 else {
00450 if (dockWindow->area()->handlePosition() == Q3DockArea::Normal)
00451
00452 p.setY(p.y() + dockWindow->height());
00453
00454 }
00455 }
00456 p = dockWindow->topLevelWidget()->mapToGlobal(p);
00457 } else {
00458 d->icon->setShape(Qt::SizeAllCursor);
00459 p = QPoint(dockWindow->width() / 2, dockWindow->height() / 2);
00460 p = dockWindow->mapToGlobal(p);
00461 }
00462 }
00463
00464 d->icon->show(p);
00465 }
00466
00467 void KKbdAccessExtensions::hideIcon()
00468 {
00469 d->icon->hide();
00470 }
00471
00472 void KKbdAccessExtensions::resizePanel(int dx, int dy, int state)
00473 {
00474 int adj = dx + dy;
00475 if (adj == 0) return;
00476
00477 QSplitter* splitter = qobject_cast<QSplitter*>( d->panel );
00478 if (splitter) {
00479 int handleNdx = d->handleNdx - 1;
00480 Q3ValueList<int> sizes = splitter->sizes();
00481
00482 sizes[handleNdx] = sizes[handleNdx] + adj;
00483
00484 splitter->setSizes(sizes);
00485 QApplication::postEvent(splitter, new QEvent(QEvent::LayoutRequest));
00486 } else {
00487
00488 Q3DockWindow* dockWindow = qobject_cast<Q3DockWindow *>(d->panel);
00489 if (!dockWindow)
00490 return;
00491
00492 if (dockWindow->area()) {
00493
00494 QSize fe = dockWindow->fixedExtent();
00495 if (d->handleNdx == 1) {
00496
00497
00498 if (dockWindow->area()->orientation() == Qt::Vertical &&
00499 dockWindow->area()->handlePosition() == Q3DockArea::Reverse) adj = -adj;
00500 int w = fe.width();
00501 if (w < 0) w = dockWindow->width();
00502 w = w + adj;
00503 if (w > 0 ) dockWindow->setFixedExtentWidth(w);
00504 } else {
00505
00506
00507 if (dockWindow->area()->orientation() == Qt::Horizontal &&
00508 dockWindow->area()->handlePosition() == Q3DockArea::Reverse) adj = -adj;
00509 int h = fe.height();
00510 if (h < 0) h = dockWindow->height();
00511 h = h + adj;
00512 if (h > 0) dockWindow->setFixedExtentHeight(h);
00513 }
00514 dockWindow->updateGeometry();
00515 QApplication::postEvent(dockWindow->area(), new QEvent(QEvent::LayoutRequest));
00516
00517 } else {
00518 if (state == Qt::ShiftModifier) {
00519 QSize s = dockWindow->size();
00520 s.setWidth(s.width() + dx);
00521 s.setHeight(s.height() + dy);
00522 dockWindow->resize(s);
00523 } else {
00524 QPoint p = dockWindow->pos();
00525 p.setX(p.x() + dx);
00526 p.setY(p.y() + dy);
00527 dockWindow->move(p);
00528 }
00529 }
00530 }
00531 }
00532
00533 void KKbdAccessExtensions::resizePanelFromKey(int key, int state)
00534 {
00535
00536 if (!d->panel) return;
00537 int dx = 0;
00538 int dy = 0;
00539 int stepSize = d->stepSize;
00540 switch (key) {
00541 case Qt::Key_Left: dx = -stepSize; break;
00542 case Qt::Key_Right: dx = stepSize; break;
00543 case Qt::Key_Up: dy = -stepSize; break;
00544 case Qt::Key_Down: dy = stepSize; break;
00545 case Qt::Key_PageUp: dy = -5 * stepSize; break;
00546 case Qt::Key_PageDown: dy = 5 * stepSize; break;
00547 }
00548 int adj = dx + dy;
00549
00550 if (adj != 0)
00551 resizePanel(dx, dy, state);
00552 else {
00553 if (key == Qt::Key_Enter) {
00554 Q3DockWindow* dockWindow = qobject_cast<Q3DockWindow *>(d->panel);
00555 if (dockWindow && dockWindow->area())
00556 dockWindow->undock();
00557 else if (dockWindow)
00558 dockWindow->dock();
00559 }
00560 }
00561 showIcon();
00562 }
00563
00564 void KKbdAccessExtensions::displayAccessKeys()
00565 {
00566
00567 QString availableAccessKeys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890";
00568 QList<KXMLGUIClient*> allClients = d->mainWindow->factory()->clients();
00569 foreach ( KXMLGUIClient* client, allClients )
00570 {
00571 QList<KAction*> actions = client->actionCollection()->actions();
00572 for (int j = 0; j < (int)actions.count(); j++) {
00573 KAction* action = actions[j];
00574 KShortcut sc = action->shortcut();
00575 for (int i = 0; i < (int)sc.count(); i++) {
00576 QKeySequence seq = sc.seq(i);
00577 if (seq.count() == 1) {
00578 QString s = seq.toString();
00579 if (availableAccessKeys.contains(s))
00580 availableAccessKeys.remove(s);
00581 }
00582 }
00583 }
00584 }
00585
00586
00587 QWidgetList allWidgets = kapp->allWidgets();
00588 int accessCount = 0;
00589 int maxAccessCount = availableAccessKeys.length();
00590 int overlap = 20;
00591 QPoint prevGlobalPos = QPoint(-overlap, -overlap);
00592 foreach ( QWidget* widget, allWidgets ) {
00593 if (accessCount >= maxAccessCount)
00594 break;
00595 if (widget->isVisible() && widget->focusPolicy() != Qt::NoFocus ) {
00596 QRect r = widget->rect();
00597 QPoint p(r.x(), r.y());
00598
00599 QPoint globalPos = widget->mapToGlobal(p);
00600 QPoint diffPos = globalPos - prevGlobalPos;
00601 if (diffPos.manhattanLength() > overlap) {
00602 accessCount++;
00603 QLabel* lab = new QLabel( widget, Qt::WDestructiveClose );
00604 lab->setBuddy( widget );
00605 lab->setPalette(QToolTip::palette());
00606 lab->setLineWidth(2);
00607 lab->setFrameStyle(QFrame::Box | QFrame::Plain);
00608 lab->setMargin(3);
00609 lab->adjustSize();
00610 lab->move(p);
00611 if (!d->accessKeyLabels) {
00612 d->accessKeyLabels = new Q3PtrList<QLabel>;
00613 d->accessKeyLabels->setAutoDelete(true);
00614 }
00615 d->accessKeyLabels->append(lab);
00616 prevGlobalPos = globalPos;
00617 }
00618 }
00619 }
00620 if (accessCount > 0) {
00621
00622 Q3ValueList<KSortedLabel> sortedLabels;
00623 for (int i = 0; i < accessCount; i++)
00624 sortedLabels.append(KSortedLabel(d->accessKeyLabels->at(i)));
00625 qHeapSort( sortedLabels );
00626
00627 for (int i = 0; i < accessCount; i++) {
00628 QLabel* lab = sortedLabels[i].label();
00629 QChar s = availableAccessKeys[i];
00630 lab->setText(s);
00631 lab->adjustSize();
00632 lab->show();
00633 }
00634 }
00635 }
00636
00637
00638 bool KKbdAccessExtensions::handleAccessKey( const QKeyEvent* ev )
00639 {
00640
00641
00642 if (!d->accessKeyLabels) return false;
00643 QChar c;
00644 if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z )
00645 c = 'A' + ev->key() - Qt::Key_A;
00646 else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 )
00647 c = '0' + ev->key() - Qt::Key_0;
00648 else {
00649
00650
00651 if( ev->text().length() == 1 )
00652 c = ev->text()[ 0 ];
00653 }
00654 if( c.isNull())
00655 return false;
00656
00657 QLabel* lab = d->accessKeyLabels->first();
00658 while (lab) {
00659 if (lab->text() == c) {
00660 lab->buddy()->setFocus();
00661 delete d->accessKeyLabels;
00662 d->accessKeyLabels = 0;
00663 return true;
00664 }
00665 lab = d->accessKeyLabels->next();
00666 }
00667 return false;
00668 }
00669
00670 KSortedLabel::KSortedLabel(QLabel* l) :
00671 m_l(l) { }
00672
00673 KSortedLabel::KSortedLabel() :
00674 m_l(0) { }
00675
00676 bool KSortedLabel::operator<( const KSortedLabel& l ) const
00677 {
00678 QPoint p1 = m_l->mapToGlobal(m_l->pos());
00679 QPoint p2 = l.label()->mapToGlobal(l.label()->pos());
00680 return (p1.y() < p2.y() || (p1.y() == p2.y() && p1.x() < p2.x()));
00681 }