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