00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "k3popupmenu.h"
00021
00022 #include <QtGui/QCursor>
00023 #include <QtGui/QPainter>
00024 #include <QtCore/QTimer>
00025 #include <QtGui/QFontMetrics>
00026 #include <QKeyEvent>
00027 #include <QPointer>
00028 #include <QMenuItem>
00029
00030 #include <kdebug.h>
00031 #include <kglobal.h>
00032 #include <klocale.h>
00033
00034 class K3PopupMenu::K3PopupMenuPrivate
00035 {
00036 public:
00037 K3PopupMenuPrivate ()
00038 : noMatches(false)
00039 , shortcuts(false)
00040 , autoExec(false)
00041 , lastHitAction(0L)
00042 #ifdef QT3_SUPPORT
00043 , state(Qt::NoButton)
00044 #endif
00045 , mouseButtons(Qt::NoButton)
00046 , keyboardModifiers(Qt::NoModifier)
00047 , m_ctxMenu(0)
00048 {}
00049
00050 ~K3PopupMenuPrivate ()
00051 {
00052 delete m_ctxMenu;
00053 }
00054
00055 QString m_lastTitle;
00056
00057
00058 QTimer clearTimer;
00059
00060 bool noMatches : 1;
00061 bool shortcuts : 1;
00062 bool autoExec : 1;
00063
00064 QString keySeq;
00065 QString originalText;
00066
00067 QAction* lastHitAction;
00068 #ifdef QT3_SUPPORT
00069 Qt::ButtonState state;
00070 Qt::MouseButtons mouseButtons;
00071 Qt::KeyboardModifiers keyboardModifiers;
00072 #endif
00073
00074
00075 Q3PopupMenu* m_ctxMenu;
00076 static bool s_continueCtxMenuShow;
00077 static QPointer<QAction> s_highlightedAction;
00078
00079 static int s_highlightedItem;
00080 static K3PopupMenu* s_contextedMenu;
00081 };
00082
00083 QPointer<QAction> K3PopupMenu::K3PopupMenuPrivate::s_highlightedAction(0L);
00084 int K3PopupMenu::K3PopupMenuPrivate::s_highlightedItem(-1);
00085 K3PopupMenu* K3PopupMenu::K3PopupMenuPrivate::s_contextedMenu(0);
00086 bool K3PopupMenu::K3PopupMenuPrivate::s_continueCtxMenuShow(true);
00087
00088 K3PopupMenu::K3PopupMenu(QWidget *parent)
00089 : Q3PopupMenu(parent)
00090 , d(new K3PopupMenuPrivate())
00091 {
00092 resetKeyboardVars();
00093 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00094 }
00095
00096 K3PopupMenu::~K3PopupMenu()
00097 {
00098 if (K3PopupMenuPrivate::s_contextedMenu == this)
00099 {
00100 K3PopupMenuPrivate::s_contextedMenu = 0;
00101 K3PopupMenuPrivate::s_highlightedAction = 0L;
00102 K3PopupMenuPrivate::s_highlightedItem = -1;
00103 }
00104
00105 delete d;
00106 }
00107
00108 QAction* K3PopupMenu::addTitle(const QString &text, QAction* before)
00109 {
00110 QAction* action = new QAction(text, this);
00111 action->setEnabled(false);
00112 QFont f = action->font();
00113 f.setBold(true);
00114 action->setFont(f);
00115 insertAction(before, action);
00116 return action;
00117 }
00118
00119 QAction* K3PopupMenu::addTitle(const QIcon &icon, const QString &text, QAction* before)
00120 {
00121 QAction* action = new QAction(icon, text, this);
00122 action->setEnabled(false);
00123 QFont f = action->font();
00124 f.setBold(true);
00125 action->setFont(f);
00126 insertAction(before, action);
00127 return action;
00128 }
00129
00133 void K3PopupMenu::closeEvent(QCloseEvent*e)
00134 {
00135 if (d->shortcuts)
00136 resetKeyboardVars();
00137 Q3PopupMenu::closeEvent(e);
00138 }
00139
00140 void K3PopupMenu::activateItemAt(int index)
00141 {
00142 #ifdef QT3_SUPPORT
00143 d->state = Qt::NoButton;
00144 #endif
00145 d->mouseButtons = Qt::NoButton;
00146 d->keyboardModifiers = Qt::NoModifier;
00147 Q3PopupMenu::activateItemAt(index);
00148 }
00149
00150 #ifdef QT3_SUPPORT
00151 Qt::ButtonState K3PopupMenu::state() const
00152 {
00153 return d->state;
00154 }
00155 #endif
00156
00157 Qt::MouseButtons K3PopupMenu::mouseButtons() const
00158 {
00159 return d->mouseButtons;
00160 }
00161
00162 Qt::KeyboardModifiers K3PopupMenu::keyboardModifiers() const
00163 {
00164 return d->keyboardModifiers;
00165 }
00166
00167 void K3PopupMenu::keyPressEvent(QKeyEvent* e)
00168 {
00169 #ifdef QT3_SUPPORT
00170 d->state = Qt::NoButton;
00171 #endif
00172 d->mouseButtons = Qt::NoButton;
00173 d->keyboardModifiers = Qt::NoModifier;
00174 if (!d->shortcuts) {
00175
00176
00177 #ifdef QT3_SUPPORT
00178 d->state = e->state();
00179 #endif
00180 d->keyboardModifiers = e->modifiers();
00181 Q3PopupMenu::keyPressEvent(e);
00182 return;
00183 }
00184
00185 QAction* a = 0L;
00186 bool firstpass = true;
00187 QString keyString = e->text();
00188
00189
00190 int key = e->key();
00191 if (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter
00192 || key == Qt::Key_Up || key == Qt::Key_Down || key == Qt::Key_Left
00193 || key == Qt::Key_Right || key == Qt::Key_F1) {
00194
00195 resetKeyboardVars();
00196
00197
00198 #ifdef QT3_SUPPORT
00199 d->state = e->state();
00200 #endif
00201 d->keyboardModifiers = e->modifiers();
00202 Q3PopupMenu::keyPressEvent(e);
00203 return;
00204 } else if ( key == Qt::Key_Shift || key == Qt::Key_Control || key == Qt::Key_Alt || key == Qt::Key_Meta )
00205 return Q3PopupMenu::keyPressEvent(e);
00206
00207
00208
00209 if (!d->keySeq.isNull()) {
00210 if (key == Qt::Key_Backspace) {
00211
00212 if (d->keySeq.length() == 1) {
00213 resetKeyboardVars();
00214 return;
00215 }
00216
00217
00218 keyString = d->keySeq.left(d->keySeq.length() - 1);
00219
00220
00221 resetKeyboardVars();
00222
00223 } else if (key == Qt::Key_Delete) {
00224 resetKeyboardVars();
00225
00226
00227 setActiveAction(0L);
00228 return;
00229
00230 } else if (d->noMatches) {
00231
00232 resetKeyboardVars();
00233
00234
00235 setActiveAction(0L);
00236
00237 } else {
00238
00239
00240 a = d->lastHitAction;
00241 }
00242
00243 } else if (key == Qt::Key_Backspace && menuAction()) {
00244
00245 hide();
00246 resetKeyboardVars();
00247 return;
00248 }
00249
00250 d->keySeq += keyString;
00251 int seqLen = d->keySeq.length();
00252
00253 foreach (a, actions()) {
00254
00255 if (!a->isEnabled())
00256 continue;
00257
00258 QString thisText;
00259
00260
00261
00262 if (a == d->lastHitAction)
00263 thisText = d->originalText;
00264 else
00265 thisText = a->text();
00266
00267
00268 thisText = KGlobal::locale()->removeAcceleratorMarker(thisText);
00269
00270
00271 thisText = thisText.left(seqLen);
00272
00273
00274 if (!thisText.contains(d->keySeq, Qt::CaseInsensitive)) {
00275
00276 if (firstpass) {
00277
00278 setActiveAction(a);
00279
00280
00281 if (d->lastHitAction != a)
00282
00283 d->lastHitAction->setText(d->originalText);
00284
00285
00286 if (d->lastHitAction != a || d->lastHitAction == 0L)
00287 d->originalText = a->text();
00288
00289
00290 a->setText(underlineText(d->originalText, d->keySeq.length()));
00291
00292
00293 d->lastHitAction = a;
00294
00295
00296 d->clearTimer.start(5000, true);
00297
00298
00299 firstpass = false;
00300 } else {
00301
00302 return;
00303 }
00304 }
00305
00306
00307 }
00308
00309 if (!firstpass) {
00310 if (d->autoExec) {
00311
00312 d->lastHitAction->activate(QAction::Trigger);
00313 resetKeyboardVars();
00314
00315 } else if (d->lastHitAction && d->lastHitAction->menu()) {
00316
00317 d->lastHitAction->activate(QAction::Trigger);
00318 resetKeyboardVars();
00319 }
00320
00321 return;
00322 }
00323
00324
00325 resetKeyboardVars(true);
00326
00327 Q3PopupMenu::keyPressEvent(e);
00328 }
00329
00330 bool K3PopupMenu::focusNextPrevChild( bool next )
00331 {
00332 resetKeyboardVars();
00333 return Q3PopupMenu::focusNextPrevChild( next );
00334 }
00335
00336 QString K3PopupMenu::underlineText(const QString& text, uint length)
00337 {
00338 QString ret = text;
00339 for (uint i = 0; i < length; i++) {
00340 if (ret[2*i] != '&')
00341 ret.insert(2*i, "&");
00342 }
00343 return ret;
00344 }
00345
00346 void K3PopupMenu::resetKeyboardVars(bool noMatches )
00347 {
00348
00349 if (d->lastHitAction) {
00350 d->lastHitAction->setText(d->originalText);
00351 d->lastHitAction = 0L;
00352 }
00353
00354 if (!noMatches) {
00355 d->keySeq.clear();
00356 }
00357
00358 d->noMatches = noMatches;
00359 }
00360
00361 void K3PopupMenu::setKeyboardShortcutsEnabled(bool enable)
00362 {
00363 d->shortcuts = enable;
00364 }
00365
00366 void K3PopupMenu::setKeyboardShortcutsExecute(bool enable)
00367 {
00368 d->autoExec = enable;
00369 }
00378 void K3PopupMenu::mousePressEvent(QMouseEvent* e)
00379 {
00380 if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00381 {
00382
00383 d->m_ctxMenu->hide();
00384 }
00385
00386 Q3PopupMenu::mousePressEvent(e);
00387 }
00388
00389 void K3PopupMenu::mouseReleaseEvent(QMouseEvent* e)
00390 {
00391 #ifdef QT3_SUPPORT
00392
00393 d->state = Qt::ButtonState(uint(e->button()) | (e->state() & Qt::KeyboardModifierMask));
00394 #endif
00395
00396 d->keyboardModifiers = e->modifiers();
00397 d->mouseButtons = e->buttons();
00398
00399 if ( !d->m_ctxMenu || !d->m_ctxMenu->isVisible() )
00400 Q3PopupMenu::mouseReleaseEvent(e);
00401 }
00402
00403 Q3PopupMenu* K3PopupMenu::contextMenu()
00404 {
00405 if (!d->m_ctxMenu)
00406 {
00407 d->m_ctxMenu = new Q3PopupMenu(this);
00408 connect(d->m_ctxMenu, SIGNAL(aboutToHide()), this, SLOT(ctxMenuHiding()));
00409 }
00410
00411 return d->m_ctxMenu;
00412 }
00413
00414 const Q3PopupMenu* K3PopupMenu::contextMenu() const
00415 {
00416 return const_cast< K3PopupMenu* >( this )->contextMenu();
00417 }
00418
00419 void K3PopupMenu::hideContextMenu()
00420 {
00421 K3PopupMenuPrivate::s_continueCtxMenuShow = false;
00422 }
00423
00424 QAction* K3PopupMenu::contextMenuFocusAction()
00425 {
00426 return K3PopupMenuPrivate::s_highlightedAction;
00427 }
00428
00429 K3PopupMenu* K3PopupMenu::contextMenuFocus()
00430 {
00431 return K3PopupMenuPrivate::s_contextedMenu;
00432 }
00433
00434 void K3PopupMenu::actionHovered(QAction* action)
00435 {
00436 if (!d->m_ctxMenu || !d->m_ctxMenu->isVisible())
00437 {
00438 return;
00439 }
00440
00441 d->m_ctxMenu->hide();
00442 showCtxMenu(actionGeometry(action).center());
00443 }
00444
00445 void K3PopupMenu::showCtxMenu(const QPoint &pos)
00446 {
00447 if (K3PopupMenuPrivate::s_highlightedAction)
00448 if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00449 disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00450
00451 K3PopupMenuPrivate::s_highlightedAction = activeAction();
00452 K3PopupMenuPrivate::s_highlightedItem = itemAtPos(pos);
00453
00454 if (!K3PopupMenuPrivate::s_highlightedAction)
00455 {
00456 K3PopupMenuPrivate::s_contextedMenu = 0;
00457 return;
00458 }
00459
00460 emit aboutToShowContextMenu(this, K3PopupMenuPrivate::s_highlightedAction, d->m_ctxMenu);
00461 emit aboutToShowContextMenu(this, K3PopupMenuPrivate::s_highlightedItem, d->m_ctxMenu);
00462
00463 if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00464 {
00465 connect(subMenu, SIGNAL(aboutToShow()), SLOT(ctxMenuHideShowingMenu()));
00466 QTimer::singleShot(100, subMenu, SLOT(hide()));
00467 }
00468
00469 if (!K3PopupMenuPrivate::s_continueCtxMenuShow)
00470 {
00471 K3PopupMenuPrivate::s_continueCtxMenuShow = true;
00472 return;
00473 }
00474
00475 K3PopupMenuPrivate::s_contextedMenu = this;
00476 d->m_ctxMenu->exec(this->mapToGlobal(pos));
00477 connect(this, SIGNAL(hovered(QAction*)), SLOT(actionHovered(QAction*)));
00478 }
00479
00480
00481
00482
00483
00484 void K3PopupMenu::ctxMenuHideShowingMenu()
00485 {
00486 if (K3PopupMenuPrivate::s_highlightedAction)
00487 if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00488 QTimer::singleShot(0, subMenu, SLOT(hide()));
00489 }
00490
00491 void K3PopupMenu::ctxMenuHiding()
00492 {
00493 if (K3PopupMenuPrivate::s_highlightedAction)
00494 if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00495 disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00496
00497 connect(this, SIGNAL(hovered(QAction*)), this, SLOT(actionHovered(QAction*)));
00498 K3PopupMenuPrivate::s_continueCtxMenuShow = true;
00499 }
00500
00501 void K3PopupMenu::contextMenuEvent(QContextMenuEvent* e)
00502 {
00503 if (d->m_ctxMenu)
00504 {
00505 if (e->reason() == QContextMenuEvent::Mouse)
00506 {
00507 showCtxMenu(e->pos());
00508 }
00509 else if (activeAction())
00510 {
00511 showCtxMenu(actionGeometry(activeAction()).center());
00512 }
00513
00514 e->accept();
00515 return;
00516 }
00517
00518 Q3PopupMenu::contextMenuEvent(e);
00519 }
00520
00521 void K3PopupMenu::hideEvent(QHideEvent *e)
00522 {
00523 if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00524 {
00525
00526
00527
00528
00529
00530
00531
00532
00533 bool blocked = blockSignals(true);
00534 d->m_ctxMenu->hide();
00535 blockSignals(blocked);
00536 }
00537 Q3PopupMenu::hideEvent(e);
00538 }
00543 void K3PopupMenu::virtual_hook( int, void* )
00544 { }
00545
00546
00547 K3PopupMenu::K3PopupMenu(const QString &title, QWidget *parent)
00548 : Q3PopupMenu(parent)
00549 , d(new K3PopupMenuPrivate())
00550 {
00551 resetKeyboardVars();
00552 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00553 addAction(title);
00554 }
00555
00556 #ifdef QT3_SUPPORT
00557 int K3PopupMenu::insertTitle(const QString &text, int id, int index)
00558 {
00559 int newid = insertItem(text, id, index);
00560 QMenuItem* menuItem = findItem(newid);
00561 Q_ASSERT(menuItem);
00562 menuItem->setEnabled(false);
00563 QFont f = menuItem->font();
00564 f.setBold(true);
00565 menuItem->setFont(f);
00566 return newid;
00567 }
00568
00569 int K3PopupMenu::insertTitle(const QPixmap &icon, const QString &text, int id, int index)
00570 {
00571 int newid = insertItem(text, id, index);
00572 QMenuItem* menuItem = findItem(newid);
00573 Q_ASSERT(menuItem);
00574 menuItem->setEnabled(false);
00575 menuItem->setIcon(icon);
00576 QFont f = menuItem->font();
00577 f.setBold(true);
00578 menuItem->setFont(f);
00579 return newid;
00580 }
00581
00582 void K3PopupMenu::changeTitle(int id, const QString &text)
00583 {
00584 QMenuItem* menuItem = findItem(id);
00585 Q_ASSERT(menuItem);
00586 if (!menuItem)
00587 return;
00588 menuItem->setText(text);
00589 menuItem->setIcon(QIcon());
00590 return;
00591 }
00592
00593 void K3PopupMenu::changeTitle(int id, const QPixmap &icon, const QString &text)
00594 {
00595 QMenuItem* menuItem = findItem(id);
00596 Q_ASSERT(menuItem);
00597 if (!menuItem)
00598 return;
00599 menuItem->setText(text);
00600 menuItem->setIcon(icon);
00601 return;
00602 }
00603
00604 QString K3PopupMenu::title(int id) const
00605 {
00606 QMenuItem* menuItem = findItem(id);
00607 Q_ASSERT(menuItem);
00608 if (!menuItem)
00609 return QString();
00610 return menuItem->text();
00611 }
00612
00613 QPixmap K3PopupMenu::titlePixmap(int id) const
00614 {
00615 QMenuItem* menuItem = findItem(id);
00616 Q_ASSERT(menuItem);
00617 if (!menuItem)
00618 return QPixmap();
00619 return menuItem->icon().pixmap();
00620 }
00621
00622 void K3PopupMenu::setTitle(const QString &title)
00623 {
00624 addAction(title);
00625 }
00626
00627 int K3PopupMenu::contextMenuFocusItem()
00628 {
00629 return K3PopupMenuPrivate::s_highlightedItem;
00630 }
00631
00632 #endif // END compat methods
00633
00634 #include "k3popupmenu.moc"