kdeui Library API Documentation

kpopupmenu.cpp

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 <meddie@yoyo.its.monash.edu.au>
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., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 */
00019 #include <qpainter.h>
00020 #include <qdrawutil.h>
00021 #include <qtimer.h>
00022 #include <qfont.h>
00023 #include <qfontmetrics.h>
00024 #include <qregexp.h>
00025 
00026 #include "kpopupmenu.h"
00027 
00028 #include <kconfig.h>
00029 #include <kdebug.h>
00030 #include <kapplication.h>
00031 #include <kipc.h>
00032 #include <kiconloader.h>
00033 #include <klocale.h>
00034 #include <kglobal.h>
00035 #include <kglobalsettings.h>
00036 #include <kstandarddirs.h>
00037 
00038 KPopupTitle::KPopupTitle(QWidget *parent, const char *name)
00039     : QWidget(parent, name)
00040 {
00041     KConfig *config = KGlobal::config();
00042     QString oldGroup = config->group();
00043     QString tmpStr;
00044 
00045     config->setGroup(QString::fromLatin1("PopupTitle"));
00046     bgColor = config->readColorEntry(QString::fromLatin1("Color"), &colorGroup().mid());
00047     grHigh = bgColor.light(150);
00048     grLow = bgColor.dark(150);
00049     fgColor = config->readColorEntry(QString::fromLatin1("TextColor"), &colorGroup().highlightedText());
00050 
00051     tmpStr = config->readEntry(QString::fromLatin1("Pixmap"));
00052     if(!tmpStr.isEmpty())
00053         fill.load(KGlobal::dirs()->findResource("wallpaper", tmpStr));
00054     if(!fill.isNull()){
00055         config->setGroup(oldGroup);
00056         useGradient = false;
00057         return;
00058     }
00059 
00060     tmpStr = config->readEntry(QString::fromLatin1("Gradient"));
00061     if(tmpStr.isEmpty()) {
00062         config->setGroup(oldGroup);
00063         useGradient = false;
00064         return;
00065     }
00066 
00067     if(tmpStr == QString::fromLatin1("Horizontal"))
00068         grType = KPixmapEffect::HorizontalGradient;
00069     else if(tmpStr == QString::fromLatin1("Vertical"))
00070         grType = KPixmapEffect::VerticalGradient;
00071     else if(tmpStr == QString::fromLatin1("Diagonal"))
00072         grType = KPixmapEffect::DiagonalGradient;
00073     else if(tmpStr == QString::fromLatin1("Pyramid"))
00074         grType = KPixmapEffect::PyramidGradient;
00075     else if(tmpStr == QString::fromLatin1("Rectangle"))
00076         grType = KPixmapEffect::RectangleGradient;
00077     else if(tmpStr == QString::fromLatin1("Elliptic"))
00078         grType = KPixmapEffect::EllipticGradient;
00079     else{
00080         kdWarning() << "KPopupMenu: Unknown gradient type " << tmpStr << " for title item" << endl;
00081         grType = KPixmapEffect::HorizontalGradient;
00082     }
00083 
00084     useGradient = true;
00085     setMinimumSize(16, fontMetrics().height()+8);
00086     config->setGroup(oldGroup);
00087 }
00088 
00089 KPopupTitle::KPopupTitle(KPixmapEffect::GradientType gradient,
00090                          const QColor &color, const QColor &textColor,
00091                          QWidget *parent, const char *name)
00092    : QWidget(parent, name)
00093 {
00094     grType = gradient;
00095     fgColor = textColor;
00096     bgColor = color;
00097     grHigh = bgColor.light(150);
00098     grLow = bgColor.dark(150);
00099     useGradient = true;
00100     setMinimumSize(16, fontMetrics().height()+8);
00101 }
00102 
00103 KPopupTitle::KPopupTitle(const KPixmap &background, const QColor &color,
00104                          const QColor &textColor, QWidget *parent,
00105                          const char *name)
00106     : QWidget(parent, name)
00107 {
00108     if(!background.isNull())
00109         fill = background;
00110     else
00111         kdWarning() << "KPopupMenu: Empty pixmap used for title." << endl;
00112     useGradient = false;
00113 
00114     fgColor = textColor;
00115     bgColor = color;
00116     grHigh = bgColor.light(150);
00117     grLow = bgColor.dark(150);
00118     setMinimumSize(16, fontMetrics().height()+8);
00119 }
00120 
00121 void KPopupTitle::setTitle(const QString &text, const QPixmap *icon)
00122 {
00123     titleStr = text;
00124     if(icon){
00125         miniicon = *icon;
00126     }
00127     else
00128         miniicon.resize(0, 0);
00129 
00130     int w = miniicon.width()+fontMetrics().width(titleStr);
00131     int h = QMAX( fontMetrics().height(), miniicon.height() );
00132     setMinimumSize( w+16, h+8 );
00133 }
00134 
00135 void KPopupTitle::setText( const QString &text )
00136 {
00137     titleStr = text;
00138     int w = miniicon.width()+fontMetrics().width(titleStr);
00139     int h = QMAX( fontMetrics().height(), miniicon.height() );
00140     setMinimumSize( w+16, h+8 );
00141 }
00142 
00143 void KPopupTitle::setIcon( const QPixmap &pix )
00144 {
00145     miniicon = pix;
00146     int w = miniicon.width()+fontMetrics().width(titleStr);
00147     int h = QMAX( fontMetrics().height(), miniicon.height() );
00148     setMinimumSize( w+16, h+8 );
00149 }
00150 
00151 void KPopupTitle::paintEvent(QPaintEvent *)
00152 {
00153     QRect r(rect());
00154     QPainter p(this);
00155 
00156     if(useGradient){
00157 
00158         if(fill.width() != r.width()-4 || fill.height() != r.height()-4){
00159             fill.resize(r.width()-4, r.height()-4);
00160             KPixmapEffect::gradient(fill, grHigh, grLow, grType);
00161         }
00162         p.drawPixmap(2, 2, fill);
00163     }
00164     else if(!fill.isNull())
00165         p.drawTiledPixmap(2, 2, r.width()-4, r.height()-4, fill);
00166     else{
00167         p.fillRect(2, 2, r.width()-4, r.height()-4, QBrush(bgColor));
00168     }
00169 
00170     if(!miniicon.isNull())
00171         p.drawPixmap(4, (r.height()-miniicon.height())/2, miniicon);
00172     if(!titleStr.isNull()){
00173         p.setPen(fgColor);
00174         if(!miniicon.isNull())
00175             p.drawText(miniicon.width()+8, 0, width()-(miniicon.width()+8),
00176                        height(), AlignLeft | AlignVCenter | SingleLine,
00177                        titleStr);
00178         else
00179             p.drawText(0, 0, width(), height(),
00180                        AlignCenter | SingleLine, titleStr);
00181     }
00182     p.setPen(Qt::black);
00183     p.drawRect(r);
00184     p.setPen(grLow);
00185     p.drawLine(r.x()+1, r.y()+1, r.right()-1, r.y()+1);
00186     p.drawLine(r.x()+1, r.y()+1, r.x()+1, r.bottom()-1);
00187     p.setPen(grHigh);
00188     p.drawLine(r.x()+1, r.bottom()-1, r.right()-1, r.bottom()-1);
00189     p.drawLine(r.right()-1, r.y()+1, r.right()-1, r.bottom()-1);
00190 }
00191 
00192 QSize KPopupTitle::sizeHint() const
00193 {
00194     return(minimumSize());
00195 }
00196 
00197 class KPopupMenu::KPopupMenuPrivate
00198 {
00199 public:
00200     KPopupMenuPrivate ()
00201         : noMatches(false)
00202         , shortcuts(false)
00203         , autoExec(false)
00204         , lastHitIndex(-1)
00205     {}
00206 
00207     QString m_lastTitle;
00208 
00209     // variables for keyboard navigation
00210     QTimer clearTimer;
00211 
00212     bool noMatches : 1;
00213     bool shortcuts : 1;
00214     bool autoExec : 1;
00215     
00216     QString keySeq;
00217     QString originalText;
00218     
00219     int lastHitIndex;
00220 };
00221 
00222 
00223 KPopupMenu::KPopupMenu(QWidget *parent, const char *name)
00224     : QPopupMenu(parent, name)
00225 {
00226     d = new KPopupMenuPrivate;
00227     resetKeyboardVars();
00228     connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00229 }
00230 
00231 KPopupMenu::~KPopupMenu()
00232 {
00233     delete d;
00234 }
00235 
00236 int KPopupMenu::insertTitle(const QString &text, int id, int index)
00237 {
00238     KPopupTitle *titleItem = new KPopupTitle();
00239     titleItem->setTitle(text);
00240     int ret = insertItem(titleItem, id, index);
00241     setItemEnabled(id, false);
00242     return ret;
00243 }
00244 
00245 int KPopupMenu::insertTitle(const QPixmap &icon, const QString &text, int id,
00246                             int index)
00247 {
00248     KPopupTitle *titleItem = new KPopupTitle();
00249     titleItem->setTitle(text, &icon);
00250     int ret = insertItem(titleItem, id, index);
00251     setItemEnabled(id, false);
00252     return ret;
00253 }
00254 
00255 void KPopupMenu::changeTitle(int id, const QString &text)
00256 {
00257     QMenuItem *item = findItem(id);
00258     if(item){
00259         if(item->widget())
00260             ((KPopupTitle *)item->widget())->setTitle(text);
00261         else
00262             qWarning("KPopupMenu: changeTitle() called with non-title id %d.", id);
00263     }
00264     else
00265         qWarning("KPopupMenu: changeTitle() called with invalid id %d.", id);
00266 }
00267 
00268 void KPopupMenu::changeTitle(int id, const QPixmap &icon, const QString &text)
00269 {
00270     QMenuItem *item = findItem(id);
00271     if(item){
00272         if(item->widget())
00273             ((KPopupTitle *)item->widget())->setTitle(text, &icon);
00274         else
00275             qWarning("KPopupMenu: changeTitle() called with non-title id %d.", id);
00276     }
00277     else
00278         qWarning("KPopupMenu: changeTitle() called with invalid id %d.", id);
00279 }
00280 
00281 QString KPopupMenu::title(int id) const
00282 {
00283     if(id == -1) // obsolete
00284         return(d->m_lastTitle);
00285     QMenuItem *item = findItem(id);
00286     if(item){
00287         if(item->widget())
00288             return(((KPopupTitle *)item->widget())->title());
00289         else
00290             qWarning("KPopupMenu: title() called with non-title id %d.", id);
00291     }
00292     else
00293         qWarning("KPopupMenu: title() called with invalid id %d.", id);
00294     return(QString::null);
00295 }
00296 
00297 QPixmap KPopupMenu::titlePixmap(int id) const
00298 {
00299     QMenuItem *item = findItem(id);
00300     if(item){
00301         if(item->widget())
00302             return(((KPopupTitle *)item->widget())->icon());
00303         else
00304             qWarning("KPopupMenu: titlePixmap() called with non-title id %d.", id);
00305     }
00306     else
00307         qWarning("KPopupMenu: titlePixmap() called with invalid id %d.", id);
00308     QPixmap tmp;
00309     return(tmp);
00310 }
00311 
00315 void KPopupMenu::closeEvent(QCloseEvent*e)
00316 {
00317     if (d->shortcuts)
00318         resetKeyboardVars();
00319     QPopupMenu::closeEvent(e);
00320 }
00321 
00322 void KPopupMenu::keyPressEvent(QKeyEvent* e)
00323 {
00324     if (!d->shortcuts) {
00325         // continue event processing by Qpopup
00326         //e->ignore();
00327         QPopupMenu::keyPressEvent(e);
00328         return;
00329     }
00330 
00331     int i = 0;
00332     bool firstpass = true;
00333     QString keyString = e->text();
00334 
00335     // check for common commands dealt with by QPopup
00336     int key = e->key();
00337     if (key == Key_Escape || key == Key_Return || key == Key_Enter
00338             || key == Key_Up || key == Key_Down || key == Key_Left
00339             || key == Key_Right || key == Key_F1) {
00340 
00341         resetKeyboardVars();
00342         // continue event processing by Qpopup
00343         //e->ignore();
00344         QPopupMenu::keyPressEvent(e);
00345         return;
00346     }
00347 
00348     // check to see if the user wants to remove a key from the sequence (backspace)
00349     // or clear the sequence (delete)
00350     if (d->keySeq != QString::null) {
00351 
00352         if (key == Key_Backspace) {
00353 
00354             if (d->keySeq.length() == 1) {
00355                 resetKeyboardVars();
00356                 return;
00357             }
00358 
00359             // keep the last sequence in keyString
00360             keyString = d->keySeq.left(d->keySeq.length() - 1);
00361 
00362             // allow sequence matching to be tried again
00363             resetKeyboardVars();
00364 
00365         } else if (key == Key_Delete) {
00366             resetKeyboardVars();
00367 
00368             // clear active item
00369             setActiveItem(0);
00370             return;
00371 
00372         } else if (d->noMatches) {
00373             // clear if there are no matches
00374             resetKeyboardVars();
00375 
00376             // clear active item
00377             setActiveItem(0);
00378 
00379         } else {
00380             // the key sequence is not a null string
00381             // therefore the lastHitIndex is valid
00382             i = d->lastHitIndex;
00383         }
00384     } else if (key == Key_Backspace && parentMenu) {
00385         // backspace with no chars in the buffer... go back a menu.
00386         hide();
00387         resetKeyboardVars();
00388         return;
00389     }
00390 
00391     d->keySeq += keyString;
00392     int seqLen = d->keySeq.length();
00393 
00394     for (; i < (int)count(); i++) {
00395         // compare typed text with text of this entry
00396         int j = idAt(i);
00397 
00398         // don't search disabled entries
00399         if (!isItemEnabled(j))
00400             continue;
00401 
00402         QString thisText;
00403 
00404         // retrieve the right text
00405         // (the last selected item one may have additional ampersands)
00406         if (i == d->lastHitIndex)
00407             thisText = d->originalText;
00408         else
00409             thisText = text(j);
00410 
00411         // if there is an accelerator present, remove it
00412         if ((int)accel(j) != 0)
00413             thisText = thisText.replace(QRegExp("&"), "");
00414 
00415         // chop text to the search length
00416         thisText = thisText.left(seqLen);
00417 
00418         // do the search
00419         if (thisText.find(d->keySeq, 0, false) == 0) {
00420 
00421             if (firstpass) {
00422                 // match
00423                 setActiveItem(i);
00424 
00425                 // check to see if we're underlining a different item
00426                 if (d->lastHitIndex != i)
00427                     // yes; revert the underlining
00428                     changeItem(idAt(d->lastHitIndex), d->originalText);
00429 
00430                 // set the original text if it's a different item
00431                 if (d->lastHitIndex != i || d->lastHitIndex == -1)
00432                     d->originalText = text(j);
00433 
00434                 // underline the currently selected item
00435                 changeItem(j, underlineText(d->originalText, d->keySeq.length()));
00436 
00437                 // remeber what's going on
00438                 d->lastHitIndex = i;
00439 
00440                 // start/restart the clear timer
00441                 d->clearTimer.start(5000, true);
00442 
00443                 // go around for another try, to see if we can execute
00444                 firstpass = false;
00445             } else {
00446                 // don't allow execution
00447                 return;
00448             }
00449         }
00450 
00451         // fall through to allow execution
00452     }
00453 
00454     if (!firstpass) {
00455         if (d->autoExec) {
00456             // activate anything
00457             activateItemAt(d->lastHitIndex);
00458             resetKeyboardVars();
00459 
00460         } else if (findItem(idAt(d->lastHitIndex)) &&
00461                  findItem(idAt(d->lastHitIndex))->popup()) {
00462             // only activate sub-menus
00463             activateItemAt(d->lastHitIndex);
00464             resetKeyboardVars();
00465         }
00466 
00467         return;
00468     }
00469 
00470     // no matches whatsoever, clean up
00471     resetKeyboardVars(true);
00472     //e->ignore();
00473     QPopupMenu::keyPressEvent(e);
00474 }
00475 
00476 QString KPopupMenu::underlineText(const QString& text, uint length)
00477 {
00478     QString ret = text;
00479     for (uint i = 0; i < length; i++) {
00480         if (ret[2*i] != '&')
00481             ret.insert(2*i, "&");
00482     }
00483     return ret;
00484 }
00485 
00486 void KPopupMenu::resetKeyboardVars(bool noMatches /* = false */)
00487 {
00488     // Clean up keyboard variables
00489     if (d->lastHitIndex != -1) {
00490         changeItem(idAt(d->lastHitIndex), d->originalText);
00491         d->lastHitIndex = -1;
00492     }
00493 
00494     if (!noMatches) {
00495         d->keySeq = QString::null;
00496     }
00497 
00498     d->noMatches = noMatches;
00499 }
00500 
00501 void KPopupMenu::setKeyboardShortcutsEnabled(bool enable)
00502 {
00503     d->shortcuts = enable;
00504 }
00505 
00506 void KPopupMenu::setKeyboardShortcutsExecute(bool enable)
00507 {
00508     d->autoExec = enable;
00509 }
00514 // Obsolete
00515 KPopupMenu::KPopupMenu(const QString& title, QWidget *parent, const char *name)
00516     : QPopupMenu(parent, name)
00517 {
00518     d = new KPopupMenuPrivate;
00519     setTitle(title);
00520 }
00521 
00522 // Obsolete
00523 void KPopupMenu::setTitle(const QString &title)
00524 {
00525     KPopupTitle *titleItem = new KPopupTitle();
00526     titleItem->setTitle(title);
00527     insertItem(titleItem);
00528     d->m_lastTitle = title;
00529 }
00530 
00531 void KPopupTitle::virtual_hook( int, void* )
00532 { /*BASE::virtual_hook( id, data );*/ }
00533 
00534 void KPopupMenu::virtual_hook( int, void* )
00535 { /*BASE::virtual_hook( id, data );*/ }
00536 
00537 #include "kpopupmenu.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:04 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001