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 QPopupMenu::mouseReleaseEvent(e);
00510 }
00511
00512 QPopupMenu*
KPopupMenu::contextMenu()
00513 {
00514
if (!d->m_ctxMenu)
00515 {
00516 d->m_ctxMenu =
new QPopupMenu(
this);
00517 connect(d->m_ctxMenu, SIGNAL(
aboutToHide()),
this, SLOT(ctxMenuHiding()));
00518 }
00519
00520
return d->m_ctxMenu;
00521 }
00522
00523 const QPopupMenu*
KPopupMenu::contextMenu()
const
00524
{
00525
return const_cast< KPopupMenu* >(
this )->contextMenu();
00526 }
00527
00528 void KPopupMenu::hideContextMenu()
00529 {
00530 KPopupMenuPrivate::s_continueCtxMenuShow =
false;
00531 }
00532
00533 int KPopupMenu::contextMenuFocusItem()
00534 {
00535
return KPopupMenuPrivate::s_highlightedItem;
00536 }
00537
00538 KPopupMenu*
KPopupMenu::contextMenuFocus()
00539 {
00540
return KPopupMenuPrivate::s_contextedMenu;
00541 }
00542
00543
void KPopupMenu::itemHighlighted(
int )
00544 {
00545
if (!d->m_ctxMenu || !d->m_ctxMenu->isVisible())
00546 {
00547
return;
00548 }
00549
00550 d->m_ctxMenu->hide();
00551 showCtxMenu(mapFromGlobal(QCursor::pos()));
00552 }
00553
00554
void KPopupMenu::showCtxMenu(
QPoint pos)
00555 {
00556 QMenuItem* item = findItem(KPopupMenuPrivate::s_highlightedItem);
00557
if (item)
00558 {
00559
QPopupMenu* subMenu = item->popup();
00560
if (subMenu)
00561 {
00562 disconnect(subMenu, SIGNAL(
aboutToShow()),
this, SLOT(ctxMenuHideShowingMenu()));
00563 }
00564 }
00565
00566 KPopupMenuPrivate::s_highlightedItem = idAt(pos);
00567
00568
if (KPopupMenuPrivate::s_highlightedItem == -1)
00569 {
00570 KPopupMenuPrivate::s_contextedMenu = 0;
00571
return;
00572 }
00573
00574 emit
aboutToShowContextMenu(
this, KPopupMenuPrivate::s_highlightedItem, d->m_ctxMenu);
00575
00576
QPopupMenu* subMenu = findItem(KPopupMenuPrivate::s_highlightedItem)->
popup();
00577
if (subMenu)
00578 {
00579 connect(subMenu, SIGNAL(
aboutToShow()), SLOT(ctxMenuHideShowingMenu()));
00580
QTimer::singleShot(100, subMenu, SLOT(hide()));
00581 }
00582
00583
if (!KPopupMenuPrivate::s_continueCtxMenuShow)
00584 {
00585 KPopupMenuPrivate::s_continueCtxMenuShow =
true;
00586
return;
00587 }
00588
00589 KPopupMenuPrivate::s_contextedMenu =
this;
00590 d->m_ctxMenu->popup(this->mapToGlobal(pos));
00591 connect(
this, SIGNAL(
highlighted(
int)),
this, SLOT(itemHighlighted(
int)));
00592 }
00593
00594
00595
00596
00597
00598
void KPopupMenu::ctxMenuHideShowingMenu()
00599 {
00600 QMenuItem* item = findItem(KPopupMenuPrivate::s_highlightedItem);
00601
if (item)
00602 {
00603
QPopupMenu* subMenu = item->
popup();
00604
if (subMenu)
00605 {
00606
QTimer::singleShot(0, subMenu, SLOT(hide()));
00607 }
00608 }
00609 }
00610
00611
void KPopupMenu::ctxMenuHiding()
00612 {
00613
if (KPopupMenuPrivate::s_highlightedItem)
00614 {
00615
QPopupMenu* subMenu = findItem(KPopupMenuPrivate::s_highlightedItem)->
popup();
00616
if (subMenu)
00617 {
00618 disconnect(subMenu, SIGNAL(
aboutToShow()),
this, SLOT(ctxMenuHideShowingMenu()));
00619 }
00620 }
00621
00622 disconnect(
this, SIGNAL(
highlighted(
int)),
this, SLOT(itemHighlighted(
int)));
00623 KPopupMenuPrivate::s_continueCtxMenuShow =
true;
00624 }
00625
00626
void KPopupMenu::contextMenuEvent(
QContextMenuEvent* e)
00627 {
00628
if (d->m_ctxMenu)
00629 {
00630
if (e->
reason() == QContextMenuEvent::Mouse)
00631 {
00632 showCtxMenu(e->
pos());
00633 }
00634
else if (actItem != -1)
00635 {
00636 showCtxMenu(itemGeometry(actItem).center());
00637 }
00638
00639 e->
accept();
00640
return;
00641 }
00642
00643 QPopupMenu::contextMenuEvent(e);
00644 }
00645
00646
void KPopupMenu::hideEvent(
QHideEvent*)
00647 {
00648
if (d->m_ctxMenu && d->m_ctxMenu->isVisible())
00649 {
00650
00651
00652
00653
00654
00655
00656
00657
00658 blockSignals(
true);
00659 d->m_ctxMenu->hide();
00660 blockSignals(
false);
00661 }
00662 }
00667
00668 KPopupMenu::KPopupMenu(
const QString& title,
QWidget *parent,
const char *name)
00669 :
QPopupMenu(parent, name)
00670 {
00671 d =
new KPopupMenuPrivate;
00672
insertTitle(title);
00673 }
00674
00675
00676 void KPopupMenu::setTitle(
const QString &title)
00677 {
00678
KPopupTitle *titleItem =
new KPopupTitle();
00679 titleItem->
setTitle(title);
00680 insertItem(titleItem);
00681 d->m_lastTitle = title;
00682 }
00683
00684
void KPopupTitle::virtual_hook(
int,
void* )
00685 { }
00686
00687
void KPopupMenu::virtual_hook(
int,
void* )
00688 { }
00689
00690
#include "kpopupmenu.moc"