Engauge Digitizer  2
DigitizeStateSelect.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 "CallbackScaleBar.h"
8 #include "CmdEditPointAxis.h"
9 #include "CmdEditPointGraph.h"
10 #include "CmdMediator.h"
11 #include "CmdMoveBy.h"
12 #include "DataKey.h"
13 #include "DigitizeStateContext.h"
14 #include "DigitizeStateSelect.h"
15 #include "DlgEditPointAxis.h"
16 #include "DlgEditPointGraph.h"
17 #include "DlgEditScale.h"
18 #include "EngaugeAssert.h"
19 #include "GraphicsItemsExtractor.h"
20 #include "GraphicsItemType.h"
21 #include "GraphicsScene.h"
22 #include "GraphicsView.h"
23 #include "Logger.h"
24 #include "MainWindow.h"
25 #include <QCursor>
26 #include <QGraphicsItem>
27 #include <QImage>
28 #include <QMessageBox>
29 #include <QObject>
30 #include <QtToString.h>
31 #include "Version.h"
32 
33 const QString MOVE_TEXT_DOWN (QObject::tr ("Move down"));
34 const QString MOVE_TEXT_LEFT (QObject::tr ("Move left"));
35 const QString MOVE_TEXT_RIGHT (QObject::tr ("Move right"));
36 const QString MOVE_TEXT_UP (QObject::tr ("Move up"));
37 
40 {
41 }
42 
43 DigitizeStateSelect::~DigitizeStateSelect ()
44 {
45 }
46 
48 {
50 }
51 
52 void DigitizeStateSelect::addHoverHighlighting()
53 {
54  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::addHoverHighlighting";
55 
56  QList<QGraphicsItem*> items = context().mainWindow().scene().items();
57  QList<QGraphicsItem*>::iterator itr;
58  for (itr = items.begin (); itr != items.end (); itr++) {
59 
60  QGraphicsItem *item = *itr;
61  if (item->data (DATA_KEY_GRAPHICS_ITEM_TYPE) == GRAPHICS_ITEM_TYPE_POINT) {
62  item->setAcceptHoverEvents(true);
63  }
64  }
65 }
66 
68  DigitizeState /* previousState */)
69 {
70  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::begin";
71 
72  setCursor(cmdMediator);
73  context().setDragMode(QGraphicsView::RubberBandDrag);
74 
75  addHoverHighlighting();
77 }
78 
79 QCursor DigitizeStateSelect::cursor(CmdMediator * /* cmdMediator */) const
80 {
81  LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateSelect::cursor";
82 
83  return QCursor (Qt::ArrowCursor);
84 }
85 
87 {
88  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::end";
89 
90  removeHoverHighlighting();
91 }
92 
94  const QString &pointIdentifier)
95 {
96  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::handleContextMenuEventAxis "
97  << " point=" << pointIdentifier.toLatin1 ().data ();
98 
99  if (cmdMediator->document().documentAxesPointsRequired() == DOCUMENT_AXES_POINTS_REQUIRED_2) {
100  handleContextMenuEventAxis2 (cmdMediator);
101  } else {
102  handleContextMenuEventAxis34 (cmdMediator,
103  pointIdentifier);
104  }
105 }
106 
107 void DigitizeStateSelect::handleContextMenuEventAxis2 (CmdMediator *cmdMediator)
108 {
109  LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateSelect::handleContextMenuEventAxis2";
110 
111  const bool IS_NOT_X_ONLY = false;
112 
113  // The point identifier we want is not necessarily the one edited but is the one with the
114  // nonzero x or y (but not both) coordinate
115  QString pointIdentifier = scaleBarPointIdentifier (cmdMediator);
116 
117  QPointF posScreen = cmdMediator->document().positionScreen (pointIdentifier);
118  QPointF posGraphBefore = cmdMediator->document().positionGraph (pointIdentifier);
119 
120  // Ask user for scale length
121  double scaleLength = scaleBarLength (cmdMediator);
122  DlgEditScale *dlg = new DlgEditScale (context().mainWindow(),
123  cmdMediator->document().modelCoords(),
124  cmdMediator->document().modelGeneral(),
126  &scaleLength);
127  int rtn = dlg->exec ();
128 
129  scaleLength = dlg->scaleLength (); // This call returns new value for scale length
130  delete dlg;
131 
132  if (rtn == QDialog::Accepted) {
133 
134  // User wants to edit the scale length, which is effectively editing this axis point, but let's perform sanity checks first
135 
136  bool isError;
137  QString errorMessage;
138 
139  bool isXNonzero = (posGraphBefore.x() != 0); // Identify which coordinate is to be edited
140  QPointF posGraphAfter (isXNonzero ? scaleLength : 0,
141  isXNonzero ? 0 : scaleLength);
142  context().mainWindow().cmdMediator()->document().checkEditPointAxis(pointIdentifier,
143  posScreen,
144  posGraphAfter,
145  isError,
146  errorMessage);
147 
148  if (isError) {
149 
150  QMessageBox::warning (0,
151  engaugeWindowTitle(),
152  errorMessage);
153 
154  } else {
155 
156  // Create a command to change the scale length
157  CmdEditPointAxis *cmd = new CmdEditPointAxis (context().mainWindow(),
158  cmdMediator->document(),
159  pointIdentifier,
160  posGraphBefore,
161  posGraphAfter,
162  IS_NOT_X_ONLY);
163  context().appendNewCmd(cmdMediator,
164  cmd);
165  }
166  }
167 }
168 
169 void DigitizeStateSelect::handleContextMenuEventAxis34 (CmdMediator *cmdMediator,
170  const QString &pointIdentifier)
171 {
172  LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateSelect::handleContextMenuEventAxis34";
173 
174  QPointF posScreen = cmdMediator->document().positionScreen (pointIdentifier);
175  QPointF posGraphBefore = cmdMediator->document().positionGraph (pointIdentifier);
176  bool isXOnly = cmdMediator->document().isXOnly (pointIdentifier);
177 
178  // Ask user for coordinates
179  double x = posGraphBefore.x();
180  double y = posGraphBefore.y();
181 
182  DlgEditPointAxis *dlg = new DlgEditPointAxis (context().mainWindow(),
183  cmdMediator->document().modelCoords(),
184  cmdMediator->document().modelGeneral(),
187  cmdMediator->document().documentAxesPointsRequired(),
188  isXOnly,
189  &x,
190  &y);
191  int rtn = dlg->exec ();
192 
193  QPointF posGraphAfter = dlg->posGraph (isXOnly); // This call returns new values for isXOnly and the graph position
194  delete dlg;
195 
196  if (rtn == QDialog::Accepted) {
197 
198  // User wants to edit this axis point, but let's perform sanity checks first
199 
200  bool isError;
201  QString errorMessage;
202 
203  context().mainWindow().cmdMediator()->document().checkEditPointAxis(pointIdentifier,
204  posScreen,
205  posGraphAfter,
206  isError,
207  errorMessage);
208 
209  if (isError) {
210 
211  QMessageBox::warning (0,
212  engaugeWindowTitle(),
213  errorMessage);
214 
215  } else {
216 
217  // Create a command to edit the point
218  CmdEditPointAxis *cmd = new CmdEditPointAxis (context().mainWindow(),
219  cmdMediator->document(),
220  pointIdentifier,
221  posGraphBefore,
222  posGraphAfter,
223  isXOnly);
224  context().appendNewCmd(cmdMediator,
225  cmd);
226  }
227  }
228 }
229 
231  const QStringList &pointIdentifiers)
232 {
233  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::handleContextMenuEventGraph "
234  << "points=" << pointIdentifiers.join(",").toLatin1 ().data ();
235 
236  double *x = 0, *y = 0;
237 
238  if (pointIdentifiers.count() == 1) {
239 
240  // There is exactly one point so pass its coordinates to the dialog
241  x = new double;
242  y = new double;
243 
244  QPointF posScreenBefore = cmdMediator->document().positionScreen (pointIdentifiers.first());
245  QPointF posGraphBefore;
247  posGraphBefore);
248 
249  // Ask user for coordinates
250  *x = posGraphBefore.x();
251  *y = posGraphBefore.y();
252  }
253 
254  DlgEditPointGraph *dlg = new DlgEditPointGraph (context().mainWindow(),
255  cmdMediator->document().modelCoords(),
256  cmdMediator->document().modelGeneral(),
259  x,
260  y);
261  if (x != 0) {
262  delete x;
263  x = 0;
264  }
265 
266  if (y != 0) {
267  delete y;
268  y = 0;
269  }
270 
271  int rtn = dlg->exec ();
272 
273  bool isXGiven, isYGiven;
274  double xGiven, yGiven;
275  dlg->posGraph (isXGiven, xGiven, isYGiven, yGiven); // One or both coordinates are returned
276  delete dlg;
277 
278  if (rtn == QDialog::Accepted) {
279 
280  // Create a command to edit the point
281  CmdEditPointGraph *cmd = new CmdEditPointGraph (context().mainWindow(),
282  cmdMediator->document(),
283  pointIdentifiers,
284  isXGiven,
285  isYGiven,
286  xGiven,
287  yGiven);
288  context().appendNewCmd(cmdMediator,
289  cmd);
290  }
291 }
292 
294 {
295  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::handleCurveChange";
296 }
297 
299  Qt::Key key,
300  bool atLeastOneSelectedItem)
301 {
302  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::handleKeyPress"
303  << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
304 
305  if (atLeastOneSelectedItem) {
306 
307  if (key == Qt::Key_Down ||
308  key == Qt::Key_Up ||
309  key == Qt::Key_Left ||
310  key == Qt::Key_Right) {
311 
312  keyPressArrow (cmdMediator,
313  key);
314 
315  }
316  }
317 }
318 
320  QPointF /* posScreen */)
321 {
322 // LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStateSelect::handleMouseMove";
323 }
324 
326  QPointF posScreen)
327 {
328  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::handleMousePress"
329  << " posScreen=" << QPointFToString (posScreen).toLatin1 ().data ();
330 
331  // Note that GraphicsView has already called GraphicsPointAbstract::resetPositionHasChanged on all items
332 
333  m_movingStart = posScreen;
334 }
335 
337  QPointF posScreen)
338 {
339  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::handleMouseRelease"
340  << " posScreen=" << QPointFToString (posScreen).toLatin1 ().data ();
341 
342  QPointF deltaScreen = posScreen - m_movingStart;
343  QStringList positionHasChangedIdentifers = context().mainWindow().scene().positionHasChangedPointIdentifiers();
344 
345  bool positionHasChanged = (positionHasChangedIdentifers.count () > 0);
346 
347  if (positionHasChanged && (
348  deltaScreen.x () != 0 ||
349  deltaScreen.y () != 0)) {
350 
351  QString moveText = moveTextFromDeltaScreen (deltaScreen);
352 
353  // Create command to move points
354  CmdMoveBy *cmd = new CmdMoveBy (context().mainWindow(),
355  cmdMediator->document(),
356  deltaScreen,
357  moveText,
358  positionHasChangedIdentifers);
359  context().appendNewCmd (cmdMediator,
360  cmd);
361 
362  } else {
363 
364  // Selection probably changed so update the MainWindow controls (especially Cut)
366 
367  }
368 }
369 
370 void DigitizeStateSelect::keyPressArrow (CmdMediator *cmdMediator,
371  Qt::Key key)
372 {
373  QPointF deltaScreen;
374  QString moveText;
375  switch (key) {
376  case Qt::Key_Down:
377  deltaScreen = QPointF (0, zoomedToUnzoomedScreenY ());
378  moveText = MOVE_TEXT_DOWN;
379  break;
380 
381  case Qt::Key_Left:
382  deltaScreen = QPointF (-1 * zoomedToUnzoomedScreenX (), 0);
383  moveText = MOVE_TEXT_LEFT;
384  break;
385 
386  case Qt::Key_Right:
387  deltaScreen = QPointF (zoomedToUnzoomedScreenX (), 0);
388  moveText = MOVE_TEXT_RIGHT;
389  break;
390 
391  case Qt::Key_Up:
392  deltaScreen = QPointF (0, -1 * zoomedToUnzoomedScreenY ());
393  moveText = MOVE_TEXT_UP;
394  break;
395 
396  default:
397  ENGAUGE_ASSERT (false);
398  }
399 
400  // Create command to move points
401  GraphicsItemsExtractor graphicsItemsExtractor;
402  const QList<QGraphicsItem*> &items = context().mainWindow().scene ().selectedItems();
403  CmdMoveBy *cmd = new CmdMoveBy (context().mainWindow(),
404  cmdMediator->document(),
405  deltaScreen,
406  moveText,
407  graphicsItemsExtractor.selectedPointIdentifiers (items));
408  context().appendNewCmd (cmdMediator,
409  cmd);
410 }
411 
412 QString DigitizeStateSelect::moveTextFromDeltaScreen (const QPointF &deltaScreen)
413 {
414  QString moveText;
415 
416  // x UP x -----> +x
417  // x x |
418  // LEFT x RIGHT |
419  // x x v
420  // x DOWN x +y
421  bool downOrRight = (deltaScreen.y () > -1.0 * deltaScreen.x ());
422  bool upOrRight = (deltaScreen.y () < deltaScreen.x ());
423  if (downOrRight && upOrRight) {
424  moveText = MOVE_TEXT_RIGHT;
425  } else if (downOrRight && !upOrRight) {
426  moveText = MOVE_TEXT_DOWN;
427  } else if (!downOrRight && upOrRight) {
428  moveText = MOVE_TEXT_UP;
429  } else {
430  moveText = MOVE_TEXT_LEFT;
431  }
432 
433  return moveText;
434 }
435 
436 void DigitizeStateSelect::removeHoverHighlighting()
437 {
438  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::removeHoverHighlighting";
439 
440  QList<QGraphicsItem*> items = context().mainWindow().scene().items();
441  QList<QGraphicsItem*>::iterator itr;
442  for (itr = items.begin (); itr != items.end (); itr++) {
443 
444  QGraphicsItem *item = *itr;
445  if (item->data (DATA_KEY_GRAPHICS_ITEM_TYPE) == GRAPHICS_ITEM_TYPE_POINT) {
446  item->setAcceptHoverEvents(false);
447  }
448  }
449 }
450 
451 double DigitizeStateSelect::scaleBarLength (CmdMediator *cmdMediator) const
452 {
453  CallbackScaleBar ftor;
454 
455  Functor2wRet<const QString &, const Point&, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
457  cmdMediator->iterateThroughCurvePointsAxes (ftorWithCallback);
458 
459  return ftor.scaleBarLength ();
460 }
461 
462 QString DigitizeStateSelect::scaleBarPointIdentifier (CmdMediator *cmdMediator) const
463 {
464  CallbackScaleBar ftor;
465 
466  Functor2wRet<const QString &, const Point&, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
468  cmdMediator->iterateThroughCurvePointsAxes (ftorWithCallback);
469 
470  return ftor.scaleBarPointIdentifier();
471 }
472 
473 void DigitizeStateSelect::setHoverHighlighting(const MainWindowModel &modelMainWindow)
474 {
475  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::addHoverHighlighting";
476 
477  // Set the opacity for all points. It should be already set for pre-existing points
478  QList<QGraphicsItem*> items = context().mainWindow().scene().items();
479  QList<QGraphicsItem*>::iterator itr;
480  for (itr = items.begin (); itr != items.end (); itr++) {
481 
482  QGraphicsItem *item = *itr;
483  if (item->data (DATA_KEY_GRAPHICS_ITEM_TYPE) == GRAPHICS_ITEM_TYPE_POINT) {
484  item->setOpacity (modelMainWindow.highlightOpacity());
485  }
486  }
487 }
488 
490 {
491  return "DigitizeStateSelect";
492 }
493 
495 {
496  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::updateAfterPointAddition";
497 
498  addHoverHighlighting ();
499 }
500 
502  const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
503 {
504  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::updateModelDigitizeCurve";
505 }
506 
508 {
509  LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStateSelect::updateModelSegments";
510 }
511 
512 double DigitizeStateSelect::zoomedToUnzoomedScreenX () const
513 {
514  double m11 = context().mainWindow ().view ().transform().m11 ();
515  return 1.0 / m11;
516 }
517 
518 double DigitizeStateSelect::zoomedToUnzoomedScreenY () const
519 {
520  double m22 = context().mainWindow ().view ().transform().m22 ();
521  return 1.0 / m22;
522 }
Dialog box for editing the information of one axis point, in a graph with two axes.
CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
Callback for identifying, for the scale bar of a map, various quantities.
virtual void handleKeyPress(CmdMediator *cmdMediator, Qt::Key key, bool atLeastOneSelectedItem)
Handle a key press that was intercepted earlier.
void setDragMode(QGraphicsView::DragMode dragMode)
Set QGraphicsView drag mode (in m_view). Called from DigitizeStateAbstractBase subclasses.
virtual QCursor cursor(CmdMediator *cmdMediator) const
Returns the state-specific cursor shape.
QString scaleBarPointIdentifier() const
Identified axis point.
virtual void updateModelSegments(const DocumentModelSegments &modelSegments)
Update the segments given the new settings.
void updateAfterMouseRelease()
Call MainWindow::updateControls (which is private) after the very specific case - a mouse press/relea...
Dialog box for editing the information of the map scale.
Definition: DlgEditScale.h:22
Command for editing the graph coordinates of one or more graph points.
virtual void handleContextMenuEventAxis(CmdMediator *cmdMediator, const QString &pointIdentifier)
Handle a right click, on an axis point, that was intercepted earlier.
virtual void handleCurveChange(CmdMediator *cmdMediator)
Handle the selection of a new curve. At a minimum, DigitizeStateSegment will generate a new set of Se...
virtual void handleMouseRelease(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse release that was intercepted earlier.
void updateViewsOfSettings(const QString &activeCurve)
Update curve-specific view of settings. Private version gets active curve name from DigitizeStateCont...
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition: Document.cpp:721
DocumentAxesPointsRequired documentAxesPointsRequired() const
Get method for DocumentAxesPointsRequired.
Definition: Document.cpp:361
Command for moving all selected Points by a specified translation.
Definition: CmdMoveBy.h:18
virtual void handleContextMenuEventGraph(CmdMediator *cmdMediator, const QStringList &pointIdentifiers)
Handle a right click, on a graph point, that was intercepted earlier.
virtual void handleMousePress(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse press that was intercepted earlier.
QStringList positionHasChangedPointIdentifiers() const
Return a list of identifiers for the points that have moved since the last call to resetPositionHasCh...
double highlightOpacity() const
Get method for highlight opacity.
CmdMediator * cmdMediator()
Accessor for commands to process the Document.
Definition: MainWindow.cpp:322
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Definition: CmdMediator.cpp:72
virtual void updateAfterPointAddition()
Update graphics attributes after possible new points. This is useful for highlight opacity...
DigitizeStateContext & context()
Reference to the DigitizeStateContext that contains all the DigitizeStateAbstractBase subclasses...
virtual QString state() const
State name for debugging.
MainWindow & mainWindow()
Reference to the MainWindow, without const.
void checkEditPointAxis(const QString &pointIdentifier, const QPointF &posScreen, const QPointF &posGraph, bool &isError, QString &errorMessage)
Check before calling editPointAxis.
Definition: Document.cpp:283
Transformation transformation() const
Return read-only copy of transformation.
This class consolidates utility routines that deal with graphics items that are getting extracted fro...
QPointF posGraph(bool &isXOnly) const
Return the graph coordinates position specified by the user. Only applies if dialog was accepted...
double scaleBarLength() const
Length of scale bar.
Model for DlgSettingsDigitizeCurve and CmdSettingsDigitizeCurve.
virtual void begin(CmdMediator *cmdMediator, DigitizeState previousState)
Method that is called at the exact moment a state is entered.
GraphicsView & view()
View for the QImage and QGraphicsItems, without const.
GraphicsScene & scene()
Scene container for the QImage and QGraphicsItems.
void setCursor(CmdMediator *cmdMediator)
Update the cursor according to the current state.
Container for all DigitizeStateAbstractBase subclasses. This functions as the context class in a stan...
Model for DlgSettingsMainWindow.
void appendNewCmd(CmdMediator *cmdMediator, QUndoCommand *cmd)
Append just-created QUndoCommand to command stack. This is called from DigitizeStateAbstractBase subc...
virtual void end()
Method that is called at the exact moment a state is exited. Typically called just before begin for t...
QPointF positionScreen(const QString &pointIdentifier) const
See Curve::positionScreen.
Definition: Document.cpp:825
virtual void handleMouseMove(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse move. This is part of an experiment to see if augmenting the cursor in Point Match mod...
QStringList selectedPointIdentifiers(const QList< QGraphicsItem *> &items) const
Return list of selected point identifiers.
DigitizeStateSelect(DigitizeStateContext &context)
Single constructor.
MainWindowModel modelMainWindow() const
Get method for main window model.
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
virtual QString activeCurve() const
Name of the active Curve. This can include AXIS_CURVE_NAME.
Command queue stack.
Definition: CmdMediator.h:23
void posGraph(bool &isX, double &x, bool &isY, double &y) const
Return one or both coordinates. Only applies if dialog was accepted.
Dialog box for editing the information of one or more points.
Model for DlgSettingsSegments and CmdSettingsSegments.
Base class for all digitizing states. This serves as an interface to DigitizeStateContext.
void iterateThroughCurvePointsAxes(const Functor2wRet< const QString &, const Point &, CallbackSearchReturn > &ftorWithCallback)
See Curve::iterateThroughCurvePoints, for the single axes curve.
Definition: CmdMediator.cpp:87
QPointF positionGraph(const QString &pointIdentifier) const
See Curve::positionGraph.
Definition: Document.cpp:820
Command for editing the graph coordinates one axis point.
QString selectedGraphCurve() const
Curve name that is currently selected in m_cmbCurve.
double scaleLength() const
Return the scale bar length specified by the user. Only applies if dialog was accepted.
virtual void updateModelDigitizeCurve(CmdMediator *cmdMediator, const DocumentModelDigitizeCurve &modelDigitizeCurve)
Update the digitize curve settings.
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition: Document.cpp:693
bool isXOnly(const QString &pointIdentifier) const
See Curve::isXOnly.
Definition: Document.cpp:439