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