1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 1997 Martin Jones <mjones@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "kxyselector.h"
9 
10 #include "loggingcategory.h"
11 
12 #include <QMouseEvent>
13 #include <QPainter>
14 #include <QStyle>
15 #include <QStyleOptionFrame>
16 
17 //-----------------------------------------------------------------------------
18 /*
19  * 2D value selector.
20  * The contents of the selector are drawn by derived class.
21  */
22 
23 class KXYSelectorPrivate
24 {
25 public:
KXYSelectorPrivate(KXYSelector * qq)26     KXYSelectorPrivate(KXYSelector *qq)
27         : q(qq)
28         , xPos(0)
29         , yPos(0)
30         , minX(0)
31         , maxX(100)
32         , minY(0)
33         , maxY(100)
34         , m_markerColor(Qt::white)
35     {
36     }
37 
38     void setValues(int _xPos, int _yPos);
39 
40     KXYSelector *const q;
41     int px;
42     int py;
43     int xPos;
44     int yPos;
45     int minX;
46     int maxX;
47     int minY;
48     int maxY;
49     QColor m_markerColor;
50 };
51 
KXYSelector(QWidget * parent)52 KXYSelector::KXYSelector(QWidget *parent)
53     : QWidget(parent)
54     , d(new KXYSelectorPrivate(this))
55 {
56 }
57 
58 KXYSelector::~KXYSelector() = default;
59 
xValue() const60 int KXYSelector::xValue() const
61 {
62     return d->xPos;
63 }
64 
yValue() const65 int KXYSelector::yValue() const
66 {
67     return d->yPos;
68 }
69 
setRange(int _minX,int _minY,int _maxX,int _maxY)70 void KXYSelector::setRange(int _minX, int _minY, int _maxX, int _maxY)
71 {
72     if (_maxX == _minX) {
73         qCWarning(KWidgetsAddonsLog) << "KXYSelector::setRange invalid range: " << _maxX << " == " << _minX << " (for X) ";
74         return;
75     }
76     if (_maxY == _minY) {
77         qCWarning(KWidgetsAddonsLog) << "KXYSelector::setRange invalid range: " << _maxY << " == " << _minY << " (for Y) ";
78         return;
79     }
80 
81     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
82     d->px = w;
83     d->py = w;
84     d->minX = _minX;
85     d->minY = _minY;
86     d->maxX = _maxX;
87     d->maxY = _maxY;
88 }
89 
setXValue(int _xPos)90 void KXYSelector::setXValue(int _xPos)
91 {
92     setValues(_xPos, d->yPos);
93 }
94 
setYValue(int _yPos)95 void KXYSelector::setYValue(int _yPos)
96 {
97     setValues(d->xPos, _yPos);
98 }
99 
setValues(int _xPos,int _yPos)100 void KXYSelector::setValues(int _xPos, int _yPos)
101 {
102     d->setValues(_xPos, _yPos);
103 }
104 
setValues(int _xPos,int _yPos)105 void KXYSelectorPrivate::setValues(int _xPos, int _yPos)
106 {
107     int w = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
108 
109     xPos = _xPos;
110     yPos = _yPos;
111 
112     if (xPos > maxX) {
113         xPos = maxX;
114     } else if (xPos < minX) {
115         xPos = minX;
116     }
117 
118     if (yPos > maxY) {
119         yPos = maxY;
120     } else if (yPos < minY) {
121         yPos = minY;
122     }
123 
124     Q_ASSERT(maxX != minX);
125     int xp = w + (q->width() - 2 * w) * xPos / (maxX - minX);
126 
127     Q_ASSERT(maxY != minY);
128     int yp = q->height() - w - (q->height() - 2 * w) * yPos / (maxY - minY);
129 
130     q->setPosition(xp, yp);
131 }
132 
setMarkerColor(const QColor & col)133 void KXYSelector::setMarkerColor(const QColor &col)
134 {
135     d->m_markerColor = col;
136 }
137 
contentsRect() const138 QRect KXYSelector::contentsRect() const
139 {
140     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
141     return rect().adjusted(w, w, -w, -w);
142 }
143 
minimumSizeHint() const144 QSize KXYSelector::minimumSizeHint() const
145 {
146     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
147     return QSize(2 * w, 2 * w);
148 }
149 
paintEvent(QPaintEvent *)150 void KXYSelector::paintEvent(QPaintEvent * /* ev */)
151 {
152     QStyleOptionFrame opt;
153     opt.initFrom(this);
154 
155     QPainter painter;
156     painter.begin(this);
157 
158     drawContents(&painter);
159     drawMarker(&painter, d->px, d->py);
160 
161     style()->drawPrimitive(QStyle::PE_Frame, &opt, &painter, this);
162 
163     painter.end();
164 }
165 
mousePressEvent(QMouseEvent * e)166 void KXYSelector::mousePressEvent(QMouseEvent *e)
167 {
168     mouseMoveEvent(e);
169 }
170 
mouseMoveEvent(QMouseEvent * e)171 void KXYSelector::mouseMoveEvent(QMouseEvent *e)
172 {
173     int xVal;
174     int yVal;
175     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
176     valuesFromPosition(e->pos().x() - w, e->pos().y() - w, xVal, yVal);
177     setValues(xVal, yVal);
178 
179     Q_EMIT valueChanged(d->xPos, d->yPos);
180 }
181 
wheelEvent(QWheelEvent * e)182 void KXYSelector::wheelEvent(QWheelEvent *e)
183 {
184     setValues(xValue() + e->angleDelta().x() / 120, yValue() + e->angleDelta().y() / 120);
185     Q_EMIT valueChanged(d->xPos, d->yPos);
186 }
187 
valuesFromPosition(int x,int y,int & xVal,int & yVal) const188 void KXYSelector::valuesFromPosition(int x, int y, int &xVal, int &yVal) const
189 {
190     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
191 
192     xVal = ((d->maxX - d->minX) * (x - w)) / (width() - 2 * w);
193     yVal = d->maxY - (((d->maxY - d->minY) * (y - w)) / (height() - 2 * w));
194 
195     if (xVal > d->maxX) {
196         xVal = d->maxX;
197     } else if (xVal < d->minX) {
198         xVal = d->minX;
199     }
200 
201     if (yVal > d->maxY) {
202         yVal = d->maxY;
203     } else if (yVal < d->minY) {
204         yVal = d->minY;
205     }
206 }
207 
setPosition(int xp,int yp)208 void KXYSelector::setPosition(int xp, int yp)
209 {
210     int w = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
211 
212     if (xp < w) {
213         xp = w;
214     } else if (xp > width() - w) {
215         xp = width() - w;
216     }
217 
218     if (yp < w) {
219         yp = w;
220     } else if (yp > height() - w) {
221         yp = height() - w;
222     }
223 
224     d->px = xp;
225     d->py = yp;
226 
227     update();
228 }
229 
drawContents(QPainter *)230 void KXYSelector::drawContents(QPainter *)
231 {
232 }
233 
drawMarker(QPainter * p,int xp,int yp)234 void KXYSelector::drawMarker(QPainter *p, int xp, int yp)
235 {
236     QPen pen(d->m_markerColor);
237     p->setPen(pen);
238 
239     /*
240       p->drawLine( xp - 6, yp - 6, xp - 2, yp - 2 );
241       p->drawLine( xp - 6, yp + 6, xp - 2, yp + 2 );
242       p->drawLine( xp + 6, yp - 6, xp + 2, yp - 2 );
243       p->drawLine( xp + 6, yp + 6, xp + 2, yp + 2 );
244     */
245     p->drawEllipse(xp - 4, yp - 4, 8, 8);
246 }
247