00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qlayout.h>
00022 #include <qstyle.h>
00023 #include <qwindowsstyle.h>
00024 #include <qpainter.h>
00025
00026 #include "kexicomboboxbase.h"
00027 #include <widget/utils/kexicomboboxdropdownbutton.h>
00028 #include "kexicomboboxpopup.h"
00029 #include "kexitableview.h"
00030 #include "kexitableitem.h"
00031 #include "kexi.h"
00032
00033 #include <klineedit.h>
00034
00035 KexiComboBoxBase::KexiComboBoxBase()
00036 {
00037 m_internalEditorValueChanged = false;
00038 m_slotInternalEditorValueChanged_enabled = true;
00039 m_mouseBtnPressedWhenPopupVisible = false;
00040 m_insideCreatePopup = false;
00041 m_setValueOrTextInInternalEditor_enabled = true;
00042 m_updatePopupSelectionOnShow = true;
00043 m_moveCursorToEndInInternalEditor_enabled = true;
00044 m_selectAllInInternalEditor_enabled = true;
00045 m_setValueInInternalEditor_enabled = true;
00046 m_setVisibleValueOnSetValueInternal = false;
00047 }
00048
00049 KexiComboBoxBase::~KexiComboBoxBase()
00050 {
00051 }
00052
00053 KexiDB::LookupFieldSchema *KexiComboBoxBase::lookupFieldSchema() const
00054 {
00055 if (field() && field()->table()) {
00056 KexiDB::LookupFieldSchema *lookupFieldSchema = field()->table()->lookupFieldSchema( *field() );
00057 if (lookupFieldSchema && !lookupFieldSchema->rowSource().name().isEmpty())
00058 return lookupFieldSchema;
00059 }
00060 return 0;
00061 }
00062
00063 int KexiComboBoxBase::rowToHighlightForLookupTable() const
00064 {
00065 if (!popup())
00066 return -1;
00067 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00068 if (!lookupFieldSchema)
00069 return -1;
00070 if (lookupFieldSchema->boundColumn()==-1)
00071 return -1;
00072 bool ok;
00073 const int rowUid = origValue().toInt();
00075 KexiTableViewData *tvData = popup()->tableView()->data();
00076 const int boundColumn = lookupFieldSchema->boundColumn();
00077 KexiTableViewData::Iterator it(tvData->iterator());
00078 int row=0;
00079 for (;it.current();++it, row++)
00080 {
00081 if (it.current()->at(boundColumn).toInt(&ok) == rowUid && ok || !ok)
00082 break;
00083 }
00084 if (!ok || !it.current())
00085 return -1;
00086 return row;
00087 }
00088
00089 void KexiComboBoxBase::setValueInternal(const QVariant& add_, bool removeOld)
00090 {
00091 Q_UNUSED(removeOld);
00092 m_mouseBtnPressedWhenPopupVisible = false;
00093 m_updatePopupSelectionOnShow = true;
00094 QString add(add_.toString());
00095 if (add.isEmpty()) {
00096 KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00097 QVariant valueToSet;
00098 bool hasValueToSet = true;
00099 int rowToHighlight = -1;
00100 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00101 if (lookupFieldSchema) {
00102
00104 if (lookupFieldSchema->boundColumn()==-1)
00106 return;
00107 if (m_setVisibleValueOnSetValueInternal) {
00108
00109 if (!popup())
00110 createPopup(false);
00111 }
00112 if (popup()) {
00113 const int rowToHighlight = rowToHighlightForLookupTable();
00114 popup()->tableView()->setHighlightedRow(rowToHighlight);
00115
00116 const int visibleColumn = lookupFieldSchema->visibleColumn( popup()->tableView()->data()->columnsCount() );
00117 if (m_setVisibleValueOnSetValueInternal && -1!=visibleColumn) {
00118
00119 KexiTableItem *it = popup()->tableView()->highlightedItem();
00120 if (it)
00121 valueToSet = it->at( visibleColumn );
00122 }
00123 else {
00124 hasValueToSet = false;
00125 }
00126 }
00127 }
00128 else if (relData) {
00129
00130 valueToSet = valueForString(origValue().toString(), &rowToHighlight, 0, 1);
00131 }
00132 else {
00133
00134 const int row = origValue().toInt();
00135 valueToSet = field()->enumHint(row).stripWhiteSpace();
00136 }
00137 if (hasValueToSet)
00138 setValueOrTextInInternalEditor( valueToSet );
00139 moveCursorToEndInInternalEditor();
00140 selectAllInInternalEditor();
00141
00142 if (popup()) {
00143 if (origValue().isNull()) {
00144 popup()->tableView()->clearSelection();
00145 popup()->tableView()->setHighlightedRow(0);
00146 } else {
00147 if (relData) {
00148 if (rowToHighlight!=-1)
00149 popup()->tableView()->setHighlightedRow(rowToHighlight);
00150 }
00151 else if (!lookupFieldSchema) {
00152
00153 popup()->tableView()->setHighlightedRow(origValue().toInt());
00154 }
00155 }
00156 }
00157 }
00158 else {
00159
00160 if (popup())
00161 popup()->tableView()->clearSelection();
00162 setValueInInternalEditor(add);
00163
00164 moveCursorToEndInInternalEditor();
00165 }
00166 }
00167
00168 KexiTableItem* KexiComboBoxBase::selectItemForEnteredValueInLookupTable(const QVariant& v)
00169 {
00170 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00171 if (!popup() || !lookupFieldSchema)
00172 return 0;
00173
00174
00175
00176 const bool valueIsText = v.type()==QVariant::String || v.type()==QVariant::CString;
00177 const QString txt( valueIsText ? v.toString().stripWhiteSpace().lower() : QString::null );
00178 KexiTableViewData *lookupData = popup()->tableView()->data();
00179 const int visibleColumn = lookupFieldSchema->visibleColumn( lookupData->columnsCount() );
00180 if (-1 == visibleColumn)
00181 return 0;
00182 KexiTableViewData::Iterator it(lookupData->iterator());
00183 int row;
00184 for (row = 0;it.current();++it, row++) {
00185 if (valueIsText) {
00186 if (it.current()->at(visibleColumn).toString().stripWhiteSpace().lower() == txt)
00187 break;
00188 }
00189 else {
00190 if (it.current()->at(visibleColumn) == v)
00191 break;
00192 }
00193 }
00194
00195 m_setValueOrTextInInternalEditor_enabled = false;
00196
00197 if (it.current())
00198 popup()->tableView()->selectRow(row);
00199 else
00200 popup()->tableView()->clearSelection();
00201
00202 m_setValueOrTextInInternalEditor_enabled = true;
00203
00204 return it.current();
00205 }
00206
00207 QString KexiComboBoxBase::valueForString(const QString& str, int* row,
00208 uint lookInColumn, uint returnFromColumn, bool allowNulls)
00209 {
00210 KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00211 if (!relData)
00212 return QString::null;
00213
00214
00215
00216
00217 const QString txt = str.stripWhiteSpace().lower();
00218 KexiTableViewData::Iterator it( relData->iterator() );
00219 for (*row = 0;it.current();++it, (*row)++) {
00220 if (it.current()->at(lookInColumn).toString().stripWhiteSpace().lower()==txt)
00221 break;
00222 }
00223 if (it.current())
00224 return it.current()->at(returnFromColumn).toString();
00225
00226 *row = -1;
00227
00228 if (column() && column()->relatedDataEditable())
00229 return str;
00230
00231 kexiwarn << "KexiComboBoxBase::valueForString(): no related row found, ID will be painted!" << endl;
00232 if (allowNulls)
00233 return QString::null;
00234 return str;
00235 }
00236
00237 QVariant KexiComboBoxBase::value()
00238 {
00239 KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00240 KexiDB::LookupFieldSchema *lookupFieldSchema = 0;
00241 if (relData) {
00242 if (m_internalEditorValueChanged) {
00243
00244
00245 int rowToHighlight;
00246 return valueForString(m_userEnteredValue.toString(), &rowToHighlight, 1, 0, true);
00247 }
00248 else {
00249
00250 KexiTableItem *it = popup() ? popup()->tableView()->selectedItem() : 0;
00251 return it ? it->at(0) : origValue();
00252 }
00253 }
00254 else if ((lookupFieldSchema = this->lookupFieldSchema()))
00255 {
00256 if (lookupFieldSchema->boundColumn()==-1)
00257 return origValue();
00258 KexiTableItem *it = popup() ? popup()->tableView()->selectedItem() : 0;
00259 if ( m_internalEditorValueChanged && !m_userEnteredValue.toString().isEmpty()) {
00260
00261 if (!popup()) {
00262 QVariant prevUserEnteredValue = m_userEnteredValue;
00263 createPopup(false);
00264 m_userEnteredValue = prevUserEnteredValue;
00265 }
00266 it = selectItemForEnteredValueInLookupTable( m_userEnteredValue );
00267 }
00268 return it ? it->at( lookupFieldSchema->boundColumn() ) : QVariant();
00269 }
00270 else if (popup()) {
00271
00272 const int row = popup()->tableView()->currentRow();
00273 if (row>=0)
00274 return QVariant( row );
00275 }
00276
00277 if (valueFromInternalEditor().toString().isEmpty())
00278 return QVariant();
00281 return origValue();
00282 }
00283
00284 QVariant KexiComboBoxBase::visibleValueForLookupField()
00285 {
00286 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00287 if (!popup() || !lookupFieldSchema)
00288 return QVariant();
00289 const int visibleColumn = lookupFieldSchema->visibleColumn( popup()->tableView()->data()->columnsCount() );
00290 if (-1 == visibleColumn)
00291 return QVariant();
00292 KexiTableItem *it = popup()->tableView()->selectedItem();
00293 return it ? it->at( QMIN( (uint)visibleColumn, it->count()-1) ) : QVariant();
00294 }
00295
00296 QVariant KexiComboBoxBase::visibleValue()
00297 {
00298 return m_visibleValue;
00299 }
00300
00301 void KexiComboBoxBase::clear()
00302 {
00303 if (popup())
00304 popup()->hide();
00305 slotInternalEditorValueChanged(QVariant());
00306 }
00307
00308 tristate KexiComboBoxBase::valueChangedInternal()
00309 {
00310
00311 KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00312 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00313 if (relData || lookupFieldSchema) {
00314 if (m_internalEditorValueChanged)
00315 return true;
00316
00317
00318 KexiTableItem *it = popup() ? popup()->tableView()->selectedItem() : 0;
00319 if (!it)
00320 return false;
00321 }
00322 else {
00323
00324 const int row = popup() ? popup()->tableView()->currentRow() : -1;
00325 if (row<0 && !m_internalEditorValueChanged)
00326 return false;
00327 }
00328
00329 return cancelled;
00330 }
00331
00332 bool KexiComboBoxBase::valueIsNull()
00333 {
00334
00335 QVariant v( value() );
00336 return v.isNull();
00337
00338 }
00339
00340 bool KexiComboBoxBase::valueIsEmpty()
00341 {
00342 return valueIsNull();
00343 }
00344
00345 void KexiComboBoxBase::showPopup()
00346 {
00347 createPopup(true);
00348 }
00349
00350 void KexiComboBoxBase::createPopup(bool show)
00351 {
00352 if (!field())
00353 return;
00354 m_insideCreatePopup = true;
00355 QWidget* thisWidget = dynamic_cast<QWidget*>(this);
00356 QWidget *widgetToFocus = internalEditor() ? internalEditor() : thisWidget;
00357 if (!popup()) {
00358 setPopup( column() ? new KexiComboBoxPopup(thisWidget, *column())
00359 : new KexiComboBoxPopup(thisWidget, *field()) );
00360 QObject::connect(popup(), SIGNAL(rowAccepted(KexiTableItem*,int)),
00361 thisWidget, SLOT(slotRowAccepted(KexiTableItem*,int)));
00362 QObject::connect(popup()->tableView(), SIGNAL(itemSelected(KexiTableItem*)),
00363 thisWidget, SLOT(slotItemSelected(KexiTableItem*)));
00364
00365 popup()->setFocusProxy( widgetToFocus );
00366 popup()->tableView()->setFocusProxy( widgetToFocus );
00367 popup()->installEventFilter(thisWidget);
00368
00369 if (origValue().isNull())
00370 popup()->tableView()->clearSelection();
00371 else {
00372 popup()->tableView()->selectRow( 0 );
00373 popup()->tableView()->setHighlightedRow( 0 );
00374 }
00375 }
00376 if (show && internalEditor() && !internalEditor()->isVisible())
00377 editRequested();
00378
00379 QPoint posMappedToGlobal = mapFromParentToGlobal(thisWidget->pos());
00380 if (posMappedToGlobal != QPoint(-1,-1)) {
00382 popup()->move( posMappedToGlobal + QPoint(0, thisWidget->height()) );
00383
00384 const int w = popupWidthHint();
00385 popup()->resize(w, 0);
00386 if (show)
00387 popup()->show();
00388 popup()->updateSize(w);
00389 if (m_updatePopupSelectionOnShow) {
00390 int rowToHighlight = -1;
00391 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00392 KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00393 if (lookupFieldSchema) {
00394 rowToHighlight = rowToHighlightForLookupTable();
00395 }
00396 else if (relData) {
00397 (void)valueForString(origValue().toString(), &rowToHighlight, 0, 1);
00398 }
00399 else
00400 rowToHighlight = origValue().toInt();
00401
00402 m_moveCursorToEndInInternalEditor_enabled = show;
00403 m_selectAllInInternalEditor_enabled = show;
00404 m_setValueInInternalEditor_enabled = show;
00405 if (rowToHighlight==-1) {
00406 rowToHighlight = QMAX( popup()->tableView()->highlightedRow(), 0);
00407 setValueInInternalEditor(QVariant());
00408 }
00409 popup()->tableView()->selectRow( rowToHighlight );
00410 popup()->tableView()->setHighlightedRow( rowToHighlight );
00411 if (rowToHighlight < popup()->tableView()->rowsPerPage())
00412 popup()->tableView()->ensureCellVisible( 0, -1 );
00413
00414 m_moveCursorToEndInInternalEditor_enabled = true;
00415 m_selectAllInInternalEditor_enabled = true;
00416 m_setValueInInternalEditor_enabled = true;
00417 }
00418 }
00419
00420 if (show) {
00421 moveCursorToEndInInternalEditor();
00422 selectAllInInternalEditor();
00423 widgetToFocus->setFocus();
00424 }
00425 m_insideCreatePopup = false;
00426 }
00427
00428 void KexiComboBoxBase::hide()
00429 {
00430 if (popup())
00431 popup()->hide();
00432 }
00433
00434 void KexiComboBoxBase::slotRowAccepted(KexiTableItem * item, int row)
00435 {
00436 Q_UNUSED(row);
00437
00438
00439 updateButton();
00440 slotItemSelected(item);
00441 acceptRequested();
00442 }
00443
00444 void KexiComboBoxBase::acceptPopupSelection()
00445 {
00446 if (!popup())
00447 return;
00448 KexiTableItem *item = popup()->tableView()->highlightedItem();
00449 if (item) {
00450 popup()->tableView()->selectRow( popup()->tableView()->highlightedRow() );
00451 slotRowAccepted(item, -1);
00452 }
00453 popup()->hide();
00454 }
00455
00456 void KexiComboBoxBase::slotItemSelected(KexiTableItem*)
00457 {
00458 kexidbg << "KexiComboBoxBase::slotItemSelected(): m_visibleValue = " << m_visibleValue << endl;
00459
00460 QVariant valueToSet;
00461 KexiTableViewData *relData = column() ? column()->relatedData() : 0;
00462 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00463
00464 m_visibleValue = lookupFieldSchema ? visibleValueForLookupField() : QVariant();
00465
00466 if (relData) {
00467
00468 KexiTableItem *item = popup()->tableView()->selectedItem();
00469 if (item)
00470 valueToSet = item->at(1);
00471 }
00472 else if (lookupFieldSchema) {
00473 KexiTableItem *item = popup()->tableView()->selectedItem();
00474 const int visibleColumn = lookupFieldSchema->visibleColumn( popup()->tableView()->data()->columnsCount() );
00475 if (item && visibleColumn!=-1 ) {
00476 valueToSet = item->at( QMIN( (uint)visibleColumn, item->count()-1) );
00477 }
00478 }
00479 else {
00480
00481 valueToSet = field()->enumHint( popup()->tableView()->currentRow() );
00482 if (valueToSet.toString().isEmpty() && !m_insideCreatePopup) {
00483 clear();
00484 QWidget* thisWidget = dynamic_cast<QWidget*>(this);
00485 thisWidget->parentWidget()->setFocus();
00486 return;
00487 }
00488 }
00489 setValueOrTextInInternalEditor( valueToSet );
00490 if (m_setValueOrTextInInternalEditor_enabled) {
00491 moveCursorToEndInInternalEditor();
00492 selectAllInInternalEditor();
00493 }
00494
00495 m_updatePopupSelectionOnShow = false;
00496 }
00497
00498 void KexiComboBoxBase::slotInternalEditorValueChanged(const QVariant& v)
00499 {
00500 if (!m_slotInternalEditorValueChanged_enabled)
00501 return;
00502 m_userEnteredValue = v;
00503 m_internalEditorValueChanged = true;
00504 if (v.toString().isEmpty()) {
00505 if (popup()) {
00506 popup()->tableView()->clearSelection();
00507 }
00508 return;
00509 }
00510 }
00511
00512 void KexiComboBoxBase::setValueOrTextInInternalEditor(const QVariant& value)
00513 {
00514 if (!m_setValueOrTextInInternalEditor_enabled)
00515 return;
00516 setValueInInternalEditor( value );
00517
00518 m_userEnteredValue = QVariant();
00519 m_internalEditorValueChanged = false;
00520 }
00521
00522 bool KexiComboBoxBase::handleKeyPressForPopup( QKeyEvent *ke )
00523 {
00524 const int k = ke->key();
00525 int highlightedOrSelectedRow = popup() ? popup()->tableView()->highlightedRow() : -1;
00526 if (popup() && highlightedOrSelectedRow < 0)
00527 highlightedOrSelectedRow = popup()->tableView()->currentRow();
00528
00529 const bool enterPressed = k==Qt::Key_Enter || k==Qt::Key_Return;
00530
00531
00532
00533
00534 if (!popup() || (!enterPressed && !popup()->isVisible())) {
00535 return false;
00536 }
00537
00538 switch (k) {
00539 case Qt::Key_Up:
00540 popup()->tableView()->setHighlightedRow(
00541 QMAX(highlightedOrSelectedRow-1, 0) );
00542 updateTextForHighlightedRow();
00543 return true;
00544 case Qt::Key_Down:
00545 popup()->tableView()->setHighlightedRow(
00546 QMIN(highlightedOrSelectedRow+1, popup()->tableView()->rows()-1) );
00547 updateTextForHighlightedRow();
00548 return true;
00549 case Qt::Key_PageUp:
00550 popup()->tableView()->setHighlightedRow(
00551 QMAX(highlightedOrSelectedRow-popup()->tableView()->rowsPerPage(), 0) );
00552 updateTextForHighlightedRow();
00553 return true;
00554 case Qt::Key_PageDown:
00555 popup()->tableView()->setHighlightedRow(
00556 QMIN(highlightedOrSelectedRow+popup()->tableView()->rowsPerPage(),
00557 popup()->tableView()->rows()-1) );
00558 updateTextForHighlightedRow();
00559 return true;
00560 case Qt::Key_Home:
00561 popup()->tableView()->setHighlightedRow( 0 );
00562 updateTextForHighlightedRow();
00563 return true;
00564 case Qt::Key_End:
00565 popup()->tableView()->setHighlightedRow( popup()->tableView()->rows()-1 );
00566 updateTextForHighlightedRow();
00567 return true;
00568 case Qt::Key_Enter:
00569 case Qt::Key_Return:
00570
00571 if (popup()->tableView()->highlightedRow()>=0)
00572 popup()->tableView()->selectRow( popup()->tableView()->highlightedRow() );
00573
00574 default: ;
00575 }
00576 return false;
00577 }
00578
00579 void KexiComboBoxBase::updateTextForHighlightedRow()
00580 {
00581 KexiTableItem *item = popup() ? popup()->tableView()->highlightedItem() : 0;
00582 if (item)
00583 slotItemSelected(item);
00584 }
00585
00586 void KexiComboBoxBase::undoChanges()
00587 {
00588 KexiDB::LookupFieldSchema *lookupFieldSchema = this->lookupFieldSchema();
00589 if (lookupFieldSchema) {
00590
00591 if (popup())
00592 popup()->tableView()->selectRow( popup()->tableView()->highlightedRow() );
00593 m_visibleValue = visibleValueForLookupField();
00594
00595 setValueOrTextInInternalEditor( m_visibleValue );
00596 }
00597 }