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