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