KDb

KDbTableViewData.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002 Lucijan Busch <lucijan@gmx.at>
3 Copyright (C) 2003 Daniel Molkentin <molkentin@kde.org>
4 Copyright (C) 2003-2017 Jarosław Staniek <staniek@kde.org>
5 Copyright (C) 2014 Michał Poteralski <michalpoteralskikde@gmail.com>
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this program; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21
22 Original Author: Till Busch <till@bux.at>
23 Original Project: buX (www.bux.at)
24*/
25
26#include "KDbTableViewData.h"
27#include "KDbConnection.h"
28#include "KDbConnectionOptions.h"
29#include "KDbCursor.h"
30#include "KDbError.h"
31#include "KDb.h"
32#include "KDbOrderByColumn.h"
33#include "KDbQuerySchema.h"
34#include "KDbRecordEditBuffer.h"
35#include "KDbTableViewColumn.h"
36#include "kdb_debug.h"
37
38#include <QApplication>
39
40#include <unicode/coll.h>
41
42// #define TABLEVIEW_NO_PROCESS_EVENTS
43
44static unsigned short charTable[] = {
45#include "chartable.txt"
46};
47
48//-------------------------------
49
50//! @internal Unicode-aware collator for comparing strings
51class CollatorInstance
52{
53public:
54 CollatorInstance() {
55 UErrorCode status = U_ZERO_ERROR;
56 m_collator = icu::Collator::createInstance(status);
57 if (U_FAILURE(status)) {
58 kdbWarning() << "Could not create instance of collator:" << status;
59 m_collator = nullptr;
60 } else {
61 // enable normalization by default
62 m_collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
63 if (U_FAILURE(status)) {
64 kdbWarning() << "Could not set collator attribute:" << status;
65 }
66 }
67 }
68
69 icu::Collator* getCollator() {
70 return m_collator;
71 }
72
73 ~CollatorInstance() {
74 delete m_collator;
75 }
76
77private:
78 icu::Collator *m_collator;
79};
80
81Q_GLOBAL_STATIC(CollatorInstance, KDb_collator)
82
83//! @internal A functor used in std::sort() in order to sort by a given column
84class LessThanFunctor
85{
86private:
88 QVariant m_leftTmp, m_rightTmp;
89 int m_sortColumn;
90
91 bool (*m_lessThanFunction)(const QVariant&, const QVariant&);
92
93#define CAST_AND_COMPARE(casting) \
94 return left.casting() < right.casting()
95
96 static bool cmpInt(const QVariant& left, const QVariant& right) {
97 CAST_AND_COMPARE(toInt);
98 }
99
100 static bool cmpUInt(const QVariant& left, const QVariant& right) {
101 CAST_AND_COMPARE(toUInt);
102 }
103
104 static bool cmpLongLong(const QVariant& left, const QVariant& right) {
105 CAST_AND_COMPARE(toLongLong);
106 }
107
108 static bool cmpULongLong(const QVariant& left, const QVariant& right) {
109 CAST_AND_COMPARE(toULongLong);
110 }
111
112 static bool cmpDouble(const QVariant& left, const QVariant& right) {
113 CAST_AND_COMPARE(toDouble);
114 }
115
116 static bool cmpDate(const QVariant& left, const QVariant& right) {
117 CAST_AND_COMPARE(toDate);
118 }
119
120 static bool cmpDateTime(const QVariant& left, const QVariant& right) {
121 CAST_AND_COMPARE(toDateTime);
122 }
123
124 static bool cmpTime(const QVariant& left, const QVariant& right) {
125 CAST_AND_COMPARE(toDate);
126 }
127
128 static bool cmpString(const QVariant& left, const QVariant& right) {
129 const QString &as = left.toString();
130 const QString &bs = right.toString();
131
132 const QChar *a = as.isEmpty() ? nullptr : as.unicode();
133 const QChar *b = bs.isEmpty() ? nullptr : bs.unicode();
134
135 if (a == nullptr) {
136 return b != nullptr;
137 }
138 if (a == b || b == nullptr) {
139 return false;
140 }
141
142 int len = qMin(as.length(), bs.length());
143 forever {
144 unsigned short au = a->unicode();
145 unsigned short bu = b->unicode();
146 au = (au <= 0x17e ? charTable[au] : 0xffff);
147 bu = (bu <= 0x17e ? charTable[bu] : 0xffff);
148
149 if (len <= 0)
150 return false;
151 len--;
152
153 if (au != bu)
154 return au < bu;
155 a++;
156 b++;
157 }
158 return false;
159 }
160
161 static bool cmpStringWithCollator(const QVariant& left, const QVariant& right) {
162 const QString &as = left.toString();
163 const QString &bs = right.toString();
164 return icu::Collator::LESS == KDb_collator->getCollator()->compare(
165 (const UChar *)as.constData(), as.size(),
166 (const UChar *)bs.constData(), bs.size());
167 }
168
169 //! Compare function for BLOB data (QByteArray). Uses size as the weight.
170 static bool cmpBLOB(const QVariant& left, const QVariant& right) {
171 return left.toByteArray().size() < right.toByteArray().size();
172 }
173
174public:
175 LessThanFunctor()
176 : m_order(KDbOrderByColumn::SortOrder::Ascending)
177 , m_sortColumn(-1)
178 , m_lessThanFunction(nullptr)
179 {
180 }
181
182 void setColumnType(const KDbField& field) {
183 const KDbField::Type t = field.type();
184 if (field.isTextType())
185 m_lessThanFunction = &cmpString;
187 m_lessThanFunction = &cmpDouble;
188 else if (t == KDbField::Integer && field.isUnsigned())
189 m_lessThanFunction = &cmpUInt;
190 else if (t == KDbField::Boolean || KDbField::isNumericType(t))
191 m_lessThanFunction = &cmpInt; //other integers
192 else if (t == KDbField::BigInteger) {
193 if (field.isUnsigned())
194 m_lessThanFunction = &cmpULongLong;
195 else
196 m_lessThanFunction = &cmpLongLong;
197 } else if (t == KDbField::Date)
198 m_lessThanFunction = &cmpDate;
199 else if (t == KDbField::Time)
200 m_lessThanFunction = &cmpTime;
201 else if (t == KDbField::DateTime)
202 m_lessThanFunction = &cmpDateTime;
203 else if (t == KDbField::BLOB)
204 //! @todo allow users to define BLOB sorting function?
205 m_lessThanFunction = &cmpBLOB;
206 else { // anything else
207 // check if CollatorInstance is not destroyed and has valid collator
208 if (!KDb_collator.isDestroyed() && KDb_collator->getCollator()) {
209 m_lessThanFunction = &cmpStringWithCollator;
210 }
211 else {
212 m_lessThanFunction = &cmpString;
213 }
214 }
215 }
216
217 void setSortOrder(KDbOrderByColumn::SortOrder order) {
218 m_order = order;
219 }
220
221 void setSortColumn(int column) {
222 m_sortColumn = column;
223 }
224
225#define _IIF(a,b) ((a) ? (b) : !(b))
226
227 //! Main comparison operator that takes column number, type and order into account
228 bool operator()(KDbRecordData* record1, KDbRecordData* record2) {
229 // compare NULLs : NULL is smaller than everything
230 if ((m_leftTmp = record1->at(m_sortColumn)).isNull())
231 return _IIF(m_order == KDbOrderByColumn::SortOrder::Ascending,
232 !record2->at(m_sortColumn).isNull());
233 if ((m_rightTmp = record2->at(m_sortColumn)).isNull())
234 return m_order == KDbOrderByColumn::SortOrder::Descending;
235
236 return _IIF(m_order == KDbOrderByColumn::SortOrder::Ascending,
237 m_lessThanFunction(m_leftTmp, m_rightTmp));
238 }
239};
240#undef _IIF
241#undef CAST_AND_COMPARE
242
243//! @internal
244class Q_DECL_HIDDEN KDbTableViewData::Private
245{
246public:
247 Private()
248 : sortColumn(0)
249 , realSortColumn(0)
250 , sortOrder(KDbOrderByColumn::SortOrder::Ascending)
251 , type(1)
252 , pRecordEditBuffer(nullptr)
253 , readOnly(false)
254 , insertingEnabled(true)
255 , containsRecordIdInfo(false)
256 , autoIncrementedColumn(-2) {
257 }
258
259 ~Private() {
260 delete pRecordEditBuffer;
261 }
262
263 //! Number of physical columns
264 int realColumnCount;
265
266 /*! Columns information */
268
269 /*! Visible columns information */
270 QList<KDbTableViewColumn*> visibleColumns;
271
272 //! (Logical) sorted column number, set by setSorting().
273 //! Can differ from realSortColumn if there's lookup column used.
274 int sortColumn;
275
276 //! Real sorted column number, set by setSorting(), used by cmp*() methods
277 int realSortColumn;
278
279 //! Specifies sorting order
281
282 LessThanFunctor lessThanFunctor;
283
284 short type;
285
286 KDbRecordEditBuffer *pRecordEditBuffer;
287
288 KDbCursor *cursor;
289
290 KDbResultInfo result;
291
292 QList<int> visibleColumnIDs;
293 QList<int> globalColumnIDs;
294
295 bool readOnly;
296
297 bool insertingEnabled;
298
299 //! @see KDbTableViewData::containsRecordIdInfo()
300 bool containsRecordIdInfo;
301
302 mutable int autoIncrementedColumn;
303};
304
305//-------------------------------
306
308 : QObject()
310 , d(new Private)
311{
312 d->realColumnCount = 0;
313 d->cursor = nullptr;
314}
315
316// db-aware ctor
318 : QObject()
320 , d(new Private)
321{
322 d->cursor = c;
323 d->containsRecordIdInfo = d->cursor->containsRecordIdInfo();
324 if (d->cursor && d->cursor->query()) {
325 const KDbQuerySchema::FieldsExpandedMode fieldsExpandedMode
328 d->realColumnCount = d->cursor->query()->fieldsExpanded(
329 d->cursor->connection(), fieldsExpandedMode).count();
330 } else {
331 d->realColumnCount = d->columns.count() + (d->containsRecordIdInfo ? 1 : 0);
332 }
333
334 // Allocate KDbTableViewColumn objects for each visible query column
335 const KDbQueryColumnInfo::Vector fields
336 = d->cursor->query()->fieldsExpanded(d->cursor->connection());
337 const int fieldCount = fields.count();
338 for (int i = 0;i < fieldCount;i++) {
339 KDbQueryColumnInfo *ci = fields[i];
340 if (ci->isVisible()) {
341 KDbQueryColumnInfo *visibleLookupColumnInfo = nullptr;
342 if (ci->indexForVisibleLookupValue() != -1) {
343 // Lookup field is defined
344 visibleLookupColumnInfo = d->cursor->query()->expandedOrInternalField(
345 d->cursor->connection(), ci->indexForVisibleLookupValue());
346 }
347 KDbTableViewColumn* col = new KDbTableViewColumn(*d->cursor->query(), ci, visibleLookupColumnInfo);
348 addColumn(col);
349 }
350 }
351}
352
354 KDbField::Type keyType, KDbField::Type valueType)
356{
357 KDbField *keyField = new KDbField(QLatin1String("key"), keyType);
358 keyField->setPrimaryKey(true);
359 KDbTableViewColumn *keyColumn
360 = new KDbTableViewColumn(keyField, KDbTableViewColumn::FieldIsOwned::Yes);
361 keyColumn->setVisible(false);
362 addColumn(keyColumn);
363
364 KDbField *valueField = new KDbField(QLatin1String("value"), valueType);
365 KDbTableViewColumn *valueColumn
366 = new KDbTableViewColumn(valueField, KDbTableViewColumn::FieldIsOwned::Yes);
367 addColumn(valueColumn);
368
369 int cnt = qMin(keys.count(), values.count());
371 QList<QVariant>::ConstIterator it_values = values.constBegin();
372 for (;cnt > 0;++it_keys, ++it_values, cnt--) {
373 KDbRecordData *record = new KDbRecordData(2);
374 (*record)[0] = (*it_keys);
375 (*record)[1] = (*it_values);
376 append(record);
377 }
378}
379
381 : KDbTableViewData(QList<QVariant>(), QList<QVariant>(), keyType, valueType)
382{
383}
384
385KDbTableViewData::~KDbTableViewData()
386{
387 emit destroying();
388 clearInternal(false /* !processEvents */);
389 qDeleteAll(d->columns);
390 delete d;
391}
392
394{
395 d->cursor = nullptr;
397}
398
400{
401 d->columns.append(col);
402 col->setData(this);
403 if (col->isVisible()) {
404 d->visibleColumns.append(col);
405 d->visibleColumnIDs.append(d->visibleColumns.count() - 1);
406 d->globalColumnIDs.append(d->columns.count() - 1);
407 } else {
408 d->visibleColumnIDs.append(-1);
409 }
410 d->autoIncrementedColumn = -2; //clear cache;
411 if (!d->cursor || !d->cursor->query()) {
412 d->realColumnCount = d->columns.count() + (d->containsRecordIdInfo ? 1 : 0);
413 }
414}
415
417{
418 if (column.isVisible()) { // column made visible
419 int indexInGlobal = d->columns.indexOf(const_cast<KDbTableViewColumn*>(&column));
420 // find previous column that is visible
421 int prevIndexInGlobal = indexInGlobal - 1;
422 while (prevIndexInGlobal >= 0 && d->visibleColumnIDs[prevIndexInGlobal] == -1) {
423 prevIndexInGlobal--;
424 }
425 int indexInVisible = prevIndexInGlobal + 1;
426 // update
427 d->visibleColumns.insert(indexInVisible, const_cast<KDbTableViewColumn*>(&column));
428 d->visibleColumnIDs[indexInGlobal] = indexInVisible;
429 d->globalColumnIDs.insert(indexInVisible, indexInGlobal);
430 for (int i = indexInGlobal + 1; i < d->columns.count(); i++) { // increment ids of the rest
431 if (d->visibleColumnIDs[i] >= 0) {
432 d->visibleColumnIDs[i]++;
433 }
434 }
435 }
436 else { // column made invisible
437 int indexInVisible = d->visibleColumns.indexOf(const_cast<KDbTableViewColumn*>(&column));
438 d->visibleColumns.removeAt(indexInVisible);
439 int indexInGlobal = globalIndexOfVisibleColumn(indexInVisible);
440 d->visibleColumnIDs[indexInGlobal] = -1;
441 d->globalColumnIDs.removeAt(indexInVisible);
442 }
443}
444
446{
447 return d->globalColumnIDs.value(visibleIndex, -1);
448}
449
450int KDbTableViewData::visibleColumnIndex(int globalIndex) const
451{
452 return d->visibleColumnIDs.value(globalIndex, -1);
453}
454
455int KDbTableViewData::columnCount() const
456{
457 return d->columns.count();
458}
459
461{
462 return d->visibleColumns.count();
463}
464
466{
467 return &d->columns;
468}
469
471{
472 return &d->visibleColumns;
473}
474
476{
477 return d->columns.value(index);
478}
479
481{
482 return d->visibleColumns.value(index);
483}
484
486{
487 return d->cursor != nullptr;
488}
489
490KDbCursor* KDbTableViewData::cursor() const
491{
492 return d->cursor;
493}
494
496{
497 return d->insertingEnabled;
498}
499
501{
502 return d->pRecordEditBuffer;
503}
504
506{
507 return d->result;
508}
509
511{
512 return d->containsRecordIdInfo;
513}
514
516{
517 return new KDbRecordData(d->realColumnCount);
518}
519
521{
522 if (d->cursor && d->cursor->query() && d->cursor->query()->masterTable())
523 return d->cursor->query()->masterTable()->name();
524 return QString();
525}
526
528{
529 d->sortOrder = order;
530 if (column < 0 || column >= d->columns.count()) {
531 d->sortColumn = -1;
532 d->realSortColumn = -1;
533 return;
534 }
535 // find proper column information for sorting (lookup column points to alternate column with visible data)
536 const KDbTableViewColumn *tvcol = d->columns.at(column);
537 const KDbQueryColumnInfo* visibleLookupColumnInfo = tvcol->visibleLookupColumnInfo();
538 const KDbField *field = visibleLookupColumnInfo ? visibleLookupColumnInfo->field() : tvcol->field();
539 d->sortColumn = column;
540 d->realSortColumn = tvcol->columnInfo()->indexForVisibleLookupValue() != -1
541 ? tvcol->columnInfo()->indexForVisibleLookupValue() : d->sortColumn;
542
543 // setup compare functor
544 d->lessThanFunctor.setColumnType(*field);
545 d->lessThanFunctor.setSortOrder(d->sortOrder);
546 d->lessThanFunctor.setSortColumn(column);
547}
548
550{
551 return d->sortColumn;
552}
553
555{
556 return d->sortOrder;
557}
558
560{
561 if (d->sortColumn < 0 || d->sortColumn >= d->columns.count()) {
562 return;
563 }
564 std::sort(begin(), end(), d->lessThanFunctor);
565}
566
568{
569 if (d->readOnly == set)
570 return;
571 d->readOnly = set;
572 if (d->readOnly)
573 setInsertingEnabled(false);
574}
575
577{
578 if (d->insertingEnabled == set)
579 return;
580 d->insertingEnabled = set;
581 if (d->insertingEnabled)
582 setReadOnly(false);
583}
584
586{
587 //init record edit buffer
588 if (!d->pRecordEditBuffer)
589 d->pRecordEditBuffer = new KDbRecordEditBuffer(isDBAware());
590 else
591 d->pRecordEditBuffer->clear();
592}
593
595 int colnum, KDbTableViewColumn* col, QVariant* newval, bool allowSignals,
596 QVariant *visibleValueForLookupField)
597{
598 if (!record || !newval) {
599 return false;
600 }
601 d->result.clear();
602 if (allowSignals)
603 emit aboutToChangeCell(record, colnum, newval, &d->result);
604 if (!d->result.success)
605 return false;
606
607 //kdbDebug() << "column #" << colnum << " = " << newval.toString();
608 if (!col) {
609 kdbWarning() << "column #" << colnum << "not found! col==0";
610 return false;
611 }
612 if (!d->pRecordEditBuffer)
613 d->pRecordEditBuffer = new KDbRecordEditBuffer(isDBAware());
614 if (d->pRecordEditBuffer->isDBAware()) {
615 if (!(col->columnInfo())) {
616 kdbWarning() << "column #" << colnum << " not found!";
617 return false;
618 }
619 d->pRecordEditBuffer->insert(col->columnInfo(), *newval);
620
621 if (col->visibleLookupColumnInfo() && visibleValueForLookupField) {
622 //this is value for lookup table: update visible value as well
623 d->pRecordEditBuffer->insert(col->visibleLookupColumnInfo(), *visibleValueForLookupField);
624 }
625 return true;
626 }
627 if (!(col->field())) {
628 kdbWarning() << "column #" << colnum << "not found!";
629 return false;
630 }
631 //not db-aware:
632 const QString colname = col->field()->name();
633 if (colname.isEmpty()) {
634 kdbWarning() << "column #" << colnum << "not found!";
635 return false;
636 }
637 d->pRecordEditBuffer->insert(colname, *newval);
638 return true;
639}
640
643 const QVariant &newval, bool allowSignals)
644{
645 QVariant newv(newval);
646 return updateRecordEditBufferRef(record, colnum, col, &newv, allowSignals);
647}
648
650 const QVariant &newval, bool allowSignals)
651{
652 KDbTableViewColumn* col = d->columns.value(colnum);
653 QVariant newv(newval);
654 return col ? updateRecordEditBufferRef(record, colnum, col, &newv, allowSignals) : false;
655}
656
657//! Get a new value (if present in the buffer), or the old one (taken here for optimization)
658static inline void saveRecordGetValue(const QVariant **pval, KDbCursor *cursor,
659 KDbRecordEditBuffer *pRecordEditBuffer,
661 KDbRecordData *record, KDbField *f, QVariant* val, int col)
662{
663 if (!*pval) {
664 *pval = cursor
665 ? pRecordEditBuffer->at( (**it_f)->columnInfo(),
666 record->at(col).isNull() /* useDefaultValueIfPossible */ )
667 : pRecordEditBuffer->at( *f );
668 *val = *pval ? **pval : record->at(col); /* get old value */
669 //kdbDebug() << col << *(**it_f)->columnInfo() << "val:" << *val;
670 }
671}
672
673//! @todo if there're multiple views for this data, we need multiple buffers!
674bool KDbTableViewData::saveRecord(KDbRecordData *record, bool insert, bool repaint)
675{
676 if (!d->pRecordEditBuffer)
677 return true; //nothing to do
678
679 //check constraints:
680 //-check if every NOT NULL and NOT EMPTY field is filled
682 int colIndex = 0;
683 const QVariant *pval = nullptr;
684 QVariant val;
685 for (;it_f != d->columns.constEnd() && colIndex < record->count();++it_f, ++colIndex) {
686 KDbField *f = (*it_f)->field();
687 if (f->isNotNull()) {
688 saveRecordGetValue(&pval, d->cursor, d->pRecordEditBuffer, &it_f, record, f, &val, colIndex);
689 //check it
690 if (val.isNull() && !f->isAutoIncrement()) {
691 //NOT NULL violated
692 d->result.message = tr("\"%1\" column requires a value to be entered.").arg(f->captionOrName())
694 d->result.description = tr("The column's constraint is declared as NOT NULL.");
695 d->result.column = colIndex;
696 return false;
697 }
698 }
699 if (f->isNotEmpty()) {
700 saveRecordGetValue(&pval, d->cursor, d->pRecordEditBuffer, &it_f, record, f, &val, colIndex);
701 if (!f->isAutoIncrement() && (val.isNull() || KDb::isEmptyValue(f->type(), val))) {
702 //NOT EMPTY violated
703 d->result.message = tr("\"%1\" column requires a value to be entered.").arg(f->captionOrName())
705 d->result.description = tr("The column's constraint is declared as NOT EMPTY.");
706 d->result.column = colIndex;
707 return false;
708 }
709 }
710 }
711
712 if (d->cursor) {//db-aware
713 if (insert) {
714 if (!d->cursor->insertRecord(record, d->pRecordEditBuffer,
715 d->containsRecordIdInfo /*also retrieve ROWID*/))
716 {
717 d->result.message = tr("Record inserting failed.") + QLatin1String("\n\n")
719 KDb::getHTMLErrorMesage(*d->cursor, &d->result);
720
721 /* if (desc)
722 *desc = */
723 /*! @todo use KexiMainWindow::showErrorMessage(const QString &title, KDbObject *obj)
724 after it will be moved somewhere to KDb (this will require moving other
725 showErrorMessage() methods from KexiMainWindow to libkexiutils....)
726 then: just call: *desc = KDb::errorMessage(d->cursor); */
727 return false;
728 }
729 } else { // record updating
730 if (!d->cursor->updateRecord(static_cast<KDbRecordData*>(record), d->pRecordEditBuffer,
731 d->containsRecordIdInfo /*use ROWID*/))
732 {
733 d->result.message = tr("Record changing failed.") + QLatin1String("\n\n")
735//! @todo set d->result.column if possible
736 KDb::getHTMLErrorMesage(*d->cursor, &d->result.description);
737 return false;
738 }
739 }
740 } else {//not db-aware version
741 KDbRecordEditBuffer::SimpleMap b = d->pRecordEditBuffer->simpleBuffer();
743 int i = -1;
744 foreach(KDbTableViewColumn *col, d->columns) {
745 i++;
746 if (col->field()->name() == it.key()) {
747 kdbDebug() << col->field()->name() << ": " << record->at(i).toString()
748 << " -> " << it.value().toString();
749 (*record)[i] = it.value();
750 }
751 }
752 }
753 }
754
755 d->pRecordEditBuffer->clear();
756
757 if (repaint)
758 emit recordRepaintRequested(record);
759 return true;
760}
761
762bool KDbTableViewData::saveRecordChanges(KDbRecordData *record, bool repaint)
763{
764 d->result.clear();
765 emit aboutToUpdateRecord(record, d->pRecordEditBuffer, &d->result);
766 if (!d->result.success)
767 return false;
768
769 if (saveRecord(record, false /*update*/, repaint)) {
770 emit recordUpdated(record);
771 return true;
772 }
773 return false;
774}
775
776bool KDbTableViewData::saveNewRecord(KDbRecordData *record, bool repaint)
777{
778 d->result.clear();
779 emit aboutToInsertRecord(record, &d->result, repaint);
780 if (!d->result.success)
781 return false;
782
783 if (saveRecord(record, true /*insert*/, repaint)) {
784 emit recordInserted(record, repaint);
785 return true;
786 }
787 return false;
788}
789
791{
792 d->result.clear();
793 emit aboutToDeleteRecord(record, &d->result, repaint);
794 if (!d->result.success)
795 return false;
796
797 if (d->cursor) {//db-aware
798 d->result.success = false;
799 if (!d->cursor->deleteRecord(static_cast<KDbRecordData*>(record), d->containsRecordIdInfo /*use ROWID*/)) {
800 d->result.message = tr("Record deleting failed.");
801 //! @todo use KDberrorMessage() for description as in KDbTableViewData::saveRecord() */
802 KDb::getHTMLErrorMesage(*d->cursor, &d->result);
803 d->result.success = false;
804 return false;
805 }
806 }
807
808 int index = indexOf(record);
809 if (index == -1) {
810 //aah - this shouldn't be!
811 kdbWarning() << "!removeRef() - IMPL. ERROR?";
812 d->result.success = false;
813 return false;
814 }
815 removeAt(index);
816 emit recordDeleted();
817 return true;
818}
819
820void KDbTableViewData::deleteRecords(const QList<int> &recordsToDelete, bool repaint)
821{
822 Q_UNUSED(repaint);
823
824 if (recordsToDelete.isEmpty())
825 return;
826 int last_r = 0;
827 KDbTableViewDataIterator it(begin());
828 for (QList<int>::ConstIterator r_it = recordsToDelete.constBegin(); r_it != recordsToDelete.constEnd(); ++r_it) {
829 for (; last_r < (*r_it); last_r++)
830 ++it;
831 it = erase(it); /* this will delete *it */
832 last_r++;
833 }
834//DON'T CLEAR BECAUSE KexiTableViewPropertyBuffer will clear BUFFERS!
835//--> emit reloadRequested(); //! \todo more effective?
836 emit recordsDeleted(recordsToDelete);
837}
838
839void KDbTableViewData::insertRecord(KDbRecordData *record, int index, bool repaint)
840{
841 insert(index = qMin(index, count()), record);
842 emit recordInserted(record, index, repaint);
843}
844
845void KDbTableViewData::clearInternal(bool processEvents)
846{
848//! @todo this is time consuming: find better data model
849 const int c = count();
850#ifndef TABLEVIEW_NO_PROCESS_EVENTS
851 const bool _processEvents = processEvents && !qApp->closingDown();
852#endif
853 for (int i = 0; i < c; i++) {
854 removeLast();
855#ifndef TABLEVIEW_NO_PROCESS_EVENTS
856 if (_processEvents && i % 1000 == 0)
857 qApp->processEvents(QEventLoop::AllEvents, 1);
858#endif
859 }
860}
861
863{
865
866 bool res = true;
867 if (d->cursor) {
868 //db-aware
869 res = d->cursor->deleteAllRecords();
870 }
871
872 if (repaint)
873 emit reloadRequested();
874 return res;
875}
876
878{
879 if (d->autoIncrementedColumn == -2) {
880 //find such a column
881 d->autoIncrementedColumn = -1;
882 foreach(KDbTableViewColumn *col, d->columns) {
883 d->autoIncrementedColumn++;
884 if (col->field()->isAutoIncrement())
885 break;
886 }
887 }
888 return d->autoIncrementedColumn;
889}
890
892{
893 if (!d->cursor)
894 return false;
895 if (!d->cursor->moveFirst() && d->cursor->result().isError())
896 return false;
897
898#ifndef TABLEVIEW_NO_PROCESS_EVENTS
899 const bool processEvents = !qApp->closingDown();
900#endif
901
902 for (int i = 0;!d->cursor->eof();i++) {
903 KDbRecordData *record = d->cursor->storeCurrentRecord();
904 if (!record) {
905 clear();
906 return false;
907 }
908// record->debug();
909 append(record);
910 if (!d->cursor->moveNext() && d->cursor->result().isError()) {
911 clear();
912 return false;
913 }
914#ifndef TABLEVIEW_NO_PROCESS_EVENTS
915 if (processEvents && (i % 1000) == 0)
916 qApp->processEvents(QEventLoop::AllEvents, 1);
917#endif
918 }
919 return true;
920}
921
923{
924 return d->readOnly || (d->cursor && d->cursor->connection()->options()->isReadOnly());
925}
926
927// static
929{
930 return tr("Please correct data in this record or use the \"Cancel record changes\" function.");
931}
932
933QDebug operator<<(QDebug dbg, const KDbTableViewData &data)
934{
935 dbg.nospace() << "TableViewData(";
936 dbg.space() << "sortColumn:" << data.sortColumn()
937 << "sortOrder:" << (data.sortOrder() == KDbOrderByColumn::SortOrder::Ascending
938 ? "asc" : "desc")
939 << "isDBAware:" << data.isDBAware()
940 << "dbTableName:" << data.dbTableName()
941 << "cursor:" << (data.cursor() ? "yes" : "no")
942 << "columnCount:" << data.columnCount()
943 << "count:" << data.count()
944 << "autoIncrementedColumn:" << data.autoIncrementedColumn()
945 << "visibleColumnCount:" << data.visibleColumnCount()
946 << "isReadOnly:" << data.isReadOnly()
947 << "isInsertingEnabled:" << data.isInsertingEnabled()
948 << "containsRecordIdInfo:" << data.containsRecordIdInfo()
949 << "result:" << data.result();
950 dbg.nospace() << ")";
951 return dbg.space();
952}
KDbConnectionOptions * options()
Provides database cursor functionality.
Definition KDbCursor.h:69
bool deleteRecord(KDbRecordData *data, bool useRecordId=false)
virtual bool moveNext()
bool updateRecord(KDbRecordData *data, KDbRecordEditBuffer *buf, bool useRecordId=false)
KDbRecordData * storeCurrentRecord() const
bool insertRecord(KDbRecordData *data, KDbRecordEditBuffer *buf, bool getRecrordId=false)
bool moveFirst()
KDbQuerySchema * query() const
bool containsRecordIdInfo() const
bool deleteAllRecords()
bool eof() const
Definition KDbCursor.h:147
KDbConnection * connection()
Meta-data for a field.
Definition KDbField.h:72
bool isNumericType() const
Definition KDbField.h:317
bool isTextType() const
Definition KDbField.h:353
QString name() const
Definition KDbField.cpp:256
bool isNotEmpty() const
Definition KDbField.h:307
bool isAutoIncrement() const
Definition KDbField.h:282
bool isNotNull() const
Definition KDbField.h:302
void setPrimaryKey(bool p)
Definition KDbField.cpp:867
QString captionOrName() const
Definition KDbField.cpp:326
@ Integer
Definition KDbField.h:90
@ Boolean
Definition KDbField.h:92
@ BigInteger
Definition KDbField.h:91
bool isFPNumericType() const
Definition KDbField.h:335
Type type() const
Definition KDbField.cpp:379
bool isUnsigned() const
if the type has the unsigned attribute
Definition KDbField.h:515
KDbOrderByColumn provides information about a single query column used for sorting.
SortOrder
Column sort order.
Helper class that assigns additional information for the column in a query.
int indexForVisibleLookupValue() const
KDbQueryColumnInfo::Vector fieldsExpanded(KDbConnection *conn, FieldsExpandedMode mode=FieldsExpandedMode::Default) const
FieldsExpandedMode
Mode for fieldsExpanded() and visibleFieldsExpanded()
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
@ WithInternalFieldsAndRecordId
Like WithInternalFields but record ID (big int type) field is appended after internal fields.
KDbQueryColumnInfo * expandedOrInternalField(KDbConnection *conn, int index) const
KDbTableSchema * masterTable() const
Structure for storing single record with type information.
const QVariant & at(int i) const
provides data for single edited database record
const QVariant * at(KDbQueryColumnInfo *ci, bool useDefaultValueIfPossible=true) const
void insert(KDbQueryColumnInfo *ci, const QVariant &val)
Inserts value val for db-aware buffer's column ci Does nothing if ci is nullptr.
bool success
result of the operation, true by default
Definition KDbError.h:124
QString description
Detailed error description, empty by default.
Definition KDbError.h:129
QString message
Error message, empty by default.
Definition KDbError.h:128
int column
Faulty column, -1 (the default) means: there is no faulty column.
Definition KDbError.h:130
bool isError() const
Definition KDbResult.cpp:64
Definition of a single column for table view.
bool isVisible() const
Column visibility. By default column is visible.
KDbQueryColumnInfo * visibleLookupColumnInfo()
void setVisible(bool v)
Changes column visibility.
KDbQueryColumnInfo * columnInfo()
void setData(KDbTableViewData *data)
used by KDbTableViewData::addColumn()
A list of records to allow configurable sorting and more.
bool deleteRecord(KDbRecordData *record, bool repaint=false)
virtual bool isInsertingEnabled() const
int visibleColumnCount() const
bool updateRecordEditBuffer(KDbRecordData *record, int colnum, KDbTableViewColumn *col, const QVariant &newval, bool allowSignals=true)
QList< KDbTableViewColumn * > * visibleColumns()
const KDbResultInfo & result() const
KDbTableViewColumn * column(int c)
void setSorting(int column, KDbOrderByColumn::SortOrder order=KDbOrderByColumn::SortOrder::Ascending)
QString dbTableName() const
void recordUpdated(KDbRecordData *)
Current record has been updated.
virtual bool isReadOnly() const
static QString messageYouCanImproveData()
bool updateRecordEditBufferRef(KDbRecordData *record, int colnum, KDbTableViewColumn *col, QVariant *newval, bool allowSignals=true, QVariant *visibleValueForLookupField=nullptr)
KDbTableViewData()
Non-db-aware version.
void aboutToInsertRecord(KDbRecordData *record, KDbResultInfo *result, bool repaint)
void addColumn(KDbTableViewColumn *col)
int globalIndexOfVisibleColumn(int visibleIndex) const
void aboutToChangeCell(KDbRecordData *record, int colnum, QVariant *newValue, KDbResultInfo *result)
KDbTableViewColumn * visibleColumn(int index)
KDbOrderByColumn::SortOrder sortOrder() const
void recordInserted(KDbRecordData *, bool repaint)
A record has been inserted.
virtual void clearInternal(bool processEvents=true)
void aboutToUpdateRecord(KDbRecordData *record, KDbRecordEditBuffer *buffer, KDbResultInfo *result)
void aboutToDeleteRecord(KDbRecordData *record, KDbResultInfo *result, bool repaint)
void insertRecord(KDbRecordData *record, int index, bool repaint=false)
QList< KDbTableViewColumn * > * columns()
virtual void setReadOnly(bool set)
virtual bool deleteAllRecords(bool repaint=false)
int visibleColumnIndex(int globalIndex) const
void recordsDeleted(const QList< int > &recordsToDelete)
Records have been deleted.
void sort()
Sorts this data using previously set order.
void columnVisibilityChanged(const KDbTableViewColumn &column)
Used by KDbTableViewColumn::setVisible()
void reloadRequested()
Displayed data needs to be reloaded in all presenters.
bool containsRecordIdInfo() const
int autoIncrementedColumn() const
void recordDeleted()
Current record has been deleted.
void deleteRecords(const QList< int > &recordsToDelete, bool repaint=false)
KDbRecordData * createItem() const
Creates a single record data with proper number of columns.
KDbRecordEditBuffer * recordEditBuffer() const
virtual void setInsertingEnabled(bool set)
Autodeleting list.
Definition KDbUtils.h:245
Q_SCRIPTABLE CaptureState status()
Type type(const QSqlDatabase &db)
KDB_EXPORT bool isEmptyValue(KDbField::Type type, const QVariant &value)
Definition KDb.cpp:429
KDB_EXPORT void getHTMLErrorMesage(const KDbResultable &resultable, QString *msg, QString *details)
Sets HTML-formatted error message with extra details obtained from result object.
Definition KDb.cpp:505
char16_t & unicode()
QDebug & nospace()
QDebug & space()
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
qsizetype indexOf(const AT &value, qsizetype from) const const
iterator insert(const_iterator before, parameter_type value)
bool isEmpty() const const
void removeAt(qsizetype i)
T value(qsizetype i) const const
typedef ConstIterator
const_iterator constBegin() const const
const_iterator constEnd() const const
void deleteLater()
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(Args &&... args) const const
const QChar * constData() const const
bool isEmpty() const const
qsizetype length() const const
qsizetype size() const const
const QChar * unicode() const const
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
bool isNull() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 19 2024 12:00:08 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.