1 /******************************************************************************************************
2  * (C) 2019 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 "Document.h"
9 #include "DocumentModelCoords.h"
10 #include "EngaugeAssert.h"
11 #include "GraphicsScene.h"
12 #include "GuidelineAbstract.h"
13 #include "GuidelineEllipse.h"
14 #include "GuidelineFactory.h"
15 #include "GuidelineLine.h"
16 #include "Guidelines.h"
17 #include "GuidelineState.h"
18 #include "Logger.h"
19 #include "MainWindow.h"
20 #include "MainWindowModel.h"
21 #include <QGraphicsItem>
22 #include <QGraphicsScene>
23 #include <QMap>
24 #include <qmath.h>
25 #include <QTextStream>
26 
Guidelines(MainWindow & mainWindow)27 Guidelines::Guidelines (MainWindow &mainWindow) :
28   m_mainWindow (mainWindow),
29   m_guidelineFactory (nullptr)
30 {
31 }
32 
~Guidelines()33 Guidelines::~Guidelines ()
34 {
35   clear ();
36   delete m_guidelineFactory;
37 }
38 
clear()39 void Guidelines::clear ()
40 {
41   GuidelineContainerPrivate::iterator itr;
42 
43   for (itr = m_guidelineContainerXT.begin(); itr != m_guidelineContainerXT.end(); itr++) {
44     GuidelineAbstract *guideline = *itr;
45 
46     // Remove the guideline from its scene
47     QGraphicsScene *scene = &guideline->scene();
48 
49     if (scene != nullptr) {
50 
51       guideline->removeFromScene (scene);
52 
53     }
54   }
55 
56   for (itr = m_guidelineContainerYR.begin(); itr != m_guidelineContainerYR.end(); itr++) {
57     GuidelineAbstract *guideline = *itr;
58 
59     // Remove the guideline from its scene
60     QGraphicsScene *scene = &guideline->scene();
61 
62     if (scene != nullptr) {
63 
64       guideline->removeFromScene (scene);
65 
66     }
67   }
68 
69   m_guidelineContainerXT.clear ();
70   m_guidelineContainerYR.clear ();
71 }
72 
color() const73 ColorPalette Guidelines::color () const
74 {
75   return m_mainWindow.modelMainWindow().guidelineColor();
76 }
77 
coordsType() const78 CoordsType Guidelines::coordsType () const
79 {
80   return m_mainWindow.cmdMediator()->document().modelCoords().coordsType();
81 }
82 
createGuideline(const QString & identifier,GuidelineState stateInitial)83 GuidelineAbstract *Guidelines::createGuideline (const QString &identifier,
84                                                 GuidelineState stateInitial)
85 {
86   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::createGuideline"
87                                << " identifier=" << identifier.toLatin1().data()
88                                << " state=" << guidelineStateAsString (stateInitial).toLatin1().data();
89 
90   GuidelineAbstract *guideline = m_guidelineFactory->createGuideline (*this,
91                                                                       stateInitial,
92                                                                       m_mainWindow,
93                                                                       identifier);
94 
95   return guideline;
96 }
97 
createGuidelineR(const QString & identifier,double r)98 void Guidelines::createGuidelineR (const QString &identifier,
99                                    double r)
100 {
101   GuidelineAbstract *guideline = createGuideline (identifier,
102                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_R_SELECT_EDIT_APPEARING);
103   if (guideline) {
104     guideline->updateGeometry (r);
105   }
106 
107   m_guidelineContainerYR.append (guideline);
108 }
109 
createGuidelineR(const QString & identifier,const QPointF & posScreen)110 void Guidelines::createGuidelineR (const QString &identifier,
111                                    const QPointF &posScreen)
112 {
113   GuidelineAbstract *guideline = createGuideline (identifier,
114                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_R_SELECT_EDIT_APPEARING);
115   if (guideline) {
116     guideline->updateGeometry (posScreen);
117   }
118 
119   m_guidelineContainerYR.append (guideline);
120 }
121 
createGuidelineT(const QString & identifier,double t)122 void Guidelines::createGuidelineT (const QString &identifier,
123                                    double t)
124 {
125   GuidelineAbstract *guideline = createGuideline (identifier,
126                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_T_SELECT_EDIT_APPEARING);
127   if (guideline) {
128     guideline->updateGeometry (t);
129   }
130 
131   m_guidelineContainerXT.append (guideline);
132 }
133 
createGuidelineT(const QString & identifier,const QPointF & posScreen)134 void Guidelines::createGuidelineT (const QString &identifier,
135                                    const QPointF &posScreen)
136 {
137   GuidelineAbstract *guideline = createGuideline (identifier,
138                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_T_SELECT_EDIT_APPEARING);
139   if (guideline) {
140     guideline->updateGeometry (posScreen);
141   }
142 
143   m_guidelineContainerXT.append (guideline);
144 }
145 
createGuidelineX(const QString & identifier,double x)146 void Guidelines::createGuidelineX (const QString &identifier,
147                                    double x)
148 {
149   GuidelineAbstract *guideline = createGuideline (identifier,
150                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_X_SELECT_EDIT_APPEARING);
151   if (guideline) {
152     guideline->updateGeometry (x);
153   }
154 
155   m_guidelineContainerXT.append (guideline);
156 }
157 
createGuidelineX(const QString & identifier,const QPointF & posScreen)158 void Guidelines::createGuidelineX (const QString &identifier,
159                                    const QPointF &posScreen)
160 {
161   GuidelineAbstract *guideline = createGuideline (identifier,
162                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_X_SELECT_EDIT_APPEARING);
163   if (guideline) {
164     guideline->updateGeometry (posScreen);
165   }
166 
167   m_guidelineContainerXT.append (guideline);
168 }
169 
createGuidelineY(const QString & identifier,double y)170 void Guidelines::createGuidelineY (const QString &identifier,
171                                    double y)
172 {
173   GuidelineAbstract *guideline = createGuideline (identifier,
174                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_Y_SELECT_EDIT_APPEARING);
175   if (guideline) {
176     guideline->updateGeometry (y);
177   }
178 
179   m_guidelineContainerYR.append (guideline);
180 }
181 
createGuidelineY(const QString & identifier,const QPointF & posScreen)182 void Guidelines::createGuidelineY (const QString &identifier,
183                                    const QPointF &posScreen)
184 {
185   GuidelineAbstract *guideline = createGuideline (identifier,
186                                                   GUIDELINE_STATE_DEPLOYED_CONSTANT_Y_SELECT_EDIT_APPEARING);
187   if (guideline) {
188     guideline->updateGeometry (posScreen);
189   }
190 
191   m_guidelineContainerYR.append (guideline);
192 }
193 
createReplacementGuideline(const QString & identifierReplaced,double newValue,GuidelineState guidelineStateForReplacement)194 void Guidelines::createReplacementGuideline (const QString &identifierReplaced,
195                                              double newValue,
196                                              GuidelineState guidelineStateForReplacement)
197 {
198   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::createReplacementGuideline";
199 
200   // Out with the old. Since it is still on the stack we only unregister, versus remove, it
201   unregisterGuideline (identifierReplaced);
202 
203   // And in with the new
204   switch (guidelineStateForReplacement) {
205   case GUIDELINE_STATE_DEPLOYED_CONSTANT_R_SELECT_EDIT:
206     createGuidelineR (identifierReplaced,
207                       newValue);
208     break;
209 
210   case GUIDELINE_STATE_DEPLOYED_CONSTANT_T_SELECT_EDIT:
211     createGuidelineT(identifierReplaced,
212                      newValue);
213     break;
214 
215   case GUIDELINE_STATE_DEPLOYED_CONSTANT_X_SELECT_EDIT:
216     createGuidelineX(identifierReplaced,
217                      newValue);
218     break;
219 
220   case GUIDELINE_STATE_DEPLOYED_CONSTANT_Y_SELECT_EDIT:
221     createGuidelineY(identifierReplaced,
222                      newValue);
223     break;
224 
225   default:
226     LOG4CPP_ERROR_S ((*mainCat)) << "Guidelines::createReplacementGuideline encountered unexpected state "
227                                  << guidelineStateAsString (guidelineStateForReplacement).toLatin1().data();
228 
229   }
230 }
231 
findIdentifierXT(const QString & identifier)232 GuidelineContainerPrivate::iterator Guidelines::findIdentifierXT (const QString &identifier)
233 {
234   GuidelineContainerPrivate::iterator itr;
235 
236   // Find the closest point
237   for (itr = m_guidelineContainerXT.begin (); itr != m_guidelineContainerXT.end (); itr++) {
238     GuidelineAbstract *guideline = *itr;
239     if (identifier == guideline->identifier()) {
240         return itr;
241     }
242   }
243 
244   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::findIdentifierXT could not find " << identifier.toLatin1().data();
245 
246   return m_guidelineContainerXT.end();
247 }
248 
findIdentifierYR(const QString & identifier)249 GuidelineContainerPrivate::iterator Guidelines::findIdentifierYR (const QString &identifier)
250 {
251   GuidelineContainerPrivate::iterator itr;
252 
253   // Find the closest point
254   for (itr = m_guidelineContainerYR.begin (); itr != m_guidelineContainerYR.end (); itr++) {
255     GuidelineAbstract *guideline = *itr;
256     if (identifier == guideline->identifier()) {
257       return itr;
258     }
259   }
260 
261   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::findIdentifierYR could not find " << identifier.toLatin1().data();
262 
263   return m_guidelineContainerYR.end(); // Return something
264 }
265 
guidelineContainerPrivateXT() const266 const GuidelineContainerPrivate &Guidelines::guidelineContainerPrivateXT () const
267 {
268   return m_guidelineContainerXT;
269 }
270 
guidelineContainerPrivateYR() const271 const GuidelineContainerPrivate &Guidelines::guidelineContainerPrivateYR () const
272 {
273   return m_guidelineContainerYR;
274 }
275 
handleActiveChange(bool active)276 void Guidelines::handleActiveChange (bool active)
277 {
278   GuidelineContainerPrivate::iterator itr;
279 
280   for (itr = m_guidelineContainerXT.begin(); itr != m_guidelineContainerXT.end(); itr++) {
281     GuidelineAbstract *guideline = *itr;
282 
283     guideline->handleActiveChange (active);
284   }
285 
286   for (itr = m_guidelineContainerYR.begin(); itr != m_guidelineContainerYR.end(); itr++) {
287     GuidelineAbstract *guideline = *itr;
288 
289     guideline->handleActiveChange (active);
290   }
291 }
292 
handleGuidelineMode(bool visible,bool isLocked)293 void Guidelines::handleGuidelineMode (bool visible,
294                                       bool isLocked)
295 {
296   GuidelineContainerPrivate::iterator itr;
297 
298   for (itr = m_guidelineContainerXT.begin(); itr != m_guidelineContainerXT.end(); itr++) {
299     GuidelineAbstract *guideline = *itr;
300 
301     guideline->handleGuidelineMode (visible,
302                                     isLocked);
303   }
304 
305   for (itr = m_guidelineContainerYR.begin(); itr != m_guidelineContainerYR.end(); itr++) {
306     GuidelineAbstract *guideline = *itr;
307 
308     guideline->handleGuidelineMode (visible,
309                                     isLocked);
310   }
311 }
312 
initialize(GraphicsScene & scene)313 void Guidelines::initialize (GraphicsScene &scene)
314 {
315   m_guidelineFactory = new GuidelineFactory (&scene);
316 }
317 
modelGuidelines() const318 DocumentModelGuidelines Guidelines::modelGuidelines () const
319 {
320   GuidelineValues valuesXT, valuesYR;
321 
322   GuidelineContainerPrivate::const_iterator itr;
323 
324   for (itr = m_guidelineContainerXT.begin(); itr != m_guidelineContainerXT.end(); itr++) {
325     const GuidelineAbstract *guideline = *itr;
326     QString identifier = guideline->identifier();
327     double value = guideline->posCursorGraph().x();
328     valuesXT [identifier] = value;
329   }
330 
331   for (itr = m_guidelineContainerYR.begin(); itr != m_guidelineContainerYR.end(); itr++) {
332     const GuidelineAbstract *guideline = *itr;
333     QString identifier = guideline->identifier();
334     double value = guideline->posCursorGraph().y();
335     valuesYR [identifier] = value;
336   }
337 
338   DocumentModelGuidelines model (valuesXT,
339                                  valuesYR);
340 
341   return model;
342 }
343 
moveGuidelineXT(const QString & identifier,double valueAfter)344 void Guidelines::moveGuidelineXT (const QString &identifier,
345                                   double valueAfter)
346 {
347   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::moveGuidelineXT"
348                                << " identifier=" << identifier.toLatin1().data()
349                                << " value=" << valueAfter;
350 
351   GuidelineContainerPrivate::iterator itr = findIdentifierXT (identifier);
352 
353    if (itr== m_guidelineContainerXT.end ()) {
354      LOG4CPP_ERROR_S ((*mainCat)) << "Guidelines::moveGuidelineXT";
355    } else {
356      // Move it
357     GuidelineAbstract *guideline = *itr;
358     guideline->updateGeometry (valueAfter);
359   }
360 }
361 
moveGuidelineYR(const QString & identifier,double valueAfter)362 void Guidelines::moveGuidelineYR (const QString &identifier,
363                                   double valueAfter)
364 {
365   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::moveGuidelineYR"
366                                << " identifier=" << identifier.toLatin1().data()
367                                << " value=" << valueAfter;
368 
369   GuidelineContainerPrivate::iterator itr = findIdentifierYR (identifier);
370 
371   if (itr == m_guidelineContainerYR.end ()) {
372     LOG4CPP_ERROR_S ((*mainCat)) << "Guidelines::moveGuidelineYR";
373   } else {
374     // Move it
375     GuidelineAbstract *guideline = *itr;
376     guideline->updateGeometry (valueAfter);
377   }
378 }
379 
registerGuidelineXT(GuidelineAbstract * guideline)380 void Guidelines::registerGuidelineXT (GuidelineAbstract *guideline)
381 {
382   m_guidelineContainerXT.push_back (guideline);
383 }
384 
registerGuidelineYR(GuidelineAbstract * guideline)385 void Guidelines::registerGuidelineYR (GuidelineAbstract *guideline)
386 {
387   m_guidelineContainerYR.push_back (guideline);
388 }
389 
removeGuideline(const QString & identifier)390 void Guidelines::removeGuideline (const QString &identifier)
391 {
392   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::removeGuideline"
393                                << " identifier=" << identifier.toLatin1().data();
394 
395   GuidelineAbstract *guideline = unregisterGuideline (identifier);
396 
397   if (guideline != nullptr) {
398     delete guideline;
399   }
400 }
401 
setModelGuidelines(CoordsType coordsType,const DocumentModelGuidelines & modelGuidelines)402 void Guidelines::setModelGuidelines (CoordsType coordsType,
403                                      const DocumentModelGuidelines &modelGuidelines)
404 {
405   clear ();
406 
407   GuidelineValues valuesXT = modelGuidelines.valuesX();
408   GuidelineValues valuesYR = modelGuidelines.valuesY();
409 
410   GuidelineValues::const_iterator itr;
411 
412   for (itr = valuesXT.begin(); itr != valuesXT.end(); itr++) {
413     QString identifier = itr.key();
414     double value = itr.value();
415 
416     if (coordsType == COORDS_TYPE_CARTESIAN) {
417       createGuidelineX (identifier,
418                         value);
419     } else {
420       createGuidelineT (identifier,
421                         value);
422     }
423   }
424 
425   for (itr = valuesYR.begin(); itr != valuesYR.end(); itr++) {
426     QString identifier = itr.key();
427     double value = itr.value();
428 
429     if (coordsType == COORDS_TYPE_CARTESIAN) {
430       createGuidelineY (identifier,
431                         value);
432     } else {
433       createGuidelineR (identifier,
434                         value);
435     }
436   }
437 }
438 
stateDump() const439 QString Guidelines::stateDump () const
440 {
441   // Sort the entries
442   QStringList sortedXT, sortedYR;
443   GuidelineContainerPrivate::const_iterator itrSort;
444 
445   for (itrSort = m_guidelineContainerXT.begin(); itrSort != m_guidelineContainerXT.end(); itrSort++) {
446     GuidelineAbstract *guideline = *itrSort;
447     sortedXT << guideline->stateDump ();
448   }
449 
450   for (itrSort = m_guidelineContainerYR.begin(); itrSort != m_guidelineContainerYR.end(); itrSort++) {
451     GuidelineAbstract *guideline = *itrSort;
452     sortedYR << guideline->stateDump ();
453   }
454 
455   std::sort (sortedXT.begin(),
456              sortedXT.end());
457   std::sort (sortedYR.begin(),
458              sortedYR.end());
459 
460   // Convert entries to output text
461   QString out;
462   QTextStream str (&out);
463 
464   str << "Guidelines::stateDump:\n";
465 
466   QStringList::const_iterator itrOut;
467 
468   for (itrOut = sortedXT.begin(); itrOut != sortedXT.end(); itrOut++) {
469     QString entry = *itrOut;
470     str << "                    " << entry << "\n";
471   }
472 
473   for (itrOut = sortedYR.begin(); itrOut != sortedYR.end(); itrOut++) {
474     QString entry = *itrOut;
475     str << "                    " << entry << "\n";
476   }
477 
478   return out;
479 }
480 
transformation() const481 Transformation Guidelines::transformation() const
482 {
483   return m_mainWindow.transformation ();
484 }
485 
unregisterGuideline(const QString & identifier)486 GuidelineAbstract *Guidelines::unregisterGuideline (const QString &identifier)
487 {
488   LOG4CPP_DEBUG_S ((*mainCat)) << "Guidelines::unregisterGuideline"
489                                << " identifier=" << identifier.toLatin1().data();
490 
491   // Try to unregister XT entry
492   GuidelineContainerPrivate::iterator itrXT = findIdentifierXT (identifier);
493   if (itrXT != m_guidelineContainerXT.end ()) {
494     m_guidelineContainerXT.erase (itrXT);
495 
496     return *itrXT;
497   }
498 
499   // Try to remove YR entry
500   GuidelineContainerPrivate::iterator itrYR = findIdentifierYR (identifier);
501   if (itrYR != m_guidelineContainerYR.end ()) {
502     m_guidelineContainerYR.erase (itrYR);
503 
504     return *itrYR;
505   }
506 
507   LOG4CPP_ERROR_S ((*mainCat)) << "Guidelines::unregisterGuideline cannot find "
508                                << identifier.toLatin1().data();
509   return nullptr;
510 }
511 
updateColor()512 void Guidelines::updateColor ()
513 {
514   GuidelineContainerPrivate::const_iterator itr;
515 
516   for (itr = m_guidelineContainerXT.begin(); itr != m_guidelineContainerXT.end(); itr++) {
517     GuidelineAbstract *guideline = *itr;
518     guideline->updateColor ();
519   }
520 
521   for (itr = m_guidelineContainerYR.begin(); itr != m_guidelineContainerYR.end(); itr++) {
522     GuidelineAbstract *guideline = *itr;
523     guideline->updateColor ();
524   }
525 }
526 
updateWithLatestTransformation()527 void Guidelines::updateWithLatestTransformation ()
528 {
529   GuidelineContainerPrivate::iterator itr;
530 
531   for (itr = m_guidelineContainerXT.begin(); itr != m_guidelineContainerXT.end(); itr++) {
532     GuidelineAbstract *guideline = *itr;
533     guideline->updateWithLatestTransformation ();
534   }
535 
536   for (itr = m_guidelineContainerYR.begin(); itr != m_guidelineContainerYR.end(); itr++) {
537     GuidelineAbstract *guideline = *itr;
538     guideline->updateWithLatestTransformation ();
539   }
540 }
541