ConfigDataTreeModel.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  **
3  ** 03-Feb-2011, KAB: This file was copied from treemodel.cpp in the
4  ** Editable Tree Model Example from Nokia (copyright notice below).
5  ** See http://doc.qt.nokia.com/4.6/itemviews-editabletreemodel.html.
6  ** It has been modified to add local customizations.
7  **
8  **
9  ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
10  ** All rights reserved.
11  ** Contact: Nokia Corporation (qt-info@nokia.com)
12  **
13  ** This file is part of the examples of the Qt Toolkit.
14  **
15  ** $QT_BEGIN_LICENSE:LGPL$
16  ** Commercial Usage
17  ** Licensees holding valid Qt Commercial licenses may use this file in
18  ** accordance with the Qt Commercial License Agreement provided with the
19  ** Software or, alternatively, in accordance with the terms contained in
20  ** a written agreement between you and Nokia.
21  **
22  ** GNU Lesser General Public License Usage
23  ** Alternatively, this file may be used under the terms of the GNU Lesser
24  ** General Public License version 2.1 as published by the Free Software
25  ** Foundation and appearing in the file LICENSE.LGPL included in the
26  ** packaging of this file. Please review the following information to
27  ** ensure the GNU Lesser General Public License version 2.1 requirements
28  ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
29  **
30  ** In addition, as a special exception, Nokia gives you certain additional
31  ** rights. These rights are described in the Nokia Qt LGPL Exception
32  ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
33  **
34  ** GNU General Public License Usage
35  ** Alternatively, this file may be used under the terms of the GNU
36  ** General Public License version 3.0 as published by the Free Software
37  ** Foundation and appearing in the file LICENSE.GPL included in the
38  ** packaging of this file. Please review the following information to
39  ** ensure the GNU General Public License version 3.0 requirements will be
40  ** met: http://www.gnu.org/copyleft/gpl.html.
41  **
42  ** If you have questions regarding the use of this file, please contact
43  ** Nokia at qt-info@nokia.com.
44  ** $QT_END_LICENSE$
45  **
46  ****************************************************************************/
47 
48 #include <DatabaseUtils/GUI/ConfigDataTreeModel.h>
49 #include <DatabaseUtils/DAQConfig/DBColumnParams.h>
50 #include <DatabaseUtils/DAQConfig/NamedConfigUtils.h>
51 #include <DatabaseUtils/SimpleParameterSet.h>
52 #include <NovaDAQUtilities/HexUtils.h>
53 #include <QtGui/QtGui>
54 #include <boost/lexical_cast.hpp>
55 #include <boost/algorithm/string.hpp>
56 #include <iomanip>
57 
58 using namespace dbutils::daqconfig;
59 
60 #define NUMBER_OF_EXTRA_COLUMNS_WHEN_SELECTORS_ARE_PRESENT 2
61 #define NOECWSAP 2
62 #define NONE_SPECIFIED_STRING "none specified"
63 
64 namespace dbutils {
65 namespace gui {
66 
68  return ("FEB " + febId);
69 }
70 
72  return ("Pixel " + pixelId);
73 }
74 
75 QString _createFEBDisplayString(const QString& febId) {
76  return _createFEBDisplayString(febId.toStdString()).c_str();
77 }
78 
79 QString _createPixelDisplayString(const QString& pixelId) {
80  return _createPixelDisplayString(pixelId.toStdString()).c_str();
81 }
82 
83 const int32_t INVALID_ID = -123;
84 
85 int32_t _getIDFromDisplayString(const std::string& displayString) {
86  int32_t result = INVALID_ID;
87 
88  std::string workString = boost::trim_copy(displayString);
89  size_t pos = workString.rfind(" ");
90  if (pos != std::string::npos) {
91  std::string idString = workString.substr(pos);
92  boost::trim(idString);
93  try {
94  result = boost::lexical_cast<int32_t>(idString);
95  }
96  catch (...) {}
97  }
98 
99  return result;
100 }
101 
103  std::string workString = boost::trim_copy(displayString);
104  size_t pos = workString.rfind(" ");
105  if (pos != std::string::npos) {
106  workString = workString.substr(pos);
107  boost::trim(workString);
108  }
109 
110  return workString;
111 }
112 
113 ConfigDataTreeModel::
114 ConfigDataTreeModel(QObject *parent) : QAbstractItemModel(parent)
115 {
116 }
117 
118 QList<QString> ConfigDataTreeModel::
119 allowsInsertBefore(const QModelIndex &index) const
120 {
121  QList<QString> EMPTY_LIST;
122  std::string selectorColumn;
123  if (! selectorColumnNameForIndex(index, selectorColumn)) {
124  return EMPTY_LIST;
125  }
126 
127  if (selectorColumn == DBColumnParams::DCMNAME_COLUMN_NAME ||
128  selectorColumn == DBColumnParams::HOSTNAME_COLUMN_NAME) {
129  return EMPTY_LIST;
130  }
131 
132  if (selectorColumn != DBColumnParams::FEBID_COLUMN_NAME &&
133  selectorColumn != DBColumnParams::PIXELID_COLUMN_NAME) {
134  return EMPTY_LIST;
135  }
136 
137  TreeItem *selectedItem = _getItem(index);
138  std::string selectedDisplayString =
139  selectedItem->data(0).toString().toStdString();
140  int thisChannelId = _getIDFromDisplayString(selectedDisplayString);
141  if (thisChannelId == INVALID_ID) {return EMPTY_LIST;}
142 
143  QModelIndex previousIndex = this->index(index.row()-1, index.column(),
144  index.parent());
145  int previousChannelId = -1;
146  if (previousIndex.isValid()) {
147  TreeItem *previousItem = _getItem(previousIndex);
148  std::string previousDisplayString =
149  previousItem->data(0).toString().toStdString();
150  previousChannelId = _getIDFromDisplayString(previousDisplayString);
151  }
152 
153  QList<QString> results;
154  for (int idx = previousChannelId+1; idx < thisChannelId; ++idx) {
155  QString idString = QString::number(idx);
156  results.push_back(idString);
157  }
158 
159  return results;
160 }
161 
162 QList<QString> ConfigDataTreeModel::
163 allowsInsertAfter(const QModelIndex &index) const
164 {
165  QList<QString> EMPTY_LIST;
166  std::string selectorColumn;
167  if (! selectorColumnNameForIndex(index, selectorColumn)) {
168  return EMPTY_LIST;
169  }
170 
171  QList<QString> results;
172  if (selectorColumn == DBColumnParams::DCMNAME_COLUMN_NAME ||
173  selectorColumn == DBColumnParams::HOSTNAME_COLUMN_NAME) {
174  QString nameString = "unknown";
175  results.push_back(nameString);
176  return results;
177  }
178 
179  if (selectorColumn != DBColumnParams::FEBID_COLUMN_NAME &&
180  selectorColumn != DBColumnParams::PIXELID_COLUMN_NAME) {
181  return EMPTY_LIST;
182  }
183 
184  TreeItem *selectedItem = _getItem(index);
185  std::string selectedDisplayString =
186  selectedItem->data(0).toString().toStdString();
187  int thisChannelId = _getIDFromDisplayString(selectedDisplayString);
188  if (thisChannelId == INVALID_ID) {return EMPTY_LIST;}
189 
190  QModelIndex nextIndex = this->index(index.row()+1, index.column(),
191  index.parent());
192  int nextChannelId = -1;
193  if (nextIndex.isValid()) {
194  TreeItem *nextItem = _getItem(nextIndex);
195  std::string nextDisplayString =
196  nextItem->data(0).toString().toStdString();
197  nextChannelId = _getIDFromDisplayString(nextDisplayString);
198  }
199  else {
200  if (selectorColumn == DBColumnParams::FEBID_COLUMN_NAME) {
201  nextChannelId = DBColumnParams::HIGH_FEB_ID + 1;
202  }
203  if (selectorColumn == DBColumnParams::PIXELID_COLUMN_NAME) {
204  nextChannelId = DBColumnParams::HIGH_PIXEL_ID + 1;
205  }
206  }
207 
208  for (int idx = thisChannelId+1; idx < nextChannelId; ++idx) {
209  QString idString = QString::number(idx);
210  results.push_back(idString);
211  }
212 
213  return results;
214 }
215 
216 QList<QString> ConfigDataTreeModel::
217 allowsChildInsert(const QModelIndex &index) const
218 {
219  QList<QString> EMPTY_LIST;
220  std::string selectorColumn;
221  if (! selectorColumnNameForChild(index, selectorColumn)) {
222  return EMPTY_LIST;
223  }
224 
225  QList<QString> results;
226  if (selectorColumn == DBColumnParams::DCMNAME_COLUMN_NAME ||
227  selectorColumn == DBColumnParams::HOSTNAME_COLUMN_NAME) {
228  QString nameString = "unknown";
229  results.push_back(nameString);
230  return results;
231  }
232 
233  if (selectorColumn != DBColumnParams::FEBID_COLUMN_NAME &&
234  selectorColumn != DBColumnParams::PIXELID_COLUMN_NAME) {
235  return EMPTY_LIST;
236  }
237 
238  int nextChannelId = -1;
239  QModelIndex childIndex = this->index(0, 0, index);
240  if (childIndex.isValid()) {
241  TreeItem *childItem = _getItem(childIndex);
242  std::string childDisplayString =
243  childItem->data(0).toString().toStdString();
244  nextChannelId = _getIDFromDisplayString(childDisplayString);
245  }
246  else {
247  if (selectorColumn == DBColumnParams::FEBID_COLUMN_NAME) {
248  nextChannelId = DBColumnParams::HIGH_FEB_ID + 1;
249  }
250  if (selectorColumn == DBColumnParams::PIXELID_COLUMN_NAME) {
251  nextChannelId = DBColumnParams::HIGH_PIXEL_ID + 1;
252  }
253  }
254 
255  for (int idx = 0; idx < nextChannelId; ++idx) {
256  QString idString = QString::number(idx);
257  results.push_back(idString);
258  }
259 
260  return results;
261 }
262 
264 loadData(boost::shared_ptr<daqconfig::ConfigDataTree> dataTree)
265 {
266  _dataTree = dataTree;
267 
268  _selectorColumnNames = _dataTree->getSelectorColumnNames();
269  QVector<QVariant> rootData;
270  if (_selectorColumnNames.size() > 0) {
271  rootData << "Component";
272  rootData << "Row Type";
273  }
274  _dataColumnNames = _dataTree->getDataColumnNames();
275  foreach (std::string dataColumn, _dataColumnNames) {
276  QString tmpQString(dataColumn.c_str());
277  rootData << tmpQString;
278  }
279  _dataColumnTypes = _dataTree->getDataColumnTypes();
280 
281  if (_rootItem.get() != 0) {
282  QModelIndex dummyIndex;
283  this->removeRows(0, _rootItem->childCount(), dummyIndex);
284  }
285  _rootItem.reset(new TreeItem(rootData));
286 
287  _setupModelData();
288  return true;
289 }
290 
292 {
293  std::vector<TreeItem*> modifiedItems;
294  this->_getModifiedItems(_rootItem.get(), modifiedItems);
295  return (modifiedItems.size() > 0);
296 }
297 
298 bool ConfigDataTreeModel::saveChanges(const int64_t& subsystemConfigId)
299 {
300  std::vector<dbutils::SimpleParameterSet> newDataList;
301 
302  std::vector<TreeItem*> modifiedItems;
303  this->_getModifiedItems(_rootItem.get(), modifiedItems);
304  for (int idx = 0; idx < (int) modifiedItems.size(); ++idx) {
305  TreeItem* item = modifiedItems[idx];
307  paramSet.put(NamedConfigUtils::SUBSYSCFGID_COLUMN_NAME,
308  boost::lexical_cast<std::string>(subsystemConfigId));
309 
310  int dataColumnOffset = 0;
311  if (this->hasSelectors()) {
312 
313  // build up a list of the item and its ancestors
314  std::vector<TreeItem*> ancestorList;
315  ancestorList.push_back(item);
316  TreeItem* parent = item->parent();
317  while (parent != 0) {
318  ancestorList.push_back(parent);
319  parent = parent->parent();
320  }
321 
322  // pop off the root node and the system-wide default
323  if (ancestorList.size() > 0) {ancestorList.pop_back();}
324  if (ancestorList.size() > 0) {ancestorList.pop_back();}
325 
326  // save the value of each selector in the ancestry
327  int selectorIndex = 0;
328  while (ancestorList.size() > 0) {
329  TreeItem* ancestor = ancestorList.back();
330  ancestorList.pop_back();
331 
332  std::string columnName = _selectorColumnNames[selectorIndex];
333  std::string value = ancestor->data(0).toString().toStdString();
334  if (columnName == DBColumnParams::DCMNAME_COLUMN_NAME &&
335  value == "Full System") {
336  value = _keysForDefaultValues[selectorIndex];
337  }
338  if (columnName == DBColumnParams::FEBID_COLUMN_NAME ||
339  columnName == DBColumnParams::PIXELID_COLUMN_NAME) {
340  value = _getIDFromDisplayString2(value);
341  }
342 
343  paramSet.put(columnName, value);
344  ++selectorIndex;
345  }
346 
347  // fill in default values for selectors not in the ancestry
348  while (selectorIndex < (int) _selectorColumnNames.size()) {
349  paramSet.put(_selectorColumnNames[selectorIndex],
350  _keysForDefaultValues[selectorIndex]);
351  ++selectorIndex;
352  }
353 
354  dataColumnOffset = NOECWSAP;
355  }
356 
357  // fill in the data values
358  int numberOfEmptyValues = 0;
359  for (int jdx = dataColumnOffset; jdx < item->columnCount(); ++jdx) {
360  std::string value = item->data(jdx).toString().toStdString();
361  boost::trim(value);
362  if (value.length() == 0 || value == NONE_SPECIFIED_STRING) {
363  ++numberOfEmptyValues;
364  }
365 
366  if (novadaq::HexUtils::isValidHex(value)) {
367  try {
368  if (_dataColumnTypes[jdx-dataColumnOffset] == "bigint") {
369  int64_t i64Value;
370  if (novadaq::HexUtils::parse(value, i64Value)) {
371  value = boost::lexical_cast<std::string>(i64Value);
372  }
373  }
374  else if (_dataColumnTypes[jdx-dataColumnOffset] == "integer" ||
375  _dataColumnTypes[jdx-dataColumnOffset] == "int") {
376  int32_t i32Value;
377  if (novadaq::HexUtils::parse(value, i32Value)) {
378  value = boost::lexical_cast<std::string>(i32Value);
379  }
380  }
381  }
382  catch (...) {}
383  }
384 
385  paramSet.put(_dataColumnNames[jdx-dataColumnOffset], value);
386  }
387 
388  // check for empty values and react accordingly
389  if (numberOfEmptyValues == 0) {
390  // all values were specified - that is good
391  newDataList.push_back(paramSet);
392  }
393  else if (item->childCount() != 0 && numberOfEmptyValues ==
394  (item->columnCount() - dataColumnOffset)) {
395  // "item" corresponds to a default and all values are empty
396  // --> it's OK to skip this row
397  }
398  else {
399  // otherwise, missing values are bad
400  std::string msgText = "Data Validation Error: ";
401  msgText.append("Values may not be left empty ");
402  msgText.append("except in new default rows that have ");
403  msgText.append("new exceptions underneath them. ");
404  msgText.append("(");
405  msgText.append(_dataTree->getTableName());
406  msgText.append(" table.)");
407  QMessageBox::critical(0, "ERROR: Empty Value(s)", msgText.c_str());
408  return false;
409  }
410  }
411 
412  if (newDataList.size() > 0) {
413  if (_dataTree->saveValues(newDataList)) {
414  for (int idx = 0; idx < (int) modifiedItems.size(); ++idx) {
415  TreeItem* item = modifiedItems[idx];
416  item->markClean();
417  item->markOld();
418  }
419  emit dataWasSaved();
420  return true;
421  }
422  else {
423  return false;
424  }
425  }
426 
427  return true;
428 }
429 
431  std::vector<TreeItem*>& list) const
432 {
433  // add this item to the list if it has been modified
434  if (baseItem->isDirty() || baseItem->isNew()) {list.push_back(baseItem);}
435 
436  // loop over children, if any
437  for (int idx = 0; idx < baseItem->childCount(); ++idx) {
438  TreeItem* childItem = baseItem->child(idx);
439  this->_getModifiedItems(childItem, list);
440  }
441 }
442 
443 int ConfigDataTreeModel::columnCount(const QModelIndex & /* parent */) const
444 {
445  return _rootItem->columnCount();
446 }
447 
448 QVariant ConfigDataTreeModel::data(const QModelIndex &index, int role) const
449 {
450  if (!index.isValid()) {return QVariant();}
451  //std::cout << "Role = " << role << std::endl;
452 
453  if (role == Qt::BackgroundRole) {
454  TreeItem *item = _getItem(index);
455  int comparator = this->hasSelectors() ? NOECWSAP : 0;
456  if ((item->isDirty() || item->isNew()) &&
457  index.column() >= comparator) {
458  QColor yellow("yellow");
459  QBrush brush(yellow);
460  return brush;
461  }
462  }
463 
464  if (role != Qt::DisplayRole && role != Qt::EditRole)
465  return QVariant();
466 
467  TreeItem *item = _getItem(index);
468 
469  return item->data(index.column());
470 }
471 
472 Qt::ItemFlags ConfigDataTreeModel::flags(const QModelIndex &index) const
473 {
474  if (!index.isValid())
475  return 0;
476 
477  // the goal of this section of code is to make all "data" columns
478  // editable and all "selector" columns non-editable, except for
479  // DCM names in new items
480  int comparator = this->hasSelectors() ? NOECWSAP : 0;
481  if (index.column() >= comparator) {
482  return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
483  }
484  else {
485  std::string selectorColumnName;
486  if (index.column() == 0 &&
487  this->selectorColumnNameForIndex(index, selectorColumnName) &&
488  (selectorColumnName == DBColumnParams::DCMNAME_COLUMN_NAME ||
489  selectorColumnName == DBColumnParams::HOSTNAME_COLUMN_NAME)) {
490  TreeItem *item = _getItem(index);
491  if (item->isNew()) {
492  return Qt::ItemIsEditable | Qt::ItemIsEnabled |
493  Qt::ItemIsSelectable;
494  }
495  }
496  return Qt::ItemIsEnabled;
497  }
498 }
499 
500 TreeItem *ConfigDataTreeModel::_getItem(const QModelIndex &index) const
501 {
502  if (index.isValid()) {
503  TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
504  if (item) return item;
505  }
506  return _rootItem.get();
507 }
508 
509 QVariant ConfigDataTreeModel::
510 headerData(int section, Qt::Orientation orientation, int role) const
511 {
512  if (role == Qt::ToolTipRole) {
513  int selectorCount = _selectorColumnNames.size();
514  if (selectorCount > 0) {
515  if (section == 0) {
516  std::string msg("For this table, values can be specified by ");
517  for (int idx = 0; idx < selectorCount; ++idx) {
518  if (idx > 0) {
519  if (selectorCount > NOECWSAP) {
520  msg.append(",");
521  }
522  msg.append(" ");
523  if (idx == selectorCount-1) {msg.append("and ");}
524  }
525  msg.append(_selectorColumnNames[idx]);
526  }
527  msg.append(".");
528  return QVariant(QString(msg.c_str()));
529  }
530  else if (section == 1) {
531  std::string msg("TODO: Describe Default and Individual.");
532  return QVariant(QString(msg.c_str()));
533  }
534  }
535  }
536 
537  if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
538  return _rootItem->data(section);
539 
540  return QVariant();
541 }
542 
543 QModelIndex ConfigDataTreeModel::index(int row, int column, const QModelIndex &parent) const
544 {
545  if (parent.isValid() && parent.column() != 0) {
546  return QModelIndex();
547  }
548 
549  TreeItem *parentItem = _getItem(parent);
550 
551  TreeItem *childItem = parentItem->child(row);
552  if (childItem)
553  return createIndex(row, column, childItem);
554  else
555  return QModelIndex();
556 }
557 
558 bool ConfigDataTreeModel::insertRows(int position, int rows, const QModelIndex &parent)
559 {
560  TreeItem *parentItem = _getItem(parent);
561 
562  beginInsertRows(parent, position, position + rows - 1);
563 
564  bool status = parentItem->insertChildren(position, rows, _rootItem->columnCount());
565  if (status) {
566  emit aChangeWasMade();
567  }
568 
569  endInsertRows();
570 
571  return status;
572 }
573 
574 QModelIndex ConfigDataTreeModel::parent(const QModelIndex &index) const
575 {
576  if (!index.isValid()) {return QModelIndex();}
577 
578  TreeItem *childItem = _getItem(index);
579  TreeItem *parentItem = childItem->parent();
580 
581  if (parentItem == _rootItem.get())
582  return QModelIndex();
583 
584  return createIndex(parentItem->childNumber(), 0, parentItem);
585 }
586 
587 bool ConfigDataTreeModel::removeRows(int position, int rows, const QModelIndex &parent)
588 {
589  TreeItem *parentItem = _getItem(parent);
590 
591  beginRemoveRows(parent, position, position + rows - 1);
592 
593  bool status = parentItem->removeChildren(position, rows);
594  if (status) {
595  emit aChangeWasMade();
596  }
597 
598  endRemoveRows();
599 
600  return status;
601 }
602 
603 int ConfigDataTreeModel::rowCount(const QModelIndex &parent) const
604 {
605  TreeItem *parentItem = _getItem(parent);
606 
607  return parentItem->childCount();
608 }
609 
610 bool ConfigDataTreeModel::setData(const QModelIndex &index,
611  const QVariant &value,
612  int role)
613 {
614  if (role != Qt::EditRole)
615  return false;
616 
617  TreeItem *item = _getItem(index);
618  QVariant oldValue = item->data(index.column());
619  if (value == oldValue) {
620  return false;
621  }
622 
623  // if this item has children (i.e. it is a default value that is
624  // being set), then offer to remove the children which would be
625  // over-ridden by the new default
626  QMessageBox::ButtonRole userChoice = QMessageBox::YesRole;
627  if (item->childCount() > 0) {
628  QMessageBox choiceBox;
629 
630  std::string msgText = "You have specified a new default value. ";
631  msgText.append("Typically, a new default value overrides ");
632  msgText.append("all existing exceptions underneath the default. ");
633  msgText.append("However, you should specify the behavior that ");
634  msgText.append("you want.");
635  choiceBox.setText(msgText.c_str());
636 
637  msgText = "Do you want to Keep None of the exceptions underneath ";
638  msgText.append("this default, Keep the New exceptions that you ");
639  msgText.append("have created or edited during this editor ");
640  msgText.append("session, Keep All of the exceptions under ");
641  msgText.append("this default, or Cancel your change?");
642  choiceBox.setInformativeText(msgText.c_str());
643 
644  QPushButton* keepNoneButton =
645  choiceBox.addButton(tr("Keep None"), QMessageBox::AcceptRole);
646  QPushButton* keepNewButton =
647  choiceBox.addButton(tr("Keep New"), QMessageBox::ApplyRole);
648  QPushButton* keepAllButton =
649  choiceBox.addButton(tr("Keep All"), QMessageBox::RejectRole);
650  QPushButton* cancelButton =
651  choiceBox.addButton(tr("Cancel"), QMessageBox::ResetRole);
652  choiceBox.setDefaultButton(keepNewButton);
653  choiceBox.exec();
654 
655  if (choiceBox.clickedButton() == cancelButton) {
656  return false;
657  }
658  else if (choiceBox.clickedButton() == keepNoneButton) {
659  userChoice = QMessageBox::AcceptRole;
660  }
661  else if (choiceBox.clickedButton() == keepNewButton) {
662  userChoice = QMessageBox::ApplyRole;
663  }
664  else if (choiceBox.clickedButton() == keepAllButton) {
665  userChoice = QMessageBox::RejectRole;
666  }
667  }
668 
669  bool result = item->setData(index.column(), value);
670 
671  if (result) {
672  item->markDirty();
673 
674  if (userChoice == QMessageBox::AcceptRole) {
675  // remove all children
676  this->removeRows(0, item->childCount(), index);
677  }
678  else if (userChoice == QMessageBox::ApplyRole) {
679  // remove clean (and old) children
680  int childIdx = 0;
681  while (childIdx < item->childCount()) {
682  TreeItem *childItem = item->child(childIdx);
683  if (! childItem->isDirty() && ! childItem->isNew()) {
684  this->removeRows(childIdx, 1, index);
685  }
686  else {
687  ++childIdx;
688  }
689  }
690  }
691  else if (userChoice == QMessageBox::RejectRole) {
692  // mark all children dirty (so they get re-saved to the DB)
693  for (int idx = 0; idx < item->childCount(); ++idx) {
694  TreeItem *childItem = item->child(idx);
695  childItem->markBranchDirty();
696  }
697  }
698 
699  emit dataChanged(index, index);
700  emit aChangeWasMade();
701  }
702 
703  return result;
704 }
705 
706 bool ConfigDataTreeModel::setHeaderData(int section, Qt::Orientation orientation,
707  const QVariant &value, int role)
708 {
709  if (role != Qt::EditRole || orientation != Qt::Horizontal)
710  return false;
711 
712  bool result = _rootItem->setData(section, value);
713 
714  if (result)
715  emit headerDataChanged(orientation, section, section);
716 
717  return result;
718 }
719 
721 {
722  // build the initial set of selections
723  _keysForDefaultValues.clear();
724  DBColumnParams& colParams = DBColumnParams::getInstance();
725  for (uint32_t idx = 0; idx < _selectorColumnNames.size(); ++idx) {
726  std::string keyForDefault;
728  keyForDefault)) {
729  _keysForDefaultValues.push_back(keyForDefault);
730  }
731  else {
733  NO_DEFAULT_KEY_STRING);
734  }
735  }
736 
737  // populate the tree
739 }
740 
741 // levels: -1 = system-wide default
742 // 0 = specific instance of first selector
743 // 1 = specific instance of second selector
744 // etc.
745 // returns the number of non-empty items that were added to the tree
748  std::vector<std::string> selectionList)
749 {
750  int numberOfNonEmptyItems = 0;
751  int selectorCount = selectionList.size();
752 
753  // add the requested item
754  int newChildIndex = parent->childCount();
755  parent->insertChildren(newChildIndex, 1, parent->columnCount());
756  TreeItem* thisItem = parent->child(newChildIndex);
757  int itemIndex = 0;
758  if (selectorCount > 0) {
759  if (thisLevel == -1) {
760  thisItem->setData(itemIndex++, QVariant("Full System"));
761  thisItem->setData(itemIndex++, QVariant("Default"));
762  }
763  else {
764  std::string text = selectionList[thisLevel];
765  if (_selectorColumnNames[thisLevel] ==
766  DBColumnParams::FEBID_COLUMN_NAME) {
767  text = _createFEBDisplayString(text);
768  }
769  else if (_selectorColumnNames[thisLevel] ==
770  DBColumnParams::PIXELID_COLUMN_NAME) {
771  text = _createPixelDisplayString(text);
772  }
773  thisItem->setData(itemIndex++, QVariant(text.c_str()));
774 
775  if (thisLevel < (selectorCount-1)) {
776  thisItem->setData(itemIndex++, QVariant("Default"));
777  }
778  else {
779  thisItem->setData(itemIndex++, QVariant("Individual"));
780  }
781  }
782  }
783  for (uint32_t dcIdx = 0; dcIdx < _dataColumnNames.size(); ++dcIdx) {
784  std::string dataColumnName = _dataColumnNames[dcIdx];
785  std::string valueString = "unknown";
786  bool status = false;
787  switch (selectorCount) {
788  case 0:
789  status = _dataTree->getTrueValue(dataColumnName, valueString);
790  break;
791  case 1:
792  status = _dataTree->getTrueValueFor1Key(selectionList[0],
793  dataColumnName,
794  valueString);
795  break;
796  case 2:
797  status = _dataTree->getTrueValueFor2Keys(selectionList[0],
798  selectionList[1],
799  dataColumnName,
800  valueString);
801  break;
802  case 3:
803  status = _dataTree->getTrueValueFor3Keys(selectionList[0],
804  selectionList[1],
805  selectionList[2],
806  dataColumnName,
807  valueString);
808  break;
809  default:
810  std::cout << "Error: unexpected number of selectors ("
811  << selectorCount << ")" << std::endl;
812  }
813  if (status) {
814  if (dataColumnName == "mask") {
815  try {
816  if (_dataColumnTypes[dcIdx] == "bigint") {
817  valueString =
818  novadaq::HexUtils::format(valueString, 16);
819  }
820  else {
821  valueString =
822  novadaq::HexUtils::format(valueString, 8);
823  }
824  }
825  catch (...) {}
826  }
827  thisItem->setData(itemIndex++, QVariant(valueString.c_str()));
828  ++numberOfNonEmptyItems;
829  }
830  else if (thisLevel < (selectorCount-1)) {
831  thisItem->setData(itemIndex++, QVariant(NONE_SPECIFIED_STRING));
832  }
833  }
834 
835  // get the distinct selection values at the next level
836  int nextLevel = thisLevel + 1;
837  if (nextLevel >= 0 && nextLevel < selectorCount &&
838  nextLevel < (int) _selectorColumnNames.size()) {
839  std::vector<std::string> distinctSelectorValues = _dataTree->
840  getDistinctSelectorColumnValues(_selectorColumnNames[nextLevel]);
841  for (uint32_t idx = 0; idx < distinctSelectorValues.size(); ++idx) {
842  if (distinctSelectorValues[idx] ==
843  _keysForDefaultValues[nextLevel]) {
844  continue;
845  }
846  selectionList[nextLevel] = distinctSelectorValues[idx];
847  numberOfNonEmptyItems += _populateSubTree(thisItem, nextLevel,
848  selectionList);
849  }
850  }
851 
852  // if this item is empty, and all of its children are empty,
853  // then remove this entry in the tree. (We shouldn't have added
854  // it in the first place, but we had to try in case there were
855  // non-empty children.)
856  if (numberOfNonEmptyItems == 0) {
857  parent->removeChildren(newChildIndex, 1);
858  }
859 
860  return numberOfNonEmptyItems;
861 }
862 
864 addChildRow(QModelIndex parentIndex, QString selectorColumnText,
865  int position)
866 {
867  TreeItem *parentItem = _getItem(parentIndex);
868 
869  // rebuild parentIndex, if needed, to get the column number set to zero
870  // (without this, the TreeView doesn't show the added row correctly)
871  if (parentIndex.column() != 0) {
872  parentIndex = this->index(parentIndex.row(), 0,
873  this->parent(parentIndex));
874  }
875 
876  beginInsertRows(parentIndex, position, position);
877 
878  bool status = parentItem->insertChildren(position, 1,
879  _rootItem->columnCount());
880  if (status) {
881  QModelIndex childIndex = this->index(position, 0, parentIndex);
882  if (childIndex.isValid()) {
883  TreeItem* childItem = _getItem(childIndex);
884  int selectorLevel = _getParentCount(childIndex) - 1;
885 
886  if (selectorLevel >= 0 &&
887  selectorLevel < (int) _selectorColumnNames.size()) {
888  if (_selectorColumnNames[selectorLevel] ==
889  DBColumnParams::FEBID_COLUMN_NAME) {
890  selectorColumnText =
891  _createFEBDisplayString(selectorColumnText);
892  }
893  else if (_selectorColumnNames[selectorLevel] ==
894  DBColumnParams::PIXELID_COLUMN_NAME) {
895  selectorColumnText =
896  _createPixelDisplayString(selectorColumnText);
897  }
898  }
899  childItem->setData(0, selectorColumnText);
900 
901  if (selectorLevel+1 == (int) _selectorColumnNames.size()) {
902  childItem->setData(1, QVariant("Individual"));
903  }
904  else {
905  childItem->setData(1, QVariant("Default"));
906  }
907  for (int idx = NOECWSAP; idx < parentItem->columnCount(); ++idx) {
908  childItem->setData(idx, parentItem->data(idx));
909  }
910  childItem->markNew();
911  }
912 
913  emit aChangeWasMade();
914  }
915 
916  endInsertRows();
917 
918  return status;
919 }
920 
922 addRowBefore(const QModelIndex &baseIndex, QString selectorColumnText)
923 {
924  TreeItem *baseItem = _getItem(baseIndex);
925  QModelIndex parentIndex = this->parent(baseIndex);
926  int baseOffset = baseItem->childNumber();
927  return this->addChildRow(parentIndex, selectorColumnText, baseOffset);
928 }
929 
931 addRowAfter(const QModelIndex &baseIndex, QString selectorColumnText)
932 {
933  TreeItem *baseItem = _getItem(baseIndex);
934  QModelIndex parentIndex = this->parent(baseIndex);
935  int baseOffset = baseItem->childNumber();
936  return this->addChildRow(parentIndex, selectorColumnText, baseOffset+1);
937 }
938 
941  std::string& selectorColumnName) const
942 {
943  return _getSelectorColumnName(index, selectorColumnName, -1);
944 }
945 
948  std::string& selectorColumnName) const
949 {
950  return _getSelectorColumnName(index, selectorColumnName, 0);
951 }
952 
954 dataColumnNameForIndex(const QModelIndex& index,
955  std::string& dataColumnName) const
956 {
957  if (! index.isValid()) {return false;}
958 
959  int column = index.column();
960  int fixedColumnCount = this->hasSelectors() ? NOECWSAP : 0;
961  if (column < fixedColumnCount) {return false;}
962 
963  int idx = column - fixedColumnCount;
964  if (idx >= 0 && idx < (int) _dataColumnNames.size()) {
965  dataColumnName = _dataColumnNames[idx];
966  return true;
967  }
968 
969  return false;
970 }
971 
973 dataColumnTypeForIndex(const QModelIndex& index,
974  std::string& dataColumnType) const
975 {
976  if (! index.isValid()) {return false;}
977 
978  int column = index.column();
979  int fixedColumnCount = this->hasSelectors() ? NOECWSAP : 0;
980  if (column < fixedColumnCount) {return false;}
981 
982  int idx = column - fixedColumnCount;
983  if (idx >= 0 && idx < (int) _dataColumnTypes.size()) {
984  dataColumnType = _dataColumnTypes[idx];
985  return true;
986  }
987 
988  return false;
989 }
990 
992 _getSelectorColumnName(const QModelIndex& index,
993  std::string& selectorColumnName,
994  int startingOffset) const
995 {
996  if (! this->hasSelectors()) {return false;}
997 
998  int column = index.column();
999  int fixedColumnCount = this->hasSelectors() ? NOECWSAP : 0;
1000  if (column >= fixedColumnCount) {return false;}
1001 
1002  int parentCount = _getParentCount(index);
1003  if (parentCount < 0) {return false;}
1004  int selectorIndex = parentCount + startingOffset;
1005  if (selectorIndex < 0 ||
1006  selectorIndex >= (int) _selectorColumnNames.size()) {
1007  return false;
1008  }
1009  selectorColumnName = _selectorColumnNames[selectorIndex];
1010  return true;
1011 }
1012 
1013 int ConfigDataTreeModel::_getParentCount(const QModelIndex& index) const
1014 {
1015  if (!index.isValid()) {return -1;}
1016 
1017  int parentCount = 0;
1018  QModelIndex parent = this->parent(index);
1019  while (parent.isValid()) {
1020  ++parentCount;
1021  parent = this->parent(parent);
1022  }
1023  return parentCount;
1024 }
1025 
1026 } // end of namespace gui
1027 } // end of namespace dbutils
bool insertChildren(int position, int count, int columns)
Definition: TreeItem.cpp:94
TreeItem * _getItem(const QModelIndex &index) const
bool addChildRow(QModelIndex index, QString selectorColumnText, int position=0)
const uint16_t HIGH_FEB_ID
int status
Definition: fabricate.py:1613
QList< QString > allowsInsertBefore(const QModelIndex &index) const
bool dataColumnTypeForIndex(const QModelIndex &index, std::string &dataColumnType) const
int32_t _getIDFromDisplayString(const std::string &displayString)
bool columnKeyToGetDefaultValue(const std::string &columnName, std::string &columnValue)
#define NOECWSAP
int childCount() const
Definition: TreeItem.cpp:71
bool addRowBefore(const QModelIndex &index, QString selectorColumnText)
void put(const std::string &key, const std::string &value)
string trim(string in)
Definition: rootgINukeVal.C:65
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
std::string trim_copy(std::string source, std::string const &t=" ")
Definition: trim.h:66
QList< QString > allowsChildInsert(const QModelIndex &index) const
bool dataColumnNameForIndex(const QModelIndex &index, std::string &dataColumnName) const
int _populateSubTree(TreeItem *parent, int level, std::vector< std::string > selectionList)
std::vector< std::string > _keysForDefaultValues
bool setData(int column, const QVariant &value)
Definition: TreeItem.cpp:125
const uint16_t HIGH_PIXEL_ID
std::vector< std::string > _dataColumnNames
QList< QString > allowsInsertAfter(const QModelIndex &index) const
std::vector< std::string > _dataColumnTypes
bool isValidHex(const std::string &hexString)
Definition: HexUtils.cpp:74
int childNumber() const
Definition: TreeItem.cpp:76
QString _createFEBDisplayString(const QString &febId)
boost::shared_ptr< daqconfig::ConfigDataTree > _dataTree
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
int columnCount() const
Definition: TreeItem.cpp:84
const XML_Char int const XML_Char * value
Definition: expat.h:331
std::string _createFEBDisplayString(const std::string &febId)
virtual bool saveChanges(const int64_t &subsystemConfigId)
bool _getSelectorColumnName(const QModelIndex &index, std::string &selectorColumnName, int startingOffset=0) const
bool addRowAfter(const QModelIndex &index, QString selectorColumnText)
virtual Qt::ItemFlags flags(const QModelIndex &index) const
virtual bool loadData(boost::shared_ptr< daqconfig::ConfigDataTree > dataTree)
bool removeChildren(int position, int count)
Definition: TreeItem.cpp:113
bool isNew() const
Definition: TreeItem.h:79
std::string format(const int32_t &value, const int &ndigits=8)
Definition: HexUtils.cpp:14
virtual bool removeRows(int position, int rows, const QModelIndex &parent=QModelIndex())
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
virtual QModelIndex parent(const QModelIndex &index) const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
tuple yellow
Definition: rootlogon.py:67
TreeItem * parent()
Definition: TreeItem.cpp:108
bool isDirty() const
Definition: TreeItem.h:74
OStream cout
Definition: OStream.cxx:6
::xsd::cxx::tree::string< char, simple_type > string
Definition: Database.h:154
std::string _getIDFromDisplayString2(const std::string &displayString)
virtual bool insertRows(int position, int rows, const QModelIndex &parent=QModelIndex())
std::string _createPixelDisplayString(const std::string &pixelId)
const int32_t INVALID_ID
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
TreeItem * child(int number)
Definition: TreeItem.cpp:66
QVariant data(int column) const
Definition: TreeItem.cpp:89
bool parse(const std::string &hexString, int32_t &decimalValue)
Definition: HexUtils.cpp:96
std::vector< std::string > _selectorColumnNames
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role=Qt::EditRole)
void _getModifiedItems(TreeItem *baseItem, std::vector< TreeItem * > &list) const
int _getParentCount(const QModelIndex &index) const
#define NONE_SPECIFIED_STRING
boost::shared_ptr< TreeItem > _rootItem
bool selectorColumnNameForChild(const QModelIndex &index, std::string &selectorColumnName) const
bool selectorColumnNameForIndex(const QModelIndex &index, std::string &selectorColumnName) const
QString _createPixelDisplayString(const QString &pixelId)