Engauge Digitizer  2
CurveNameList.cpp
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "CurveNameList.h"
8 #include "DocumentSerialize.h"
9 #include "EngaugeAssert.h"
10 #include "Logger.h"
11 #include <qdebug.h>
12 #include <QHash>
13 #include <QTextStream>
14 #include "QtToString.h"
15 #include <QVariant>
16 #include <QXmlStreamWriter>
17 
19  QStandardItemModel()
20 {
21 }
22 
23 int CurveNameList::columnCount (const QModelIndex & /* parent */) const
24 {
25  return NUMBER_CURVE_NAME_LIST_COLUMNS;
26 }
27 
28 bool CurveNameList::containsCurveNameCurrent (const QString &curveName) const
29 {
30  LOG4CPP_INFO_S ((*mainCat)) << "CurveNameList::containsCurveNameCurrent";
31 
32  // Search for curve with matching name
33  CurrentCurveToOriginalCurve::const_iterator itr;
34  for (itr = m_currentCurveToOriginalCurve.begin (); itr != m_currentCurveToOriginalCurve.end (); ++itr) {
35 
36  if (itr.key () == curveName) {
37  return true;
38  }
39  }
40 
41  return false;
42 }
43 
45 {
46  LOG4CPP_INFO_S ((*mainCat)) << "CurveNameList::currentCurvesAsString";
47 
48  QString out;
49  QTextStream str (&out);
50 
51  for (int row = 0; row < rowCount (); row++) {
52 
53  QString curveCurrent = data (index (row, CURVE_NAME_LIST_COLUMN_CURRENT)).toString ();
54  QString curveOriginal;
55  unsigned int points = 0;
56  if (m_currentCurveToOriginalCurve.contains (curveCurrent)) {
57  curveOriginal = m_currentCurveToOriginalCurve [curveCurrent];
58  if (m_originalCurveToPointCount.contains (curveOriginal)) {
59 
60  points = m_originalCurveToPointCount [curveOriginal];
61  }
62  }
63 
64  str << "\n current=" << curveCurrent.toLatin1().data()
65  << " original=" << curveOriginal
66  << " points=" << points;
67  }
68 
69  return out;
70 }
71 
72 QString CurveNameList::currentCurveToOriginalCurve (const QString &currentCurve) const
73 {
74  return m_currentCurveToOriginalCurve [currentCurve];
75 }
76 
77 unsigned int CurveNameList::currentCurveToPointCount (const QString &currentCurve) const
78 {
79  QString originalCurve = m_currentCurveToOriginalCurve [currentCurve];
80 
81  return m_originalCurveToPointCount [originalCurve];
82 }
83 
84 bool CurveNameList::curveNameIsAcceptable (const QString &curveNameNew,
85  int row) const
86 {
87  // First test is to verify curve name is not empty
88  bool success = (!curveNameNew.isEmpty ());
89 
90  if (success) {
91 
92  // First test was passed. Second test is to check for duplication
93  for (int row1 = 0; row1 < rowCount(); row1++) {
94 
95  // Use table entry except for the one row that gets overridden
96  QModelIndex index1 = index (row1, CURVE_NAME_LIST_COLUMN_CURRENT);
97  QString curveNameCurrent1 = (row1 == row ?
98  curveNameNew :
99  data (index1).toString ());
100 
101  for (int row2 = row1 + 1; row2 < rowCount(); row2++) {
102 
103  // Use table entry except for the one row that gets overridden
104  QModelIndex index2 = index (row2, CURVE_NAME_LIST_COLUMN_CURRENT);
105  QString curveNameCurrent2 = (row2 == row ?
106  curveNameNew :
107  data (index2).toString ());
108 
109  if (curveNameCurrent1 == curveNameCurrent2) {
110 
111  // Duplicate!
112  success = false;
113  break;
114  }
115  }
116  }
117  }
118 
119  return success;
120 }
121 
122 Qt::ItemFlags CurveNameList::flags (const QModelIndex &index) const
123 {
124  if (index.isValid ()) {
125 
126  // Not root item. ItemIsDropEnabled is unwanted during dragging since dragged entry would overwrite
127  // another entry if user forgets to drop into the space between successive entries
128  return (QStandardItemModel::flags (index) |
129  Qt::ItemIsDragEnabled |
130  Qt::ItemIsEnabled |
131  Qt::ItemIsSelectable |
132  Qt::ItemIsEditable) & ~Qt::ItemIsDropEnabled;
133 
134  } else {
135 
136  // Root item
137  return QStandardItemModel::flags (index) |
138  Qt::ItemIsDropEnabled;
139 
140  }
141 }
142 
144  const QString &curveCurrent,
145  const QString &curveOriginal,
146  unsigned int pointCount)
147 {
148  LOG4CPP_DEBUG_S ((*mainCat)) << "CurveNameList::insertRow"
149  << " row=" <<row
150  << " curveCurrent=" << curveCurrent.toLatin1().data()
151  << " curveOriginal=" << curveOriginal.toLatin1().data()
152  << " points=" << pointCount;
153 
154  QStandardItem *item = new QStandardItem (curveCurrent);
155  QStandardItemModel::insertRow (row, item);
156 
157  // Documentation for QAbstractItemModels says beginInsertRows/endInsertRows send off a rowsAboutToBeInserted signal
158  beginInsertRows (QModelIndex (),
159  row,
160  row);
161 
162  m_currentCurveToOriginalCurve [curveCurrent] = curveOriginal;
163  m_originalCurveToPointCount [curveOriginal] = pointCount;
164 
165  endInsertRows ();
166 }
167 
168 QStandardItem *CurveNameList::item(int row, int column) const
169 {
170  LOG4CPP_DEBUG_S ((*mainCat)) << "CurveNameList::item"
171  << " row=" << row;
172 
173  ENGAUGE_ASSERT (row < rowCount ());
174 
175  return QStandardItemModel::item (row, column);
176 }
177 
178 unsigned int CurveNameList::numPointsForSelectedCurves (const QList<unsigned int> &rowsSelected) const
179 {
180  int numPoints = 0;
181  for (int i = 0; i < rowsSelected.count(); i++) {
182  int row = rowsSelected [i];
183 
184  QModelIndex idx = index (row, CURVE_NAME_LIST_COLUMN_CURRENT);
185  QString currentCurve = data (idx).toString ();
186  if (m_currentCurveToOriginalCurve.contains (currentCurve)) {
187 
188  QString originalCurve = m_currentCurveToOriginalCurve [currentCurve];
189  if (m_originalCurveToPointCount.contains (originalCurve)) {
190 
191  numPoints += m_originalCurveToPointCount [originalCurve];
192  }
193  }
194  }
195 
196  return numPoints;
197 }
198 
200  int count,
201  const QModelIndex &parent)
202 {
203  // LOG4CPP is below
204 
205  bool skip = (count != 1 || row < 0 || row > rowCount () || parent.isValid());
206 
207  QString before, after;
208  if (!skip) {
209 
210  before = currentCurvesAsString ();
211 
212  // As documented for QAbstractItemModel, beginRemoveRows "emits the rowsAboutToBeRemoved() signal which connected views
213  // (or proxies) must handle before the data is removed. Otherwise, the views may end up in an invalid state."
214  beginRemoveRows (QModelIndex (),
215  row,
216  row + count - 1);
217 
218  // We do not call QStandardItemModel::removeRow or QAbstractItemModel::removeRow here since that leads to an infinite loop when it calls this method
219  for (int rowRemove = row; rowRemove < row + count; rowRemove++) {
220  QStandardItemModel::removeRows (row,
221  count,
222  parent);
223  }
224 
225  endRemoveRows ();
226 
227  after = currentCurvesAsString ();
228  }
229 
230  LOG4CPP_DEBUG_S ((*mainCat)) << "CurveNameList::removeRows"
231  << " row=" << row
232  << " count=" << count
233  << " isRoot=" << (parent.isValid () ? "no" : "yes")
234  << " skip=" << (skip ? "yes" : "no")
235  << " before=" << before.toLatin1().data()
236  << " after=" << after.toLatin1().data();
237  return true;
238 }
239 
241 {
242  LOG4CPP_INFO_S ((*mainCat)) << "CurveNameList::reset";
243 
244  clear();
245  m_currentCurveToOriginalCurve.clear();
246  m_originalCurveToPointCount.clear();
247 }
248 
249 int CurveNameList::rowCount (const QModelIndex & /* parent */) const
250 {
251  int count = QStandardItemModel::rowCount ();
252 
253 // LOG4CPP_DEBUG_S ((*mainCat)) << "CurveNameList::rowCount"
254 // << " count=" << count;
255 
256  return count;
257 }
258 
259 bool CurveNameList::setData (const QModelIndex &index,
260  const QVariant &value,
261  int role)
262 {
263  LOG4CPP_DEBUG_S ((*mainCat)) << "CurveNameList::setData"
264  << " row=" << index.row()
265  << " value=" << value.toString().toLatin1().data()
266  << " role=" << roleAsString (role).toLatin1().data();
267 
268  bool success;
269  if (role == Qt::EditRole) {
270 
271  // Each curve name must be unique
272  if (curveNameIsAcceptable (value.toString(),
273  index.row())) {
274 
275  // Curve name is fine
276  QModelIndex idxOld = QStandardItemModel::index (index.row(), CURVE_NAME_LIST_COLUMN_CURRENT);
277 
278  // Old and new curve names
279  QString curveCurrentOld = data (idxOld).toString ();
280  QString curveCurrentNew = value.toString ();
281 
282  // Remove old entry after saving original curve name
283  QString curveOriginal;
284  if (m_currentCurveToOriginalCurve.contains (curveCurrentOld)) {
285 
286  // Remember old original curve name
287  curveOriginal = m_currentCurveToOriginalCurve [curveCurrentOld];
288 
289  // Remove old entry
290  m_currentCurveToOriginalCurve.remove (curveCurrentOld);
291 
292  // Add new entry
293  m_currentCurveToOriginalCurve [curveCurrentNew] = curveOriginal;
294  }
295 
296  success = QStandardItemModel::setData (index,
297  value,
298  role);
299  } else {
300 
301  // Curve name is unacceptable
302  success = false;
303 
304  }
305  } else {
306 
307  // For non-edits, this method just calls the superclass method
308  success = QStandardItemModel::setData (index,
309  value,
310  role);
311  }
312 
313  return success;
314 }
315 
317  int column,
318  QStandardItem *item)
319 {
320  // LOG4CPP is below
321 
322  ENGAUGE_ASSERT (column == CURVE_NAME_LIST_COLUMN_CURRENT);
323 
324  QString before = currentCurvesAsString ();
325 
326  QStandardItemModel::setItem (row,
327  column,
328  item);
329 
330  QString after = currentCurvesAsString ();
331 
332  LOG4CPP_DEBUG_S ((*mainCat)) << "CurveNameList::setItem"
333  << " row=" << row
334  << " before=" << before.toLatin1().data()
335  << " after=" << after.toLatin1().data();
336 }
337 
338 Qt::DropActions CurveNameList::supportedDropActions () const
339 {
340  return Qt::MoveAction;
341 }
virtual int columnCount(const QModelIndex &parent) const
One column.
bool containsCurveNameCurrent(const QString &curveName) const
Return true if specified curve name is already in the list.
QString currentCurvesAsString() const
For debugging we dump the curve names.
virtual Qt::ItemFlags flags(const QModelIndex &index) const
Override normal flags with additional editing flags.
virtual void setItem(int row, int column, QStandardItem *item)
Store one curve name data.
QString currentCurveToOriginalCurve(const QString &currentCurve) const
Return the original curve for the specified current curve.
unsigned int numPointsForSelectedCurves(const QList< unsigned int > &rowsSelected) const
Return the number of points associated with the selected curves, as specified by their row numbers...
virtual bool removeRows(int row, int count, const QModelIndex &parent)
Remove one row.
unsigned int currentCurveToPointCount(const QString &currentCurve) const
Return the point count for the specified current curve.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
One row per curve name.
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
Store data for one curve name.
virtual QStandardItem * item(int row, int column=0) const
Retrieve data from model.
virtual Qt::DropActions supportedDropActions() const
Allow dragging for reordering.
void insertRow(int row, const QString &curveCurrent, const QString &curveOriginal, unsigned int pointCount)
Create a new entry at the specified row.
CurveNameList()
Default constructor.
void reset()
Clear all information.