• Skip to content
  • Skip to link menu
KDE 4.1 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 
00032 class K3PopupMenu::K3PopupMenuPrivate
00033 {
00034 public:
00035     K3PopupMenuPrivate ()
00036         : noMatches(false)
00037         , shortcuts(false)
00038         , autoExec(false)
00039         , lastHitAction(0L)
00040 #ifdef QT3_SUPPORT
00041         , state(Qt::NoButton)
00042 #endif
00043         , mouseButtons(Qt::NoButton)
00044         , keyboardModifiers(Qt::NoModifier)
00045         , m_ctxMenu(0)
00046     {}
00047 
00048     ~K3PopupMenuPrivate ()
00049     {
00050         delete m_ctxMenu;
00051     }
00052 
00053     QString m_lastTitle;
00054 
00055     // variables for keyboard navigation
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 #ifdef QT3_SUPPORT
00067     Qt::ButtonState state;
00068     Qt::MouseButtons mouseButtons;
00069     Qt::KeyboardModifiers keyboardModifiers;
00070 #endif
00071 
00072     // support for RMB menus on menus
00073     Q3PopupMenu* m_ctxMenu;
00074     static bool s_continueCtxMenuShow;
00075     static QPointer<QAction> s_highlightedAction;
00076     // KDE4: deprecated
00077     static int s_highlightedItem;
00078     static K3PopupMenu* s_contextedMenu;
00079 };
00080 
00081 QPointer<QAction> K3PopupMenu::K3PopupMenuPrivate::s_highlightedAction(0L);
00082 int K3PopupMenu::K3PopupMenuPrivate::s_highlightedItem(-1);
00083 K3PopupMenu* K3PopupMenu::K3PopupMenuPrivate::s_contextedMenu(0);
00084 bool K3PopupMenu::K3PopupMenuPrivate::s_continueCtxMenuShow(true);
00085 
00086 K3PopupMenu::K3PopupMenu(QWidget *parent)
00087     : Q3PopupMenu(parent)
00088     , d(new K3PopupMenuPrivate())
00089 {
00090     resetKeyboardVars();
00091     connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00092 }
00093 
00094 K3PopupMenu::~K3PopupMenu()
00095 {
00096     if (K3PopupMenuPrivate::s_contextedMenu == this)
00097     {
00098         K3PopupMenuPrivate::s_contextedMenu = 0;
00099         K3PopupMenuPrivate::s_highlightedAction = 0L;
00100         K3PopupMenuPrivate::s_highlightedItem = -1;
00101     }
00102 
00103     delete d;
00104 }
00105 
00106 QAction* K3PopupMenu::addTitle(const QString &text, QAction* before)
00107 {
00108     QAction* action = new QAction(text, this);
00109     action->setEnabled(false);
00110     QFont f = action->font();
00111     f.setBold(true);
00112     action->setFont(f);
00113     insertAction(before, action);
00114     return action;
00115 }
00116 
00117 QAction* K3PopupMenu::addTitle(const QIcon &icon, const QString &text, QAction* before)
00118 {
00119     QAction* action = new QAction(icon, text, this);
00120     action->setEnabled(false);
00121     QFont f = action->font();
00122     f.setBold(true);
00123     action->setFont(f);
00124     insertAction(before, action);
00125     return action;
00126 }
00127 
00131 void K3PopupMenu::closeEvent(QCloseEvent*e)
00132 {
00133     if (d->shortcuts)
00134         resetKeyboardVars();
00135     Q3PopupMenu::closeEvent(e);
00136 }
00137 
00138 void K3PopupMenu::activateItemAt(int index)
00139 {
00140 #ifdef QT3_SUPPORT
00141     d->state = Qt::NoButton;
00142 #endif
00143     d->mouseButtons = Qt::NoButton;
00144     d->keyboardModifiers = Qt::NoModifier;
00145     Q3PopupMenu::activateItemAt(index);
00146 }
00147 
00148 #ifdef QT3_SUPPORT
00149 Qt::ButtonState K3PopupMenu::state() const
00150 {
00151     return d->state;
00152 }
00153 #endif
00154 
00155 Qt::MouseButtons K3PopupMenu::mouseButtons() const
00156 {
00157     return d->mouseButtons;
00158 }
00159 
00160 Qt::KeyboardModifiers K3PopupMenu::keyboardModifiers() const
00161 {
00162     return d->keyboardModifiers;
00163 }
00164 
00165 void K3PopupMenu::keyPressEvent(QKeyEvent* e)
00166 {
00167 #ifdef QT3_SUPPORT
00168     d->state = Qt::NoButton;
00169 #endif
00170     d->mouseButtons = Qt::NoButton;
00171     d->keyboardModifiers = Qt::NoModifier;
00172     if (!d->shortcuts) {
00173         // continue event processing by Qpopup
00174         //e->ignore();
00175 #ifdef QT3_SUPPORT
00176         d->state = e->state();
00177 #endif
00178         d->keyboardModifiers = e->modifiers();
00179         Q3PopupMenu::keyPressEvent(e);
00180         return;
00181     }
00182 
00183     QAction* a = 0L;
00184     bool firstpass = true;
00185     QString keyString = e->text();
00186 
00187     // check for common commands dealt with by QPopup
00188     int key = e->key();
00189     if (key == Qt::Key_Escape || key == Qt::Key_Return || key == Qt::Key_Enter
00190             || key == Qt::Key_Up || key == Qt::Key_Down || key == Qt::Key_Left
00191             || key == Qt::Key_Right || key == Qt::Key_F1) {
00192 
00193         resetKeyboardVars();
00194         // continue event processing by Qpopup
00195         //e->ignore();
00196 #ifdef QT3_SUPPORT
00197         d->state = e->state();
00198 #endif
00199         d->keyboardModifiers = e->modifiers();
00200         Q3PopupMenu::keyPressEvent(e);
00201         return;
00202     } else if ( key == Qt::Key_Shift || key == Qt::Key_Control || key == Qt::Key_Alt || key == Qt::Key_Meta )
00203         return Q3PopupMenu::keyPressEvent(e);
00204 
00205     // check to see if the user wants to remove a key from the sequence (backspace)
00206     // or clear the sequence (delete)
00207     if (!d->keySeq.isNull()) {
00208         if (key == Qt::Key_Backspace) {
00209 
00210             if (d->keySeq.length() == 1) {
00211                 resetKeyboardVars();
00212                 return;
00213             }
00214 
00215             // keep the last sequence in keyString
00216             keyString = d->keySeq.left(d->keySeq.length() - 1);
00217 
00218             // allow sequence matching to be tried again
00219             resetKeyboardVars();
00220 
00221         } else if (key == Qt::Key_Delete) {
00222             resetKeyboardVars();
00223 
00224             // clear active item
00225             setActiveAction(0L);
00226             return;
00227 
00228         } else if (d->noMatches) {
00229             // clear if there are no matches
00230             resetKeyboardVars();
00231 
00232             // clear active item
00233             setActiveAction(0L);
00234 
00235         } else {
00236             // the key sequence is not a null string
00237             // therefore the lastHitAction is valid
00238             a = d->lastHitAction;
00239         }
00240 
00241     } else if (key == Qt::Key_Backspace && menuAction()) {
00242         // backspace with no chars in the buffer... go back a menu.
00243         hide();
00244         resetKeyboardVars();
00245         return;
00246     }
00247 
00248     d->keySeq += keyString;
00249     int seqLen = d->keySeq.length();
00250 
00251     foreach (a, actions()) {
00252         // don't search disabled entries
00253         if (!a->isEnabled())
00254             continue;
00255 
00256         QString thisText;
00257 
00258         // retrieve the right text
00259         // (the last selected item one may have additional ampersands)
00260         if (a == d->lastHitAction)
00261             thisText = d->originalText;
00262         else
00263             thisText = a->text();
00264 
00265         // if there is an accelerator present, remove it
00266         thisText = thisText.remove("&");
00267 
00268         // chop text to the search length
00269         thisText = thisText.left(seqLen);
00270 
00271         // do the search
00272         if (!thisText.contains(d->keySeq, Qt::CaseInsensitive)) {
00273 
00274             if (firstpass) {
00275                 // match
00276                 setActiveAction(a);
00277 
00278                 // check to see if we're underlining a different item
00279                 if (d->lastHitAction != a)
00280                     // yes; revert the underlining
00281                     d->lastHitAction->setText(d->originalText);
00282 
00283                 // set the original text if it's a different item
00284                 if (d->lastHitAction != a || d->lastHitAction == 0L)
00285                     d->originalText = a->text();
00286 
00287                 // underline the currently selected item
00288                 a->setText(underlineText(d->originalText, d->keySeq.length()));
00289 
00290                 // remember what's going on
00291                 d->lastHitAction = a;
00292 
00293                 // start/restart the clear timer
00294                 d->clearTimer.start(5000, true);
00295 
00296                 // go around for another try, to see if we can execute
00297                 firstpass = false;
00298             } else {
00299                 // don't allow execution
00300                 return;
00301             }
00302         }
00303 
00304         // fall through to allow execution
00305     }
00306 
00307     if (!firstpass) {
00308         if (d->autoExec) {
00309             // activate anything
00310             d->lastHitAction->activate(QAction::Trigger);
00311             resetKeyboardVars();
00312 
00313         } else if (d->lastHitAction && d->lastHitAction->menu()) {
00314             // only activate sub-menus
00315             d->lastHitAction->activate(QAction::Trigger);
00316             resetKeyboardVars();
00317         }
00318 
00319         return;
00320     }
00321 
00322     // no matches whatsoever, clean up
00323     resetKeyboardVars(true);
00324     //e->ignore();
00325     Q3PopupMenu::keyPressEvent(e);
00326 }
00327 
00328 bool K3PopupMenu::focusNextPrevChild( bool next )
00329 {
00330     resetKeyboardVars();
00331     return Q3PopupMenu::focusNextPrevChild( next );
00332 }
00333 
00334 QString K3PopupMenu::underlineText(const QString& text, uint length)
00335 {
00336     QString ret = text;
00337     for (uint i = 0; i < length; i++) {
00338         if (ret[2*i] != '&')
00339             ret.insert(2*i, "&");
00340     }
00341     return ret;
00342 }
00343 
00344 void K3PopupMenu::resetKeyboardVars(bool noMatches /* = false */)
00345 {
00346     // Clean up keyboard variables
00347     if (d->lastHitAction) {
00348         d->lastHitAction->setText(d->originalText);
00349         d->lastHitAction = 0L;
00350     }
00351 
00352     if (!noMatches) {
00353         d->keySeq.clear();
00354     }
00355 
00356     d->noMatches = noMatches;
00357 }
00358 
00359 void K3PopupMenu::setKeyboardShortcutsEnabled(bool enable)
00360 {
00361     d->shortcuts = enable;
00362 }
00363 
00364 void K3PopupMenu::setKeyboardShortcutsExecute(bool enable)
00365 {
00366     d->autoExec = enable;
00367 }
00376 void K3PopupMenu::mousePressEvent(QMouseEvent* e)
00377 {
00378     if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00379     {
00380         // hide on a second context menu event
00381         d->m_ctxMenu->hide();
00382     }
00383 
00384     Q3PopupMenu::mousePressEvent(e);
00385 }
00386 
00387 void K3PopupMenu::mouseReleaseEvent(QMouseEvent* e)
00388 {
00389 #ifdef QT3_SUPPORT
00390     // Save the button, and the modifiers from state()
00391     d->state = Qt::ButtonState(e->button() | (e->state() & Qt::KeyboardModifierMask));
00392 #endif
00393     // Save the button, and the modifiers
00394     d->keyboardModifiers = e->modifiers();
00395     d->mouseButtons = e->buttons();
00396 
00397     if ( !d->m_ctxMenu || !d->m_ctxMenu->isVisible() )
00398     Q3PopupMenu::mouseReleaseEvent(e);
00399 }
00400 
00401 Q3PopupMenu* K3PopupMenu::contextMenu()
00402 {
00403     if (!d->m_ctxMenu)
00404     {
00405         d->m_ctxMenu = new Q3PopupMenu(this);
00406         connect(d->m_ctxMenu, SIGNAL(aboutToHide()), this, SLOT(ctxMenuHiding()));
00407     }
00408 
00409     return d->m_ctxMenu;
00410 }
00411 
00412 const Q3PopupMenu* K3PopupMenu::contextMenu() const
00413 {
00414     return const_cast< K3PopupMenu* >( this )->contextMenu();
00415 }
00416 
00417 void K3PopupMenu::hideContextMenu()
00418 {
00419     K3PopupMenuPrivate::s_continueCtxMenuShow = false;
00420 }
00421 
00422 QAction* K3PopupMenu::contextMenuFocusAction()
00423 {
00424     return K3PopupMenuPrivate::s_highlightedAction;
00425 }
00426 
00427 K3PopupMenu* K3PopupMenu::contextMenuFocus()
00428 {
00429     return K3PopupMenuPrivate::s_contextedMenu;
00430 }
00431 
00432 void K3PopupMenu::actionHovered(QAction* action)
00433 {
00434     if (!d->m_ctxMenu || !d->m_ctxMenu->isVisible())
00435     {
00436         return;
00437     }
00438 
00439     d->m_ctxMenu->hide();
00440     showCtxMenu(actionGeometry(action).center());
00441 }
00442 
00443 void K3PopupMenu::showCtxMenu(const QPoint &pos)
00444 {
00445     if (K3PopupMenuPrivate::s_highlightedAction)
00446         if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00447             disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00448 
00449     K3PopupMenuPrivate::s_highlightedAction = activeAction();
00450     K3PopupMenuPrivate::s_highlightedItem = itemAtPos(pos);
00451 
00452     if (!K3PopupMenuPrivate::s_highlightedAction)
00453     {
00454         K3PopupMenuPrivate::s_contextedMenu = 0;
00455         return;
00456     }
00457 
00458     emit aboutToShowContextMenu(this, K3PopupMenuPrivate::s_highlightedAction, d->m_ctxMenu);
00459     emit aboutToShowContextMenu(this, K3PopupMenuPrivate::s_highlightedItem, d->m_ctxMenu);
00460 
00461     if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00462     {
00463         connect(subMenu, SIGNAL(aboutToShow()), SLOT(ctxMenuHideShowingMenu()));
00464         QTimer::singleShot(100, subMenu, SLOT(hide()));
00465     }
00466 
00467     if (!K3PopupMenuPrivate::s_continueCtxMenuShow)
00468     {
00469         K3PopupMenuPrivate::s_continueCtxMenuShow = true;
00470         return;
00471     }
00472 
00473     K3PopupMenuPrivate::s_contextedMenu = this;
00474     d->m_ctxMenu->exec(this->mapToGlobal(pos));
00475     connect(this, SIGNAL(hovered(QAction*)), SLOT(actionHovered(QAction*)));
00476 }
00477 
00478 /*
00479  * this method helps prevent submenus popping up while we have a context menu
00480  * showing
00481  */
00482 void K3PopupMenu::ctxMenuHideShowingMenu()
00483 {
00484     if (K3PopupMenuPrivate::s_highlightedAction)
00485         if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00486             QTimer::singleShot(0, subMenu, SLOT(hide()));
00487 }
00488 
00489 void K3PopupMenu::ctxMenuHiding()
00490 {
00491     if (K3PopupMenuPrivate::s_highlightedAction)
00492         if (QMenu* subMenu = K3PopupMenuPrivate::s_highlightedAction->menu())
00493             disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00494 
00495     connect(this, SIGNAL(hovered(QAction*)), this, SLOT(actionHovered(QAction*)));
00496     K3PopupMenuPrivate::s_continueCtxMenuShow = true;
00497 }
00498 
00499 void K3PopupMenu::contextMenuEvent(QContextMenuEvent* e)
00500 {
00501     if (d->m_ctxMenu)
00502     {
00503         if (e->reason() == QContextMenuEvent::Mouse)
00504         {
00505             showCtxMenu(e->pos());
00506         }
00507         else if (activeAction())
00508         {
00509             showCtxMenu(actionGeometry(activeAction()).center());
00510         }
00511 
00512         e->accept();
00513         return;
00514     }
00515 
00516     Q3PopupMenu::contextMenuEvent(e);
00517 }
00518 
00519 void K3PopupMenu::hideEvent(QHideEvent *e)
00520 {
00521     if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00522     {
00523         // we need to block signals here when the ctxMenu is showing
00524         // to prevent the QPopupMenu::activated(int) signal from emitting
00525         // when hiding with a context menu, the user doesn't expect the
00526         // menu to actually do anything.
00527         // since hideEvent gets called very late in the process of hiding
00528         // (deep within QWidget::hide) the activated(int) signal is the
00529         // last signal to be emitted, even after things like aboutToHide()
00530         // AJS
00531         bool blocked = blockSignals(true);
00532         d->m_ctxMenu->hide();
00533         blockSignals(blocked);
00534     }
00535     Q3PopupMenu::hideEvent(e);
00536 }
00541 void K3PopupMenu::virtual_hook( int, void* )
00542 { /*BASE::virtual_hook( id, data );*/ }
00543 
00544 
00545 K3PopupMenu::K3PopupMenu(const QString &title, QWidget *parent)
00546     : Q3PopupMenu(parent)
00547     , d(new K3PopupMenuPrivate())
00548 {
00549     resetKeyboardVars();
00550     connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00551     addAction(title);
00552 }
00553 
00554 #ifdef QT3_SUPPORT
00555 int K3PopupMenu::insertTitle(const QString &text, int id, int index)
00556 {
00557     int newid = insertItem(text, id, index);
00558     QMenuItem* menuItem = findItem(newid);
00559     Q_ASSERT(menuItem);
00560     menuItem->setEnabled(false);
00561     QFont f = menuItem->font();
00562     f.setBold(true);
00563     menuItem->setFont(f);
00564     return newid;
00565 }
00566 
00567 int K3PopupMenu::insertTitle(const QPixmap &icon, const QString &text, int id, int index)
00568 {
00569     int newid = insertItem(text, id, index);
00570     QMenuItem* menuItem = findItem(newid);
00571     Q_ASSERT(menuItem);
00572     menuItem->setEnabled(false);
00573     menuItem->setIcon(icon);
00574     QFont f = menuItem->font();
00575     f.setBold(true);
00576     menuItem->setFont(f);
00577     return newid;
00578 }
00579 
00580 void K3PopupMenu::changeTitle(int id, const QString &text)
00581 {
00582     QMenuItem* menuItem = findItem(id);
00583     Q_ASSERT(menuItem);
00584     if (!menuItem)
00585         return;
00586     menuItem->setText(text);
00587     menuItem->setIcon(QIcon());
00588     return;
00589 }
00590 
00591 void K3PopupMenu::changeTitle(int id, const QPixmap &icon, const QString &text)
00592 {
00593     QMenuItem* menuItem = findItem(id);
00594     Q_ASSERT(menuItem);
00595     if (!menuItem)
00596         return;
00597     menuItem->setText(text);
00598     menuItem->setIcon(icon);
00599     return;
00600 }
00601 
00602 QString K3PopupMenu::title(int id) const
00603 {
00604     QMenuItem* menuItem = findItem(id);
00605     Q_ASSERT(menuItem);
00606     if (!menuItem)
00607         return QString();
00608     return menuItem->text();
00609 }
00610 
00611 QPixmap K3PopupMenu::titlePixmap(int id) const
00612 {
00613     QMenuItem* menuItem = findItem(id);
00614     Q_ASSERT(menuItem);
00615     if (!menuItem)
00616         return QPixmap();
00617     return menuItem->icon().pixmap();
00618 }
00619 
00620 void K3PopupMenu::setTitle(const QString &title)
00621 {
00622     addAction(title);
00623 }
00624 
00625 int K3PopupMenu::contextMenuFocusItem()
00626 {
00627     return K3PopupMenuPrivate::s_highlightedItem;
00628 }
00629 
00630 #endif // END compat methods
00631 
00632 #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
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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