|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtSql module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qsqltablemodel.h" |
|
43 |
|
44 #include "qsqldriver.h" |
|
45 #include "qsqlerror.h" |
|
46 #include "qsqlfield.h" |
|
47 #include "qsqlindex.h" |
|
48 #include "qsqlquery.h" |
|
49 #include "qsqlrecord.h" |
|
50 #include "qsqlresult.h" |
|
51 |
|
52 #include "qsqltablemodel_p.h" |
|
53 |
|
54 #include <qdebug.h> |
|
55 |
|
56 QT_BEGIN_NAMESPACE |
|
57 |
|
58 /*! \internal |
|
59 Populates our record with values. |
|
60 */ |
|
61 QSqlRecord QSqlTableModelPrivate::record(const QVector<QVariant> &values) const |
|
62 { |
|
63 QSqlRecord r = rec; |
|
64 for (int i = 0; i < r.count() && i < values.count(); ++i) |
|
65 r.setValue(i, values.at(i)); |
|
66 return r; |
|
67 } |
|
68 |
|
69 /*! \internal |
|
70 Set a record for OnFieldChange and OnRowChange. |
|
71 */ |
|
72 bool QSqlTableModelPrivate::setRecord(int row, const QSqlRecord &record) |
|
73 { |
|
74 Q_Q(QSqlTableModel); |
|
75 bool isOk = true; |
|
76 |
|
77 QSqlTableModel::EditStrategy oldStrategy = strategy; |
|
78 |
|
79 // FieldChange strategy makes no sense when setting an entire row |
|
80 if (strategy == QSqlTableModel::OnFieldChange) |
|
81 strategy = QSqlTableModel::OnRowChange; |
|
82 for (int i = 0; i < record.count(); ++i) { |
|
83 int idx = nameToIndex(record.fieldName(i)); |
|
84 if (idx == -1) |
|
85 continue; |
|
86 QModelIndex cIndex = q->createIndex(row, idx); |
|
87 QVariant value = record.value(i); |
|
88 QVariant oldValue = q->data(cIndex); |
|
89 if (oldValue.isNull() || oldValue != value) |
|
90 isOk &= q->setData(cIndex, value, Qt::EditRole); |
|
91 } |
|
92 if (isOk && oldStrategy == QSqlTableModel::OnFieldChange) |
|
93 q->submitAll(); |
|
94 strategy = oldStrategy; |
|
95 |
|
96 return isOk; |
|
97 } |
|
98 |
|
99 int QSqlTableModelPrivate::nameToIndex(const QString &name) const |
|
100 { |
|
101 QString fieldname = name; |
|
102 if (db.driver()->isIdentifierEscaped(fieldname, QSqlDriver::FieldName)) |
|
103 fieldname = db.driver()->stripDelimiters(fieldname, QSqlDriver::FieldName); |
|
104 return rec.indexOf(fieldname); |
|
105 } |
|
106 |
|
107 void QSqlTableModelPrivate::initRecordAndPrimaryIndex() |
|
108 { |
|
109 rec = db.record(tableName); |
|
110 primaryIndex = db.primaryIndex(tableName); |
|
111 } |
|
112 |
|
113 void QSqlTableModelPrivate::clear() |
|
114 { |
|
115 editIndex = -1; |
|
116 sortColumn = -1; |
|
117 sortOrder = Qt::AscendingOrder; |
|
118 tableName.clear(); |
|
119 editQuery.clear(); |
|
120 editBuffer.clear(); |
|
121 cache.clear(); |
|
122 primaryIndex.clear(); |
|
123 rec.clear(); |
|
124 filter.clear(); |
|
125 } |
|
126 |
|
127 void QSqlTableModelPrivate::revertInsertedRow() |
|
128 { |
|
129 Q_Q(QSqlTableModel); |
|
130 if (insertIndex == -1) |
|
131 return; |
|
132 |
|
133 q->beginRemoveRows(QModelIndex(), insertIndex, insertIndex); |
|
134 insertIndex = -1; |
|
135 q->endRemoveRows(); |
|
136 } |
|
137 |
|
138 void QSqlTableModelPrivate::clearEditBuffer() |
|
139 { |
|
140 editBuffer = rec; |
|
141 } |
|
142 |
|
143 void QSqlTableModelPrivate::clearCache() |
|
144 { |
|
145 cache.clear(); |
|
146 } |
|
147 |
|
148 void QSqlTableModelPrivate::revertCachedRow(int row) |
|
149 { |
|
150 Q_Q(QSqlTableModel); |
|
151 ModifiedRow r = cache.value(row); |
|
152 switch (r.op) { |
|
153 case QSqlTableModelPrivate::None: |
|
154 Q_ASSERT_X(false, "QSqlTableModelPrivate::revertCachedRow()", "Invalid entry in cache map"); |
|
155 return; |
|
156 case QSqlTableModelPrivate::Update: |
|
157 case QSqlTableModelPrivate::Delete: |
|
158 cache.remove(row); |
|
159 emit q->dataChanged(q->createIndex(row, 0), |
|
160 q->createIndex(row, q->columnCount() - 1)); |
|
161 break; |
|
162 case QSqlTableModelPrivate::Insert: { |
|
163 QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = cache.find(row); |
|
164 if (it == cache.end()) |
|
165 return; |
|
166 q->beginRemoveRows(QModelIndex(), row, row); |
|
167 it = cache.erase(it); |
|
168 while (it != cache.end()) { |
|
169 int oldKey = it.key(); |
|
170 const QSqlTableModelPrivate::ModifiedRow oldValue = it.value(); |
|
171 cache.erase(it); |
|
172 it = cache.insert(oldKey - 1, oldValue); |
|
173 ++it; |
|
174 } |
|
175 q->endRemoveRows(); |
|
176 break; } |
|
177 } |
|
178 } |
|
179 |
|
180 bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement, |
|
181 const QSqlRecord &rec, const QSqlRecord &whereValues) |
|
182 { |
|
183 if (stmt.isEmpty()) |
|
184 return false; |
|
185 |
|
186 // lazy initialization of editQuery |
|
187 if (editQuery.driver() != db.driver()) |
|
188 editQuery = QSqlQuery(db); |
|
189 |
|
190 // workaround for In-Process databases - remove all read locks |
|
191 // from the table to make sure the editQuery succeeds |
|
192 if (db.driver()->hasFeature(QSqlDriver::SimpleLocking)) |
|
193 const_cast<QSqlResult *>(query.result())->detachFromResultSet(); |
|
194 |
|
195 if (prepStatement) { |
|
196 if (editQuery.lastQuery() != stmt) { |
|
197 if (!editQuery.prepare(stmt)) { |
|
198 error = editQuery.lastError(); |
|
199 return false; |
|
200 } |
|
201 } |
|
202 int i; |
|
203 for (i = 0; i < rec.count(); ++i) { |
|
204 if (rec.isGenerated(i) && rec.value(i).type() != QVariant::Invalid) |
|
205 editQuery.addBindValue(rec.value(i)); |
|
206 } |
|
207 for (i = 0; i < whereValues.count(); ++i) { |
|
208 if (whereValues.isGenerated(i) && !whereValues.isNull(i)) |
|
209 editQuery.addBindValue(whereValues.value(i)); |
|
210 } |
|
211 |
|
212 if (!editQuery.exec()) { |
|
213 error = editQuery.lastError(); |
|
214 return false; |
|
215 } |
|
216 } else { |
|
217 if (!editQuery.exec(stmt)) { |
|
218 error = editQuery.lastError(); |
|
219 return false; |
|
220 } |
|
221 } |
|
222 return true; |
|
223 } |
|
224 |
|
225 QSqlRecord QSqlTableModelPrivate::primaryValues(int row) |
|
226 { |
|
227 QSqlRecord record; |
|
228 if (!query.seek(row)) { |
|
229 error = query.lastError(); |
|
230 return record; |
|
231 } |
|
232 if (primaryIndex.isEmpty()) { |
|
233 record = rec; |
|
234 for (int i = 0; i < record.count(); ++i) |
|
235 record.setValue(i, query.value(i)); |
|
236 } else { |
|
237 record = primaryIndex; |
|
238 for (int i = 0; i < record.count(); ++i) |
|
239 record.setValue(i, query.value(rec.indexOf(record.fieldName(i)))); |
|
240 } |
|
241 return record; |
|
242 } |
|
243 |
|
244 /*! |
|
245 \class QSqlTableModel |
|
246 \brief The QSqlTableModel class provides an editable data model |
|
247 for a single database table. |
|
248 |
|
249 \ingroup database |
|
250 \inmodule QtSql |
|
251 |
|
252 QSqlTableModel is a high-level interface for reading and writing |
|
253 database records from a single table. It is build on top of the |
|
254 lower-level QSqlQuery and can be used to provide data to view |
|
255 classes such as QTableView. For example: |
|
256 |
|
257 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 24 |
|
258 |
|
259 We set the SQL table's name and the edit strategy, then we set up |
|
260 the labels displayed in the view header. The edit strategy |
|
261 dictates when the changes done by the user in the view are |
|
262 actually applied to the database. The possible values are \l |
|
263 OnFieldChange, \l OnRowChange, and \l OnManualSubmit. |
|
264 |
|
265 QSqlTableModel can also be used to access a database |
|
266 programmatically, without binding it to a view: |
|
267 |
|
268 \snippet doc/src/snippets/sqldatabase/sqldatabase.cpp 25 |
|
269 |
|
270 The code snippet above extracts the \c salary field from record 4 in |
|
271 the result set of the query \c{SELECT * from employee}. |
|
272 |
|
273 It is possible to set filters using setFilter(), or modify the |
|
274 sort order using setSort(). At the end, you must call select() to |
|
275 populate the model with data. |
|
276 |
|
277 The \l{sql/tablemodel} example illustrates how to use |
|
278 QSqlTableModel as the data source for a QTableView. |
|
279 |
|
280 QSqlTableModel provides no direct support for foreign keys. Use |
|
281 the QSqlRelationalTableModel and QSqlRelationalDelegate if you |
|
282 want to resolve foreign keys. |
|
283 |
|
284 \sa QSqlRelationalTableModel, QSqlQuery, {Model/View Programming}, |
|
285 {Table Model Example}, {Cached Table Example} |
|
286 */ |
|
287 |
|
288 /*! |
|
289 \fn QSqlTableModel::beforeDelete(int row) |
|
290 |
|
291 This signal is emitted by deleteRowFromTable() before the \a row |
|
292 is deleted from the currently active database table. |
|
293 */ |
|
294 |
|
295 /*! |
|
296 \fn void QSqlTableModel::primeInsert(int row, QSqlRecord &record) |
|
297 |
|
298 This signal is emitted by insertRows(), when an insertion is |
|
299 initiated in the given \a row of the currently active database |
|
300 table. The \a record parameter can be written to (since it is a |
|
301 reference), for example to populate some fields with default |
|
302 values. |
|
303 */ |
|
304 |
|
305 /*! |
|
306 \fn QSqlTableModel::beforeInsert(QSqlRecord &record) |
|
307 |
|
308 This signal is emitted by insertRowIntoTable() before a new row is |
|
309 inserted into the currently active database table. The values that |
|
310 are about to be inserted are stored in \a record and can be |
|
311 modified before they will be inserted. |
|
312 */ |
|
313 |
|
314 /*! |
|
315 \fn QSqlTableModel::beforeUpdate(int row, QSqlRecord &record) |
|
316 |
|
317 This signal is emitted by updateRowInTable() before the \a row is |
|
318 updated in the currently active database table with the values |
|
319 from \a record. |
|
320 |
|
321 Note that only values that are marked as generated will be updated. |
|
322 The generated flag can be set with \l QSqlRecord::setGenerated() |
|
323 and checked with \l QSqlRecord::isGenerated(). |
|
324 |
|
325 \sa QSqlRecord::isGenerated() |
|
326 */ |
|
327 |
|
328 /*! |
|
329 Creates an empty QSqlTableModel and sets the parent to \a parent |
|
330 and the database connection to \a db. If \a db is not valid, the |
|
331 default database connection will be used. |
|
332 |
|
333 The default edit strategy is \l OnRowChange. |
|
334 */ |
|
335 QSqlTableModel::QSqlTableModel(QObject *parent, QSqlDatabase db) |
|
336 : QSqlQueryModel(*new QSqlTableModelPrivate, parent) |
|
337 { |
|
338 Q_D(QSqlTableModel); |
|
339 d->db = db.isValid() ? db : QSqlDatabase::database(); |
|
340 } |
|
341 |
|
342 /*! \internal |
|
343 */ |
|
344 QSqlTableModel::QSqlTableModel(QSqlTableModelPrivate &dd, QObject *parent, QSqlDatabase db) |
|
345 : QSqlQueryModel(dd, parent) |
|
346 { |
|
347 Q_D(QSqlTableModel); |
|
348 d->db = db.isValid() ? db : QSqlDatabase::database(); |
|
349 } |
|
350 |
|
351 /*! |
|
352 Destroys the object and frees any allocated resources. |
|
353 */ |
|
354 QSqlTableModel::~QSqlTableModel() |
|
355 { |
|
356 } |
|
357 |
|
358 /*! |
|
359 Sets the database table on which the model operates to \a |
|
360 tableName. Does not select data from the table, but fetches its |
|
361 field information. |
|
362 |
|
363 To populate the model with the table's data, call select(). |
|
364 |
|
365 Error information can be retrieved with \l lastError(). |
|
366 |
|
367 \sa select(), setFilter(), lastError() |
|
368 */ |
|
369 void QSqlTableModel::setTable(const QString &tableName) |
|
370 { |
|
371 Q_D(QSqlTableModel); |
|
372 clear(); |
|
373 d->tableName = tableName; |
|
374 d->initRecordAndPrimaryIndex(); |
|
375 d->initColOffsets(d->rec.count()); |
|
376 |
|
377 if (d->rec.count() == 0) |
|
378 d->error = QSqlError(QLatin1String("Unable to find table ") + d->tableName, QString(), |
|
379 QSqlError::StatementError); |
|
380 } |
|
381 |
|
382 /*! |
|
383 Returns the name of the currently selected table. |
|
384 */ |
|
385 QString QSqlTableModel::tableName() const |
|
386 { |
|
387 Q_D(const QSqlTableModel); |
|
388 return d->tableName; |
|
389 } |
|
390 |
|
391 /*! |
|
392 Populates the model with data from the table that was set via setTable(), using the |
|
393 specified filter and sort condition, and returns true if successful; otherwise |
|
394 returns false. |
|
395 |
|
396 \sa setTable(), setFilter(), selectStatement() |
|
397 */ |
|
398 bool QSqlTableModel::select() |
|
399 { |
|
400 Q_D(QSqlTableModel); |
|
401 QString query = selectStatement(); |
|
402 if (query.isEmpty()) |
|
403 return false; |
|
404 |
|
405 revertAll(); |
|
406 QSqlQuery qu(query, d->db); |
|
407 setQuery(qu); |
|
408 |
|
409 if (!qu.isActive()) { |
|
410 // something went wrong - revert to non-select state |
|
411 d->initRecordAndPrimaryIndex(); |
|
412 return false; |
|
413 } |
|
414 return true; |
|
415 } |
|
416 |
|
417 /*! |
|
418 \reimp |
|
419 */ |
|
420 QVariant QSqlTableModel::data(const QModelIndex &index, int role) const |
|
421 { |
|
422 Q_D(const QSqlTableModel); |
|
423 if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::EditRole)) |
|
424 return QVariant(); |
|
425 |
|
426 QModelIndex item = indexInQuery(index); |
|
427 |
|
428 switch (d->strategy) { |
|
429 case OnFieldChange: |
|
430 case OnRowChange: |
|
431 if (index.row() == d->insertIndex) { |
|
432 QVariant val; |
|
433 if (item.column() < 0 || item.column() >= d->rec.count()) |
|
434 return val; |
|
435 val = d->editBuffer.value(index.column()); |
|
436 if (val.type() == QVariant::Invalid) |
|
437 val = QVariant(d->rec.field(item.column()).type()); |
|
438 return val; |
|
439 } |
|
440 if (d->editIndex == item.row()) { |
|
441 QVariant var = d->editBuffer.value(item.column()); |
|
442 if (var.isValid()) |
|
443 return var; |
|
444 } |
|
445 break; |
|
446 case OnManualSubmit: { |
|
447 const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row()); |
|
448 const QVariant var = row.rec.value(item.column()); |
|
449 if (var.isValid() || row.op == QSqlTableModelPrivate::Insert) |
|
450 return var; |
|
451 break; } |
|
452 } |
|
453 return QSqlQueryModel::data(item, role); |
|
454 } |
|
455 |
|
456 /*! |
|
457 \reimp |
|
458 */ |
|
459 QVariant QSqlTableModel::headerData(int section, Qt::Orientation orientation, int role) const |
|
460 { |
|
461 Q_D(const QSqlTableModel); |
|
462 if (orientation == Qt::Vertical && role == Qt::DisplayRole) { |
|
463 switch (d->strategy) { |
|
464 case OnFieldChange: |
|
465 case OnRowChange: |
|
466 if (d->insertIndex == section) |
|
467 return QLatin1String("*"); |
|
468 break; |
|
469 case OnManualSubmit: |
|
470 QSqlTableModelPrivate::Op op = d->cache.value(section).op; |
|
471 if (op == QSqlTableModelPrivate::Insert) |
|
472 return QLatin1String("*"); |
|
473 else if (op == QSqlTableModelPrivate::Delete) |
|
474 return QLatin1String("!"); |
|
475 break; |
|
476 } |
|
477 } |
|
478 return QSqlQueryModel::headerData(section, orientation, role); |
|
479 } |
|
480 |
|
481 /*! |
|
482 Returns true if the value at the index \a index is dirty, otherwise false. |
|
483 Dirty values are values that were modified in the model |
|
484 but not yet written into the database. |
|
485 |
|
486 If \a index is invalid or points to a non-existing row, false is returned. |
|
487 */ |
|
488 bool QSqlTableModel::isDirty(const QModelIndex &index) const |
|
489 { |
|
490 Q_D(const QSqlTableModel); |
|
491 if (!index.isValid()) |
|
492 return false; |
|
493 |
|
494 switch (d->strategy) { |
|
495 case OnFieldChange: |
|
496 return false; |
|
497 case OnRowChange: |
|
498 return index.row() == d->editIndex && d->editBuffer.value(index.column()).isValid(); |
|
499 case OnManualSubmit: { |
|
500 const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row()); |
|
501 return row.op == QSqlTableModelPrivate::Insert |
|
502 || row.op == QSqlTableModelPrivate::Delete |
|
503 || (row.op == QSqlTableModelPrivate::Update |
|
504 && row.rec.value(index.column()).isValid()); |
|
505 } |
|
506 } |
|
507 return false; |
|
508 } |
|
509 |
|
510 /*! |
|
511 Sets the data for the item \a index for the role \a role to \a |
|
512 value. Depending on the edit strategy, the value might be applied |
|
513 to the database at once or cached in the model. |
|
514 |
|
515 Returns true if the value could be set or false on error, for |
|
516 example if \a index is out of bounds. |
|
517 |
|
518 \sa editStrategy(), data(), submit(), submitAll(), revertRow() |
|
519 */ |
|
520 bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role) |
|
521 { |
|
522 Q_D(QSqlTableModel); |
|
523 if (role != Qt::EditRole) |
|
524 return QSqlQueryModel::setData(index, value, role); |
|
525 |
|
526 if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount()) |
|
527 return false; |
|
528 |
|
529 bool isOk = true; |
|
530 switch (d->strategy) { |
|
531 case OnFieldChange: { |
|
532 if (index.row() == d->insertIndex) { |
|
533 d->editBuffer.setValue(index.column(), value); |
|
534 return true; |
|
535 } |
|
536 d->clearEditBuffer(); |
|
537 d->editBuffer.setValue(index.column(), value); |
|
538 isOk = updateRowInTable(index.row(), d->editBuffer); |
|
539 if (isOk) |
|
540 select(); |
|
541 emit dataChanged(index, index); |
|
542 break; } |
|
543 case OnRowChange: |
|
544 if (index.row() == d->insertIndex) { |
|
545 d->editBuffer.setValue(index.column(), value); |
|
546 return true; |
|
547 } |
|
548 if (d->editIndex != index.row()) { |
|
549 if (d->editIndex != -1) |
|
550 submit(); |
|
551 d->clearEditBuffer(); |
|
552 } |
|
553 d->editBuffer.setValue(index.column(), value); |
|
554 d->editIndex = index.row(); |
|
555 emit dataChanged(index, index); |
|
556 break; |
|
557 case OnManualSubmit: { |
|
558 QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()]; |
|
559 if (row.op == QSqlTableModelPrivate::None) { |
|
560 row.op = QSqlTableModelPrivate::Update; |
|
561 row.rec = d->rec; |
|
562 row.primaryValues = d->primaryValues(indexInQuery(index).row()); |
|
563 } |
|
564 row.rec.setValue(index.column(), value); |
|
565 emit dataChanged(index, index); |
|
566 break; } |
|
567 } |
|
568 return isOk; |
|
569 } |
|
570 |
|
571 /*! |
|
572 This function simply calls QSqlQueryModel::setQuery(\a query). |
|
573 You should normally not call it on a QSqlTableModel. Instead, use |
|
574 setTable(), setSort(), setFilter(), etc., to set up the query. |
|
575 |
|
576 \sa selectStatement() |
|
577 */ |
|
578 void QSqlTableModel::setQuery(const QSqlQuery &query) |
|
579 { |
|
580 QSqlQueryModel::setQuery(query); |
|
581 } |
|
582 |
|
583 /*! |
|
584 Updates the given \a row in the currently active database table |
|
585 with the specified \a values. Returns true if successful; otherwise |
|
586 returns false. |
|
587 |
|
588 This is a low-level method that operates directly on the database |
|
589 and should not be called directly. Use setData() to update values. |
|
590 The model will decide depending on its edit strategy when to modify |
|
591 the database. |
|
592 |
|
593 Note that only values that have the generated-flag set are updated. |
|
594 The generated-flag can be set with QSqlRecord::setGenerated() and |
|
595 tested with QSqlRecord::isGenerated(). |
|
596 |
|
597 \sa QSqlRecord::isGenerated(), setData() |
|
598 */ |
|
599 bool QSqlTableModel::updateRowInTable(int row, const QSqlRecord &values) |
|
600 { |
|
601 Q_D(QSqlTableModel); |
|
602 QSqlRecord rec(values); |
|
603 emit beforeUpdate(row, rec); |
|
604 |
|
605 const QSqlRecord whereValues = d->strategy == OnManualSubmit ? d->cache[row].primaryValues : d->primaryValues(row); |
|
606 bool prepStatement = d->db.driver()->hasFeature(QSqlDriver::PreparedQueries); |
|
607 QString stmt = d->db.driver()->sqlStatement(QSqlDriver::UpdateStatement, d->tableName, |
|
608 rec, prepStatement); |
|
609 QString where = d->db.driver()->sqlStatement(QSqlDriver::WhereStatement, d->tableName, |
|
610 whereValues, prepStatement); |
|
611 |
|
612 if (stmt.isEmpty() || where.isEmpty() || row < 0 || row >= rowCount()) { |
|
613 d->error = QSqlError(QLatin1String("No Fields to update"), QString(), |
|
614 QSqlError::StatementError); |
|
615 return false; |
|
616 } |
|
617 stmt.append(QLatin1Char(' ')).append(where); |
|
618 |
|
619 return d->exec(stmt, prepStatement, rec, whereValues); |
|
620 } |
|
621 |
|
622 |
|
623 /*! |
|
624 Inserts the values \a values into the currently active database table. |
|
625 |
|
626 This is a low-level method that operates directly on the database |
|
627 and should not be called directly. Use insertRow() and setData() |
|
628 to insert values. The model will decide depending on its edit strategy |
|
629 when to modify the database. |
|
630 |
|
631 Returns true if the values could be inserted, otherwise false. |
|
632 Error information can be retrieved with \l lastError(). |
|
633 |
|
634 \sa lastError(), insertRow(), insertRows() |
|
635 */ |
|
636 bool QSqlTableModel::insertRowIntoTable(const QSqlRecord &values) |
|
637 { |
|
638 Q_D(QSqlTableModel); |
|
639 QSqlRecord rec = values; |
|
640 emit beforeInsert(rec); |
|
641 |
|
642 bool prepStatement = d->db.driver()->hasFeature(QSqlDriver::PreparedQueries); |
|
643 QString stmt = d->db.driver()->sqlStatement(QSqlDriver::InsertStatement, d->tableName, |
|
644 rec, prepStatement); |
|
645 |
|
646 if (stmt.isEmpty()) { |
|
647 d->error = QSqlError(QLatin1String("No Fields to update"), QString(), |
|
648 QSqlError::StatementError); |
|
649 return false; |
|
650 } |
|
651 |
|
652 return d->exec(stmt, prepStatement, rec); |
|
653 } |
|
654 |
|
655 /*! |
|
656 Deletes the given \a row from the currently active database table. |
|
657 |
|
658 This is a low-level method that operates directly on the database |
|
659 and should not be called directly. Use removeRow() or removeRows() |
|
660 to delete values. The model will decide depending on its edit strategy |
|
661 when to modify the database. |
|
662 |
|
663 Returns true if the row was deleted; otherwise returns false. |
|
664 |
|
665 \sa removeRow(), removeRows() |
|
666 */ |
|
667 bool QSqlTableModel::deleteRowFromTable(int row) |
|
668 { |
|
669 Q_D(QSqlTableModel); |
|
670 emit beforeDelete(row); |
|
671 |
|
672 const QSqlRecord whereValues = d->strategy == OnManualSubmit ? d->cache[row].primaryValues : d->primaryValues(row); |
|
673 bool prepStatement = d->db.driver()->hasFeature(QSqlDriver::PreparedQueries); |
|
674 QString stmt = d->db.driver()->sqlStatement(QSqlDriver::DeleteStatement, |
|
675 d->tableName, |
|
676 QSqlRecord(), |
|
677 prepStatement); |
|
678 QString where = d->db.driver()->sqlStatement(QSqlDriver::WhereStatement, |
|
679 d->tableName, |
|
680 whereValues, |
|
681 prepStatement); |
|
682 |
|
683 if (stmt.isEmpty() || where.isEmpty()) { |
|
684 d->error = QSqlError(QLatin1String("Unable to delete row"), QString(), |
|
685 QSqlError::StatementError); |
|
686 return false; |
|
687 } |
|
688 stmt.append(QLatin1Char(' ')).append(where); |
|
689 |
|
690 return d->exec(stmt, prepStatement, whereValues); |
|
691 } |
|
692 |
|
693 /*! |
|
694 Submits all pending changes and returns true on success. |
|
695 Returns false on error, detailed error information can be |
|
696 obtained with lastError(). |
|
697 |
|
698 On success the model will be repopulated. Any views |
|
699 presenting it will lose their selections. |
|
700 |
|
701 Note: In OnManualSubmit mode, already submitted changes won't |
|
702 be cleared from the cache when submitAll() fails. This allows |
|
703 transactions to be rolled back and resubmitted again without |
|
704 losing data. |
|
705 |
|
706 \sa revertAll(), lastError() |
|
707 */ |
|
708 bool QSqlTableModel::submitAll() |
|
709 { |
|
710 Q_D(QSqlTableModel); |
|
711 |
|
712 switch (d->strategy) { |
|
713 case OnFieldChange: |
|
714 if (d->insertIndex == -1) |
|
715 return true; |
|
716 // else fall through |
|
717 case OnRowChange: |
|
718 if (d->editBuffer.isEmpty()) |
|
719 return true; |
|
720 if (d->insertIndex != -1) { |
|
721 if (!insertRowIntoTable(d->editBuffer)) |
|
722 return false; |
|
723 d->bottom = d->bottom.sibling(d->bottom.row() + 1, d->bottom.column()); |
|
724 } else { |
|
725 if (!updateRowInTable(d->editIndex, d->editBuffer)) |
|
726 return false; |
|
727 } |
|
728 d->clearEditBuffer(); |
|
729 d->editIndex = -1; |
|
730 d->insertIndex = -1; |
|
731 return select(); |
|
732 case OnManualSubmit: |
|
733 for (QSqlTableModelPrivate::CacheMap::ConstIterator it = d->cache.constBegin(); |
|
734 it != d->cache.constEnd(); ++it) { |
|
735 switch (it.value().op) { |
|
736 case QSqlTableModelPrivate::Insert: |
|
737 if (!insertRowIntoTable(it.value().rec)) |
|
738 return false; |
|
739 d->bottom = d->bottom.sibling(d->bottom.row() + 1, d->bottom.column()); |
|
740 break; |
|
741 case QSqlTableModelPrivate::Update: |
|
742 if (!updateRowInTable(it.key(), it.value().rec)) |
|
743 return false; |
|
744 break; |
|
745 case QSqlTableModelPrivate::Delete: |
|
746 if (!deleteRowFromTable(it.key())) |
|
747 return false; |
|
748 break; |
|
749 case QSqlTableModelPrivate::None: |
|
750 Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation"); |
|
751 break; |
|
752 } |
|
753 } |
|
754 d->clearCache(); |
|
755 return select(); |
|
756 } |
|
757 return false; |
|
758 } |
|
759 |
|
760 /*! |
|
761 This reimplemented slot is called by the item delegates when the |
|
762 user stopped editing the current row. |
|
763 |
|
764 Submits the currently edited row if the model's strategy is set |
|
765 to OnRowChange or OnFieldChange. Does nothing for the OnManualSubmit |
|
766 strategy. |
|
767 |
|
768 Use submitAll() to submit all pending changes for the |
|
769 OnManualSubmit strategy. |
|
770 |
|
771 Returns true on success; otherwise returns false. Use lastError() |
|
772 to query detailed error information. |
|
773 |
|
774 On success the model will be repopulated. Any views |
|
775 presenting it will lose their selections. |
|
776 |
|
777 \sa revert(), revertRow(), submitAll(), revertAll(), lastError() |
|
778 */ |
|
779 bool QSqlTableModel::submit() |
|
780 { |
|
781 Q_D(QSqlTableModel); |
|
782 if (d->strategy == OnRowChange || d->strategy == OnFieldChange) |
|
783 return submitAll(); |
|
784 return true; |
|
785 } |
|
786 |
|
787 /*! |
|
788 This reimplemented slot is called by the item delegates when the |
|
789 user canceled editing the current row. |
|
790 |
|
791 Reverts the changes if the model's strategy is set to |
|
792 OnRowChange. Does nothing for the other edit strategies. |
|
793 |
|
794 Use revertAll() to revert all pending changes for the |
|
795 OnManualSubmit strategy or revertRow() to revert a specific row. |
|
796 |
|
797 \sa submit(), submitAll(), revertRow(), revertAll() |
|
798 */ |
|
799 void QSqlTableModel::revert() |
|
800 { |
|
801 Q_D(QSqlTableModel); |
|
802 if (d->strategy == OnRowChange) |
|
803 revertAll(); |
|
804 } |
|
805 |
|
806 /*! |
|
807 \enum QSqlTableModel::EditStrategy |
|
808 |
|
809 This enum type describes which strategy to choose when editing values in the database. |
|
810 |
|
811 \value OnFieldChange All changes to the model will be applied immediately to the database. |
|
812 \value OnRowChange Changes to a row will be applied when the user selects a different row. |
|
813 \value OnManualSubmit All changes will be cached in the model until either submitAll() |
|
814 or revertAll() is called. |
|
815 |
|
816 Note: To prevent inserting only partly initialized rows into the database, |
|
817 \c OnFieldChange will behave like \c OnRowChange for newly inserted rows. |
|
818 |
|
819 \sa setEditStrategy() |
|
820 */ |
|
821 |
|
822 |
|
823 /*! |
|
824 Sets the strategy for editing values in the database to \a |
|
825 strategy. |
|
826 |
|
827 This will revert any pending changes. |
|
828 |
|
829 \sa editStrategy(), revertAll() |
|
830 */ |
|
831 void QSqlTableModel::setEditStrategy(EditStrategy strategy) |
|
832 { |
|
833 Q_D(QSqlTableModel); |
|
834 revertAll(); |
|
835 d->strategy = strategy; |
|
836 } |
|
837 |
|
838 /*! |
|
839 Returns the current edit strategy. |
|
840 |
|
841 \sa setEditStrategy() |
|
842 */ |
|
843 QSqlTableModel::EditStrategy QSqlTableModel::editStrategy() const |
|
844 { |
|
845 Q_D(const QSqlTableModel); |
|
846 return d->strategy; |
|
847 } |
|
848 |
|
849 /*! |
|
850 Reverts all pending changes. |
|
851 |
|
852 \sa revert(), revertRow(), submitAll() |
|
853 */ |
|
854 void QSqlTableModel::revertAll() |
|
855 { |
|
856 Q_D(QSqlTableModel); |
|
857 switch (d->strategy) { |
|
858 case OnFieldChange: |
|
859 break; |
|
860 case OnRowChange: |
|
861 if (d->editIndex != -1) |
|
862 revertRow(d->editIndex); |
|
863 else if (d->insertIndex != -1) |
|
864 revertRow(d->insertIndex); |
|
865 break; |
|
866 case OnManualSubmit: |
|
867 while (!d->cache.isEmpty()) |
|
868 revertRow(d->cache.constBegin().key()); |
|
869 break; |
|
870 } |
|
871 } |
|
872 |
|
873 /*! |
|
874 Reverts all changes for the specified \a row. |
|
875 |
|
876 \sa revert(), revertAll(), submit(), submitAll() |
|
877 */ |
|
878 void QSqlTableModel::revertRow(int row) |
|
879 { |
|
880 if (row < 0) |
|
881 return; |
|
882 |
|
883 Q_D(QSqlTableModel); |
|
884 switch (d->strategy) { |
|
885 case OnFieldChange: |
|
886 break; |
|
887 case OnRowChange: { |
|
888 if (d->editIndex == row) { |
|
889 d->editBuffer.clear(); |
|
890 int oldIndex = d->editIndex; |
|
891 d->editIndex = -1; |
|
892 emit dataChanged(createIndex(oldIndex, 0), createIndex(oldIndex, columnCount())); |
|
893 } else if (d->insertIndex == row) { |
|
894 d->revertInsertedRow(); |
|
895 } |
|
896 break; } |
|
897 case OnManualSubmit: |
|
898 d->revertCachedRow(row); |
|
899 break; |
|
900 } |
|
901 } |
|
902 |
|
903 /*! |
|
904 Returns the primary key for the current table, or an empty |
|
905 QSqlIndex if the table is not set or has no primary key. |
|
906 |
|
907 \sa setTable(), setPrimaryKey(), QSqlDatabase::primaryIndex() |
|
908 */ |
|
909 QSqlIndex QSqlTableModel::primaryKey() const |
|
910 { |
|
911 Q_D(const QSqlTableModel); |
|
912 return d->primaryIndex; |
|
913 } |
|
914 |
|
915 /*! |
|
916 Protected method that allows subclasses to set the primary key to |
|
917 \a key. |
|
918 |
|
919 Normally, the primary index is set automatically whenever you |
|
920 call setTable(). |
|
921 |
|
922 \sa primaryKey(), QSqlDatabase::primaryIndex() |
|
923 */ |
|
924 void QSqlTableModel::setPrimaryKey(const QSqlIndex &key) |
|
925 { |
|
926 Q_D(QSqlTableModel); |
|
927 d->primaryIndex = key; |
|
928 } |
|
929 |
|
930 /*! |
|
931 Returns a pointer to the used QSqlDatabase or 0 if no database was set. |
|
932 */ |
|
933 QSqlDatabase QSqlTableModel::database() const |
|
934 { |
|
935 Q_D(const QSqlTableModel); |
|
936 return d->db; |
|
937 } |
|
938 |
|
939 /*! |
|
940 Sorts the data by \a column with the sort order \a order. |
|
941 This will immediately select data, use setSort() |
|
942 to set a sort order without populating the model with data. |
|
943 |
|
944 \sa setSort(), select(), orderByClause() |
|
945 */ |
|
946 void QSqlTableModel::sort(int column, Qt::SortOrder order) |
|
947 { |
|
948 setSort(column, order); |
|
949 select(); |
|
950 } |
|
951 |
|
952 /*! |
|
953 Sets the sort order for \a column to \a order. This does not |
|
954 affect the current data, to refresh the data using the new |
|
955 sort order, call select(). |
|
956 |
|
957 \sa select(), orderByClause() |
|
958 */ |
|
959 void QSqlTableModel::setSort(int column, Qt::SortOrder order) |
|
960 { |
|
961 Q_D(QSqlTableModel); |
|
962 d->sortColumn = column; |
|
963 d->sortOrder = order; |
|
964 } |
|
965 |
|
966 /*! |
|
967 Returns an SQL \c{ORDER BY} clause based on the currently set |
|
968 sort order. |
|
969 |
|
970 \sa setSort(), selectStatement() |
|
971 */ |
|
972 QString QSqlTableModel::orderByClause() const |
|
973 { |
|
974 Q_D(const QSqlTableModel); |
|
975 QString s; |
|
976 QSqlField f = d->rec.field(d->sortColumn); |
|
977 if (!f.isValid()) |
|
978 return s; |
|
979 |
|
980 QString table = d->tableName; |
|
981 //we can safely escape the field because it would have been obtained from the database |
|
982 //and have the correct case |
|
983 QString field = d->db.driver()->escapeIdentifier(f.name(), QSqlDriver::FieldName); |
|
984 s.append(QLatin1String("ORDER BY ")).append(table).append(QLatin1Char('.')).append(field); |
|
985 s += d->sortOrder == Qt::AscendingOrder ? QLatin1String(" ASC") : QLatin1String(" DESC"); |
|
986 |
|
987 return s; |
|
988 } |
|
989 |
|
990 /*! |
|
991 Returns the index of the field \a fieldName. |
|
992 */ |
|
993 int QSqlTableModel::fieldIndex(const QString &fieldName) const |
|
994 { |
|
995 Q_D(const QSqlTableModel); |
|
996 return d->rec.indexOf(fieldName); |
|
997 } |
|
998 |
|
999 /*! |
|
1000 Returns the SQL \c SELECT statement used internally to populate |
|
1001 the model. The statement includes the filter and the \c{ORDER BY} |
|
1002 clause. |
|
1003 |
|
1004 \sa filter(), orderByClause() |
|
1005 */ |
|
1006 QString QSqlTableModel::selectStatement() const |
|
1007 { |
|
1008 Q_D(const QSqlTableModel); |
|
1009 QString query; |
|
1010 if (d->tableName.isEmpty()) { |
|
1011 d->error = QSqlError(QLatin1String("No table name given"), QString(), |
|
1012 QSqlError::StatementError); |
|
1013 return query; |
|
1014 } |
|
1015 if (d->rec.isEmpty()) { |
|
1016 d->error = QSqlError(QLatin1String("Unable to find table ") + d->tableName, QString(), |
|
1017 QSqlError::StatementError); |
|
1018 return query; |
|
1019 } |
|
1020 |
|
1021 query = d->db.driver()->sqlStatement(QSqlDriver::SelectStatement, |
|
1022 d->tableName, |
|
1023 d->rec, |
|
1024 false); |
|
1025 if (query.isEmpty()) { |
|
1026 d->error = QSqlError(QLatin1String("Unable to select fields from table ") + d->tableName, |
|
1027 QString(), QSqlError::StatementError); |
|
1028 return query; |
|
1029 } |
|
1030 if (!d->filter.isEmpty()) |
|
1031 query.append(QLatin1String(" WHERE ")).append(d->filter); |
|
1032 QString orderBy(orderByClause()); |
|
1033 if (!orderBy.isEmpty()) |
|
1034 query.append(QLatin1Char(' ')).append(orderBy); |
|
1035 |
|
1036 return query; |
|
1037 } |
|
1038 |
|
1039 /*! |
|
1040 Removes \a count columns from the \a parent model, starting at |
|
1041 index \a column. |
|
1042 |
|
1043 Returns if the columns were successfully removed; otherwise |
|
1044 returns false. |
|
1045 |
|
1046 \sa removeRows() |
|
1047 */ |
|
1048 bool QSqlTableModel::removeColumns(int column, int count, const QModelIndex &parent) |
|
1049 { |
|
1050 Q_D(QSqlTableModel); |
|
1051 if (parent.isValid() || column < 0 || column + count > d->rec.count()) |
|
1052 return false; |
|
1053 for (int i = 0; i < count; ++i) |
|
1054 d->rec.remove(column); |
|
1055 if (d->query.isActive()) |
|
1056 return select(); |
|
1057 return true; |
|
1058 } |
|
1059 |
|
1060 /*! |
|
1061 Removes \a count rows starting at \a row. Since this model |
|
1062 does not support hierarchical structures, \a parent must be |
|
1063 an invalid model index. |
|
1064 |
|
1065 Emits the beforeDelete() signal before a row is deleted. When |
|
1066 the edit strategy is OnManualSubmit signal emission is delayed |
|
1067 until submitAll() is called. |
|
1068 |
|
1069 Returns true if all rows could be removed; otherwise returns |
|
1070 false. Detailed error information can be retrieved using |
|
1071 lastError(). |
|
1072 |
|
1073 \sa removeColumns(), insertRows() |
|
1074 */ |
|
1075 bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent) |
|
1076 { |
|
1077 Q_D(QSqlTableModel); |
|
1078 if (parent.isValid() || row < 0 || count <= 0) |
|
1079 return false; |
|
1080 |
|
1081 int i; |
|
1082 switch (d->strategy) { |
|
1083 case OnFieldChange: |
|
1084 case OnRowChange: |
|
1085 for (i = 0; i < count; ++i) { |
|
1086 if (row + i == d->insertIndex) |
|
1087 d->revertInsertedRow(); |
|
1088 else if (!deleteRowFromTable(row + i)) |
|
1089 return false; |
|
1090 } |
|
1091 select(); |
|
1092 break; |
|
1093 case OnManualSubmit: |
|
1094 for (i = 0; i < count; ++i) { |
|
1095 int idx = row + i; |
|
1096 if (idx >= rowCount()) |
|
1097 return false; |
|
1098 if (d->cache.value(idx).op == QSqlTableModelPrivate::Insert) |
|
1099 revertRow(idx); |
|
1100 else { |
|
1101 d->cache[idx].op = QSqlTableModelPrivate::Delete; |
|
1102 d->cache[idx].primaryValues = d->primaryValues(indexInQuery(createIndex(idx, 0)).row()); |
|
1103 emit headerDataChanged(Qt::Vertical, idx, idx); |
|
1104 } |
|
1105 } |
|
1106 break; |
|
1107 } |
|
1108 return true; |
|
1109 } |
|
1110 |
|
1111 /*! |
|
1112 Inserts \a count empty rows at position \a row. Note that \a |
|
1113 parent must be invalid, since this model does not support |
|
1114 parent-child relations. |
|
1115 |
|
1116 Only one row at a time can be inserted when using the |
|
1117 OnFieldChange or OnRowChange update strategies. |
|
1118 |
|
1119 The primeInsert() signal will be emitted for each new row. |
|
1120 Connect to it if you want to initialize the new row with default |
|
1121 values. |
|
1122 |
|
1123 Returns false if the parameters are out of bounds; otherwise |
|
1124 returns true. |
|
1125 |
|
1126 \sa primeInsert(), insertRecord() |
|
1127 */ |
|
1128 bool QSqlTableModel::insertRows(int row, int count, const QModelIndex &parent) |
|
1129 { |
|
1130 Q_D(QSqlTableModel); |
|
1131 if (row < 0 || count <= 0 || row > rowCount() || parent.isValid()) |
|
1132 return false; |
|
1133 |
|
1134 switch (d->strategy) { |
|
1135 case OnFieldChange: |
|
1136 case OnRowChange: |
|
1137 if (count != 1) |
|
1138 return false; |
|
1139 beginInsertRows(parent, row, row); |
|
1140 d->insertIndex = row; |
|
1141 // ### apply dangling changes... |
|
1142 d->clearEditBuffer(); |
|
1143 emit primeInsert(row, d->editBuffer); |
|
1144 break; |
|
1145 case OnManualSubmit: |
|
1146 beginInsertRows(parent, row, row + count - 1); |
|
1147 if (!d->cache.isEmpty()) { |
|
1148 QMap<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = d->cache.end(); |
|
1149 while (it != d->cache.begin() && (--it).key() >= row) { |
|
1150 int oldKey = it.key(); |
|
1151 const QSqlTableModelPrivate::ModifiedRow oldValue = it.value(); |
|
1152 d->cache.erase(it); |
|
1153 it = d->cache.insert(oldKey + count, oldValue); |
|
1154 } |
|
1155 } |
|
1156 |
|
1157 for (int i = 0; i < count; ++i) { |
|
1158 d->cache[row + i] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Insert, |
|
1159 d->rec); |
|
1160 emit primeInsert(row + i, d->cache[row + i].rec); |
|
1161 } |
|
1162 break; |
|
1163 } |
|
1164 endInsertRows(); |
|
1165 return true; |
|
1166 } |
|
1167 |
|
1168 /*! |
|
1169 Inserts the \a record after \a row. If \a row is negative, the |
|
1170 record will be appended to the end. Calls insertRows() and |
|
1171 setRecord() internally. |
|
1172 |
|
1173 Returns true if the row could be inserted, otherwise false. |
|
1174 |
|
1175 \sa insertRows(), removeRows() |
|
1176 */ |
|
1177 bool QSqlTableModel::insertRecord(int row, const QSqlRecord &record) |
|
1178 { |
|
1179 Q_D(QSqlTableModel); |
|
1180 if (row < 0) |
|
1181 row = rowCount(); |
|
1182 if (!insertRow(row, QModelIndex())) |
|
1183 return false; |
|
1184 if (!setRecord(row, record)) |
|
1185 return false; |
|
1186 if (d->strategy == OnFieldChange || d->strategy == OnRowChange) |
|
1187 return submit(); |
|
1188 return true; |
|
1189 } |
|
1190 |
|
1191 /*! \reimp |
|
1192 */ |
|
1193 int QSqlTableModel::rowCount(const QModelIndex &parent) const |
|
1194 { |
|
1195 Q_D(const QSqlTableModel); |
|
1196 |
|
1197 if (parent.isValid()) |
|
1198 return 0; |
|
1199 |
|
1200 int rc = QSqlQueryModel::rowCount(); |
|
1201 if (d->strategy == OnManualSubmit) { |
|
1202 for (QSqlTableModelPrivate::CacheMap::ConstIterator it = d->cache.constBegin(); |
|
1203 it != d->cache.constEnd(); ++it) { |
|
1204 if (it.value().op == QSqlTableModelPrivate::Insert) |
|
1205 ++rc; |
|
1206 } |
|
1207 } else if (d->insertIndex >= 0) { |
|
1208 ++rc; |
|
1209 } |
|
1210 return rc; |
|
1211 } |
|
1212 |
|
1213 /*! |
|
1214 Returns the index of the value in the database result set for the |
|
1215 given \a item in the model. |
|
1216 |
|
1217 The return value is identical to \a item if no columns or rows |
|
1218 have been inserted, removed, or moved around. |
|
1219 |
|
1220 Returns an invalid model index if \a item is out of bounds or if |
|
1221 \a item does not point to a value in the result set. |
|
1222 |
|
1223 \sa QSqlQueryModel::indexInQuery() |
|
1224 */ |
|
1225 QModelIndex QSqlTableModel::indexInQuery(const QModelIndex &item) const |
|
1226 { |
|
1227 Q_D(const QSqlTableModel); |
|
1228 const QModelIndex it = QSqlQueryModel::indexInQuery(item); |
|
1229 if (d->strategy == OnManualSubmit) { |
|
1230 int rowOffset = 0; |
|
1231 QSqlTableModelPrivate::CacheMap::ConstIterator i = d->cache.constBegin(); |
|
1232 while (i != d->cache.constEnd() && i.key() <= it.row()) { |
|
1233 if (i.value().op == QSqlTableModelPrivate::Insert) |
|
1234 ++rowOffset; |
|
1235 ++i; |
|
1236 } |
|
1237 return createIndex(it.row() - rowOffset, it.column(), it.internalPointer()); |
|
1238 } else { |
|
1239 if (d->insertIndex >= 0 && it.row() >= d->insertIndex) |
|
1240 return createIndex(it.row() - 1, it.column(), it.internalPointer()); |
|
1241 } |
|
1242 return it; |
|
1243 } |
|
1244 |
|
1245 /*! |
|
1246 Returns the currently set filter. |
|
1247 |
|
1248 \sa setFilter(), select() |
|
1249 */ |
|
1250 QString QSqlTableModel::filter() const |
|
1251 { |
|
1252 Q_D(const QSqlTableModel); |
|
1253 return d->filter; |
|
1254 } |
|
1255 |
|
1256 /*! |
|
1257 Sets the current filter to \a filter. |
|
1258 |
|
1259 The filter is a SQL \c WHERE clause without the keyword \c WHERE |
|
1260 (for example, \c{name='Josephine')}. |
|
1261 |
|
1262 If the model is already populated with data from a database, |
|
1263 the model re-selects it with the new filter. Otherwise, the filter |
|
1264 will be applied the next time select() is called. |
|
1265 |
|
1266 \sa filter(), select(), selectStatement(), orderByClause() |
|
1267 */ |
|
1268 void QSqlTableModel::setFilter(const QString &filter) |
|
1269 { |
|
1270 Q_D(QSqlTableModel); |
|
1271 d->filter = filter; |
|
1272 if (d->query.isActive()) |
|
1273 select(); |
|
1274 } |
|
1275 |
|
1276 /*! \reimp |
|
1277 */ |
|
1278 void QSqlTableModel::clear() |
|
1279 { |
|
1280 Q_D(QSqlTableModel); |
|
1281 d->clear(); |
|
1282 QSqlQueryModel::clear(); |
|
1283 } |
|
1284 |
|
1285 /*! \reimp |
|
1286 */ |
|
1287 Qt::ItemFlags QSqlTableModel::flags(const QModelIndex &index) const |
|
1288 { |
|
1289 Q_D(const QSqlTableModel); |
|
1290 if (index.internalPointer() || index.column() < 0 || index.column() >= d->rec.count() |
|
1291 || index.row() < 0) |
|
1292 return 0; |
|
1293 if (d->rec.field(index.column()).isReadOnly()) |
|
1294 return Qt::ItemIsSelectable | Qt::ItemIsEnabled; |
|
1295 return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; |
|
1296 } |
|
1297 |
|
1298 /*! |
|
1299 Sets the values at the specified \a row to the values of \a |
|
1300 record. Returns true if all the values could be set; otherwise |
|
1301 returns false. |
|
1302 |
|
1303 \sa record() |
|
1304 */ |
|
1305 bool QSqlTableModel::setRecord(int row, const QSqlRecord &record) |
|
1306 { |
|
1307 Q_D(QSqlTableModel); |
|
1308 Q_ASSERT_X(row >= 0, "QSqlTableModel::setRecord()", "Cannot set a record to a row less than 0"); |
|
1309 if (row >= rowCount()) |
|
1310 return false; |
|
1311 |
|
1312 bool isOk = true; |
|
1313 switch (d->strategy) { |
|
1314 case OnFieldChange: |
|
1315 case OnRowChange: |
|
1316 return d->setRecord(row, record); |
|
1317 case OnManualSubmit: { |
|
1318 QSqlTableModelPrivate::ModifiedRow &mrow = d->cache[row]; |
|
1319 if (mrow.op == QSqlTableModelPrivate::None) { |
|
1320 mrow.op = QSqlTableModelPrivate::Update; |
|
1321 mrow.rec = d->rec; |
|
1322 mrow.primaryValues = d->primaryValues(indexInQuery(createIndex(row, 0)).row()); |
|
1323 } |
|
1324 QString fieldName; |
|
1325 for (int i = 0; i < record.count(); ++i) { |
|
1326 fieldName = record.fieldName(i); |
|
1327 if (d->db.driver()->isIdentifierEscaped(fieldName, QSqlDriver::FieldName)) |
|
1328 fieldName = d->db.driver()->stripDelimiters(fieldName, QSqlDriver::FieldName); |
|
1329 int idx = mrow.rec.indexOf(fieldName); |
|
1330 if (idx == -1) |
|
1331 isOk = false; |
|
1332 else |
|
1333 mrow.rec.setValue(idx, record.value(i)); |
|
1334 } |
|
1335 return isOk; } |
|
1336 } |
|
1337 return false; |
|
1338 } |
|
1339 |
|
1340 QT_END_NAMESPACE |