• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDE3Support

k3popupmenu.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org>
00003    Copyright (C) 2002 Hamish Rodda <rodda@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License version 2 as published by the Free Software Foundation.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
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     // variables for keyboard navigation
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     // support for RMB menus on menus
00075     Q3PopupMenu* m_ctxMenu;
00076     static bool s_continueCtxMenuShow;
00077     static QPointer<QAction> s_highlightedAction;
00078     // KDE4: deprecated
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         // continue event processing by Qpopup
00176         //e->ignore();
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     // check for common commands dealt with by QPopup
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         // continue event processing by Qpopup
00197         //e->ignore();
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     // check to see if the user wants to remove a key from the sequence (backspace)
00208     // or clear the sequence (delete)
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             // keep the last sequence in keyString
00218             keyString = d->keySeq.left(d->keySeq.length() - 1);
00219 
00220             // allow sequence matching to be tried again
00221             resetKeyboardVars();
00222 
00223         } else if (key == Qt::Key_Delete) {
00224             resetKeyboardVars();
00225 
00226             // clear active item
00227             setActiveAction(0L);
00228             return;
00229 
00230         } else if (d->noMatches) {
00231             // clear if there are no matches
00232             resetKeyboardVars();
00233 
00234             // clear active item
00235             setActiveAction(0L);
00236 
00237         } else {
00238             // the key sequence is not a null string
00239             // therefore the lastHitAction is valid
00240             a = d->lastHitAction;
00241         }
00242 
00243     } else if (key == Qt::Key_Backspace && menuAction()) {
00244         // backspace with no chars in the buffer... go back a menu.
00245         hide();
00246         resetKeyboardVars();
00247         return;
00248     }
00249 
00250     d->keySeq += keyString;
00251     int seqLen = d->keySeq.length();
00252 
00253     foreach (a, actions()) {
00254         // don't search disabled entries
00255         if (!a->isEnabled())
00256             continue;
00257 
00258         QString thisText;
00259 
00260         // retrieve the right text
00261         // (the last selected item one may have additional ampersands)
00262         if (a == d->lastHitAction)
00263             thisText = d->originalText;
00264         else
00265             thisText = a->text();
00266 
00267         // if there is an accelerator present, remove it
00268         thisText = KGlobal::locale()->removeAcceleratorMarker(thisText);
00269 
00270         // chop text to the search length
00271         thisText = thisText.left(seqLen);
00272 
00273         // do the search
00274         if (!thisText.contains(d->keySeq, Qt::CaseInsensitive)) {
00275 
00276             if (firstpass) {
00277                 // match
00278                 setActiveAction(a);
00279 
00280                 // check to see if we're underlining a different item
00281                 if (d->lastHitAction != a)
00282                     // yes; revert the underlining
00283                     d->lastHitAction->setText(d->originalText);
00284 
00285                 // set the original text if it's a different item
00286                 if (d->lastHitAction != a || d->lastHitAction == 0L)
00287                     d->originalText = a->text();
00288 
00289                 // underline the currently selected item
00290                 a->setText(underlineText(d->originalText, d->keySeq.length()));
00291 
00292                 // remember what's going on
00293                 d->lastHitAction = a;
00294 
00295                 // start/restart the clear timer
00296                 d->clearTimer.start(5000, true);
00297 
00298                 // go around for another try, to see if we can execute
00299                 firstpass = false;
00300             } else {
00301                 // don't allow execution
00302                 return;
00303             }
00304         }
00305 
00306         // fall through to allow execution
00307     }
00308 
00309     if (!firstpass) {
00310         if (d->autoExec) {
00311             // activate anything
00312             d->lastHitAction->activate(QAction::Trigger);
00313             resetKeyboardVars();
00314 
00315         } else if (d->lastHitAction && d->lastHitAction->menu()) {
00316             // only activate sub-menus
00317             d->lastHitAction->activate(QAction::Trigger);
00318             resetKeyboardVars();
00319         }
00320 
00321         return;
00322     }
00323 
00324     // no matches whatsoever, clean up
00325     resetKeyboardVars(true);
00326     //e->ignore();
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 /* = false */)
00347 {
00348     // Clean up keyboard variables
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         // hide on a second context menu event
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     // Save the button, and the modifiers from state()
00393     d->state = Qt::ButtonState(uint(e->button()) | (e->state() & Qt::KeyboardModifierMask));
00394 #endif
00395     // Save the button, and the modifiers
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  * this method helps prevent submenus popping up while we have a context menu
00482  * showing
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         // we need to block signals here when the ctxMenu is showing
00526         // to prevent the QPopupMenu::activated(int) signal from emitting
00527         // when hiding with a context menu, the user doesn't expect the
00528         // menu to actually do anything.
00529         // since hideEvent gets called very late in the process of hiding
00530         // (deep within QWidget::hide) the activated(int) signal is the
00531         // last signal to be emitted, even after things like aboutToHide()
00532         // AJS
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 { /*BASE::virtual_hook( id, data );*/ }
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"

KDE3Support

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal