Engauge Digitizer  2
 All Classes Files Functions Variables Enumerations Enumerator Friends Pages
GraphicsView.cpp
1 #include "DataKey.h"
2 #include "GraphicsItemType.h"
3 #include "GraphicsView.h"
4 #include "LoadFileInfo.h"
5 #include "Logger.h"
6 #include "MainWindow.h"
7 #include "Point.h"
8 #include <QApplication>
9 #include <QDebug>
10 #include <QDropEvent>
11 #include <QGraphicsPixmapItem>
12 #include <QGraphicsPolygonItem>
13 #include <QGraphicsScene>
14 #include <QMimeData>
15 #include <QMouseEvent>
16 #include <QScrollBar>
17 #include "QtToString.h"
18 
19 extern const QString AXIS_CURVE_NAME;
20 
21 GraphicsView::GraphicsView(QGraphicsScene *scene,
22  MainWindow &mainWindow) :
23  QGraphicsView (scene)
24 {
25  connect (this, SIGNAL (signalContextMenuEvent (QString)), &mainWindow, SLOT (slotContextMenuEvent (QString)));
26  connect (this, SIGNAL (signalDraggedDigFile (QString)), &mainWindow, SLOT (slotFileOpenDraggedDigFile (QString)));
27  connect (this, SIGNAL (signalDraggedImage (QImage)), &mainWindow, SLOT (slotFileImportDraggedImage (QImage)));
28  connect (this, SIGNAL (signalDraggedImageUrl (QUrl)), &mainWindow, SLOT (slotFileImportDraggedImageUrl (QUrl)));
29  connect (this, SIGNAL (signalKeyPress (Qt::Key, bool)), &mainWindow, SLOT (slotKeyPress (Qt::Key, bool)));
30  connect (this, SIGNAL (signalLeave ()), &mainWindow, SLOT (slotLeave ()));
31  connect (this, SIGNAL (signalMouseMove(QPointF)), &mainWindow, SLOT (slotMouseMove (QPointF)));
32  connect (this, SIGNAL (signalMousePress (QPointF)), &mainWindow, SLOT (slotMousePress (QPointF)));
33  connect (this, SIGNAL (signalMouseRelease (QPointF)), &mainWindow, SLOT (slotMouseRelease (QPointF)));
34 
35  setMouseTracking (true);
36  setAcceptDrops (true);
37  setEnabled (true);
38  setRenderHints(QPainter::Antialiasing);
39  setBackgroundBrush (QBrush (QColor (Qt::gray)));
40  verticalScrollBar()->setCursor (QCursor (Qt::ArrowCursor));
41  horizontalScrollBar()->setCursor (QCursor (Qt::ArrowCursor));
42 
43  // Skip setStatusTip here since that will overwrite much more important messages, and trigger gratuitous showing of status bar
44  setWhatsThis (tr ("Main Window\n\n"
45  "After an image file is imported, or an Engauge Document opened, an image appears in this area. "
46  "Points are added to the image.\n\n"
47  "If the image is a graph with two axes and one or more curves, then three axis points must be "
48  "created along those axes. Just put two axis points on one axis and a third axis point on the other "
49  "axis, as far apart as possible for higher accuracy. Then curve points can be added along the curves.\n\n"
50  "If the image is a map with a scale to define length, then two axis points must be "
51  "created at either end of the scale. Then curve points can be added.\n\n"
52  "Zooming the image in or out is performed using any of several methods:\n"
53  "1) rotating the mouse wheel when the cursor is outside of the image\n"
54  "2) pressing the minus or plus keys\n"
55  "3) selecting a new zoom setting from the View/Zoom menu"));
56 }
57 
58 GraphicsView::~GraphicsView()
59 {
60 }
61 
62 void GraphicsView::contextMenuEvent (QContextMenuEvent *event)
63 {
64  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::contextMenuEvent";
65 
66  QList<QGraphicsItem*> items = scene()->selectedItems ();
67 
68  if (items.count () == 1) {
69 
70  QGraphicsItem *item = items.first ();
71  QString pointIdentifier = item->data (DATA_KEY_IDENTIFIER).toString ();
72  GraphicsItemType type = (GraphicsItemType) item->data (DATA_KEY_GRAPHICS_ITEM_TYPE).toInt ();
73  QString curveName = Point::curveNameFromPointIdentifier (pointIdentifier);
74 
75  if ((type == GRAPHICS_ITEM_TYPE_POINT) &&
76  (curveName == AXIS_CURVE_NAME)) {
77 
78  // A single axis point is selected so edit it
79  emit signalContextMenuEvent (pointIdentifier);
80  event->accept ();
81 
82  return;
83  }
84  }
85 
86  QGraphicsView::contextMenuEvent (event);
87 }
88 
89 void GraphicsView::dragEnterEvent (QDragEnterEvent *event)
90 {
91  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dragEnterEvent " << (event->mimeData ()->hasUrls () ? "urls" : "non-urls");
92 
93  if (event->mimeData ()->hasImage () ||
94  event->mimeData ()->hasUrls ()) {
95  event->acceptProposedAction();
96  }
97 }
98 
99 void GraphicsView::dragMoveEvent (QDragMoveEvent *event)
100 {
101  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dragMoveEvent";
102 
103  if (event->mimeData ()->hasImage () ||
104  event->mimeData ()->hasUrls ()) {
105  event->acceptProposedAction();
106  }
107 }
108 
109 void GraphicsView::dropEvent (QDropEvent *event)
110 {
111  const QString MIME_FORMAT_TEXT_PLAIN ("text/plain");
112 
113  // Urls from text/uri-list
114  QList<QUrl> urlList = event->mimeData ()->urls ();
115  QString urls;
116  QTextStream str (&urls);
117  QList<QUrl>::const_iterator itr;
118  for (itr = urlList.begin (); itr != urlList.end (); itr++) {
119  QUrl url = *itr;
120  str << " url=" << url.toString () << " ";
121  }
122 
123  QString textPlain (event->mimeData()->data (MIME_FORMAT_TEXT_PLAIN));
124 
125  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dropEvent"
126  << " formats=(" << event->mimeData()->formats().join (", ").toLatin1().data() << ")"
127  << " hasUrls=" << (event->mimeData()->hasUrls() ? "yes" : "no")
128  << " urlCount=" << urlList.count()
129  << " urls=(" << urls.toLatin1().data() << ")"
130  << " text=" << textPlain.toLatin1().data()
131  << " hasImage=" << (event->mimeData()->hasImage() ? "yes" : "no");
132 
133  LoadFileInfo loadFileInfo;
134  if (loadFileInfo.loadsAsDigFile (textPlain)) {
135 
136  LOG4CPP_INFO_S ((*mainCat)) << "QGraphicsView::dropEvent dig file";
137  QUrl url (textPlain);
138  emit signalDraggedDigFile (url.toLocalFile());
139  event->acceptProposedAction();
140 
141  } else if (event->mimeData ()->hasImage ()) {
142 
143  // This branch never seems to get executed, but will be kept in case it ever applies (since hasUrls branch is messier and less reliable)
144  QImage image = qvariant_cast<QImage> (event->mimeData ()->imageData ());
145  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dropEvent image";
146  emit signalDraggedImage (image);
147 
148  } else if (event->mimeData ()->hasUrls () &&
149  urlList.count () > 0) {
150 
151  // Sometimes images can be dragged in, but sometimes the url points to an html page that
152  // contains just the image, in which case importing will fail
153  QUrl url = urlList.at(0);
154  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dropEvent url=" << url.toString ().toLatin1 ().data ();
155  emit signalDraggedImageUrl (url);
156  event->acceptProposedAction();
157 
158  } else {
159 
160  LOG4CPP_INFO_S ((*mainCat)) << "GraphicsView::dropEvent dropped";
161  QGraphicsView::dropEvent (event);
162 
163  }
164 }
165 
166 bool GraphicsView::inBounds (const QPointF &posScreen)
167 {
168  QRectF boundingRect = scene()->sceneRect();
169 
170  return 0 <= posScreen.x () &&
171  0 <= posScreen.y () &&
172  posScreen.x () < boundingRect.width() &&
173  posScreen.y () < boundingRect.height();
174 }
175 
176 void GraphicsView::keyPressEvent (QKeyEvent *event)
177 {
178  LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::keyPressEvent";
179 
180  // Intercept up/down/left/right if any items are selected
181  Qt::Key key = (Qt::Key) event->key();
182 
183  bool atLeastOneSelectedItem = (scene ()->selectedItems ().count () > 0);
184 
185  if (key == Qt::Key_Down ||
186  key == Qt::Key_Left ||
187  key == Qt::Key_Right ||
188  key == Qt::Key_Up) {
189 
190  emit signalKeyPress (key, atLeastOneSelectedItem);
191  event->accept();
192 
193  } else {
194 
195  QGraphicsView::keyPressEvent (event);
196 
197  }
198 }
199 
200 void GraphicsView::leaveEvent (QEvent *event)
201 {
202  LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::leaveEvent";
203 
204  emit signalLeave ();
205 
206  QGraphicsView::leaveEvent (event);
207 }
208 
209 void GraphicsView::mouseMoveEvent (QMouseEvent *event)
210 {
211 // LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::mouseMoveEvent cursor="
212 // << QtCursorToString (cursor().shape()).toLatin1 ().data ();
213 
214  QPointF posScreen = mapToScene (event->pos ());
215 
216  if (!inBounds (posScreen)) {
217 
218  // Set to out-of-bounds value
219  posScreen = QPointF (-1.0, -1.0);
220  }
221 
222  emit signalMouseMove (posScreen);
223 
224  QGraphicsView::mouseMoveEvent (event);
225 }
226 
227 void GraphicsView::mousePressEvent (QMouseEvent *event)
228 {
229  LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::mousePressEvent";
230 
231  QPointF posScreen = mapToScene (event->pos ());
232 
233  if (!inBounds (posScreen)) {
234 
235  // Set to out-of-bounds value
236  posScreen = QPointF (-1.0, -1.0);
237  }
238 
239  emit signalMousePress (posScreen);
240 
241  QGraphicsView::mousePressEvent (event);
242 }
243 
244 void GraphicsView::mouseReleaseEvent (QMouseEvent *event)
245 {
246  LOG4CPP_DEBUG_S ((*mainCat)) << "GraphicsView::mouseReleaseEvent signalMouseRelease";
247 
248  QPointF posScreen = mapToScene (event->pos ());
249 
250  if (!inBounds (posScreen)) {
251 
252  // Set to out-of-bounds value
253  posScreen = QPointF (-1.0, -1.0);
254  }
255 
256  // Send a signal, unless this is a right click. We still send if out of bounds since
257  // a click-and-drag often ends out of bounds (and user is unlikely to expect different
258  // behavior when endpoint is outside, versus inside, the image boundary)
259  int bitFlag = (event->buttons () & Qt::RightButton);
260  bool isRightClick = (bitFlag != 0);
261 
262  if (!isRightClick) {
263 
264 
265  emit signalMouseRelease (posScreen);
266 
267  }
268 
269  QGraphicsView::mouseReleaseEvent (event);
270 }
void signalMouseMove(QPointF)
Send mouse move to MainWindow for eventual display of cursor coordinates in StatusBar.
static QString curveNameFromPointIdentifier(const QString &pointIdentifier)
Parse the curve name from the specified point identifier. This does the opposite of uniqueIdentifierG...
Definition: Point.cpp:204
Returns information about files.
Definition: LoadFileInfo.h:7
virtual void keyPressEvent(QKeyEvent *event)
Intercept key press events to handle left/right/up/down moving.
virtual void dragMoveEvent(QDragMoveEvent *event)
Intercept mouse move event to support drag-and-drop.
virtual void dropEvent(QDropEvent *event)
Intercept mouse drop event to support drag-and-drop. This initiates asynchronous loading of the dragg...
void contextMenuEvent(QContextMenuEvent *event)
Intercept right click to support point editing.
virtual void mouseMoveEvent(QMouseEvent *event)
Intercept mouse move events to populate the current cursor position in StatusBar. ...
GraphicsView(QGraphicsScene *scene, MainWindow &mainWindow)
Single constructor.
void signalMousePress(QPointF)
Send mouse press to MainWindow for creating one or more Points.
void signalKeyPress(Qt::Key, bool atLeastOneSelectedItem)
Send keypress to MainWindow for eventual processing by DigitizeStateAbstractBase subclasses.
virtual void mousePressEvent(QMouseEvent *event)
Intercept mouse press events to create one or more Points.
void signalDraggedImage(QImage)
Send dragged image to MainWindow for import. This typically comes from dragging a file...
virtual void leaveEvent(QEvent *event)
Intercept leave events to manage override cursor.
bool loadsAsDigFile(const QString &urlString) const
Returns true if specified file name can be loaded as a DIG file.
void signalDraggedDigFile(QString)
Send dragged dig file to MainWindow for import. This comes from dragging an engauge dig file...
void signalMouseRelease(QPointF)
Send mouse release to MainWindow for moving Points.
virtual void dragEnterEvent(QDragEnterEvent *event)
Intercept mouse drag event to support drag-and-drop.
virtual void mouseReleaseEvent(QMouseEvent *event)
Intercept mouse release events to move one or more Points.
void signalDraggedImageUrl(QUrl)
Send dragged url to MainWindow for import. This typically comes from dragging an image from a browser...
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:66
void signalLeave()
Send leave to MainWindow for managing the override cursor.
void signalContextMenuEvent(QString pointIdentifier)
Send right click on axis point to MainWindow for editing.