00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <qcursor.h>
00020 #include <qpainter.h>
00021 #include <qtimer.h>
00022 #include <qfontmetrics.h>
00023 #include <qstyle.h>
00024
00025 #include "kpopupmenu.h"
00026
00027 #include <kdebug.h>
00028 #include <kapplication.h>
00029
00030 KPopupTitle::KPopupTitle(QWidget *parent, const char *name)
00031 : QWidget(parent, name)
00032 {
00033 setMinimumSize(16, fontMetrics().height()+8);
00034 }
00035
00036 KPopupTitle::KPopupTitle(KPixmapEffect::GradientType ,
00037 const QColor &, const QColor &,
00038 QWidget *parent, const char *name)
00039 : QWidget(parent, name)
00040 {
00041 calcSize();
00042 }
00043
00044 KPopupTitle::KPopupTitle(const KPixmap & , const QColor &,
00045 const QColor &, QWidget *parent,
00046 const char *name)
00047 : QWidget(parent, name)
00048 {
00049 calcSize();
00050 }
00051
00052 void KPopupTitle::setTitle(const QString &text, const QPixmap *icon)
00053 {
00054 titleStr = text;
00055 if (icon)
00056 miniicon = *icon;
00057 else
00058 miniicon.resize(0, 0);
00059
00060 calcSize();
00061 }
00062
00063 void KPopupTitle::setText( const QString &text )
00064 {
00065 titleStr = text;
00066 calcSize();
00067 }
00068
00069 void KPopupTitle::setIcon( const QPixmap &pix )
00070 {
00071 miniicon = pix;
00072 calcSize();
00073 }
00074
00075 void KPopupTitle::calcSize()
00076 {
00077 QFont f = font();
00078 f.setBold(true);
00079 int w = miniicon.width()+QFontMetrics(f).width(titleStr);
00080 int h = QMAX( fontMetrics().height(), miniicon.height() );
00081 setMinimumSize( w+16, h+8 );
00082 }
00083
00084 void KPopupTitle::paintEvent(QPaintEvent *)
00085 {
00086 QRect r(rect());
00087 QPainter p(this);
00088 kapp->style().drawPrimitive(QStyle::PE_HeaderSection, &p, r, palette().active());
00089
00090 if (!miniicon.isNull())
00091 p.drawPixmap(4, (r.height()-miniicon.height())/2, miniicon);
00092
00093 if (!titleStr.isNull())
00094 {
00095 p.setPen(palette().active().text());
00096 QFont f = p.font();
00097 f.setBold(true);
00098 p.setFont(f);
00099 if(!miniicon.isNull())
00100 {
00101 p.drawText(miniicon.width()+8, 0, width()-(miniicon.width()+8),
00102 height(), AlignLeft | AlignVCenter | SingleLine,
00103 titleStr);
00104 }
00105 else
00106 {
00107 p.drawText(0, 0, width(), height(),
00108 AlignCenter | SingleLine, titleStr);
00109 }
00110 }
00111 }
00112
00113 QSize KPopupTitle::sizeHint() const
00114 {
00115 return minimumSize();
00116 }
00117
00118 class KPopupMenu::KPopupMenuPrivate
00119 {
00120 public:
00121 KPopupMenuPrivate ()
00122 : noMatches(false)
00123 , shortcuts(false)
00124 , autoExec(false)
00125 , lastHitIndex(-1)
00126 , state(Qt::NoButton)
00127 , m_ctxMenu(0)
00128 {}
00129
00130 ~KPopupMenuPrivate ()
00131 {
00132 delete m_ctxMenu;
00133 }
00134
00135 QString m_lastTitle;
00136
00137
00138 QTimer clearTimer;
00139
00140 bool noMatches : 1;
00141 bool shortcuts : 1;
00142 bool autoExec : 1;
00143
00144 QString keySeq;
00145 QString originalText;
00146
00147 int lastHitIndex;
00148 Qt::ButtonState state;
00149
00150
00151 QPopupMenu* m_ctxMenu;
00152 static bool s_continueCtxMenuShow;
00153 static int s_highlightedItem;
00154 static KPopupMenu* s_contextedMenu;
00155 };
00156
00157 int KPopupMenu::KPopupMenuPrivate::s_highlightedItem(-1);
00158 KPopupMenu* KPopupMenu::KPopupMenuPrivate::s_contextedMenu(0);
00159 bool KPopupMenu::KPopupMenuPrivate::s_continueCtxMenuShow(true);
00160
00161 KPopupMenu::KPopupMenu(QWidget *parent, const char *name)
00162 : QPopupMenu(parent, name)
00163 {
00164 d = new KPopupMenuPrivate;
00165 resetKeyboardVars();
00166 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00167 }
00168
00169 KPopupMenu::~KPopupMenu()
00170 {
00171 if (KPopupMenuPrivate::s_contextedMenu == this)
00172 {
00173 KPopupMenuPrivate::s_contextedMenu = 0;
00174 KPopupMenuPrivate::s_highlightedItem = -1;
00175 }
00176
00177 delete d;
00178 }
00179
00180 int KPopupMenu::insertTitle(const QString &text, int id, int index)
00181 {
00182 KPopupTitle *titleItem = new KPopupTitle();
00183 titleItem->setTitle(text);
00184 int ret = insertItem(titleItem, id, index);
00185 setItemEnabled(ret, false);
00186 return ret;
00187 }
00188
00189 int KPopupMenu::insertTitle(const QPixmap &icon, const QString &text, int id,
00190 int index)
00191 {
00192 KPopupTitle *titleItem = new KPopupTitle();
00193 titleItem->setTitle(text, &icon);
00194 int ret = insertItem(titleItem, id, index);
00195 setItemEnabled(ret, false);
00196 return ret;
00197 }
00198
00199 void KPopupMenu::changeTitle(int id, const QString &text)
00200 {
00201 QMenuItem *item = findItem(id);
00202 if(item){
00203 if(item->widget())
00204 ((KPopupTitle *)item->widget())->setTitle(text);
00205 #ifndef NDEBUG
00206 else
00207 kdWarning() << "KPopupMenu: changeTitle() called with non-title id "<< id << endl;
00208 #endif
00209 }
00210 #ifndef NDEBUG
00211 else
00212 kdWarning() << "KPopupMenu: changeTitle() called with invalid id " << id << endl;
00213 #endif
00214 }
00215
00216 void KPopupMenu::changeTitle(int id, const QPixmap &icon, const QString &text)
00217 {
00218 QMenuItem *item = findItem(id);
00219 if(item){
00220 if(item->widget())
00221 ((KPopupTitle *)item->widget())->setTitle(text, &icon);
00222 #ifndef NDEBUG
00223 else
00224 kdWarning() << "KPopupMenu: changeTitle() called with non-title id "<< id << endl;
00225 #endif
00226 }
00227 #ifndef NDEBUG
00228 else
00229 kdWarning() << "KPopupMenu: changeTitle() called with invalid id " << id << endl;
00230 #endif
00231 }
00232
00233 QString KPopupMenu::title(int id) const
00234 {
00235 if(id == -1)
00236 return d->m_lastTitle;
00237 QMenuItem *item = findItem(id);
00238 if(item){
00239 if(item->widget())
00240 return ((KPopupTitle *)item->widget())->title();
00241 else
00242 qWarning("KPopupMenu: title() called with non-title id %d.", id);
00243 }
00244 else
00245 qWarning("KPopupMenu: title() called with invalid id %d.", id);
00246 return QString::null;
00247 }
00248
00249 QPixmap KPopupMenu::titlePixmap(int id) const
00250 {
00251 QMenuItem *item = findItem(id);
00252 if(item){
00253 if(item->widget())
00254 return ((KPopupTitle *)item->widget())->icon();
00255 else
00256 qWarning("KPopupMenu: titlePixmap() called with non-title id %d.", id);
00257 }
00258 else
00259 qWarning("KPopupMenu: titlePixmap() called with invalid id %d.", id);
00260 QPixmap tmp;
00261 return tmp;
00262 }
00263
00267 void KPopupMenu::closeEvent(QCloseEvent*e)
00268 {
00269 if (d->shortcuts)
00270 resetKeyboardVars();
00271 QPopupMenu::closeEvent(e);
00272 }
00273
00274 void KPopupMenu::activateItemAt(int index)
00275 {
00276 d->state = Qt::NoButton;
00277 QPopupMenu::activateItemAt(index);
00278 }
00279
00280 Qt::ButtonState KPopupMenu::state() const
00281 {
00282 return d->state;
00283 }
00284
00285 void KPopupMenu::keyPressEvent(QKeyEvent* e)
00286 {
00287 d->state = Qt::NoButton;
00288 if (!d->shortcuts) {
00289
00290
00291 d->state = e->state();
00292 QPopupMenu::keyPressEvent(e);
00293 return;
00294 }
00295
00296 int i = 0;
00297 bool firstpass = true;
00298 QString keyString = e->text();
00299
00300
00301 int key = e->key();
00302 if (key == Key_Escape || key == Key_Return || key == Key_Enter
00303 || key == Key_Up || key == Key_Down || key == Key_Left
00304 || key == Key_Right || key == Key_F1) {
00305
00306 resetKeyboardVars();
00307
00308
00309 d->state = e->state();
00310 QPopupMenu::keyPressEvent(e);
00311 return;
00312 } else if ( key == Key_Shift || key == Key_Control || key == Key_Alt || key == Key_Meta )
00313 return QPopupMenu::keyPressEvent(e);
00314
00315
00316
00317 if (!d->keySeq.isNull()) {
00318
00319 if (key == Key_Backspace) {
00320
00321 if (d->keySeq.length() == 1) {
00322 resetKeyboardVars();
00323 return;
00324 }
00325
00326
00327 keyString = d->keySeq.left(d->keySeq.length() - 1);
00328
00329
00330 resetKeyboardVars();
00331
00332 } else if (key == Key_Delete) {
00333 resetKeyboardVars();
00334
00335
00336 setActiveItem(0);
00337 return;
00338
00339 } else if (d->noMatches) {
00340
00341 resetKeyboardVars();
00342
00343
00344 setActiveItem(0);
00345
00346 } else {
00347
00348
00349 i = d->lastHitIndex;
00350 }
00351 } else if (key == Key_Backspace && parentMenu) {
00352
00353 hide();
00354 resetKeyboardVars();
00355 return;
00356 }
00357
00358 d->keySeq += keyString;
00359 int seqLen = d->keySeq.length();
00360
00361 for (; i < (int)count(); i++) {
00362
00363 int j = idAt(i);
00364
00365
00366 if (!isItemEnabled(j))
00367 continue;
00368
00369 QString thisText;
00370
00371
00372
00373 if (i == d->lastHitIndex)
00374 thisText = d->originalText;
00375 else
00376 thisText = text(j);
00377
00378
00379 if ((int)accel(j) != 0)
00380 thisText = thisText.replace("&", QString::null);
00381
00382
00383 thisText = thisText.left(seqLen);
00384
00385
00386 if (!thisText.find(d->keySeq, 0, false)) {
00387
00388 if (firstpass) {
00389
00390 setActiveItem(i);
00391
00392
00393 if (d->lastHitIndex != i)
00394
00395 changeItem(idAt(d->lastHitIndex), d->originalText);
00396
00397
00398 if (d->lastHitIndex != i || d->lastHitIndex == -1)
00399 d->originalText = text(j);
00400
00401
00402 changeItem(j, underlineText(d->originalText, d->keySeq.length()));
00403
00404
00405 d->lastHitIndex = i;
00406
00407
00408 d->clearTimer.start(5000, true);
00409
00410
00411 firstpass = false;
00412 } else {
00413
00414 return;
00415 }
00416 }
00417
00418
00419 }
00420
00421 if (!firstpass) {
00422 if (d->autoExec) {
00423
00424 activateItemAt(d->lastHitIndex);
00425 resetKeyboardVars();
00426
00427 } else if (findItem(idAt(d->lastHitIndex)) &&
00428 findItem(idAt(d->lastHitIndex))->popup()) {
00429
00430 activateItemAt(d->lastHitIndex);
00431 resetKeyboardVars();
00432 }
00433
00434 return;
00435 }
00436
00437
00438 resetKeyboardVars(true);
00439
00440 QPopupMenu::keyPressEvent(e);
00441 }
00442
00443 bool KPopupMenu::focusNextPrevChild( bool next )
00444 {
00445 resetKeyboardVars();
00446 return QPopupMenu::focusNextPrevChild( next );
00447 }
00448
00449 QString KPopupMenu::underlineText(const QString& text, uint length)
00450 {
00451 QString ret = text;
00452 for (uint i = 0; i < length; i++) {
00453 if (ret[2*i] != '&')
00454 ret.insert(2*i, "&");
00455 }
00456 return ret;
00457 }
00458
00459 void KPopupMenu::resetKeyboardVars(bool noMatches )
00460 {
00461
00462 if (d->lastHitIndex != -1) {
00463 changeItem(idAt(d->lastHitIndex), d->originalText);
00464 d->lastHitIndex = -1;
00465 }
00466
00467 if (!noMatches) {
00468 d->keySeq = QString::null;
00469 }
00470
00471 d->noMatches = noMatches;
00472 }
00473
00474 void KPopupMenu::setKeyboardShortcutsEnabled(bool enable)
00475 {
00476 d->shortcuts = enable;
00477 }
00478
00479 void KPopupMenu::setKeyboardShortcutsExecute(bool enable)
00480 {
00481 d->autoExec = enable;
00482 }
00491 void KPopupMenu::mousePressEvent(QMouseEvent* e)
00492 {
00493 if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00494 {
00495
00496 d->m_ctxMenu->hide();
00497 }
00498
00499 QPopupMenu::mousePressEvent(e);
00500 }
00501
00502 void KPopupMenu::mouseReleaseEvent(QMouseEvent* e)
00503 {
00504
00505 d->state = Qt::ButtonState(e->button() | (e->state() & KeyButtonMask));
00506
00507 if ( !d->m_ctxMenu || !d->m_ctxMenu->isVisible() )
00508 QPopupMenu::mouseReleaseEvent(e);
00509 }
00510
00511 QPopupMenu* KPopupMenu::contextMenu()
00512 {
00513 if (!d->m_ctxMenu)
00514 {
00515 d->m_ctxMenu = new QPopupMenu(this);
00516 connect(d->m_ctxMenu, SIGNAL(aboutToHide()), this, SLOT(ctxMenuHiding()));
00517 }
00518
00519 return d->m_ctxMenu;
00520 }
00521
00522 const QPopupMenu* KPopupMenu::contextMenu() const
00523 {
00524 return const_cast< KPopupMenu* >( this )->contextMenu();
00525 }
00526
00527 void KPopupMenu::hideContextMenu()
00528 {
00529 KPopupMenuPrivate::s_continueCtxMenuShow = false;
00530 }
00531
00532 int KPopupMenu::contextMenuFocusItem()
00533 {
00534 return KPopupMenuPrivate::s_highlightedItem;
00535 }
00536
00537 KPopupMenu* KPopupMenu::contextMenuFocus()
00538 {
00539 return KPopupMenuPrivate::s_contextedMenu;
00540 }
00541
00542 void KPopupMenu::itemHighlighted(int )
00543 {
00544 if (!d->m_ctxMenu || !d->m_ctxMenu->isVisible())
00545 {
00546 return;
00547 }
00548
00549 d->m_ctxMenu->hide();
00550 showCtxMenu(mapFromGlobal(QCursor::pos()));
00551 }
00552
00553 void KPopupMenu::showCtxMenu(QPoint pos)
00554 {
00555 QMenuItem* item = findItem(KPopupMenuPrivate::s_highlightedItem);
00556 if (item)
00557 {
00558 QPopupMenu* subMenu = item->popup();
00559 if (subMenu)
00560 {
00561 disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00562 }
00563 }
00564
00565 KPopupMenuPrivate::s_highlightedItem = idAt(pos);
00566
00567 if (KPopupMenuPrivate::s_highlightedItem == -1)
00568 {
00569 KPopupMenuPrivate::s_contextedMenu = 0;
00570 return;
00571 }
00572
00573 emit aboutToShowContextMenu(this, KPopupMenuPrivate::s_highlightedItem, d->m_ctxMenu);
00574
00575 QPopupMenu* subMenu = findItem(KPopupMenuPrivate::s_highlightedItem)->popup();
00576 if (subMenu)
00577 {
00578 connect(subMenu, SIGNAL(aboutToShow()), SLOT(ctxMenuHideShowingMenu()));
00579 QTimer::singleShot(100, subMenu, SLOT(hide()));
00580 }
00581
00582 if (!KPopupMenuPrivate::s_continueCtxMenuShow)
00583 {
00584 KPopupMenuPrivate::s_continueCtxMenuShow = true;
00585 return;
00586 }
00587
00588 KPopupMenuPrivate::s_contextedMenu = this;
00589 d->m_ctxMenu->popup(this->mapToGlobal(pos));
00590 connect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
00591 }
00592
00593
00594
00595
00596
00597 void KPopupMenu::ctxMenuHideShowingMenu()
00598 {
00599 QMenuItem* item = findItem(KPopupMenuPrivate::s_highlightedItem);
00600 if (item)
00601 {
00602 QPopupMenu* subMenu = item->popup();
00603 if (subMenu)
00604 {
00605 QTimer::singleShot(0, subMenu, SLOT(hide()));
00606 }
00607 }
00608 }
00609
00610 void KPopupMenu::ctxMenuHiding()
00611 {
00612 if (KPopupMenuPrivate::s_highlightedItem)
00613 {
00614 QPopupMenu* subMenu = findItem(KPopupMenuPrivate::s_highlightedItem)->popup();
00615 if (subMenu)
00616 {
00617 disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00618 }
00619 }
00620
00621 disconnect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
00622 KPopupMenuPrivate::s_continueCtxMenuShow = true;
00623 }
00624
00625 void KPopupMenu::contextMenuEvent(QContextMenuEvent* e)
00626 {
00627 if (d->m_ctxMenu)
00628 {
00629 if (e->reason() == QContextMenuEvent::Mouse)
00630 {
00631 showCtxMenu(e->pos());
00632 }
00633 else if (actItem != -1)
00634 {
00635 showCtxMenu(itemGeometry(actItem).center());
00636 }
00637
00638 e->accept();
00639 return;
00640 }
00641
00642 QPopupMenu::contextMenuEvent(e);
00643 }
00644
00645 void KPopupMenu::hideEvent(QHideEvent*)
00646 {
00647 if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00648 {
00649
00650
00651
00652
00653
00654
00655
00656
00657 blockSignals(true);
00658 d->m_ctxMenu->hide();
00659 blockSignals(false);
00660 }
00661 }
00666
00667 KPopupMenu::KPopupMenu(const QString& title, QWidget *parent, const char *name)
00668 : QPopupMenu(parent, name)
00669 {
00670 d = new KPopupMenuPrivate;
00671 insertTitle(title);
00672 }
00673
00674
00675 void KPopupMenu::setTitle(const QString &title)
00676 {
00677 KPopupTitle *titleItem = new KPopupTitle();
00678 titleItem->setTitle(title);
00679 insertItem(titleItem);
00680 d->m_lastTitle = title;
00681 }
00682
00683 void KPopupTitle::virtual_hook( int, void* )
00684 { }
00685
00686 void KPopupMenu::virtual_hook( int, void* )
00687 { }
00688
00689 #include "kpopupmenu.moc"