00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 #ifdef QT_OPENGL_SUPPORT
00043 #include <QGLWidget>
00044 #endif
00045
00046 #include "hoverpoints.h"
00047
00048 #define printf
00049
00050 HoverPoints::HoverPoints(QWidget *widget, PointShape shape)
00051 : QObject(widget)
00052 {
00053 m_widget = widget;
00054 widget->installEventFilter(this);
00055 widget->setAttribute(Qt::WA_AcceptTouchEvents);
00056
00057 m_connectionType = CurveConnection;
00058 m_sortType = NoSort;
00059 m_shape = shape;
00060 m_pointPen = QPen(QColor(255, 255, 255, 191), 1);
00061 m_connectionPen = QPen(QColor(255, 255, 255, 127), 2);
00062 m_pointBrush = QBrush(QColor(191, 191, 191, 127));
00063 m_pointSize = QSize(11, 11);
00064 m_currentIndex = -1;
00065 m_editable = true;
00066 m_enabled = true;
00067
00068 connect(this, SIGNAL(pointsChanged(QPolygonF)),
00069 m_widget, SLOT(update()));
00070 }
00071
00072
00073 void HoverPoints::setEnabled(bool enabled)
00074 {
00075 if (m_enabled != enabled) {
00076 m_enabled = enabled;
00077 m_widget->update();
00078 }
00079 }
00080
00081
00082 bool HoverPoints::eventFilter(QObject *object, QEvent *event)
00083 {
00084 if (object == m_widget && m_enabled) {
00085 switch (event->type()) {
00086
00087 case QEvent::MouseButtonPress:
00088 {
00089 if (!m_fingerPointMapping.isEmpty())
00090 return true;
00091 QMouseEvent *me = (QMouseEvent *) event;
00092
00093 QPointF clickPos = me->pos();
00094 int index = -1;
00095 for (int i=0; i<m_points.size(); ++i) {
00096 QPainterPath path;
00097 if (m_shape == CircleShape)
00098 path.addEllipse(pointBoundingRect(i));
00099 else
00100 path.addRect(pointBoundingRect(i));
00101
00102 if (path.contains(clickPos)) {
00103 index = i;
00104 break;
00105 }
00106 }
00107
00108 if (me->button() == Qt::LeftButton) {
00109 if (index == -1) {
00110 if (!m_editable)
00111 return false;
00112 int pos = 0;
00113
00114 if (m_sortType == XSort) {
00115 for (int i=0; i<m_points.size(); ++i)
00116 if (m_points.at(i).x() > clickPos.x()) {
00117 pos = i;
00118 break;
00119 }
00120 } else if (m_sortType == YSort) {
00121 for (int i=0; i<m_points.size(); ++i)
00122 if (m_points.at(i).y() > clickPos.y()) {
00123 pos = i;
00124 break;
00125 }
00126 }
00127 m_points.insert(pos, clickPos);
00128 m_locks.insert(pos, 0);
00129 m_currentIndex = pos;
00130 firePointChange();
00131 } else {
00132 m_currentIndex = index;
00133 }
00134 return true;
00135
00136 } else if (me->button() == Qt::RightButton) {
00137 if (index >= 0 && m_editable) {
00138 if (m_locks[index] == 0) {
00139 m_locks.remove(index);
00140 m_points.remove(index);
00141 }
00142 firePointChange();
00143 return true;
00144 }
00145 }
00146
00147 }
00148 break;
00149
00150 case QEvent::MouseButtonRelease:
00151 if (!m_fingerPointMapping.isEmpty())
00152 return true;
00153 m_currentIndex = -1;
00154 break;
00155
00156 case QEvent::MouseMove:
00157 if (!m_fingerPointMapping.isEmpty())
00158 return true;
00159 if (m_currentIndex >= 0)
00160 movePoint(m_currentIndex, ((QMouseEvent *)event)->pos());
00161 break;
00162 case QEvent::TouchBegin:
00163 case QEvent::TouchUpdate:
00164 {
00165 const QTouchEvent *const touchEvent = static_cast<const QTouchEvent*>(event);
00166 const QList<QTouchEvent::TouchPoint> points = touchEvent->touchPoints();
00167 const qreal pointSize = qMax(m_pointSize.width(), m_pointSize.height());
00168 foreach (const QTouchEvent::TouchPoint &touchPoint, points) {
00169 const int id = touchPoint.id();
00170 switch (touchPoint.state()) {
00171 case Qt::TouchPointPressed:
00172 {
00173
00174 QSet<int> activePoints = QSet<int>::fromList(m_fingerPointMapping.values());
00175 int activePoint = -1;
00176 qreal distance = -1;
00177 const int pointsCount = m_points.size();
00178 const int activePointCount = activePoints.size();
00179 if (pointsCount == 2 && activePointCount == 1) {
00180 activePoint = activePoints.contains(0) ? 1 : 0;
00181 } else {
00182 for (int i=0; i<pointsCount; ++i) {
00183 if (activePoints.contains(i))
00184 continue;
00185
00186 qreal d = QLineF(touchPoint.pos(), m_points.at(i)).length();
00187 if ((distance < 0 && d < 12 * pointSize) || d < distance) {
00188 distance = d;
00189 activePoint = i;
00190 }
00191
00192 }
00193 }
00194 if (activePoint != -1) {
00195 m_fingerPointMapping.insert(touchPoint.id(), activePoint);
00196 movePoint(activePoint, touchPoint.pos());
00197 }
00198 }
00199 break;
00200 case Qt::TouchPointReleased:
00201 {
00202
00203 QHash<int,int>::iterator it = m_fingerPointMapping.find(id);
00204 movePoint(it.value(), touchPoint.pos());
00205 m_fingerPointMapping.erase(it);
00206 }
00207 break;
00208 case Qt::TouchPointMoved:
00209 {
00210
00211 const int pointIdx = m_fingerPointMapping.value(id, -1);
00212 if (pointIdx >= 0)
00213 movePoint(pointIdx, touchPoint.pos());
00214 }
00215 break;
00216 default:
00217 break;
00218 }
00219 }
00220 if (m_fingerPointMapping.isEmpty()) {
00221 event->ignore();
00222 return false;
00223 } else {
00224 return true;
00225 }
00226 }
00227 break;
00228 case QEvent::TouchEnd:
00229 if (m_fingerPointMapping.isEmpty()) {
00230 event->ignore();
00231 return false;
00232 }
00233 return true;
00234 break;
00235
00236 case QEvent::Resize:
00237 {
00238 QResizeEvent *e = (QResizeEvent *) event;
00239 if (e->oldSize().width() == 0 || e->oldSize().height() == 0)
00240 break;
00241 qreal stretch_x = e->size().width() / qreal(e->oldSize().width());
00242 qreal stretch_y = e->size().height() / qreal(e->oldSize().height());
00243 if (stretch_x<=0.0)
00244 {
00245 stretch_x=1.0;
00246 }
00247 if (stretch_y<=0.0)
00248 {
00249 stretch_y=1.0;
00250 }
00251 for (int i=0; i<m_points.size(); ++i) {
00252 QPointF p = m_points[i];
00253 movePoint(i, QPointF(p.x() * stretch_x, p.y() * stretch_y), false);
00254 }
00255
00256 firePointChange();
00257 break;
00258 }
00259
00260 case QEvent::Paint:
00261 {
00262 QWidget *that_widget = m_widget;
00263 m_widget = 0;
00264 QApplication::sendEvent(object, event);
00265 m_widget = that_widget;
00266 paintPoints();
00267 #ifdef QT_OPENGL_SUPPORT
00268 ArthurFrame *af = qobject_cast<ArthurFrame *>(that_widget);
00269 if (af && af->usesOpenGL())
00270 af->glWidget()->swapBuffers();
00271 #endif
00272 return true;
00273 }
00274 default:
00275 break;
00276 }
00277 }
00278
00279 return false;
00280 }
00281
00282
00283 void HoverPoints::paintPoints()
00284 {
00285 QPainter p;
00286 #ifdef QT_OPENGL_SUPPORT
00287 ArthurFrame *af = qobject_cast<ArthurFrame *>(m_widget);
00288 if (af && af->usesOpenGL())
00289 p.begin(af->glWidget());
00290 else
00291 p.begin(m_widget);
00292 #else
00293 p.begin(m_widget);
00294 #endif
00295
00296 p.setRenderHint(QPainter::Antialiasing);
00297
00298 if (m_connectionPen.style() != Qt::NoPen && m_connectionType != NoConnection) {
00299 p.setPen(m_connectionPen);
00300
00301 if (m_connectionType == CurveConnection) {
00302 QPainterPath path;
00303 path.moveTo(m_points.at(0));
00304 for (int i=1; i<m_points.size(); ++i) {
00305 QPointF p1 = m_points.at(i-1);
00306 QPointF p2 = m_points.at(i);
00307 qreal distance = p2.x() - p1.x();
00308
00309 path.cubicTo(p1.x() + distance / 2, p1.y(),
00310 p1.x() + distance / 2, p2.y(),
00311 p2.x(), p2.y());
00312 }
00313 p.drawPath(path);
00314 } else {
00315 p.drawPolyline(m_points);
00316 }
00317 }
00318
00319 p.setPen(m_pointPen);
00320 p.setBrush(m_pointBrush);
00321
00322 for (int i=0; i<m_points.size(); ++i) {
00323 QRectF bounds = pointBoundingRect(i);
00324 if (m_shape == CircleShape)
00325 p.drawEllipse(bounds);
00326 else
00327 p.drawRect(bounds);
00328 }
00329 }
00330
00331 static QPointF bound_point(const QPointF &point, const QRectF &bounds, int lock)
00332 {
00333 QPointF p = point;
00334
00335 qreal left = bounds.left();
00336 qreal right = bounds.right();
00337 qreal top = bounds.top();
00338 qreal bottom = bounds.bottom();
00339
00340 if (p.x() < left || (lock & HoverPoints::LockToLeft)) p.setX(left);
00341 else if (p.x() > right || (lock & HoverPoints::LockToRight)) p.setX(right);
00342
00343 if (p.y() < top || (lock & HoverPoints::LockToTop)) p.setY(top);
00344 else if (p.y() > bottom || (lock & HoverPoints::LockToBottom)) p.setY(bottom);
00345
00346 return p;
00347 }
00348
00349 void HoverPoints::setPoints(const QPolygonF &points)
00350 {
00351 if (points.size() != m_points.size())
00352 m_fingerPointMapping.clear();
00353 m_points.clear();
00354 for (int i=0; i<points.size(); ++i)
00355 m_points << bound_point(points.at(i), boundingRect(), 0);
00356
00357 m_locks.clear();
00358 if (m_points.size() > 0) {
00359 m_locks.resize(m_points.size());
00360
00361 m_locks.fill(0);
00362 }
00363 }
00364
00365
00366 void HoverPoints::movePoint(int index, const QPointF &point, bool emitUpdate)
00367 {
00368 m_points[index] = bound_point(point, boundingRect(), m_locks.at(index));
00369
00370 if (index==0)
00371 {
00372 m_points[m_points.count()-1].setY(m_points[index].y());
00373 }else if (index==m_points.count()-1)
00374 {
00375 m_points[0].setY(m_points[index].y());
00376 }
00377
00378 if (emitUpdate)
00379 firePointChange();
00380 }
00381
00382
00383 inline static bool x_less_than(const QPointF &p1, const QPointF &p2)
00384 {
00385 return p1.x() < p2.x();
00386 }
00387
00388
00389 inline static bool y_less_than(const QPointF &p1, const QPointF &p2)
00390 {
00391 return p1.y() < p2.y();
00392 }
00393
00394 void HoverPoints::firePointChange()
00395 {
00396
00397
00398 if (m_sortType != NoSort) {
00399
00400 QPointF oldCurrent;
00401 if (m_currentIndex != -1) {
00402 oldCurrent = m_points[m_currentIndex];
00403 }
00404
00405 if (m_sortType == XSort)
00406 qSort(m_points.begin(), m_points.end(), x_less_than);
00407 else if (m_sortType == YSort)
00408 qSort(m_points.begin(), m_points.end(), y_less_than);
00409
00410
00411 if (m_currentIndex != -1) {
00412 for (int i=0; i<m_points.size(); ++i) {
00413 if (m_points[i] == oldCurrent) {
00414 m_currentIndex = i;
00415 break;
00416 }
00417 }
00418 }
00419
00420
00421
00422 }
00423
00424
00425
00426
00427
00428
00429 emit pointsChanged(m_points);
00430 }