kexi

kexidataprovider.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kexidataprovider.h"
00021 
00022 #include <qwidget.h>
00023 #include <qobjectlist.h>
00024 
00025 #include <kdebug.h>
00026 #include <klocale.h>
00027 
00028 #include <widget/tableview/kexitableitem.h>
00029 #include <widget/tableview/kexitableviewdata.h>
00030 #include <widget/tableview/kexicomboboxbase.h>
00031 #include <kexidb/queryschema.h>
00032 #include <kexiutils/utils.h>
00033 
00034 #include "widgets/kexidbform.h"
00035 
00036 KexiFormDataProvider::KexiFormDataProvider()
00037  : KexiDataItemChangesListener()
00038  , m_mainWidget(0)
00039  , m_duplicatedItems(0)
00040  , m_disableFillDuplicatedDataItems(false)
00041 {
00042 }
00043 
00044 KexiFormDataProvider::~KexiFormDataProvider()
00045 {
00046     delete m_duplicatedItems;
00047 }
00048 
00049 void KexiFormDataProvider::setMainDataSourceWidget(QWidget* mainWidget)
00050 {
00051     m_mainWidget = mainWidget;
00052     m_dataItems.clear();
00053     m_usedDataSources.clear();
00054     m_fieldNumbersForDataItems.clear();
00055     if (!m_mainWidget)
00056         return;
00057 
00058     //find widgets whose will work as data items
00059     QObjectList *l = m_mainWidget->queryList( "QWidget" );
00060     QObjectListIt it( *l );
00061     QObject *obj;
00062     QDict<char> tmpSources;
00063     for ( ; (obj = it.current()) != 0; ++it ) {
00064         KexiFormDataItemInterface* const formDataItem = dynamic_cast<KexiFormDataItemInterface*>(obj);
00065         if (!formDataItem)
00066             continue;
00067         if (formDataItem->parentInterface()) //item with parent interface: collect parent instead...
00068             continue;
00069 #if 0 
00070         KexiDBForm *dbForm = KexiUtils::findParent<KexiDBForm>(obj, "KexiDBForm"); //form's surface...
00071         if (dbForm!=m_mainWidget) //only set data for this form's data items
00072             continue;
00073 #else
00074         //tmp: reject widgets within subforms
00075         if (KexiUtils::findParent<KexiDBForm>(obj, "KexiDBSubForm"))
00076             continue;
00077 #endif
00078         QString dataSource( formDataItem->dataSource().lower() );
00079         if (dataSource.isEmpty())
00080             continue;
00081         kexipluginsdbg << obj->name() << endl;
00082         m_dataItems.append( formDataItem );
00083         formDataItem->installListener( this );
00084         tmpSources.replace( dataSource, (char*)1 );
00085     }
00086     delete l;
00087     //now we've got a set (unique list) of field names in tmpSources
00088     //remember it in m_usedDataSources
00089     for (QDictIterator<char> it(tmpSources); it.current(); ++it) {
00090         m_usedDataSources += it.currentKey();
00091     }
00092 }
00093 
00094 void KexiFormDataProvider::fillDataItems(KexiTableItem& row, bool cursorAtNewRow)
00095 {
00096     kexipluginsdbg << "KexiFormDataProvider::fillDataItems() cnt=" << row.count() << endl;
00097     for (KexiFormDataItemInterfaceToIntMap::ConstIterator it = m_fieldNumbersForDataItems.constBegin(); 
00098         it!=m_fieldNumbersForDataItems.constEnd(); ++it)
00099     {
00100         KexiFormDataItemInterface *itemIface = it.key();
00101         if (!itemIface->columnInfo()) {
00102             kexipluginsdbg << "KexiFormDataProvider::fillDataItems(): itemIface->columnInfo() == 0" << endl;
00103             continue;
00104         }
00105         //1. Is this a value with a combo box (lookup)?
00106         int indexForVisibleLookupValue = itemIface->columnInfo()->indexForVisibleLookupValue();
00107         if (indexForVisibleLookupValue<0 && indexForVisibleLookupValue>=(int)row.count()) //sanity
00108             indexForVisibleLookupValue = -1; //no
00109         const QVariant value(row.at(it.data()));
00110         QVariant visibleLookupValue;
00111         if (indexForVisibleLookupValue!=-1)
00112             visibleLookupValue = row.at(indexForVisibleLookupValue);
00113         kexipluginsdbg << "fill data of '" << itemIface->dataSource() <<  "' at idx=" << it.data() 
00114             << " data=" << value << (indexForVisibleLookupValue!=-1 
00115                 ? QString(" SPECIAL: indexForVisibleLookupValue=%1 visibleValue=%2")
00116                     .arg(indexForVisibleLookupValue).arg(visibleLookupValue.toString())
00117                 : QString::null)
00118             << endl;
00119         const bool displayDefaultValue = cursorAtNewRow && (value.isNull() && visibleLookupValue.isNull())
00120             && !itemIface->columnInfo()->field->defaultValue().isNull() 
00121             && !itemIface->columnInfo()->field->isAutoIncrement(); //no value to set but there is default value defined
00122         itemIface->setValue( 
00123             displayDefaultValue ? itemIface->columnInfo()->field->defaultValue() : value,
00124             QVariant(), /*add*/false, 
00126             indexForVisibleLookupValue==-1 ? 0 : &visibleLookupValue //pass visible value if available
00127         );
00128         // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off)
00129         if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue)
00130             itemIface->setDisplayDefaultValue( dynamic_cast<QWidget*>(itemIface), displayDefaultValue );
00131     }
00132 }
00133 
00134 void KexiFormDataProvider::fillDuplicatedDataItems(
00135     KexiFormDataItemInterface* item, const QVariant& value)
00136 {
00137     if (m_disableFillDuplicatedDataItems)
00138         return;
00139     if (!m_duplicatedItems) {
00140         //build (once) a set of duplicated data items (having the same fields assigned)
00141         //so we can later check if an item is duplicated with a cost of o(1)
00142         QMap<KexiDB::Field*,int> tmpDuplicatedItems;
00143         QMapIterator<KexiDB::Field*,int> it_dup;
00144         for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
00145             if (!it.current()->columnInfo() || !it.current()->columnInfo()->field)
00146                 continue;
00147             kdDebug() << " ** " << it.current()->columnInfo()->field->name() << endl;
00148             it_dup = tmpDuplicatedItems.find( it.current()->columnInfo()->field );
00149             uint count;
00150             if (it_dup==tmpDuplicatedItems.end())
00151                 count = 0;
00152             else
00153                 count = it_dup.data();
00154             tmpDuplicatedItems.insert( it.current()->columnInfo()->field, ++count );
00155         }
00156         m_duplicatedItems = new QPtrDict<char>(101);
00157         for (it_dup = tmpDuplicatedItems.begin(); it_dup!=tmpDuplicatedItems.end(); ++it_dup) {
00158             if (it_dup.data() > 1) {
00159                 m_duplicatedItems->insert( it_dup.key(), (char*)1 );
00160                 kexipluginsdbg << "duplicated item: " << static_cast<KexiDB::Field*>(it_dup.key())->name() 
00161                     << " (" << it_dup.data() << " times)" << endl;
00162             }
00163         }
00164     }
00165     if (item->columnInfo() && m_duplicatedItems->find( item->columnInfo()->field )) {
00166         for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
00167             if (it.current()!=item && item->columnInfo()->field == it.current()->columnInfo()->field) {
00168                 kexipluginsdbg << "- setting a copy of value for item '" 
00169                     << dynamic_cast<QObject*>(it.current())->name() << "' == " << value << endl;
00170                 it.current()->setValue( value );
00171             }
00172         }
00173     }
00174 }
00175 
00176 void KexiFormDataProvider::valueChanged(KexiDataItemInterface* item)
00177 {
00178     Q_UNUSED( item );
00179 }
00180 
00181 bool KexiFormDataProvider::cursorAtNewRow() const
00182 {
00183     return false;
00184 }
00185 
00186 void KexiFormDataProvider::invalidateDataSources( const QDict<char>& invalidSources,
00187  KexiDB::QuerySchema* query)
00188 {
00189     //fill m_fieldNumbersForDataItems mapping from data item to field number
00190     //(needed for fillDataItems)
00191     KexiDB::QueryColumnInfo::Vector fieldsExpanded;
00192 //  uint dataFieldsCount; // == fieldsExpanded.count() if query is available or else == m_dataItems.count()
00193 
00194     if (query) {
00195         fieldsExpanded = query->fieldsExpanded( KexiDB::QuerySchema::WithInternalFields );
00196 //      dataFieldsCount = fieldsExpanded.count();
00197         QMap<KexiDB::QueryColumnInfo*,int> columnsOrder( query->columnsOrder() );
00198         for (QMapConstIterator<KexiDB::QueryColumnInfo*,int> it = columnsOrder.constBegin(); it!=columnsOrder.constEnd(); ++it) {
00199             kexipluginsdbg << "query->columnsOrder()[ " << it.key()->field->name() << " ] = " << it.data() << endl;
00200         }
00201         for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
00202             KexiFormDataItemInterface *item = it.current();
00203             KexiDB::QueryColumnInfo* ci = query->columnInfo( it.current()->dataSource() );
00204             int index = ci ? columnsOrder[ ci ] : -1;
00205             kexipluginsdbg << "query->columnsOrder()[ " << (ci ? ci->field->name() : "") << " ] = " << index 
00206                 << " (dataSource: " << item->dataSource() << ", name=" << dynamic_cast<QObject*>(item)->name() << ")" << endl;
00207             if (index!=-1 && !m_fieldNumbersForDataItems[ item ])
00208                 m_fieldNumbersForDataItems.insert( item, index );
00209     //todo
00210     //WRONG: not only used data sources can be fetched!
00211     //          m_fieldNumbersForDataItems.insert( it.current(), 
00212     //              m_usedDataSources.findIndex(it.current()->dataSource().lower()) );
00213         }
00214     }
00215     else {
00216 //      dataFieldsCount = m_dataItems.count();
00217     }
00218 
00219 #if 0 //moved down
00220     //in 'newIndices' let's collect new indices for every data source
00221     foreach(QValueList<uint>::ConstIterator, it, invalidSources) {
00222         //all previous indices have corresponding data source
00223 //      for (; i < (*it); i++) {
00224 //          newIndices[i] = number++;
00225             //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl;
00226 //      }
00227         //this index have no corresponding data source
00228 //      newIndices[i]=-1;
00229         KexiFormDataItemInterface *item = m_dataItems.at( *it );
00230         if (item)
00231             item->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") );
00232         m_dataItems.remove(*it);
00233         kexipluginsdbg << "invalidateDataSources(): " << (*it) << " -> " << -1 << endl;
00234 //      i++;
00235     }
00236 #endif
00237     //fill remaining part of the vector
00238 //  for (; i < dataFieldsCount; i++) { //m_dataItems.count(); i++) {
00239         //newIndices[i] = number++;
00240         //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl;
00241     //}
00242 
00243 #if 0
00244     //recreate m_fieldNumbersForDataItems and mark widgets with invalid data sources
00245     KexiFormDataItemInterfaceToIntMap newFieldNumbersForDataItems;
00246     foreach(KexiFormDataItemInterfaceToIntMap::ConstIterator, it, m_fieldNumbersForDataItems) {
00247         bool ok;
00248         const int newIndex = newIndices.at( it.data(), &ok );
00249         if (ok && newIndex!=-1) {
00250             kexipluginsdbg << "invalidateDataSources(): " << it.key()->dataSource() << ": " << it.data() << " -> " << newIndex << endl;
00251             newFieldNumbersForDataItems.replace(it.key(), newIndex);
00252         }
00253         else {
00254             kexipluginsdbg << "invalidateDataSources(): removing " << it.key()->dataSource() << endl;
00255             m_dataItems.remove(it.key());
00256             it.key()->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") );
00257         }
00258     }
00259 #endif
00260 //  m_fieldNumbersForDataItems = newFieldNumbersForDataItems;
00261 
00262     //update data sources set (some of them may be removed)
00263     QDict<char> tmpUsedDataSources(1013);
00264 
00265     if (query)
00266         query->debug();
00267 
00268     //if (query && m_dataItems.count()!=query->fieldCount()) {
00269     //  kdWarning() << "KexiFormDataProvider::invalidateDataSources(): m_dataItems.count()!=query->fieldCount() ("
00270     //   << m_dataItems.count() << "," << query->fieldCount() << ")" << endl;
00271     //}
00272     //i = 0;
00273     m_disableFillDuplicatedDataItems = true; // temporary disable fillDuplicatedDataItems()
00274                                              // because setColumnInfo() can activate it
00275     for (QPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current();) {
00276         KexiFormDataItemInterface * item = it.current();
00277         if (invalidSources[ item->dataSource().lower() ]) {
00278             item->setInvalidState( QString::fromLatin1("#") + i18n("NAME") + QString::fromLatin1("?") );
00279             m_dataItems.remove(item);
00280             continue;
00281         }
00282         uint fieldNumber = m_fieldNumbersForDataItems[ item ];
00283         if (query) {
00284             KexiDB::QueryColumnInfo *ci = fieldsExpanded[fieldNumber];
00285             item->setColumnInfo(ci);
00286             kexipluginsdbg << "- item=" << dynamic_cast<QObject*>(item)->name() 
00287                 << " dataSource=" << item->dataSource()
00288                 << " field=" << ci->field->name() << endl;
00289             const int indexForVisibleLookupValue = ci->indexForVisibleLookupValue();
00290             if (-1 != indexForVisibleLookupValue && indexForVisibleLookupValue < (int)fieldsExpanded.count()) {
00291                 //there's lookup column defined: set visible column as well
00292                 KexiDB::QueryColumnInfo *visibleColumnInfo = fieldsExpanded[ indexForVisibleLookupValue ];
00293                 if (visibleColumnInfo) {
00294                     item->setVisibleColumnInfo( visibleColumnInfo );
00295                     if (dynamic_cast<KexiComboBoxBase*>(item) && m_mainWidget
00296                         && dynamic_cast<KexiComboBoxBase*>(item)->internalEditor()) {
00297                         // m_mainWidget (dbform) should filter the (just created using setVisibleColumnInfo()) 
00298                         // combo box' internal editor (actually, only if the combo is in 'editable' mode)
00299                         dynamic_cast<KexiComboBoxBase*>(item)->internalEditor()->installEventFilter(m_mainWidget);
00300                     }
00301                     kexipluginsdbg << " ALSO SET visibleColumn=" << visibleColumnInfo->debugString() 
00302                         << "\n at position " << indexForVisibleLookupValue << endl;
00303                 }
00304             }
00305         }
00306         tmpUsedDataSources.replace( item->dataSource().lower(), (char*)1 );
00307         ++it;
00308     }
00309     m_disableFillDuplicatedDataItems = false;
00310     m_usedDataSources.clear();
00311     foreach_list(QDictIterator<char>, it, tmpUsedDataSources) {
00312         m_usedDataSources += it.currentKey();
00313     }
00314 }
KDE Home | KDE Accessibility Home | Description of Access Keys