File indexing completed on 2025-01-26 05:09:31

0001 /*
0002  * This file is part of the KDE wacomtablet project. For copyright
0003  * information and license terms see the AUTHORS and COPYING files
0004  * in the top-level directory of this distribution.
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of
0009  * the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "pressurecurvewidget.h"
0021 
0022 // Qt includes
0023 #include <QDebug>
0024 #include <QMouseEvent>
0025 #include <QPainter>
0026 #include <QPainterPath>
0027 #include <QResizeEvent>
0028 #include <QTabletEvent>
0029 
0030 using namespace Wacom;
0031 
0032 PressureCurveWidget::PressureCurveWidget(QWidget *parent)
0033     : QWidget(parent)
0034 {
0035     setBackgroundRole(QPalette::Base);
0036     setAutoFillBackground(true);
0037 }
0038 
0039 void PressureCurveWidget::setControlPoints(qreal p1, qreal p2, qreal p3, qreal p4)
0040 {
0041     // change y values upside down (xsetwacom has 0,0 in the lower left QWidget in the upper left)
0042     p2 = 100 - p2;
0043     p4 = 100 - p4;
0044     m_cP1 = QPointF((p1 / 100.0) * width(), (p2 / 100.0) * height());
0045     m_cP2 = QPointF((p3 / 100.0) * width(), (p4 / 100.0) * height());
0046 }
0047 
0048 void PressureCurveWidget::mousePressEvent(QMouseEvent *event)
0049 {
0050     setNearestPoint(event->localPos());
0051 }
0052 
0053 void PressureCurveWidget::mouseMoveEvent(QMouseEvent *event)
0054 {
0055     moveControlPoint(event->localPos());
0056     update();
0057 }
0058 
0059 void PressureCurveWidget::mouseReleaseEvent(QMouseEvent *event)
0060 {
0061     Q_UNUSED(event);
0062     m_activePoint = 0;
0063 }
0064 
0065 void PressureCurveWidget::resizeEvent(QResizeEvent *event)
0066 {
0067     // avoid unpredictable ratio on first initialisation
0068     if (event->oldSize().width() == -1 || event->oldSize().width() == 0 || event->oldSize().height() == 0) {
0069         return;
0070     }
0071 
0072     const qreal xRatio = static_cast<qreal>(event->size().width()) / event->oldSize().width();
0073     const qreal yRatio = static_cast<qreal>(event->size().height()) / event->oldSize().height();
0074 
0075     m_cP1.setX(m_cP1.x() * xRatio);
0076     m_cP1.setY(m_cP1.y() * yRatio);
0077     m_cP2.setX(m_cP2.x() * xRatio);
0078     m_cP2.setY(m_cP2.y() * yRatio);
0079 }
0080 
0081 void PressureCurveWidget::tabletEvent(QTabletEvent *event)
0082 {
0083     event->accept();
0084     m_pressure = event->pressure();
0085 
0086     constexpr qreal threshold = 0.001;
0087     if (m_pressure <= threshold) {
0088         m_activePoint = 0;
0089     }
0090 
0091     if (m_activePoint > 0) {
0092         moveControlPoint(event->pos());
0093     } else if (m_pressure > threshold) {
0094         setNearestPoint(event->pos());
0095     }
0096 
0097     update();
0098 }
0099 
0100 void PressureCurveWidget::setNearestPoint(const QPointF &pos)
0101 {
0102     qreal d1 = QLineF(pos, m_cP1).length();
0103     qreal d2 = QLineF(pos, m_cP2).length();
0104 
0105     if (d1 < m_pointSize) {
0106         m_activePoint = 1;
0107     } else if (d2 < m_pointSize) {
0108         m_activePoint = 2;
0109     }
0110 }
0111 
0112 void PressureCurveWidget::moveControlPoint(const QPointF &pos)
0113 {
0114     int x;
0115     int y;
0116 
0117     if (pos.x() > width()) {
0118         x = width();
0119     } else if (pos.x() < 0) {
0120         x = 0;
0121     } else {
0122         x = qRound(pos.x());
0123     }
0124 
0125     if (pos.y() > height()) {
0126         y = height();
0127     } else if (pos.y() < 0) {
0128         y = 0;
0129     } else {
0130         y = qRound(pos.y());
0131     }
0132 
0133     switch (m_activePoint) {
0134     case 1:
0135         m_cP1 = QPoint(x, y);
0136         m_cP2 = QPoint(y, x); // seems xsetwacom is expecting to be both controllpoints the same with switches xy values
0137         // makes no sense to me, but otherwise the wacom driver complains
0138         // the wacom_utility does it the same way though
0139         break;
0140     case 2:
0141         m_cP2 = QPoint(x, y);
0142         m_cP1 = QPoint(y, x); // see above
0143         break;
0144     }
0145 
0146     // build string with controlpoints as used in xsetwacom settings
0147     int p1 = qRound((m_cP1.x() / width()) * 100.0);
0148     int p2 = qRound((m_cP1.y() / height()) * 100.0);
0149     int p3 = qRound((m_cP2.x() / width()) * 100.0);
0150     int p4 = qRound((m_cP2.y() / height()) * 100.0);
0151 
0152     // change y values upside down
0153     p2 = 100 - p2;
0154     p4 = 100 - p4;
0155     QString pointsString = QString::fromLatin1("%1 %2 %3 %4").arg(p1).arg(p2).arg(p3).arg(p4);
0156 
0157     emit controlPointsChanged(pointsString);
0158 }
0159 
0160 void PressureCurveWidget::paintEvent(QPaintEvent *event)
0161 {
0162     Q_UNUSED(event);
0163 
0164     QPainter painter(this);
0165     painter.setRenderHint(QPainter::Antialiasing, true);
0166     painter.translate(+0.5, +0.5);
0167 
0168     // draw the background grid
0169     int yStep = height() / 10;
0170     int xStep = width() / 10;
0171     painter.setPen(QColor(Qt::gray));
0172 
0173     for (int i = 1; i < 10; i++) {
0174         painter.drawLine(i * xStep, 0, i * xStep, height());
0175         painter.drawLine(0, i * yStep, width(), i * yStep);
0176     }
0177 
0178     // draw controllpoint lines
0179     painter.setPen(QColor(Qt::gray));
0180     painter.drawLine(m_cP1, QPoint(0, height()));
0181     painter.drawLine(m_cP2, QPoint(width(), 0));
0182 
0183     // create presscurve
0184     QPainterPath curvePath;
0185     curvePath.moveTo(0, height());
0186     curvePath.cubicTo(m_cP1, m_cP2, QPoint(width(), 0));
0187 
0188     // create polygon for the area below the curve
0189     QPainterPath areaBelowCurve(curvePath);
0190     areaBelowCurve.lineTo(width(), height());
0191     areaBelowCurve.lineTo(0, height());
0192     QRectF clearRect(m_pressure * width(), 0, width(), height());
0193     QPainterPath subtract;
0194     subtract.addRect(clearRect);
0195 
0196     // draw below curve area
0197     painter.setPen(QPen());
0198     painter.setBrush(QColor(0, 102, 255));
0199     painter.drawPath(areaBelowCurve.subtracted(subtract));
0200 
0201     // draw presscurve
0202     QPen curvePen;
0203     curvePen.setWidth(2);
0204     curvePen.setColor(m_curveColor);
0205     painter.setPen(curvePen);
0206     painter.setBrush(QBrush());
0207     painter.drawPath(curvePath);
0208 
0209     // draw controllpoints
0210     painter.setPen(QColor(226, 8, 0));
0211     painter.setBrush(QColor(226, 8, 0));
0212     painter.drawEllipse(m_cP1, m_pointSize, m_pointSize);
0213     painter.drawEllipse(m_cP2, m_pointSize, m_pointSize);
0214 }
0215 
0216 #include "moc_pressurecurvewidget.cpp"