/* This file is part of the KDE project Copyright (C) 2005-2006 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexidataprovider.h" #include #include #include #include #include #include #include #include #include #include "widgets/kexidbform.h" KexiFormDataProvider::KexiFormDataProvider() : KexiDataItemChangesListener() , m_mainWidget(0) , m_duplicatedItems(0) , m_disableFillDuplicatedDataItems(false) { } KexiFormDataProvider::~KexiFormDataProvider() { delete m_duplicatedItems; } void KexiFormDataProvider::setMainDataSourceWidget(TQWidget* mainWidget) { m_mainWidget = mainWidget; m_dataItems.clear(); m_usedDataSources.clear(); m_fieldNumbersForDataItems.clear(); if (!m_mainWidget) return; //find widgets whose will work as data items TQObjectList *l = m_mainWidget->queryList( TQWIDGET_OBJECT_NAME_STRING ); TQObjectListIt it( *l ); TQObject *obj; TQDict tmpSources; for ( ; (obj = it.current()) != 0; ++it ) { KexiFormDataItemInterface* const formDataItem = dynamic_cast(obj); if (!formDataItem) continue; if (formDataItem->parentInterface()) //item with parent interface: collect parent instead... continue; #if 0 //! @todo reenable when subform is moved to KexiDBForm KexiDBForm *dbForm = KexiUtils::findParent(obj, "KexiDBForm"); //form's surface... if (dbForm!=m_mainWidget) //only set data for this form's data items continue; #else //tmp: reject widgets within subforms if (KexiUtils::findParent(obj, "KexiDBSubForm")) continue; #endif TQString dataSource( formDataItem->dataSource().lower() ); if (dataSource.isEmpty()) continue; kexipluginsdbg << obj->name() << endl; m_dataItems.append( formDataItem ); formDataItem->installListener( this ); tmpSources.replace( dataSource, (char*)1 ); } delete l; //now we've got a set (unique list) of field names in tmpSources //remember it in m_usedDataSources for (TQDictIterator it(tmpSources); it.current(); ++it) { m_usedDataSources += it.currentKey(); } } void KexiFormDataProvider::fillDataItems(KexiTableItem& row, bool cursorAtNewRow) { kexipluginsdbg << "KexiFormDataProvider::fillDataItems() cnt=" << row.count() << endl; for (KexiFormDataItemInterfaceToIntMap::ConstIterator it = m_fieldNumbersForDataItems.constBegin(); it!=m_fieldNumbersForDataItems.constEnd(); ++it) { KexiFormDataItemInterface *itemIface = it.key(); if (!itemIface->columnInfo()) { kexipluginsdbg << "KexiFormDataProvider::fillDataItems(): itemIface->columnInfo() == 0" << endl; continue; } //1. Is this a value with a combo box (lookup)? int indexForVisibleLookupValue = itemIface->columnInfo()->indexForVisibleLookupValue(); if (indexForVisibleLookupValue<0 && indexForVisibleLookupValue>=(int)row.count()) //sanity indexForVisibleLookupValue = -1; //no const TQVariant value(row.at(it.data())); TQVariant visibleLookupValue; if (indexForVisibleLookupValue!=-1 && (int)row.size()>indexForVisibleLookupValue) visibleLookupValue = row.at(indexForVisibleLookupValue); kexipluginsdbg << "fill data of '" << itemIface->dataSource() << "' at idx=" << it.data() << " data=" << value << (indexForVisibleLookupValue!=-1 ? TQString(" SPECIAL: indexForVisibleLookupValue=%1 visibleValue=%2") .arg(indexForVisibleLookupValue).arg(visibleLookupValue.toString()) : TQString()) << endl; const bool displayDefaultValue = cursorAtNewRow && (value.isNull() && visibleLookupValue.isNull()) && !itemIface->columnInfo()->field->defaultValue().isNull() && !itemIface->columnInfo()->field->isAutoIncrement(); //no value to set but there is default value defined itemIface->setValue( displayDefaultValue ? itemIface->columnInfo()->field->defaultValue() : value, TQVariant(), /*add*/ /*!remove old*/false, indexForVisibleLookupValue==-1 ? 0 : &visibleLookupValue //pass visible value if available ); // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off) if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue) itemIface->setDisplayDefaultValue( dynamic_cast(itemIface), displayDefaultValue ); } } void KexiFormDataProvider::fillDuplicatedDataItems( KexiFormDataItemInterface* item, const TQVariant& value) { if (m_disableFillDuplicatedDataItems) return; if (!m_duplicatedItems) { //build (once) a set of duplicated data items (having the same fields assigned) //so we can later check if an item is duplicated with a cost of o(1) TQMap tmpDuplicatedItems; TQMapIterator it_dup; for (TQPtrListIterator it(m_dataItems); it.current(); ++it) { if (!it.current()->columnInfo() || !it.current()->columnInfo()->field) continue; kdDebug() << " ** " << it.current()->columnInfo()->field->name() << endl; it_dup = tmpDuplicatedItems.find( it.current()->columnInfo()->field ); uint count; if (it_dup==tmpDuplicatedItems.end()) count = 0; else count = it_dup.data(); tmpDuplicatedItems.insert( it.current()->columnInfo()->field, ++count ); } m_duplicatedItems = new TQPtrDict(101); for (it_dup = tmpDuplicatedItems.begin(); it_dup!=tmpDuplicatedItems.end(); ++it_dup) { if (it_dup.data() > 1) { m_duplicatedItems->insert( it_dup.key(), (char*)1 ); kexipluginsdbg << "duplicated item: " << static_cast(it_dup.key())->name() << " (" << it_dup.data() << " times)" << endl; } } } if (item->columnInfo() && m_duplicatedItems->find( item->columnInfo()->field )) { for (TQPtrListIterator it(m_dataItems); it.current(); ++it) { if (it.current()!=item && item->columnInfo()->field == it.current()->columnInfo()->field) { kexipluginsdbg << "- setting a copy of value for item '" << dynamic_cast(it.current())->name() << "' == " << value << endl; it.current()->setValue( value ); } } } } void KexiFormDataProvider::valueChanged(KexiDataItemInterface* item) { Q_UNUSED( item ); } bool KexiFormDataProvider::cursorAtNewRow() const { return false; } void KexiFormDataProvider::invalidateDataSources( const TQDict& invalidSources, KexiDB::QuerySchema* query) { //fill m_fieldNumbersForDataItems mapping from data item to field number //(needed for fillDataItems) KexiDB::QueryColumnInfo::Vector fieldsExpanded; // uint dataFieldsCount; // == fieldsExpanded.count() if query is available or else == m_dataItems.count() if (query) { fieldsExpanded = query->fieldsExpanded( KexiDB::QuerySchema::WithInternalFields ); // dataFieldsCount = fieldsExpanded.count(); TQMap columnsOrder( query->columnsOrder() ); for (TQMapConstIterator it = columnsOrder.constBegin(); it!=columnsOrder.constEnd(); ++it) { kexipluginsdbg << "query->columnsOrder()[ " << it.key()->field->name() << " ] = " << it.data() << endl; } for (TQPtrListIterator it(m_dataItems); it.current(); ++it) { KexiFormDataItemInterface *item = it.current(); KexiDB::QueryColumnInfo* ci = query->columnInfo( it.current()->dataSource() ); int index = ci ? columnsOrder[ ci ] : -1; kexipluginsdbg << "query->columnsOrder()[ " << (ci ? ci->field->name() : "") << " ] = " << index << " (dataSource: " << item->dataSource() << ", name=" << dynamic_cast(item)->name() << ")" << endl; if (index!=-1 && !m_fieldNumbersForDataItems[ item ]) m_fieldNumbersForDataItems.insert( item, index ); //todo //WRONG: not only used data sources can be fetched! // m_fieldNumbersForDataItems.insert( it.current(), // m_usedDataSources.findIndex(it.current()->dataSource().lower()) ); } } else {//!query // dataFieldsCount = m_dataItems.count(); } #if 0 //moved down //in 'newIndices' let's collect new indices for every data source foreach(TQValueList::ConstIterator, it, invalidSources) { //all previous indices have corresponding data source // for (; i < (*it); i++) { // newIndices[i] = number++; //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl; // } //this index have no corresponding data source // newIndices[i]=-1; KexiFormDataItemInterface *item = m_dataItems.at( *it ); if (item) item->setInvalidState( TQString::fromLatin1("#") + i18n("NAME") + TQString::fromLatin1("?") ); m_dataItems.remove(*it); kexipluginsdbg << "invalidateDataSources(): " << (*it) << " -> " << -1 << endl; // i++; } #endif //fill remaining part of the vector // for (; i < dataFieldsCount; i++) { //m_dataItems.count(); i++) { //newIndices[i] = number++; //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl; //} #if 0 //recreate m_fieldNumbersForDataItems and mark widgets with invalid data sources KexiFormDataItemInterfaceToIntMap newFieldNumbersForDataItems; foreach(KexiFormDataItemInterfaceToIntMap::ConstIterator, it, m_fieldNumbersForDataItems) { bool ok; const int newIndex = newIndices.at( it.data(), &ok ); if (ok && newIndex!=-1) { kexipluginsdbg << "invalidateDataSources(): " << it.key()->dataSource() << ": " << it.data() << " -> " << newIndex << endl; newFieldNumbersForDataItems.replace(it.key(), newIndex); } else { kexipluginsdbg << "invalidateDataSources(): removing " << it.key()->dataSource() << endl; m_dataItems.remove(it.key()); it.key()->setInvalidState( TQString::fromLatin1("#") + i18n("NAME") + TQString::fromLatin1("?") ); } } #endif // m_fieldNumbersForDataItems = newFieldNumbersForDataItems; //update data sources set (some of them may be removed) TQDict tmpUsedDataSources(1013); if (query) query->debug(); //if (query && m_dataItems.count()!=query->fieldCount()) { // kdWarning() << "KexiFormDataProvider::invalidateDataSources(): m_dataItems.count()!=query->fieldCount() (" // << m_dataItems.count() << "," << query->fieldCount() << ")" << endl; //} //i = 0; m_disableFillDuplicatedDataItems = true; // temporary disable fillDuplicatedDataItems() // because setColumnInfo() can activate it for (TQPtrListIterator it(m_dataItems); it.current();) { KexiFormDataItemInterface * item = it.current(); if (invalidSources[ item->dataSource().lower() ]) { item->setInvalidState( TQString::fromLatin1("#") + i18n("NAME") + TQString::fromLatin1("?") ); m_dataItems.remove(item); continue; } uint fieldNumber = m_fieldNumbersForDataItems[ item ]; if (query) { KexiDB::QueryColumnInfo *ci = fieldsExpanded[fieldNumber]; item->setColumnInfo(ci); kexipluginsdbg << "- item=" << dynamic_cast(item)->name() << " dataSource=" << item->dataSource() << " field=" << ci->field->name() << endl; const int indexForVisibleLookupValue = ci->indexForVisibleLookupValue(); if (-1 != indexForVisibleLookupValue && indexForVisibleLookupValue < (int)fieldsExpanded.count()) { //there's lookup column defined: set visible column as well KexiDB::QueryColumnInfo *visibleColumnInfo = fieldsExpanded[ indexForVisibleLookupValue ]; if (visibleColumnInfo) { item->setVisibleColumnInfo( visibleColumnInfo ); if (dynamic_cast(item) && m_mainWidget && dynamic_cast(item)->internalEditor()) { // m_mainWidget (dbform) should filter the (just created using setVisibleColumnInfo()) // combo box' internal editor (actually, only if the combo is in 'editable' mode) dynamic_cast(item)->internalEditor()->installEventFilter(m_mainWidget); } kexipluginsdbg << " ALSO SET visibleColumn=" << visibleColumnInfo->debugString() << "\n at position " << indexForVisibleLookupValue << endl; } } } tmpUsedDataSources.replace( item->dataSource().lower(), (char*)1 ); ++it; } m_disableFillDuplicatedDataItems = false; m_usedDataSources.clear(); foreach_list(TQDictIterator, it, tmpUsedDataSources) { m_usedDataSources += it.currentKey(); } }