KDb

KDbTableViewData.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2002 Lucijan Busch <[email protected]>
3  Copyright (C) 2003 Daniel Molkentin <[email protected]>
4  Copyright (C) 2003-2017 Jarosław Staniek <[email protected]>
5  Copyright (C) 2014 Michał Poteralski <[email protected]>
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 <[email protected]>
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 
44 static unsigned short charTable[] = {
45 #include "chartable.txt"
46 };
47 
48 //-------------------------------
49 
50 //! @internal Unicode-aware collator for comparing strings
51 class CollatorInstance
52 {
53 public:
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 
77 private:
78  icu::Collator *m_collator;
79 };
80 
81 Q_GLOBAL_STATIC(CollatorInstance, KDb_collator)
82 
83 //! @internal A functor used in qSort() in order to sort by a given column
84 class LessThanFunctor
85 {
86 private:
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 
174 public:
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
244 class Q_DECL_HIDDEN KDbTableViewData::Private
245 {
246 public:
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
280  KDbOrderByColumn::SortOrder sortOrder;
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)
355  : KDbTableViewData()
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 
385 KDbTableViewData::~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 
450 int KDbTableViewData::visibleColumnIndex(int globalIndex) const
451 {
452  return d->visibleColumnIDs.value(globalIndex, -1);
453 }
454 
455 int 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 
490 KDbCursor* 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  qSort(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 
642  KDbTableViewColumn* col,
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)
658 static 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!
674 bool 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
681  QList<KDbTableViewColumn*>::ConstIterator it_f(d->columns.constBegin());
682  int col = 0;
683  const QVariant *pval = nullptr;
684  QVariant val;
685  for (;it_f != d->columns.constEnd() && col < record->count();++it_f, ++col) {
686  KDbField *f = (*it_f)->field();
687  if (f->isNotNull()) {
688  saveRecordGetValue(&pval, d->cursor, d->pRecordEditBuffer, &it_f, record, f, &val, col);
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 = col;
696  return false;
697  }
698  }
699  if (f->isNotEmpty()) {
700  saveRecordGetValue(&pval, d->cursor, d->pRecordEditBuffer, &it_f, record, f, &val, col);
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 = col;
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 
762 bool 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 
776 bool 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 
820 void 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 
839 void KDbTableViewData::insertRecord(KDbRecordData *record, int index, bool repaint)
840 {
841  insert(index = qMin(index, count()), record);
842  emit recordInserted(record, index, repaint);
843 }
844 
845 void 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 {
864  clearInternal();
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 
933 QDebug 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 }
KDbTableViewData()
Non-db-aware version.
bool isNull() const const
QMap::const_iterator constBegin() const const
Provides database cursor functionality.
Definition: KDbCursor.h:68
QTextStream & right(QTextStream &stream)
bool isNotEmpty() const
Definition: KDbField.h:307
QList< KDbTableViewColumn * > * visibleColumns()
Autodeleting list.
Definition: KDbUtils.h:244
const QChar * constData() const const
bool isDBAware() const
int indexForVisibleLookupValue() const
bool isVisible() const
Column visibility. By default column is visible.
void aboutToChangeCell(KDbRecordData *record, int colnum, QVariant *newValue, KDbResultInfo *result)
KDbOrderByColumn provides information about a single query column used for sorting.
int size() const const
int autoIncrementedColumn() const
virtual bool isReadOnly() const
KDbRecordEditBuffer * recordEditBuffer() const
Type type(const QSqlDatabase &db)
void aboutToUpdateRecord(KDbRecordData *record, KDbRecordEditBuffer *buffer, KDbResultInfo *result)
int count(const T &value) const const
void setVisible(bool v)
Changes column visibility.
Definition of a single column for table view.
QTextStream & left(QTextStream &stream)
QDebug & nospace()
void recordInserted(KDbRecordData *, bool repaint)
A record has been inserted.
void addColumn(KDbTableViewColumn *col)
static QString messageYouCanImproveData()
QDebug & space()
QDataStream & operator<<(QDataStream &out, const KDateTime &dateTime)
const KDbResultInfo & result() const
bool updateRecordEditBufferRef(KDbRecordData *record, int colnum, KDbTableViewColumn *col, QVariant *newval, bool allowSignals=true, QVariant *visibleValueForLookupField=nullptr)
QList::const_iterator constBegin() const const
const QChar * unicode() const const
A list of records to allow configurable sorting and more.
void sort()
Sorts this data using previously set order.
bool isFPNumericType() const
Definition: KDbField.h:335
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
bool isUnsigned() const
if the type has the unsigned attribute
Definition: KDbField.h:515
virtual void setReadOnly(bool set)
virtual bool isInsertingEnabled() const
void aboutToDeleteRecord(KDbRecordData *record, KDbResultInfo *result, bool repaint)
void setPrimaryKey(bool p)
Definition: KDbField.cpp:867
void deleteLater()
KDbRecordData * createItem() const
Creates a single record data with proper number of columns.
bool containsRecordIdInfo() const
void recordUpdated(KDbRecordData *)
Current record has been updated.
bool updateRecordEditBuffer(KDbRecordData *record, int colnum, KDbTableViewColumn *col, const QVariant &newval, bool allowSignals=true)
virtual void setInsertingEnabled(bool set)
bool isTextType() const
Definition: KDbField.h:353
void deleteRecords(const QList< int > &recordsToDelete, bool repaint=false)
void columnVisibilityChanged(const KDbTableViewColumn &column)
Used by KDbTableViewColumn::setVisible()
QMap::const_iterator constEnd() const const
@ Integer
Definition: KDbField.h:90
void aboutToInsertRecord(KDbRecordData *record, KDbResultInfo *result, bool repaint)
bool isEmpty() const const
int visibleColumnIndex(int globalIndex) const
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
int length() const const
int visibleColumnCount() const
KDbQueryColumnInfo * visibleLookupColumnInfo()
virtual bool deleteAllRecords(bool repaint=false)
virtual void clearInternal(bool processEvents=true)
bool isEmpty() const const
provides data for single edited database record
@ BigInteger
Definition: KDbField.h:91
Type type() const
Definition: KDbField.cpp:379
const QVariant * at(KDbQueryColumnInfo *ci, bool useDefaultValueIfPossible=true) const
bool isNumericType() const
Definition: KDbField.h:317
FieldsExpandedMode
Mode for fieldsExpanded() and visibleFieldsExpanded()
QString dbTableName() const
KDbTableViewColumn * visibleColumn(int index)
KDB_EXPORT bool isEmptyValue(KDbField::Type type, const QVariant &value)
Definition: KDb.cpp:429
@ WithInternalFieldsAndRecordId
Like WithInternalFields but record ID (big int type) field is appended after internal fields.
KDbOrderByColumn::SortOrder sortOrder() const
void recordsDeleted(const QList< int > &recordsToDelete)
Records have been deleted.
bool isAutoIncrement() const
Definition: KDbField.h:282
void insertRecord(KDbRecordData *record, int index, bool repaint=false)
@ Boolean
Definition: KDbField.h:92
Structure for storing single record with type information.
Definition: KDbRecordData.h:36
int globalIndexOfVisibleColumn(int visibleIndex) const
QList::const_iterator constEnd() const const
QList< KDbTableViewColumn * > * columns()
const QVariant & at(int i) const
Definition: KDbRecordData.h:72
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void reloadRequested()
Displayed data needs to be reloaded in all presenters.
bool isNotNull() const
Definition: KDbField.h:302
void setData(KDbTableViewData *data)
used by KDbTableViewData::addColumn()
void recordDeleted()
Current record has been deleted.
Meta-data for a field.
Definition: KDbField.h:71
int count(const T &value) const const
typedef ConstIterator
SortOrder
Column sort order.
bool deleteRecord(KDbRecordData *record, bool repaint=false)
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
KDbQueryColumnInfo * columnInfo()
KDbTableViewColumn * column(int c)
QString name() const
Definition: KDbField.cpp:256
@ WithInternalFields
Like Default but internal fields (for lookup) are appended.
void setSorting(int column, KDbOrderByColumn::SortOrder order=KDbOrderByColumn::SortOrder::Ascending)
QString tr(const char *sourceText, const char *disambiguation, int n)
Helper class that assigns additional information for the column in a query.
QVector< V > values(const QMultiHash< K, V > &c)
QString captionOrName() const
Definition: KDbField.cpp:326
ushort unicode() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.