00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kexidbcombobox.h"
00021 #include "kexidblineedit.h"
00022 #include "../kexiformscrollview.h"
00023
00024 #include <kcombobox.h>
00025 #include <kdebug.h>
00026 #include <kapplication.h>
00027
00028 #include <qmetaobject.h>
00029 #include <qpainter.h>
00030 #include <qstyle.h>
00031 #include <qdrawutil.h>
00032 #include <qptrdict.h>
00033 #include <qcursor.h>
00034
00035 #include <kexidb/queryschema.h>
00036 #include <widget/tableview/kexicomboboxpopup.h>
00037 #include <widget/tableview/kexicelleditorfactory.h>
00038 #include <kexiutils/utils.h>
00039
00041 class KexiDBComboBox::Private
00042 {
00043 public:
00044 Private()
00045 : popup(0)
00046 , visibleColumnInfo(0)
00047 , subWidgetsWithDisabledEvents(0)
00048 , isEditable(false)
00049 , buttonPressed(false)
00050 , mouseOver(false)
00051 , dataEnteredByHand(true)
00052 {
00053 }
00054 ~Private()
00055 {
00056 delete subWidgetsWithDisabledEvents;
00057 subWidgetsWithDisabledEvents = 0;
00058 }
00059
00060 KexiComboBoxPopup *popup;
00061 KComboBox *paintedCombo;
00062 QSize sizeHint;
00063
00064 KexiDB::QueryColumnInfo* visibleColumnInfo;
00065 QPtrDict<char> *subWidgetsWithDisabledEvents;
00066 bool isEditable : 1;
00067 bool buttonPressed : 1;
00068 bool mouseOver : 1;
00069 bool dataEnteredByHand : 1;
00070 bool designMode : 1;
00071 };
00072
00073
00074
00075 KexiDBComboBox::KexiDBComboBox(QWidget *parent, const char *name, bool designMode)
00076 : KexiDBAutoField(parent, name, designMode, NoLabel)
00077 , KexiComboBoxBase()
00078 , d(new Private())
00079 {
00080 setMouseTracking(true);
00081 setFocusPolicy(WheelFocus);
00082 installEventFilter(this);
00083 d->designMode = designMode;
00084 d->paintedCombo = new KComboBox(this);
00085 d->paintedCombo->hide();
00086 d->paintedCombo->move(0,0);
00087 }
00088
00089 KexiDBComboBox::~KexiDBComboBox()
00090 {
00091 delete d;
00092 }
00093
00094 KexiComboBoxPopup *KexiDBComboBox::popup() const
00095 {
00096 return d->popup;
00097 }
00098
00099 void KexiDBComboBox::setPopup(KexiComboBoxPopup *popup)
00100 {
00101 d->popup = popup;
00102 }
00103
00104 void KexiDBComboBox::setEditable(bool set)
00105 {
00106 if (d->isEditable == set)
00107 return;
00108 d->isEditable = set;
00109 d->paintedCombo->setEditable(set);
00110 if (set)
00111 createEditor();
00112 else {
00113 delete m_subwidget;
00114 m_subwidget = 0;
00115 }
00116 update();
00117 }
00118
00119 bool KexiDBComboBox::isEditable() const
00120 {
00121 return d->isEditable;
00122 }
00123
00124 void KexiDBComboBox::paintEvent( QPaintEvent * )
00125 {
00126 QPainter p( this );
00127 QColorGroup cg( palette().active() );
00128
00129
00130
00131 cg.setColor(QColorGroup::Base, paletteBackgroundColor());
00132 p.setPen(cg.text());
00133
00134 QStyle::SFlags flags = QStyle::Style_Default;
00135 if (isEnabled())
00136 flags |= QStyle::Style_Enabled;
00137 if (hasFocus())
00138 flags |= QStyle::Style_HasFocus;
00139 if (d->mouseOver)
00140 flags |= QStyle::Style_MouseOver;
00141
00142 if ( width() < 5 || height() < 5 ) {
00143 qDrawShadePanel( &p, rect(), cg, FALSE, 2, &cg.brush( QColorGroup::Button ) );
00144 return;
00145 }
00146
00148
00149 style().drawComplexControl( QStyle::CC_ComboBox, &p, d->paintedCombo , rect(), cg,
00150 flags, (uint)QStyle::SC_All,
00151 (d->buttonPressed ? QStyle::SC_ComboBoxArrow : QStyle::SC_None )
00152 );
00153
00154 if (d->isEditable) {
00155
00156 }
00157 else {
00158 QRect editorGeometry( this->editorGeometry() );
00159 if ( hasFocus() ) {
00160 if (0==qstrcmp(style().name(), "windows"))
00161 p.fillRect( editorGeometry, cg.brush( QColorGroup::Highlight ) );
00162 QRect r( QStyle::visualRect( style().subRect( QStyle::SR_ComboBoxFocusRect, d->paintedCombo ), this ) );
00163 r = QRect(r.left()-1, r.top()-1, r.width()+2, r.height()+2);
00164 style().drawPrimitive( QStyle::PE_FocusRect, &p,
00165 r, cg, flags | QStyle::Style_FocusAtBorder, QStyleOption(cg.highlight()));
00166 }
00167
00168 }
00169 }
00170
00171 QRect KexiDBComboBox::editorGeometry() const
00172 {
00173 QRect r( QStyle::visualRect(
00174 style().querySubControlMetrics(QStyle::CC_ComboBox, d->paintedCombo,
00175 QStyle::SC_ComboBoxEditField), d->paintedCombo ) );
00176
00177
00178
00179 return r;
00180 }
00181
00182 void KexiDBComboBox::createEditor()
00183 {
00184 KexiDBAutoField::createEditor();
00185 if (m_subwidget) {
00186 m_subwidget->setGeometry( editorGeometry() );
00187 if (!d->isEditable) {
00188 m_subwidget->setCursor(QCursor(Qt::ArrowCursor));
00190 QPalette subwidgetPalette( m_subwidget->palette() );
00191 subwidgetPalette.setColor(QPalette::Active, QColorGroup::Base,
00192 subwidgetPalette.color(QPalette::Active, QColorGroup::Button));
00193 m_subwidget->setPalette( subwidgetPalette );
00194 if (d->subWidgetsWithDisabledEvents)
00195 d->subWidgetsWithDisabledEvents->clear();
00196 else
00197 d->subWidgetsWithDisabledEvents = new QPtrDict<char>();
00198 d->subWidgetsWithDisabledEvents->insert(m_subwidget, (char*)1);
00199 m_subwidget->installEventFilter(this);
00200 QObjectList *l = m_subwidget->queryList( "QWidget" );
00201 for ( QObjectListIt it( *l ); it.current(); ++it ) {
00202 d->subWidgetsWithDisabledEvents->insert(it.current(), (char*)1);
00203 it.current()->installEventFilter(this);
00204 }
00205 delete l;
00206 }
00207 }
00208 updateGeometry();
00209 }
00210
00211 void KexiDBComboBox::setLabelPosition(LabelPosition position)
00212 {
00213 if(m_subwidget) {
00214 if (-1 != m_subwidget->metaObject()->findProperty("frameShape", true))
00215 m_subwidget->setProperty("frameShape", QVariant((int)QFrame::NoFrame));
00216 m_subwidget->setGeometry( editorGeometry() );
00217 }
00218
00219
00220
00221 QSizePolicy sizePolicy( this->sizePolicy() );
00222 if(position == Left)
00223 sizePolicy.setHorData( QSizePolicy::Minimum );
00224 else
00225 sizePolicy.setVerData( QSizePolicy::Minimum );
00226
00227 setSizePolicy(sizePolicy);
00228
00229
00230 }
00231
00232 QRect KexiDBComboBox::buttonGeometry() const
00233 {
00234 QRect arrowRect(
00235 style().querySubControlMetrics( QStyle::CC_ComboBox, d->paintedCombo, QStyle::SC_ComboBoxArrow) );
00236 arrowRect = QStyle::visualRect(arrowRect, d->paintedCombo);
00237 arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) );
00238 return arrowRect;
00239 }
00240
00241 bool KexiDBComboBox::handleMousePressEvent(QMouseEvent *e)
00242 {
00243 if ( e->button() != Qt::LeftButton || d->designMode )
00244 return true;
00245
00246
00247
00248
00249
00250 if ( ( !isEditable() || buttonGeometry().contains( e->pos() ) ) ) {
00251 d->buttonPressed = false;
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265 showPopup();
00266 return true;
00267 }
00268 return false;
00269 }
00270
00271 bool KexiDBComboBox::handleKeyPressEvent(QKeyEvent *ke)
00272 {
00273 const int k = ke->key();
00274 const bool dropDown = (ke->state() == Qt::NoButton && ((k==Qt::Key_F2 && !d->isEditable) || k==Qt::Key_F4))
00275 || (ke->state() == Qt::AltButton && k==Qt::Key_Down);
00276 const bool escPressed = ke->state() == Qt::NoButton && k==Qt::Key_Escape;
00277 const bool popupVisible = popup() && popup()->isVisible();
00278 if ((dropDown || escPressed) && popupVisible) {
00279 popup()->hide();
00280 return true;
00281 }
00282 else if (dropDown && !popupVisible) {
00283 d->buttonPressed = false;
00284 showPopup();
00285 return true;
00286 }
00287 else if (popupVisible) {
00288 const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return;
00289 if (enterPressed) {
00290 acceptPopupSelection();
00291 return true;
00292 }
00293 return handleKeyPressForPopup( ke );
00294 }
00295
00296 return false;
00297 }
00298
00299 bool KexiDBComboBox::keyPressed(QKeyEvent *ke)
00300 {
00301 if (KexiDBAutoField::keyPressed(ke))
00302 return true;
00303
00304 const int k = ke->key();
00305 const bool popupVisible = popup() && popup()->isVisible();
00306 const bool escPressed = ke->state() == Qt::NoButton && k==Qt::Key_Escape;
00307 if (escPressed && popupVisible) {
00308 popup()->hide();
00309 return true;
00310 }
00311 if (ke->state() == Qt::NoButton && (k==Qt::Key_PageDown || k==Qt::Key_PageUp) && popupVisible)
00312 return true;
00313 return false;
00314 }
00315
00316 void KexiDBComboBox::mousePressEvent( QMouseEvent *e )
00317 {
00318 if (handleMousePressEvent(e))
00319 return;
00320
00321
00322
00323
00324 KexiDBAutoField::mousePressEvent( e );
00325 }
00326
00327 void KexiDBComboBox::mouseDoubleClickEvent( QMouseEvent *e )
00328 {
00329 mousePressEvent( e );
00330 }
00331
00332 bool KexiDBComboBox::eventFilter( QObject *o, QEvent *e )
00333 {
00334 if (o==this) {
00335 if (e->type()==QEvent::Resize) {
00336 d->paintedCombo->resize(size());
00337 if (m_subwidget)
00338 m_subwidget->setGeometry( editorGeometry() );
00339 }
00340 else if (e->type()==QEvent::Enter) {
00341 if (!d->isEditable
00342 || buttonGeometry().contains( static_cast<QMouseEvent*>(e)->pos() ))
00343 {
00344 d->mouseOver = true;
00345 update();
00346 }
00347 }
00348 else if (e->type()==QEvent::MouseMove) {
00349 if (d->isEditable) {
00350 const bool overButton = buttonGeometry().contains( static_cast<QMouseEvent*>(e)->pos() );
00351 if (overButton != d->mouseOver) {
00352 d->mouseOver = overButton;
00353 update();
00354 }
00355 }
00356 }
00357 else if (e->type()==QEvent::Leave) {
00358 d->mouseOver = false;
00359 update();
00360 }
00361 else if (e->type()==QEvent::KeyPress) {
00362
00363 if (handleKeyPressEvent(static_cast<QKeyEvent*>(e)))
00364 return true;
00365 }
00366 else if (e->type()==QEvent::FocusOut) {
00367 if (popup() && popup()->isVisible()) {
00368 popup()->hide();
00369 undoChanges();
00370 }
00371 }
00372 }
00373 else if (!d->isEditable && d->subWidgetsWithDisabledEvents && d->subWidgetsWithDisabledEvents->find(o)) {
00374 if (e->type()==QEvent::MouseButtonPress) {
00375
00376 if (handleMousePressEvent(static_cast<QMouseEvent*>(e)))
00377 return true;
00378 }
00379 else if (e->type()==QEvent::KeyPress) {
00380 if (handleKeyPressEvent(static_cast<QKeyEvent*>(e)))
00381 return true;
00382 }
00383 return e->type()!=QEvent::Paint;
00384 }
00385 return KexiDBAutoField::eventFilter( o, e );
00386 }
00387
00388 bool KexiDBComboBox::subwidgetStretchRequired(KexiDBAutoField* autoField) const
00389 {
00390 Q_UNUSED(autoField);
00391 return true;
00392 }
00393
00394 void KexiDBComboBox::setPaletteBackgroundColor( const QColor & color )
00395 {
00396 KexiDBAutoField::setPaletteBackgroundColor(color);
00397 QPalette pal(palette());
00398 QColorGroup cg(pal.active());
00399 pal.setColor(QColorGroup::Base, red);
00400 pal.setColor(QColorGroup::Background, red);
00401 pal.setActive(cg);
00402 QWidget::setPalette(pal);
00403 update();
00404 }
00405
00406 bool KexiDBComboBox::valueChanged()
00407 {
00408 kdDebug() << "KexiDataItemInterface::valueChanged(): " << m_origValue.toString() << " ? " << value().toString() << endl;
00409 return m_origValue != value();
00410 }
00411
00412 void
00413 KexiDBComboBox::setColumnInfo(KexiDB::QueryColumnInfo* cinfo)
00414 {
00415 KexiFormDataItemInterface::setColumnInfo(cinfo);
00416 }
00417
00418 void KexiDBComboBox::setVisibleColumnInfo(KexiDB::QueryColumnInfo* cinfo)
00419 {
00420 d->visibleColumnInfo = cinfo;
00421
00422 setColumnInfoInternal(columnInfo(), d->visibleColumnInfo);
00423 }
00424
00425 KexiDB::QueryColumnInfo* KexiDBComboBox::visibleColumnInfo() const
00426 {
00427 return d->visibleColumnInfo;
00428 }
00429
00430 void KexiDBComboBox::moveCursorToEndInInternalEditor()
00431 {
00432 if (d->isEditable && m_moveCursorToEndInInternalEditor_enabled)
00433 moveCursorToEnd();
00434 }
00435
00436 void KexiDBComboBox::selectAllInInternalEditor()
00437 {
00438 if (d->isEditable && m_selectAllInInternalEditor_enabled)
00439 selectAll();
00440 }
00441
00442 void KexiDBComboBox::setValueInternal(const QVariant& add, bool removeOld)
00443 {
00446 if (popup())
00447 popup()->hide();
00448 KexiComboBoxBase::setValueInternal(add, removeOld);
00449 }
00450
00451 void KexiDBComboBox::setVisibleValueInternal(const QVariant& value)
00452 {
00453 KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
00454 if(iface)
00455 iface->setValue(value, QVariant(), false );
00456 }
00457
00458 QVariant KexiDBComboBox::visibleValue()
00459 {
00460 return KexiComboBoxBase::visibleValue();
00461 }
00462
00463 void KexiDBComboBox::setValueInInternalEditor(const QVariant& value)
00464 {
00465 if (!m_setValueInInternalEditor_enabled)
00466 return;
00467 KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
00468 if(iface)
00469 iface->setValue(value, QVariant(), false);
00470 }
00471
00472 QVariant KexiDBComboBox::valueFromInternalEditor()
00473 {
00474 return KexiDBAutoField::value();
00475 }
00476
00477 QPoint KexiDBComboBox::mapFromParentToGlobal(const QPoint& pos) const
00478 {
00479
00480 if (!parentWidget())
00481 return QPoint(-1,-1);
00482 return parentWidget()->mapToGlobal(pos);
00483
00484 }
00485
00486 int KexiDBComboBox::popupWidthHint() const
00487 {
00488 return width();
00489 }
00490
00491 void KexiDBComboBox::fontChange( const QFont & oldFont )
00492 {
00493 d->sizeHint = QSize();
00494 KexiDBAutoField::fontChange(oldFont);
00495 }
00496
00497 void KexiDBComboBox::styleChange( QStyle& oldStyle )
00498 {
00499 KexiDBAutoField::styleChange( oldStyle );
00500 d->sizeHint = QSize();
00501 if (m_subwidget)
00502 m_subwidget->setGeometry( editorGeometry() );
00503 }
00504
00505 QSize KexiDBComboBox::sizeHint() const
00506 {
00507 if ( isVisible() && d->sizeHint.isValid() )
00508 return d->sizeHint;
00509
00510 const int maxWidth = 7 * fontMetrics().width(QChar('x')) + 18;
00511 const int maxHeight = QMAX( fontMetrics().lineSpacing(), 14 ) + 2;
00512 d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, d->paintedCombo,
00513 QSize(maxWidth, maxHeight)).expandedTo(QApplication::globalStrut()));
00514
00515 return d->sizeHint;
00516 }
00517
00518 void KexiDBComboBox::editRequested()
00519 {
00520 }
00521
00522 void KexiDBComboBox::acceptRequested()
00523 {
00524 signalValueChanged();
00525 }
00526
00527 void KexiDBComboBox::slotRowAccepted(KexiTableItem *item, int row)
00528 {
00529 d->dataEnteredByHand = false;
00530 KexiComboBoxBase::slotRowAccepted(item, row);
00531 d->dataEnteredByHand = true;
00532 }
00533
00534 void KexiDBComboBox::beforeSignalValueChanged()
00535 {
00536 if (d->dataEnteredByHand) {
00537 KexiFormDataItemInterface *iface = dynamic_cast<KexiFormDataItemInterface*>((QWidget*)m_subwidget);
00538 if (iface) {
00539 slotInternalEditorValueChanged( iface->value() );
00540 }
00541 }
00542 }
00543
00544 void KexiDBComboBox::undoChanges()
00545 {
00546 KexiDBAutoField::undoChanges();
00547 KexiComboBoxBase::undoChanges();
00548 }
00549
00550 #include "kexidbcombobox.moc"