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

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

38.58
/ui/src/virtualconsole/vcxypad.cpp
1
/*
2
  Q Light Controller Plus
3
  vcxypad.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Stefan Krumm
7
                Massimo Callegari
8

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

13
      http://www.apache.org/licenses/LICENSE-2.0.txt
14

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

22
#include <QXmlStreamReader>
23
#include <QXmlStreamWriter>
24
#include <QTreeWidgetItem>
25
#include <QTreeWidget>
26
#include <QMouseEvent>
27
#include <QMessageBox>
28
#include <QGridLayout>
29
#include <QByteArray>
30
#include <QSettings>
31
#include <QPainter>
32
#include <QPixmap>
33
#include <QCursor>
34
#include <QSlider>
35
#include <qmath.h>
36
#include <QDebug>
37
#include <QPoint>
38
#include <QMenu>
39
#include <QList>
40

41
#include "qlcmacros.h"
42

43
#include "vcpropertieseditor.h"
44
#include "vcxypadproperties.h"
45
#include "ctkrangeslider.h"
46
#include "mastertimer.h"
47
#include "vcxypadarea.h"
48
#include "flowlayout.h"
49
#include "vcxypad.h"
50
#include "fixture.h"
51
#include "apputil.h"
52
#include "scene.h"
53
#include "efx.h"
54
#include "doc.h"
55

56
const quint8 VCXYPad::panInputSourceId = 0;
57
const quint8 VCXYPad::tiltInputSourceId = 1;
58
const quint8 VCXYPad::widthInputSourceId = 2;
59
const quint8 VCXYPad::heightInputSourceId = 3;
60
const quint8 VCXYPad::panFineInputSourceId = 4;
61
const quint8 VCXYPad::tiltFineInputSourceId = 5;
62

63
const qreal MAX_VALUE = 256.0;
64
const qreal MAX_DMX_VALUE = MAX_VALUE - 1.0/256;
65

66
static const QString presetBtnSS = "QPushButton { background-color: %1; height: 32px; border: 2px solid #6A6A6A; border-radius: 5px; }"
67
                                   "QPushButton:pressed { border: 2px solid #0000FF; }"
68
                                   "QPushButton:checked { border: 2px solid #0000FF; }"
69
                                   "QPushButton:unchecked { border: 2px solid #6A6A6A; }"
70
                                   "QPushButton:disabled { border: 2px solid #BBBBBB; color: #8f8f8f }";
71

72
/*****************************************************************************
73
 * VCXYPad Initialization
74
 *****************************************************************************/
75

76
VCXYPad::VCXYPad(QWidget* parent, Doc* doc) : VCWidget(parent, doc)
9✔
77
{
78
    /* Set the class name "VCXYPad" as the object name as well */
79
    setObjectName(VCXYPad::staticMetaObject.className());
9✔
80

81
    m_mainVbox = new QVBoxLayout(this);
9✔
82

83
    m_padBox = new QHBoxLayout;
9✔
84
    m_mainVbox->addLayout(m_padBox);
9✔
85

86
    m_lvbox = new QVBoxLayout;
9✔
87
    m_lvbox->addSpacing(20);
9✔
88
    // left side vertical range slider
89
    m_vRangeSlider = new ctkRangeSlider(this);
9✔
90
    m_lvbox->addWidget(m_vRangeSlider);
9✔
91
    m_lvbox->addSpacing(25);
9✔
92

93
    m_padBox->addLayout(m_lvbox);
9✔
94

95
    m_cvbox = new QVBoxLayout;
9✔
96
    m_padBox->addLayout(m_cvbox);
9✔
97

98
    // top horizontal range slider
99
    m_hRangeSlider = new ctkRangeSlider(Qt::Horizontal, this);
9✔
100
    m_cvbox->addWidget(m_hRangeSlider);
9✔
101

102
    // central XYPad
103
    m_area = new VCXYPadArea(this);
9✔
104
    m_cvbox->addWidget(m_area);
9✔
105

106
    // bottom horizontal slider
107
    m_hSlider = new QSlider(Qt::Horizontal, this);
9✔
108
    m_cvbox->addWidget(m_hSlider);
9✔
109

110
    m_rvbox = new QVBoxLayout;
9✔
111
    m_padBox->addLayout(m_rvbox);
9✔
112
    m_rvbox->addSpacing(20);
9✔
113

114
    // left side vertical slider
115
    m_vSlider = new QSlider(this);
9✔
116
    m_rvbox->addWidget(m_vSlider);
9✔
117
    m_rvbox->addSpacing(25);
9✔
118

119
    // bottom preset space
120
    m_presetsLayout = new FlowLayout();
9✔
121
    m_mainVbox->addLayout(m_presetsLayout);
9✔
122
    m_efx = NULL;
9✔
123
    m_efxStartXOverrideId = Function::invalidAttributeId();
9✔
124
    m_efxStartYOverrideId = Function::invalidAttributeId();
9✔
125
    m_efxWidthOverrideId = Function::invalidAttributeId();
9✔
126
    m_efxHeightOverrideId = Function::invalidAttributeId();
9✔
127

128
    m_scene = NULL;
9✔
129

130
    m_vSlider->setRange(0, 256);
9✔
131
    m_hSlider->setRange(0, 256);
9✔
132
    m_vSlider->setInvertedAppearance(true);
9✔
133
    m_vSlider->setTickPosition(QSlider::TicksLeft);
9✔
134
    m_vSlider->setTickInterval(16);
9✔
135
    m_hSlider->setTickPosition(QSlider::TicksAbove);
9✔
136
    m_hSlider->setTickInterval(16);
9✔
137
    m_vSlider->setStyle(AppUtil::saneStyle());
9✔
138
    m_hSlider->setStyle(AppUtil::saneStyle());
9✔
139

140
    m_hRangeSlider->setRange(0, 256);
9✔
141
    m_vRangeSlider->setInvertedAppearance(true);
9✔
142
    m_vRangeSlider->setRange(0, 256);
9✔
143
    m_hRangeSlider->setMaximumPosition(256);
9✔
144
    m_vRangeSlider->setMaximumPosition(256);
9✔
145

146
    connect(m_area, SIGNAL(positionChanged(const QPointF&)),
9✔
147
            this, SLOT(slotPositionChanged(const QPointF&)));
148
    connect(this, SIGNAL(fixturePositions(const QVariantList)),
18✔
149
            m_area, SLOT(slotFixturePositions(const QVariantList)));
9✔
150
    connect(m_vSlider, SIGNAL(valueChanged(int)),
9✔
151
            this, SLOT(slotSliderValueChanged()));
152
    connect(m_hSlider, SIGNAL(valueChanged(int)),
9✔
153
            this, SLOT(slotSliderValueChanged()));
154
    connect(m_hRangeSlider, SIGNAL(positionsChanged(int,int)),
9✔
155
            this, SLOT(slotRangeValueChanged()));
156
    connect(m_vRangeSlider, SIGNAL(positionsChanged(int,int)),
9✔
157
            this, SLOT(slotRangeValueChanged()));
158

159
    setFrameStyle(KVCFrameStyleSunken);
9✔
160
    setType(VCWidget::XYPadWidget);
9✔
161
    setCaption("XY Pad");
9✔
162
    setMinimumSize(20, 20);
9✔
163

164
    QSettings settings;
9✔
165
    QVariant var = settings.value(SETTINGS_XYPAD_SIZE);
9✔
166
    if (var.isValid() == true)
9✔
167
        resize(var.toSize());
×
168
    else
169
        resize(QSize(230, 230));
9✔
170
    m_padInteraction = false;
9✔
171
    m_sliderInteraction = false;
9✔
172
    m_inputValueChanged = false;
9✔
173

174
    slotModeChanged(m_doc->mode());
9✔
175
    setLiveEdit(m_liveEdit);
9✔
176

177
    m_doc->masterTimer()->registerDMXSource(this);
9✔
178
    connect(m_doc->inputOutputMap(), SIGNAL(universeWritten(quint32,QByteArray)),
9✔
179
            this, SLOT(slotUniverseWritten(quint32,QByteArray)));
180
}
9✔
181

182
VCXYPad::~VCXYPad()
12✔
183
{
184
    m_doc->masterTimer()->unregisterDMXSource(this);
9✔
185
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
9✔
186
    {
187
        if (!fader.isNull())
×
188
            fader->requestDelete();
×
189
    }
9✔
190
    m_fadersMap.clear();
9✔
191

192
    resetPresets();
9✔
193
}
12✔
194

195
void VCXYPad::enableWidgetUI(bool enable)
11✔
196
{
197
    m_vSlider->setEnabled(enable);
11✔
198
    m_hSlider->setEnabled(enable);
11✔
199
    m_area->setMode(enable ? Doc::Operate : Doc::Design);
11✔
200

201
    QMutableListIterator <VCXYPadFixture> it(m_fixtures);
11✔
202
    while (it.hasNext() == true)
13✔
203
    {
204
        VCXYPadFixture fxi = it.next();
2✔
205
        if (enable)
2✔
206
            fxi.arm();
1✔
207
        else
208
            fxi.disarm();
1✔
209
        it.setValue(fxi);
2✔
210
    }
2✔
211

212
    foreach (QWidget *presetBtn, m_presets.keys())
22✔
213
        presetBtn->setEnabled(enable);
11✔
214

215
    /* Reset the changed flag in m_area so that the pad won't immediately set a value
216
       when mode is changed */
217
    m_area->position();
11✔
218
}
11✔
219

220
/*****************************************************************************
221
 * Clipboard
222
 *****************************************************************************/
223

224
VCWidget* VCXYPad::createCopy(VCWidget* parent)
1✔
225
{
226
    Q_ASSERT(parent != NULL);
1✔
227

228
    VCXYPad* xypad = new VCXYPad(parent, m_doc);
1✔
229
    if (xypad->copyFrom(this) == false)
1✔
230
    {
231
        delete xypad;
×
232
        xypad = NULL;
×
233
    }
234

235
    for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
1✔
236
            it != m_presets.end(); ++it)
1✔
237
    {
238
        VCXYPadPreset *preset = it.value();
×
239
        xypad->addPreset(*preset);
×
240
    }
241

242
    return xypad;
1✔
243
}
244

245
bool VCXYPad::copyFrom(const VCWidget* widget)
1✔
246
{
247
    const VCXYPad* xypad = qobject_cast <const VCXYPad*> (widget);
1✔
248
    if (xypad == NULL)
1✔
249
        return false;
×
250
    resize(xypad->size());
1✔
251

252
    /* Get rid of existing channels */
253
    m_fixtures.clear();
1✔
254

255
    /* Copy the other widget's fixtures */
256
    m_fixtures = xypad->fixtures();
1✔
257

258
    /* Copy the current position */
259
    m_area->setPosition(xypad->m_area->position());
1✔
260
    m_vSlider->setValue(xypad->m_vSlider->value());
1✔
261
    m_hSlider->setValue(xypad->m_hSlider->value());
1✔
262

263
    /* Copy common stuff */
264
    return VCWidget::copyFrom(widget);
1✔
265
}
266

267
/*****************************************************************************
268
 * Caption
269
 *****************************************************************************/
270

271
void VCXYPad::setCaption(const QString& text)
12✔
272
{
273
    m_area->setWindowTitle(text);
12✔
274
    VCWidget::setCaption(text);
12✔
275
}
12✔
276

277
bool VCXYPad::invertedAppearance() const
7✔
278
{
279
    return !(m_vSlider->invertedAppearance());
7✔
280
}
281

282
void VCXYPad::setInvertedAppearance(bool invert)
×
283
{
284
    if (invert == true)
×
285
        m_vSlider->setInvertedAppearance(false);
×
286
    else
287
        m_vSlider->setInvertedAppearance(true);
×
288
}
×
289

290
/*****************************************************************************
291
 * Properties
292
 *****************************************************************************/
293

294
void VCXYPad::editProperties()
×
295
{
296
    VCXYPadProperties prop(this, m_doc);
×
297
    if (prop.exec() == QDialog::Accepted)
×
298
        m_doc->setModified();
×
299
}
×
300

301
/*****************************************************************************
302
 * Fixtures
303
 *****************************************************************************/
304

305
void VCXYPad::appendFixture(const VCXYPadFixture& fxi)
17✔
306
{
307
    if (fxi.head().isValid() && m_fixtures.indexOf(fxi) == -1)
17✔
308
        m_fixtures.append(fxi);
13✔
309

310
    updateDegreesRange();
17✔
311
}
17✔
312

313
void VCXYPad::removeFixture(GroupHead const & head)
2✔
314
{
315
    VCXYPadFixture fixture(m_doc);
2✔
316
    fixture.setHead(head);
2✔
317

318
    m_fixtures.removeAll(fixture);
2✔
319

320
    updateDegreesRange();
2✔
321
}
2✔
322

323
void VCXYPad::clearFixtures()
1✔
324
{
325
    m_fixtures.clear();
1✔
326

327
    updateDegreesRange();
1✔
328
}
1✔
329

330
QList <VCXYPadFixture> VCXYPad::fixtures() const
8✔
331
{
332
    return m_fixtures;
8✔
333
}
334

335
QRectF VCXYPad::computeCommonDegreesRange() const
20✔
336
{
337
    QRectF commonRange;
20✔
338

339
    foreach (VCXYPadFixture fixture, m_fixtures)
21✔
340
    {
341
        QRectF range = fixture.degreesRange();
18✔
342
        if (!range.isValid())
18✔
343
            return QRectF();
17✔
344

345
        if (commonRange.isValid())
1✔
346
        {
347
            if (range != commonRange)
×
348
                return QRectF();
×
349
        }
350
        else
351
        {
352
            commonRange = range;
1✔
353
        }
354
    }
38✔
355

356
    return commonRange;
3✔
357
}
358

359
void VCXYPad::updateDegreesRange()
20✔
360
{
361
    QRectF range = computeCommonDegreesRange();
20✔
362

363
    m_area->setDegreesRange(range);
20✔
364
}
20✔
365

366
/*****************************************************************************
367
 * Current XY position
368
 *****************************************************************************/
369

370
void VCXYPad::writeDMX(MasterTimer* timer, QList<Universe *> universes)
×
371
{
372
    if (m_scene != NULL)
×
373
        writeScenePositions(timer, universes);
×
374
    else
375
        writeXYFixtures(timer, universes);
×
376
}
×
377

378
void VCXYPad::writeXYFixtures(MasterTimer *timer, QList<Universe *> universes)
×
379
{
380
    Q_UNUSED(timer);
381

382
    if (m_area->hasPositionChanged() == false)
×
383
        return;
×
384

385
    // This call also resets the m_changed flag in m_area
386
    QPointF pt = m_area->position();
×
387

388
    /* Scale XY coordinate values to 0.0 - 1.0 */
389
    qreal x = SCALE(pt.x(), qreal(0), qreal(256), qreal(0), qreal(1));
×
390
    qreal y = SCALE(pt.y(), qreal(0), qreal(256), qreal(0), qreal(1));
×
391

392
    if (invertedAppearance())
×
393
        y = qreal(1) - y;
×
394

395
    /* Write values outside of mutex lock to keep UI snappy */
396
    foreach (VCXYPadFixture fixture, m_fixtures)
×
397
    {
398
        if (fixture.isEnabled())
×
399
        {
400
            quint32 universe = fixture.universe();
×
401
            if (universe == Universe::invalid())
×
402
                continue;
×
403

404
            QSharedPointer<GenericFader> fader = m_fadersMap.value(universe, QSharedPointer<GenericFader>());
×
405
            if (fader.isNull())
×
406
            {
407
                fader = universes[universe]->requestFader();
×
408
                fader->adjustIntensity(intensity());
×
409
                m_fadersMap[universe] = fader;
×
410
            }
411
            fixture.writeDMX(x, y, fader, universes[universe]);
×
412
        }
×
413
    }
×
414
}
415

416
void VCXYPad::updateSceneChannel(FadeChannel *fc, uchar value)
×
417
{
418
    fc->addFlag(FadeChannel::Relative);
×
419
    fc->setStart(value);
×
420
    fc->setCurrent(value);
×
421
    fc->setTarget(value);
×
422
    fc->setElapsed(0);
×
423
    fc->setReady(false);
×
424
}
×
425

426
void VCXYPad::writeScenePositions(MasterTimer *timer, QList<Universe *> universes)
×
427
{
428
    Q_UNUSED(timer);
429

430
    if (m_scene == NULL || m_scene->isRunning() == false)
×
431
        return;
×
432

433
    QPointF pt = m_area->position();
×
434
    uchar panCoarse = uchar(qFloor(pt.x()));
×
435
    uchar panFine = uchar((pt.x() - qFloor(pt.x())) * 256);
×
436
    uchar tiltCoarse = uchar(qFloor(pt.y()));
×
437
    uchar tiltFine = uchar((pt.y() - qFloor(pt.y())) * 256);
×
438

439
    foreach (SceneChannel sc, m_sceneChannels)
×
440
    {
441
        if (sc.m_universe >= (quint32)universes.count())
×
442
            continue;
×
443

444
        QSharedPointer<GenericFader> fader = m_fadersMap.value(sc.m_universe, QSharedPointer<GenericFader>());
×
445
        if (fader.isNull())
×
446
        {
447
            fader = universes[sc.m_universe]->requestFader();
×
448
            fader->adjustIntensity(intensity());
×
449
            m_fadersMap[sc.m_universe] = fader;
×
450
        }
451

452
        if (sc.m_group == QLCChannel::Pan)
×
453
        {
454
            if (sc.m_subType == QLCChannel::MSB)
×
455
            {
456
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
457
                updateSceneChannel(fc, panCoarse);
×
458
            }
459
            else
460
            {
461
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
462
                updateSceneChannel(fc, panFine);
×
463
            }
464
        }
465
        else
466
        {
467
            if (sc.m_subType == QLCChannel::MSB)
×
468
            {
469
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
470
                updateSceneChannel(fc, tiltCoarse);
×
471
            }
472
            else
473
            {
474
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
475
                updateSceneChannel(fc, tiltFine);
×
476
            }
477
        }
478
    }
×
479
}
480

481
void VCXYPad::slotPositionChanged(const QPointF& pt)
6✔
482
{
483
    if (m_sliderInteraction == true)
6✔
484
        return;
×
485

486
    m_padInteraction = true;
6✔
487
    m_hSlider->setValue(pt.x());
6✔
488
    if (invertedAppearance() == false)
6✔
489
    {
490
        m_vSlider->setValue(pt.y());
6✔
491
    }
492
    else
493
    {
494
        m_vSlider->setValue(MAX_DMX_VALUE - pt.y());
×
495
    }
496

497
    if (m_inputValueChanged == false)
6✔
498
        updateFeedback();
6✔
499
    m_padInteraction = false;
6✔
500
    m_inputValueChanged = false;
6✔
501
}
502

503
void VCXYPad::slotSliderValueChanged()
10✔
504
{
505
    if (m_padInteraction == true)
10✔
506
        return;
10✔
507

508
    QPointF pt = m_area->position(false);
×
509

510
    m_sliderInteraction = true;
×
511
    if (QObject::sender() == m_hSlider)
×
512
    {
513
        pt.setX(m_hSlider->value());
×
514

515
        int Xfb = (int)SCALE(float(m_hSlider->value()), float(m_hSlider->minimum()),
×
516
                             float(m_hSlider->maximum()), float(0), float(UCHAR_MAX));
517
        sendFeedback(Xfb, panInputSourceId);
×
518
    }
519
    else
520
    {
521
        if (invertedAppearance() == false)
×
522
            pt.setY(m_vSlider->value());
×
523
        else
524
            pt.setY(MAX_DMX_VALUE - m_vSlider->value());
×
525

526
        int Yfb = (int)SCALE(float(m_vSlider->value()), float(m_vSlider->minimum()),
×
527
                             float(m_vSlider->maximum()), float(0), float(UCHAR_MAX));
528
        sendFeedback(Yfb, tiltInputSourceId);
×
529
    }
530

531
    m_area->setPosition(pt);
×
532
    m_area->update();
×
533
    m_sliderInteraction = false;
×
534
}
535

536
void VCXYPad::slotRangeValueChanged()
×
537
{
538
    QRectF rect(QPointF(m_hRangeSlider->minimumPosition(), m_vRangeSlider->minimumPosition()),
×
539
               QPointF(m_hRangeSlider->maximumPosition(), m_vRangeSlider->maximumPosition()));
×
540
    m_area->setRangeWindow(rect);
×
541
    if (m_efx != NULL && m_efx->isRunning())
×
542
    {
543
        m_efx->adjustAttribute(rect.x() + rect.width() / 2, m_efxStartXOverrideId);
×
544
        m_efx->adjustAttribute(rect.y() + rect.height() / 2, m_efxStartYOverrideId);
×
545
        m_efx->adjustAttribute(rect.width() / 2, m_efxWidthOverrideId);
×
546
        m_efx->adjustAttribute(rect.height() / 2, m_efxHeightOverrideId);
×
547

548
        // recalculate preview polygons
549
        QPolygonF polygon;
×
550
        m_efx->preview(polygon);
×
551

552
        QVector <QPolygonF> fixturePoints;
×
553
        m_efx->previewFixtures(fixturePoints);
×
554

555
        m_area->setEFXPolygons(polygon, fixturePoints);
×
556
        m_area->setEFXInterval(m_efx->duration());
×
557
    }
×
558
    m_area->update();
×
559
    if (QObject::sender() == m_hRangeSlider)
×
560
        sendFeedback(m_hRangeSlider->maximumValue(), heightInputSourceId);
×
561
    else if (QObject::sender() == m_vRangeSlider)
×
562
        sendFeedback(m_vRangeSlider->maximumValue(), widthInputSourceId);
×
563
}
×
564

565
void VCXYPad::slotUniverseWritten(quint32 idx, const QByteArray &universeData)
×
566
{
567
    QVariantList positions;
×
568

569
    if (m_scene)
×
570
    {
571
        QMap <quint32, QPointF> fxMap;
×
572

573
        foreach (SceneChannel sc, m_sceneChannels)
×
574
        {
575
            if (sc.m_universe != idx)
×
576
                continue;
×
577

578
            qreal x = fxMap[sc.m_fixture].x();
×
579
            qreal y = fxMap[sc.m_fixture].y();
×
580

581
            if (sc.m_group == QLCChannel::Pan)
×
582
            {
583
                if (sc.m_subType == QLCChannel::MSB)
×
584
                    x += (uchar)universeData.at(sc.m_channel);
×
585
                else
586
                    x += ((uchar)universeData.at(sc.m_channel) / 255);
×
587
            }
588
            else
589
            {
590
                if (sc.m_subType == QLCChannel::MSB)
×
591
                    y += (uchar)universeData.at(sc.m_channel);
×
592
                else
593
                    y += ((uchar)universeData.at(sc.m_channel) / 255);
×
594
            }
595
            fxMap[sc.m_fixture] = QPointF(x, y);
×
596
        }
×
597

598
        foreach (QPointF pt, fxMap)
×
599
        {
600
            if (invertedAppearance())
×
601
                pt.setY(256 - pt.y());
×
602
            positions.append(pt);
×
603
        }
×
604
    }
×
605
    else
606
    {
607
        foreach (VCXYPadFixture fixture, m_fixtures)
×
608
        {
609
            if (fixture.isEnabled() == false)
×
610
                continue;
×
611

612
            if (fixture.universe() != idx)
×
613
                continue;
×
614

615
            qreal x(-1), y(-1);
×
616
            fixture.readDMX(universeData, x, y);
×
617
            if (x != -1.0 && y != -1.0)
×
618
            {
619
                if (invertedAppearance())
×
620
                    y = qreal(1) - y;
×
621

622
               x *= 256;
×
623
               y *= 256;
×
624
               positions.append(QPointF(x, y));
×
625
            }
626
        }
×
627
    }
628

629
    emit fixturePositions(positions);
×
630
}
×
631

632
/*********************************************************************
633
 * Presets
634
 *********************************************************************/
635

636
void VCXYPad::addPreset(const VCXYPadPreset &preset)
×
637
{
638
    QString label = preset.m_name;
×
639

640
    if (label.isEmpty())
×
641
    {
642
        qDebug() << "VCXYPad Preset label empty. Not adding it";
×
643
        return;
×
644
    }
645

646
    QPushButton *presetButton = new QPushButton(this);
×
647
    QWidget *presetWidget = presetButton;
×
648
    presetButton->setStyleSheet(presetBtnSS.arg(preset.getColor()));
×
649
    presetButton->setMinimumWidth(36);
×
650
    presetButton->setMaximumWidth(80);
×
651
    presetButton->setFocusPolicy(Qt::TabFocus);
×
652
    presetButton->setText(fontMetrics().elidedText(label, Qt::ElideRight, 72));
×
653
    if (preset.m_type == VCXYPadPreset::EFX ||
×
654
        preset.m_type == VCXYPadPreset::Scene ||
×
655
        preset.m_type == VCXYPadPreset::FixtureGroup)
×
656
            presetButton->setCheckable(true);
×
657

658
    connect(presetButton, SIGNAL(clicked(bool)),
×
659
            this, SLOT(slotPresetClicked(bool)));
660

661
    if (mode() == Doc::Design)
×
662
        presetWidget->setEnabled(false);
×
663

664
    m_presets[presetWidget] = new VCXYPadPreset(preset);
×
665
    m_presetsLayout->addWidget(presetWidget);
×
666

667
    if (m_presets[presetWidget]->m_inputSource != NULL)
×
668
    {
669
        setInputSource(m_presets[presetWidget]->m_inputSource, m_presets[presetWidget]->m_id);
×
670
    }
671
}
×
672

673
void VCXYPad::resetPresets()
9✔
674
{
675
    for (QHash<QWidget *, VCXYPadPreset *>::iterator it = m_presets.begin();
9✔
676
            it != m_presets.end(); ++it)
9✔
677
    {
678
        QWidget* widget = it.key();
×
679
        m_presetsLayout->removeWidget(widget);
×
680
        delete widget;
×
681

682
        VCXYPadPreset* preset = it.value();
×
683
        if (!preset->m_inputSource.isNull())
×
684
            setInputSource(QSharedPointer<QLCInputSource>(), preset->m_id);
×
685
        delete preset;
×
686
    }
687
    m_presets.clear();
9✔
688
}
9✔
689

690
QList<VCXYPadPreset *> VCXYPad::presets() const
1✔
691
{
692
    QList<VCXYPadPreset*> presets = m_presets.values();
1✔
693
    std::sort(presets.begin(), presets.end(), VCXYPadPreset::compare);
1✔
694
    return presets;
1✔
695
}
×
696

697
QMap<quint32,QString> VCXYPad::presetsMap() const
×
698
{
699
    QMap<quint32,QString> map;
×
700

701
    foreach (VCXYPadPreset *control, m_presets)
×
702
        map.insert(control->m_id, VCXYPadPreset::typeToString(control->m_type));
×
703

704
    return map;
×
705
}
×
706

707
void VCXYPad::slotPresetClicked(bool checked)
×
708
{
709
    if (mode() == Doc::Design)
×
710
        return;
×
711

712
    QPushButton *btn = qobject_cast<QPushButton*>(sender());
×
713
    VCXYPadPreset *preset = m_presets[btn];
×
714

715
    Q_ASSERT(preset != NULL);
×
716

717
    // stop any previously started EFX
718
    if (m_efx != NULL && m_efx->isRunning())
×
719
    {
720
        disconnect(m_efx, SIGNAL(durationChanged(uint)), this, SLOT(slotEFXDurationChanged(uint)));
×
721

722
        m_efx->stopAndWait();
×
723
        m_efx = NULL;
×
724
        m_efxStartXOverrideId = Function::invalidAttributeId();
×
725
        m_efxStartYOverrideId = Function::invalidAttributeId();
×
726
        m_efxWidthOverrideId = Function::invalidAttributeId();
×
727
        m_efxHeightOverrideId = Function::invalidAttributeId();
×
728
    }
729

730
    // stop any previously started Scene
731
    if (m_scene != NULL)
×
732
    {
733
        m_scene->stop(functionParent());
×
734
        m_scene = NULL;
×
735
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
736
        {
737
            if (!fader.isNull())
×
738
                fader->requestDelete();
×
739
        }
×
740
        m_fadersMap.clear();
×
741
    }
742

743
    // deactivate all previously activated buttons first
744
    for (QHash<QWidget *, VCXYPadPreset *>::iterator it = m_presets.begin();
×
745
            it != m_presets.end(); ++it)
×
746
    {
747
        QPushButton* cBtn = reinterpret_cast<QPushButton*>(it.key());
×
748
        VCXYPadPreset *cPr = it.value();
×
749
        if (preset->m_id == cPr->m_id)
×
750
            continue;
×
751

752
        cBtn->blockSignals(true);
×
753
        if (preset->m_type == VCXYPadPreset::FixtureGroup)
×
754
        {
755
            if (cPr->m_type == VCXYPadPreset::FixtureGroup &&
×
756
                cBtn->isChecked() == true)
×
757
            {
758
                cBtn->setChecked(false);
×
759
                if (cPr->m_inputSource.isNull() == false)
×
760
                    sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource);
×
761
            }
762
        }
763
        else if (cPr->m_type == VCXYPadPreset::EFX ||
×
764
            cPr->m_type == VCXYPadPreset::Scene)
×
765
        {
766
            if (cBtn->isChecked() == true)
×
767
            {
768
                cBtn->setChecked(false);
×
769
                if (cPr->m_inputSource.isNull() == false)
×
770
                    sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource);
×
771
            }
772
        }
773
        else
774
        {
775
            if (cBtn->isDown() == true)
×
776
            {
777
                cBtn->setDown(false);
×
778
                if (cPr->m_inputSource.isNull() == false)
×
779
                    sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource);
×
780
            }
781
        }
782
        cBtn->blockSignals(false);
×
783
        if (cPr->m_inputSource.isNull() == false)
×
784
            sendFeedback(cPr->m_inputSource->feedbackValue(QLCInputFeedback::LowerValue), cPr->m_inputSource);
×
785
    }
786

787
    if (preset->m_type == VCXYPadPreset::EFX)
×
788
    {
789
        if (checked == false)
×
790
        {
791
            m_area->enableEFXPreview(false);
×
792
            return;
×
793
        }
794

795
        Function *f = m_doc->function(preset->m_funcID);
×
796
        if (f == NULL || f->type() != Function::EFXType)
×
797
            return;
×
798
        m_efx = qobject_cast<EFX*>(f);
×
799

800
        QRectF rect(QPointF(m_hRangeSlider->minimumPosition(), m_vRangeSlider->minimumPosition()),
×
801
                   QPointF(m_hRangeSlider->maximumPosition(), m_vRangeSlider->maximumPosition()));
×
802
        m_area->setRangeWindow(rect);
×
803
        if (rect.isValid())
×
804
        {
805
            m_efxStartXOverrideId = m_efx->requestAttributeOverride(EFX::XOffset, rect.x() + rect.width() / 2);
×
806
            m_efxStartYOverrideId = m_efx->requestAttributeOverride(EFX::YOffset, rect.y() + rect.height() / 2);
×
807
            m_efxWidthOverrideId = m_efx->requestAttributeOverride(EFX::Width, rect.width() / 2);
×
808
            m_efxHeightOverrideId = m_efx->requestAttributeOverride(EFX::Height, rect.height() / 2);
×
809
        }
810

811
        QPolygonF polygon;
×
812
        m_efx->preview(polygon);
×
813

814
        QVector <QPolygonF> fixturePoints;
×
815
        m_efx->previewFixtures(fixturePoints);
×
816

817
        m_area->enableEFXPreview(true);
×
818
        m_area->setEFXPolygons(polygon, fixturePoints);
×
819
        m_area->setEFXInterval(m_efx->duration());
×
820
        m_efx->start(m_doc->masterTimer(), functionParent());
×
821

822
        connect(m_efx, SIGNAL(durationChanged(uint)), this, SLOT(slotEFXDurationChanged(uint)));
×
823

824
        if (preset->m_inputSource.isNull() == false)
×
825
            sendFeedback(preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue), preset->m_inputSource);
×
826
    }
×
827
    else if (preset->m_type == VCXYPadPreset::Scene)
×
828
    {
829
        if (checked == false)
×
830
            return;
×
831

832
        Function *f = m_doc->function(preset->m_funcID);
×
833
        if (f == NULL || f->type() != Function::SceneType)
×
834
            return;
×
835

836
        m_scene = qobject_cast<Scene*>(f);
×
837
        m_sceneChannels.clear();
×
838

839
        foreach (SceneValue scv, m_scene->values())
×
840
        {
841
            Fixture *fixture = m_doc->fixture(scv.fxi);
×
842
            if (fixture == NULL)
×
843
                continue;
×
844
            const QLCChannel *ch = fixture->channel(scv.channel);
×
845
            if (ch == NULL)
×
846
                continue;
×
847
            if (ch->group() != QLCChannel::Pan && ch->group() != QLCChannel::Tilt)
×
848
                continue;
×
849

850
            SceneChannel sChan;
851
            sChan.m_universe = fixture->universe();
×
852
            sChan.m_fixture = fixture->id();
×
853
            sChan.m_channel = scv.channel;
×
854
            sChan.m_group = ch->group();
×
855
            sChan.m_subType = ch->controlByte();
×
856
            m_sceneChannels.append(sChan);
×
857
        }
×
858

859
        m_area->enableEFXPreview(false);
×
860
        // reset the area window as we're switching to relative
861
        m_area->setRangeWindow(QRectF());
×
862
        m_area->setPosition(QPointF(128, 128));
×
863
        m_area->repaint();
×
864
        m_scene->start(m_doc->masterTimer(), functionParent());
×
865

866
        if (preset->m_inputSource.isNull() == false)
×
867
            sendFeedback(preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue), preset->m_inputSource);
×
868
    }
869
    else if (preset->m_type == VCXYPadPreset::Position)
×
870
    {
871
        m_area->enableEFXPreview(false);
×
872
        QRectF rect(QPointF(m_hRangeSlider->minimumPosition(), m_vRangeSlider->minimumPosition()),
×
873
                   QPointF(m_hRangeSlider->maximumPosition(), m_vRangeSlider->maximumPosition()));
×
874
        m_area->setRangeWindow(rect);
×
875
        m_area->setPosition(preset->m_dmxPos);
×
876
        m_area->repaint();
×
877
        if (preset->m_inputSource.isNull() == false)
×
878
            sendFeedback(preset->m_inputSource->feedbackValue(QLCInputFeedback::UpperValue), preset->m_inputSource);
×
879
        btn->blockSignals(true);
×
880
        btn->setDown(true);
×
881
        btn->blockSignals(false);
×
882
    }
883
    else if (preset->m_type == VCXYPadPreset::FixtureGroup)
×
884
    {
885
        QList<GroupHead> heads = preset->fixtureGroup();
×
886

887
        for (int i = 0; i < m_fixtures.count(); i++)
×
888
        {
889
            if (checked == false)
×
890
            {
891
                m_fixtures[i].setEnabled(true);
×
892
            }
893
            else
894
            {
895
                if (heads.contains(m_fixtures[i].head()))
×
896
                {
897
                    qDebug() << "Enabling head" << m_fixtures[i].head().fxi << m_fixtures[i].head().head;
×
898
                    m_fixtures[i].setEnabled(true);
×
899
                }
900
                else
901
                {
902
                    qDebug() << "Disabling head" << m_fixtures[i].head().fxi << m_fixtures[i].head().head;
×
903
                    m_fixtures[i].setEnabled(false);
×
904
                }
905
            }
906
        }
907
    }
×
908
}
909

910
void VCXYPad::slotEFXDurationChanged(uint duration)
×
911
{
912
    if (m_efx == NULL)
×
913
        return;
×
914

915
    m_area->setEFXInterval(duration);
×
916
}
917

918
FunctionParent VCXYPad::functionParent() const
×
919
{
920
    return FunctionParent(FunctionParent::ManualVCWidget, id());
×
921
}
922

923
/*********************************************************************
924
 * External input
925
 *********************************************************************/
926

927
void VCXYPad::updateFeedback()
6✔
928
{
929
    int Xfb = (int)SCALE(float(m_hSlider->value()), float(m_hSlider->minimum()),
6✔
930
                         float(m_hSlider->maximum()), float(0), float(UCHAR_MAX));
931
    sendFeedback(Xfb, panInputSourceId);
6✔
932

933
    int Yfb = (int)SCALE(float(m_vSlider->value()), float(m_vSlider->minimum()),
6✔
934
                         float(m_vSlider->maximum()), float(0), float(UCHAR_MAX));
935
    sendFeedback(Yfb, tiltInputSourceId);
6✔
936

937
/*
938
    for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
939
            it != m_presets.end(); ++it)
940
    {
941
        VCXYPadPreset* preset = it.value();
942
        if (preset->m_inputSource != NULL)
943
        {
944
            {
945
                QPushButton* button = reinterpret_cast<QPushButton*>(it.key());
946
                if (preset->m_inputSource.isNull() == false)
947
                    sendFeedback(button->isDown() ?
948
                                 preset->m_inputSource->upperValue() :
949
                                 preset->m_inputSource->lowerValue(),
950
                                 preset->m_inputSource);
951
            }
952
        }
953
    }
954
*/
955
}
6✔
956

957
void VCXYPad::updatePosition()
×
958
{
959
    QPointF pt = m_area->position(false);
×
960
    qreal xOffset = 0;
×
961
    qreal yOffset = 0;
×
962
    qreal areaWidth = MAX_VALUE;
×
963
    qreal areaHeight = MAX_VALUE;
×
964

965
    QRectF rangeWindow = m_area->rangeWindow();
×
966
    if (rangeWindow.isValid())
×
967
    {
968
        xOffset = rangeWindow.x();
×
969
        yOffset = rangeWindow.y();
×
970
        areaWidth = rangeWindow.width();
×
971
        areaHeight = rangeWindow.height();
×
972
    }
973

974
    pt.setX(xOffset + SCALE((qreal(m_lastPos.x()) * 256.0) + qreal(m_lastPos.width()), qreal(0), qreal(65535),
×
975
                            qreal(0), areaWidth));
976

977
    if (invertedAppearance() == false)
×
978
        pt.setY(yOffset + SCALE((qreal(m_lastPos.y()) * 256.0) + qreal(m_lastPos.height()), qreal(0), qreal(65535),
×
979
                                qreal(0), areaHeight));
980
    else
981
        pt.setY(yOffset + SCALE((qreal(m_lastPos.y()) * 256.0) + qreal(m_lastPos.height()), qreal(65535), qreal(0),
×
982
                                qreal(0), areaHeight));
983

984
    m_inputValueChanged = true;
×
985

986
    m_area->setPosition(pt);
×
987
    m_area->update();
×
988
}
×
989

990
void VCXYPad::slotInputValueChanged(quint32 universe, quint32 channel,
×
991
                                     uchar value)
992
{
993
    /* Don't let input data through in design mode or if disabled */
994
    if (acceptsInput() == false)
×
995
        return;
×
996

997
    quint32 pagedCh = (page() << 16) | channel;
×
998

999
    if (checkInputSource(universe, pagedCh, value, sender(), panInputSourceId))
×
1000
    {
1001
        if (m_efx == NULL)
×
1002
        {
1003
            m_lastPos.moveLeft(value);
×
1004
            updatePosition();
×
1005
        }
1006
        else
1007
        {
1008
            if (m_efx->isRunning() == false)
×
1009
                return;
×
1010

1011
            m_hRangeSlider->setMinimumValue(value);
×
1012
            slotRangeValueChanged();
×
1013
            return;
×
1014
        }
1015
    }
1016
    else if (checkInputSource(universe, pagedCh, value, sender(), panFineInputSourceId))
×
1017
    {
1018
        if (m_efx == NULL)
×
1019
        {
1020
            m_lastPos.setWidth(value);
×
1021
            updatePosition();
×
1022
        }
1023
    }
1024
    else if (checkInputSource(universe, pagedCh, value, sender(), tiltInputSourceId))
×
1025
    {
1026
        if (m_efx == NULL)
×
1027
        {
1028
            m_lastPos.moveTop(value);
×
1029
            updatePosition();
×
1030
        }
1031
        else
1032
        {
1033
            if (m_efx->isRunning() == false)
×
1034
                return;
×
1035

1036
            m_vRangeSlider->setMinimumValue(value);
×
1037
            slotRangeValueChanged();
×
1038
        }
1039
    }
1040
    else if (checkInputSource(universe, pagedCh, value, sender(), tiltFineInputSourceId))
×
1041
    {
1042
        if (m_efx == NULL)
×
1043
        {
1044
            m_lastPos.setHeight(value);
×
1045
            updatePosition();
×
1046
        }
1047
    }
1048
    else if (checkInputSource(universe, pagedCh, value, sender(), widthInputSourceId))
×
1049
    {
1050
        if (m_efx != NULL && m_efx->isRunning())
×
1051
        {
1052
            m_hRangeSlider->setMaximumValue(value);
×
1053
            slotRangeValueChanged();
×
1054
        }
1055
    }
1056
    else if (checkInputSource(universe, pagedCh, value, sender(), heightInputSourceId))
×
1057
    {
1058
        if (m_efx != NULL && m_efx->isRunning())
×
1059
        {
1060
            m_vRangeSlider->setMaximumValue(value);
×
1061
            slotRangeValueChanged();
×
1062
        }
1063
    }
1064
    else
1065
    {
1066
        for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
×
1067
                it != m_presets.end(); ++it)
×
1068
        {
1069
            VCXYPadPreset *preset = it.value();
×
1070
            if (preset->m_inputSource != NULL &&
×
1071
                    preset->m_inputSource->universe() == universe &&
×
1072
                    preset->m_inputSource->channel() == pagedCh)
×
1073
            {
1074
                {
1075
                    QPushButton *button = reinterpret_cast<QPushButton*>(it.key());
×
1076
                    button->click();
×
1077
                    return;
×
1078
                }
1079
            }
1080
        }
1081
    }
1082
}
1083

1084
void VCXYPad::slotKeyPressed(const QKeySequence &keySequence)
×
1085
{
1086
    if (acceptsInput() == false)
×
1087
        return;
×
1088

1089
    for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
×
1090
            it != m_presets.end(); ++it)
×
1091
    {
1092
        VCXYPadPreset *preset = it.value();
×
1093
        if (preset->m_keySequence == keySequence)
×
1094
        {
1095
            QPushButton *button = reinterpret_cast<QPushButton*>(it.key());
×
1096
            button->click();
×
1097
        }
1098
    }
1099
}
1100

1101
/*****************************************************************************
1102
 * QLC mode
1103
 *****************************************************************************/
1104

1105
void VCXYPad::slotModeChanged(Doc::Mode mode)
11✔
1106
{
1107
    if (mode == Doc::Operate && isDisabled() == false)
11✔
1108
    {
1109
        enableWidgetUI(true);
1✔
1110
    }
1111
    else
1112
    {
1113
        enableWidgetUI(false);
10✔
1114
    }
1115

1116
    VCWidget::slotModeChanged(mode);
11✔
1117
}
11✔
1118

1119
/*****************************************************************************
1120
 * Load & Save
1121
 *****************************************************************************/
1122

1123
bool VCXYPad::loadXML(QXmlStreamReader &root)
4✔
1124
{
1125
    bool visible = false;
4✔
1126
    int x = 0;
4✔
1127
    int y = 0;
4✔
1128
    int w = 0;
4✔
1129
    int h = 0;
4✔
1130

1131
    int xpos = 0;
4✔
1132
    int ypos = 0;
4✔
1133

1134
    if (root.name() != KXMLQLCVCXYPad)
4✔
1135
    {
1136
        qWarning() << Q_FUNC_INFO << "XY Pad node not found";
1✔
1137
        return false;
1✔
1138
    }
1139

1140
    /* Widget commons */
1141
    loadXMLCommon(root);
3✔
1142

1143
    QXmlStreamAttributes attrs = root.attributes();
3✔
1144

1145
    if (attrs.hasAttribute(KXMLQLCVCXYPadInvertedAppearance))
3✔
1146
    {
1147
        if (attrs.value(KXMLQLCVCXYPadInvertedAppearance).toString() == "0")
×
1148
            setInvertedAppearance(false);
×
1149
        else
1150
            setInvertedAppearance(true);
×
1151
    }
1152

1153
    // Sorted list for new presets
1154
    QList<VCXYPadPreset> newPresets;
3✔
1155

1156
    /* Children */
1157
    while (root.readNextStartElement())
15✔
1158
    {
1159
        if (root.name() == KXMLQLCWindowState)
12✔
1160
        {
1161
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
2✔
1162
        }
1163
        else if (root.name() == KXMLQLCVCWidgetAppearance)
10✔
1164
        {
1165
            loadXMLAppearance(root);
2✔
1166
        }
1167
        else if (root.name() == KXMLQLCVCXYPadPan)
8✔
1168
        {
1169
            xpos = root.attributes().value(KXMLQLCVCXYPadPosition).toString().toInt();
2✔
1170
            loadXMLSources(root, panInputSourceId);
1✔
1171
        }
1172
        else if (root.name() == KXMLQLCVCXYPadTilt)
7✔
1173
        {
1174
            ypos = root.attributes().value(KXMLQLCVCXYPadPosition).toString().toInt();
2✔
1175
            loadXMLSources(root, tiltInputSourceId);
1✔
1176
        }
1177
        else if (root.name() == KXMLQLCVCXYPadPanFine)
6✔
1178
        {
1179
            loadXMLSources(root, panFineInputSourceId);
×
1180
        }
1181
        else if (root.name() == KXMLQLCVCXYPadTiltFine)
6✔
1182
        {
1183
            loadXMLSources(root, tiltFineInputSourceId);
×
1184
        }
1185
        else if (root.name() == KXMLQLCVCXYPadWidth)
6✔
1186
        {
1187
            loadXMLSources(root, widthInputSourceId);
×
1188
        }
1189
        else if (root.name() == KXMLQLCVCXYPadHeight)
6✔
1190
        {
1191
            loadXMLSources(root, heightInputSourceId);
×
1192
        }
1193
        else if (root.name() == KXMLQLCVCXYPadRangeWindow)
6✔
1194
        {
1195
            QXmlStreamAttributes wAttrs = root.attributes();
×
1196
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeHorizMin))
×
1197
                m_hRangeSlider->setMinimumPosition(wAttrs.value(KXMLQLCVCXYPadRangeHorizMin).toString().toInt());
×
1198
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeHorizMax))
×
1199
                m_hRangeSlider->setMaximumPosition(wAttrs.value(KXMLQLCVCXYPadRangeHorizMax).toString().toInt());
×
1200
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeVertMin))
×
1201
                m_vRangeSlider->setMinimumPosition(wAttrs.value(KXMLQLCVCXYPadRangeVertMin).toString().toInt());
×
1202
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeVertMax))
×
1203
                m_vRangeSlider->setMaximumPosition(wAttrs.value(KXMLQLCVCXYPadRangeVertMax).toString().toInt());
×
1204
            slotRangeValueChanged();
×
1205
            root.skipCurrentElement();
×
1206
        }
×
1207
        else if (root.name() == KXMLQLCVCXYPadPosition) // Legacy
6✔
1208
        {
1209
            QXmlStreamAttributes pAttrs = root.attributes();
2✔
1210
            xpos = pAttrs.value(KXMLQLCVCXYPadPositionX).toString().toInt();
2✔
1211
            ypos = pAttrs.value(KXMLQLCVCXYPadPositionY).toString().toInt();
2✔
1212
            root.skipCurrentElement();
2✔
1213
        }
2✔
1214
        else if (root.name() == KXMLQLCVCXYPadFixture)
4✔
1215
        {
1216
            VCXYPadFixture fxi(m_doc);
4✔
1217
            if (fxi.loadXML(root) == true)
4✔
1218
                appendFixture(fxi);
4✔
1219
        }
4✔
1220
        else if (root.name() == KXMLQLCVCXYPadPreset)
×
1221
        {
1222
            VCXYPadPreset preset(0xff);
×
1223
            if (preset.loadXML(root))
×
1224
                newPresets.insert(std::lower_bound(newPresets.begin(), newPresets.end(), preset), preset);
×
1225
        }
×
1226
        else
1227
        {
1228
            qWarning() << Q_FUNC_INFO << "Unknown XY Pad tag:" << root.name().toString();
×
1229
            root.skipCurrentElement();
×
1230
        }
1231
    }
1232

1233
    foreach (VCXYPadPreset const& preset, newPresets)
3✔
1234
        addPreset(preset);
3✔
1235

1236
    setGeometry(x, y, w, h);
3✔
1237
    show(); // Qt doesn't update the widget's geometry without this.
3✔
1238
    m_area->setPosition(QPointF(xpos, ypos));
3✔
1239

1240
    return true;
3✔
1241
}
3✔
1242

1243
bool VCXYPad::saveXML(QXmlStreamWriter *doc)
1✔
1244
{
1245
    Q_ASSERT(doc != NULL);
1✔
1246

1247
    /* VC XY Pad entry */
1248
    doc->writeStartElement(KXMLQLCVCXYPad);
2✔
1249

1250
    saveXMLCommon(doc);
1✔
1251

1252
    doc->writeAttribute(KXMLQLCVCXYPadInvertedAppearance, QString::number(invertedAppearance()));
2✔
1253

1254
    /* Window state */
1255
    saveXMLWindowState(doc);
1✔
1256

1257
    /* Appearance */
1258
    saveXMLAppearance(doc);
1✔
1259

1260
    /* Fixtures */
1261
    foreach (VCXYPadFixture fixture, m_fixtures)
3✔
1262
        fixture.saveXML(doc);
3✔
1263

1264
    /* Current XY position */
1265
    QPointF pt(m_area->position(false));
1✔
1266

1267
    /* Custom range window */
1268
    if (m_hRangeSlider->minimumPosition() != 0 ||
1✔
1269
        m_hRangeSlider->maximumPosition() != 256 ||
1✔
1270
        m_vRangeSlider->minimumPosition() != 0 ||
3✔
1271
        m_vRangeSlider->maximumPosition() != 256)
1✔
1272
    {
1273
        doc->writeStartElement(KXMLQLCVCXYPadRangeWindow);
×
1274
        doc->writeAttribute(KXMLQLCVCXYPadRangeHorizMin, QString::number(m_hRangeSlider->minimumPosition()));
×
1275
        doc->writeAttribute(KXMLQLCVCXYPadRangeHorizMax, QString::number(m_hRangeSlider->maximumPosition()));
×
1276
        doc->writeAttribute(KXMLQLCVCXYPadRangeVertMin, QString::number(m_vRangeSlider->minimumPosition()));
×
1277
        doc->writeAttribute(KXMLQLCVCXYPadRangeVertMax, QString::number(m_vRangeSlider->maximumPosition()));
×
1278
        doc->writeEndElement();
×
1279
    }
1280

1281
    /* Pan */
1282
    doc->writeStartElement(KXMLQLCVCXYPadPan);
2✔
1283
    doc->writeAttribute(KXMLQLCVCXYPadPosition, QString::number(int(pt.x())));
2✔
1284
    saveXMLInput(doc, inputSource(panInputSourceId));
1✔
1285
    doc->writeEndElement();
1✔
1286

1287
    /* Tilt */
1288
    doc->writeStartElement(KXMLQLCVCXYPadTilt);
2✔
1289
    doc->writeAttribute(KXMLQLCVCXYPadPosition, QString::number(int(pt.y())));
2✔
1290
    saveXMLInput(doc, inputSource(tiltInputSourceId));
1✔
1291
    doc->writeEndElement();
1✔
1292

1293
    /* Pan Fine */
1294
    QSharedPointer<QLCInputSource> pfSrc = inputSource(panFineInputSourceId);
1✔
1295
    if (!pfSrc.isNull() && pfSrc->isValid())
1✔
1296
    {
1297
        doc->writeStartElement(KXMLQLCVCXYPadPanFine);
2✔
1298
        saveXMLInput(doc, pfSrc);
1✔
1299
        doc->writeEndElement();
1✔
1300
    }
1301

1302
    /* Tilt Fine */
1303
    QSharedPointer<QLCInputSource> tfSrc = inputSource(tiltFineInputSourceId);
1✔
1304
    if (!tfSrc.isNull() && tfSrc->isValid())
1✔
1305
    {
1306
        doc->writeStartElement(KXMLQLCVCXYPadTiltFine);
2✔
1307
        saveXMLInput(doc, tfSrc);
1✔
1308
        doc->writeEndElement();
1✔
1309
    }
1310

1311
    /* Width */
1312
    QSharedPointer<QLCInputSource> wSrc = inputSource(widthInputSourceId);
1✔
1313
    if (!wSrc.isNull() && wSrc->isValid())
1✔
1314
    {
1315
        doc->writeStartElement(KXMLQLCVCXYPadWidth);
2✔
1316
        saveXMLInput(doc, wSrc);
1✔
1317
        doc->writeEndElement();
1✔
1318
    }
1319

1320
    /* Height */
1321
    QSharedPointer<QLCInputSource> hSrc = inputSource(heightInputSourceId);
1✔
1322
    if (!hSrc.isNull() && hSrc->isValid())
1✔
1323
    {
1324
        doc->writeStartElement(KXMLQLCVCXYPadHeight);
2✔
1325
        saveXMLInput(doc, hSrc);
1✔
1326
        doc->writeEndElement();
1✔
1327
    }
1328

1329
    // Presets
1330
    foreach (VCXYPadPreset *preset, presets())
2✔
1331
        preset->saveXML(doc);
1✔
1332

1333
    /* End the >XYPad> tag */
1334
    doc->writeEndElement();
1✔
1335

1336
    return true;
1✔
1337
}
1✔
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