kexi

kexidataprovider.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "kexidataprovider.h"
21
22#include <tqwidget.h>
23#include <tqobjectlist.h>
24
25#include <kdebug.h>
26#include <tdelocale.h>
27
28#include <widget/tableview/kexitableitem.h>
29#include <widget/tableview/kexitableviewdata.h>
30#include <widget/tableview/kexicomboboxbase.h>
31#include <kexidb/queryschema.h>
32#include <kexiutils/utils.h>
33
34#include "widgets/kexidbform.h"
35
36KexiFormDataProvider::KexiFormDataProvider()
38 , m_mainWidget(0)
39 , m_duplicatedItems(0)
40 , m_disableFillDuplicatedDataItems(false)
41{
42}
43
44KexiFormDataProvider::~KexiFormDataProvider()
45{
46 delete m_duplicatedItems;
47}
48
50{
51 m_mainWidget = mainWidget;
52 m_dataItems.clear();
53 m_usedDataSources.clear();
54 m_fieldNumbersForDataItems.clear();
55 if (!m_mainWidget)
56 return;
57
58 //find widgets whose will work as data items
59 TQObjectList *l = m_mainWidget->queryList( "TQWidget" );
60 TQObjectListIt it( *l );
61 TQObject *obj;
62 TQDict<char> tmpSources;
63 for ( ; (obj = it.current()) != 0; ++it ) {
64 KexiFormDataItemInterface* const formDataItem = dynamic_cast<KexiFormDataItemInterface*>(obj);
65 if (!formDataItem)
66 continue;
67 if (formDataItem->parentInterface()) //item with parent interface: collect parent instead...
68 continue;
69#if 0
70 KexiDBForm *dbForm = KexiUtils::findParent<KexiDBForm>(obj, "KexiDBForm"); //form's surface...
71 if (dbForm!=m_mainWidget) //only set data for this form's data items
72 continue;
73#else
74 //tmp: reject widgets within subforms
75 if (KexiUtils::findParent<KexiDBForm>(obj, "KexiDBSubForm"))
76 continue;
77#endif
78 TQString dataSource( formDataItem->dataSource().lower() );
79 if (dataSource.isEmpty())
80 continue;
81 kexipluginsdbg << obj->name() << endl;
82 m_dataItems.append( formDataItem );
83 formDataItem->installListener( this );
84 tmpSources.replace( dataSource, (char*)1 );
85 }
86 delete l;
87 //now we've got a set (unique list) of field names in tmpSources
88 //remember it in m_usedDataSources
89 for (TQDictIterator<char> it(tmpSources); it.current(); ++it) {
90 m_usedDataSources += it.currentKey();
91 }
92}
93
95{
96 kexipluginsdbg << "KexiFormDataProvider::fillDataItems() cnt=" << row.count() << endl;
97 for (KexiFormDataItemInterfaceToIntMap::ConstIterator it = m_fieldNumbersForDataItems.constBegin();
98 it!=m_fieldNumbersForDataItems.constEnd(); ++it)
99 {
100 KexiFormDataItemInterface *itemIface = it.key();
101 if (!itemIface->columnInfo()) {
102 kexipluginsdbg << "KexiFormDataProvider::fillDataItems(): itemIface->columnInfo() == 0" << endl;
103 continue;
104 }
105 //1. Is this a value with a combo box (lookup)?
106 int indexForVisibleLookupValue = itemIface->columnInfo()->indexForVisibleLookupValue();
107 if (indexForVisibleLookupValue<0 && indexForVisibleLookupValue>=(int)row.count()) //sanity
108 indexForVisibleLookupValue = -1; //no
109 const TQVariant value(row.at(it.data()));
110 TQVariant visibleLookupValue;
111 if (indexForVisibleLookupValue!=-1 && (int)row.size()>indexForVisibleLookupValue)
112 visibleLookupValue = row.at(indexForVisibleLookupValue);
113 kexipluginsdbg << "fill data of '" << itemIface->dataSource() << "' at idx=" << it.data()
114 << " data=" << value << (indexForVisibleLookupValue!=-1
115 ? TQString(" SPECIAL: indexForVisibleLookupValue=%1 visibleValue=%2")
116 .arg(indexForVisibleLookupValue).arg(visibleLookupValue.toString())
117 : TQString())
118 << endl;
119 const bool displayDefaultValue = cursorAtNewRow && (value.isNull() && visibleLookupValue.isNull())
120 && !itemIface->columnInfo()->field->defaultValue().isNull()
121 && !itemIface->columnInfo()->field->isAutoIncrement(); //no value to set but there is default value defined
122 itemIface->setValue(
123 displayDefaultValue ? itemIface->columnInfo()->field->defaultValue() : value,
124 TQVariant(), /*add*/false,
126 indexForVisibleLookupValue==-1 ? 0 : &visibleLookupValue //pass visible value if available
127 );
128 // now disable/enable "display default value" if needed (do it after setValue(), before setValue() turns it off)
129 if (itemIface->hasDisplayedDefaultValue() != displayDefaultValue)
130 itemIface->setDisplayDefaultValue( dynamic_cast<TQWidget*>(itemIface), displayDefaultValue );
131 }
132}
133
135 KexiFormDataItemInterface* item, const TQVariant& value)
136{
137 if (m_disableFillDuplicatedDataItems)
138 return;
139 if (!m_duplicatedItems) {
140 //build (once) a set of duplicated data items (having the same fields assigned)
141 //so we can later check if an item is duplicated with a cost of o(1)
142 TQMap<KexiDB::Field*,int> tmpDuplicatedItems;
143 TQMapIterator<KexiDB::Field*,int> it_dup;
144 for (TQPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
145 if (!it.current()->columnInfo() || !it.current()->columnInfo()->field)
146 continue;
147 kdDebug() << " ** " << it.current()->columnInfo()->field->name() << endl;
148 it_dup = tmpDuplicatedItems.find( it.current()->columnInfo()->field );
149 uint count;
150 if (it_dup==tmpDuplicatedItems.end())
151 count = 0;
152 else
153 count = it_dup.data();
154 tmpDuplicatedItems.insert( it.current()->columnInfo()->field, ++count );
155 }
156 m_duplicatedItems = new TQPtrDict<char>(1009);
157 for (it_dup = tmpDuplicatedItems.begin(); it_dup!=tmpDuplicatedItems.end(); ++it_dup) {
158 if (it_dup.data() > 1) {
159 m_duplicatedItems->insert( it_dup.key(), (char*)1 );
160 kexipluginsdbg << "duplicated item: " << static_cast<KexiDB::Field*>(it_dup.key())->name()
161 << " (" << it_dup.data() << " times)" << endl;
162 }
163 }
164 }
165 if (item->columnInfo() && m_duplicatedItems->find( item->columnInfo()->field )) {
166 for (TQPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
167 if (it.current()!=item && item->columnInfo()->field == it.current()->columnInfo()->field) {
168 kexipluginsdbg << "- setting a copy of value for item '"
169 << dynamic_cast<TQObject*>(it.current())->name() << "' == " << value << endl;
170 it.current()->setValue( value );
171 }
172 }
173 }
174}
175
177{
178 Q_UNUSED( item );
179}
180
182{
183 return false;
184}
185
186void KexiFormDataProvider::invalidateDataSources( const TQDict<char>& invalidSources,
187 KexiDB::QuerySchema* query)
188{
189 //fill m_fieldNumbersForDataItems mapping from data item to field number
190 //(needed for fillDataItems)
191 KexiDB::QueryColumnInfo::Vector fieldsExpanded;
192// uint dataFieldsCount; // == fieldsExpanded.count() if query is available or else == m_dataItems.count()
193
194 if (query) {
196// dataFieldsCount = fieldsExpanded.count();
197 TQMap<KexiDB::QueryColumnInfo*,int> columnsOrder( query->columnsOrder() );
198 for (TQMapConstIterator<KexiDB::QueryColumnInfo*,int> it = columnsOrder.constBegin(); it!=columnsOrder.constEnd(); ++it) {
199 kexipluginsdbg << "query->columnsOrder()[ " << it.key()->field->name() << " ] = " << it.data() << endl;
200 }
201 for (TQPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current(); ++it) {
202 KexiFormDataItemInterface *item = it.current();
203 KexiDB::QueryColumnInfo* ci = query->columnInfo( it.current()->dataSource() );
204 int index = ci ? columnsOrder[ ci ] : -1;
205 kexipluginsdbg << "query->columnsOrder()[ " << (ci ? ci->field->name() : "") << " ] = " << index
206 << " (dataSource: " << item->dataSource() << ", name=" << dynamic_cast<TQObject*>(item)->name() << ")" << endl;
207 if (index!=-1 && !m_fieldNumbersForDataItems[ item ])
208 m_fieldNumbersForDataItems.insert( item, index );
209 //todo
210 //WRONG: not only used data sources can be fetched!
211 // m_fieldNumbersForDataItems.insert( it.current(),
212 // m_usedDataSources.findIndex(it.current()->dataSource().lower()) );
213 }
214 }
215 else {
216// dataFieldsCount = m_dataItems.count();
217 }
218
219#if 0 //moved down
220 //in 'newIndices' let's collect new indices for every data source
221 foreach(TQValueList<uint>::ConstIterator, it, invalidSources) {
222 //all previous indices have corresponding data source
223// for (; i < (*it); i++) {
224// newIndices[i] = number++;
225 //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl;
226// }
227 //this index have no corresponding data source
228// newIndices[i]=-1;
229 KexiFormDataItemInterface *item = m_dataItems.at( *it );
230 if (item)
231 item->setInvalidState( TQString::fromLatin1("#") + i18n("NAME") + TQString::fromLatin1("?") );
232 m_dataItems.remove(*it);
233 kexipluginsdbg << "invalidateDataSources(): " << (*it) << " -> " << -1 << endl;
234// i++;
235 }
236#endif
237 //fill remaining part of the vector
238// for (; i < dataFieldsCount; i++) { //m_dataItems.count(); i++) {
239 //newIndices[i] = number++;
240 //kexipluginsdbg << "invalidateDataSources(): " << i << " -> " << number-1 << endl;
241 //}
242
243#if 0
244 //recreate m_fieldNumbersForDataItems and mark widgets with invalid data sources
245 KexiFormDataItemInterfaceToIntMap newFieldNumbersForDataItems;
246 foreach(KexiFormDataItemInterfaceToIntMap::ConstIterator, it, m_fieldNumbersForDataItems) {
247 bool ok;
248 const int newIndex = newIndices.at( it.data(), &ok );
249 if (ok && newIndex!=-1) {
250 kexipluginsdbg << "invalidateDataSources(): " << it.key()->dataSource() << ": " << it.data() << " -> " << newIndex << endl;
251 newFieldNumbersForDataItems.replace(it.key(), newIndex);
252 }
253 else {
254 kexipluginsdbg << "invalidateDataSources(): removing " << it.key()->dataSource() << endl;
255 m_dataItems.remove(it.key());
256 it.key()->setInvalidState( TQString::fromLatin1("#") + i18n("NAME") + TQString::fromLatin1("?") );
257 }
258 }
259#endif
260// m_fieldNumbersForDataItems = newFieldNumbersForDataItems;
261
262 //update data sources set (some of them may be removed)
263 TQDict<char> tmpUsedDataSources(1009);
264
265 if (query)
266 query->debug();
267
268 //if (query && m_dataItems.count()!=query->fieldCount()) {
269 // kdWarning() << "KexiFormDataProvider::invalidateDataSources(): m_dataItems.count()!=query->fieldCount() ("
270 // << m_dataItems.count() << "," << query->fieldCount() << ")" << endl;
271 //}
272 //i = 0;
273 m_disableFillDuplicatedDataItems = true; // temporary disable fillDuplicatedDataItems()
274 // because setColumnInfo() can activate it
275 for (TQPtrListIterator<KexiFormDataItemInterface> it(m_dataItems); it.current();) {
276 KexiFormDataItemInterface * item = it.current();
277 if (invalidSources[ item->dataSource().lower() ]) {
278 item->setInvalidState( TQString::fromLatin1("#") + i18n("NAME") + TQString::fromLatin1("?") );
279 m_dataItems.remove(item);
280 continue;
281 }
282 uint fieldNumber = m_fieldNumbersForDataItems[ item ];
283 if (query) {
284 KexiDB::QueryColumnInfo *ci = fieldsExpanded[fieldNumber];
285 item->setColumnInfo(ci);
286 kexipluginsdbg << "- item=" << dynamic_cast<TQObject*>(item)->name()
287 << " dataSource=" << item->dataSource()
288 << " field=" << ci->field->name() << endl;
289 const int indexForVisibleLookupValue = ci->indexForVisibleLookupValue();
290 if (-1 != indexForVisibleLookupValue && indexForVisibleLookupValue < (int)fieldsExpanded.count()) {
291 //there's lookup column defined: set visible column as well
292 KexiDB::QueryColumnInfo *visibleColumnInfo = fieldsExpanded[ indexForVisibleLookupValue ];
293 if (visibleColumnInfo) {
294 item->setVisibleColumnInfo( visibleColumnInfo );
295 if (dynamic_cast<KexiComboBoxBase*>(item) && m_mainWidget
296 && dynamic_cast<KexiComboBoxBase*>(item)->internalEditor())
297 {
298 // m_mainWidget (dbform) should filter the (just created using setVisibleColumnInfo())
299 // combo box' internal editor (actually, only if the combo is in 'editable' mode)
300 dynamic_cast<KexiComboBoxBase*>(item)->internalEditor()->installEventFilter(m_mainWidget);
301 }
302 kexipluginsdbg << " ALSO SET visibleColumn=" << visibleColumnInfo->debugString()
303 << "\n at position " << indexForVisibleLookupValue << endl;
304 }
305 }
306 }
307 tmpUsedDataSources.replace( item->dataSource().lower(), (char*)1 );
308 ++it;
309 }
310 m_disableFillDuplicatedDataItems = false;
311 m_usedDataSources.clear();
312 foreach_list(TQDictIterator<char>, it, tmpUsedDataSources) {
313 m_usedDataSources += it.currentKey();
314 }
315}
A base class for handling data-aware combo boxes. This class is used by KexiComboBoxTableEdit and Kex...
A DB-aware form widget, acting as form's toplevel widget.
Definition kexidbform.h:44
Meta-data for a field.
Definition field.h:73
bool isAutoIncrement() const
Definition field.h:231
TQVariant defaultValue() const
Definition field.h:326
Helper class that assigns additional information for the column in a query.
Definition queryschema.h:49
int indexForVisibleLookupValue() const
Definition queryschema.h:75
TQString debugString() const
KexiDB::QuerySchema provides information about database query.
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
TQMap< QueryColumnInfo *, int > columnsOrder(ColumnsOrderOptions options=ExpandedList)
QueryColumnInfo::Vector fieldsExpanded(FieldsExpandedOptions options=Default)
QueryColumnInfo * columnInfo(const TQString &identifier, bool expanded=true)
An helper class used to react on KexiDataItemInterface objects' changes.
An interface for declaring widgets to be data-aware.
KexiDataItemInterface * parentInterface() const
void setValue(const TQVariant &value, const TQVariant &add=TQVariant(), bool removeOld=false, const TQVariant *visibleValue=0)
virtual void installListener(KexiDataItemChangesListener *listener)
Sets listener. No need to reimplement this.
An interface for declaring form widgets to be data-aware.
virtual KexiDB::QueryColumnInfo * columnInfo() const
virtual void setVisibleColumnInfo(KexiDB::QueryColumnInfo *cinfo)
virtual void setColumnInfo(KexiDB::QueryColumnInfo *cinfo)
virtual void setDisplayDefaultValue(TQWidget *widget, bool displayDefaultValue)
virtual bool hasDisplayedDefaultValue() const
virtual void setInvalidState(const TQString &displayText)=0
void setMainDataSourceWidget(TQWidget *mainWidget)
void fillDuplicatedDataItems(KexiFormDataItemInterface *item, const TQVariant &value)
void invalidateDataSources(const TQDict< char > &invalidSources, KexiDB::QuerySchema *query=0)
virtual void valueChanged(KexiDataItemInterface *item)
virtual bool cursorAtNewRow() const
void fillDataItems(KexiTableItem &row, bool cursorAtNewRow)
type * findParent(TQObject *o, const char *className)