Engauge Digitizer  2
NonPdfFrameHandle.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 "NonPdfCropping.h"
8 #include "NonPdfFrameHandle.h"
9 #include <QBrush>
10 #include <QGraphicsScene>
11 #include <QGraphicsView>
12 #include <QStyleOptionGraphicsItem>
13 
14 const double HANDLE_SIZE_AS_FRACTION_OF_WINDOW_SIZE = 30;
15 
16 NonPdfFrameHandle::NonPdfFrameHandle (QGraphicsScene &scene,
17  QGraphicsView &view,
18  const QPointF &pointReference,
19  int orientationFlags,
20  NonPdfCropping &nonPdfCropping,
21  int zValue) :
22  m_nonPdfCropping (nonPdfCropping),
23  m_orientationFlags (orientationFlags),
24  m_disableEventsWhileMovingAutomatically (false),
25  m_scene (scene),
26  m_view (view)
27 {
28  const double SUBTLE_OPACITY = 0.2;
29 
30  // Advantages of using ItemIgnoresTransformations:
31  // 1) handles do not get bigger or smaller depending on the size of the image
32  // 2) handles never get ugly when zoomed in
33  // 3) handles never get too tiny when zoomed out
34  // Disadvantages of using ItemIgnoresTransformation:
35  // 1) Resizing the preview window with ItemIgnoresTransformations moves the handles out of position
36  // 2) More conversions back and forth between untransformed and transformed coordinates. This was the deal breaker since
37  // the transformations were undocumented and ultimately too difficult
38  // The solution is to have constant-size handles WITHOUT ItemIgnoresTransformations. This means resizing the window
39  // also involves resizing the handles, but everything else is pretty easy
40  //
41  // ItemIgnoresTransformations flag must agree with the QGraphicsRectItem used for the frame box by PdfCropping
42  setFlags (QGraphicsItem::ItemIsMovable |
43  QGraphicsItem::ItemIsSelectable |
44  QGraphicsItem::ItemSendsScenePositionChanges);
45 
46  // Fill with nice color for better visibility. Note that the pen is disabled by overriding the paint method
47  setBrush (QBrush (Qt::blue));
48  setVisible (true);
49  setZValue (zValue);
50  setOpacity (SUBTLE_OPACITY);
51  setPos (pointReference); // Point position is handled in scene/view coordinates
52 
53  // Add to scene
54  scene.addItem (this);
55 
56  QSize handleSize = m_nonPdfCropping.windowSize() / HANDLE_SIZE_AS_FRACTION_OF_WINDOW_SIZE;
57 
58  // Adjust positions of handles that are not at the top left so handles are laid out symmetrically
59  QPointF pointPos = pointReference;
60  if ((orientationFlags & NonPdfCropping::NON_PDF_CROPPING_LEFT) != 0) {
61  pointPos.setX (pointPos.x() - handleSize.width() / 2.0);
62  } else if ((orientationFlags & NonPdfCropping::NON_PDF_CROPPING_RIGHT) != 0) {
63  pointPos.setX (pointPos.x() + handleSize.width() / 2.0);
64  }
65  if ((orientationFlags & NonPdfCropping::NON_PDF_CROPPING_TOP) != 0) {
66  pointPos.setY (pointPos.y() - handleSize.height() / 2.0);
67  } else if ((orientationFlags & NonPdfCropping::NON_PDF_CROPPING_BOTTOM) != 0) {
68  pointPos.setY (pointPos.y() + handleSize.height() / 2.0);
69  }
70 
71  // Start with geometry. Since point positions are handled in scene/view coordinates, we have to convert
72  // to local coordinates for the rectangle
73  QPointF topLeftLocal = mapFromScene (pointPos);
74 
75  setRect (QRectF (topLeftLocal,
76  handleSize));
77 }
78 
79 QVariant NonPdfFrameHandle::itemChange (GraphicsItemChange change,
80  const QVariant &value)
81 {
82  QVariant valueFiltered = value;
83 
84  if (change == ItemPositionChange && scene()) {
85 
86  QPointF sizeAsPointF (boundingRect().size().width(),
87  boundingRect().size().height());
88 
89  // New position is in the value argument
90  QPointF newPos = valueFiltered.toPointF();
91  QPointF oldPos = pos ();
92 
93  // This sequence is from http://www.qtcentre.org/threads/47248-How-to-efficiently-get-position-of-a-QGraphicsItem-in-view-coordinates
94  QRectF newRectItem (newPos,
95  QSize (boundingRect().size().width(),
96  boundingRect().size().height()));
97  QPolygonF newRectScene = mapToScene (newRectItem);
98  QPolygon newRectView = m_view.mapFromScene (newRectScene.boundingRect());
99 
100  // Skip moving of this handle if it will go outside of the window
101  QRectF rectWindow = m_scene.sceneRect();
102  if (!rectWindow.contains (newRectView.boundingRect())) {
103 
104  // Keep the item inside the scene rectangle
105  newPos.setX (qMin (rectWindow.right(), qMax (newPos.x(), rectWindow.left())));
106  newPos.setY (qMin (rectWindow.bottom(), qMax (newPos.y(), rectWindow.top())));
107 
108  valueFiltered = (newPos);
109 
110  }
111 
112  // Skip moving of other handles, in response to the move of this handle, if event handling is (temporarily) off,
113  // to prevent an infinite loop
114  if (!m_disableEventsWhileMovingAutomatically) {
115 
116  bool left = ((m_orientationFlags & NonPdfCropping::NON_PDF_CROPPING_LEFT ) != 0);
117  bool right = ((m_orientationFlags & NonPdfCropping::NON_PDF_CROPPING_RIGHT ) != 0);
118  bool top = ((m_orientationFlags & NonPdfCropping::NON_PDF_CROPPING_TOP ) != 0);
119  bool bottom = ((m_orientationFlags & NonPdfCropping::NON_PDF_CROPPING_BOTTOM) != 0);
120 
121  if (left && top) {
122  m_nonPdfCropping.moveTL (newPos, oldPos);
123  } else if (right && top) {
124  m_nonPdfCropping.moveTR (newPos, oldPos);
125  } else if (right && bottom) {
126  m_nonPdfCropping.moveBR (newPos, oldPos);
127  } else if (left && bottom) {
128  m_nonPdfCropping.moveBL (newPos, oldPos);
129  }
130  }
131  }
132 
133  return QGraphicsItem::itemChange(change, valueFiltered);
134 }
135 
136 void NonPdfFrameHandle::paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
137 {
138  // Temporarily remove the selected option
139  QStyleOptionGraphicsItem scrubbed (*option);
140  scrubbed.state &= ~QStyle::State_Selected;
141  QGraphicsRectItem::paint (painter, &scrubbed, widget);
142 }
143 
145 {
146  m_disableEventsWhileMovingAutomatically = disable;
147 }
static const int NON_PDF_CROPPING_LEFT
Bit flag when handle is aligned with left edge at reference point.
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
Override the paint method so the dashed-border-when-selected can be removed.
This class shows a frame around the selected portion of the import preview window.
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value)
Intercept the drags and process them, which is the whole point of handles.
void moveTL(const QPointF &newPos, const QPointF &oldPos)
Top left corner handle was moved.
QSize windowSize() const
Size of window in scene coordinates.
static const int NON_PDF_CROPPING_TOP
Bit flag when handle is aligned with top edge at reference point.
static const int NON_PDF_CROPPING_BOTTOM
Bit flag when handle is aligned with bottom edge at reference point.
void moveTR(const QPointF &newPos, const QPointF &oldPos)
Top right corner handle was moved.
void moveBR(const QPointF &newPos, const QPointF &oldPos)
Bottom right corner handle was moved.
void moveBL(const QPointF &newPos, const QPointF &oldPos)
Bottom left corner handle was moved.
void setDisableEventsWhileMovingAutomatically(bool disable)
Temporarily disable event handling so code can move this object without triggering a cascade of event...
NonPdfFrameHandle(QGraphicsScene &scene, QGraphicsView &view, const QPointF &pointReference, int orientationFlags, NonPdfCropping &nonPdfCropping, int zValue)
Single constructor.
static const int NON_PDF_CROPPING_RIGHT
Bit flag when handle is aligned with right edge at reference point.