Engauge Digitizer  2
 All Classes Files Functions Variables Enumerations Enumerator Friends Pages
DlgEditPoint.cpp
1 /******************************************************************************************************
2  * (C) 2014 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 "DigitizeStateAbstractBase.h"
8 #include "DlgEditPoint.h"
9 #include "DlgValidatorAbstract.h"
10 #include "DlgValidatorFactory.h"
11 #include "DocumentAxesPointsRequired.h"
12 #include "DocumentModelCoords.h"
13 #include "EngaugeAssert.h"
14 #include "FormatCoordsUnits.h"
15 #include "FormatDateTime.h"
16 #include "FormatDegreesMinutesSecondsNonPolarTheta.h"
17 #include "FormatDegreesMinutesSecondsPolarTheta.h"
18 #include "Logger.h"
19 #include "MainWindow.h"
20 #include "MainWindowModel.h"
21 #include <QDoubleValidator>
22 #include <QGridLayout>
23 #include <QGroupBox>
24 #include <QHBoxLayout>
25 #include <QLabel>
26 #include <QRect>
27 #include <QVBoxLayout>
28 #include "Transformation.h"
29 
30 const Qt::Alignment ALIGNMENT = Qt::AlignCenter;
31 
32 const int MIN_WIDTH_TO_FIT_STRANGE_UNITS = 200;
33 
34 const bool IS_X_THETA = true;
35 const bool IS_NOT_X_THETA = false;
36 
38  DigitizeStateAbstractBase &digitizeState,
39  const DocumentModelCoords &modelCoords,
40  const MainWindowModel &modelMainWindow,
41  const QCursor &cursorShape,
42  const Transformation &transformation,
43  DocumentAxesPointsRequired documentAxesPointsRequired,
44  bool isXOnly,
45  const double *xInitialValue,
46  const double *yInitialValue) :
47  QDialog (&mainWindow),
48  m_cursorShape (cursorShape),
49  m_documentAxesPointsRequired (documentAxesPointsRequired),
50  m_modelCoords (modelCoords),
51  m_modelMainWindow (modelMainWindow)
52 {
53  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPoint::DlgEditPoint";
54 
55  // Either one or two coordinates are desired
56  bool isX = (documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_3) || isXOnly;
57  bool isY = (documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_3) || !isXOnly;
58 
59  // To guarantee the override cursor is always removed, we call removeOverrideCursor here rather than in the code that
60  // allocates this DlgEditPoint. The digitizeState argument is otherwise unused.
61  digitizeState.removeOverrideCursor();
62 
63  connect (this, SIGNAL (signalSetOverrideCursor (QCursor)), &mainWindow, SLOT (slotSetOverrideCursor (QCursor)));
64 
65  QVBoxLayout *layout = new QVBoxLayout;
66  setLayout (layout);
67 
68  setCursor (QCursor (Qt::ArrowCursor));
69  setModal(true);
70  setWindowTitle (tr ("Edit Axis Point"));
71 
72  createCoords (layout);
73  createOkCancel (layout);
74 
75  initializeGraphCoordinates (xInitialValue,
76  yInitialValue,
77  transformation,
78  isX,
79  isY);
80 
81  updateControls ();
82 }
83 
84 DlgEditPoint::~DlgEditPoint()
85 {
86  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPoint::~DlgEditPoint";
87 
88  emit signalSetOverrideCursor (m_cursorShape);
89 }
90 
91 void DlgEditPoint::createCoords (QVBoxLayout *layoutOuter)
92 {
93  // Constraints on x and y are needed for log scaling
94  bool isConstraintX = (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG);
95  bool isConstraintY = (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG);
96  DlgValidatorFactory dlgValidatorFactory;
97  m_validatorGraphX = dlgValidatorFactory.createCartesianOrPolarWithPolarPolar (m_modelCoords.coordScaleXTheta(),
98  isCartesian (),
99  m_modelCoords.coordUnitsX(),
100  m_modelCoords.coordUnitsTheta(),
101  m_modelCoords.coordUnitsDate(),
102  m_modelCoords.coordUnitsTime(),
103  m_modelMainWindow.locale());
104  m_validatorGraphY = dlgValidatorFactory.createCartesianOrPolarWithNonPolarPolar (m_modelCoords.coordScaleYRadius(),
105  isCartesian (),
106  m_modelCoords.coordUnitsY(),
107  m_modelCoords.coordUnitsRadius(),
108  m_modelCoords.coordUnitsDate(),
109  m_modelCoords.coordUnitsTime(),
110  m_modelMainWindow.locale());
111 
112  // Label, with guidance in terms of legal ranges and units
113  QString description = QString ("%1 (%2, %3)%4%5%6%7%8%9 %10 (%11, %12):")
114  .arg (tr ("Graph Coordinates"))
115  .arg (nameXTheta ())
116  .arg (nameYRadius ())
117  .arg (isConstraintX || isConstraintY ? " with " : "")
118  .arg (isConstraintX ? QString (nameXTheta ()) : "")
119  .arg (isConstraintX ? " > 0" : "")
120  .arg (isConstraintX && isConstraintY ? " and " : "")
121  .arg ( isConstraintY ? QString (nameYRadius ()) : "")
122  .arg ( isConstraintY ? " > 0" : "")
123  .arg (tr ("as"))
124  .arg (unitsType (IS_X_THETA))
125  .arg (unitsType (IS_NOT_X_THETA));
126  QGroupBox *panel = new QGroupBox (description, this);
127  layoutOuter->addWidget (panel);
128 
129  QHBoxLayout *layout = new QHBoxLayout (panel);
130  panel->setLayout (layout);
131 
132  // Row
133  QLabel *labelGraphParLeft = new QLabel (tr ("("), this);
134  layout->addWidget(labelGraphParLeft, 0);
135 
136  m_editGraphX = new QLineEdit;
137  m_editGraphX->setMinimumWidth(MIN_WIDTH_TO_FIT_STRANGE_UNITS);
138  m_editGraphX->setAlignment (ALIGNMENT);
139  m_editGraphX->setValidator (m_validatorGraphX);
140  // setStatusTip does not work for modal dialogs
141  m_editGraphX->setWhatsThis (tr ("Enter the first graph coordinate of the axis point.\n\n"
142  "For cartesian plots this is X. For polar plots this is the radius R.\n\n"
143  "The expected format of the coordinate value is determined by the locale setting. If "
144  "typed values are not recognized as expected, check the locale setting in Settings / Main Window..."));
145  layout->addWidget(m_editGraphX, 0);
146  connect (m_editGraphX, SIGNAL (textChanged (const QString &)), this, SLOT (slotTextChanged (const QString &)));
147 
148  QLabel *labelGraphComma = new QLabel (tr (", "), this);
149  layout->addWidget(labelGraphComma, 0);
150 
151  m_editGraphY = new QLineEdit;
152  m_editGraphY->setMinimumWidth(MIN_WIDTH_TO_FIT_STRANGE_UNITS);
153  m_editGraphY->setAlignment (ALIGNMENT);
154  m_editGraphY->setValidator (m_validatorGraphY);
155  // setStatusTip does not work for modal dialogs
156  m_editGraphY->setWhatsThis (tr ("Enter the second graph coordinate of the axis point.\n\n"
157  "For cartesian plots this is Y. For plot plots this is the angle Theta.\n\n"
158  "The expected format of the coordinate value is determined by the locale setting. If "
159  "typed values are not recognized as expected, check the locale setting in Settings / Main Window..."));
160  layout->addWidget(m_editGraphY, 0);
161  connect (m_editGraphY, SIGNAL (textChanged (const QString &)), this, SLOT (slotTextChanged (const QString &)));
162 
163  QLabel *labelGraphParRight = new QLabel (tr (")"), this);
164  layout->addWidget(labelGraphParRight, 0);
165 }
166 
167 void DlgEditPoint::createOkCancel (QVBoxLayout *layoutOuter)
168 {
169  QWidget *panel = new QWidget (this);
170  layoutOuter->addWidget (panel, 0, Qt::AlignCenter);
171 
172  QHBoxLayout *layout = new QHBoxLayout (panel);
173  panel->setLayout (layout);
174 
175  m_btnOk = new QPushButton (tr ("Ok"), this);
176  layout->addWidget(m_btnOk);
177  connect (m_btnOk, SIGNAL (released ()), this, SLOT (accept ()));
178 
179  m_btnCancel = new QPushButton (tr ("Cancel"), this);
180  layout->addWidget(m_btnCancel);
181  connect (m_btnCancel, SIGNAL (released ()), this, SLOT (reject ()));
182 }
183 
184 void DlgEditPoint::initializeGraphCoordinates (const double *xInitialValue,
185  const double *yInitialValue,
186  const Transformation &transformation,
187  bool isX,
188  bool isY)
189 {
190  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPoint::initializeGraphCoordinates";
191 
192  QString xTheta, yRadius;
193  if ((xInitialValue != 0) &&
194  (yInitialValue != 0)) {
195 
196  FormatCoordsUnits format;
197  format.unformattedToFormatted (*xInitialValue,
198  *yInitialValue,
199  m_modelCoords,
200  m_modelMainWindow,
201  xTheta,
202  yRadius,
203  transformation);
204  }
205 
206  if (isX) {
207  m_editGraphX->setText (xTheta);
208  } else {
209  m_editGraphX->setText ("");
210  }
211 
212  if (isY) {
213  m_editGraphY->setText (yRadius);
214  } else {
215  m_editGraphY->setText ("");
216  }
217 }
218 
219 bool DlgEditPoint::isCartesian () const
220 {
221  return (m_modelCoords.coordsType() == COORDS_TYPE_CARTESIAN);
222 }
223 
224 QChar DlgEditPoint::nameXTheta () const
225 {
226  return (isCartesian () ? QChar ('X') : THETA);
227 }
228 
229 QChar DlgEditPoint::nameYRadius () const
230 {
231  return (isCartesian () ? QChar ('Y') : QChar ('R'));
232 }
233 
234 QPointF DlgEditPoint::posGraph (bool &isXOnly) const
235 {
236  double xTheta, yRadius;
237 
238  FormatCoordsUnits format;
239 
240  format.formattedToUnformatted (m_editGraphX->text(),
241  m_editGraphY->text(),
242  m_modelCoords,
243  m_modelMainWindow,
244  xTheta,
245  yRadius);
246 
247  // If yRadius value is empty then this is the xTheta value only
248  isXOnly = m_editGraphY->text().isEmpty();
249 
250  return QPointF (xTheta,
251  yRadius);
252 }
253 
254 void DlgEditPoint::slotTextChanged (const QString &)
255 {
256  updateControls ();
257 }
258 
259 QString DlgEditPoint::unitsType (bool isXTheta) const
260 {
261  if (isCartesian ()) {
262  if (isXTheta) {
263  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsX());
264  } else {
265  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsY());
266  }
267  } else {
268  if (isXTheta) {
269  return coordUnitsPolarThetaToBriefType (m_modelCoords.coordUnitsTheta());
270  } else {
271  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsRadius());
272  }
273  }
274 }
275 
276 void DlgEditPoint::updateControls ()
277 {
278  QString textX = m_editGraphX->text();
279  QString textY = m_editGraphY->text();
280 
281  int posX, posY;
282 
283  if (m_documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_4) {
284 
285  bool gotX = (!textX.isEmpty() &&
286  (m_validatorGraphX->validate(textX, posX) == QValidator::Acceptable));
287  bool gotY = (!textY.isEmpty() &&
288  (m_validatorGraphY->validate(textY, posY) == QValidator::Acceptable));
289 
290  // Check for not empty in one coordinate and for valid number in the other coordinate
291  m_btnOk->setEnabled ((textX.isEmpty() && gotY) ||
292  (textY.isEmpty() && gotX));
293 
294  } else {
295 
296  // Check for not empty (which allows single minus sign) and for valid number (which prevents single minus sign)
297  m_btnOk->setEnabled (!textX.isEmpty () &&
298  !textY.isEmpty () &&
299  (m_validatorGraphX->validate(textX, posX) == QValidator::Acceptable) &&
300  (m_validatorGraphY->validate(textY, posY) == QValidator::Acceptable));
301 
302  }
303 }
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.
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.
QPointF posGraph(bool &isXOnly) const
Return the graph coordinates position specified by the user. Only applies if dialog was accepted...
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 signalSetOverrideCursor(QCursor)
Send a signal to trigger the setting of the override cursor.
DlgEditPoint(MainWindow &mainWindow, DigitizeStateAbstractBase &digitizeState, const DocumentModelCoords &modelCoords, const MainWindowModel &modelMainWindow, const QCursor &cursorShape, const Transformation &transformation, DocumentAxesPointsRequired documentAxesPointsRequired, bool isXOnly=false, const double *xInitialValue=0, const double *yInitialValue=0)
Constructor for existing point which already has graph coordinates (which may be changed using this d...
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.
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...
void removeOverrideCursor()
Remove the override cursor if it is in use. This is called after a leave event, and prior to displayi...
Base class for all digitizing states. This serves as an interface to DigitizeStateContext.
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:77
CoordUnitsPolarTheta coordUnitsTheta() const
Get method for theta unit.