• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 14531411288

18 Apr 2025 07:15AM UTC coverage: 31.87% (-0.001%) from 31.871%
14531411288

push

github

web-flow
Merge pull request #1729 from Jurrie/bugfix/NPE_in_vcxypadarea_after_stop-all-functions

Prevent NPE with setEFXInterval(uint) after 'stop all functions' is hit

0 of 2 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

14690 of 46093 relevant lines covered (31.87%)

26441.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.47
/ui/src/virtualconsole/vcxypadarea.cpp
1
/*
2
  Q Light Controller
3
  vcxypadarea.cpp
4

5
  Copyright (c) Heikki Junnila, Stefan Krumm
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QMouseEvent>
21
#include <QPainter>
22
#include <QPixmap>
23
#include <QCursor>
24
#include <qmath.h>
25
#include <QMutex>
26
#include <QDebug>
27
#include <QPoint>
28

29
#include "qlcmacros.h"
30

31
#include "efxpreviewarea.h"
32
#include "vcxypadarea.h"
33
#include "vcframe.h"
34

35
const qreal MAX_VALUE = 256.0;
36
const qreal MAX_DMX_VALUE = MAX_VALUE - 1.0/256;
37

38
/*****************************************************************************
39
 * Initialization
40
 *****************************************************************************/
41

42
VCXYPadArea::VCXYPadArea(QWidget* parent)
14✔
43
    : QFrame(parent)
44
    , m_changed(false)
14✔
45
    , m_activePixmap(":/xypad-point-blue.png")
14✔
46
    , m_fixturePixmap(":/xypad-point.png")
14✔
47
    , m_rangeDmxRect()
48
    , m_rangeWindowRect()
49
    , m_degreesRange()
50
    , m_previewArea(NULL)
28✔
51
{
52
    setFrameStyle(KVCFrameStyleSunken);
14✔
53
    setWindowTitle("XY Pad");
14✔
54
    setMode(Doc::Design);
14✔
55
    setFocusPolicy(Qt::ClickFocus);
14✔
56
    new QVBoxLayout(this);
14✔
57
}
14✔
58

59
VCXYPadArea::~VCXYPadArea()
23✔
60
{
61
}
23✔
62

63
void VCXYPadArea::setMode(Doc::Mode mode)
30✔
64
{
65
    m_mode = mode;
30✔
66
    if (mode == Doc::Design)
30✔
67
        setEnabled(false);
25✔
68
    else
69
        setEnabled(true);
5✔
70
    update();
30✔
71
}
30✔
72

73
/*****************************************************************************
74
 * Current XY position
75
 *****************************************************************************/
76

77
QPointF VCXYPadArea::position(bool resetChanged) const
33✔
78
{
79
    QMutexLocker locker(&m_mutex);
33✔
80
    QPointF pos(m_dmxPos);
33✔
81
    if (resetChanged)
33✔
82
        m_changed = false;
22✔
83
    return pos;
33✔
84
}
85

86
void VCXYPadArea::setPosition(const QPointF& point)
13✔
87
{
88
    {
89
        QMutexLocker locker(&m_mutex);
13✔
90

91
        if (m_dmxPos != point)
13✔
92
        {
93
            m_dmxPos = point;
12✔
94

95
            if (m_dmxPos.x() > MAX_DMX_VALUE)
12✔
96
                m_dmxPos.setX(MAX_DMX_VALUE);
97
            if (m_dmxPos.y() > MAX_DMX_VALUE)
12✔
98
                m_dmxPos.setY(MAX_DMX_VALUE);
99

100
            m_changed = true;
12✔
101
        }
102
    }
103

104
    emit positionChanged(point);
13✔
105
}
13✔
106

107
void VCXYPadArea::nudgePosition(qreal dx, qreal dy)
×
108
{
109
    {
110
        QMutexLocker locker(&m_mutex);
×
111

112
        m_dmxPos.setX(CLAMP(m_dmxPos.x() + dx, qreal(0), MAX_DMX_VALUE));
×
113
        m_dmxPos.setY(CLAMP(m_dmxPos.y() + dy, qreal(0), MAX_DMX_VALUE));
×
114

115
        m_changed = true;
×
116
    }
117

118
    emit positionChanged(m_dmxPos);
×
119
}
×
120

121
bool VCXYPadArea::hasPositionChanged()
6✔
122
{
123
    QMutexLocker locker(&m_mutex);
6✔
124
    return m_changed;
6✔
125
}
126

127
void VCXYPadArea::slotFixturePositions(const QVariantList positions)
×
128
{
129
    if (positions == m_fixturePositions)
×
130
        return;
131

132
    m_fixturePositions = positions;
×
133
    update();
×
134
}
135

136
void VCXYPadArea::checkDmxRange()
×
137
{
138
     QPointF pt(CLAMP(m_dmxPos.x(), m_rangeDmxRect.left(), m_rangeDmxRect.right()),
×
139
         CLAMP(m_dmxPos.y(), m_rangeDmxRect.top(), m_rangeDmxRect.bottom()));
×
140

141
     setPosition(pt);
×
142
}
×
143

144
void VCXYPadArea::updateWindowPos()
5✔
145
{
146
    m_windowPos.setX(SCALE(m_dmxPos.x(), qreal(0), qreal(256), qreal(0), qreal(width())));
5✔
147
    m_windowPos.setY(SCALE(m_dmxPos.y(), qreal(0), qreal(256), qreal(0), qreal(height())));
5✔
148
}
5✔
149

150
static int coarseByte(qreal value)
151
{
152
    return value;
5✔
153
}
154

155
static int fineByte(qreal value)
156
{
157
    return (value - floor(value)) * 256;
10✔
158
}
159

160
QString VCXYPadArea::positionString() const
5✔
161
{
162
    QPointF pos = position(false);
5✔
163
    return QString("%1.%2 : %3.%4")
5✔
164
        .arg(coarseByte(pos.x()), 3, 10, QChar('0'))
10✔
165
        .arg(fineByte(pos.x()), 3, 10, QChar('0'))
10✔
166
        .arg(coarseByte(pos.y()), 3, 10, QChar('0'))
10✔
167
        .arg(fineByte(pos.y()), 3,10, QChar('0'));
10✔
168
}
169

170
QString VCXYPadArea::angleString() const
5✔
171
{
172
    QPointF pos = position(false);
5✔
173
    QRectF range = degreesRange();
5✔
174

175
    if (range.isValid())
176
    {
177
        return QString("%1%2 : %3%4")
×
178
            .arg(range.x() + pos.x() * range.width() / 256)
×
179
            .arg(QChar(0xb0))
×
180
            .arg(range.y() + pos.y() * range.height() / 256)
×
181
            .arg(QChar(0xb0));
×
182
    }
183
    else
184
    {
185
        return QString("%1 % : %2 %")
10✔
186
            .arg(pos.x() * 100 / MAX_DMX_VALUE, 7, 'f', 3, '0')
10✔
187
            .arg(pos.y() * 100 / MAX_DMX_VALUE, 7, 'f', 3, '0');
5✔
188
    }
189

190
}
191

192
/*************************************************************************
193
 * Range window
194
 *************************************************************************/
195

196
QRectF VCXYPadArea::rangeWindow()
×
197
{
198
    return m_rangeDmxRect;
×
199
}
200

201
void VCXYPadArea::setRangeWindow(QRectF rect)
×
202
{
203
    m_rangeDmxRect = rect;
×
204
    updateRangeWindow();
×
205
}
×
206

207
void VCXYPadArea::updateRangeWindow()
5✔
208
{
209
    int x = m_rangeDmxRect.x() * width() / 256;
5✔
210
    int y = m_rangeDmxRect.y() * height() / 256;
5✔
211
    int w = m_rangeDmxRect.width() * width() / 256;
5✔
212
    int h = m_rangeDmxRect.height() * height() / 256;
5✔
213
    m_rangeWindowRect = QRect(x, y, w, h);
5✔
214
}
5✔
215

216
/*************************************************************************
217
 * Degrees range
218
 *************************************************************************/
219

220
QRectF VCXYPadArea::degreesRange() const
5✔
221
{
222
    return m_degreesRange;
5✔
223
}
224

225
void VCXYPadArea::setDegreesRange(QRectF range)
20✔
226
{
227
    m_degreesRange = range;
20✔
228
    update();
20✔
229
}
20✔
230

231
/*************************************************************************
232
 * EFX Preview
233
 *************************************************************************/
234

235
void VCXYPadArea::enableEFXPreview(bool enable)
×
236
{
237
    if (enable)
×
238
    {
239
        if (m_previewArea == NULL)
×
240
        {
241
            m_previewArea = new EFXPreviewArea(this);
×
242
            m_previewArea->setBackgroundAlpha(0);
×
243
            layout()->setContentsMargins(0, 0, 0, 0);
×
244
            layout()->addWidget(m_previewArea);
×
245
        }
246
    }
247
    else
248
    {
249
        if (m_previewArea)
×
250
        {
251
            m_previewArea->deleteLater();
×
252
            m_previewArea = NULL;
×
253
        }
254
    }
255
}
×
256

257
void VCXYPadArea::setEFXPolygons(const QPolygonF &pattern, const QVector<QPolygonF> fixtures)
×
258
{
259
    if (m_previewArea == NULL)
×
260
        enableEFXPreview(true);
×
261

262
    m_previewArea->setPolygon(pattern);
×
263
    m_previewArea->setFixturePolygons(fixtures);
×
264
}
×
265

266
void VCXYPadArea::setEFXInterval(uint duration)
×
267
{
NEW
268
    if (m_previewArea != NULL)
×
NEW
269
        m_previewArea->draw(duration / m_previewArea->polygonsCount());
×
UNCOV
270
}
×
271

272
/*****************************************************************************
273
 * Event handlers
274
 *****************************************************************************/
275

276
void VCXYPadArea::paintEvent(QPaintEvent* e)
5✔
277
{
278
    if (m_rangeWindowRect.isValid() && (m_mode == Doc::Operate))
×
279
        checkDmxRange();
×
280

281
    /* Let the parent class draw its stuff first */
282
    QFrame::paintEvent(e);
5✔
283

284
    QPainter p(this);
5✔
285
    QPen pen;
5✔
286

287
    if (m_previewArea == NULL)
5✔
288
    {
289
        QString title = QString("%1%2%3\n%4\n")
10✔
290
            .arg(windowTitle())
10✔
291
            .arg(windowTitle().isEmpty() ? "" : "\n")
15✔
292
            .arg(positionString())
10✔
293
            .arg(angleString());
10✔
294

295
        /* Draw name (offset just a bit to avoid frame) */
296
        p.drawText(1, 1, width() - 2, height() - 2,
5✔
297
                   Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, title);
298

299
        QFont font = p.font();
5✔
300
        font.setPointSize(font.pointSize() - 2);
5✔
301
        p.setFont(font);
5✔
302
        p.drawText(1, 1, width() - 2, height() - 2,
5✔
303
                   Qt::AlignRight | Qt::AlignBottom | Qt::TextWordWrap, tr("Shift: fine, Ctrl:10x"));
5✔
304
    }
5✔
305
    /* Draw crosshairs to indicate the center position */
306
    pen.setStyle(Qt::DotLine);
5✔
307
    pen.setColor(palette().color(QPalette::WindowText));
10✔
308
    pen.setWidth(0);
5✔
309
    p.setPen(pen);
5✔
310
    p.drawLine(width() / 2, 0, width() / 2, height());
5✔
311
    p.drawLine(0, height() / 2, width(), height() / 2);
5✔
312

313
    /* Draw the range window if not full size */
314
    if (m_rangeWindowRect.isValid())
315
    {
316
        pen.setStyle(Qt::SolidLine);
×
317
        pen.setColor(QColor(0, 120, 0, 170));
×
318
        p.setPen(pen);
×
319
        p.fillRect(m_rangeWindowRect, QBrush(QColor(155, 200, 165, 130)));
×
320
        p.drawRect(m_rangeWindowRect);
321
    }
322

323
    updateWindowPos();
5✔
324

325
    if (m_previewArea == NULL)
5✔
326
    {
327
        foreach (QVariant pos, m_fixturePositions)
10✔
328
        {
329
            QPointF pt = pos.toPointF();
×
330
            pt.setX(SCALE(pt.x(), qreal(0), qreal(256), qreal(0), qreal(width())));
×
331
            pt.setY(SCALE(pt.y(), qreal(0), qreal(256), qreal(0), qreal(height())));
×
332

333
            p.drawPixmap(pt.x() - (m_fixturePixmap.width() / 2),
×
334
                     pt.y() - (m_fixturePixmap.height() / 2),
×
335
                     m_fixturePixmap);
×
336
        }
×
337

338
        /* Draw the current point pixmap */
339
        p.drawPixmap(m_windowPos.x() - (m_activePixmap.width() / 2),
5✔
340
                     m_windowPos.y() - (m_activePixmap.height() / 2),
5✔
341
                     m_activePixmap);
5✔
342
    }
343
}
5✔
344

345
void VCXYPadArea::resizeEvent(QResizeEvent *e)
5✔
346
{
347
    QFrame::resizeEvent(e);
5✔
348
    updateRangeWindow();
5✔
349
}
5✔
350

351
void VCXYPadArea::mousePressEvent(QMouseEvent* e)
2✔
352
{
353
    if (m_mode == Doc::Operate)
2✔
354
    {
355
        QPointF pt(CLAMP(e->pos().x(), 0, width()), CLAMP(e->pos().y(), 0, height()));
1✔
356
        pt.setX(SCALE(pt.x(), qreal(0), qreal(width()), qreal(0), qreal(256)));
1✔
357
        pt.setY(SCALE(pt.y(), qreal(0), qreal(height()), qreal(0), qreal(256)));
1✔
358

359
        setPosition(pt);
1✔
360
        setMouseTracking(true);
1✔
361
        setCursor(Qt::CrossCursor);
1✔
362
        update();
1✔
363
    }
364

365
    QFrame::mousePressEvent(e);
2✔
366
}
2✔
367

368
void VCXYPadArea::mouseReleaseEvent(QMouseEvent* e)
1✔
369
{
370
    if (m_mode == Doc::Operate)
1✔
371
    {
372
        QPointF pt(CLAMP(e->pos().x(), 0, width()), CLAMP(e->pos().y(), 0, height()));
1✔
373
        pt.setX(SCALE(pt.x(), qreal(0), qreal(width()), qreal(0), qreal(256)));
1✔
374
        pt.setY(SCALE(pt.y(), qreal(0), qreal(height()), qreal(0), qreal(256)));
1✔
375

376
        setPosition(pt);
1✔
377
        setMouseTracking(false);
1✔
378
        unsetCursor();
1✔
379
    }
380

381
    QFrame::mouseReleaseEvent(e);
1✔
382
}
1✔
383

384
void VCXYPadArea::mouseMoveEvent(QMouseEvent* e)
1✔
385
{
386
    if (m_mode == Doc::Operate)
1✔
387
    {
388
        QPointF pt(CLAMP(e->pos().x(), 0, width()), CLAMP(e->pos().y(), 0, height()));
1✔
389
        pt.setX(SCALE(pt.x(), qreal(0), qreal(width()), qreal(0), qreal(256)));
1✔
390
        pt.setY(SCALE(pt.y(), qreal(0), qreal(height()), qreal(0), qreal(256)));
1✔
391

392
        setPosition(pt);
1✔
393
        update();
1✔
394
    }
395

396
    QFrame::mouseMoveEvent(e);
1✔
397
}
1✔
398

399
void VCXYPadArea::keyPressEvent(QKeyEvent *e)
×
400
{
401
    if (m_mode == Doc::Operate)
×
402
    {
403
        qreal step = 1;
404
        if (e->modifiers().testFlag(Qt::ControlModifier))
×
405
            step *= 10;
406
        if (e->modifiers().testFlag(Qt::ShiftModifier))
×
407
            step /= 256;
×
408

409
        if (e->key() == Qt::Key_Left)
×
410
        {
411
            nudgePosition(-step , 0);
×
412
            update();
×
413
        }
414
        else if (e->key() == Qt::Key_Right)
×
415
        {
416
            nudgePosition(step, 0);
×
417
            update();
×
418
        }
419
        else if (e->key() == Qt::Key_Up)
×
420
        {
421
            nudgePosition(0, -step);
×
422
            update();
×
423
        }
424
        else if (e->key() == Qt::Key_Down)
×
425
        {
426
            nudgePosition(0, step);
×
427
            update();
×
428
        }
429
        else
430
        {
431
            QFrame::keyPressEvent(e);
×
432
        }
433
    }
434

435
    else QFrame::keyPressEvent(e);
×
436
}
×
437

438

439
void VCXYPadArea::keyReleaseEvent (QKeyEvent * e)
×
440
{
441
    QFrame::keyReleaseEvent(e);
×
442
}
×
443

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc