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 p.setPen(palette().active().highlight());
00113 p.drawLine(0, 0, r.right(), 0);
00114 }
00115
00116 QSize KPopupTitle::sizeHint() const
00117 {
00118 return minimumSize();
00119 }
00120
00121 class KPopupMenu::KPopupMenuPrivate
00122 {
00123 public:
00124 KPopupMenuPrivate ()
00125 : noMatches(false)
00126 , shortcuts(false)
00127 , autoExec(false)
00128 , lastHitIndex(-1)
00129 , state(Qt::NoButton)
00130 , m_ctxMenu(0)
00131 {}
00132
00133 ~KPopupMenuPrivate ()
00134 {
00135 delete m_ctxMenu;
00136 }
00137
00138 QString m_lastTitle;
00139
00140
00141 QTimer clearTimer;
00142
00143 bool noMatches : 1;
00144 bool shortcuts : 1;
00145 bool autoExec : 1;
00146
00147 QString keySeq;
00148 QString originalText;
00149
00150 int lastHitIndex;
00151 Qt::ButtonState state;
00152
00153
00154 QPopupMenu* m_ctxMenu;
00155 static bool s_continueCtxMenuShow;
00156 static int s_highlightedItem;
00157 static KPopupMenu* s_contextedMenu;
00158 };
00159
00160 int KPopupMenu::KPopupMenuPrivate::s_highlightedItem(-1);
00161 KPopupMenu* KPopupMenu::KPopupMenuPrivate::s_contextedMenu(0);
00162 bool KPopupMenu::KPopupMenuPrivate::s_continueCtxMenuShow(true);
00163
00164 KPopupMenu::KPopupMenu(QWidget *parent, const char *name)
00165 : QPopupMenu(parent, name)
00166 {
00167 d = new KPopupMenuPrivate;
00168 resetKeyboardVars();
00169 connect(&(d->clearTimer), SIGNAL(timeout()), SLOT(resetKeyboardVars()));
00170 }
00171
00172 KPopupMenu::~KPopupMenu()
00173 {
00174 if (KPopupMenuPrivate::s_contextedMenu == this)
00175 {
00176 KPopupMenuPrivate::s_contextedMenu = 0;
00177 KPopupMenuPrivate::s_highlightedItem = -1;
00178 }
00179
00180 delete d;
00181 }
00182
00183 int KPopupMenu::insertTitle(const QString &text, int id, int index)
00184 {
00185 KPopupTitle *titleItem = new KPopupTitle();
00186 titleItem->setTitle(text);
00187 int ret = insertItem(titleItem, id, index);
00188 setItemEnabled(ret, false);
00189 return ret;
00190 }
00191
00192 int KPopupMenu::insertTitle(const QPixmap &icon, const QString &text, int id,
00193 int index)
00194 {
00195 KPopupTitle *titleItem = new KPopupTitle();
00196 titleItem->setTitle(text, &icon);
00197 int ret = insertItem(titleItem, id, index);
00198 setItemEnabled(ret, false);
00199 return ret;
00200 }
00201
00202 void KPopupMenu::changeTitle(int id, const QString &text)
00203 {
00204 QMenuItem *item = findItem(id);
00205 if(item){
00206 if(item->widget())
00207 ((KPopupTitle *)item->widget())->setTitle(text);
00208 #ifndef NDEBUG
00209 else
00210 kdWarning() << "KPopupMenu: changeTitle() called with non-title id "<< id << endl;
00211 #endif
00212 }
00213 #ifndef NDEBUG
00214 else
00215 kdWarning() << "KPopupMenu: changeTitle() called with invalid id " << id << endl;
00216 #endif
00217 }
00218
00219 void KPopupMenu::changeTitle(int id, const QPixmap &icon, const QString &text)
00220 {
00221 QMenuItem *item = findItem(id);
00222 if(item){
00223 if(item->widget())
00224 ((KPopupTitle *)item->widget())->setTitle(text, &icon);
00225 #ifndef NDEBUG
00226 else
00227 kdWarning() << "KPopupMenu: changeTitle() called with non-title id "<< id << endl;
00228 #endif
00229 }
00230 #ifndef NDEBUG
00231 else
00232 kdWarning() << "KPopupMenu: changeTitle() called with invalid id " << id << endl;
00233 #endif
00234 }
00235
00236 QString KPopupMenu::title(int id) const
00237 {
00238 if(id == -1)
00239 return d->m_lastTitle;
00240 QMenuItem *item = findItem(id);
00241 if(item){
00242 if(item->widget())
00243 return ((KPopupTitle *)item->widget())->title();
00244 else
00245 qWarning("KPopupMenu: title() called with non-title id %d.", id);
00246 }
00247 else
00248 qWarning("KPopupMenu: title() called with invalid id %d.", id);
00249 return QString::null;
00250 }
00251
00252 QPixmap KPopupMenu::titlePixmap(int id) const
00253 {
00254 QMenuItem *item = findItem(id);
00255 if(item){
00256 if(item->widget())
00257 return ((KPopupTitle *)item->widget())->icon();
00258 else
00259 qWarning("KPopupMenu: titlePixmap() called with non-title id %d.", id);
00260 }
00261 else
00262 qWarning("KPopupMenu: titlePixmap() called with invalid id %d.", id);
00263 QPixmap tmp;
00264 return tmp;
00265 }
00266
00270 void KPopupMenu::closeEvent(QCloseEvent*e)
00271 {
00272 if (d->shortcuts)
00273 resetKeyboardVars();
00274 QPopupMenu::closeEvent(e);
00275 }
00276
00277 void KPopupMenu::activateItemAt(int index)
00278 {
00279 d->state = Qt::NoButton;
00280 QPopupMenu::activateItemAt(index);
00281 }
00282
00283 Qt::ButtonState KPopupMenu::state() const
00284 {
00285 return d->state;
00286 }
00287
00288 void KPopupMenu::keyPressEvent(QKeyEvent* e)
00289 {
00290 d->state = Qt::NoButton;
00291 if (!d->shortcuts) {
00292
00293
00294 d->state = e->state();
00295 QPopupMenu::keyPressEvent(e);
00296 return;
00297 }
00298
00299 int i = 0;
00300 bool firstpass = true;
00301 QString keyString = e->text();
00302
00303
00304 int key = e->key();
00305 if (key == Key_Escape || key == Key_Return || key == Key_Enter
00306 || key == Key_Up || key == Key_Down || key == Key_Left
00307 || key == Key_Right || key == Key_F1) {
00308
00309 resetKeyboardVars();
00310
00311
00312 d->state = e->state();
00313 QPopupMenu::keyPressEvent(e);
00314 return;
00315 } else if ( key == Key_Shift || key == Key_Control || key == Key_Alt || key == Key_Meta )
00316 return QPopupMenu::keyPressEvent(e);
00317
00318
00319
00320 if (!d->keySeq.isNull()) {
00321
00322 if (key == Key_Backspace) {
00323
00324 if (d->keySeq.length() == 1) {
00325 resetKeyboardVars();
00326 return;
00327 }
00328
00329
00330 keyString = d->keySeq.left(d->keySeq.length() - 1);
00331
00332
00333 resetKeyboardVars();
00334
00335 } else if (key == Key_Delete) {
00336 resetKeyboardVars();
00337
00338
00339 setActiveItem(0);
00340 return;
00341
00342 } else if (d->noMatches) {
00343
00344 resetKeyboardVars();
00345
00346
00347 setActiveItem(0);
00348
00349 } else {
00350
00351
00352 i = d->lastHitIndex;
00353 }
00354 } else if (key == Key_Backspace && parentMenu) {
00355
00356 hide();
00357 resetKeyboardVars();
00358 return;
00359 }
00360
00361 d->keySeq += keyString;
00362 int seqLen = d->keySeq.length();
00363
00364 for (; i < (int)count(); i++) {
00365
00366 int j = idAt(i);
00367
00368
00369 if (!isItemEnabled(j))
00370 continue;
00371
00372 QString thisText;
00373
00374
00375
00376 if (i == d->lastHitIndex)
00377 thisText = d->originalText;
00378 else
00379 thisText = text(j);
00380
00381
00382 if ((int)accel(j) != 0)
00383 thisText = thisText.replace("&", QString::null);
00384
00385
00386 thisText = thisText.left(seqLen);
00387
00388
00389 if (!thisText.find(d->keySeq, 0, false)) {
00390
00391 if (firstpass) {
00392
00393 setActiveItem(i);
00394
00395
00396 if (d->lastHitIndex != i)
00397
00398 changeItem(idAt(d->lastHitIndex), d->originalText);
00399
00400
00401 if (d->lastHitIndex != i || d->lastHitIndex == -1)
00402 d->originalText = text(j);
00403
00404
00405 changeItem(j, underlineText(d->originalText, d->keySeq.length()));
00406
00407
00408 d->lastHitIndex = i;
00409
00410
00411 d->clearTimer.start(5000, true);
00412
00413
00414 firstpass = false;
00415 } else {
00416
00417 return;
00418 }
00419 }
00420
00421
00422 }
00423
00424 if (!firstpass) {
00425 if (d->autoExec) {
00426
00427 activateItemAt(d->lastHitIndex);
00428 resetKeyboardVars();
00429
00430 } else if (findItem(idAt(d->lastHitIndex)) &&
00431 findItem(idAt(d->lastHitIndex))->popup()) {
00432
00433 activateItemAt(d->lastHitIndex);
00434 resetKeyboardVars();
00435 }
00436
00437 return;
00438 }
00439
00440
00441 resetKeyboardVars(true);
00442
00443 QPopupMenu::keyPressEvent(e);
00444 }
00445
00446 bool KPopupMenu::focusNextPrevChild( bool next )
00447 {
00448 resetKeyboardVars();
00449 return QPopupMenu::focusNextPrevChild( next );
00450 }
00451
00452 QString KPopupMenu::underlineText(const QString& text, uint length)
00453 {
00454 QString ret = text;
00455 for (uint i = 0; i < length; i++) {
00456 if (ret[2*i] != '&')
00457 ret.insert(2*i, "&");
00458 }
00459 return ret;
00460 }
00461
00462 void KPopupMenu::resetKeyboardVars(bool noMatches )
00463 {
00464
00465 if (d->lastHitIndex != -1) {
00466 changeItem(idAt(d->lastHitIndex), d->originalText);
00467 d->lastHitIndex = -1;
00468 }
00469
00470 if (!noMatches) {
00471 d->keySeq = QString::null;
00472 }
00473
00474 d->noMatches = noMatches;
00475 }
00476
00477 void KPopupMenu::setKeyboardShortcutsEnabled(bool enable)
00478 {
00479 d->shortcuts = enable;
00480 }
00481
00482 void KPopupMenu::setKeyboardShortcutsExecute(bool enable)
00483 {
00484 d->autoExec = enable;
00485 }
00494 void KPopupMenu::mousePressEvent(QMouseEvent* e)
00495 {
00496 if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00497 {
00498
00499 d->m_ctxMenu->hide();
00500 }
00501
00502 QPopupMenu::mousePressEvent(e);
00503 }
00504
00505 void KPopupMenu::mouseReleaseEvent(QMouseEvent* e)
00506 {
00507
00508 d->state = Qt::ButtonState(e->button() | (e->state() & KeyButtonMask));
00509
00510 if ( !d->m_ctxMenu || !d->m_ctxMenu->isVisible() )
00511 QPopupMenu::mouseReleaseEvent(e);
00512 }
00513
00514 QPopupMenu* KPopupMenu::contextMenu()
00515 {
00516 if (!d->m_ctxMenu)
00517 {
00518 d->m_ctxMenu = new QPopupMenu(this);
00519 connect(d->m_ctxMenu, SIGNAL(aboutToHide()), this, SLOT(ctxMenuHiding()));
00520 }
00521
00522 return d->m_ctxMenu;
00523 }
00524
00525 const QPopupMenu* KPopupMenu::contextMenu() const
00526 {
00527 return const_cast< KPopupMenu* >( this )->contextMenu();
00528 }
00529
00530 void KPopupMenu::hideContextMenu()
00531 {
00532 KPopupMenuPrivate::s_continueCtxMenuShow = false;
00533 }
00534
00535 int KPopupMenu::contextMenuFocusItem()
00536 {
00537 return KPopupMenuPrivate::s_highlightedItem;
00538 }
00539
00540 KPopupMenu* KPopupMenu::contextMenuFocus()
00541 {
00542 return KPopupMenuPrivate::s_contextedMenu;
00543 }
00544
00545 void KPopupMenu::itemHighlighted(int )
00546 {
00547 if (!d->m_ctxMenu || !d->m_ctxMenu->isVisible())
00548 {
00549 return;
00550 }
00551
00552 d->m_ctxMenu->hide();
00553 showCtxMenu(mapFromGlobal(QCursor::pos()));
00554 }
00555
00556 void KPopupMenu::showCtxMenu(QPoint pos)
00557 {
00558 QMenuItem* item = findItem(KPopupMenuPrivate::s_highlightedItem);
00559 if (item)
00560 {
00561 QPopupMenu* subMenu = item->popup();
00562 if (subMenu)
00563 {
00564 disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00565 }
00566 }
00567
00568 KPopupMenuPrivate::s_highlightedItem = idAt(pos);
00569
00570 if (KPopupMenuPrivate::s_highlightedItem == -1)
00571 {
00572 KPopupMenuPrivate::s_contextedMenu = 0;
00573 return;
00574 }
00575
00576 emit aboutToShowContextMenu(this, KPopupMenuPrivate::s_highlightedItem, d->m_ctxMenu);
00577
00578 QPopupMenu* subMenu = findItem(KPopupMenuPrivate::s_highlightedItem)->popup();
00579 if (subMenu)
00580 {
00581 connect(subMenu, SIGNAL(aboutToShow()), SLOT(ctxMenuHideShowingMenu()));
00582 QTimer::singleShot(100, subMenu, SLOT(hide()));
00583 }
00584
00585 if (!KPopupMenuPrivate::s_continueCtxMenuShow)
00586 {
00587 KPopupMenuPrivate::s_continueCtxMenuShow = true;
00588 return;
00589 }
00590
00591 KPopupMenuPrivate::s_contextedMenu = this;
00592 d->m_ctxMenu->popup(this->mapToGlobal(pos));
00593 connect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
00594 }
00595
00596
00597
00598
00599
00600 void KPopupMenu::ctxMenuHideShowingMenu()
00601 {
00602 QMenuItem* item = findItem(KPopupMenuPrivate::s_highlightedItem);
00603 if (item)
00604 {
00605 QPopupMenu* subMenu = item->popup();
00606 if (subMenu)
00607 {
00608 QTimer::singleShot(0, subMenu, SLOT(hide()));
00609 }
00610 }
00611 }
00612
00613 void KPopupMenu::ctxMenuHiding()
00614 {
00615 if (KPopupMenuPrivate::s_highlightedItem)
00616 {
00617 QPopupMenu* subMenu = findItem(KPopupMenuPrivate::s_highlightedItem)->popup();
00618 if (subMenu)
00619 {
00620 disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(ctxMenuHideShowingMenu()));
00621 }
00622 }
00623
00624 disconnect(this, SIGNAL(highlighted(int)), this, SLOT(itemHighlighted(int)));
00625 KPopupMenuPrivate::s_continueCtxMenuShow = true;
00626 }
00627
00628 void KPopupMenu::contextMenuEvent(QContextMenuEvent* e)
00629 {
00630 if (d->m_ctxMenu)
00631 {
00632 if (e->reason() == QContextMenuEvent::Mouse)
00633 {
00634 showCtxMenu(e->pos());
00635 }
00636 else if (actItem != -1)
00637 {
00638 showCtxMenu(itemGeometry(actItem).center());
00639 }
00640
00641 e->accept();
00642 return;
00643 }
00644
00645 QPopupMenu::contextMenuEvent(e);
00646 }
00647
00648 void KPopupMenu::hideEvent(QHideEvent*)
00649 {
00650 if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00651 {
00652
00653
00654
00655
00656
00657
00658
00659
00660 blockSignals(true);
00661 d->m_ctxMenu->hide();
00662 blockSignals(false);
00663 }
00664 }
00669
00670 KPopupMenu::KPopupMenu(const QString& title, QWidget *parent, const char *name)
00671 : QPopupMenu(parent, name)
00672 {
00673 d = new KPopupMenuPrivate;
00674 insertTitle(title);
00675 }
00676
00677
00678 void KPopupMenu::setTitle(const QString &title)
00679 {
00680 KPopupTitle *titleItem = new KPopupTitle();
00681 titleItem->setTitle(title);
00682 insertItem(titleItem);
00683 d->m_lastTitle = title;
00684 }
00685
00686 void KPopupTitle::virtual_hook( int, void* )
00687 { }
00688
00689 void KPopupMenu::virtual_hook( int, void* )
00690 { }
00691
00692 #include "kpopupmenu.moc"