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