summaryrefslogtreecommitdiff
path: root/libopie2/opieui/opopupmenu.cpp
Side-by-side diff
Diffstat (limited to 'libopie2/opieui/opopupmenu.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--libopie2/opieui/opopupmenu.cpp604
1 files changed, 604 insertions, 0 deletions
diff --git a/libopie2/opieui/opopupmenu.cpp b/libopie2/opieui/opopupmenu.cpp
new file mode 100644
index 0000000..ac73188
--- a/dev/null
+++ b/libopie2/opieui/opopupmenu.cpp
@@ -0,0 +1,604 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org>
+ Copyright (C) 2002 Hamish Rodda <meddie@yoyo.its.monash.edu.au>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/* QT */
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qtimer.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qregexp.h>
+#include <qstyle.h>
+
+/* OPIE */
+
+#include <opie2/opopupmenu.h>
+#include <opie2/oconfig.h>
+
+OPopupTitle::OPopupTitle(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ setMinimumSize(16, fontMetrics().height()+8);
+}
+
+OPopupTitle::OPopupTitle(OPixmapEffect::GradientType /* gradient */,
+ const QColor &/* color */, const QColor &/* textColor */,
+ QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ setMinimumSize(16, fontMetrics().height()+8);
+}
+
+OPopupTitle::OPopupTitle(const OPixmap & /* background */, const QColor &/* color */,
+ const QColor &/* textColor */, QWidget *parent,
+ const char *name)
+ : QWidget(parent, name)
+{
+ setMinimumSize(16, fontMetrics().height()+8);
+}
+
+void OPopupTitle::setTitle(const QString &text, const QPixmap *icon)
+{
+ titleStr = text;
+ if (icon)
+ miniicon = *icon;
+ else
+ miniicon.resize(0, 0);
+
+ int w = miniicon.width()+fontMetrics().width(titleStr);
+ int h = QMAX( fontMetrics().height(), miniicon.height() );
+ setMinimumSize( w+16, h+8 );
+}
+
+void OPopupTitle::setText( const QString &text )
+{
+ titleStr = text;
+ int w = miniicon.width()+fontMetrics().width(titleStr);
+ int h = QMAX( fontMetrics().height(), miniicon.height() );
+ setMinimumSize( w+16, h+8 );
+}
+
+void OPopupTitle::setIcon( const QPixmap &pix )
+{
+ miniicon = pix;
+ int w = miniicon.width()+fontMetrics().width(titleStr);
+ int h = QMAX( fontMetrics().height(), miniicon.height() );
+ setMinimumSize( w+16, h+8 );
+}
+
+void OPopupTitle::paintEvent(QPaintEvent *)
+{
+ QRect r(rect());
+ QPainter p(this);
+ #if QT_VERSION > 290
+ qApp->style().drawPrimitive(QStyle::PE_HeaderSection, &p, r, palette().active());
+ #else
+ #warning OPopupMenu is not fully functional on Qt2
+ #endif
+
+ if (!miniicon.isNull())
+ p.drawPixmap(4, (r.height()-miniicon.height())/2, miniicon);
+
+ if (!titleStr.isNull())
+ {
+ p.setPen(palette().active().text());
+ QFont f = p.font();
+ f.setBold(true);
+ p.setFont(f);
+ if(!miniicon.isNull())
+ {
+ p.drawText(miniicon.width()+8, 0, width()-(miniicon.width()+8),
+ height(), AlignLeft | AlignVCenter | SingleLine,
+ titleStr);
+ }
+ else
+ {
+ p.drawText(0, 0, width(), height(),
+ AlignCenter | SingleLine, titleStr);
+ }
+ }
+
+ p.setPen(palette().active().highlight());
+ p.drawLine(0, 0, r.right(), 0);
+}
+
+QSize OPopupTitle::sizeHint() const
+{
+ return(minimumSize());
+}
+
+class OPopupMenu::OPopupMenuPrivate
+{
+public:
+ OPopupMenuPrivate ()
+ : noMatches(false)
+ , shortcuts(false)
+ , autoExec(false)
+ , lastHitIndex(-1)
+ , m_ctxMenu(0)
+ {}
+
+ ~OPopupMenuPrivate ()
+ {
+ delete m_ctxMenu;
+ }
+
+ QString m_lastTitle;
+
+ // variables for keyboard navigation
+ QTimer clearTimer;
+
+ bool noMatches : 1;
+ bool shortcuts : 1;
+ bool autoExec : 1;
+
+ QString keySeq;
+ QString originalText;
+
+ int lastHitIndex;
+
+ // support for RMB menus on menus
+ QPopupMenu* m_ctxMenu;
+ static bool s_continueCtxMenuShow;
+ static int s_highlightedItem;
+ static OPopupMenu* s_contextedMenu;
+};
+
+int OPopupMenu::OPopupMenuPrivate::s_highlightedItem(-1);
+OPopupMenu* OPopupMenu::OPopupMenuPrivate::s_contextedMenu(0);
+bool OPopupMenu::OPopupMenuPrivate::s_continueCtxMenuShow(true);
+
+OPopupMenu::OPopupMenu(QWidget *parent, const char *name)
+ : QPopupMenu(parent, name)
+{
+ d = new OPopupMenuPrivate;
+ resetKeyboardVars();
+ connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
+}
+
+OPopupMenu::~OPopupMenu()
+{
+ if (OPopupMenuPrivate::s_contextedMenu == this)
+ {
+ OPopupMenuPrivate::s_contextedMenu = 0;
+ OPopupMenuPrivate::s_highlightedItem = -1;
+ }
+
+ delete d;
+}
+
+int OPopupMenu::insertTitle(const QString &text, int id, int index)
+{
+ OPopupTitle *titleItem = new OPopupTitle();
+ titleItem->setTitle(text);
+ int ret = insertItem(titleItem, id, index);
+ setItemEnabled(id, false);
+ return ret;
+}
+
+int OPopupMenu::insertTitle(const QPixmap &icon, const QString &text, int id,
+ int index)
+{
+ OPopupTitle *titleItem = new OPopupTitle();
+ titleItem->setTitle(text, &icon);
+ int ret = insertItem(titleItem, id, index);
+ setItemEnabled(id, false);
+ return ret;
+}
+
+void OPopupMenu::changeTitle(int id, const QString &text)
+{
+ QMenuItem *item = findItem(id);
+ if(item){
+ if(item->widget())
+ ((OPopupTitle *)item->widget())->setTitle(text);
+#ifndef NDEBUG
+ else
+ qWarning( "KPopupMenu: changeTitle() called with non-title id %d", id );
+#endif
+ }
+#ifndef NDEBUG
+ else
+ qWarning( "KPopupMenu: changeTitle() called with invalid id %d", id );
+#endif
+}
+
+void OPopupMenu::changeTitle(int id, const QPixmap &icon, const QString &text)
+{
+ QMenuItem *item = findItem(id);
+ if(item){
+ if(item->widget())
+ ((OPopupTitle *)item->widget())->setTitle(text, &icon);
+#ifndef NDEBUG
+ else
+ qWarning( "KPopupMenu: changeTitle() called with non-title id %d", id );
+#endif
+ }
+#ifndef NDEBUG
+ else
+ qWarning( "KPopupMenu: changeTitle() called with invalid id %d", id );
+#endif
+}
+
+QString OPopupMenu::title(int id) const
+{
+ if(id == -1) // obsolete
+ return(d->m_lastTitle);
+ QMenuItem *item = findItem(id);
+ if(item){
+ if(item->widget())
+ return(((OPopupTitle *)item->widget())->title());
+ else
+ qWarning("OPopupMenu: title() called with non-title id %d.", id);
+ }
+ else
+ qWarning("OPopupMenu: title() called with invalid id %d.", id);
+ return(QString::null);
+}
+
+QPixmap OPopupMenu::titlePixmap(int id) const
+{
+ QMenuItem *item = findItem(id);
+ if(item){
+ if(item->widget())
+ return(((OPopupTitle *)item->widget())->icon());
+ else
+ qWarning("KPopupMenu: titlePixmap() called with non-title id %d.", id);
+ }
+ else
+ qWarning("KPopupMenu: titlePixmap() called with invalid id %d.", id);
+ QPixmap tmp;
+ return(tmp);
+}
+
+/**
+ * This is re-implemented for keyboard navigation.
+ */
+void OPopupMenu::closeEvent(QCloseEvent*e)
+{
+ if (d->shortcuts)
+ resetKeyboardVars();
+ QPopupMenu::closeEvent(e);
+}
+
+void OPopupMenu::keyPressEvent(QKeyEvent* e)
+{
+ if (!d->shortcuts) {
+ // continue event processing by Qpopup
+ //e->ignore();
+ QPopupMenu::keyPressEvent(e);
+ return;
+ }
+
+ int i = 0;
+ bool firstpass = true;
+ QString keyString = e->text();
+
+ // check for common commands dealt with by QPopup
+ int key = e->key();
+ if (key == Key_Escape || key == Key_Return || key == Key_Enter
+ || key == Key_Up || key == Key_Down || key == Key_Left
+ || key == Key_Right || key == Key_F1) {
+
+ resetKeyboardVars();
+ // continue event processing by Qpopup
+ //e->ignore();
+ QPopupMenu::keyPressEvent(e);
+ return;
+ }
+
+ // check to see if the user wants to remove a key from the sequence (backspace)
+ // or clear the sequence (delete)
+ if (!d->keySeq.isNull()) {
+
+ if (key == Key_Backspace) {
+
+ if (d->keySeq.length() == 1) {
+ resetKeyboardVars();
+ return;
+ }
+
+ // keep the last sequence in keyString
+ keyString = d->keySeq.left(d->keySeq.length() - 1);
+
+ // allow sequence matching to be tried again
+ resetKeyboardVars();
+
+ } else if (key == Key_Delete) {
+ resetKeyboardVars();
+
+ // clear active item
+ setActiveItem(0);
+ return;
+
+ } else if (d->noMatches) {
+ // clear if there are no matches
+ resetKeyboardVars();
+
+ // clear active item
+ setActiveItem(0);
+
+ } else {
+ // the key sequence is not a null string
+ // therefore the lastHitIndex is valid
+ i = d->lastHitIndex;
+ }
+ } else if (key == Key_Backspace && parentMenu) {
+ // backspace with no chars in the buffer... go back a menu.
+ hide();
+ resetKeyboardVars();
+ return;
+ }
+
+ d->keySeq += keyString;
+ int seqLen = d->keySeq.length();
+
+ for (; i < (int)count(); i++) {
+ // compare typed text with text of this entry
+ int j = idAt(i);
+
+ // don't search disabled entries
+ if (!isItemEnabled(j))
+ continue;
+
+ QString thisText;
+
+ // retrieve the right text
+ // (the last selected item one may have additional ampersands)
+ if (i == d->lastHitIndex)
+ thisText = d->originalText;
+ else
+ thisText = text(j);
+
+ // if there is an accelerator present, remove it
+ if ((int)accel(j) != 0)
+ thisText = thisText.replace(QRegExp("&"), "");
+
+ // chop text to the search length
+ thisText = thisText.left(seqLen);
+
+ // do the search
+ if (thisText.find(d->keySeq, 0, false) == 0) {
+
+ if (firstpass) {
+ // match
+ setActiveItem(i);
+
+ // check to see if we're underlining a different item
+ if (d->lastHitIndex != i)
+ // yes; revert the underlining
+ changeItem(idAt(d->lastHitIndex), d->originalText);
+
+ // set the original text if it's a different item
+ if (d->lastHitIndex != i || d->lastHitIndex == -1)
+ d->originalText = text(j);
+
+ // underline the currently selected item
+ changeItem(j, underlineText(d->originalText, d->keySeq.length()));
+
+ // remeber what's going on
+ d->lastHitIndex = i;
+
+ // start/restart the clear timer
+ d->clearTimer.start(5000, true);
+
+ // go around for another try, to see if we can execute
+ firstpass = false;
+ } else {
+ // don't allow execution
+ return;
+ }
+ }
+
+ // fall through to allow execution
+ }
+
+ if (!firstpass) {
+ if (d->autoExec) {
+ // activate anything
+ activateItemAt(d->lastHitIndex);
+ resetKeyboardVars();
+
+ } else if (findItem(idAt(d->lastHitIndex)) &&
+ findItem(idAt(d->lastHitIndex))->popup()) {
+ // only activate sub-menus
+ activateItemAt(d->lastHitIndex);
+ resetKeyboardVars();
+ }
+
+ return;
+ }
+
+ // no matches whatsoever, clean up
+ resetKeyboardVars(true);
+ //e->ignore();
+ QPopupMenu::keyPressEvent(e);
+}
+
+QString OPopupMenu::underlineText(const QString& text, uint length)
+{
+ QString ret = text;
+ for (uint i = 0; i < length; i++) {
+ if (ret[2*i] != '&')
+ ret.insert(2*i, "&");
+ }
+ return ret;
+}
+
+void OPopupMenu::resetKeyboardVars(bool noMatches /* = false */)
+{
+ // Clean up keyboard variables
+ if (d->lastHitIndex != -1) {
+ changeItem(idAt(d->lastHitIndex), d->originalText);
+ d->lastHitIndex = -1;
+ }
+
+ if (!noMatches) {
+ d->keySeq = QString::null;
+ }
+
+ d->noMatches = noMatches;
+}
+
+void OPopupMenu::setKeyboardShortcutsEnabled(bool enable)
+{
+ d->shortcuts = enable;
+}
+
+void OPopupMenu::setKeyboardShortcutsExecute(bool enable)
+{
+ d->autoExec = enable;
+}
+/**
+ * End keyboard navigation.
+ */
+
+/**
+ * RMB menus on menus
+ */
+QPopupMenu* OPopupMenu::contextMenu()
+{
+ if (!d->m_ctxMenu)
+ {
+ d->m_ctxMenu = new QPopupMenu(this);
+ installEventFilter(this);
+ connect(d->m_ctxMenu, SIGNAL(aboutToHide()), this, SLOT(ctxMenuHiding()));
+ }
+
+ return d->m_ctxMenu;
+}
+
+void OPopupMenu::cancelContextMenuShow()
+{
+ OPopupMenuPrivate::s_continueCtxMenuShow = false;
+}
+
+int OPopupMenu::contextMenuFocusItem()
+{
+ return OPopupMenuPrivate::s_highlightedItem;
+}
+
+OPopupMenu* OPopupMenu::contextMenuFocus()
+{
+ return OPopupMenuPrivate::s_contextedMenu;
+}
+
+void OPopupMenu::itemHighlighted(int /* whichItem */)
+{
+ if (!d->m_ctxMenu || !d->m_ctxMenu->isVisible())
+ {
+ return;
+ }
+
+ d->m_ctxMenu->hide();
+ showCtxMenu(mapFromGlobal(QCursor::pos()));
+}
+
+void OPopupMenu::showCtxMenu(QPoint pos)
+{
+ OPopupMenuPrivate::s_highlightedItem = idAt(pos);
+
+ if (OPopupMenuPrivate::s_highlightedItem == -1)
+ {
+ OPopupMenuPrivate::s_contextedMenu = 0;
+ return;
+ }
+
+ emit aboutToShowContextMenu(this, OPopupMenuPrivate::s_highlightedItem, d->m_ctxMenu);
+
+ if (!OPopupMenuPrivate::s_continueCtxMenuShow)
+ {
+ OPopupMenuPrivate::s_continueCtxMenuShow = true;
+ return;
+ }
+
+ OPopupMenuPrivate::s_contextedMenu = this;
+ d->m_ctxMenu->popup(this->mapToGlobal(pos));
+ connect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
+}
+
+void OPopupMenu::ctxMenuHiding()
+{
+ disconnect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
+ OPopupMenuPrivate::s_continueCtxMenuShow = true;
+}
+
+bool OPopupMenu::eventFilter(QObject* obj, QEvent* event)
+{
+ if (d->m_ctxMenu && obj == this)
+ {
+ if (event->type() == QEvent::MouseButtonRelease)
+ {
+ if (d->m_ctxMenu->isVisible())
+ {
+ return true;
+ }
+ }
+ #if QT_VERSION > 290
+ else if (event->type() == QEvent::ContextMenu)
+ #else
+ else if ( (event->type() == QEvent::MouseButtonPress) &&
+ ( (QMouseEvent*) event )->button() == QMouseEvent::RightButton )
+ #endif
+ {
+ showCtxMenu(mapFromGlobal(QCursor::pos()));
+ return true;
+ }
+ }
+
+ return QWidget::eventFilter(obj, event);
+}
+
+void OPopupMenu::hideEvent(QHideEvent*)
+{
+ if (d->m_ctxMenu)
+ {
+ d->m_ctxMenu->hide();
+ }
+}
+/**
+ * end of RMB menus on menus support
+ */
+
+// Obsolete
+OPopupMenu::OPopupMenu(const QString& title, QWidget *parent, const char *name)
+ : QPopupMenu(parent, name)
+{
+ d = new OPopupMenuPrivate;
+ setTitle(title);
+}
+
+// Obsolete
+void OPopupMenu::setTitle(const QString &title)
+{
+ OPopupTitle *titleItem = new OPopupTitle();
+ titleItem->setTitle(title);
+ insertItem(titleItem);
+ d->m_lastTitle = title;
+}
+
+void OPopupTitle::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+void OPopupMenu::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+