• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • widgets
kmenu.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org>
3 Copyright (C) 2002,2006 Hamish Rodda <rodda@kde.org>
4 Copyright (C) 2006 Olivier Goffart <ogoffart@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "kmenu.h"
22#include "khbox.h"
23
24#include <QtCore/QMetaMethod>
25#include <QtCore/QObject>
26#include <QtCore/QPointer>
27#include <QtCore/QTimer>
28#include <QtGui/QApplication>
29#include <QtGui/QCursor>
30#include <QtGui/QFontMetrics>
31#include <QtGui/QHBoxLayout>
32#include <QtGui/QKeyEvent>
33#include <QtGui/QMenuItem>
34#include <QtGui/QLabel>
35#include <QtGui/QPainter>
36#include <QtGui/QStyle>
37#include <QtGui/QToolButton>
38#include <QtGui/QWidgetAction>
39
40#include <kdebug.h>
41#include <kglobal.h>
42#include <klocale.h>
43#include <kacceleratormanager.h>
44
45static const char KMENU_TITLE[] = "kmenu_title";
46
47class KMenu::KMenuPrivate
48 : public QObject
49{
50public:
51 KMenuPrivate (KMenu *_parent);
52 ~KMenuPrivate ();
53
54 void resetKeyboardVars(bool noMatches = false);
55 void actionHovered(QAction* action);
56 void showCtxMenu(const QPoint &pos);
57 void skipTitles(QKeyEvent *event);
58
69 bool eventFilter(QObject *object, QEvent *event)
70 {
71 Q_UNUSED(object);
72
73 if (event->type() == QEvent::Paint ||
74 event->type() == QEvent::KeyPress ||
75 event->type() == QEvent::KeyRelease) {
76 return false;
77 }
78
79 event->accept();
80 return true;
81 }
82
83 KMenu *parent;
84
85 // variables for keyboard navigation
86 QTimer clearTimer;
87
88 bool noMatches : 1;
89 bool shortcuts : 1;
90 bool autoExec : 1;
91
92 QString keySeq;
93 QString originalText;
94
95 QAction* lastHitAction;
96 QAction* lastHoveredAction;
97 Qt::MouseButtons mouseButtons;
98 Qt::KeyboardModifiers keyboardModifiers;
99
100 // support for RMB menus on menus
101 QMenu* ctxMenu;
102 QPointer<QAction> highlightedAction;
103
104};
105
106KMenu::KMenuPrivate::KMenuPrivate (KMenu *_parent)
107 : parent(_parent)
108 , noMatches(false)
109 , shortcuts(false)
110 , autoExec(false)
111 , lastHitAction(0L)
112 , lastHoveredAction(0L)
113 , mouseButtons(Qt::NoButton)
114 , keyboardModifiers(Qt::NoModifier)
115 , ctxMenu(0)
116 , highlightedAction(0)
117{
118 resetKeyboardVars();
119 KAcceleratorManager::manage(parent);
120}
121
122KMenu::KMenuPrivate::~KMenuPrivate ()
123{
124 delete ctxMenu;
125}
126
127
132class KMenuContext {
133public:
134 KMenuContext();
135 KMenuContext(const KMenuContext& o);
136 KMenuContext(QPointer<KMenu> menu,QPointer<QAction> action);
137
138 inline QPointer<KMenu> menu() const { return m_menu; }
139 inline QPointer<QAction> action() const { return m_action; }
140
141private:
142 QPointer<KMenu> m_menu;
143 QPointer<QAction> m_action;
144};
145
146
147Q_DECLARE_METATYPE(KMenuContext)
148
149
150
151KMenu::KMenu(QWidget *parent)
152 : QMenu(parent)
153 , d(new KMenuPrivate(this))
154{
155 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
156}
157
158KMenu::KMenu( const QString & title, QWidget * parent )
159 : QMenu(title, parent)
160 , d(new KMenuPrivate(this))
161{
162 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
163}
164
165KMenu::~KMenu()
166{
167 delete d;
168}
169
170QAction* KMenu::addTitle(const QString &text, QAction* before)
171{
172 return addTitle(QIcon(), text, before);
173}
174
175QAction* KMenu::addTitle(const QIcon &icon, const QString &text, QAction* before)
176{
177 QAction *buttonAction = new QAction(this);
178 QFont font = buttonAction->font();
179 font.setBold(true);
180 buttonAction->setFont(font);
181 buttonAction->setText(text);
182 buttonAction->setIcon(icon);
183
184 QWidgetAction *action = new QWidgetAction(this);
185 action->setObjectName(KMENU_TITLE);
186 QToolButton *titleButton = new QToolButton(this);
187 titleButton->installEventFilter(d); // prevent clicks on the title of the menu
188 titleButton->setDefaultAction(buttonAction);
189 titleButton->setDown(true); // prevent hover style changes in some styles
190 titleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
191 action->setDefaultWidget(titleButton);
192
193 insertAction(before, action);
194 return action;
195}
196
200void KMenu::closeEvent(QCloseEvent*e)
201{
202 if (d->shortcuts)
203 d->resetKeyboardVars();
204 QMenu::closeEvent(e);
205}
206
207Qt::MouseButtons KMenu::mouseButtons() const
208{
209 return d->mouseButtons;
210}
211
212Qt::KeyboardModifiers KMenu::keyboardModifiers() const
213{
214 return d->keyboardModifiers;
215}
216
217void KMenu::keyPressEvent(QKeyEvent* e)
218{
219 d->mouseButtons = Qt::NoButton;
220 d->keyboardModifiers = Qt::NoModifier;
221
222 if (!d->shortcuts) {
223 d->keyboardModifiers = e->modifiers();
224 QMenu::keyPressEvent(e);
225
226 if (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down) {
227 d->skipTitles(e);
228 }
229
230 return;
231 }
232
233 QAction* a = 0L;
234 bool firstpass = true;
235 QString keyString = e->text();
236
237 // check for common commands dealt with by QMenu
238 int key = e->key();
239 if (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter
240 || key == Qt::Key_Up || key == Qt::Key_Down || key == Qt::Key_Left
241 || key == Qt::Key_Right || key == Qt::Key_F1 || key == Qt::Key_PageUp
242 || key == Qt::Key_PageDown || key == Qt::Key_Back || key == Qt::Key_Select) {
243
244 d->resetKeyboardVars();
245 // continue event processing by QMenu
246 //e->ignore();
247 d->keyboardModifiers = e->modifiers();
248 QMenu::keyPressEvent(e);
249
250 if (key == Qt::Key_Up || key == Qt::Key_Down) {
251 d->skipTitles(e);
252 }
253 return;
254 } else if ( key == Qt::Key_Shift || key == Qt::Key_Control || key == Qt::Key_Alt || key == Qt::Key_Meta )
255 return QMenu::keyPressEvent(e);
256
257 // check to see if the user wants to remove a key from the sequence (backspace)
258 // or clear the sequence (delete)
259 if (!d->keySeq.isNull()) {
260 if (key == Qt::Key_Backspace) {
261
262 if (d->keySeq.length() == 1) {
263 d->resetKeyboardVars();
264 return;
265 }
266
267 // keep the last sequence in keyString
268 keyString = d->keySeq.left(d->keySeq.length() - 1);
269
270 // allow sequence matching to be tried again
271 d->resetKeyboardVars();
272
273 } else if (key == Qt::Key_Delete) {
274 d->resetKeyboardVars();
275
276 // clear active item
277 setActiveAction(0L);
278 return;
279
280 } else if (d->noMatches) {
281 // clear if there are no matches
282 d->resetKeyboardVars();
283
284 // clear active item
285 setActiveAction(0L);
286
287 } else {
288 // the key sequence is not a null string
289 // therefore the lastHitAction is valid
290 a = d->lastHitAction;
291 }
292
293 } else if (key == Qt::Key_Backspace && menuAction()) {
294 // backspace with no chars in the buffer... go back a menu.
295 hide();
296 d->resetKeyboardVars();
297 return;
298 }
299
300 d->keySeq += keyString;
301 const int seqLen = d->keySeq.length();
302
303 foreach (a, actions()) {
304 // don't search disabled entries
305 if (!a->isEnabled())
306 continue;
307
308 QString thisText;
309
310 // retrieve the right text
311 // (the last selected item one may have additional ampersands)
312 if (a == d->lastHitAction)
313 thisText = d->originalText;
314 else
315 thisText = a->text();
316
317 // if there is an accelerator present, remove it
318 thisText = KGlobal::locale()->removeAcceleratorMarker(thisText);
319
320 // chop text to the search length
321 thisText = thisText.left(seqLen);
322
323 // do the search
324 if (!thisText.indexOf(d->keySeq, 0, Qt::CaseInsensitive)) {
325
326 if (firstpass) {
327 // match
328 setActiveAction(a);
329
330 // check to see if we're underlining a different item
331 if (d->lastHitAction && d->lastHitAction != a)
332 // yes; revert the underlining
333 d->lastHitAction->setText(d->originalText);
334
335 // set the original text if it's a different item
336 if (d->lastHitAction != a || d->lastHitAction == 0L)
337 d->originalText = a->text();
338
339 // underline the currently selected item
340 a->setText(underlineText(d->originalText, d->keySeq.length()));
341
342 // remember what's going on
343 d->lastHitAction = a;
344
345 // start/restart the clear timer
346 d->clearTimer.setSingleShot(true);
347 d->clearTimer.start(5000);
348
349 // go around for another try, to see if we can execute
350 firstpass = false;
351 } else {
352 // don't allow execution
353 return;
354 }
355 }
356
357 // fall through to allow execution
358 }
359
360 if (!firstpass) {
361 if (d->autoExec) {
362 // activate anything
363 d->lastHitAction->activate(QAction::Trigger);
364 d->resetKeyboardVars();
365
366 } else if (d->lastHitAction && d->lastHitAction->menu()) {
367 // only activate sub-menus
368 d->lastHitAction->activate(QAction::Trigger);
369 d->resetKeyboardVars();
370 }
371
372 return;
373 }
374
375 // no matches whatsoever, clean up
376 d->resetKeyboardVars(true);
377 //e->ignore();
378 QMenu::keyPressEvent(e);
379}
380
381bool KMenu::focusNextPrevChild( bool next )
382{
383 d->resetKeyboardVars();
384 return QMenu::focusNextPrevChild( next );
385}
386
387QString KMenu::underlineText(const QString& text, uint length)
388{
389 QString ret = text;
390 for (uint i = 0; i < length; i++) {
391 if (ret[2*i] != '&')
392 ret.insert(2*i, '&');
393 }
394 return ret;
395}
396
397void KMenu::KMenuPrivate::resetKeyboardVars(bool _noMatches)
398{
399 // Clean up keyboard variables
400 if (lastHitAction) {
401 lastHitAction->setText(originalText);
402 lastHitAction = 0L;
403 }
404
405 if (!noMatches) {
406 keySeq.clear();
407 }
408
409 noMatches = _noMatches;
410}
411
412void KMenu::setKeyboardShortcutsEnabled(bool enable)
413{
414 d->shortcuts = enable;
415}
416
417void KMenu::setKeyboardShortcutsExecute(bool enable)
418{
419 d->autoExec = enable;
420}
429void KMenu::mousePressEvent(QMouseEvent* e)
430{
431 if (d->ctxMenu && d->ctxMenu->isVisible())
432 {
433 // hide on a second context menu event
434 d->ctxMenu->hide();
435 }
436
437 if( e->button() == Qt::MidButton)
438 return;
439
440 QMenu::mousePressEvent(e);
441}
442
443void KMenu::mouseReleaseEvent(QMouseEvent* e)
444{
445 // Save the button, and the modifiers
446 d->keyboardModifiers = e->modifiers();
447 d->mouseButtons = e->buttons();
448
449 if ( e->button() == Qt::MidButton) {
450 if(activeAction() ) {
451 const QMetaObject *metaObject = activeAction()->metaObject();
452 const int index = metaObject->indexOfMethod("triggered(Qt::MouseButtons,Qt::KeyboardModifiers)");
453 if (index != -1) {
454 const QMetaMethod method = metaObject->method(index);
455 method.invoke(activeAction(), Qt::DirectConnection,
456 Q_ARG(Qt::MouseButtons, e->button()),
457 Q_ARG(Qt::KeyboardModifiers, QApplication::keyboardModifiers() ));
458 }
459 }
460 return;
461 }
462
463 if ( !d->ctxMenu || !d->ctxMenu->isVisible() )
464 QMenu::mouseReleaseEvent(e);
465}
466
467QMenu* KMenu::contextMenu()
468{
469 if (!d->ctxMenu)
470 {
471 d->ctxMenu = new QMenu(this);
472 connect(this, SIGNAL(hovered(QAction*)), SLOT(actionHovered(QAction*)));
473 }
474
475 return d->ctxMenu;
476}
477
478const QMenu* KMenu::contextMenu() const
479{
480 return const_cast< KMenu* >( this )->contextMenu();
481}
482
483void KMenu::hideContextMenu()
484{
485 if (!d->ctxMenu || !d->ctxMenu->isVisible())
486 {
487 return;
488 }
489
490 d->ctxMenu->hide();
491}
492
493void KMenu::KMenuPrivate::actionHovered(QAction* action)
494{
495 lastHoveredAction = action;
496 parent->hideContextMenu();
497}
498
499static void KMenuSetActionData(QMenu *menu,KMenu* contextedMenu, QAction* contextedAction) {
500 const QList<QAction*> actions=menu->actions();
501 QVariant v;
502 v.setValue(KMenuContext(contextedMenu,contextedAction));
503 for(int i=0;i<actions.count();i++) {
504 actions[i]->setData(v);
505 }
506}
507
508void KMenu::KMenuPrivate::showCtxMenu(const QPoint &pos)
509{
510 highlightedAction = parent->activeAction();
511
512 if (!highlightedAction)
513 {
514 KMenuSetActionData(parent,0,0);
515 return;
516 }
517
518 emit parent->aboutToShowContextMenu(parent, highlightedAction, ctxMenu);
519 KMenuSetActionData(parent,parent,highlightedAction);
520
521
522 if (QMenu* subMenu = highlightedAction->menu())
523 {
524 QTimer::singleShot(100, subMenu, SLOT(hide()));
525 }
526
527
528 ctxMenu->popup(parent->mapToGlobal(pos));
529}
530
531void KMenu::KMenuPrivate::skipTitles(QKeyEvent *event)
532{
533 QWidgetAction *action = qobject_cast<QWidgetAction*>(parent->activeAction());
534 QWidgetAction *firstAction = action;
535 while (action && action->objectName() == KMENU_TITLE)
536 {
537 parent->keyPressEvent(event);
538 action = qobject_cast<QWidgetAction*>(parent->activeAction());
539 if (firstAction == action) { // we looped and only found titles
540 parent->setActiveAction(0);
541 break;
542 }
543 }
544}
545
546KMenu * KMenu::contextMenuFocus( )
547{
548 return qobject_cast<KMenu*>(QApplication::activePopupWidget());
549}
550
551QAction * KMenu::contextMenuFocusAction( )
552{
553 if (KMenu* menu = qobject_cast<KMenu*>(QApplication::activePopupWidget())) {
554 if (!menu->d->lastHoveredAction) {
555 return 0;
556 }
557 QVariant var = menu->d->lastHoveredAction->data();
558 KMenuContext ctx = var.value<KMenuContext>();
559 Q_ASSERT(ctx.menu() == menu);
560 return ctx.action();
561 }
562
563 return 0L;
564}
565
566void KMenu::contextMenuEvent(QContextMenuEvent* e)
567{
568 if (d->ctxMenu)
569 {
570 if (e->reason() == QContextMenuEvent::Mouse)
571 {
572 d->showCtxMenu(e->pos());
573 }
574 else if (activeAction())
575 {
576 d->showCtxMenu(actionGeometry(activeAction()).center());
577 }
578
579 e->accept();
580 return;
581 }
582
583 QMenu::contextMenuEvent(e);
584}
585
586void KMenu::hideEvent(QHideEvent *e)
587{
588 if (d->ctxMenu && d->ctxMenu->isVisible())
589 {
590 // we need to block signals here when the ctxMenu is showing
591 // to prevent the QPopupMenu::activated(int) signal from emitting
592 // when hiding with a context menu, the user doesn't expect the
593 // menu to actually do anything.
594 // since hideEvent gets called very late in the process of hiding
595 // (deep within QWidget::hide) the activated(int) signal is the
596 // last signal to be emitted, even after things like aboutToHide()
597 // AJS
598 bool blocked = blockSignals(true);
599 d->ctxMenu->hide();
600 blockSignals(blocked);
601 }
602 QMenu::hideEvent(e);
603}
612KMenuContext::KMenuContext( )
613 : m_menu(0L)
614 , m_action(0L)
615{
616}
617
618KMenuContext::KMenuContext( const KMenuContext & o )
619 : m_menu(o.m_menu)
620 , m_action(o.m_action)
621{
622}
623
624KMenuContext::KMenuContext(QPointer<KMenu> menu,QPointer<QAction> action)
625 : m_menu(menu)
626 , m_action(action)
627{
628}
629
630#include "kmenu.moc"
KAcceleratorManager::manage
static void manage(QWidget *widget, bool programmers_mode=false)
Manages the accelerators of a widget.
Definition: kacceleratormanager.cpp:509
KLocale::removeAcceleratorMarker
QString removeAcceleratorMarker(const QString &label) const
KMenu
A menu with keyboard searching.
Definition: kmenu.h:42
KMenu::contextMenuEvent
virtual void contextMenuEvent(QContextMenuEvent *e)
Definition: kmenu.cpp:566
KMenu::mouseReleaseEvent
virtual void mouseReleaseEvent(QMouseEvent *e)
Definition: kmenu.cpp:443
KMenu::keyPressEvent
virtual void keyPressEvent(QKeyEvent *e)
Definition: kmenu.cpp:217
KMenu::hideEvent
virtual void hideEvent(QHideEvent *)
Definition: kmenu.cpp:586
KMenu::hideContextMenu
void hideContextMenu()
Hides the context menu if shown.
Definition: kmenu.cpp:483
KMenu::~KMenu
~KMenu()
Destructs the object.
Definition: kmenu.cpp:165
KMenu::focusNextPrevChild
virtual bool focusNextPrevChild(bool next)
Definition: kmenu.cpp:381
KMenu::mousePressEvent
virtual void mousePressEvent(QMouseEvent *e)
End keyboard navigation.
Definition: kmenu.cpp:429
KMenu::addTitle
QAction * addTitle(const QString &text, QAction *before=0L)
Inserts a title item with no icon.
Definition: kmenu.cpp:170
KMenu::KMenu
KMenu(QWidget *parent=0L)
Constructs a KMenu.
Definition: kmenu.cpp:151
KMenu::contextMenu
QMenu * contextMenu()
Returns the context menu associated with this menu The data property of all actions inserted into the...
Definition: kmenu.cpp:467
KMenu::closeEvent
virtual void closeEvent(QCloseEvent *)
This is re-implemented for keyboard navigation.
Definition: kmenu.cpp:200
KMenu::keyboardModifiers
Qt::KeyboardModifiers keyboardModifiers() const
Return the state of the keyboard modifiers when the last menuitem was activated.
Definition: kmenu.cpp:212
KMenu::mouseButtons
Qt::MouseButtons mouseButtons() const
Return the state of the mouse buttons when the last menuitem was activated.
Definition: kmenu.cpp:207
KMenu::setKeyboardShortcutsEnabled
void setKeyboardShortcutsEnabled(bool enable)
Enables keyboard navigation by searching for the entered key sequence.
Definition: kmenu.cpp:412
KMenu::contextMenuFocus
static KMenu * contextMenuFocus()
Returns the KMenu associated with the current context menu.
Definition: kmenu.cpp:546
KMenu::contextMenuFocusAction
static QAction * contextMenuFocusAction()
returns the QAction associated with the current context menu
Definition: kmenu.cpp:551
KMenu::setKeyboardShortcutsExecute
void setKeyboardShortcutsExecute(bool enable)
Enables execution of the menu item once it is uniquely specified.
Definition: kmenu.cpp:417
QAction
QList
QMenu
QObject
QToolButton
QWidgetAction
QWidget
kacceleratormanager.h
kdebug.h
kglobal.h
khbox.h
timeout
int timeout
klocale.h
KMenuSetActionData
static void KMenuSetActionData(QMenu *menu, KMenu *contextedMenu, QAction *contextedAction)
Definition: kmenu.cpp:499
KMENU_TITLE
static const char KMENU_TITLE[]
Definition: kmenu.cpp:45
kmenu.h
KGlobal::locale
KLocale * locale()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal