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

mcallegari / qlcplus / 6446977943

08 Oct 2023 10:09AM UTC coverage: 28.056%. Remained the same
6446977943

push

github

mcallegari
vcxypad: fix Scene preset controlling wrong channels (fix #1456)

Discussed: https://www.qlcplus.org/forum/viewtopic.php?p=69373

1 of 1 new or added line in 1 file covered. (100.0%)

15332 of 54647 relevant lines covered (28.06%)

20326.55 hits per line

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

37.37
/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

61
const qreal MAX_VALUE = 256.0;
62
const qreal MAX_DMX_VALUE = MAX_VALUE - 1.0/256;
63

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

70
/*****************************************************************************
71
 * VCXYPad Initialization
72
 *****************************************************************************/
73

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

79
    m_mainVbox = new QVBoxLayout(this);
9✔
80

81
    m_padBox = new QHBoxLayout;
9✔
82
    m_mainVbox->addLayout(m_padBox);
9✔
83

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

91
    m_padBox->addLayout(m_lvbox);
9✔
92

93
    m_cvbox = new QVBoxLayout;
9✔
94
    m_padBox->addLayout(m_cvbox);
9✔
95

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

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

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

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

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

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

126
    m_scene = NULL;
9✔
127

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

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

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

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

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

172
    slotModeChanged(m_doc->mode());
9✔
173
    setLiveEdit(m_liveEdit);
9✔
174

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

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

191
void VCXYPad::enableWidgetUI(bool enable)
11✔
192
{
193
    m_vSlider->setEnabled(enable);
11✔
194
    m_hSlider->setEnabled(enable);
11✔
195
    m_area->setMode(enable ? Doc::Operate : Doc::Design);
11✔
196

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

208
    foreach(QWidget *presetBtn, m_presets.keys())
22✔
209
        presetBtn->setEnabled(enable);
×
210

211
    /* Reset the changed flag in m_area so that the pad won't immediately set a value
212
       when mode is changed */
213
    m_area->position();
11✔
214
}
11✔
215

216
/*****************************************************************************
217
 * Clipboard
218
 *****************************************************************************/
219

220
VCWidget* VCXYPad::createCopy(VCWidget* parent)
1✔
221
{
222
    Q_ASSERT(parent != NULL);
1✔
223

224
    VCXYPad* xypad = new VCXYPad(parent, m_doc);
1✔
225
    if (xypad->copyFrom(this) == false)
1✔
226
    {
227
        delete xypad;
×
228
        xypad = NULL;
×
229
    }
230

231
    return xypad;
1✔
232
}
233

234
bool VCXYPad::copyFrom(const VCWidget* widget)
1✔
235
{
236
    const VCXYPad* xypad = qobject_cast <const VCXYPad*> (widget);
1✔
237
    if (xypad == NULL)
1✔
238
        return false;
×
239
    resize(xypad->size());
1✔
240

241
    /* Get rid of existing channels */
242
    m_fixtures.clear();
1✔
243

244
    /* Copy the other widget's fixtures */
245
    m_fixtures = xypad->fixtures();
1✔
246

247
    /* Copy the current position */
248
    m_area->setPosition(xypad->m_area->position());
1✔
249
    m_vSlider->setValue(xypad->m_vSlider->value());
1✔
250
    m_hSlider->setValue(xypad->m_hSlider->value());
1✔
251

252
    /* Copy common stuff */
253
    return VCWidget::copyFrom(widget);
1✔
254
}
255

256
/*****************************************************************************
257
 * Caption
258
 *****************************************************************************/
259

260
void VCXYPad::setCaption(const QString& text)
12✔
261
{
262
    m_area->setWindowTitle(text);
12✔
263
    VCWidget::setCaption(text);
12✔
264
}
12✔
265

266
bool VCXYPad::invertedAppearance() const
7✔
267
{
268
    return !(m_vSlider->invertedAppearance());
7✔
269
}
270

271
void VCXYPad::setInvertedAppearance(bool invert)
×
272
{
273
    if (invert == true)
×
274
        m_vSlider->setInvertedAppearance(false);
×
275
    else
276
        m_vSlider->setInvertedAppearance(true);
×
277
}
×
278

279
/*****************************************************************************
280
 * Properties
281
 *****************************************************************************/
282

283
void VCXYPad::editProperties()
×
284
{
285
    VCXYPadProperties prop(this, m_doc);
×
286
    if (prop.exec() == QDialog::Accepted)
×
287
        m_doc->setModified();
×
288
}
×
289

290
/*****************************************************************************
291
 * Fixtures
292
 *****************************************************************************/
293

294
void VCXYPad::appendFixture(const VCXYPadFixture& fxi)
17✔
295
{
296
    if (fxi.head().isValid() && m_fixtures.indexOf(fxi) == -1)
17✔
297
        m_fixtures.append(fxi);
13✔
298

299
    updateDegreesRange();
17✔
300
}
17✔
301

302
void VCXYPad::removeFixture(GroupHead const & head)
2✔
303
{
304
    VCXYPadFixture fixture(m_doc);
4✔
305
    fixture.setHead(head);
2✔
306

307
    m_fixtures.removeAll(fixture);
2✔
308

309
    updateDegreesRange();
2✔
310
}
2✔
311

312
void VCXYPad::clearFixtures()
1✔
313
{
314
    m_fixtures.clear();
1✔
315

316
    updateDegreesRange();
1✔
317
}
1✔
318

319
QList <VCXYPadFixture> VCXYPad::fixtures() const
8✔
320
{
321
    return m_fixtures;
8✔
322
}
323

324
QRectF VCXYPad::computeCommonDegreesRange() const
20✔
325
{
326
    QRectF commonRange;
20✔
327

328
    foreach (VCXYPadFixture fixture, m_fixtures)
22✔
329
    {
330
        QRectF range = fixture.degreesRange();
18✔
331
        if (!range.isValid())
18✔
332
            return QRectF();
17✔
333

334
        if (commonRange.isValid())
1✔
335
        {
336
            if (range != commonRange)
×
337
                return QRectF();
×
338
        }
339
        else
340
        {
341
            commonRange = range;
1✔
342
        }
343
    }
344

345
    return commonRange;
3✔
346
}
347

348
void VCXYPad::updateDegreesRange()
20✔
349
{
350
    QRectF range = computeCommonDegreesRange();
20✔
351

352
    m_area->setDegreesRange(range);
20✔
353
}
20✔
354

355
/*****************************************************************************
356
 * Current XY position
357
 *****************************************************************************/
358

359
void VCXYPad::writeDMX(MasterTimer* timer, QList<Universe *> universes)
×
360
{
361
    if (m_scene != NULL)
×
362
        writeScenePositions(timer, universes);
×
363
    else
364
        writeXYFixtures(timer, universes);
×
365
}
×
366

367
void VCXYPad::writeXYFixtures(MasterTimer *timer, QList<Universe *> universes)
×
368
{
369
    Q_UNUSED(timer);
370

371
    if (m_area->hasPositionChanged() == false)
×
372
        return;
×
373

374
    // This call also resets the m_changed flag in m_area
375
    QPointF pt = m_area->position();
×
376

377
    /* Scale XY coordinate values to 0.0 - 1.0 */
378
    qreal x = SCALE(pt.x(), qreal(0), qreal(256), qreal(0), qreal(1));
×
379
    qreal y = SCALE(pt.y(), qreal(0), qreal(256), qreal(0), qreal(1));
×
380

381
    if (invertedAppearance())
×
382
        y = qreal(1) - y;
×
383

384
    /* Write values outside of mutex lock to keep UI snappy */
385
    foreach (VCXYPadFixture fixture, m_fixtures)
×
386
    {
387
        if (fixture.isEnabled())
×
388
        {
389
            quint32 universe = fixture.universe();
×
390
            if (universe == Universe::invalid())
×
391
                continue;
×
392

393
            QSharedPointer<GenericFader> fader = m_fadersMap.value(universe, QSharedPointer<GenericFader>());
×
394
            if (fader.isNull())
×
395
            {
396
                fader = universes[universe]->requestFader();
×
397
                fader->adjustIntensity(intensity());
×
398
                m_fadersMap[universe] = fader;
×
399
            }
400
            fixture.writeDMX(x, y, fader, universes[universe]);
×
401
        }
402
    }
403
}
404

405
void VCXYPad::updateSceneChannel(FadeChannel *fc, uchar value)
×
406
{
407
    fc->addFlag(FadeChannel::Relative);
×
408
    fc->setStart(value);
×
409
    fc->setCurrent(value);
×
410
    fc->setTarget(value);
×
411
    fc->setElapsed(0);
×
412
    fc->setReady(false);
×
413
}
×
414

415
void VCXYPad::writeScenePositions(MasterTimer *timer, QList<Universe *> universes)
×
416
{
417
    Q_UNUSED(timer);
418

419
    if (m_scene == NULL || m_scene->isRunning() == false)
×
420
        return;
×
421

422
    QPointF pt = m_area->position();
×
423
    uchar panCoarse = uchar(qFloor(pt.x()));
×
424
    uchar panFine = uchar((pt.x() - qFloor(pt.x())) * 256);
×
425
    uchar tiltCoarse = uchar(qFloor(pt.y()));
×
426
    uchar tiltFine = uchar((pt.y() - qFloor(pt.y())) * 256);
×
427

428
    foreach(SceneChannel sc, m_sceneChannels)
×
429
    {
430
        if (sc.m_universe >= (quint32)universes.count())
×
431
            continue;
×
432

433
        QSharedPointer<GenericFader> fader = m_fadersMap.value(sc.m_universe, QSharedPointer<GenericFader>());
×
434
        if (fader.isNull())
×
435
        {
436
            fader = universes[sc.m_universe]->requestFader();
×
437
            fader->adjustIntensity(intensity());
×
438
            m_fadersMap[sc.m_universe] = fader;
×
439
        }
440

441
        if (sc.m_group == QLCChannel::Pan)
×
442
        {
443
            if (sc.m_subType == QLCChannel::MSB)
×
444
            {
445
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
446
                updateSceneChannel(fc, panCoarse);
×
447
            }
448
            else
449
            {
450
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
451
                updateSceneChannel(fc, panFine);
×
452
            }
453
        }
454
        else
455
        {
456
            if (sc.m_subType == QLCChannel::MSB)
×
457
            {
458
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
459
                updateSceneChannel(fc, tiltCoarse);
×
460
            }
461
            else
462
            {
463
                FadeChannel *fc = fader->getChannelFader(m_doc, universes[sc.m_universe], sc.m_fixture, sc.m_channel);
×
464
                updateSceneChannel(fc, tiltFine);
×
465
            }
466
        }
467
    }
468
}
469

470
void VCXYPad::slotPositionChanged(const QPointF& pt)
6✔
471
{
472
    if (m_sliderInteraction == true)
6✔
473
        return;
×
474

475
    m_padInteraction = true;
6✔
476
    m_hSlider->setValue(pt.x());
6✔
477
    if (invertedAppearance() == false)
6✔
478
    {
479
        m_vSlider->setValue(pt.y());
6✔
480
    }
481
    else
482
    {
483
        m_vSlider->setValue(MAX_DMX_VALUE - pt.y());
×
484
    }
485

486
    if (m_inputValueChanged == false)
6✔
487
        updateFeedback();
6✔
488
    m_padInteraction = false;
6✔
489
    m_inputValueChanged = false;
6✔
490
}
491

492
void VCXYPad::slotSliderValueChanged()
10✔
493
{
494
    if (m_padInteraction == true)
10✔
495
        return;
10✔
496

497
    QPointF pt = m_area->position(false);
×
498

499
    m_sliderInteraction = true;
×
500
    if (QObject::sender() == m_hSlider)
×
501
    {
502
        pt.setX(m_hSlider->value());
×
503

504
        int Xfb = (int)SCALE(float(m_hSlider->value()), float(m_hSlider->minimum()),
×
505
                             float(m_hSlider->maximum()), float(0), float(UCHAR_MAX));
506
        sendFeedback(Xfb, panInputSourceId);
×
507
    }
508
    else
509
    {
510
        if (invertedAppearance() == false)
×
511
            pt.setY(m_vSlider->value());
×
512
        else
513
            pt.setY(MAX_DMX_VALUE - m_vSlider->value());
×
514

515
        int Yfb = (int)SCALE(float(m_vSlider->value()), float(m_vSlider->minimum()),
×
516
                             float(m_vSlider->maximum()), float(0), float(UCHAR_MAX));
517
        sendFeedback(Yfb, tiltInputSourceId);
×
518
    }
519

520
    m_area->setPosition(pt);
×
521
    m_area->update();
×
522
    m_sliderInteraction = false;
×
523
}
524

525
void VCXYPad::slotRangeValueChanged()
×
526
{
527
    QRectF rect(QPointF(m_hRangeSlider->minimumPosition(), m_vRangeSlider->minimumPosition()),
×
528
               QPointF(m_hRangeSlider->maximumPosition(), m_vRangeSlider->maximumPosition()));
×
529
    m_area->setRangeWindow(rect);
×
530
    if (m_efx != NULL && m_efx->isRunning())
×
531
    {
532
        m_efx->adjustAttribute(rect.x() + rect.width() / 2, m_efxStartXOverrideId);
×
533
        m_efx->adjustAttribute(rect.y() + rect.height() / 2, m_efxStartYOverrideId);
×
534
        m_efx->adjustAttribute(rect.width() / 2, m_efxWidthOverrideId);
×
535
        m_efx->adjustAttribute(rect.height() / 2, m_efxHeightOverrideId);
×
536

537
        // recalculate preview polygons
538
        QPolygonF polygon;
×
539
        m_efx->preview(polygon);
×
540

541
        QVector <QPolygonF> fixturePoints;
×
542
        m_efx->previewFixtures(fixturePoints);
×
543

544
        m_area->setEFXPolygons(polygon, fixturePoints);
×
545
        m_area->setEFXInterval(m_efx->duration());
×
546
    }
547
    m_area->update();
×
548
    if (QObject::sender() == m_hRangeSlider)
×
549
        sendFeedback(m_hRangeSlider->maximumValue(), heightInputSourceId);
×
550
    else if(QObject::sender() == m_vRangeSlider)
×
551
        sendFeedback(m_vRangeSlider->maximumValue(), widthInputSourceId);
×
552
}
×
553

554
void VCXYPad::slotUniverseWritten(quint32 idx, const QByteArray &universeData)
×
555
{
556
    QVariantList positions;
×
557

558
    if (m_scene)
×
559
    {
560
        QMap <quint32, QPointF> fxMap;
×
561

562
        foreach(SceneChannel sc, m_sceneChannels)
×
563
        {
564
            if (sc.m_universe != idx)
×
565
                continue;
×
566

567
            qreal x = fxMap[sc.m_fixture].x();
×
568
            qreal y = fxMap[sc.m_fixture].y();
×
569

570
            if (sc.m_group == QLCChannel::Pan)
×
571
            {
572
                if (sc.m_subType == QLCChannel::MSB)
×
573
                    x += (uchar)universeData.at(sc.m_channel);
×
574
                else
575
                    x += ((uchar)universeData.at(sc.m_channel) / 255);
×
576
            }
577
            else
578
            {
579
                if (sc.m_subType == QLCChannel::MSB)
×
580
                    y += (uchar)universeData.at(sc.m_channel);
×
581
                else
582
                    y += ((uchar)universeData.at(sc.m_channel) / 255);
×
583
            }
584
            fxMap[sc.m_fixture] = QPointF(x, y);
×
585
        }
586

587
        foreach(QPointF pt, fxMap.values())
×
588
        {
589
            if (invertedAppearance())
×
590
                pt.setY(256 - pt.y());
×
591
            positions.append(pt);
×
592
        }
593
    }
594
    else
595
    {
596
        foreach (VCXYPadFixture fixture, m_fixtures)
×
597
        {
598
            if (fixture.isEnabled() == false)
×
599
                continue;
×
600

601
            if (fixture.universe() != idx)
×
602
                continue;
×
603

604
            qreal x(-1), y(-1);
×
605
            fixture.readDMX(universeData, x, y);
×
606
            if( x != -1.0 && y != -1.0)
×
607
            {
608
                if (invertedAppearance())
×
609
                    y = qreal(1) - y;
×
610

611
               x *= 256;
×
612
               y *= 256;
×
613
               positions.append(QPointF(x, y));
×
614
            }
615
        }
616
    }
617

618
    emit fixturePositions(positions);
×
619
}
×
620

621
/*********************************************************************
622
 * Presets
623
 *********************************************************************/
624

625
void VCXYPad::addPreset(const VCXYPadPreset &preset)
×
626
{
627
    QString label = preset.m_name;
×
628

629
    if(label.isEmpty())
×
630
    {
631
        qDebug() << "VCXYPad Preset label empty. Not adding it";
×
632
        return;
×
633
    }
634

635
    QPushButton *presetButton = new QPushButton(this);
×
636
    QWidget *presetWidget = presetButton;
×
637
    presetButton->setStyleSheet(presetBtnSS.arg(preset.getColor()));
×
638
    presetButton->setMinimumWidth(36);
×
639
    presetButton->setMaximumWidth(80);
×
640
    presetButton->setFocusPolicy(Qt::TabFocus);
×
641
    presetButton->setText(fontMetrics().elidedText(label, Qt::ElideRight, 72));
×
642
    if (preset.m_type == VCXYPadPreset::EFX ||
×
643
        preset.m_type == VCXYPadPreset::Scene ||
×
644
        preset.m_type == VCXYPadPreset::FixtureGroup)
×
645
            presetButton->setCheckable(true);
×
646

647
    connect(presetButton, SIGNAL(clicked(bool)),
×
648
            this, SLOT(slotPresetClicked(bool)));
649

650
    if (mode() == Doc::Design)
×
651
        presetWidget->setEnabled(false);
×
652

653
    m_presets[presetWidget] = new VCXYPadPreset(preset);
×
654
    m_presetsLayout->addWidget(presetWidget);
×
655

656
    if (m_presets[presetWidget]->m_inputSource != NULL)
×
657
    {
658
        setInputSource(m_presets[presetWidget]->m_inputSource, m_presets[presetWidget]->m_id);
×
659
    }
660
}
661

662
void VCXYPad::resetPresets()
×
663
{
664
    for (QHash<QWidget *, VCXYPadPreset *>::iterator it = m_presets.begin();
×
665
            it != m_presets.end(); ++it)
×
666
    {
667
        QWidget* widget = it.key();
×
668
        m_presetsLayout->removeWidget(widget);
×
669
        delete widget;
×
670

671
        VCXYPadPreset* preset = it.value();
×
672
        if (!preset->m_inputSource.isNull())
×
673
            setInputSource(QSharedPointer<QLCInputSource>(), preset->m_id);
×
674
        delete preset;
×
675
    }
676
    m_presets.clear();
×
677
}
×
678

679
QList<VCXYPadPreset *> VCXYPad::presets() const
1✔
680
{
681
    QList<VCXYPadPreset*> presets = m_presets.values();
1✔
682
    std::sort(presets.begin(), presets.end(), VCXYPadPreset::compare);
1✔
683
    return presets;
1✔
684
}
685

686
void VCXYPad::slotPresetClicked(bool checked)
×
687
{
688
    if (mode() == Doc::Design)
×
689
        return;
×
690

691
    QPushButton *btn = qobject_cast<QPushButton*>(sender());
×
692
    VCXYPadPreset *preset = m_presets[btn];
×
693

694
    Q_ASSERT(preset != NULL);
×
695

696
    // stop any previously started EFX
697
    if (m_efx != NULL && m_efx->isRunning())
×
698
    {
699
        disconnect(m_efx, SIGNAL(durationChanged(uint)), this, SLOT(slotEFXDurationChanged(uint)));
×
700

701
        m_efx->stopAndWait();
×
702
        m_efx = NULL;
×
703
        m_efxStartXOverrideId = Function::invalidAttributeId();
×
704
        m_efxStartYOverrideId = Function::invalidAttributeId();
×
705
        m_efxWidthOverrideId = Function::invalidAttributeId();
×
706
        m_efxHeightOverrideId = Function::invalidAttributeId();
×
707
    }
708

709
    // stop any previously started Scene
710
    if (m_scene != NULL)
×
711
    {
712
        m_scene->stop(functionParent());
×
713
        m_scene = NULL;
×
714
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap.values())
×
715
        {
716
            if (!fader.isNull())
×
717
                fader->requestDelete();
×
718
        }
719
        m_fadersMap.clear();
×
720
    }
721

722
    // deactivate all previously activated buttons first
723
    for (QHash<QWidget *, VCXYPadPreset *>::iterator it = m_presets.begin();
×
724
            it != m_presets.end(); ++it)
×
725
    {
726
        QPushButton* cBtn = reinterpret_cast<QPushButton*>(it.key());
×
727
        VCXYPadPreset *cPr = it.value();
×
728
        if (preset->m_id == cPr->m_id)
×
729
            continue;
×
730

731
        cBtn->blockSignals(true);
×
732
        if (preset->m_type == VCXYPadPreset::FixtureGroup)
×
733
        {
734
            if (cPr->m_type == VCXYPadPreset::FixtureGroup &&
×
735
                cBtn->isChecked() == true)
×
736
            {
737
                cBtn->setChecked(false);
×
738
                if (cPr->m_inputSource.isNull() == false)
×
739
                    sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource);
×
740
            }
741
        }
742
        else if (cPr->m_type == VCXYPadPreset::EFX ||
×
743
            cPr->m_type == VCXYPadPreset::Scene)
×
744
        {
745
            if (cBtn->isChecked() == true)
×
746
            {
747
                cBtn->setChecked(false);
×
748
                if (cPr->m_inputSource.isNull() == false)
×
749
                    sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource);
×
750
            }
751
        }
752
        else
753
        {
754
            if (cBtn->isDown() == true)
×
755
            {
756
                cBtn->setDown(false);
×
757
                if (cPr->m_inputSource.isNull() == false)
×
758
                    sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource);
×
759
            }
760
        }
761
        cBtn->blockSignals(false);
×
762
        if (cPr->m_inputSource.isNull() == false)
×
763
            sendFeedback(cPr->m_inputSource->lowerValue(), cPr->m_inputSource);
×
764
    }
765

766
    if (preset->m_type == VCXYPadPreset::EFX)
×
767
    {
768
        if (checked == false)
×
769
        {
770
            m_area->enableEFXPreview(false);
×
771
            return;
×
772
        }
773

774
        Function *f = m_doc->function(preset->m_funcID);
×
775
        if (f == NULL || f->type() != Function::EFXType)
×
776
            return;
×
777
        m_efx = qobject_cast<EFX*>(f);
×
778

779
        QRectF rect(QPointF(m_hRangeSlider->minimumPosition(), m_vRangeSlider->minimumPosition()),
×
780
                   QPointF(m_hRangeSlider->maximumPosition(), m_vRangeSlider->maximumPosition()));
×
781
        m_area->setRangeWindow(rect);
×
782
        if (rect.isValid())
×
783
        {
784
            m_efxStartXOverrideId = m_efx->requestAttributeOverride(EFX::XOffset, rect.x() + rect.width() / 2);
×
785
            m_efxStartYOverrideId = m_efx->requestAttributeOverride(EFX::YOffset, rect.y() + rect.height() / 2);
×
786
            m_efxWidthOverrideId = m_efx->requestAttributeOverride(EFX::Width, rect.width() / 2);
×
787
            m_efxHeightOverrideId = m_efx->requestAttributeOverride(EFX::Height, rect.height() / 2);
×
788
        }
789

790
        QPolygonF polygon;
×
791
        m_efx->preview(polygon);
×
792

793
        QVector <QPolygonF> fixturePoints;
×
794
        m_efx->previewFixtures(fixturePoints);
×
795

796
        m_area->enableEFXPreview(true);
×
797
        m_area->setEFXPolygons(polygon, fixturePoints);
×
798
        m_area->setEFXInterval(m_efx->duration());
×
799
        m_efx->start(m_doc->masterTimer(), functionParent());
×
800

801
        connect(m_efx, SIGNAL(durationChanged(uint)), this, SLOT(slotEFXDurationChanged(uint)));
×
802

803
        if (preset->m_inputSource.isNull() == false)
×
804
            sendFeedback(preset->m_inputSource->upperValue(), preset->m_inputSource);
×
805
    }
806
    else if (preset->m_type == VCXYPadPreset::Scene)
×
807
    {
808
        if (checked == false)
×
809
            return;
×
810

811
        Function *f = m_doc->function(preset->m_funcID);
×
812
        if (f == NULL || f->type() != Function::SceneType)
×
813
            return;
×
814

815
        m_scene = qobject_cast<Scene*>(f);
×
816
        m_sceneChannels.clear();
×
817

818
        foreach(SceneValue scv, m_scene->values())
×
819
        {
820
            Fixture *fixture = m_doc->fixture(scv.fxi);
×
821
            if (fixture == NULL)
×
822
                continue;
×
823
            const QLCChannel *ch = fixture->channel(scv.channel);
×
824
            if (ch == NULL)
×
825
                continue;
×
826
            if (ch->group() != QLCChannel::Pan && ch->group() != QLCChannel::Tilt)
×
827
                continue;
×
828

829
            SceneChannel sChan;
830
            sChan.m_universe = fixture->universe();
×
831
            sChan.m_fixture = fixture->id();
×
832
            sChan.m_channel = scv.channel;
×
833
            sChan.m_group = ch->group();
×
834
            sChan.m_subType = ch->controlByte();
×
835
            m_sceneChannels.append(sChan);
×
836
        }
837

838
        m_area->enableEFXPreview(false);
×
839
        // reset the area window as we're switching to relative
840
        m_area->setRangeWindow(QRectF());
×
841
        m_area->setPosition(QPointF(128, 128));
×
842
        m_area->repaint();
×
843
        m_scene->start(m_doc->masterTimer(), functionParent());
×
844

845
        if (preset->m_inputSource.isNull() == false)
×
846
            sendFeedback(preset->m_inputSource->upperValue(), preset->m_inputSource);
×
847
    }
848
    else if (preset->m_type == VCXYPadPreset::Position)
×
849
    {
850
        m_area->enableEFXPreview(false);
×
851
        QRectF rect(QPointF(m_hRangeSlider->minimumPosition(), m_vRangeSlider->minimumPosition()),
×
852
                   QPointF(m_hRangeSlider->maximumPosition(), m_vRangeSlider->maximumPosition()));
×
853
        m_area->setRangeWindow(rect);
×
854
        m_area->setPosition(preset->m_dmxPos);
×
855
        m_area->repaint();
×
856
        if (preset->m_inputSource.isNull() == false)
×
857
            sendFeedback(preset->m_inputSource->upperValue(), preset->m_inputSource);
×
858
        btn->blockSignals(true);
×
859
        btn->setDown(true);
×
860
        btn->blockSignals(false);
×
861
    }
862
    else if (preset->m_type == VCXYPadPreset::FixtureGroup)
×
863
    {
864
        QList<GroupHead> heads = preset->fixtureGroup();
×
865

866
        for (int i = 0; i < m_fixtures.count(); i++)
×
867
        {
868
            if (checked == false)
×
869
            {
870
                m_fixtures[i].setEnabled(true);
×
871
            }
872
            else
873
            {
874
                if (heads.contains(m_fixtures[i].head()))
×
875
                {
876
                    qDebug() << "Enabling head" << m_fixtures[i].head().fxi << m_fixtures[i].head().head;
×
877
                    m_fixtures[i].setEnabled(true);
×
878
                }
879
                else
880
                {
881
                    qDebug() << "Disabling head" << m_fixtures[i].head().fxi << m_fixtures[i].head().head;
×
882
                    m_fixtures[i].setEnabled(false);
×
883
                }
884
            }
885
        }
886
    }
887
}
888

889
void VCXYPad::slotEFXDurationChanged(uint duration)
×
890
{
891
    if (m_efx == NULL)
×
892
        return;
×
893

894
    m_area->setEFXInterval(duration);
×
895
}
896

897
FunctionParent VCXYPad::functionParent() const
×
898
{
899
    return FunctionParent(FunctionParent::ManualVCWidget, id());
×
900
}
901

902
/*********************************************************************
903
 * External input
904
 *********************************************************************/
905

906
void VCXYPad::updateFeedback()
6✔
907
{
908
    int Xfb = (int)SCALE(float(m_hSlider->value()), float(m_hSlider->minimum()),
6✔
909
                         float(m_hSlider->maximum()), float(0), float(UCHAR_MAX));
910
    sendFeedback(Xfb, panInputSourceId);
6✔
911

912
    int Yfb = (int)SCALE(float(m_vSlider->value()), float(m_vSlider->minimum()),
6✔
913
                         float(m_vSlider->maximum()), float(0), float(UCHAR_MAX));
914
    sendFeedback(Yfb, tiltInputSourceId);
6✔
915

916
/*
917
    for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
918
            it != m_presets.end(); ++it)
919
    {
920
        VCXYPadPreset* preset = it.value();
921
        if (preset->m_inputSource != NULL)
922
        {
923
            {
924
                QPushButton* button = reinterpret_cast<QPushButton*>(it.key());
925
                if (preset->m_inputSource.isNull() == false)
926
                    sendFeedback(button->isDown() ?
927
                                 preset->m_inputSource->upperValue() :
928
                                 preset->m_inputSource->lowerValue(),
929
                                 preset->m_inputSource);
930
            }
931
        }
932
    }
933
*/
934
}
6✔
935

936
void VCXYPad::slotInputValueChanged(quint32 universe, quint32 channel,
×
937
                                     uchar value)
938
{
939
    /* Don't let input data through in design mode or if disabled */
940
    if (acceptsInput() == false)
×
941
        return;
×
942

943
    QPointF pt = m_area->position(false);
×
944
    quint32 pagedCh = (page() << 16) | channel;
×
945

946
    if (checkInputSource(universe, pagedCh, value, sender(), panInputSourceId))
×
947
    {
948
        if (m_efx == NULL)
×
949
        {
950
            qreal areaWidth = MAX_VALUE;
×
951
            qreal xOffset = 0;
×
952
            QRectF rangeWindow = m_area->rangeWindow();
×
953
            if (rangeWindow.isValid())
×
954
            {
955
                areaWidth = rangeWindow.width();
×
956
                xOffset = rangeWindow.x();
×
957
            }
958
            pt.setX(xOffset + SCALE(qreal(value), qreal(0), qreal(255),
×
959
                          qreal(0), areaWidth));
960
        }
961
        else
962
        {
963
            if (m_efx->isRunning() == false)
×
964
                return;
×
965
            m_hRangeSlider->setMinimumValue(value);
×
966
            slotRangeValueChanged();
×
967
            return;
×
968
        }
969
    }
970
    else if (checkInputSource(universe, pagedCh, value, sender(), tiltInputSourceId))
×
971
    {
972
        if (m_efx == NULL)
×
973
        {
974
            qreal yOffset = 0;
×
975
            qreal areaHeight = MAX_VALUE;
×
976
            QRectF rangeWindow = m_area->rangeWindow();
×
977
            if (rangeWindow.isValid())
×
978
            {
979
                areaHeight = rangeWindow.height();
×
980
                yOffset = rangeWindow.y();
×
981
            }
982
            if (invertedAppearance() == false)
×
983
                pt.setY(yOffset + SCALE(qreal(value), qreal(0), qreal(255),
×
984
                              qreal(0), areaHeight));
985
            else
986
                pt.setY(yOffset + SCALE(qreal(value), qreal(255), qreal(0),
×
987
                              qreal(0), areaHeight));
988
        }
989
        else
990
        {
991
            if (m_efx->isRunning() == false)
×
992
                return;
×
993
            m_vRangeSlider->setMinimumValue(value);
×
994
            slotRangeValueChanged();
×
995
            return;
×
996
        }
997
    }
998
    else if (checkInputSource(universe, pagedCh, value, sender(), widthInputSourceId))
×
999
    {
1000
        if (m_efx != NULL && m_efx->isRunning())
×
1001
        {
1002
            m_hRangeSlider->setMaximumValue(value);
×
1003
            slotRangeValueChanged();
×
1004
        }
1005
        return;
×
1006
    }
1007
    else if (checkInputSource(universe, pagedCh, value, sender(), heightInputSourceId))
×
1008
    {
1009
        if (m_efx != NULL && m_efx->isRunning())
×
1010
        {
1011
            m_vRangeSlider->setMaximumValue(value);
×
1012
            slotRangeValueChanged();
×
1013
        }
1014
        return;
×
1015
    }
1016
    else
1017
    {
1018
        for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
×
1019
                it != m_presets.end(); ++it)
×
1020
        {
1021
            VCXYPadPreset *preset = it.value();
×
1022
            if (preset->m_inputSource != NULL &&
×
1023
                    preset->m_inputSource->universe() == universe &&
×
1024
                    preset->m_inputSource->channel() == pagedCh)
×
1025
            {
1026
                {
1027
                    QPushButton *button = reinterpret_cast<QPushButton*>(it.key());
×
1028
                    button->click();
×
1029
                    return;
×
1030
                }
1031
            }
1032
        }
1033
    }
1034

1035
    m_inputValueChanged = true;
×
1036

1037
    m_area->setPosition(pt);
×
1038
    m_area->update();
×
1039
}
1040

1041
void VCXYPad::slotKeyPressed(const QKeySequence &keySequence)
×
1042
{
1043
    if (acceptsInput() == false)
×
1044
        return;
×
1045

1046
    for (QHash<QWidget*, VCXYPadPreset*>::iterator it = m_presets.begin();
×
1047
            it != m_presets.end(); ++it)
×
1048
    {
1049
        VCXYPadPreset *preset = it.value();
×
1050
        if (preset->m_keySequence == keySequence)
×
1051
        {
1052
            QPushButton *button = reinterpret_cast<QPushButton*>(it.key());
×
1053
            button->click();
×
1054
        }
1055
    }
1056
}
1057

1058
/*****************************************************************************
1059
 * QLC mode
1060
 *****************************************************************************/
1061

1062
void VCXYPad::slotModeChanged(Doc::Mode mode)
11✔
1063
{
1064
    if (mode == Doc::Operate && isDisabled() == false)
11✔
1065
    {
1066
        enableWidgetUI(true);
1✔
1067
    }
1068
    else
1069
    {
1070
        enableWidgetUI(false);
10✔
1071
    }
1072

1073
    VCWidget::slotModeChanged(mode);
11✔
1074
}
11✔
1075

1076
/*****************************************************************************
1077
 * Load & Save
1078
 *****************************************************************************/
1079

1080
bool VCXYPad::loadXML(QXmlStreamReader &root)
4✔
1081
{
1082
    bool visible = false;
4✔
1083
    int x = 0;
4✔
1084
    int y = 0;
4✔
1085
    int w = 0;
4✔
1086
    int h = 0;
4✔
1087

1088
    int xpos = 0;
4✔
1089
    int ypos = 0;
4✔
1090

1091
    if (root.name() != KXMLQLCVCXYPad)
4✔
1092
    {
1093
        qWarning() << Q_FUNC_INFO << "XY Pad node not found";
1✔
1094
        return false;
1✔
1095
    }
1096

1097
    /* Widget commons */
1098
    loadXMLCommon(root);
3✔
1099

1100
    QXmlStreamAttributes attrs = root.attributes();
6✔
1101

1102
    if (attrs.hasAttribute(KXMLQLCVCXYPadInvertedAppearance))
3✔
1103
    {
1104
        if (attrs.value(KXMLQLCVCXYPadInvertedAppearance).toString() == "0")
×
1105
            setInvertedAppearance(false);
×
1106
        else
1107
            setInvertedAppearance(true);
×
1108
    }
1109

1110
    // Sorted list for new presets
1111
    QList<VCXYPadPreset> newPresets;
3✔
1112

1113
    /* Children */
1114
    while (root.readNextStartElement())
15✔
1115
    {
1116
        if (root.name() == KXMLQLCWindowState)
12✔
1117
        {
1118
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
2✔
1119
        }
1120
        else if (root.name() == KXMLQLCVCWidgetAppearance)
10✔
1121
        {
1122
            loadXMLAppearance(root);
2✔
1123
        }
1124
        else if (root.name() == KXMLQLCVCXYPadPan)
8✔
1125
        {
1126
            xpos = root.attributes().value(KXMLQLCVCXYPadPosition).toString().toInt();
1✔
1127
            loadXMLSources(root, panInputSourceId);
1✔
1128
        }
1129
        else if (root.name() == KXMLQLCVCXYPadTilt)
7✔
1130
        {
1131
            ypos = root.attributes().value(KXMLQLCVCXYPadPosition).toString().toInt();
1✔
1132
            loadXMLSources(root, tiltInputSourceId);
1✔
1133
        }
1134
        else if (root.name() == KXMLQLCVCXYPadWidth)
6✔
1135
        {
1136
            loadXMLSources(root, widthInputSourceId);
×
1137
        }
1138
        else if (root.name() == KXMLQLCVCXYPadHeight)
6✔
1139
        {
1140
            loadXMLSources(root, heightInputSourceId);
×
1141
        }
1142
        else if (root.name() == KXMLQLCVCXYPadRangeWindow)
6✔
1143
        {
1144
            QXmlStreamAttributes wAttrs = root.attributes();
×
1145
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeHorizMin))
×
1146
                m_hRangeSlider->setMinimumPosition(wAttrs.value(KXMLQLCVCXYPadRangeHorizMin).toString().toInt());
×
1147
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeHorizMax))
×
1148
                m_hRangeSlider->setMaximumPosition(wAttrs.value(KXMLQLCVCXYPadRangeHorizMax).toString().toInt());
×
1149
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeVertMin))
×
1150
                m_vRangeSlider->setMinimumPosition(wAttrs.value(KXMLQLCVCXYPadRangeVertMin).toString().toInt());
×
1151
            if (wAttrs.hasAttribute(KXMLQLCVCXYPadRangeVertMax))
×
1152
                m_vRangeSlider->setMaximumPosition(wAttrs.value(KXMLQLCVCXYPadRangeVertMax).toString().toInt());
×
1153
            slotRangeValueChanged();
×
1154
            root.skipCurrentElement();
×
1155
        }
1156
        else if (root.name() == KXMLQLCVCXYPadPosition) // Legacy
6✔
1157
        {
1158
            QXmlStreamAttributes pAttrs = root.attributes();
4✔
1159
            xpos = pAttrs.value(KXMLQLCVCXYPadPositionX).toString().toInt();
2✔
1160
            ypos = pAttrs.value(KXMLQLCVCXYPadPositionY).toString().toInt();
2✔
1161
            root.skipCurrentElement();
2✔
1162
        }
1163
        else if (root.name() == KXMLQLCVCXYPadFixture)
4✔
1164
        {
1165
            VCXYPadFixture fxi(m_doc);
8✔
1166
            if (fxi.loadXML(root) == true)
4✔
1167
                appendFixture(fxi);
4✔
1168
        }
1169
        else if(root.name() == KXMLQLCVCXYPadPreset)
×
1170
        {
1171
            VCXYPadPreset preset(0xff);
×
1172
            if (preset.loadXML(root))
×
1173
                newPresets.insert(std::lower_bound(newPresets.begin(), newPresets.end(), preset), preset);
×
1174
        }
1175
        else
1176
        {
1177
            qWarning() << Q_FUNC_INFO << "Unknown XY Pad tag:" << root.name().toString();
×
1178
            root.skipCurrentElement();
×
1179
        }
1180
    }
1181

1182
    foreach (VCXYPadPreset const& preset, newPresets)
3✔
1183
        addPreset(preset);
×
1184

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

1189
    return true;
3✔
1190
}
1191

1192
bool VCXYPad::saveXML(QXmlStreamWriter *doc)
1✔
1193
{
1194
    Q_ASSERT(doc != NULL);
1✔
1195

1196
    /* VC XY Pad entry */
1197
    doc->writeStartElement(KXMLQLCVCXYPad);
1✔
1198

1199
    saveXMLCommon(doc);
1✔
1200

1201
    doc->writeAttribute(KXMLQLCVCXYPadInvertedAppearance, QString::number(invertedAppearance()));
1✔
1202

1203
    /* Window state */
1204
    saveXMLWindowState(doc);
1✔
1205

1206
    /* Appearance */
1207
    saveXMLAppearance(doc);
1✔
1208

1209
    /* Fixtures */
1210
    foreach (VCXYPadFixture fixture, m_fixtures)
5✔
1211
        fixture.saveXML(doc);
2✔
1212

1213
    /* Current XY position */
1214
    QPointF pt(m_area->position(false));
1✔
1215

1216
    /* Custom range window */
1217
    if (m_hRangeSlider->minimumPosition() != 0 ||
1✔
1218
        m_hRangeSlider->maximumPosition() != 256 ||
1✔
1219
        m_vRangeSlider->minimumPosition() != 0 ||
3✔
1220
        m_vRangeSlider->maximumPosition() != 256)
1✔
1221
    {
1222
        doc->writeStartElement(KXMLQLCVCXYPadRangeWindow);
×
1223
        doc->writeAttribute(KXMLQLCVCXYPadRangeHorizMin, QString::number(m_hRangeSlider->minimumPosition()));
×
1224
        doc->writeAttribute(KXMLQLCVCXYPadRangeHorizMax, QString::number(m_hRangeSlider->maximumPosition()));
×
1225
        doc->writeAttribute(KXMLQLCVCXYPadRangeVertMin, QString::number(m_vRangeSlider->minimumPosition()));
×
1226
        doc->writeAttribute(KXMLQLCVCXYPadRangeVertMax, QString::number(m_vRangeSlider->maximumPosition()));
×
1227
        doc->writeEndElement();
×
1228
    }
1229

1230
    /* Pan */
1231
    doc->writeStartElement(KXMLQLCVCXYPadPan);
1✔
1232
    doc->writeAttribute(KXMLQLCVCXYPadPosition, QString::number(int(pt.x())));
1✔
1233
    saveXMLInput(doc, inputSource(panInputSourceId));
1✔
1234
    doc->writeEndElement();
1✔
1235

1236
    /* Tilt */
1237
    doc->writeStartElement(KXMLQLCVCXYPadTilt);
1✔
1238
    doc->writeAttribute(KXMLQLCVCXYPadPosition, QString::number(int(pt.y())));
1✔
1239
    saveXMLInput(doc, inputSource(tiltInputSourceId));
1✔
1240
    doc->writeEndElement();
1✔
1241

1242
    /* Width */
1243
    QSharedPointer<QLCInputSource> wSrc = inputSource(widthInputSourceId);
2✔
1244
    if (!wSrc.isNull() && wSrc->isValid())
1✔
1245
    {
1246
        doc->writeStartElement(KXMLQLCVCXYPadWidth);
1✔
1247
        saveXMLInput(doc, wSrc);
1✔
1248
        doc->writeEndElement();
1✔
1249
    }
1250

1251
    /* Height */
1252
    QSharedPointer<QLCInputSource> hSrc = inputSource(heightInputSourceId);
1✔
1253
    if (!hSrc.isNull() && hSrc->isValid())
1✔
1254
    {
1255
        doc->writeStartElement(KXMLQLCVCXYPadHeight);
1✔
1256
        saveXMLInput(doc, hSrc);
1✔
1257
        doc->writeEndElement();
1✔
1258
    }
1259

1260
    // Presets
1261
    foreach(VCXYPadPreset *preset, presets())
2✔
1262
        preset->saveXML(doc);
×
1263

1264
    /* End the >XYPad> tag */
1265
    doc->writeEndElement();
1✔
1266

1267
    return true;
2✔
1268
}
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