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