00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00040 #include "kkbdaccessextensions.h"
00041
00042
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
00082 if (isActive) kapp->restoreOverrideCursor();
00083 QCursor::setShape(shayp);
00084 if (isActive) kapp->setOverrideCursor(*this);
00085 }
00086 }
00087
00088
00089 QSize delta(const QPoint p)
00090 {
00091 QPoint d = p - currentPos;
00092 return QSize(d.x(), d.y());
00093 }
00094
00095
00096
00097 QSize delta() { return delta(pos()); }
00098
00099
00100 bool isActive;
00101
00102 private:
00103
00104 QPoint currentPos;
00105
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
00126 if (accessKeyLabels) {
00127 accessKeyLabels->setAutoDelete(false);
00128 delete accessKeyLabels;
00129 }
00130 }
00131
00132
00133 KAction* fwdAction;
00134 KAction* revAction;
00135
00136
00137 KAction* accessKeysAction;
00138
00139
00140 QWidget* panel;
00141
00142
00143
00144
00145 uint handleNdx;
00146
00147
00148 KPanelKbdSizerIcon* icon;
00149
00150
00151 int stepSize;
00152
00153
00154 QPtrList<QLabel>* accessKeyLabels;
00155
00156
00157 KMainWindow* mainWindow;
00158 };
00159
00160 KKbdAccessExtensions::KKbdAccessExtensions(KMainWindow* parent, const char* name) :
00161 QObject(parent, name)
00162 {
00163
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
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
00195
00196
00197
00198
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
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
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
00252
00253
00254
00255
00256
00257 else if (e->type() == QEvent::MouseMove && d->icon->isActive && d->panel) {
00258
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
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
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
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
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
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
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
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
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
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
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
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
00409 p = splitter->mapToGlobal(p);
00410
00411 } else {
00412 QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
00413 p = dockWindow->pos();
00414 if (dockWindow->area()) {
00415
00416 p = dockWindow->parentWidget()->mapTo(dockWindow->topLevelWidget(), p);
00417
00418
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
00424 p.setX(p.x() + dockWindow->width());
00425
00426 } else
00427
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
00435 p.setY(p.y() + dockWindow->height());
00436 else {
00437 if (dockWindow->area()->handlePosition() == QDockArea::Normal)
00438
00439 p.setY(p.y() + dockWindow->height());
00440
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);
00448 }
00449 }
00450
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
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
00469 sizes[handleNdx] = sizes[handleNdx] + adj;
00470
00471 splitter->setSizes(sizes);
00472 QApplication::postEvent(splitter, new QEvent(QEvent::LayoutHint));
00473 } else {
00474
00475 QDockWindow* dockWindow = dynamic_cast<QDockWindow *>(d->panel);
00476 if (dockWindow->area()) {
00477
00478 QSize fe = dockWindow->fixedExtent();
00479 if (d->handleNdx == 1) {
00480
00481
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
00490
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
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
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
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
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
00573
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
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
00608 QValueList<KSortedLabel> sortedLabels;
00609 for (int i = 0; i < accessCount; i++)
00610 sortedLabels.append(KSortedLabel(d->accessKeyLabels->at(i)));
00611 qHeapSort( sortedLabels );
00612
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
00624 bool KKbdAccessExtensions::handleAccessKey( const QKeyEvent* ev )
00625 {
00626
00627
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
00636
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 }