kexi

kexitableviewdata.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002   Lucijan Busch <lucijan@gmx.at>
00003    Copyright (C) 2003   Daniel Molkentin <molkentin@kde.org>
00004    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00005 
00006    This program is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this program; see the file COPYING.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  
00021    Original Author:  Till Busch <till@bux.at>
00022    Original Project: buX (www.bux.at)
00023 */
00024 
00025 #include "kexitableviewdata.h"
00026 
00027 #include <kexiutils/validator.h>
00028 
00029 #include <kexidb/field.h>
00030 #include <kexidb/queryschema.h>
00031 #include <kexidb/roweditbuffer.h>
00032 #include <kexidb/cursor.h>
00033 #include <kexidb/utils.h>
00034 #include <kexi.h>
00035 
00036 #include <kdebug.h>
00037 #include <klocale.h>
00038 
00039 #include <qapplication.h>
00040 
00041 unsigned short KexiTableViewData::charTable[]=
00042 {
00043     #include "chartable.txt"
00044 };
00045 
00046 KexiTableViewColumn::KexiTableViewColumn(KexiDB::Field& f, bool owner)
00047 : columnInfo(0)
00048 , visibleLookupColumnInfo(0)
00049 , m_field(&f)
00050 {
00051     isDBAware = false;
00052     m_fieldOwned = owner;
00053     m_captionAliasOrName = m_field->captionOrName();
00054     init();
00055 }
00056 
00057 KexiTableViewColumn::KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype,
00058     uint cconst,
00059     uint options,
00060     uint length, uint precision,
00061     QVariant defaultValue,
00062     const QString& caption, const QString& description, uint width
00063 )
00064 : columnInfo(0)
00065 , visibleLookupColumnInfo(0)
00066 {
00067     m_field = new KexiDB::Field(
00068         name, ctype,
00069         cconst,
00070         options,
00071         length, precision,
00072         defaultValue,
00073         caption, description, width);
00074 
00075     isDBAware = false;
00076     m_fieldOwned = true;
00077     m_captionAliasOrName = m_field->captionOrName();
00078     init();
00079 }
00080 
00081 KexiTableViewColumn::KexiTableViewColumn(const QString& name, KexiDB::Field::Type ctype, 
00082     const QString& caption, const QString& description)
00083 : columnInfo(0)
00084 , visibleLookupColumnInfo(0)
00085 {
00086     m_field = new KexiDB::Field(
00087         name, ctype,
00088         KexiDB::Field::NoConstraints,
00089         KexiDB::Field::NoOptions,
00090         0, 0,
00091         QVariant(),
00092         caption, description);
00093 
00094     isDBAware = false;
00095     m_fieldOwned = true;
00096     m_captionAliasOrName = m_field->captionOrName();
00097     init();
00098 }
00099 
00100 // db-aware
00101 KexiTableViewColumn::KexiTableViewColumn(
00102     const KexiDB::QuerySchema &query, KexiDB::QueryColumnInfo& aColumnInfo,
00103     KexiDB::QueryColumnInfo* aVisibleLookupColumnInfo)
00104 : columnInfo(&aColumnInfo)
00105 , visibleLookupColumnInfo(aVisibleLookupColumnInfo)
00106 , m_field(aColumnInfo.field)
00107 {
00108     isDBAware = true;
00109     m_fieldOwned = false;
00110 
00111     //setup column's caption:
00112     if (!columnInfo->field->caption().isEmpty()) {
00113         m_captionAliasOrName = columnInfo->field->caption();
00114     }
00115     else {
00116         //reuse alias if available:
00117         m_captionAliasOrName = columnInfo->alias;
00118         //last hance: use field name
00119         if (m_captionAliasOrName.isEmpty())
00120             m_captionAliasOrName = columnInfo->field->name();
00121         //todo: compute other auto-name?
00122     }
00123     init();
00124     //setup column's readonly flag: 
00125     // true if it's not from parent table's field or if the query itself is coming read-only connection
00126     m_readOnly = (query.masterTable()!=columnInfo->field->table())
00127         || (query.connection() && query.connection()->isReadOnly());
00128 //  kdDebug() << "KexiTableViewColumn: query.masterTable()==" 
00129 //      << (query.masterTable() ? query.masterTable()->name() : "notable") << ", columnInfo->field->table()=="
00130 //      << (columnInfo->field->table() ? columnInfo->field->table()->name()  : "notable") << endl;
00131 
00132 //  m_visible = query.isFieldVisible(&f);
00133 }
00134 
00135 KexiTableViewColumn::KexiTableViewColumn(bool)
00136 : columnInfo(0)
00137 , visibleLookupColumnInfo(0)
00138 , m_field(0)
00139 {
00140     isDBAware = false;
00141     init();
00142 }
00143 
00144 KexiTableViewColumn::~KexiTableViewColumn()
00145 {
00146     if (m_fieldOwned)
00147         delete m_field;
00148     setValidator( 0 );
00149     delete m_relatedData;
00150 }
00151 
00152 void KexiTableViewColumn::init()
00153 {
00154     m_relatedData = 0;
00155     m_readOnly = false;
00156     m_visible = true;
00157     m_data = 0;
00158     m_validator = 0;
00159     m_relatedDataEditable = false;
00160     m_headerTextVisible = true;
00161 }
00162 
00163 void KexiTableViewColumn::setValidator( KexiUtils::Validator* v )
00164 {
00165     if (m_validator) {//remove old one
00166         if (!m_validator->parent()) //destroy if has no parent
00167             delete m_validator;
00168     }
00169     m_validator = v;
00170 }
00171 
00172 void KexiTableViewColumn::setRelatedData(KexiTableViewData *data)
00173 {
00174     if (isDBAware)
00175         return;
00176     if (m_relatedData)
00177         delete m_relatedData;
00178     m_relatedData = 0;
00179     if (!data)
00180         return;
00181     //find a primary key
00182     KexiTableViewColumn::ListIterator it( data->columns );
00183     for (int id = 0;it.current();++it, id++) {
00184         if (it.current()->field()->isPrimaryKey()) {
00185             //found, remember
00186             m_relatedDataPKeyID = id;
00187             m_relatedData = data;
00188             return;
00189         }
00190     }
00191 }
00192 
00193 void KexiTableViewColumn::setRelatedDataEditable(bool set)
00194 {
00195     m_relatedDataEditable = set;
00196 }
00197 
00198 bool KexiTableViewColumn::acceptsFirstChar(const QChar& ch) const
00199 {
00200     // the field we're looking at can be related to "visible lookup column" 
00201     // if lookup column is present
00202     KexiDB::Field *visibleField = visibleLookupColumnInfo 
00203         ? visibleLookupColumnInfo->field : m_field;
00204     if (visibleField->isNumericType()) {
00205         if (ch=='.' || ch==',')
00206             return visibleField->isFPNumericType();
00207         if (ch=='-')
00208              return !visibleField->isUnsigned();
00209         if (ch=='+' || (ch>='0' && ch<='9'))
00210             return true;
00211         return false;
00212     }
00213 
00214     switch (visibleField->type()) {
00215     case KexiDB::Field::Boolean:
00216         return false;
00217     case KexiDB::Field::Date:
00218     case KexiDB::Field::DateTime:
00219     case KexiDB::Field::Time:
00220         return ch>='0' && ch<='9';
00221     default:;
00222     }
00223     return true;
00224 }
00225 
00226 
00227 //------------------------------------------------------
00228 
00229 KexiTableViewData::KexiTableViewData()
00230     : QObject()
00231     , KexiTableViewDataBase()
00232 {
00233     init();
00234 }
00235 
00236 // db-aware ctor
00237 KexiTableViewData::KexiTableViewData(KexiDB::Cursor *c)
00238     : QObject()
00239     , KexiTableViewDataBase()
00240 {
00241     init();
00242     m_cursor = c;
00243     m_containsROWIDInfo = m_cursor->containsROWIDInfo();
00244     if (m_cursor && m_cursor->query()) {
00245         const KexiDB::QuerySchema::FieldsExpandedOptions fieldsExpandedOptions
00246             = m_containsROWIDInfo ? KexiDB::QuerySchema::WithInternalFieldsAndRowID 
00247             : KexiDB::QuerySchema::WithInternalFields;
00248         m_itemSize = m_cursor->query()->fieldsExpanded( fieldsExpandedOptions ).count();
00249     }
00250     else
00251         m_itemSize = columns.count()+(m_containsROWIDInfo?1:0);
00252 
00253     // Allocate KexiTableViewColumn objects for each visible query column
00254     const KexiDB::QueryColumnInfo::Vector fields = m_cursor->query()->fieldsExpanded();
00255     const uint fieldsCount = fields.count();
00256     for (uint i=0;i < fieldsCount;i++) {
00257         KexiDB::QueryColumnInfo *ci = fields[i];
00258         if (ci->visible) {
00259             KexiDB::QueryColumnInfo *visibleLookupColumnInfo = 0;
00260             if (ci->indexForVisibleLookupValue() != -1) {
00261                 //Lookup field is defined
00262                 visibleLookupColumnInfo = m_cursor->query()->expandedOrInternalField( ci->indexForVisibleLookupValue() );
00263                 /* not needed 
00264                 if (visibleLookupColumnInfo) {
00265                     // 2. Create a KexiTableViewData object for each found lookup field
00266                 }*/
00267             }
00268             KexiTableViewColumn* col = new KexiTableViewColumn(*m_cursor->query(), *ci, visibleLookupColumnInfo);
00269             addColumn( col );
00270         }
00271     }
00272 }
00273 
00274 KexiTableViewData::KexiTableViewData(
00275     const QValueList<QVariant> &keys, const QValueList<QVariant> &values,
00276     KexiDB::Field::Type keyType, KexiDB::Field::Type valueType)
00277     : QObject()
00278     , KexiTableViewDataBase()
00279 {
00280     init(keys, values, keyType, valueType);
00281 }
00282 
00283 KexiTableViewData::KexiTableViewData(
00284     KexiDB::Field::Type keyType, KexiDB::Field::Type valueType)
00285 {
00286     const QValueList<QVariant> empty;
00287     init(empty, empty, keyType, valueType);
00288 }
00289 
00290 KexiTableViewData::~KexiTableViewData()
00291 {
00292     emit destroying();
00293     clearInternal();
00294 }
00295 
00296 void KexiTableViewData::init(
00297     const QValueList<QVariant> &keys, const QValueList<QVariant> &values,
00298     KexiDB::Field::Type keyType, KexiDB::Field::Type valueType)
00299 {
00300     init();
00301     KexiDB::Field *keyField = new KexiDB::Field("key", keyType);
00302     keyField->setPrimaryKey(true);
00303     KexiTableViewColumn *keyColumn = new KexiTableViewColumn(*keyField, true);
00304     keyColumn->setVisible(false);
00305     addColumn(keyColumn);
00306 
00307     KexiDB::Field *valueField = new KexiDB::Field("value", valueType);
00308     KexiTableViewColumn *valueColumn = new KexiTableViewColumn(*valueField, true);
00309     addColumn(valueColumn);
00310 
00311     uint cnt = QMIN(keys.count(), values.count());
00312     QValueList<QVariant>::ConstIterator it_keys = keys.constBegin();
00313     QValueList<QVariant>::ConstIterator it_values = values.constBegin();
00314     for (;cnt>0;++it_keys, ++it_values, cnt--) {
00315         KexiTableItem *item = new KexiTableItem(2);
00316         (*item)[0] = (*it_keys);
00317         (*item)[1] = (*it_values);
00318         append( item );
00319     }
00320 }
00321 
00322 void KexiTableViewData::init()
00323 {
00324     m_sortedColumn = 0;
00325     m_realSortedColumn = 0;
00326 //  m_order = 1;
00327     m_order = 0;
00328     m_type = 1;
00329     m_pRowEditBuffer = 0;
00330     m_cursor = 0;
00331     m_readOnly = false;
00332     m_insertingEnabled = true;
00333 
00334     setAutoDelete(true);
00335     columns.setAutoDelete(true);
00336     m_visibleColumnsCount=0;
00337     m_visibleColumnsIDs.resize(100);
00338     m_globalColumnsIDs.resize(100);
00339 
00340     m_autoIncrementedColumn = -2;
00341     m_containsROWIDInfo = false;
00342     m_itemSize = 0;
00343 }
00344 
00345 void KexiTableViewData::deleteLater()
00346 {
00347     m_cursor = 0;
00348     QObject::deleteLater();
00349 }
00350 
00351 void KexiTableViewData::addColumn( KexiTableViewColumn* col )
00352 {
00353 //  if (!col->isDBAware) {
00354 //      if (!m_simpleColumnsByName)
00355 //          m_simpleColumnsByName = new QDict<KexiTableViewColumn>(101);
00356 //      m_simpleColumnsByName->insert(col->caption,col);//for faster lookup
00357 //  }
00358     columns.append( col );
00359     col->m_data = this;
00360     if (m_globalColumnsIDs.size() < columns.count()) {//sanity
00361         m_globalColumnsIDs.resize( m_globalColumnsIDs.size()*2 );
00362     }
00363     if (col->visible()) {
00364         m_visibleColumnsCount++;
00365         if (m_visibleColumnsIDs.size() < m_visibleColumnsCount) {//sanity
00366             m_visibleColumnsIDs.resize( m_visibleColumnsIDs.size()*2 );
00367         }
00368         m_visibleColumnsIDs[ columns.count()-1 ] = m_visibleColumnsCount-1;
00369         m_globalColumnsIDs[ m_visibleColumnsCount-1 ] = columns.count()-1;
00370     }
00371     else {
00372         m_visibleColumnsIDs[ columns.count()-1 ] = -1;
00373     }
00374     m_autoIncrementedColumn = -2; //clear cache;
00375     if (!m_cursor || !m_cursor->query())
00376         m_itemSize = columns.count()+(m_containsROWIDInfo?1:0);
00377 }
00378 
00379 QString KexiTableViewData::dbTableName() const
00380 {
00381     if (m_cursor && m_cursor->query() && m_cursor->query()->masterTable())
00382         return m_cursor->query()->masterTable()->name();
00383     return QString::null;
00384 }
00385 
00386 void KexiTableViewData::setSorting(int column, bool ascending)
00387 {
00388     if (column>=0 && column<(int)columns.count()) {
00389         m_order = (ascending ? 1 : -1);
00390     } 
00391     else {
00392         m_order = 0;
00393         m_sortedColumn = -1;
00394         m_realSortedColumn = -1;
00395         return;
00396     }
00397     // find proper column information for sorting (lookup column points to alternate column with visible data)
00398     const KexiTableViewColumn *tvcol = columns.at(column);
00399     KexiDB::QueryColumnInfo* visibleLookupColumnInfo = tvcol->visibleLookupColumnInfo;
00400     const KexiDB::Field *field = visibleLookupColumnInfo ? visibleLookupColumnInfo->field : tvcol->field();
00401     m_sortedColumn = column;
00402     m_realSortedColumn = tvcol->columnInfo->indexForVisibleLookupValue()!=-1 
00403         ? tvcol->columnInfo->indexForVisibleLookupValue() : m_sortedColumn;
00404 
00405     // setup compare function
00406     const int t = field->type();
00407     if (field->isTextType())
00408         cmpFunc = &KexiTableViewData::cmpStr;
00409     else if (KexiDB::Field::isFPNumericType(t))
00410         cmpFunc = &KexiTableViewData::cmpDouble;
00411     else if (t==KexiDB::Field::BigInteger) {
00412         if (field->isUnsigned())
00413             cmpFunc = &KexiTableViewData::cmpULongLong;
00414         else
00415             cmpFunc = &KexiTableViewData::cmpLongLong;
00416     }
00417     else if (t == KexiDB::Field::Integer && field->isUnsigned())
00418         cmpFunc = &KexiTableViewData::cmpUInt;
00419     else if (t == KexiDB::Field::Boolean || KexiDB::Field::isNumericType(t))
00420         cmpFunc = &KexiTableViewData::cmpInt; //other integers
00421     else if (t == KexiDB::Field::Date)
00422         cmpFunc = &KexiTableViewData::cmpDate;
00423     else if (t == KexiDB::Field::Time)
00424         cmpFunc = &KexiTableViewData::cmpTime;
00425     else if (t == KexiDB::Field::DateTime)
00426         cmpFunc = &KexiTableViewData::cmpDateTime;
00427     else if (t == KexiDB::Field::BLOB)
00429         cmpFunc = &KexiTableViewData::cmpBLOB;
00430     else
00431         cmpFunc = &KexiTableViewData::cmpStr; //anything else
00432 }
00433 
00434 int KexiTableViewData::compareItems(Item item1, Item item2)
00435 {
00436     return ((this->*cmpFunc) (item1, item2));
00437 }
00438 
00440 #define CMP_NULLS(item1, item2) \
00441     m_leftTmp = ((KexiTableItem *)item1)->at(m_realSortedColumn); \
00442     if (m_leftTmp.isNull()) \
00443         return -m_order; \
00444     m_rightTmp = ((KexiTableItem *)item2)->at(m_realSortedColumn); \
00445     if (m_rightTmp.isNull()) \
00446         return m_order
00447 
00448 #define CAST_AND_COMPARE(casting, item1, item2) \
00449     CMP_NULLS(item1, item2); \
00450     if (m_leftTmp.casting() < m_rightTmp.casting()) \
00451         return -m_order; \
00452     if (m_leftTmp.casting() > m_rightTmp.casting()) \
00453         return m_order; \
00454     return 0
00455 
00456 int KexiTableViewData::cmpInt(Item item1, Item item2)
00457 {
00458     CAST_AND_COMPARE(toInt, item1, item2);
00459 }
00460 
00461 int KexiTableViewData::cmpUInt(Item item1, Item item2)
00462 {
00463     CAST_AND_COMPARE(toUInt, item1, item2);
00464 }
00465 
00466 int KexiTableViewData::cmpLongLong(Item item1, Item item2)
00467 {
00468     CAST_AND_COMPARE(toLongLong, item1, item2);
00469 }
00470 
00471 int KexiTableViewData::cmpULongLong(Item item1, Item item2)
00472 {
00473     CAST_AND_COMPARE(toULongLong, item1, item2);
00474 }
00475 
00476 int KexiTableViewData::cmpDouble(Item item1, Item item2)
00477 {
00478     CAST_AND_COMPARE(toDouble, item1, item2);
00479 }
00480 
00481 int KexiTableViewData::cmpDate(Item item1, Item item2)
00482 {
00483     CAST_AND_COMPARE(toDate, item1, item2);
00484 }
00485 
00486 int KexiTableViewData::cmpDateTime(Item item1, Item item2)
00487 {
00488     CAST_AND_COMPARE(toDateTime, item1, item2);
00489 }
00490 
00491 int KexiTableViewData::cmpTime(Item item1, Item item2)
00492 {
00493     CAST_AND_COMPARE(toDate, item1, item2);
00494 }
00495 
00496 int KexiTableViewData::cmpStr(Item item1, Item item2)
00497 {
00498     CMP_NULLS(item1, item2);
00499     const QString &as = m_leftTmp.toString();
00500     const QString &bs = m_rightTmp.toString();
00501 
00502     const QChar *a = as.unicode();
00503     const QChar *b = bs.unicode();
00504 
00505     if ( a == b )
00506         return 0;
00507     if ( a == 0 )
00508         return -1;
00509     if ( b == 0 )
00510         return 1;
00511 
00512     unsigned short au;
00513     unsigned short bu;
00514 
00515     int l=QMIN(as.length(),bs.length());
00516 
00517     au = a->unicode();
00518     bu = b->unicode();
00519     au = (au <= 0x17e ? charTable[au] : 0xffff);
00520     bu = (bu <= 0x17e ? charTable[bu] : 0xffff);
00521 
00522     while (l-- && au == bu)
00523     {
00524         a++,b++;
00525         au = a->unicode();
00526         bu = b->unicode();
00527         au = (au <= 0x17e ? charTable[au] : 0xffff);
00528         bu = (bu <= 0x17e ? charTable[bu] : 0xffff);
00529     }
00530 
00531     if ( l==-1 )
00532         return m_order*(as.length()-bs.length());
00533 
00534     return m_order*(au-bu);
00535 }
00536 
00537 int KexiTableViewData::cmpBLOB(Item item1, Item item2)
00538 {
00539     CMP_NULLS(item1, item2);
00540     return m_leftTmp.toByteArray().size() - m_rightTmp.toByteArray().size();
00541 }
00542 
00543 void KexiTableViewData::setReadOnly(bool set)
00544 {
00545     if (m_readOnly == set)
00546         return;
00547     m_readOnly = set;
00548     if (m_readOnly)
00549         setInsertingEnabled(false);
00550 }
00551 
00552 void KexiTableViewData::setInsertingEnabled(bool set)
00553 {
00554     if (m_insertingEnabled == set)
00555         return;
00556     m_insertingEnabled = set;
00557     if (m_insertingEnabled)
00558         setReadOnly(false);
00559 }
00560 
00561 void KexiTableViewData::clearRowEditBuffer()
00562 {
00563     //init row edit buffer
00564     if (!m_pRowEditBuffer)
00565         m_pRowEditBuffer = new KexiDB::RowEditBuffer(isDBAware());
00566     else
00567         m_pRowEditBuffer->clear();
00568 }
00569 
00570 bool KexiTableViewData::updateRowEditBufferRef(KexiTableItem *item, 
00571     int colnum, KexiTableViewColumn* col, QVariant& newval, bool allowSignals,
00572     QVariant *visibleValueForLookupField)
00573 {
00574     m_result.clear();
00575     if (allowSignals)
00576         emit aboutToChangeCell(item, colnum, newval, &m_result);
00577     if (!m_result.success)
00578         return false;
00579 
00580     kdDebug() << "KexiTableViewData::updateRowEditBufferRef() column #" 
00581         << colnum << " = " << newval.toString() << endl;
00582     if (!col) {
00583         kdWarning() << "KexiTableViewData::updateRowEditBufferRef(): column #" 
00584             << colnum << " not found! col==0" << endl;
00585         return false;
00586     }
00587     if (!m_pRowEditBuffer)
00588         m_pRowEditBuffer = new KexiDB::RowEditBuffer(isDBAware());
00589     if (m_pRowEditBuffer->isDBAware()) {
00590         if (!(col->columnInfo)) {
00591             kdWarning() << "KexiTableViewData::updateRowEditBufferRef(): column #" 
00592                 << colnum << " not found!" << endl;
00593             return false;
00594         }
00595         m_pRowEditBuffer->insert( *col->columnInfo, newval);
00596 
00597         if (col->visibleLookupColumnInfo && visibleValueForLookupField) {
00598             //this is value for lookup table: update visible value as well
00599             m_pRowEditBuffer->insert( *col->visibleLookupColumnInfo, *visibleValueForLookupField);
00600         }
00601         return true;
00602     }
00603     if (!(col->field())) {
00604         kdDebug() << "KexiTableViewData::updateRowEditBufferRef(): column #" << colnum<<" not found!" << endl;
00605         return false;
00606     }
00607     //not db-aware:
00608     const QString colname = col->field()->name();
00609     if (colname.isEmpty()) {
00610         kdDebug() << "KexiTableViewData::updateRowEditBufferRef(): column #" << colnum<<" not found!" << endl;
00611         return false;
00612     }
00613     m_pRowEditBuffer->insert(colname, newval);
00614     return true;
00615 }
00616 
00617 //get a new value (if present in the buffer), or the old one, otherwise
00618 //(taken here for optimization)
00619 #define GET_VALUE if (!val) { \
00620     val = m_cursor \
00621                 ? m_pRowEditBuffer->at( *it_f.current()->columnInfo, true /* useDefaultValueIfPossible */ ) \
00622                 : m_pRowEditBuffer->at( *f ); \
00623     if (!val) \
00624         val = &(*it_r); /* get old value */ \
00625     }
00626 
00628 bool KexiTableViewData::saveRow(KexiTableItem& item, bool insert, bool repaint)
00629 {
00630     if (!m_pRowEditBuffer)
00631         return true; //nothing to do
00632 
00633     //check constraints:
00634     //-check if every NOT NULL and NOT EMPTY field is filled
00635     KexiTableViewColumn::ListIterator it_f(columns);
00636     KexiDB::RowData::ConstIterator it_r = item.constBegin();
00637     int col = 0;
00638     const QVariant *val;
00639     for (;it_f.current() && it_r!=item.constEnd();++it_f,++it_r,col++) {
00640         KexiDB::Field *f = it_f.current()->field();
00641         val = 0;
00642         if (f->isNotNull()) {
00643             GET_VALUE;
00644             //check it
00645             if (val->isNull() && !f->isAutoIncrement()) {
00646                 //NOT NULL violated
00647                 m_result.msg = i18n("\"%1\" column requires a value to be entered.")
00648                     .arg(f->captionOrName()) + "\n\n" + Kexi::msgYouCanImproveData();
00649                 m_result.desc = i18n("The column's constraint is declared as NOT NULL.");
00650                 m_result.column = col;
00651                 return false;
00652             }
00653         }
00654         if (f->isNotEmpty()) {
00655             GET_VALUE;
00656             if (!f->isAutoIncrement() && (val->isNull() || KexiDB::isEmptyValue( f, *val ))) {
00657                 //NOT EMPTY violated
00658                 m_result.msg = i18n("\"%1\" column requires a value to be entered.")
00659                     .arg(f->captionOrName()) + "\n\n" + Kexi::msgYouCanImproveData();
00660                 m_result.desc = i18n("The column's constraint is declared as NOT EMPTY.");
00661                 m_result.column = col;
00662                 return false;
00663             }
00664         }
00665     }
00666 
00667     if (m_cursor) {//db-aware
00668         if (insert) {
00669             if (!m_cursor->insertRow( static_cast<KexiDB::RowData&>(item), *m_pRowEditBuffer, 
00670                 m_containsROWIDInfo/*also retrieve ROWID*/ )) 
00671             {
00672                 m_result.msg = i18n("Row inserting failed.") + "\n\n" 
00673                     + Kexi::msgYouCanImproveData();
00674                 KexiDB::getHTMLErrorMesage(m_cursor, &m_result);
00675 
00676 /*          if (desc)
00677             *desc = 
00678 js: TODO: use KexiMainWindowImpl::showErrorMessage(const QString &title, KexiDB::Object *obj)
00679     after it will be moved somewhere to kexidb (this will require moving other 
00680       showErrorMessage() methods from KexiMainWindowImpl to libkexiutils....)
00681     then: just call: *desc = KexiDB::errorMessage(m_cursor);
00682 */
00683                 return false;
00684             }
00685         }
00686         else { // row updating
00687 //          if (m_containsROWIDInfo)
00688 //              ROWID = item[columns.count()].toULongLong();
00689             if (!m_cursor->updateRow( static_cast<KexiDB::RowData&>(item), *m_pRowEditBuffer,
00690                     m_containsROWIDInfo/*use ROWID*/))
00691             {
00692                 m_result.msg = i18n("Row changing failed.") + "\n\n" + Kexi::msgYouCanImproveData();
00694                 KexiDB::getHTMLErrorMesage(m_cursor, m_result.desc);
00695                 return false;
00696             }
00697         }
00698     }
00699     else {//not db-aware version
00700         KexiDB::RowEditBuffer::SimpleMap b = m_pRowEditBuffer->simpleBuffer();
00701         for (KexiDB::RowEditBuffer::SimpleMap::ConstIterator it = b.constBegin();it!=b.constEnd();++it) {
00702             uint i=0;
00703             for (KexiTableViewColumn::ListIterator it2(columns);it2.current();++it2, i++) {
00704                 if (it2.current()->field()->name()==it.key()) {
00705                     kdDebug() << it2.current()->field()->name()<< ": "<<item[i].toString()<<" -> "<<it.data().toString()<<endl;
00706                     item[i] = it.data();
00707                 }
00708             }
00709         }
00710     }
00711     
00712     m_pRowEditBuffer->clear();
00713 
00714     if (repaint)
00715         emit rowRepaintRequested(item);
00716     return true;
00717 }
00718 
00719 bool KexiTableViewData::saveRowChanges(KexiTableItem& item, bool repaint)
00720 {
00721     kdDebug() << "KexiTableViewData::saveRowChanges()..." << endl;
00722     m_result.clear();
00723     emit aboutToUpdateRow(&item, m_pRowEditBuffer, &m_result);
00724     if (!m_result.success)
00725         return false;
00726 
00727     if (saveRow(item, false /*update*/, repaint)) {
00728         emit rowUpdated(&item);
00729         return true;
00730     }
00731     return false;
00732 }
00733 
00734 bool KexiTableViewData::saveNewRow(KexiTableItem& item, bool repaint)
00735 {
00736     kdDebug() << "KexiTableViewData::saveNewRow()..." << endl;
00737     m_result.clear();
00738     emit aboutToInsertRow(&item, &m_result, repaint);
00739     if (!m_result.success)
00740         return false;
00741     
00742     if (saveRow(item, true /*insert*/, repaint)) {
00743         emit rowInserted(&item, repaint);
00744         return true;
00745     }
00746     return false;
00747 }
00748 
00749 bool KexiTableViewData::deleteRow(KexiTableItem& item, bool repaint)
00750 {
00751     m_result.clear();
00752     emit aboutToDeleteRow(item, &m_result, repaint);
00753     if (!m_result.success)
00754         return false;
00755 
00756     if (m_cursor) {//db-aware
00757         m_result.success = false;
00758         if (!m_cursor->deleteRow( static_cast<KexiDB::RowData&>(item), m_containsROWIDInfo/*use ROWID*/ )) {
00759             m_result.msg = i18n("Row deleting failed.");
00760 /*js: TODO: use KexiDB::errorMessage() for description (desc) as in KexiTableViewData::saveRow() */
00761             KexiDB::getHTMLErrorMesage(m_cursor, &m_result);
00762             m_result.success = false;
00763             return false;
00764         }
00765     }
00766 
00767     if (!removeRef(&item)) {
00768         //aah - this shouldn't be!
00769         kdWarning() << "KexiTableViewData::deleteRow(): !removeRef() - IMPL. ERROR?" << endl;
00770         m_result.success = false;
00771         return false;
00772     }
00773     emit rowDeleted();
00774     return true;
00775 }
00776 
00777 void KexiTableViewData::deleteRows( const QValueList<int> &rowsToDelete, bool repaint )
00778 {
00779     Q_UNUSED( repaint );
00780 
00781     if (rowsToDelete.isEmpty())
00782         return;
00783     int last_r=0;
00784     first();
00785     for (QValueList<int>::ConstIterator r_it = rowsToDelete.constBegin(); r_it!=rowsToDelete.constEnd(); ++r_it) {
00786         for (; last_r<(*r_it); last_r++) {
00787             next();
00788         }
00789         remove();
00790         last_r++;
00791     }
00792 //DON'T CLEAR BECAUSE KexiTableViewPropertyBuffer will clear BUFFERS!
00793 //-->   emit reloadRequested(); //! \todo more effective?
00794     emit rowsDeleted( rowsToDelete );
00795 }
00796 
00797 void KexiTableViewData::insertRow(KexiTableItem& item, uint index, bool repaint)
00798 {
00799     if (!insert( index = QMIN(index, count()), &item ))
00800         return;
00801     emit rowInserted(&item, index, repaint);
00802 }
00803 
00804 void KexiTableViewData::clearInternal()
00805 {
00806     clearRowEditBuffer();
00807 //  qApp->processEvents( 1 );
00808 //TODO: this is time consuming: find better data model
00809 //  KexiTableViewDataBase::clear();
00810     const uint c = count();
00811     for (uint i=0; i<c; i++) {
00812         removeLast();
00813 #ifndef KEXI_NO_PROCESS_EVENTS
00814         if (i % 1000 == 0)
00815             qApp->processEvents( 1 );
00816 #endif
00817     }
00818 }
00819 
00820 bool KexiTableViewData::deleteAllRows(bool repaint)
00821 {
00822     clearInternal();
00823 
00824     bool res = true;
00825     if (m_cursor) {
00826         //db-aware
00827         res = m_cursor->deleteAllRows();
00828     }
00829 
00830     if (repaint)
00831         emit reloadRequested();
00832     return res;
00833 }
00834 
00835 int KexiTableViewData::autoIncrementedColumn()
00836 {
00837     if (m_autoIncrementedColumn==-2) {
00838         //find such a column
00839         m_autoIncrementedColumn = 0;
00840         KexiTableViewColumn::ListIterator it(columns);
00841         for (; it.current(); ++it, m_autoIncrementedColumn++) {
00842             if (it.current()->field()->isAutoIncrement())
00843                 break;
00844         }
00845         if (!it.current())
00846             m_autoIncrementedColumn = -1;
00847     }
00848     return m_autoIncrementedColumn;
00849 }
00850 
00851 void KexiTableViewData::preloadAllRows()
00852 {
00853     if (!m_cursor)
00854         return;
00855 
00856     //const uint fcount = m_cursor->fieldCount() + (m_containsROWIDInfo ? 1 : 0);
00857     m_cursor->moveFirst();
00858     for (int i=0;!m_cursor->eof();i++) {
00859         KexiTableItem *item = new KexiTableItem(0);
00860         m_cursor->storeCurrentRow(*item);
00861 //      item->debug();
00862         append( item );
00863         m_cursor->moveNext();
00864 #ifndef KEXI_NO_PROCESS_EVENTS
00865         if ((i % 1000) == 0)
00866             qApp->processEvents( 1 );
00867 #endif
00868     }
00869 }
00870 
00871 bool KexiTableViewData::isReadOnly() const
00872 {
00873     return m_readOnly || (m_cursor && m_cursor->connection()->isReadOnly());
00874 }
00875 
00876 #include "kexitableviewdata.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys