Engauge Digitizer  2
DlgEditPointGraph.cpp
1 /******************************************************************************************************
2  * (C) 2016 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 "DlgEditPointGraph.h"
8 #include "DlgEditPointGraphLineEdit.h"
9 #include "DlgValidatorAbstract.h"
10 #include "DlgValidatorFactory.h"
11 #include "DocumentModelCoords.h"
12 #include "DocumentModelGeneral.h"
13 #include "FormatCoordsUnits.h"
14 #include "Logger.h"
15 #include "MainWindow.h"
16 #include "MainWindowModel.h"
17 #include <QGroupBox>
18 #include <QLabel>
19 #include <QPushButton>
20 #include "QtToString.h"
21 #include <QVBoxLayout>
22 #include "Transformation.h"
23 
24 const Qt::Alignment ALIGNMENT = Qt::AlignCenter;
25 
26 const int MIN_WIDTH_TO_FIT_STRANGE_UNITS = 200;
27 
29  const DocumentModelCoords &modelCoords,
30  const DocumentModelGeneral &modelGeneral,
31  const MainWindowModel &modelMainWindow,
32  const Transformation &transformation,
33  const double *xInitialValue,
34  const double *yInitialValue) :
35  QDialog (&mainWindow),
36  m_changed (false),
37  m_modelCoords (modelCoords),
38  m_modelGeneral (modelGeneral),
39  m_modelMainWindow (modelMainWindow)
40 {
41  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPointGraph::DlgEditPointGraph";
42 
43  QVBoxLayout *layout = new QVBoxLayout;
44  setLayout (layout);
45 
46  setCursor (QCursor (Qt::ArrowCursor));
47  setModal(true);
48  setWindowTitle (tr ("Edit Curve Point(s)"));
49 
50  createCoords (layout);
51  createHint (layout);
52  createOkCancel (layout);
53 
54  initializeGraphCoordinates (xInitialValue,
55  yInitialValue,
56  transformation);
57 
58  m_changed = false; // Initialization of coordinate vaues changed this flag so we reset it and update the controls
59  updateControls ();
60 }
61 
62 DlgEditPointGraph::~DlgEditPointGraph()
63 {
64  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPointGraph::~DlgEditPointGraph";
65 }
66 
67 void DlgEditPointGraph::createCoords (QVBoxLayout *layoutOuter)
68 {
69  // Constraints on x and y are needed for log scaling
70  bool isConstraintX = (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG);
71  bool isConstraintY = (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG);
72  DlgValidatorFactory dlgValidatorFactory;
73  m_validatorGraphX = dlgValidatorFactory.createCartesianOrPolarWithPolarPolar (m_modelCoords.coordScaleXTheta(),
74  isCartesian (),
75  m_modelCoords.coordUnitsX(),
76  m_modelCoords.coordUnitsTheta(),
77  m_modelCoords.coordUnitsDate(),
78  m_modelCoords.coordUnitsTime(),
79  m_modelMainWindow.locale());
80  m_validatorGraphY = dlgValidatorFactory.createCartesianOrPolarWithNonPolarPolar (m_modelCoords.coordScaleYRadius(),
81  isCartesian (),
82  m_modelCoords.coordUnitsY(),
83  m_modelCoords.coordUnitsRadius(),
84  m_modelCoords.coordUnitsDate(),
85  m_modelCoords.coordUnitsTime(),
86  m_modelMainWindow.locale());
87 
88  // Label, with guidance in terms of legal ranges and units
89  QString description = QString ("%1 (%2, %3)%4%5%6%7%8%9 %10:")
90  .arg (tr ("Graph Coordinates"))
91  .arg (nameXTheta ())
92  .arg (nameYRadius ())
93  .arg (isConstraintX || isConstraintY ? " with " : "")
94  .arg (isConstraintX ? QString (nameXTheta ()) : "")
95  .arg (isConstraintX ? " > 0" : "")
96  .arg (isConstraintX && isConstraintY ? " and " : "")
97  .arg ( isConstraintY ? QString (nameYRadius ()) : "")
98  .arg ( isConstraintY ? " > 0" : "")
99  .arg (tr ("as"));
100  QGroupBox *panel = new QGroupBox (description, this);
101  layoutOuter->addWidget (panel);
102 
103  QHBoxLayout *layout = new QHBoxLayout (panel);
104  panel->setLayout (layout);
105 
106  // Row
107  QLabel *labelGraphParLeft = new QLabel (tr ("("), this);
108  layout->addWidget(labelGraphParLeft, 0);
109 
110  m_editGraphX = new DlgEditPointGraphLineEdit;
111  m_editGraphX->setMinimumWidth(MIN_WIDTH_TO_FIT_STRANGE_UNITS);
112  m_editGraphX->setAlignment (ALIGNMENT);
113  m_editGraphX->setValidator (m_validatorGraphX);
114  // setStatusTip does not work for modal dialogs
115  m_editGraphX->setWhatsThis (tr ("Enter the first graph coordinate value to be applied to the graph points.\n\n"
116  "Leave this field empty if no value is to be applied to the graph points.\n\n"
117  "For cartesian plots this is the X coordinate. For polar plots this is the radius R.\n\n"
118  "The expected format of the coordinate value is determined by the locale setting. If "
119  "typed values are not recognized as expected, check the locale setting in Settings / Main Window..."));
120  layout->addWidget(m_editGraphX, 0);
121  connect (m_editGraphX, SIGNAL (textChanged (const QString &)), this, SLOT (slotTextChanged (const QString &)));
122 
123  QLabel *labelGraphComma = new QLabel (tr (", "), this);
124  layout->addWidget(labelGraphComma, 0);
125 
126  m_editGraphY = new DlgEditPointGraphLineEdit;
127  m_editGraphY->setMinimumWidth(MIN_WIDTH_TO_FIT_STRANGE_UNITS);
128  m_editGraphY->setAlignment (ALIGNMENT);
129  m_editGraphY->setValidator (m_validatorGraphY);
130  // setStatusTip does not work for modal dialogs
131  m_editGraphY->setWhatsThis (tr ("Enter the second graph coordinate value to be applied to the graph points.\n\n"
132  "Leave this field empty if no value is to be applied to the graph points.\n\n"
133  "For cartesian plots this is the Y coordinate. For polar plots this is the angle Theta.\n\n"
134  "The expected format of the coordinate value is determined by the locale setting. If "
135  "typed values are not recognized as expected, check the locale setting in Settings / Main Window..."));
136  layout->addWidget(m_editGraphY, 0);
137  connect (m_editGraphY, SIGNAL (textChanged (const QString &)), this, SLOT (slotTextChanged (const QString &)));
138 
139  QLabel *labelGraphParRight = new QLabel (tr (")"), this);
140  layout->addWidget(labelGraphParRight, 0);
141 }
142 
143 void DlgEditPointGraph::createHint (QVBoxLayout *layoutOuter)
144 {
145  // Insert a hint explaining why decimal points may not be accepted. Very confusing for user to figure out the problem at first, and
146  // then figure out which setting should change to fix it. The hint is centered so it is slightly less intrusive
147 
148  QWidget *widget = new QWidget;
149  layoutOuter->addWidget (widget, 0, Qt::AlignCenter);
150 
151  QHBoxLayout *layout = new QHBoxLayout;
152  widget->setLayout (layout);
153 
154  QString locale = QLocaleToString (m_modelMainWindow.locale ());
155  QString hint = QString ("%1: %2")
156  .arg (tr ("Number format"))
157  .arg (locale);
158  QLabel *label = new QLabel (hint);
159  layout->addWidget (label);
160 }
161 
162 void DlgEditPointGraph::createOkCancel (QVBoxLayout *layoutOuter)
163 {
164  QWidget *panel = new QWidget (this);
165  layoutOuter->addWidget (panel, 0, Qt::AlignCenter);
166 
167  QHBoxLayout *layout = new QHBoxLayout (panel);
168  panel->setLayout (layout);
169 
170  m_btnOk = new QPushButton (tr ("Ok"), this);
171  layout->addWidget(m_btnOk);
172  connect (m_btnOk, SIGNAL (released ()), this, SLOT (accept ()));
173 
174  m_btnCancel = new QPushButton (tr ("Cancel"), this);
175  layout->addWidget(m_btnCancel);
176  connect (m_btnCancel, SIGNAL (released ()), this, SLOT (reject ()));
177 }
178 
179 void DlgEditPointGraph::initializeGraphCoordinates (const double *xInitialValue,
180  const double *yInitialValue,
181  const Transformation &transformation)
182 {
183  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPointGraph::initializeGraphCoordinates";
184 
185  QString xTheta, yRadius;
186  if ((xInitialValue != 0) &&
187  (yInitialValue != 0)) {
188 
189  FormatCoordsUnits format;
190  format.unformattedToFormatted (*xInitialValue,
191  *yInitialValue,
192  m_modelCoords,
193  m_modelGeneral,
194  m_modelMainWindow,
195  xTheta,
196  yRadius,
197  transformation);
198  }
199 
200  m_editGraphX->setText (xTheta);
201  m_editGraphY->setText (yRadius);
202 }
203 
204 bool DlgEditPointGraph::isCartesian () const
205 {
206  return (m_modelCoords.coordsType() == COORDS_TYPE_CARTESIAN);
207 }
208 
209 QChar DlgEditPointGraph::nameXTheta () const
210 {
211  return (isCartesian () ? QChar ('X') : THETA);
212 }
213 
214 QChar DlgEditPointGraph::nameYRadius () const
215 {
216  return (isCartesian () ? QChar ('Y') : QChar ('R'));
217 }
218 
220  double &x,
221  bool &isY,
222  double &y) const
223 {
224  FormatCoordsUnits format;
225 
226  // Use zero for any empty coordinate
227  QString xTextNotEmpty = QString ("%1").arg (m_editGraphX->text().isEmpty () ? "0" : m_editGraphX->text());
228  QString yTextNotEmpty = QString ("%1").arg (m_editGraphY->text().isEmpty () ? "0" : m_editGraphY->text());
229 
230  format.formattedToUnformatted (xTextNotEmpty,
231  yTextNotEmpty,
232  m_modelCoords,
233  m_modelMainWindow,
234  x,
235  y);
236 
237  isX = !m_editGraphX->text().isEmpty();
238  isY = !m_editGraphY->text().isEmpty();
239 }
240 
241 void DlgEditPointGraph::slotTextChanged (const QString &)
242 {
243  m_changed = true;
244  updateControls ();
245 }
246 
247 QString DlgEditPointGraph::unitsType (bool isXTheta) const
248 {
249  if (isCartesian ()) {
250  if (isXTheta) {
251  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsX());
252  } else {
253  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsY());
254  }
255  } else {
256  if (isXTheta) {
257  return coordUnitsPolarThetaToBriefType (m_modelCoords.coordUnitsTheta());
258  } else {
259  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsRadius());
260  }
261  }
262 }
263 
264 void DlgEditPointGraph::updateControls ()
265 {
266  QString textX = m_editGraphX->text();
267  QString textY = m_editGraphY->text();
268 
269  // Feedback indicating that empty coordinate will be skipped rather than applied to the selected points
270  m_editGraphX->updateBackground ();
271  m_editGraphY->updateBackground ();
272 
273  // Tests that all have to be true
274  // 1) At least one value has been changed
275  // 2) At least one value is not empty
276  // 3) The values that are not empty are properly formatted. This is done remembering that we need to
277  // check for not empty (which allows single minus sign) and for valid number (which prevents single
278  // minus sign)
279  bool test2 = (!textX.isEmpty() || !textY.isEmpty());
280 
281  int posX, posY;
282  bool test3 = true;
283  if (!textX.isEmpty()) {
284  test3 &= (m_validatorGraphX->validate(textX, posX) == QValidator::Acceptable);
285  }
286  if (!textY.isEmpty()) {
287  test3 &= (m_validatorGraphY->validate(textY, posY) == QValidator::Acceptable);
288  }
289 
290  m_btnOk->setEnabled (m_changed && test2 && test3);
291 }
Model for DlgSettingsGeneral and CmdSettingsGeneral.
Adds hover highlighting to QLineEdit.
CoordUnitsNonPolarTheta coordUnitsRadius() const
Get method for radius units.
void updateBackground()
Update background given the current state.
void formattedToUnformatted(const QString &xThetaFormatted, const QString &yRadiusFormatted, const DocumentModelCoords &modelCoords, const MainWindowModel &mainWindowModel, double &xThetaUnformatted, double &yRadiusUnformatted) const
Convert formatted string to unformatted numeric value.
CoordUnitsNonPolarTheta coordUnitsY() const
Get method for x units.
virtual QValidator::State validate(QString &input, int &pos) const =0
Validate according to the numeric format specific to the leaf class.
CoordUnitsPolarTheta coordUnitsTheta() const
Get method for theta unit.
void unformattedToFormatted(double xThetaUnformatted, double yRadiusUnformatted, const DocumentModelCoords &modelCoords, const DocumentModelGeneral &modelGeneral, const MainWindowModel &mainWindowModel, QString &xThetaFormatted, QString &yRadiusFormatted, const Transformation &transformation) const
Convert unformatted numeric value to formatted string. Transformation is used to determine best resol...
Affine transformation between screen and graph coordinates, based on digitized axis points...
CoordUnitsTime coordUnitsTime() const
Get method for time format when used.
Model for DlgSettingsMainWindow.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
CoordUnitsDate coordUnitsDate() const
Get method for date format when used.
Model for DlgSettingsCoords and CmdSettingsCoords.
Highest-level wrapper around other Formats classes.
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
QLocale locale() const
Get method for locale.
DlgValidatorAbstract * createCartesianOrPolarWithNonPolarPolar(CoordScale coordScale, bool isCartesian, CoordUnitsNonPolarTheta coordUnitsCartesian, CoordUnitsNonPolarTheta coordUnitsPolar, CoordUnitsDate coordUnitsDate, CoordUnitsTime coordUnitsTime, const QLocale &locale) const
Factory method for generating validators for either cartesian or polar case, when polar format is spe...
CoordsType coordsType() const
Get method for coordinates type.
void posGraph(bool &isX, double &x, bool &isY, double &y) const
Return one or both coordinates. Only applies if dialog was accepted.
CoordUnitsNonPolarTheta coordUnitsX() const
Get method for x units.
DlgEditPointGraph(MainWindow &mainWindow, const DocumentModelCoords &modelCoords, const DocumentModelGeneral &modelGeneral, const MainWindowModel &modelMainWindow, const Transformation &transformation, const double *xInitialValue=0, const double *yInitialValue=0)
Constructor for existing point which already has graph coordinates (which may be changed using this d...
Validator factory.
DlgValidatorAbstract * createCartesianOrPolarWithPolarPolar(CoordScale coordScale, bool isCartesian, CoordUnitsNonPolarTheta coordUnitsCartesian, CoordUnitsPolarTheta coordUnitsPolar, CoordUnitsDate coordUnitsDate, CoordUnitsTime coordUnitsTime, const QLocale &locale) const
Factory method for generating validators for either cartesian or polar case, when polar format is spe...
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:89