WARNING: USE THIS SOFTWARE AT YOUR OWN RISK! THIS IS EXPERIMENTAL SOFTWARE NOT INTENDED FOR PRODUCTION USE! Zuble is currently an early stage prototype. As such Zuble is minimally tested and inherently unstable. It is provided for experimental, development, and demonstration purposes only. Zuble QML Types   |  Zuble C++ Classes   |  Zuble Overview
Zuble  0.1
Zuble Framework C++/QML extension API
ZTableModel.cpp
Go to the documentation of this file.
1 /*
2  * Zuble - A run-time system for QML/Javascript applications
3  * Copyright (C) 2013, 2014 Bob Dinitto
4  *
5  * Filename: ZTableModel.cpp
6  * Created on: 11/9/2014
7  * Author: Bob Dinitto
8  *
9  * Zuble is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #include <QVariant>
26 #include "ZTableModel.h"
27 #include "ZblException.h"
28 #include "zglobal.h"
29 #include "ZApplication.h"
30 #include <QtQml>
31 #include <QJsonDocument>
32 #include <QJsonObject>
33 #include <QJsonArray>
34 #include <QHashIterator>
35 
36 namespace Zbl
37 {
38 
40 
41 ZTableModel::ZTableModel(QObject *parent) :
42  QAbstractTableModel(parent)
43 {
44 
45 }
46 
48 {
50 }
51 
53 {
54  qmlRegisterType<ZTableModel>
55  ("org.zuble.qml", 1, 0, "ZTableModel");
56 }
57 
59 {
60  return Qt::UserRole;
61 }
62 
63 
64 
66  const QModelIndex & parent) const
67 {
68  if(parent.isValid())
69  return 0;
70 
71  int count = -1;
72 
74 
75  count = m_model.rowCount(NULL);
76  ZBL_SLOT_END_RETURN(count, -1,
77  Z_FAC_JS, ZblTableModel::rowCount, rowCount failed)
78 
79 }
80 
82  const QModelIndex & parent) const
83 {
84  if(parent.isValid())
85  return 0;
86 
89  Z_FAC_JS, ZblTableModel::columnCount, columnCount failed)
90 
91 }
92 
94  const QModelIndex & index,
95  int role) const
96 {
97 
98  //qDebug() << "ZTableModel::data role/row/col" << role << index.row() << index.column();
99 
100  // Check boudaries
101  if(index.column() < 0 ||
102  columnCount() <= index.column() ||
103  index.row() < 0 ||
104  rowCount() <= index.row())
105  {
106  qDebug() << "ZTableModel::data - Warning out of bounds: " << index.row() << ", " << index.column();
107  return QVariant();
108  }
109 
111  ZBL_SLOT_END_RETURN(m_model.data(role,index.row(), index.column()), QVariant(),
112  Z_FAC_JS, ZblTableModel::data, data failed)
113 
114  return QVariant();
115 
116 }
117 
118 
119 QHash<int, QByteArray> ZTableModel::roleNames() const
120 {
121 
123  ZBL_SLOT_END_RETURN(m_model.roleNames(), (QHash<int, QByteArray>()),
125 
126 }
127 
129 {
130  // TBD: NOT THREAD SAFE!!!!!
131 
133  beginResetModel();
135  endResetModel();
136  ZBL_SLOT_END_VOID(Z_FAC_JS, ZblTableModel::clearRoles, clearRoles failed)
137 }
138 
140 {
141  // TBD: NOT THREAD SAFE!!!!!
142 
144  beginResetModel();
145  m_model.clearData();
146  endResetModel();
147  ZBL_SLOT_END_VOID(Z_FAC_JS, ZblTableModel::clearData, clearData failed)
148 }
149 
151 {
152  // TBD: NOT THREAD SAFE!!!!!
153 
155  QList<QByteArray> names = roleNames().values();
156 
157  QByteArray nextValue;
158 
159  qDebug("dumping model role names");
160 
161  for(int i=0; i<names.length();i++)
162  {
163  nextValue = names.at(i);
164  qDebug("name=%s",nextValue.constData());
165  }
167  ZBL_SLOT_END_VOID(Z_FAC_JS, ZblTableModel::dumpModelData, dumpModelData failed)
168 }
169 
170 
171 
172 void ZTableModel::putValue(int role, int row, const QVariant& value)
173 {
175 
176  if(inObjectThread(this))
177  {
178 
179  //QString strValue(value.toString());
180  //QString strAddress(ZApplication::getCurrentThreadAddress());
181 
182  m_model.setData(role, row, 0, value);
183 
184  emit tableModified(role, row);
185 
186  QVector<int> modifiedRole;
187  modifiedRole.append(role);
188  QModelIndex index = createIndex(row, 0);
189 
190  emit dataChanged(index, index, modifiedRole);
191  }
192  else
193  {
194  //QString strValue(value.toString());
195  //QString strAddress(ZApplication::getCurrentThreadAddress());
196 
197  QVariant valueCopy(value);
198 
199  QMetaObject::invokeMethod(this,"putValue",
200  Qt::BlockingQueuedConnection,
201  Q_ARG(int, role),
202  Q_ARG(int, row),
203  Q_ARG(QVariant, valueCopy)
204  );
205  }
206 
207  ZBL_SLOT_END_VOID(Z_FAC_JS, ZTableModel::put, put failed)
208 }
209 
210 void ZTableModel::putValue(int role, int row, int column, const QVariant& value)
211 {
213 
214  if(inObjectThread(this))
215  {
216 
217  //QString strValue(value.toString());
218  //QString strAddress(ZApplication::getCurrentThreadAddress());
219 
220  m_model.setData(role, row, column, value);
221 
222  emit tableModified(role, row);
223 
224  QVector<int> modifiedRole;
225  modifiedRole.append(role);
226  QModelIndex index = createIndex(row, column);
227 
228  emit dataChanged(index, index, modifiedRole);
229  }
230  else
231  {
232  //QString strValue(value.toString());
233  //QString strAddress(ZApplication::getCurrentThreadAddress());
234 
235  QVariant valueCopy(value);
236 
237  QMetaObject::invokeMethod(this,"putValue",
238  Qt::BlockingQueuedConnection,
239  Q_ARG(int, role),
240  Q_ARG(int, row),
241  Q_ARG(int, column),
242  Q_ARG(QVariant, valueCopy)
243  );
244  }
245 
246  ZBL_SLOT_END_VOID(Z_FAC_JS, ZTableModel::put, put failed)
247 }
248 
249 
250 
251 void ZTableModel::appendRow( QVariant data )
252 {
254 
255  //TBD: can we eliminate invokeMethod here since it's
256  // called by appendRow(ZRoleRow)?
257 
258  if(inObjectThread(this))
259  {
260  if(data.canConvert<Zbl::ZRoleRow>())
261  {
262  ZRoleRow roleRow = data.value<Zbl::ZRoleRow>();
263 
264  appendRow(roleRow);
265  }
266  }
267  else
268  {
269  //QMap<int, QList<QVariant > > dataCopy = data;
270 
271  QMetaObject::invokeMethod(this,"appendRow",
272  Qt::QueuedConnection,
273  Q_ARG( QVariant, data ) );
274 
275  }
276 
278 
279 }
281 {
283 
284  //TBD: can we eliminate invokeMethod here since it's
285  // called by appendRows(ZRoleRowList)?
286 
287  if(inObjectThread(this))
288  {
289  if(data.canConvert<Zbl::ZRoleRowList>())
290  {
291  ZRoleRowList roleRows = data.value<Zbl::ZRoleRowList>();
292 
293  appendRows(roleRows);
294  }
295  }
296  else
297  {
298  QMetaObject::invokeMethod(this,"appendRows",
299  Qt::QueuedConnection,
300  Q_ARG( QVariant, data ) );
301  }
302 
304 
305 }
306 
308 {
310 
311  if(inObjectThread(this))
312  {
313  int insertIndex = m_model.rowCount();
314 
315  beginInsertRows(QModelIndex(), insertIndex, insertIndex);
316 
317  m_model.appendRow(roleRow);
318 
319  endInsertRows();
320  }
321  else
322  {
323  QMetaObject::invokeMethod(this,"appendRow",
324  Qt::QueuedConnection,
325  Q_ARG( ZRoleRow, roleRow ) );
326  }
327 
329 
330 }
331 
333 {
334  int insertIndex = -1;
335 
337 
338  if(inObjectThread(this))
339  {
340  insertIndex = m_model.rowCount();
341 
342  beginInsertRows(QModelIndex(), insertIndex, insertIndex);
343 
344  m_model.appendRow(roleRow);
345 
346  endInsertRows();
347  }
348  else
349  {
350  QMetaObject::invokeMethod(this,"appendRowIndex",
351  Qt::BlockingQueuedConnection,
352  Q_RETURN_ARG(int, insertIndex),
353  Q_ARG(ZRoleRow, roleRow));
354  }
355 
356  ZBL_SLOT_END_RETURN(insertIndex, insertIndex,
358 }
359 
361 {
363 
364  if(inObjectThread(this))
365  {
366  int insertIndex = m_model.rowCount();
367 
368  beginInsertRows(QModelIndex(), insertIndex, insertIndex+roleRowList.count()-1);
369 
370  m_model.appendRows(roleRowList);
371 
372  endInsertRows();
373  }
374  else
375  {
376  QMetaObject::invokeMethod(this,"appendRows",
377  Qt::QueuedConnection,
378  Q_ARG( ZRoleRowList, roleRowList ) );
379  }
380 
382 
383 }
384 
386  QVariant roleRow,
387  const QString& keyText,
388  int keyColumn)
389 
390 {
391 
393 
394  if(inObjectThread(this))
395  {
396  if(roleRow.canConvert<Zbl::ZRoleRow>())
397  {
398  ZRoleRow zRow = roleRow.value<Zbl::ZRoleRow>();
399 
400  appendMissingRow(zRow, keyText, keyColumn);
401  }
402 
403  }
404  else
405  {
406  QMetaObject::invokeMethod(this,"appendMissingRow",
407  Qt::QueuedConnection,
408  Q_ARG(QVariant, roleRow),
409  Q_ARG(const QString&, keyText),
410  Q_ARG(int, keyColumn));
411  }
412 
414  appendMissingRow failed)
415 }
416 
418  ZRoleRow roleRow,
419  const QString& keyText,
420  int keyColumn)
421 
422 {
423  int foundRow = -1;
424 
426 
427  if(inObjectThread(this))
428  {
429  foundRow = m_model.findNextItemRow(
430  0, keyText, keyColumn, true, true, true);
431 
432  if(foundRow == -1)
433  appendRow(roleRow);
434  }
435  else
436  {
437  QVariant row = QVariant::fromValue<ZRoleRow>(roleRow);
438 
439  QMetaObject::invokeMethod(this,"appendMissingRow",
440  Qt::QueuedConnection,
441  Q_ARG(QVariant, row),
442  Q_ARG(const QString&, keyText),
443  Q_ARG(int, keyColumn));
444  }
445 
448 }
449 
450 
451 
452 
453 
455 
457 {
459 
460  //TBD: can we eliminate invokeMethod here since it's
461  // called by appendRow(ZRoleRow)?
462 
463  if(inObjectThread(this))
464  {
465  if(data.canConvert<Zbl::ZRoleRow>())
466  {
467  ZRoleRow roleRow = data.value<Zbl::ZRoleRow>();
468 
469  prependRow(roleRow);
470  }
471  }
472  else
473  {
474  //QMap<int, QList<QVariant > > dataCopy = data;
475 
476  QMetaObject::invokeMethod(this,"prependRow",
477  Qt::QueuedConnection,
478  Q_ARG( QVariant, data ) );
479 
480  }
481 
483 
484 }
486 {
488 
489  //TBD: can we eliminate invokeMethod here since it's
490  // called by prependRows(ZRoleRowList)?
491 
492  if(inObjectThread(this))
493  {
494  if(data.canConvert<Zbl::ZRoleRowList>())
495  {
496  ZRoleRowList roleRows = data.value<Zbl::ZRoleRowList>();
497 
498  prependRows(roleRows);
499  }
500  }
501  else
502  {
503  QMetaObject::invokeMethod(this,"prependRows",
504  Qt::QueuedConnection,
505  Q_ARG( QVariant, data ) );
506  }
507 
509 
510 }
511 
513 {
515 
516  if(inObjectThread(this))
517  {
518  //int insertIndex = m_model.rowCount();
519 
520  beginInsertRows(QModelIndex(), 0, 0); // TBD: should we expose column index?
521 
522  m_model.prependRow(roleRow);
523 
524  endInsertRows();
525  }
526  else
527  {
528  QMetaObject::invokeMethod(this,"prependRow",
529  Qt::QueuedConnection,
530  Q_ARG( ZRoleRow, roleRow ) );
531  }
532 
534 
535 }
536 
538 {
540 
541  if(inObjectThread(this))
542  {
543  //int insertIndex = m_model.rowCount();
544 
545  beginInsertRows(QModelIndex(), 0, roleRowList.count()-1);
546 
547  m_model.prependRows(roleRowList);
548 
549  endInsertRows();
550  }
551  else
552  {
553  QMetaObject::invokeMethod(this,"prependRows",
554  Qt::QueuedConnection,
555  Q_ARG( ZRoleRowList, roleRowList ) );
556  }
557 
559 
560 }
561 
563 
564 
565 
567 {
568  removeRows(row, 1);
569 }
570 
571 void ZTableModel::removeRows(int row, int count)
572 {
573  // WARNING: Infinite loop! Don't generate log messages in this method!
574 
576 
577  //zDebug() << "removeRows - row=" << row << ", count=" << count;
578 
579  if(inObjectThread(this))
580  {
581  beginRemoveRows(QModelIndex(), row, row+count-1);
582 
583  //blockSignals(true);
584 
585  m_model.removeRows(row, count);
586 
587  //blockSignals(false);
588 
589  endRemoveRows();
590 
591 
592  }
593  else
594  {
595  QMetaObject::invokeMethod(this,"removeRows",
596  Qt::QueuedConnection,
597  Q_ARG( int, row ),
598  Q_ARG( int, count ) );
599  }
600 
602 }
603 
604 void ZTableModel::truncate(int rowCount, bool removeFromFront)
605 {
607 
608  if(inObjectThread(this))
609  {
610  const int modelCount = m_model.rowCount();
611 
612  int removeCount = modelCount - rowCount;
613 
614  if(removeCount > 0)
615  {
616  if(removeFromFront)
617  removeRows(0, removeCount);
618  else
619  {
620  int firstRow = modelCount - removeCount;
621  if(firstRow < 0)
622  {
623  removeCount = removeCount + firstRow;
624  firstRow = 0;
625  }
626  removeRows(firstRow, removeCount);
627  }
628  }
629 
630  }
631  else
632  {
633  QMetaObject::invokeMethod(this,"truncate",
634  Qt::QueuedConnection,
635  Q_ARG( int, rowCount ),
636  Q_ARG( bool, removeFromFront ) );
637  }
638 
640 }
641 
642 bool ZTableModel::copyColumn(int fromColumn, int toColumn)
643 {
645 
646  bool status = false;
647 
648  if(inObjectThread(this))
649  {
650  status = m_model.copyColumn(fromColumn, toColumn);
651  }
652  else
653  {
654  QMetaObject::invokeMethod(this,"copyColumn",
655  Qt::BlockingQueuedConnection,
656  Q_RETURN_ARG(bool, status),
657  Q_ARG( int, fromColumn ),
658  Q_ARG( int, toColumn ));
659  }
660 
661  ZBL_SLOT_END_RETURN(status, false,
663 }
664 
665 
666 
667 
668 
670  const ZDataRow cells,
671  int startIndex,
672  int columnCount,
673  const QList<int> roles,
674  int& nextIndex)
675 {
676  ZRoleRow roleRow;
677 
678  const int roleCount = roles.count();
679 
680  if(!roleCount)
681  return roleRow;
682 
683  const int cellCount = cells.count();
684 
685  if(!cellCount)
686  return roleRow;
687 
688  const int stopIndex = startIndex + (roleCount*columnCount);
689 
690  if(stopIndex > cellCount)
691  throw ZblException("Insufficient cells to coalesce a row.");
692 
693  for(int i=startIndex, r=0; i<stopIndex; i++)
694  {
695  roleRow[roles.at(r++)].push_back(cells.at(i));
696 
697  if(r >= roleCount)
698  r = 0;
699  }
700 
701  nextIndex = stopIndex;
702 
703  return roleRow;
704 }
705 
707  const ZDataRow cells,
708  int columnCount,
709  const QList<int> roles)
710 {
711  ZRoleRowList roleRowList;
712 
713  const int roleCount = roles.count();
714 
715  if(!roleCount)
716  return roleRowList;
717 
718  const int cellCount = cells.count();
719 
720  if(!cellCount)
721  return roleRowList;
722 
723  const int rowCount = cellCount / (roleCount*columnCount);
724 
725  if(rowCount < 1)
726  return roleRowList;
727 
728  int nextIndex = 0;
729 
730  for(int i=0; i<rowCount; i++)
731  roleRowList.append(coalesceCellRow(cells, nextIndex, columnCount, roles, nextIndex));
732 
733  return roleRowList;
734 }
735 
736 void ZTableModel::appendCells(QVariant data, bool truncateModel)
737 {
739 
740  if(inObjectThread(this))
741  {
742 
743  mergeCells(data, false, truncateModel);
744  }
745  else
746  {
747  QMetaObject::invokeMethod(this,"appendCells",
748  Qt::QueuedConnection,
749  Q_ARG( QVariant, data ),
750  Q_ARG(bool, truncateModel));
751  }
752 
754 
755 }
756 
757 void ZTableModel::prependCells(QVariant data, bool truncateModel)
758 {
760 
761  if(inObjectThread(this))
762  {
763  mergeCells(data, true, truncateModel);
764  }
765  else
766  {
767  QMetaObject::invokeMethod(this,"prependCells",
768  Qt::QueuedConnection,
769  Q_ARG( QVariant, data ),
770  Q_ARG(bool, truncateModel));
771  }
772 
774 }
775 
776 void ZTableModel::mergeCells(QVariant data, bool front, bool truncateModel)
777 {
778 
779  const int startingRowCount = m_model.rowCount();
780  int rowsAdded = 0;
781 
782  if(data.canConvert<Zbl::ZDataRow>())
783  {
784  ZDataRow cellData = data.value<Zbl::ZDataRow>();
785 
786  const int cellCount = cellData.count();
787 
788  const int roleCount = m_model.roleCount();
789 
790  if(!roleCount)
791  throw ZblException("Invalid role count - data model must "
792  "contain at least one role.");
793 
794  const int colCount = m_model.columnCount();
795 
796  const int rowCount = cellCount / (roleCount*colCount);
797 
798  if(rowCount<1)
799  throw ZblException("Invalid cell count - data cell count must "
800  "be at least 1 row of cells, "
801  "ie: roleCount * columnCount.");
802 
803  const int remainderCells = cellCount % (roleCount*colCount);
804 
805  if(remainderCells)
806  throw ZblException("Invalid cell count - data cell count must "
807  "be modulo (roleCount * columnCount), "
808  "ie: an integral number of rows.");
809 
810  QList<int> roles = m_model.roles();
811 
812  int nextIndex = 0;
813 
814  if(rowCount == 1)
815  {
816  if(front)
817  prependRow(coalesceCellRow(cellData, 0, colCount, roles, nextIndex));
818  else
819  appendRow(coalesceCellRow(cellData, 0, colCount, roles, nextIndex));
820  }
821  else
822  {
823  if(front)
824  prependRows(coalesceCellRows(cellData, colCount, roles));
825  else
826  appendRows(coalesceCellRows(cellData, colCount, roles));
827  }
828 
829  rowsAdded = rowCount;
830 
831  }
832  else if(data.canConvert(QMetaType::QString))
833  {
834  QString oneCell = data.toString();
835 
836  if(m_model.roleCount() != 1 || m_model.columnCount() != 1)
837  throw ZblException("Models with multiple roles or columns require "
838  "more than one cell to fill a row."
839  " Pass a string array containing (roleCount"
840  " * columnCount * rowCount) number of strings.");
841 
842  ZRoleRow roleRow;
843  ZDataRow dataRow;
844 
845  dataRow.append(oneCell);
846 
847  QList<int> roleList = m_model.roles();
848 
849  roleRow.insert(roleList.at(0), dataRow);
850 
851  appendRow(roleRow);
852 
853  rowsAdded = 1;
854  }
855 
856  // TBD: WARN AND RETURN IF NOT POSSIBLE TO MERGE CELLS.
857 
858 
859  if(truncateModel && (startingRowCount!=0))
860  {
861  zDebug() << "Truncating to row count: " << startingRowCount;
862  zDebug() << "Truncating front: " << front;
863  truncate(startingRowCount, !front);
864  }
865 }
866 
868 {
869  int count = -1;
870 
872 
873  if(inObjectThread(this))
874  {
875  count = m_model.rowCount();
876  }
877  else
878  {
879  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"modelRowCount",
880  Qt::BlockingQueuedConnection,
881  Q_RETURN_ARG(int, count));
882  }
883 
884  ZBL_SLOT_END_RETURN(count, -1,
886 }
887 
888 
890 {
891  int count = -1;
892 
894 
895  if(inObjectThread(this))
896  {
897  count = m_model.columnCount();
898  }
899  else
900  {
901  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"modelColumnCount",
902  Qt::BlockingQueuedConnection,
903  Q_RETURN_ARG(int, count));
904  }
905 
906  ZBL_SLOT_END_RETURN(count, -1,
908 }
909 
911 {
913 
914  if(inObjectThread(this))
915  {
916  QCoreApplication::sendPostedEvents(this);
917  }
918  else
919  {
920  QMetaObject::invokeMethod(this,"sync",
921  Qt::BlockingQueuedConnection);
922  }
923 
925 }
926 
928 {
930 
931  if(inObjectThread(this))
932  {
933  m_model.setColumnCount(count);
934  }
935  else
936  {
937  QMetaObject::invokeMethod(this,"setColumnCount",
938  Qt::BlockingQueuedConnection,
939  Q_ARG(int, count));
940 
941  }
942 
944 }
945 
946 bool ZTableModel::addRole(int roleNumber)
947 {
948  bool status;
949 
951 
952  if(inObjectThread(this))
953  {
954  status = m_model.addRole(roleNumber);
955  }
956  else
957  {
958  QMetaObject::invokeMethod(this,"addRole",
959  Qt::BlockingQueuedConnection,
960  Q_RETURN_ARG(bool, status),
961  Q_ARG(int,roleNumber));
962  }
963 
964  ZBL_SLOT_END_RETURN(status, false,
966 }
967 
968 bool ZTableModel::addRole(int roleNumber, const QString& roleName)
969 {
970  bool status;
971 
973 
974  if(inObjectThread(this))
975  {
976  status = m_model.addRole(roleNumber, roleName.toUtf8());
977  }
978  else
979  {
980  QMetaObject::invokeMethod(this,"addRole",
981  Qt::BlockingQueuedConnection,
982  Q_RETURN_ARG(bool, status),
983  Q_ARG(int, roleNumber),
984  Q_ARG(const QString&, roleName));
985  }
986 
987  ZBL_SLOT_END_RETURN(status, false,
989 }
990 
992 {
993  int count = 0;
994 
996 
997  if(inObjectThread(this))
998  {
999  count = m_model.roleCount();
1000  }
1001  else
1002  {
1003  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"getRoleCount",
1004  Qt::BlockingQueuedConnection,
1005  Q_RETURN_ARG(int, count));
1006  }
1007 
1008  ZBL_SLOT_END_RETURN(count, 0,
1010 }
1011 
1012 QList<int> ZTableModel::roles() const
1013 {
1014  QList<int> roleList;
1015 
1017 
1018  if(inObjectThread(this))
1019  {
1020  roleList = m_model.roles();
1021  }
1022  else
1023  {
1024  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"roles",
1025  Qt::BlockingQueuedConnection,
1026  Q_RETURN_ARG(QList<int>, roleList));
1027  }
1028 
1029  ZBL_SLOT_END_RETURN(roleList, QList<int>(),
1031 }
1032 
1033 QVariantMap ZTableModel::roleMap() const
1034 {
1035  QVariantMap map;
1036 
1038 
1039  if(inObjectThread(this))
1040  {
1041  QHash<int, QByteArray> hash = roleNames();
1042 
1043  QList<int> keys = hash.keys();
1044 
1045  const int keyCount = keys.size();
1046 
1047  for(int i=0; i<keyCount; i++)
1048  {
1049  const int nextRole = keys.at(i);
1050 
1051  map.insert(QString::fromUtf8(hash.value(nextRole)), QVariant(nextRole));
1052  }
1053 
1054  }
1055  else
1056  {
1057  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"roleMap",
1058  Qt::BlockingQueuedConnection,
1059  Q_RETURN_ARG(QVariantMap, map));
1060  }
1061 
1062  ZBL_SLOT_END_RETURN(map, QVariantMap(),
1064 
1065 }
1066 
1067 
1068 
1069 
1070 QVariant ZTableModel::getValue(int role, int row)
1071 {
1072  QVariant value;
1073 
1074  //if(role < 0 || row < 0)
1075  // return value;
1076 
1078 
1079  if(inObjectThread(this))
1080  {
1081  value = m_model.data(role, row, 0);
1082  }
1083  else
1084  {
1085  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"getValue",
1086  Qt::BlockingQueuedConnection,
1087  Q_RETURN_ARG(QVariant, value),
1088  Q_ARG(int,role),
1089  Q_ARG(int,row));
1090  }
1091 
1092  ZBL_SLOT_END_RETURN(value, false,
1094 }
1095 
1096 QVariant ZTableModel::getValue(int role, int row, int column)
1097 {
1098  QVariant value;
1099 
1100  //if(role < 0 || row < 0)
1101  // return value;
1102 
1104 
1105  if(inObjectThread(this))
1106  {
1107  value = m_model.data(role, row, column);
1108  }
1109  else
1110  {
1111  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"getValue",
1112  Qt::BlockingQueuedConnection,
1113  Q_RETURN_ARG(QVariant, value),
1114  Q_ARG(int,role),
1115  Q_ARG(int,row),
1116  Q_ARG(int,column));
1117  }
1118 
1119  ZBL_SLOT_END_RETURN(value, false,
1121 }
1122 
1124  QList<int> roles,
1125  int startRow,
1126  int col,
1127  int rowCount)
1128 {
1129  QVariant value;
1130 
1132 
1133  if(inObjectThread(this))
1134  {
1135  value = m_model.getTableColumnRows( roles, startRow, col, rowCount);
1136  }
1137  else
1138  {
1139  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"getTableColumnRows",
1140  Qt::BlockingQueuedConnection,
1141  Q_RETURN_ARG(QVariant, value),
1142  Q_ARG(QList<int> ,roles),
1143  Q_ARG(int,startRow),
1144  Q_ARG(int,col),
1145  Q_ARG(int,rowCount));
1146  }
1147 
1148  ZBL_SLOT_END_RETURN(value, QVariant(),
1150 
1151 }
1152 
1153 
1155  QVariant rows,
1156  int startRow,
1157  int col)
1158 {
1160 
1161  insertTableColumnRows(rows, startRow, col, false);
1162 
1165 }
1166 
1168  QVariant rows,
1169  int startRow,
1170  int col)
1171 {
1173 
1174  insertTableColumnRows(rows, startRow, col, true);
1175 
1178 }
1179 
1181  QVariant rows,
1182  int startRow,
1183  int col,
1184  bool asynchronous)
1185 {
1186  if(!asynchronous && inObjectThread(this))
1187  {
1188  m_model.putTableColumnRows(rows, startRow, col);
1189  }
1190  else
1191  {
1192  Qt::ConnectionType connectType = asynchronous ? Qt::QueuedConnection : Qt::BlockingQueuedConnection;
1193 
1194  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"putTableColumnRows",
1195  connectType,
1196  Q_ARG(QVariant, rows),
1197  Q_ARG(int, startRow),
1198  Q_ARG(int, col));
1199  }
1200 }
1201 
1202 
1203 
1205  int startRow,
1206  const QString& text,
1207  int column,
1208  bool caseSensitive,
1209  bool forwardDirection,
1210  bool keySearch) const
1211 {
1212  int foundRow = -1;
1213 
1215 
1216  if(inObjectThread(this))
1217  {
1218  foundRow = m_model.findNextItemRow(
1219  startRow, text, column, caseSensitive,
1220  forwardDirection, keySearch );
1221  }
1222  else
1223  {
1224  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"findNextItemRow",
1225  Qt::BlockingQueuedConnection,
1226  Q_RETURN_ARG(int, foundRow),
1227  Q_ARG(int, startRow),
1228  Q_ARG(const QString&,text),
1229  Q_ARG(int, column),
1230  Q_ARG(bool, caseSensitive),
1231  Q_ARG(bool, forwardDirection),
1232  Q_ARG(bool, keySearch));
1233  }
1234 
1235  ZBL_SLOT_END_RETURN(foundRow, -1,
1237 
1238 }
1239 
1240 void ZTableModel::putJsonData(QJsonObject data/*, bool putRoles*/)
1241 {
1242  /* {"roles": {
1243  * "col-0-role-number":"col-0-role-name",
1244  * "col-1-role-number":"col-1-role-name",
1245  * "col-2-role-number":"col-2-role-name"
1246  * }
1247  * "data-rows": [
1248  * ["row-0-col-0-value","row-0-col-1-value","row-0-col-2-value"]
1249  * ["row-1-col-0-value","row-1-col-1-value","row-1-col-2-value"]
1250  * ]}
1251  */
1252 
1254 
1255  if(inObjectThread(this))
1256  {
1257  QJsonValue roleValue = data.value("roles");
1258 
1259  if(roleValue.isUndefined())
1260  throw ZblException("JSON object is missing \"roles\" value.");
1261 
1262  if(!roleValue.isObject())
1263  throw ZblException("JSON object \"roles\" value should be an object of number/name pairs.");
1264 
1265  QJsonValue dataValue = data.value("data-rows");
1266 
1267  if(dataValue.isUndefined())
1268  throw ZblException("JSON object is missing \"data-rows\" value.");
1269 
1270  if(!dataValue.isArray())
1271  throw ZblException("JSON object \"data-rows\" value should be an array of row arrays.");
1272 
1273 
1274  QJsonObject jRoles;
1275  jRoles = roleValue.toObject();
1276 
1277  QJsonArray jData;
1278  jData = dataValue.toArray();
1279 
1280  QStringList roleKeys = jRoles.keys();
1281  const int roleCount = roleKeys.count();
1282 
1283  clearRoles();
1284 
1285  for(int i=0; i<roleCount; i++)
1286  {
1287  QString strNextRoleKey = roleKeys.at(i);
1288  int nextRoleKey = strNextRoleKey.toInt();
1289 
1290  QJsonValue nextRoleValue(jRoles.value(strNextRoleKey));
1291 
1292  m_model.addRole(nextRoleKey, nextRoleValue.toVariant().toByteArray());
1293  }
1294 
1295  const int rowCount = jData.count();
1296 
1297  for(int j=0; j<rowCount; j++)
1298  {
1299  QJsonValue nextRowValue = jData.at(j);
1300 
1301  if(!nextRowValue.isArray())
1302  {
1303  zWarning() << "WARNING: Invalid data: data rows array should "
1304  "contain only arrays of data cells.";
1305  }
1306 
1307  QJsonArray jNextRow = nextRowValue.toArray();
1308 
1309  QVariantList nextRowList = jNextRow.toVariantList();
1310 
1311  ZRoleRow roleRow;
1312 
1313  for(int k=0; k<roleCount; k++)
1314  {
1315  QVariant nextDataValue = nextRowList.at(k);
1316 
1317  QString strNextRoleKey = roleKeys.at(k);
1318 
1319  int nextRoleKey = strNextRoleKey.toInt();
1320 
1321  //QString nextRoleValue = jRoles.at(nextRoleKey);
1322 
1323  QVariantList nextRowCell;
1324  nextRowCell.append(nextDataValue);
1325 
1326  roleRow.insert(nextRoleKey,nextRowCell);
1327  }
1328 
1329  appendRow(roleRow);
1330  }
1331 
1332  }
1333  else
1334  {
1335  QMetaObject::invokeMethod(this,"putJsonData",
1336  Qt::BlockingQueuedConnection,
1337  Q_ARG(QJsonObject, data));
1338  }
1339 
1342 }
1343 
1344 
1345 QJsonObject ZTableModel::getJsonData() const
1346 {
1347  /* {"roles": {
1348  * "col-0-role-number":"col-0-role-name",
1349  * "col-1-role-number":"col-1-role-name",
1350  * "col-2-role-number":"col-2-role-name"
1351  * }
1352  * "data-rows": [
1353  * ["row-0-col-0-value","row-0-col-1-value","row-0-col-2-value"]
1354  * ["row-1-col-0-value","row-1-col-1-value","row-1-col-2-value"]
1355  * ]}
1356  */
1357 
1359 
1360  QJsonObject modelObject;
1361 
1362  if(inObjectThread(this))
1363  {
1364  QJsonObject jRoles;
1365  QHash<int, QByteArray> roles(roleNames());
1366  QHashIterator<int, QByteArray> it(roles);
1367 
1368  while(it.hasNext())
1369  {
1370  it.next();
1371 
1372  jRoles.insert(QString::number(it.key()),
1373  QJsonValue(QString::fromLatin1(it.value())));
1374  }
1375 
1376  QJsonArray jData;
1377  const int rows = rowCount();
1378  //const int cols = columnCount();
1379  QStringList roleKeys = jRoles.keys();
1380  const int roleCount = roleKeys.count();
1381 
1382  for(int r=0; r<rows; r++)
1383  {
1384  QJsonArray rowData;
1385 
1386  for(int v=0; v<roleCount; v++)
1387  {
1388  QString roleKey = roleKeys.at(v);
1389 
1390  QJsonValue roleValue = jRoles.value(roleKey);
1391 
1392  QJsonValue::Type valueType = roleValue.type();
1393 
1394  QString strRoleValue = roleValue.toString();
1395 
1396  int nextRole = roleKey.toInt();
1397 
1398  rowData.append(QJsonValue::fromVariant(
1399  m_model.data(nextRole, r, 0)));
1400  }
1401 
1402  jData.append(rowData);
1403  }
1404 
1405 
1406  modelObject.insert("roles", QJsonValue(jRoles));
1407  modelObject.insert("data-rows", QJsonValue(jData));
1408 
1409  }
1410  else
1411  {
1412  QMetaObject::invokeMethod(const_cast<ZTableModel*>(this),"getJsonData",
1413  Qt::BlockingQueuedConnection,
1414  Q_RETURN_ARG(QJsonObject, modelObject));
1415  }
1416 
1417  ZBL_SLOT_END_RETURN(modelObject, QJsonObject(),
1419 
1420 }
1421 
1422 void ZTableModel::moveModelToThread(QThread* targetThread)
1423 {
1425 
1426  if(!targetThread)
1427  throw ZblException("Invalid argument: targetThread is NULL!");
1428 
1429  if(this->thread() != targetThread)
1430  {
1431  if(inObjectThread(this))
1432  {
1433  moveToThread(targetThread);
1434  }
1435  else
1436  {
1437  QMetaObject::invokeMethod(this,"moveModelToThread",
1438  Qt::BlockingQueuedConnection,
1439  Q_ARG(QThread*, targetThread));
1440  }
1441 
1442  }
1445 }
1446 
1448 {
1450 
1451 
1452 
1453  if(inObjectThread(this))
1454  {
1455  beginResetModel();
1456  endResetModel();
1457  }
1458  else
1459  {
1460  QMetaObject::invokeMethod(this,"invalidateModel",
1461  Qt::BlockingQueuedConnection
1462  );
1463  }
1464 
1465 
1468 }
1469 
1470 } // Zbl
1471 
void dumpModelData(const ZblTableCell *cell=NULL) const
Prints diagnostic information about the state of the contained data to debug output.
void tableModified(int dataRole, int rowNumber)
Sent when a table cell has been modified.
Q_INVOKABLE void appendMissingRow(ZRoleRow roleRow, const QString &keyText, int keyColumn=0)
Appends the specified row if no rows contain the specified text in the specified column. Otherwise does nothing.
bool copyColumn(int fromColumn, int toColumn, ZblTableCell *cell=NULL)
Copies data between data model columns for all rows and roles in the model.
Q_INVOKABLE void prependCells(QVariant data, bool truncateModel=false)
Asynchronously converts an array of cell data into one or more rows of model data and prepends it to ...
int findNextItemRow(int startRow, const QString &text, int column=0, bool caseSensitive=true, bool forwardDirection=true, bool keySearch=false, const ZblTableCell *cell=NULL) const
Searches for the specified text from startIndex.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
QAbstractTableModel override.
Definition: ZTableModel.cpp:65
void setColumnCount(int count, ZblTableCell *cell=NULL)
Sets the number of columns in the data table. This method fails if the data table contains rows...
Q_INVOKABLE void appendCells(QVariant data, bool truncateModel=false)
Asynchronously converts an array of cell data into one or more rows of model data and appends it to t...
void prependRows(QList< QMap< int, QList< QVariant > > > data, ZblTableCell *cell=NULL)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
QAbstractTableModel override.
Definition: ZTableModel.cpp:93
Q_INVOKABLE void clearData()
Removes all data from the model. Roles and role names remain intact.
int getUserRole()
Returns the value of Qt::UserRole. ie: the first role number available for user defined roles...
Definition: ZTableModel.cpp:58
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
QAbstractTableModel override.
Definition: ZTableModel.cpp:81
#define Z_FAC_JS
Definition: zglobal.h:123
Q_INVOKABLE int appendRowIndex(ZRoleRow roleRow)
void moveModelToThread(QThread *targetThread)
Q_INVOKABLE QJsonObject getJsonData() const
Copies data from the table model into a newly created JSON object and returns it. ...
void clearData(ZblTableCell *cell=NULL)
Removes all data rows from the specified table cell.
Q_INVOKABLE void putValue(int role, int row, const QVariant &value)
Sets the value of the specified cell in the data set.
Q_INVOKABLE void prependRow(QVariant data)
int getRoleCount() const
Returns the number of roles defined for this model.
Q_INVOKABLE void truncate(int rowCount, bool removeFromFront=false)
Q_INVOKABLE QVariantMap roleMap() const
Determines which roles are in the data set. This method may block the current thread.
#define ZBL_REGISTER_LOGGED_OBJECT
Definition: zglobal.h:104
void invalidateModel()
Resets the model.
Q_INVOKABLE void dumpModelData() const
dumps diagnostic information about the model to the debug output.
void setData(int role, int row, int col, const QVariant &value, ZblTableCell *cell=NULL)
int rowCount(const ZblTableCell *cell=NULL) const
ZRoleRowList coalesceCellRows(const ZDataRow cells, int columnCount, const QList< int > roles)
Q_INVOKABLE QList< int > roles() const
Determines which roles are in the data set. This method may block the current thread.
Q_INVOKABLE QVariant getValue(int role, int row)
Obtains the value of the specified data cell.
void prependRow(QMap< int, QList< QVariant > > data, ZblTableCell *cell=NULL)
Q_INVOKABLE int findNextItemRow(int startRow, const QString &text, int column=0, bool caseSensitive=true, bool forwardDirection=true, bool keySearch=false) const
Searches for the specified text from startIndex.
Q_INVOKABLE void appendRows(QVariant data)
Definition: ZAndGate.cpp:6
QMap< int, QList< QVariant > > ZRoleRow
Represents a single row (or column for column headers) of data cell values for multiple roles...
Definition: ZTableModel.h:63
#define ZBL_SLOT_BEGIN_TRY
Definition: zglobal.h:128
static void registerType()
Registers ZTableModel as a QML type.
Definition: ZTableModel.cpp:52
#define ZBL_DEFINE_LOGGED_OBJECT(class_name)
Definition: zglobal.h:99
#define zWarning()
Definition: zglobal.h:111
This two dimensional table model is used to store and manipulate data.
Definition: ZTableModel.h:96
#define ZBL_SLOT_END_VOID(facility, code, error_message)
Definition: zglobal.h:134
Q_INVOKABLE void clearRoles()
Removes all roles, role names and data from the model.
void putTableColumnRows(QVariant rows, int startRow, int col, ZblTableCell *cell=NULL)
#define zDebug()
Definition: zglobal.h:113
Q_INVOKABLE void putJsonData(QJsonObject data)
Writes data from the specified JSON object into the data table. Previous contents of the data table a...
void clearRoles()
Removes all information from the model and returns the model to it&#39;s uninitialized state...
Q_INVOKABLE bool copyColumn(int fromColumn, int toColumn)
ZRoleRow coalesceCellRow(const ZDataRow cells, int startIndex, int columnCount, const QList< int > roles, int &nextIndex)
Q_INVOKABLE void setColumnCount(int count)
Sets the number of columns the table will contain. This method may block the current thread...
bool inObjectThread(const QObject &object)
Definition: zglobal.h:173
Q_INVOKABLE int modelRowCount() const
Returns the number of rows in the data set. This method may block the current thread.
void appendRows(QList< QMap< int, QList< QVariant > > > data, ZblTableCell *cell=NULL)
Q_INVOKABLE QVariant getTableColumnRows(QList< int > roles, int startRow, int col, int rowCount)
Obtain data values for a set of rows from a single model column.
Q_INVOKABLE void putTableColumnRows(QVariant rows, int startRow, int col)
Replaces the current value for a set of rows for a single model column.
Q_INVOKABLE void mergeCells(QVariant data, bool front=true, bool truncateModel=true)
Converts an array of cell data into one or more rows of model data and either appends or prepends it ...
int roleCount(const ZblTableCell *cell=NULL) const
Obtains the number of roles contained by the specified cell.
Zuble&#39;s Qt Exception Object.
Definition: ZblException.h:45
bool addRole(int roleNumber, ZblTableCell *cell=NULL)
Adds the specified role to the data table.
QList< int > roles(const ZblTableCell *cell=NULL) const
Determines which roles are in the data set.
int roleCount
Returns the number of roles defined for this model.
Definition: ZTableModel.h:125
static void registerLogCategory()
Definition: ZTableModel.cpp:47
Q_INVOKABLE bool addRole(int roleNumber)
Adds the specified role to the data model. This method may block the current thread.
Q_INVOKABLE void sync()
Blocks thread until model&#39;s event queue has been processed.
QList< QMap< int, QList< QVariant > > > ZRoleRowList
Represents multiple rows of data cell values for multiple roles for multiple columns.
Definition: ZTableModel.h:69
Q_INVOKABLE void sendTableColumnRows(QVariant rows, int startRow, int col)
Asynchronously replaces the current value for a set of rows for a single model column.
void insertTableColumnRows(QVariant rows, int startRow, int col, bool asynchronous)
Q_INVOKABLE void removeRows(int row, int count)
Q_INVOKABLE void removeRow(int row)
void appendRow(QMap< int, QList< QVariant > > data, ZblTableCell *cell=NULL)
virtual QHash< int, QByteArray > roleNames() const
QAbstractTableModel override.
#define ZBL_SLOT_END_RETURN(return_success, return_failed, facility, code, error_message)
Definition: zglobal.h:141
zRoleNames roleNames() const
Obtains a hash that maps role numbers to role names.
QList< QVariant > ZDataRow
Represents a single row (or column for column headers) of data cell values for a single role...
Definition: ZTableModel.h:57
void removeRows(int row, int count, ZblTableCell *cell=NULL)
Removes the specified data rows from the model.
ZblDataModel m_model
Definition: ZTableModel.h:655
int columnCount(const ZblTableCell *cell=NULL) const
Q_INVOKABLE void appendRow(QVariant data)
QVariant getTableColumnRows(QList< int > roles, int startRow, int col, int rowCount, ZblTableCell *cell=NULL) const
Q_INVOKABLE void prependRows(QVariant data)
Q_INVOKABLE int modelColumnCount() const
Returns the number of columns in the data set. This method may block the current thread.
QVariant data(int role, int row, int col, const ZblTableCell *cell=NULL) const