00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kmenu.h"
00022 #include "khbox.h"
00023
00024 #include <QtCore/QObject>
00025 #include <QtCore/QPointer>
00026 #include <QtCore/QTimer>
00027 #include <QtGui/QApplication>
00028 #include <QtGui/QCursor>
00029 #include <QtGui/QFontMetrics>
00030 #include <QtGui/QHBoxLayout>
00031 #include <QtGui/QKeyEvent>
00032 #include <QtGui/QMenuItem>
00033 #include <QtGui/QLabel>
00034 #include <QtGui/QPainter>
00035 #include <QtGui/QStyle>
00036 #include <QtGui/QToolButton>
00037 #include <QtGui/QWidgetAction>
00038
00039 #include <kdebug.h>
00040
00041 class KMenu::KMenuPrivate
00042 {
00043 public:
00044 KMenuPrivate (KMenu *_parent);
00045 ~KMenuPrivate ();
00046
00047 void resetKeyboardVars(bool noMatches = false);
00048 void actionHovered(QAction* action);
00049 void showCtxMenu(const QPoint &pos);
00050
00051 KMenu *parent;
00052
00053
00054 QTimer clearTimer;
00055
00056 bool noMatches : 1;
00057 bool shortcuts : 1;
00058 bool autoExec : 1;
00059
00060 QString keySeq;
00061 QString originalText;
00062
00063 QAction* lastHitAction;
00064 Qt::MouseButtons mouseButtons;
00065 Qt::KeyboardModifiers keyboardModifiers;
00066
00067
00068 QMenu* ctxMenu;
00069 QPointer<QAction> highlightedAction;
00070
00071 class EventSniffer;
00072 EventSniffer *eventSniffer;
00073 };
00074
00085 class KMenu::KMenuPrivate::EventSniffer
00086 : public QObject
00087 {
00088 public:
00089 EventSniffer(QObject *parent = 0)
00090 : QObject(parent) { }
00091
00092 ~EventSniffer() { }
00093
00094 bool eventFilter(QObject *object, QEvent *event)
00095 {
00096 Q_UNUSED(object);
00097
00098 if (event->type() == QEvent::Paint ||
00099 event->type() == QEvent::KeyPress ||
00100 event->type() == QEvent::KeyRelease) {
00101 return false;
00102 }
00103
00104 event->accept();
00105 return true;
00106 }
00107 };
00108
00109 KMenu::KMenuPrivate::KMenuPrivate (KMenu *_parent)
00110 : parent(_parent)
00111 , noMatches(false)
00112 , shortcuts(false)
00113 , autoExec(false)
00114 , lastHitAction(0L)
00115 , mouseButtons(Qt::NoButton)
00116 , keyboardModifiers(Qt::NoModifier)
00117 , ctxMenu(0)
00118 , highlightedAction(0)
00119 , eventSniffer(new EventSniffer)
00120 {
00121 resetKeyboardVars();
00122 }
00123
00124 KMenu::KMenuPrivate::~KMenuPrivate ()
00125 {
00126 delete ctxMenu;
00127 delete eventSniffer;
00128 }
00129
00130
00135 class KMenuContext {
00136 public:
00137 KMenuContext();
00138 KMenuContext(const KMenuContext& o);
00139 KMenuContext(QPointer<KMenu> menu,QPointer<QAction> action);
00140
00141 inline QPointer<KMenu> menu() const { return m_menu; }
00142 inline QPointer<QAction> action() const { return m_action; }
00143
00144 private:
00145 QPointer<KMenu> m_menu;
00146 QPointer<QAction> m_action;
00147 };
00148
00149
00150 Q_DECLARE_METATYPE(KMenuContext)
00151
00152
00153
00154 KMenu::KMenu(QWidget *parent)
00155 : QMenu(parent)
00156 , d(new KMenuPrivate(this))
00157 {
00158 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00159 }
00160
00161 KMenu::KMenu( const QString & title, QWidget * parent )
00162 : QMenu(title, parent)
00163 , d(new KMenuPrivate(this))
00164 {
00165 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00166 }
00167
00168 KMenu::~KMenu()
00169 {
00170 delete d;
00171 }
00172
00173 QAction* KMenu::addTitle(const QString &text, QAction* before)
00174 {
00175 return addTitle(QIcon(), text, before);
00176 }
00177
00178 QAction* KMenu::addTitle(const QIcon &icon, const QString &text, QAction* before)
00179 {
00180 QAction *buttonAction = new QAction(this);
00181 QFont font = buttonAction->font();
00182 font.setBold(true);
00183 buttonAction->setFont(font);
00184 buttonAction->setText(text);
00185 buttonAction->setIcon(icon);
00186
00187 QWidgetAction *action = new QWidgetAction(this);
00188 QToolButton *titleButton = new QToolButton(this);
00189 titleButton->installEventFilter(d->eventSniffer);
00190 titleButton->setDefaultAction(buttonAction);
00191 titleButton->setDown(true);
00192 titleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
00193 action->setDefaultWidget(titleButton);
00194
00195 insertAction(before, action);
00196 return action;
00197 }
00198
00202 void KMenu::closeEvent(QCloseEvent*e)
00203 {
00204 if (d->shortcuts)
00205 d->resetKeyboardVars();
00206 QMenu::closeEvent(e);
00207 }
00208
00209 Qt::MouseButtons KMenu::mouseButtons() const
00210 {
00211 return d->mouseButtons;
00212 }
00213
00214 Qt::KeyboardModifiers KMenu::keyboardModifiers() const
00215 {
00216 return d->keyboardModifiers;
00217 }
00218
00219 void KMenu::keyPressEvent(QKeyEvent* e)
00220 {
00221 d->mouseButtons = Qt::NoButton;
00222 d->keyboardModifiers = Qt::NoModifier;
00223
00224 if (!d->shortcuts) {
00225 d->keyboardModifiers = e->modifiers();
00226 QMenu::keyPressEvent(e);
00227 return;
00228 }
00229
00230 QAction* a = 0L;
00231 bool firstpass = true;
00232 QString keyString = e->text();
00233
00234
00235 int key = e->key();
00236 if (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter
00237 || key == Qt::Key_Up || key == Qt::Key_Down || key == Qt::Key_Left
00238 || key == Qt::Key_Right || key == Qt::Key_F1 || key == Qt::Key_PageUp
00239 || key == Qt::Key_PageDown || key == Qt::Key_Back || key == Qt::Key_Select) {
00240
00241 d->resetKeyboardVars();
00242
00243
00244 d->keyboardModifiers = e->modifiers();
00245 QMenu::keyPressEvent(e);
00246 return;
00247 } else if ( key == Qt::Key_Shift || key == Qt::Key_Control || key == Qt::Key_Alt || key == Qt::Key_Meta )
00248 return QMenu::keyPressEvent(e);
00249
00250
00251
00252 if (!d->keySeq.isNull()) {
00253 if (key == Qt::Key_Backspace) {
00254
00255 if (d->keySeq.length() == 1) {
00256 d->resetKeyboardVars();
00257 return;
00258 }
00259
00260
00261 keyString = d->keySeq.left(d->keySeq.length() - 1);
00262
00263
00264 d->resetKeyboardVars();
00265
00266 } else if (key == Qt::Key_Delete) {
00267 d->resetKeyboardVars();
00268
00269
00270 setActiveAction(0L);
00271 return;
00272
00273 } else if (d->noMatches) {
00274
00275 d->resetKeyboardVars();
00276
00277
00278 setActiveAction(0L);
00279
00280 } else {
00281
00282
00283 a = d->lastHitAction;
00284 }
00285
00286 } else if (key == Qt::Key_Backspace && menuAction()) {
00287
00288 hide();
00289 d->resetKeyboardVars();
00290 return;
00291 }
00292
00293 d->keySeq += keyString;
00294 int seqLen = d->keySeq.length();
00295
00296 foreach (a, actions()) {
00297
00298 if (!a->isEnabled())
00299 continue;
00300
00301 QString thisText;
00302
00303
00304
00305 if (a == d->lastHitAction)
00306 thisText = d->originalText;
00307 else
00308 thisText = a->text();
00309
00310
00311 thisText = thisText.remove('&');
00312
00313
00314 thisText = thisText.left(seqLen);
00315
00316
00317 if (!thisText.indexOf(d->keySeq, 0, Qt::CaseInsensitive)) {
00318
00319 if (firstpass) {
00320
00321 setActiveAction(a);
00322
00323
00324 if (d->lastHitAction && d->lastHitAction != a)
00325
00326 d->lastHitAction->setText(d->originalText);
00327
00328
00329 if (d->lastHitAction != a || d->lastHitAction == 0L)
00330 d->originalText = a->text();
00331
00332
00333 a->setText(underlineText(d->originalText, d->keySeq.length()));
00334
00335
00336 d->lastHitAction = a;
00337
00338
00339 d->clearTimer.setSingleShot(true);
00340 d->clearTimer.start(5000);
00341
00342
00343 firstpass = false;
00344 } else {
00345
00346 return;
00347 }
00348 }
00349
00350
00351 }
00352
00353 if (!firstpass) {
00354 if (d->autoExec) {
00355
00356 d->lastHitAction->activate(QAction::Trigger);
00357 d->resetKeyboardVars();
00358
00359 } else if (d->lastHitAction && d->lastHitAction->menu()) {
00360
00361 d->lastHitAction->activate(QAction::Trigger);
00362 d->resetKeyboardVars();
00363 }
00364
00365 return;
00366 }
00367
00368
00369 d->resetKeyboardVars(true);
00370
00371 QMenu::keyPressEvent(e);
00372 }
00373
00374 bool KMenu::focusNextPrevChild( bool next )
00375 {
00376 d->resetKeyboardVars();
00377 return QMenu::focusNextPrevChild( next );
00378 }
00379
00380 QString KMenu::underlineText(const QString& text, uint length)
00381 {
00382 QString ret = text;
00383 for (uint i = 0; i < length; i++) {
00384 if (ret[2*i] != '&')
00385 ret.insert(2*i, '&');
00386 }
00387 return ret;
00388 }
00389
00390 void KMenu::KMenuPrivate::resetKeyboardVars(bool _noMatches)
00391 {
00392
00393 if (lastHitAction) {
00394 lastHitAction->setText(originalText);
00395 lastHitAction = 0L;
00396 }
00397
00398 if (!noMatches) {
00399 keySeq.clear();
00400 }
00401
00402 noMatches = _noMatches;
00403 }
00404
00405 void KMenu::setKeyboardShortcutsEnabled(bool enable)
00406 {
00407 d->shortcuts = enable;
00408 }
00409
00410 void KMenu::setKeyboardShortcutsExecute(bool enable)
00411 {
00412 d->autoExec = enable;
00413 }
00422 void KMenu::mousePressEvent(QMouseEvent* e)
00423 {
00424 if (d->ctxMenu && d->ctxMenu->isVisible())
00425 {
00426
00427 d->ctxMenu->hide();
00428 }
00429
00430 if( e->button() == Qt::MidButton)
00431 return;
00432
00433 QMenu::mousePressEvent(e);
00434 }
00435
00436 void KMenu::mouseReleaseEvent(QMouseEvent* e)
00437 {
00438
00439 d->keyboardModifiers = e->modifiers();
00440 d->mouseButtons = e->buttons();
00441
00442 if ( e->button() == Qt::MidButton) {
00443 if(activeAction() ) {
00444 QMetaObject::invokeMethod(activeAction(), "triggered", Qt::DirectConnection,
00445 Q_ARG(Qt::MouseButtons, e->button()),
00446 Q_ARG(Qt::KeyboardModifiers, QApplication::keyboardModifiers() ));
00447 }
00448 return;
00449 }
00450
00451 if ( !d->ctxMenu || !d->ctxMenu->isVisible() )
00452 QMenu::mouseReleaseEvent(e);
00453 }
00454
00455 QMenu* KMenu::contextMenu()
00456 {
00457 if (!d->ctxMenu)
00458 {
00459 d->ctxMenu = new QMenu(this);
00460 connect(this, SIGNAL(hovered(QAction*)), SLOT(actionHovered(QAction*)));
00461 }
00462
00463 return d->ctxMenu;
00464 }
00465
00466 const QMenu* KMenu::contextMenu() const
00467 {
00468 return const_cast< KMenu* >( this )->contextMenu();
00469 }
00470
00471 void KMenu::hideContextMenu()
00472 {
00473 if (!d->ctxMenu || !d->ctxMenu->isVisible())
00474 {
00475 return;
00476 }
00477
00478 d->ctxMenu->hide();
00479 }
00480
00481 void KMenu::KMenuPrivate::actionHovered(QAction* )
00482 {
00483 parent->hideContextMenu();
00484 }
00485
00486 static void KMenuSetActionData(QMenu *menu,KMenu* contextedMenu, QAction* contextedAction) {
00487 const QList<QAction*> actions=menu->actions();
00488 QVariant v;
00489 v.setValue(KMenuContext(contextedMenu,contextedAction));
00490 for(int i=0;i<actions.count();i++) {
00491 actions[i]->setData(v);
00492 }
00493 }
00494
00495 void KMenu::KMenuPrivate::showCtxMenu(const QPoint &pos)
00496 {
00497 highlightedAction = parent->activeAction();
00498
00499 if (!highlightedAction)
00500 {
00501 KMenuSetActionData(parent,0,0);
00502 return;
00503 }
00504
00505 emit parent->aboutToShowContextMenu(parent, highlightedAction, ctxMenu);
00506 KMenuSetActionData(parent,parent,highlightedAction);
00507
00508
00509 if (QMenu* subMenu = highlightedAction->menu())
00510 {
00511 QTimer::singleShot(100, subMenu, SLOT(hide()));
00512 }
00513
00514
00515 ctxMenu->popup(parent->mapToGlobal(pos));
00516 }
00517
00518 KMenu * KMenu::contextMenuFocus( )
00519 {
00520 return qobject_cast<KMenu*>(QApplication::activePopupWidget());
00521 }
00522
00523 QAction * KMenu::contextMenuFocusAction( )
00524 {
00525 if (KMenu* menu = qobject_cast<KMenu*>(QApplication::activePopupWidget())) {
00526
00527 QVariant var = menu->activeAction()->data();
00528 KMenuContext ctx = var.value<KMenuContext>();
00529 Q_ASSERT(ctx.menu() == menu);
00530 return ctx.action();
00531 }
00532
00533 return 0L;
00534 }
00535
00536 void KMenu::contextMenuEvent(QContextMenuEvent* e)
00537 {
00538 if (d->ctxMenu)
00539 {
00540 if (e->reason() == QContextMenuEvent::Mouse)
00541 {
00542 d->showCtxMenu(e->pos());
00543 }
00544 else if (activeAction())
00545 {
00546 d->showCtxMenu(actionGeometry(activeAction()).center());
00547 }
00548
00549 e->accept();
00550 return;
00551 }
00552
00553 QMenu::contextMenuEvent(e);
00554 }
00555
00556 void KMenu::hideEvent(QHideEvent *e)
00557 {
00558 if (d->ctxMenu && d->ctxMenu->isVisible())
00559 {
00560
00561
00562
00563
00564
00565
00566
00567
00568 bool blocked = blockSignals(true);
00569 d->ctxMenu->hide();
00570 blockSignals(blocked);
00571 }
00572 QMenu::hideEvent(e);
00573 }
00582 KMenuContext::KMenuContext( )
00583 : m_menu(0L)
00584 , m_action(0L)
00585 {
00586 }
00587
00588 KMenuContext::KMenuContext( const KMenuContext & o )
00589 : m_menu(o.m_menu)
00590 , m_action(o.m_action)
00591 {
00592 }
00593
00594 KMenuContext::KMenuContext(QPointer<KMenu> menu,QPointer<QAction> action)
00595 : m_menu(menu)
00596 , m_action(action)
00597 {
00598 }
00599
00600 #include "kmenu.moc"