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