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 "CmdMediator.h"
8 #include "CmdSettingsDigitizeCurve.h"
9 #include "CursorFactory.h"
10 #include "CursorSize.h"
11 #include "DlgSettingsDigitizeCurve.h"
12 #include "DocumentModelDigitizeCurve.h"
13 #include "EngaugeAssert.h"
14 #include "Logger.h"
15 #include "MainWindow.h"
16 #include "PointStyle.h"
17 #include <QCheckBox>
18 #include <QComboBox>
19 #include <QGraphicsPixmapItem>
20 #include <QGridLayout>
21 #include <QGraphicsScene>
22 #include <QGroupBox>
23 #include <QLabel>
24 #include <qmath.h>
25 #include <QRadioButton>
26 #include <QSpinBox>
27 #include "Segment.h"
28 #include "SegmentFactory.h"
29 #include "ViewPreview.h"
30 
31 const int IMAGE_WIDTH = 100;
32 const int IMAGE_HEIGHT = 100;
33 const int MINIMUM_HEIGHT = 450;
34 const int INNER_RADIUS_MAX = 64;
35 const int INNER_RADIUS_MIN = 0;
36 const int LINE_LENGTH_MIN = 2; // Min length of one line in the cursor, in pixels
37 const int LINE_WIDTH_MAX = 32;
38 const int LINE_WIDTH_MIN = 1;
39 
DlgSettingsDigitizeCurve(MainWindow & mainWindow)40 DlgSettingsDigitizeCurve::DlgSettingsDigitizeCurve(MainWindow &mainWindow) :
41   DlgSettingsAbstractBase (tr ("Digitize Curve"),
42                            "DlgSettingsDigitizeCurve",
43                            mainWindow),
44   m_scenePreview (nullptr),
45   m_viewPreview (nullptr),
46   m_modelDigitizeCurveBefore (nullptr),
47   m_modelDigitizeCurveAfter (nullptr)
48 {
49   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::DlgSettingsDigitizeCurve";
50 
51   QWidget *subPanel = createSubPanel ();
52   finishPanel (subPanel);
53 }
54 
~DlgSettingsDigitizeCurve()55 DlgSettingsDigitizeCurve::~DlgSettingsDigitizeCurve()
56 {
57   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::~DlgSettingsDigitizeCurve";
58 }
59 
createControls(QGridLayout * layout,int & row)60 void DlgSettingsDigitizeCurve::createControls (QGridLayout *layout,
61                                                int &row)
62 {
63   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::createControls";
64 
65   m_boxCursor = new QGroupBox (tr ("Cursor"));
66   layout->addWidget (m_boxCursor, row++, 1, 1, 2);
67 
68   // Layout inside cursor group box
69   QGridLayout *layoutCursor = new QGridLayout;
70   m_boxCursor->setLayout (layoutCursor);
71   int rowCursor = 0;
72 
73   QLabel *labelCursorType = new QLabel(QString ("%1:").arg (tr ("Type")));
74   layoutCursor->addWidget (labelCursorType, rowCursor, 0);
75 
76   m_btnStandard = new QRadioButton (tr ("Standard cross"));
77   m_btnStandard->setWhatsThis (tr ("Selects the standard cross cursor"));
78   layoutCursor->addWidget (m_btnStandard, rowCursor++, 1);
79   connect (m_btnStandard, SIGNAL (toggled (bool)), this, SLOT (slotCursorStandard(bool)));
80 
81   m_btnCustom = new QRadioButton (tr ("Custom cross"));
82   m_btnCustom->setWhatsThis (tr ("Selects a custom cursor based on the settings selected below"));
83   layoutCursor->addWidget (m_btnCustom, rowCursor++, 1);
84   connect (m_btnCustom, SIGNAL (toggled (bool)), this, SLOT (slotCursorCustom(bool)));
85 
86   QLabel *labelSize = new QLabel(QString ("%1:").arg (tr ("Size (pixels)")));
87   layoutCursor->addWidget (labelSize, rowCursor, 0);
88 
89   m_cmbSize = new QComboBox;
90   m_cmbSize->addItem (QString::number (CursorSizeToPixels (CURSOR_SIZE_16)), QVariant (CURSOR_SIZE_16));
91   m_cmbSize->addItem (QString::number (CursorSizeToPixels (CURSOR_SIZE_32)), QVariant (CURSOR_SIZE_32));
92   m_cmbSize->addItem (QString::number (CursorSizeToPixels (CURSOR_SIZE_48)), QVariant (CURSOR_SIZE_48));
93   m_cmbSize->addItem (QString::number (CursorSizeToPixels (CURSOR_SIZE_64)), QVariant (CURSOR_SIZE_64));
94   ENGAUGE_ASSERT (m_cmbSize->count() == NUM_CURSOR_SIZES);
95   m_cmbSize->setWhatsThis (tr ("Horizontal and vertical size of the cursor in pixels"));
96   layoutCursor->addWidget (m_cmbSize, rowCursor++, 1);
97   connect (m_cmbSize, SIGNAL (currentIndexChanged (const QString &)), this, SLOT (slotCursorSize (const QString &)));
98 
99   QLabel *labelInnerRadius = new QLabel(QString ("%1:").arg (tr ("Inner radius (pixels)")));
100   layoutCursor->addWidget (labelInnerRadius, rowCursor, 0);
101 
102   m_spinInnerRadius = new QSpinBox;
103   m_spinInnerRadius->setRange (INNER_RADIUS_MIN, INNER_RADIUS_MAX);
104   m_spinInnerRadius->setWhatsThis (tr ("Radius of circle at the center of the cursor that will remain empty"));
105   layoutCursor->addWidget (m_spinInnerRadius, rowCursor++, 1);
106   connect (m_spinInnerRadius, SIGNAL (valueChanged(const QString &)), this, SLOT (slotCursorInnerRadius (const QString &)));
107 
108   QLabel *labelLineWidth = new QLabel(QString ("%1:").arg (tr ("Line width (pixels)")));
109   layoutCursor->addWidget (labelLineWidth, rowCursor, 0);
110 
111   m_spinLineWidth = new QSpinBox;
112   m_spinLineWidth->setRange (LINE_WIDTH_MIN, LINE_WIDTH_MAX);
113   m_spinLineWidth->setWhatsThis (tr ("Width of each arm of the cross of the cursor"));
114   layoutCursor->addWidget (m_spinLineWidth, rowCursor++, 1);
115   connect (m_spinLineWidth, SIGNAL (valueChanged(const QString &)), this, SLOT (slotCursorLineWidth (const QString &)));
116 }
117 
createOptionalSaveDefault(QHBoxLayout *)118 void DlgSettingsDigitizeCurve::createOptionalSaveDefault (QHBoxLayout * /* layout */)
119 {
120 }
121 
createPreview(QGridLayout * layout,int & row)122 void DlgSettingsDigitizeCurve::createPreview (QGridLayout *layout,
123                                          int &row)
124 {
125   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::createPreview";
126 
127   QLabel *labelPreview = new QLabel (tr ("Preview"));
128   layout->addWidget (labelPreview, row++, 0, 1, 4);
129 
130   m_scenePreview = new QGraphicsScene (this);
131   m_scenePreview->setSceneRect(0,
132                                0,
133                                IMAGE_WIDTH,
134                                IMAGE_HEIGHT);
135 
136   m_viewPreview = new ViewPreview (m_scenePreview,
137                                    ViewPreview::VIEW_ASPECT_RATIO_VARIABLE,
138                                    this);
139   m_viewPreview->setWhatsThis (tr ("Preview window showing the currently selected cursor.\n\n"
140                                    "Drag the cursor over this area to see the effects of the current settings on the cursor shape."));
141   m_viewPreview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
142   m_viewPreview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
143   m_viewPreview->setMinimumHeight (MINIMUM_PREVIEW_HEIGHT);
144 
145   layout->addWidget (m_viewPreview, row++, 0, 1, 4);
146 }
147 
createSubPanel()148 QWidget *DlgSettingsDigitizeCurve::createSubPanel ()
149 {
150   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::createSubPanel";
151 
152   QWidget *subPanel = new QWidget ();
153   QGridLayout *layout = new QGridLayout (subPanel);
154   subPanel->setLayout (layout);
155 
156   layout->setColumnStretch (0, 1); // Empty first column
157   layout->setColumnStretch (1, 0); // Labels
158   layout->setColumnStretch (2, 0); // User controls
159   layout->setColumnStretch (3, 1); // Empty last column
160 
161   int row = 0;
162   createControls(layout, row);
163   createPreview (layout, row);
164 
165   return subPanel;
166 }
167 
handleOk()168 void DlgSettingsDigitizeCurve::handleOk ()
169 {
170   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::handleOk";
171 
172   CmdSettingsDigitizeCurve *cmd = new CmdSettingsDigitizeCurve (mainWindow (),
173                                                                 cmdMediator ().document(),
174                                                                 *m_modelDigitizeCurveBefore,
175                                                                 *m_modelDigitizeCurveAfter);
176   cmdMediator ().push (cmd);
177 
178   hide ();
179 }
180 
load(CmdMediator & cmdMediator)181 void DlgSettingsDigitizeCurve::load (CmdMediator &cmdMediator)
182 {
183   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::load";
184 
185   setCmdMediator (cmdMediator);
186 
187   // Flush old data
188   delete m_modelDigitizeCurveBefore;
189   delete m_modelDigitizeCurveAfter;
190 
191   // Save new data
192   m_modelDigitizeCurveBefore = new DocumentModelDigitizeCurve (cmdMediator.document());
193   m_modelDigitizeCurveAfter = new DocumentModelDigitizeCurve (cmdMediator.document());
194 
195   // Sanity checks. Incoming defaults must be acceptable to the local limits
196   ENGAUGE_ASSERT (INNER_RADIUS_MIN <= m_modelDigitizeCurveAfter->cursorInnerRadius ());
197   ENGAUGE_ASSERT (INNER_RADIUS_MAX >= m_modelDigitizeCurveAfter->cursorInnerRadius ());
198   ENGAUGE_ASSERT (LINE_WIDTH_MIN <= m_modelDigitizeCurveAfter->cursorLineWidth ());
199   ENGAUGE_ASSERT (LINE_WIDTH_MAX >= m_modelDigitizeCurveAfter->cursorLineWidth ());
200 
201   // Populate controls
202   m_btnStandard->setChecked (m_modelDigitizeCurveAfter->cursorStandardCross());
203   m_btnCustom->setChecked (!m_modelDigitizeCurveAfter->cursorStandardCross());
204   m_spinInnerRadius->setValue (m_modelDigitizeCurveAfter->cursorInnerRadius());
205   int index = m_cmbSize->findData (QVariant (m_modelDigitizeCurveAfter->cursorSize()));
206   m_cmbSize->setCurrentIndex (index);
207   m_spinLineWidth->setValue (m_modelDigitizeCurveAfter->cursorLineWidth());
208 
209   updateControls();
210   enableOk (false); // Disable Ok button since there not yet any changes
211   updatePreview();
212 }
213 
setSmallDialogs(bool smallDialogs)214 void DlgSettingsDigitizeCurve::setSmallDialogs(bool smallDialogs)
215 {
216   if (!smallDialogs) {
217     setMinimumHeight (MINIMUM_HEIGHT);
218   }
219 }
220 
slotCursorCustom(bool)221 void DlgSettingsDigitizeCurve::slotCursorCustom (bool)
222 {
223   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::slotCursorCustom";
224 
225   m_modelDigitizeCurveAfter->setCursorStandardCross(false);
226   updateControls();
227   updatePreview();
228 }
229 
slotCursorInnerRadius(const QString &)230 void DlgSettingsDigitizeCurve::slotCursorInnerRadius (const QString &)
231 {
232   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::slotCursorInnerRadius";
233 
234   m_modelDigitizeCurveAfter->setCursorInnerRadius (m_spinInnerRadius->value());
235   updateControls();
236   updatePreview();
237 }
238 
slotCursorLineWidth(const QString &)239 void DlgSettingsDigitizeCurve::slotCursorLineWidth (const QString &)
240 {
241   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::slotCursorLineWidth";
242 
243   m_modelDigitizeCurveAfter->setCursorLineWidth (m_spinLineWidth->value());
244   updateControls();
245   updatePreview();
246 }
247 
slotCursorSize(const QString &)248 void DlgSettingsDigitizeCurve::slotCursorSize (const QString &)
249 {
250   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::slotCursorSize";
251 
252   m_modelDigitizeCurveAfter->setCursorSize (static_cast<CursorSize> (m_cmbSize->currentData().toInt()));
253   updateControls();
254   updatePreview();
255 }
256 
slotCursorStandard(bool)257 void DlgSettingsDigitizeCurve::slotCursorStandard (bool)
258 {
259   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::slotCursorStandard";
260 
261   m_modelDigitizeCurveAfter->setCursorStandardCross(true);
262   updateControls();
263   updatePreview();
264 }
265 
updateControls()266 void DlgSettingsDigitizeCurve::updateControls()
267 {
268   // Cursor has to fit into current extent
269   bool isGoodState = 2 * (m_modelDigitizeCurveAfter->cursorInnerRadius() + LINE_LENGTH_MIN) <=
270                      CursorSizeToPixels (m_modelDigitizeCurveAfter->cursorSize());
271   enableOk (isGoodState);
272 
273   m_spinInnerRadius->setEnabled (m_btnCustom->isChecked());
274   m_cmbSize->setEnabled (m_btnCustom->isChecked());
275   m_spinLineWidth->setEnabled (m_btnCustom->isChecked());
276 }
277 
updatePreview()278 void DlgSettingsDigitizeCurve::updatePreview()
279 {
280   LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsDigitizeCurve::updatePreview";
281 
282   CursorFactory cursorFactory;
283   QCursor cursor = cursorFactory.generate (*m_modelDigitizeCurveAfter);
284   m_viewPreview->setCursor (cursor);
285 }
286