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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

38.07
/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)),
9✔
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);
18✔
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.values())
18✔
186
    {
187
        if (!fader.isNull())
×
188
            fader->requestDelete();
×
189
    }
190
    m_fadersMap.clear();
9✔
191
}
12✔
192

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

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

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

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

218
/*****************************************************************************
219
 * Clipboard
220
 *****************************************************************************/
221

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

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

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

240
    return xypad;
1✔
241
}
242

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

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

253
    /* Copy the other widget's fixtures */
254
    m_fixtures = xypad->fixtures();
2✔
255

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

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

265
/*****************************************************************************
266
 * Caption
267
 *****************************************************************************/
268

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

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

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

288
/*****************************************************************************
289
 * Properties
290
 *****************************************************************************/
291

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

299
/*****************************************************************************
300
 * Fixtures
301
 *****************************************************************************/
302

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

308
    updateDegreesRange();
17✔
309
}
17✔
310

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

316
    m_fixtures.removeAll(fixture);
2✔
317

318
    updateDegreesRange();
2✔
319
}
2✔
320

321
void VCXYPad::clearFixtures()
1✔
322
{
323
    m_fixtures.clear();
1✔
324

325
    updateDegreesRange();
1✔
326
}
1✔
327

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

333
QRectF VCXYPad::computeCommonDegreesRange() const
20✔
334
{
335
    QRectF commonRange;
20✔
336

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

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

354
    return commonRange;
3✔
355
}
356

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

361
    m_area->setDegreesRange(range);
20✔
362
}
20✔
363

364
/*****************************************************************************
365
 * Current XY position
366
 *****************************************************************************/
367

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

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

380
    if (m_area->hasPositionChanged() == false)
×
381
        return;
×
382

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

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

390
    if (invertedAppearance())
×
391
        y = qreal(1) - y;
×
392

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

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

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

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

428
    if (m_scene == NULL || m_scene->isRunning() == false)
×
429
        return;
430

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

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

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

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

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

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

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

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

506
    QPointF pt = m_area->position(false);
×
507

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

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

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

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

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

546
        // recalculate preview polygons
547
        QPolygonF polygon;
548
        m_efx->preview(polygon);
×
549

550
        QVector <QPolygonF> fixturePoints;
551
        m_efx->previewFixtures(fixturePoints);
×
552

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

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

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

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

576
            qreal x = fxMap[sc.m_fixture].x();
×
577
            qreal y = fxMap[sc.m_fixture].y();
×
578

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

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

610
            if (fixture.universe() != idx)
×
611
                continue;
×
612

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

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

627
    emit fixturePositions(positions);
×
628
}
×
629

630
/*********************************************************************
631
 * Presets
632
 *********************************************************************/
633

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

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

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

656
    connect(presetButton, SIGNAL(clicked(bool)),
×
657
            this, SLOT(slotPresetClicked(bool)));
658

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

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

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

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

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

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

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

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

702
    return map;
×
703
}
×
704

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

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

713
    Q_ASSERT(preset != NULL);
714

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

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

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

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

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

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

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

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

809
        QPolygonF polygon;
810
        m_efx->preview(polygon);
×
811

812
        QVector <QPolygonF> fixturePoints;
813
        m_efx->previewFixtures(fixturePoints);
×
814

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

820
        connect(m_efx, SIGNAL(durationChanged(uint)), this, SLOT(slotEFXDurationChanged(uint)));
×
821

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

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

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

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

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

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

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

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

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

913
    m_area->setEFXInterval(duration);
×
914
}
915

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

921
/*********************************************************************
922
 * External input
923
 *********************************************************************/
924

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

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

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

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

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

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

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

982
    m_inputValueChanged = true;
×
983

984
    m_area->setPosition(pt);
×
985
    m_area->update();
×
986
}
×
987

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

995
    quint32 pagedCh = (page() << 16) | channel;
×
996

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

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

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

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

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

1099
/*****************************************************************************
1100
 * QLC mode
1101
 *****************************************************************************/
1102

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

1114
    VCWidget::slotModeChanged(mode);
11✔
1115
}
11✔
1116

1117
/*****************************************************************************
1118
 * Load & Save
1119
 *****************************************************************************/
1120

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

1129
    int xpos = 0;
1130
    int ypos = 0;
1131

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

1138
    /* Widget commons */
1139
    loadXMLCommon(root);
3✔
1140

1141
    QXmlStreamAttributes attrs = root.attributes();
3✔
1142

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

1151
    // Sorted list for new presets
1152
    QList<VCXYPadPreset> newPresets;
1153

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

1223
    foreach (VCXYPadPreset const& preset, newPresets)
3✔
1224
        addPreset(preset);
×
1225

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

1230
    return true;
1231
}
3✔
1232

1233
bool VCXYPad::saveXML(QXmlStreamWriter *doc)
1✔
1234
{
1235
    Q_ASSERT(doc != NULL);
1236

1237
    /* VC XY Pad entry */
1238
    doc->writeStartElement(KXMLQLCVCXYPad);
1✔
1239

1240
    saveXMLCommon(doc);
1✔
1241

1242
    doc->writeAttribute(KXMLQLCVCXYPadInvertedAppearance, QString::number(invertedAppearance()));
1✔
1243

1244
    /* Window state */
1245
    saveXMLWindowState(doc);
1✔
1246

1247
    /* Appearance */
1248
    saveXMLAppearance(doc);
1✔
1249

1250
    /* Fixtures */
1251
    foreach (VCXYPadFixture fixture, m_fixtures)
3✔
1252
        fixture.saveXML(doc);
2✔
1253

1254
    /* Current XY position */
1255
    QPointF pt(m_area->position(false));
1✔
1256

1257
    /* Custom range window */
1258
    if (m_hRangeSlider->minimumPosition() != 0 ||
2✔
1259
        m_hRangeSlider->maximumPosition() != 256 ||
2✔
1260
        m_vRangeSlider->minimumPosition() != 0 ||
3✔
1261
        m_vRangeSlider->maximumPosition() != 256)
1✔
1262
    {
1263
        doc->writeStartElement(KXMLQLCVCXYPadRangeWindow);
×
1264
        doc->writeAttribute(KXMLQLCVCXYPadRangeHorizMin, QString::number(m_hRangeSlider->minimumPosition()));
×
1265
        doc->writeAttribute(KXMLQLCVCXYPadRangeHorizMax, QString::number(m_hRangeSlider->maximumPosition()));
×
1266
        doc->writeAttribute(KXMLQLCVCXYPadRangeVertMin, QString::number(m_vRangeSlider->minimumPosition()));
×
1267
        doc->writeAttribute(KXMLQLCVCXYPadRangeVertMax, QString::number(m_vRangeSlider->maximumPosition()));
×
1268
        doc->writeEndElement();
×
1269
    }
1270

1271
    /* Pan */
1272
    doc->writeStartElement(KXMLQLCVCXYPadPan);
1✔
1273
    doc->writeAttribute(KXMLQLCVCXYPadPosition, QString::number(int(pt.x())));
1✔
1274
    saveXMLInput(doc, inputSource(panInputSourceId));
1✔
1275
    doc->writeEndElement();
1✔
1276

1277
    /* Tilt */
1278
    doc->writeStartElement(KXMLQLCVCXYPadTilt);
1✔
1279
    doc->writeAttribute(KXMLQLCVCXYPadPosition, QString::number(int(pt.y())));
1✔
1280
    saveXMLInput(doc, inputSource(tiltInputSourceId));
1✔
1281
    doc->writeEndElement();
1✔
1282

1283
    /* Width */
1284
    QSharedPointer<QLCInputSource> wSrc = inputSource(widthInputSourceId);
1✔
1285
    if (!wSrc.isNull() && wSrc->isValid())
1✔
1286
    {
1287
        doc->writeStartElement(KXMLQLCVCXYPadWidth);
1✔
1288
        saveXMLInput(doc, wSrc);
1✔
1289
        doc->writeEndElement();
1✔
1290
    }
1291

1292
    /* Height */
1293
    QSharedPointer<QLCInputSource> hSrc = inputSource(heightInputSourceId);
1✔
1294
    if (!hSrc.isNull() && hSrc->isValid())
1✔
1295
    {
1296
        doc->writeStartElement(KXMLQLCVCXYPadHeight);
1✔
1297
        saveXMLInput(doc, hSrc);
1✔
1298
        doc->writeEndElement();
1✔
1299
    }
1300

1301
    // Presets
1302
    foreach (VCXYPadPreset *preset, presets())
2✔
1303
        preset->saveXML(doc);
×
1304

1305
    /* End the >XYPad> tag */
1306
    doc->writeEndElement();
1✔
1307

1308
    return true;
1✔
1309
}
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