Engauge Digitizer  2
DlgSettingsCurveAddRemove.cpp
1 #include "CmdMediator.h"
2 #include "CmdSettingsCurveAddRemove.h"
3 #include "CurveNameList.h"
4 #include "DlgSettingsCurveAddRemove.h"
5 #include "EngaugeAssert.h"
6 #include "Logger.h"
7 #include "MainWindow.h"
8 #include <QDebug>
9 #include <QGridLayout>
10 #include <QLabel>
11 #include <QListView>
12 #include <QMessageBox>
13 #include <QPushButton>
14 #include "QtToString.h"
15 
17  DlgSettingsAbstractBase ("Curve Add/Remove",
18  "DlgSettingsCurveAddRemove",
19  mainWindow)
20 {
21  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::DlgSettingsCurveAddRemove";
22 
23  QWidget *subPanel = createSubPanel ();
24  finishPanel (subPanel);
25 }
26 
27 DlgSettingsCurveAddRemove::~DlgSettingsCurveAddRemove()
28 {
29  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::~DlgSettingsCurveAddRemove";
30 }
31 
32 void DlgSettingsCurveAddRemove::appendCurveName (const QString &curveNameNew,
33  const QString &curveNameOriginal,
34  int numPoints)
35 {
36  ENGAUGE_CHECK_PTR (m_curveNameList);
37 
38  int row = m_curveNameList->rowCount ();
39  insertCurveName (row,
40  curveNameNew,
41  curveNameOriginal,
42  numPoints);
43 }
44 
45 void DlgSettingsCurveAddRemove::createButtons (QGridLayout *layout,
46  int &row)
47 {
48  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::createButtons";
49 
50  m_btnNew = new QPushButton ("New...");
51  m_btnNew->setWhatsThis (tr ("Adds a new curve to the curve list. The curve name can be edited in the curve name list.\n\n"
52  "If a curve is selected then the new curve will be inserted just before it, otherwise the new curve "
53  "will be added at the end.\n\n"
54  "Every curve name must be unique"));
55  m_btnNew->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
56  connect (m_btnNew, SIGNAL (released ()), this, SLOT (slotNew()));
57  layout->addWidget (m_btnNew, row, 1, 1, 1, Qt::AlignLeft);
58 
59  m_btnRemove = new QPushButton ("Remove");
60  m_btnRemove->setWhatsThis (tr ("Removes the currently selected curve from the curve list.\n\n"
61  "There must always be at least one curve"));
62  m_btnRemove->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
63  connect (m_btnRemove, SIGNAL (released ()), this, SLOT (slotRemove()));
64  layout->addWidget (m_btnRemove, row++, 2, 1, 1, Qt::AlignRight);
65 }
66 
67 void DlgSettingsCurveAddRemove::createListCurves (QGridLayout *layout,
68  int &row)
69 {
70  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::createListCurves";
71 
72  QLabel *label = new QLabel (tr ("Curve Names:"));
73  layout->addWidget (label, row++, 1);
74 
75  m_curveNameList = new CurveNameList;
76 
77  // There is no Qt::ItemIsEditable flag for QListView, so instead we set that flag for the QListViewItems
78  m_listCurves = new QListView;
79  m_listCurves->setWhatsThis (tr ("List of the curves belonging to this document.\n\n"
80  "Click on a curve name to edit it.\n\n"
81  "Reorder curves by dragging them around."));
82  m_listCurves->setMinimumHeight (200);
83  m_listCurves->setSelectionMode (QAbstractItemView::ExtendedSelection);
84  m_listCurves->setDefaultDropAction (Qt::MoveAction);
85  m_listCurves->setDragDropOverwriteMode (true);
86  m_listCurves->setDragEnabled (true);
87  m_listCurves->setDropIndicatorShown (true);
88  m_listCurves->setDragDropMode (QAbstractItemView::InternalMove);
89  m_listCurves->setViewMode (QListView::ListMode);
90  m_listCurves->setMovement (QListView::Snap);
91  m_listCurves->setModel (m_curveNameList);
92  layout->addWidget (m_listCurves, row++, 1, 1, 2);
93  connect (m_curveNameList, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex &, const QVector<int> &)),
94  this, SLOT (slotDataChanged (const QModelIndex &, const QModelIndex &, const QVector<int> &)));
95  connect (m_listCurves->selectionModel (), SIGNAL (selectionChanged (QItemSelection, QItemSelection)),
96  this, SLOT (slotSelectionChanged (QItemSelection, QItemSelection)));
97 }
98 
100 {
101  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::createSubPanel";
102 
103  const int EMPTY_COLUMN_WIDTH = 30;
104  const int EMPTY_ROW_HEIGHT = 40;
105 
106  QWidget *subPanel = new QWidget ();
107  QGridLayout *layout = new QGridLayout (subPanel);
108  subPanel->setLayout (layout);
109 
110  int row = 1;
111  createListCurves (layout, row);
112  createButtons (layout, row);
113 
114  layout->setColumnStretch (0, 0); // Empty first column
115  layout->setColumnMinimumWidth (0, EMPTY_COLUMN_WIDTH);
116  layout->setColumnStretch (1, 1); // New
117  layout->setColumnStretch (2, 1); // Remove
118  layout->setColumnStretch (3, 0); // Empty last column
119  layout->setColumnMinimumWidth (3, EMPTY_COLUMN_WIDTH);
120 
121  layout->setRowStretch (0, 0); // Empty first row
122  layout->setRowMinimumHeight (0, EMPTY_ROW_HEIGHT);
123  layout->setRowStretch (row, 0); // Empty last row
124  layout->setRowMinimumHeight (row, EMPTY_ROW_HEIGHT);
125 
126  return subPanel;
127 }
128 
129 bool DlgSettingsCurveAddRemove::endsWithNumber (const QString &str) const
130 {
131  bool success = false;
132 
133  if (!str.isEmpty ()) {
134 
135  success = (str.right (1).at (0).digitValue() >= 0);
136  }
137 
138  return success;
139 }
140 
142 {
143  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::handleOk";
144 
146  cmdMediator ().document(),
147  *m_curveNameList);
148  cmdMediator ().push (cmd);
149 
150  hide ();
151 }
152 
153 void DlgSettingsCurveAddRemove::insertCurveName (int row,
154  const QString &curveNameNew,
155  const QString &curveNameOriginal,
156  int numPoints)
157 {
158  if (m_curveNameList->insertRow (row)) {
159 
160  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::insertCurveName curveName=" << curveNameNew.toLatin1 ().data ();
161 
162  CurveNameListEntry curvesEntry (curveNameNew,
163  curveNameOriginal,
164  numPoints);
165 
166  m_curveNameList->setData (m_curveNameList->index (row, 0),
167  curvesEntry.curveNameCurrent ());
168  m_curveNameList->setData (m_curveNameList->index (row, 1),
169  curvesEntry.curveNameOriginal ());
170  m_curveNameList->setData (m_curveNameList->index (row, 2),
171  numPoints);
172 
173  } else {
174 
175  LOG4CPP_ERROR_S ((*mainCat)) << "DlgSettingsCurveAddRemove::insertCurveName failed curveName="
176  << curveNameNew.toLatin1 ().data ();
177 
178  }
179 }
180 
182 {
183  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::load";
184 
185  setCmdMediator (cmdMediator);
186 
187  // Remove any data from previous showing of dialog
188  while (m_curveNameList->rowCount () > 0) {
189  m_curveNameList->removeRow (0);
190  }
191 
192  QStringList curveNames = cmdMediator.curvesGraphsNames ();
193  QStringList::const_iterator itr;
194  for (itr = curveNames.begin (); itr != curveNames.end (); itr++) {
195  QString curveName = *itr;
196  appendCurveName (curveName,
197  curveName,
198  cmdMediator.curvesGraphsNumPoints (curveName));
199  }
200 
201  enableOk (false); // Disable Ok button since there not yet any changes
202 }
203 
204 QString DlgSettingsCurveAddRemove::nextCurveName () const
205 {
206  const QString DASH_ONE ("-1"); // Nice value to start a new range at a lower level than the current level
207 
208  ENGAUGE_CHECK_PTR (m_listCurves);
209 
210  int numSelectedItems = m_listCurves->selectionModel ()->selectedIndexes ().count ();
211  int numItems = m_listCurves->model ()->rowCount ();
212 
213  // Determine index where new entry will be inserted
214  int currentIndex = -1;
215  if ((numSelectedItems == 0) &&
216  (numItems > 0)) {
217 
218  // Append after list which has at least one entry
219  currentIndex = numItems;
220 
221  } else if (numSelectedItems == 1) {
222 
223  // Insert before the selected index
224  currentIndex = m_listCurves->selectionModel ()->selectedIndexes ().at (0).row ();
225 
226  }
227 
228  // Curves names of existing before/after curves
229  QString curveNameBefore, curveNameAfter;
230  if (currentIndex > 0) {
231 
232  QModelIndex index = m_curveNameList->index (currentIndex - 1, 0);
233  curveNameBefore = m_curveNameList->data (index).toString ();
234 
235  }
236 
237  if ((0 <= currentIndex) && (currentIndex < numItems)) {
238 
239  QModelIndex index = m_curveNameList->index (currentIndex, 0);
240  curveNameAfter = m_curveNameList->data (index).toString ();
241 
242  }
243 
244  // New curve name
245  QString curveNameNext;
246  if (curveNameBefore.isEmpty () && !curveNameAfter.isEmpty () && endsWithNumber (curveNameAfter)) {
247 
248  // Pick a name before curveNameAfter
249  int numberAfter = numberAtEnd (curveNameAfter);
250  int numberNew = numberAfter - 1;
251  int pos = curveNameAfter.lastIndexOf (QString::number (numberAfter));
252  if (pos >= 0) {
253 
254  curveNameNext = QString ("%1%2")
255  .arg (curveNameAfter.left (pos))
256  .arg (numberNew);
257 
258  } else {
259 
260  curveNameNext = curveNameAfter; // Better than nothing
261 
262  }
263 
264  } else if (curveNameBefore.isEmpty ()) {
265 
266  curveNameNext = DEFAULT_GRAPH_CURVE_NAME; // If necessary, this will be deconflicted below
267 
268  } else {
269 
270  curveNameNext = curveNameBefore; // This will be deconflicted below
271 
272  if (endsWithNumber (curveNameBefore)) {
273 
274  // Curve name ends with a number. Pick a name after curveNameBefore, being sure to not match curveNameAfter
275  int numberBefore = numberAtEnd (curveNameBefore);
276  int numberNew = numberBefore + 1;
277  int pos = curveNameBefore.lastIndexOf (QString::number (numberBefore));
278  if (pos >= 0) {
279 
280  curveNameNext = QString ("%1%2")
281  .arg (curveNameBefore.left (pos))
282  .arg (numberNew);
283  if (curveNameNext == curveNameAfter) {
284 
285  // The difference between before and after is exactly one so we go to a lower level
286  curveNameNext = QString ("%1%2")
287  .arg (curveNameBefore)
288  .arg (DASH_ONE);
289  }
290  }
291  }
292  }
293 
294  // At this point we have curveNameNext which does not conflict with curveNameBefore or
295  // curveNameAfter, but it may in rare cases conflict with some other curve name. We keep
296  // adding to the name until there is no conflict
297  while (m_curveNameList->containsCurveNameCurrent (curveNameNext)) {
298  curveNameNext += DASH_ONE;
299  }
300 
301  return curveNameNext;
302 }
303 
304 int DlgSettingsCurveAddRemove::numberAtEnd (const QString &str) const
305 {
306  ENGAUGE_ASSERT (endsWithNumber (str));
307 
308  // Go backward until the first nondigit
309  int sign = +1;
310  int ch = str.size () - 1;
311  while (str.at (ch).digitValue() >= 0) {
312  --ch;
313 
314  if (ch < 0) {
315  break;
316  }
317  }
318  ++ch;
319 
320  return sign * str.mid (ch).toInt ();
321 }
322 
323 void DlgSettingsCurveAddRemove::removeSelectedCurves ()
324 {
325  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::removeSelectedCurves";
326 
327  for (int i = m_listCurves->selectionModel ()->selectedIndexes ().count () - 1; i >= 0; i--) {
328 
329  int row = m_listCurves->selectionModel ()->selectedIndexes ().at (i).row ();
330 
331  m_curveNameList->removeRow (row);
332  }
333 }
334 
335 void DlgSettingsCurveAddRemove::slotDataChanged (const QModelIndex &topLeft,
336  const QModelIndex &bottomRight,
337  const QVector<int> &roles)
338 {
339  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::slotDataChanged"
340  << " topLeft=(" << topLeft.row () << "," << topLeft.column () << ")"
341  << " bottomRight=(" << bottomRight.row () << "," << bottomRight.column () << ")"
342  << " roles=" << rolesAsString (roles).toLatin1 ().data ();
343 
344  updateControls ();
345 }
346 
347 void DlgSettingsCurveAddRemove::slotNew ()
348 {
349  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::slotNew";
350 
351  const QString NO_ORIGINAL_CURVE_NAME;
352  const int NO_POINTS = 0;
353 
354  QString curveNameSuggestion = nextCurveName ();
355 
356  if (m_listCurves->selectionModel ()->selectedIndexes ().count () == 1) {
357 
358  QModelIndex idx = m_listCurves->selectionModel ()->selectedIndexes ().at (0);
359  insertCurveName (idx.row (),
360  curveNameSuggestion,
361  NO_ORIGINAL_CURVE_NAME,
362  NO_POINTS);
363 
364  } else {
365 
366  appendCurveName (curveNameSuggestion,
367  NO_ORIGINAL_CURVE_NAME,
368  NO_POINTS);
369 
370  }
371 
372  updateControls();
373 }
374 
375 void DlgSettingsCurveAddRemove::slotRemove ()
376 {
377  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::slotRemove";
378 
379  int numPoints = 0;
380  for (int i = 0; i < m_listCurves->selectionModel ()->selectedIndexes ().count (); i++) {
381 
382  int row = m_listCurves->selectionModel ()->selectedIndexes ().at (i).row ();
383  QModelIndex idx = m_curveNameList->index (row, CurveNameListEntry::COL_NUM_POINTS ());
384  int curvePoints = m_curveNameList->data (idx, Qt::DisplayRole).toInt ();
385 
386  numPoints += curvePoints;
387  }
388 
389  int rtn = QMessageBox::Ok;
390  if (numPoints > 0) {
391 
392  QString msg;
393  if (m_listCurves->selectionModel ()->selectedIndexes ().count () == 1) {
394  msg = QString ("Removing this curve will also remove %1 points. Continue?").arg (numPoints);
395  } else {
396  msg = QString ("Removing these curves will also remove %1 points. Continue?").arg (numPoints);
397  }
398 
399  rtn = QMessageBox::warning (0,
400  "Curves With Points",
401  msg,
402  QMessageBox::Ok,
403  QMessageBox::Cancel);
404  }
405 
406  if (rtn == QMessageBox::Ok) {
407  removeSelectedCurves ();
408  }
409 
410  updateControls();
411 }
412 
413 void DlgSettingsCurveAddRemove::slotSelectionChanged (QItemSelection, QItemSelection)
414 {
415  updateControls ();
416 }
417 
418 void DlgSettingsCurveAddRemove::updateControls ()
419 {
420  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsCurveAddRemove::updateControls";
421 
422  enableOk (true);
423 
424  ENGAUGE_CHECK_PTR (m_listCurves);
425 
426  int numSelectedItems = m_listCurves->selectionModel ()->selectedIndexes ().count ();
427  int numItems = m_curveNameList->rowCount ();
428 
429  if (numSelectedItems < 2 ) {
430 
431  // Add or Insert is possible
432  m_btnNew->setEnabled (true);
433  m_btnNew->setText (numSelectedItems == 0 ? "Add" : "Insert");
434 
435  } else {
436 
437  // Placement of new curve would be ambigous
438  m_btnNew->setEnabled (false);
439  }
440 
441  // Leave at least one curve
442  m_btnRemove->setEnabled ((numSelectedItems > 0) && (numSelectedItems < numItems));
443 }
void setCmdMediator(CmdMediator &cmdMediator)
Store CmdMediator for easy access by the leaf class.
bool containsCurveNameCurrent(const QString &curveName) const
Return true if specified curve name is already in the list.
Utility class for converting the QVariant in CurveNameList to/from the curve names as QStrings...
Command for DlgSettingsCurveAddRemove.
virtual QWidget * createSubPanel()
Create dialog-specific panel to which base class will add Ok and Cancel buttons.
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
Store one curve name data.
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
Retrieve data from model.
void finishPanel(QWidget *subPanel)
Add Ok and Cancel buttons to subpanel to get the whole dialog.
static int COL_NUM_POINTS()
Get method for number of points constant.
void load(CmdMediator &cmdMediator)
Load settings from Document.
void enableOk(bool enable)
Let leaf subclass control the Ok button.
Command queue stack.
Definition: CmdMediator.h:16
Abstract base class for all Settings dialogs.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
One row per curve name.
Model for DlgSettingsCurveAddRemove and CmdSettingsCurveAddRemove.
Definition: CurveNameList.h:10
int curvesGraphsNumPoints(const QString &curveName) const
See CurvesGraphs::curvesGraphsNumPoints.
Definition: CmdMediator.cpp:56
MainWindow & mainWindow()
Get method for MainWindow.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:60
DlgSettingsCurveAddRemove(MainWindow &mainWindow)
Single constructor.
QStringList curvesGraphsNames() const
See CurvesGraphs::curvesGraphsNames.
Definition: CmdMediator.cpp:51
CmdMediator & cmdMediator()
Provide access to Document information wrapped inside CmdMediator.
virtual void handleOk()
Process slotOk.