22 #include <QDoubleValidator>
23 #include <QGraphicsRectItem>
24 #include <QGridLayout>
26 #include <QGraphicsScene>
31 #include <QRadioButton>
32 #include <QStackedWidget>
33 #include <QVBoxLayout>
65 const double PI = 3.1415926535;
79 m_btnCartesian (nullptr),
81 m_validatorOriginRadius (nullptr),
84 m_scenePreview (nullptr),
85 m_viewPreview (nullptr),
86 m_modelCoordsBefore (nullptr),
87 m_modelCoordsAfter (nullptr)
101 void DlgSettingsCoords::annotateAngles (
const QFont &defaultFont) {
104 for (
int direction = 0; direction < 4; direction++) {
107 CoordUnitsPolarTheta thetaUnits = static_cast<CoordUnitsPolarTheta> (m_cmbXThetaUnits->currentData().toInt());
109 switch (thetaUnits) {
113 angle = QString::number (90.0 * direction);
117 angle = QString::number (90.0 * direction);
118 if (direction == 1) {
120 }
else if (direction == 3) {
126 angle = QString::number (100.0 * direction);
131 static QString radiansUnits [] = {
"0",
"PI / 2",
"PI",
"3 * PI / 2"};
133 angle = radiansUnits [direction];
139 static QString turnsUnits [] = {
"0",
"1 / 4",
"1 / 2",
"3 / 4"};
141 angle = turnsUnits [direction];
149 QGraphicsTextItem *textAngle = m_scenePreview->addText (angle);
150 textAngle->setFont (QFont (defaultFont.defaultFamily(),
FONT_SIZE));
158 x =
XCENTER - textAngle->boundingRect().width () / 2.0;
177 textAngle->setPos (x, y);
181 void DlgSettingsCoords::annotateRadiusAtOrigin(
const QFont &defaultFont) {
183 QGraphicsTextItem *textRadius = m_scenePreview->addText (m_editOriginRadius->text());
184 textRadius->setFont (QFont (defaultFont.defaultFamily(),
FONT_SIZE));
185 textRadius->setPos (
XCENTER - textRadius->boundingRect().width () / 2.0,
189 void DlgSettingsCoords::boundingRectGraph (
CmdMediator &cmdMediator,
191 QPointF &boundingRectGraphMin,
192 QPointF &boundingRectGraphMax)
const
197 Functor2wRet<const QString &, const Point&, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
210 boundingRectGraphMin = ftor.boundingRectGraphMin (isEmpty);
211 boundingRectGraphMax = ftor.boundingRectGraphMax (isEmpty);
214 void DlgSettingsCoords::createDateTime (QGridLayout *layout,
219 QLabel *label =
new QLabel(QString (
"%1:").arg (tr (
"Date/Time")));
220 layout->addWidget (label, row, 1);
222 QWidget *widgetCombos =
new QWidget;
223 layout->addWidget (widgetCombos, row++, 2);
224 QHBoxLayout *layoutCombos =
new QHBoxLayout;
225 widgetCombos->setLayout (layoutCombos);
228 m_cmbDate =
new QComboBox;
229 m_cmbDate->setWhatsThis (tr (
"Date format to be used for date values, and date portion of mixed date/time values, "
230 "during input and output.\n\n"
231 "Setting the format to an empty value results in just the time portion appearing in output."));
232 connect (m_cmbDate, SIGNAL (activated (
const QString &)),
this, SLOT (slotDate (
const QString &)));
233 layoutCombos->addWidget (m_cmbDate);
235 m_cmbTime =
new QComboBox;
236 m_cmbTime->setWhatsThis (tr (
"Time format to be used for time values, and time portion of mixed date/time values, "
237 "during input and output.\n\n"
238 "Setting the format to an empty value results in just the date portion appearing in output."));
239 connect (m_cmbTime, SIGNAL (activated (
const QString &)),
this, SLOT (slotTime (
const QString &)));
240 layoutCombos->addWidget (m_cmbTime);
243 void DlgSettingsCoords::createGroupCoordsType (QGridLayout *layout,
248 m_boxCoordsType =
new QGroupBox(tr (
"Coordinates Types"));
249 layout->addWidget (m_boxCoordsType, row++, 1, 1, 2);
251 QVBoxLayout *layoutGroup =
new QVBoxLayout (m_boxCoordsType);
253 QString polarButtonText = QString(tr (
"Polar") +
" (") +
THETA + QString(
", " + tr (
"R") +
")");
255 m_btnCartesian =
new QRadioButton (tr (
"Cartesian (X, Y)"), m_boxCoordsType);
256 m_btnCartesian->setWhatsThis (QString(tr(
"Select cartesian coordinates.\n\n"
257 "The X and Y coordinates will be used")));
258 connect (m_btnCartesian, SIGNAL (toggled(
bool)),
this, SLOT (slotCartesianPolar (
bool)));
259 layoutGroup->addWidget (m_btnCartesian);
261 m_btnPolar =
new QRadioButton (polarButtonText, m_boxCoordsType);
262 m_btnPolar->setWhatsThis (QString(tr(
"Select polar coordinates.\n\n"
263 "The Theta and R coordinates will be used.\n\n"
264 "Polar coordinates are not allowed with log scale for Theta")));
265 connect (m_btnPolar, SIGNAL (toggled(
bool)),
this, SLOT (slotCartesianPolar (
bool)));
266 layoutGroup->addWidget (m_btnPolar);
269 void DlgSettingsCoords::createGroupXTheta (QGridLayout *layout,
275 layout->addWidget (m_boxXTheta, row, 1, 1, 1);
277 QGridLayout *layoutXTheta =
new QGridLayout (m_boxXTheta);
278 m_boxXTheta->setLayout (layoutXTheta);
281 QLabel *labelScale =
new QLabel (QString (
"%1:").arg (tr (
"Scale")));
282 layoutXTheta->addWidget (labelScale, rowGroup++,
COLUMN_0);
284 m_xThetaLinear =
new QRadioButton (tr (
"Linear"), m_boxXTheta);
285 m_xThetaLinear->setWhatsThis (QString(tr(
"Specifies linear scale for the X or Theta coordinate")));
286 connect (m_xThetaLinear, SIGNAL (released ()),
this, SLOT (slotXThetaLinear()));
287 layoutXTheta->addWidget (m_xThetaLinear, rowGroup++,
COLUMN_0);
289 m_xThetaLog =
new QRadioButton (tr (
"Log"), m_boxXTheta);
290 m_xThetaLog->setWhatsThis (QString(tr(
"Specifies logarithmic scale for the X or Theta coordinate.\n\n"
291 "Log scale is not allowed if there are negative coordinates.\n\n"
292 "Log scale is not allowed for the Theta coordinate.")));
293 connect (m_xThetaLog, SIGNAL (released ()),
this, SLOT (slotXThetaLog()));
294 layoutXTheta->addWidget (m_xThetaLog, rowGroup++,
COLUMN_0);
296 QLabel *labelThetaUnits =
new QLabel(QString (
"%1:").arg (tr (
"Units")));
297 layoutXTheta->addWidget (labelThetaUnits, rowGroup++,
COLUMN_0);
299 m_cmbXThetaUnits =
new QComboBox;
300 connect (m_cmbXThetaUnits, SIGNAL (activated (
const QString &)),
this, SLOT (slotUnitsXTheta(
const QString &)));
301 layoutXTheta->addWidget (m_cmbXThetaUnits, rowGroup++,
COLUMN_0, 1, 2);
304 void DlgSettingsCoords::createGroupYRadius (QGridLayout *layout,
310 layout->addWidget (m_boxYRadius, row++, 2, 1, 1);
312 QGridLayout *layoutYRadius =
new QGridLayout (m_boxYRadius);
313 m_boxYRadius->setLayout (layoutYRadius);
316 QLabel *labelScale =
new QLabel (QString (
"%1:").arg (tr (
"Scale")));
317 layoutYRadius->addWidget (labelScale, rowGroup++,
COLUMN_0);
319 m_yRadiusLinear =
new QRadioButton (tr (
"Linear"), m_boxYRadius);
320 m_yRadiusLinear->setWhatsThis (QString(tr(
"Specifies linear scale for the Y or R coordinate")));
321 connect (m_yRadiusLinear, SIGNAL(released()),
this, SLOT (slotYRadiusLinear()));
322 layoutYRadius->addWidget (m_yRadiusLinear, rowGroup,
COLUMN_0);
324 QLabel *labelOriginRadius =
new QLabel(QString (
"%1:").arg (tr (
"Origin radius value")));
325 layoutYRadius->addWidget (labelOriginRadius, rowGroup++,
COLUMN_1);
327 m_yRadiusLog =
new QRadioButton (tr (
"Log"), m_boxYRadius);
328 m_yRadiusLog->setWhatsThis (QString(tr(
"Specifies logarithmic scale for the Y or R coordinate\n\n"
329 "Log scale is not allowed if there are negative coordinates.")));
330 connect (m_yRadiusLog, SIGNAL(released ()),
this, SLOT (slotYRadiusLog ()));
331 layoutYRadius->addWidget (m_yRadiusLog, rowGroup,
COLUMN_0);
333 m_editOriginRadius =
new QLineEdit (m_boxYRadius);
335 m_editOriginRadius->setWhatsThis (QString(tr(
"Specify radius value at origin.\n\n"
336 "Normally the radius at the origin is 0, but a nonzero value may be applied in other cases "
337 "(like when the radial units are decibels).")));
338 connect (m_editOriginRadius, SIGNAL (textChanged (
const QString &)),
this, SLOT (slotPolarOriginRadius(
const QString &)));
339 layoutYRadius->addWidget (m_editOriginRadius, rowGroup++,
COLUMN_1);
341 QLabel *labelUnits =
new QLabel(QString (
"%1:").arg (tr (
"Units")));
342 layoutYRadius->addWidget (labelUnits, rowGroup++,
COLUMN_0);
344 m_cmbYRadiusUnits =
new QComboBox;
345 connect (m_cmbYRadiusUnits, SIGNAL (activated (
const QString &)),
this, SLOT (slotUnitsYRadius(
const QString &)));
346 layoutYRadius->addWidget (m_cmbYRadiusUnits, rowGroup++,
COLUMN_0, 1, 2);
353 void DlgSettingsCoords::createPreview (QGridLayout *layout,
358 QLabel *labelPreview =
new QLabel (tr (
"Preview"));
359 layout->addWidget (labelPreview, row++, 0, 1, 4);
361 m_scenePreview =
new QGraphicsScene (
this);
365 m_viewPreview->setWhatsThis (tr (
"Preview window that shows how current settings affect the coordinate system."));
366 m_viewPreview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
367 m_viewPreview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
370 layout->addWidget (m_viewPreview, row++, 0, 1, 4);
377 QWidget *subPanel =
new QWidget ();
379 QGridLayout *layout =
new QGridLayout (subPanel);
380 subPanel->setLayout (layout);
382 layout->setColumnStretch(0, 1);
383 layout->setColumnStretch(1, 0);
384 layout->setColumnStretch(2, 0);
385 layout->setColumnStretch(3, 1);
388 createGroupCoordsType(layout, row);
389 createGroupXTheta (layout, row);
390 createGroupYRadius (layout, row);
391 createDateTime (layout, row);
392 createPreview (layout, row);
397 void DlgSettingsCoords::drawCartesianLinearX ()
406 line->setPen(QPen (QBrush ((isHighlighted ? Qt::gray : Qt::lightGray)),
408 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
411 line->setPen(QPen (QBrush (Qt::black),
418 void DlgSettingsCoords::drawCartesianLinearY ()
427 line->setPen(QPen (QBrush (isHighlighted ? Qt::gray : Qt::lightGray),
429 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
432 line->setPen(QPen (QBrush (Qt::black),
439 void DlgSettingsCoords::drawCartesianLogX ()
450 line->setPen(QPen (QBrush (isHighlighted ? Qt::gray : Qt::lightGray),
452 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
455 line->setPen(QPen (QBrush (Qt::black),
462 void DlgSettingsCoords::drawCartesianLogY ()
473 line->setPen(QPen (QBrush (isHighlighted ? Qt::gray : Qt::lightGray),
475 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
478 line->setPen(QPen (QBrush (Qt::black),
485 void DlgSettingsCoords::drawPolarLinearRadius ()
491 QGraphicsEllipseItem *line = m_scenePreview->addEllipse (
XCENTER - radius,
496 line->setPen(QPen (QBrush (isHighlighted ? Qt::gray : Qt::lightGray),
498 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
502 void DlgSettingsCoords::drawPolarLogRadius ()
510 QGraphicsEllipseItem *line = m_scenePreview->addEllipse (
XCENTER - radius,
515 line->setPen(QPen (QBrush (isHighlighted ? Qt::gray : Qt::lightGray),
517 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
521 void DlgSettingsCoords::drawPolarTheta ()
532 line->setPen(QPen (QBrush (isHighlighted ? Qt::gray : Qt::lightGray),
534 (isHighlighted ? Qt::SolidLine : Qt::DashLine)));
537 line->setPen(QPen (QBrush (Qt::black),
550 *m_modelCoordsBefore,
551 *m_modelCoordsAfter);
565 QPointF boundingRectGraphMin, boundingRectGraphMax;
568 boundingRectGraphMin,
569 boundingRectGraphMax);
570 bool xThetaGoesNegative = !isEmpty && (boundingRectGraphMin.x() <= 0);
571 bool yRGoesNegative = !isEmpty && (boundingRectGraphMin.y() <= 0);
572 m_xThetaLinear->setEnabled (!xThetaGoesNegative);
573 m_xThetaLog->setEnabled (!xThetaGoesNegative);
574 m_yRadiusLinear->setEnabled (!yRGoesNegative);
575 m_yRadiusLog->setEnabled (!yRGoesNegative);
578 delete m_modelCoordsBefore;
579 delete m_modelCoordsAfter;
587 m_validatorOriginRadius = dlgValidatorFactory.createWithNonPolar (m_modelCoordsAfter->
coordScaleYRadius(),
592 m_editOriginRadius->setValidator (m_validatorOriginRadius);
593 m_editOriginRadius->setText (QString::number (m_modelCoordsAfter->
originRadius ()));
596 m_btnCartesian->setChecked (
true);
598 m_btnPolar->setChecked (
true);
615 void DlgSettingsCoords::loadComboBoxDate()
632 int index = m_cmbDate->findData (QVariant (m_modelCoordsAfter->
coordUnitsDate()));
633 m_cmbDate->setCurrentIndex (index);
636 void DlgSettingsCoords::loadComboBoxTime()
651 int index = m_cmbTime->findData (QVariant (m_modelCoordsAfter->
coordUnitsTime()));
652 m_cmbTime->setCurrentIndex (index);
655 void DlgSettingsCoords::loadComboBoxUnitsNonPolar (QComboBox &cmb,
673 cmb.setWhatsThis (QString (tr (
"Numbers have the simplest and most general format.\n\n"
674 "Date and time values have date and/or time components.\n\n"
675 "Degrees Minutes Seconds (DDD MM SS.S) format uses two integer number for degrees and minutes, and a real number for "
676 "seconds. There are 60 seconds per minute. During input, spaces must be inserted between the three numbers.")));
678 int index = cmb.findData (coordUnits);
679 cmb.setCurrentIndex (index);
682 void DlgSettingsCoords::loadComboBoxUnitsPolar (QComboBox &cmb,
706 cmb.setWhatsThis (QString (tr (
"Degrees (DDD.DDDDD) format uses a single real number. One complete revolution is 360 degrees.\n\n"
707 "Degrees Minutes (DDD MM.MMM) format uses one integer number for degrees, and a real number for minutes. There are "
708 "60 minutes per degree. During input, a space must be inserted between the two numbers.\n\n"
709 "Degrees Minutes Seconds (DDD MM SS.S) format uses two integer number for degrees and minutes, and a real number for "
710 "seconds. There are 60 seconds per minute. During input, spaces must be inserted between the three numbers.\n\n"
711 "Gradians format uses a single real number. One complete revolution is 400 gradians.\n\n"
712 "Radians format uses a single real number. One complete revolution is 2*pi radians.\n\n"
713 "Turns format uses a single real number. One complete revolution is one turn.")));
715 int index = cmb.findData (coordUnits);
716 cmb.setCurrentIndex (index);
719 void DlgSettingsCoords::resetSceneRectangle ()
726 QGraphicsRectItem *itemPerimeter =
new QGraphicsRectItem(rect);
727 itemPerimeter->setVisible(
false);
728 m_scenePreview->addItem (itemPerimeter);
729 m_viewPreview->centerOn (QPointF (0.0, 0.0));
739 void DlgSettingsCoords::slotCartesianPolar (
bool)
743 if (m_btnCartesian->isChecked ()) {
753 void DlgSettingsCoords::slotDate(
const QString &)
757 CoordUnitsDate coordUnits = static_cast<CoordUnitsDate> (m_cmbDate->currentData ().toInt());
763 void DlgSettingsCoords::slotPolarOriginRadius(
const QString &)
767 QString numberText = m_editOriginRadius->text();
774 void DlgSettingsCoords::slotTime(
const QString &)
778 CoordUnitsTime coordUnits = static_cast<CoordUnitsTime> (m_cmbTime->currentData ().toInt());
784 void DlgSettingsCoords::slotUnitsXTheta(
const QString &)
789 CoordUnitsNonPolarTheta coordUnits = static_cast<CoordUnitsNonPolarTheta> (m_cmbXThetaUnits->currentData ().toInt ());
792 CoordUnitsPolarTheta coordUnits = static_cast<CoordUnitsPolarTheta> (m_cmbXThetaUnits->currentData ().toInt ());
799 void DlgSettingsCoords::slotUnitsYRadius(
const QString &)
803 CoordUnitsNonPolarTheta coordUnits = static_cast<CoordUnitsNonPolarTheta> (m_cmbYRadiusUnits->currentData ().toInt ());
813 void DlgSettingsCoords::slotXThetaLinear()
822 void DlgSettingsCoords::slotXThetaLog()
831 void DlgSettingsCoords::slotYRadiusLinear()
835 delete m_validatorOriginRadius;
843 m_editOriginRadius->setValidator (m_validatorOriginRadius);
850 void DlgSettingsCoords::slotYRadiusLog()
854 delete m_validatorOriginRadius;
862 m_editOriginRadius->setValidator (m_validatorOriginRadius);
869 void DlgSettingsCoords::updateControls ()
873 QString textOriginRadius = m_editOriginRadius->text();
874 int posOriginRadius = 0;
876 bool goodOriginRadius =
true;
877 if (m_editOriginRadius->isEnabled ()) {
880 goodOriginRadius = (m_validatorOriginRadius->
validate (textOriginRadius,
881 posOriginRadius) == QValidator::Acceptable);
886 m_boxCoordsType->setEnabled (!m_xThetaLog->isChecked ());
888 m_xThetaLinear->setEnabled (!m_btnPolar->isChecked ());
889 m_xThetaLog->setEnabled (!m_btnPolar->isChecked ());
890 if (m_btnCartesian->isChecked()) {
891 m_yRadiusLinear->setEnabled (
true);
892 m_yRadiusLog->setEnabled (
true);
902 int posOriginRadiusOther;
903 bool goodOriginRadiusOther = (dlg->
validate (textOriginRadius, posOriginRadiusOther) == QValidator::Acceptable);
907 m_yRadiusLinear->setEnabled (goodOriginRadius && goodOriginRadiusOther);
908 m_yRadiusLog->setEnabled (goodOriginRadius && goodOriginRadiusOther);
910 m_editOriginRadius->setEnabled (m_btnPolar->isChecked ());
912 QString captionXTheta = (m_btnCartesian->isChecked () ?
914 THETA) + QString (
" %1")
915 .arg (tr (
"Coordinates"));
916 QString captionYRadius = (m_btnCartesian->isChecked () ?
918 QString (tr (
"R"))) + QString (
" %1")
919 .arg (tr (
"Coordinates"));
921 if (m_boxXTheta->title() != captionXTheta) {
922 m_boxXTheta->setTitle (captionXTheta);
925 if (m_boxYRadius->title () != captionYRadius) {
926 m_boxYRadius->setTitle (captionYRadius);
930 if (m_btnCartesian->isChecked()) {
936 m_cmbDate->setEnabled (enableDateTime);
937 m_cmbTime->setEnabled (enableDateTime);
940 <<
" textOriginRadius=" << textOriginRadius.toLatin1().data()
941 <<
" goodOriginRadius=" << (goodOriginRadius ?
"true" :
"false")
942 <<
" originRadius=" << posOriginRadius
943 <<
" btnPolarChecked=" << (m_btnPolar->isChecked() ?
"true" :
"false")
944 <<
" enableDateTime=" << (enableDateTime ?
"true" :
"false");
947 void DlgSettingsCoords::updateCoordUnits()
950 if (m_btnCartesian->isChecked()) {
951 loadComboBoxUnitsNonPolar (*m_cmbXThetaUnits,
953 loadComboBoxUnitsNonPolar (*m_cmbYRadiusUnits,
956 loadComboBoxUnitsPolar (*m_cmbXThetaUnits,
958 loadComboBoxUnitsNonPolar (*m_cmbYRadiusUnits,
963 void DlgSettingsCoords::updatePreview()
965 m_scenePreview->clear();
973 if (m_btnCartesian->isChecked()) {
976 if (m_xThetaLinear->isChecked()) {
977 drawCartesianLinearX ();
979 drawCartesianLogX ();
982 if (m_yRadiusLinear->isChecked()) {
983 drawCartesianLinearY ();
985 drawCartesianLogY ();
992 if (m_yRadiusLinear->isChecked()) {
993 drawPolarLinearRadius ();
995 drawPolarLogRadius ();
999 annotateRadiusAtOrigin (defaultFont);
1000 annotateAngles (defaultFont);
1003 resetSceneRectangle();