VideoVis  0.9
Generates a volume visualisation of a video
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
hoverpoints.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #ifdef QT_OPENGL_SUPPORT
43 #include <QGLWidget>
44 #endif
45 
46 #include "arthurwidgets.h"
47 #include "hoverpoints.h"
48 #include "transferbox.h"
49 
50 
51 #define printf
52 
53 HoverPoints::HoverPoints(QWidget *widget, PointShape shape)
54  : QObject(widget)
55 {
56  m_widget = widget;
57  widget->installEventFilter(this);
58  widget->setAttribute(Qt::WA_AcceptTouchEvents);
59 
60  m_connectionType = CurveConnection;
61  m_sortType = NoSort;
62  m_shape = shape;
63  m_pointPen = QPen(QColor(255, 255, 255, 191), 1);
64  m_connectionPen = QPen(QColor(255, 255, 255, 127), 2);
65  m_pointBrush = QBrush(QColor(191, 191, 191, 127));
66  m_pointSize = QSize(11, 11);
67  m_currentIndex = -1;
68  m_editable = true;
69  m_enabled = true;
70  m_dialog = new QColorDialog();
71 
72  connect(this, SIGNAL(pointsChanged(QPolygonF)),
73  m_widget, SLOT(update()));
74 }
75 
76 
77 void HoverPoints::setEnabled(bool enabled)
78 {
79  if (m_enabled != enabled) {
80  m_enabled = enabled;
81  m_widget->update();
82  }
83 }
84 
85 
86 bool HoverPoints::eventFilter(QObject *object, QEvent *event)
87 {
88  if (object == m_widget && m_enabled) {
89  switch (event->type()) {
90  case QEvent::MouseButtonPress:
91  {
92  if (!m_fingerPointMapping.isEmpty())
93  return true;
94  QMouseEvent *me = (QMouseEvent *) event;
95  QPointF clickPos = me->pos();
96  int index = -1;
97  for (int i=0; i<m_points.size(); ++i) {
98  QPainterPath path;
99  if (m_shape == CircleShape) {
100  path.addEllipse(pointBoundingRect(i));
101  } else {
102  path.addRect(pointBoundingRect(i));
103  }
104  if (path.contains(clickPos)) {
105  index = i;
106  break;
107  }
108  }
109 
110  if (me->button() == Qt::LeftButton) {
111  if (index == -1) {
112  if (!m_editable)
113  return false;
114  int pos = 0;
115  // Insert sort for x or y
116  if (m_sortType == XSort) {
117  for (int i=0; i<m_points.size(); ++i)
118  if (m_points.at(i).x() > clickPos.x()) {
119  pos = i;
120  break;
121  }
122  } else if (m_sortType == YSort) {
123  for (int i=0; i<m_points.size(); ++i)
124  if (m_points.at(i).y() > clickPos.y()) {
125  pos = i;
126  break;
127  }
128  }
129 
130  QColor color = QColor("black");
131  m_points.insert(pos, clickPos);
132  m_locks.insert(pos, 0);
133  m_currentIndex = pos;
134  firePointChange();
135  TransferBox *box = (TransferBox*) m_widget;
136  box->addColor(m_points, color);
137  } else {
138  m_currentIndex = index;
139  }
140  return true;
141 
142  } else if (me->button() == Qt::RightButton) {
143  if (index >= 0 && m_editable) {
144  if (m_locks[index] == 0) {
145  m_locks.remove(index);
146  m_points.remove(index);
147  TransferBox *box = (TransferBox*) m_widget;
148  box->deleteColor(m_points);
149  }
150  firePointChange();
151  return true;
152  }
153  } else if (me->button() == Qt::MiddleButton) {
154  if (index >= 0 && m_editable) {
155  QColor color = QColorDialog::getColor();
156  if(color.isValid()) {
157  TransferBox *box = (TransferBox*) m_widget;
158  box->changeColor(index, color);
159  }
160  return true;
161  }
162  }
163 
164  }
165  break;
166 
167  case QEvent::MouseButtonRelease:
168  if (!m_fingerPointMapping.isEmpty())
169  return true;
170  m_currentIndex = -1;
171  break;
172 
173  case QEvent::MouseMove:
174  if (!m_fingerPointMapping.isEmpty())
175  return true;
176  if (m_currentIndex >= 0) {
177  movePoint(m_currentIndex, ((QMouseEvent *)event)->pos());
178  TransferBox *box = (TransferBox*) m_widget;
179  box->setPoints(m_points);
180  box->repaint();
181  }
182  break;
183  case QEvent::TouchBegin:
184  case QEvent::TouchUpdate:
185  {
186  const QTouchEvent *const touchEvent = static_cast<const QTouchEvent*>(event);
187  const QList<QTouchEvent::TouchPoint> points = touchEvent->touchPoints();
188  const qreal pointSize = qMax(m_pointSize.width(), m_pointSize.height());
189  foreach (const QTouchEvent::TouchPoint &touchPoint, points) {
190  const int id = touchPoint.id();
191  switch (touchPoint.state()) {
192  case Qt::TouchPointPressed:
193  {
194  // find the point, move it
195  QSet<int> activePoints = QSet<int>::fromList(m_fingerPointMapping.values());
196  int activePoint = -1;
197  qreal distance = -1;
198  const int pointsCount = m_points.size();
199  const int activePointCount = activePoints.size();
200  if (pointsCount == 2 && activePointCount == 1) { // only two points
201  activePoint = activePoints.contains(0) ? 1 : 0;
202  } else {
203  for (int i=0; i<pointsCount; ++i) {
204  if (activePoints.contains(i))
205  continue;
206 
207  qreal d = QLineF(touchPoint.pos(), m_points.at(i)).length();
208  if ((distance < 0 && d < 12 * pointSize) || d < distance) {
209  distance = d;
210  activePoint = i;
211  }
212 
213  }
214  }
215  if (activePoint != -1) {
216  m_fingerPointMapping.insert(touchPoint.id(), activePoint);
217  movePoint(activePoint, touchPoint.pos());
218  }
219  }
220  break;
221  case Qt::TouchPointReleased:
222  {
223  // move the point and release
224  QHash<int,int>::iterator it = m_fingerPointMapping.find(id);
225  movePoint(it.value(), touchPoint.pos());
226  m_fingerPointMapping.erase(it);
227  }
228  break;
229  case Qt::TouchPointMoved:
230  {
231  // move the point
232  const int pointIdx = m_fingerPointMapping.value(id, -1);
233  if (pointIdx >= 0) // do we track this point?
234  movePoint(pointIdx, touchPoint.pos());
235  }
236  break;
237  default:
238  break;
239  }
240  }
241  if (m_fingerPointMapping.isEmpty()) {
242  event->ignore();
243  return false;
244  } else {
245  return true;
246  }
247  }
248  break;
249  case QEvent::TouchEnd:
250  if (m_fingerPointMapping.isEmpty()) {
251  event->ignore();
252  return false;
253  }
254  return true;
255  break;
256 
257  case QEvent::Resize:
258  {
259  QResizeEvent *e = (QResizeEvent *) event;
260  if (e->oldSize().width() == 0 || e->oldSize().height() == 0)
261  break;
262  qreal stretch_x = e->size().width() / qreal(e->oldSize().width());
263  qreal stretch_y = e->size().height() / qreal(e->oldSize().height());
264  for (int i=0; i<m_points.size(); ++i) {
265  QPointF p = m_points[i];
266  movePoint(i, QPointF(p.x() * stretch_x, p.y() * stretch_y), false);
267  }
268 
269  firePointChange();
270  break;
271  }
272 
273  case QEvent::Paint:
274  {
275  QWidget *that_widget = m_widget;
276  m_widget = 0;
277  QApplication::sendEvent(object, event);
278  m_widget = that_widget;
279  paintPoints();
280 #ifdef QT_OPENGL_SUPPORT
281  ArthurFrame *af = qobject_cast<ArthurFrame *>(that_widget);
282  if (af && af->usesOpenGL())
283  af->glWidget()->swapBuffers();
284 #endif
285  return true;
286  }
287  default:
288  break;
289  }
290  }
291 
292  return false;
293 }
294 
295 
297 {
298  QPainter p;
299 #ifdef QT_OPENGL_SUPPORT
300  ArthurFrame *af = qobject_cast<ArthurFrame *>(m_widget);
301  if (af && af->usesOpenGL())
302  p.begin(af->glWidget());
303  else
304  p.begin(m_widget);
305 #else
306  p.begin(m_widget);
307 #endif
308 
309  p.setRenderHint(QPainter::Antialiasing);
310 
311  if (m_connectionPen.style() != Qt::NoPen && m_connectionType != NoConnection) {
312  p.setPen(m_connectionPen);
313 
314  if (m_connectionType == CurveConnection) {
315  QPainterPath path;
316  path.moveTo(m_points.at(0));
317  for (int i=1; i<m_points.size(); ++i) {
318  QPointF p1 = m_points.at(i-1);
319  QPointF p2 = m_points.at(i);
320  qreal distance = p2.x() - p1.x();
321 
322  path.cubicTo(p1.x() + distance / 2, p1.y(),
323  p1.x() + distance / 2, p2.y(),
324  p2.x(), p2.y());
325  }
326  p.drawPath(path);
327  } else {
328  p.drawPolyline(m_points);
329  }
330  }
331 
332  p.setPen(m_pointPen);
333  p.setBrush(m_pointBrush);
334 
335  for (int i=0; i<m_points.size(); ++i) {
336  QRectF bounds = pointBoundingRect(i);
337  if (m_shape == CircleShape)
338  p.drawEllipse(bounds);
339  else
340  p.drawRect(bounds);
341  }
342 }
343 
344 static QPointF bound_point(const QPointF &point, const QRectF &bounds, int lock)
345 {
346  QPointF p = point;
347 
348  qreal left = bounds.left();
349  qreal right = bounds.right();
350  qreal top = bounds.top();
351  qreal bottom = bounds.bottom();
352 
353  if (p.x() < left || (lock & HoverPoints::LockToLeft)) p.setX(left);
354  else if (p.x() > right || (lock & HoverPoints::LockToRight)) p.setX(right);
355 
356  if (p.y() < top || (lock & HoverPoints::LockToTop)) p.setY(top);
357  else if (p.y() > bottom || (lock & HoverPoints::LockToBottom)) p.setY(bottom);
358 
359  return p;
360 }
361 
362 void HoverPoints::setPoints(const QPolygonF &points)
363 {
364 
365  if (points.size() != m_points.size())
366  m_fingerPointMapping.clear();
367  m_points.clear();
368  for (int i=0; i<points.size(); ++i)
369  m_points << bound_point(points.at(i), boundingRect(), 0);
370 
371  m_locks.clear();
372  if (m_points.size() > 0) {
373  m_locks.resize(m_points.size());
374 
375  m_locks.fill(0);
376  }
377 }
378 
379 
380 void HoverPoints::movePoint(int index, const QPointF &point, bool emitUpdate)
381 {
382  m_points[index] = bound_point(point, boundingRect(), m_locks.at(index));
383  if (emitUpdate)
384  firePointChange();
385 }
386 
387 
388 inline static bool x_less_than(const QPointF &p1, const QPointF &p2)
389 {
390  return p1.x() < p2.x();
391 }
392 
393 
394 inline static bool y_less_than(const QPointF &p1, const QPointF &p2)
395 {
396  return p1.y() < p2.y();
397 }
398 
400 {
401  // printf("HoverPoints::firePointChange(), current=%d\n", m_currentIndex);
402 
403  if (m_sortType != NoSort) {
404 
405  QPointF oldCurrent;
406  if (m_currentIndex != -1) {
407  oldCurrent = m_points[m_currentIndex];
408  }
409 
410  if (m_sortType == XSort)
411  qSort(m_points.begin(), m_points.end(), x_less_than);
412  else if (m_sortType == YSort)
413  qSort(m_points.begin(), m_points.end(), y_less_than);
414 
415  // Compensate for changed order...
416  if (m_currentIndex != -1) {
417  for (int i=0; i<m_points.size(); ++i) {
418  if (m_points[i] == oldCurrent) {
419  m_currentIndex = i;
420  break;
421  }
422  }
423  }
424 
425  // printf(" - firePointChange(), current=%d\n", m_currentIndex);
426 
427  }
428 
429  // for (int i=0; i<m_points.size(); ++i) {
430  // printf(" - point(%2d)=[%.2f, %.2f], lock=%d\n",
431  // i, m_points.at(i).x(), m_points.at(i).y(), m_locks.at(i));
432  // }
433 
434  emit pointsChanged(m_points);
435 }