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

mcallegari / qlcplus / 14809507932

03 May 2025 09:13AM UTC coverage: 31.879% (+0.03%) from 31.845%
14809507932

push

github

web-flow
Merge pull request #1745 from mcallegari/qmluiqt6

Port QML UI to Qt6

2 of 9 new or added lines in 3 files covered. (22.22%)

3720 existing lines in 174 files now uncovered.

16422 of 51513 relevant lines covered (31.88%)

19079.9 hits per line

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

73.54
/engine/src/fixture.cpp
1
/*
2
  Q Light Controller Plus
3
  fixture.cpp
4

5
  Copyright (C) Heikki Junnila
6
                Massimo Callegari
7

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

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

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

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QString>
24
#include <QtMath>
25
#include <QDebug>
26

27
#include "qlcfixturedefcache.h"
28
#include "channelmodifier.h"
29
#include "qlcfixturemode.h"
30
#include "qlcfixturehead.h"
31
#include "qlcfixturedef.h"
32
#include "qlccapability.h"
33
#include "qlcchannel.h"
34
#include "qlcfile.h"
35

36
#include "fixture.h"
37
#include "doc.h"
38

39
/*****************************************************************************
40
 * Initialization
41
 *****************************************************************************/
42

43
Fixture::Fixture(QObject* parent) : QObject(parent)
16,824✔
44
{
45
    m_id = Fixture::invalidId();
16,824✔
46

47
    m_address = 0;
16,824✔
48
    m_channels = 0;
16,824✔
49
    m_crossUniverse = false;
16,824✔
50

51
    m_fixtureDef = NULL;
16,824✔
52
    m_fixtureMode = NULL;
16,824✔
53
}
16,824✔
54

55
Fixture::~Fixture()
33,626✔
56
{
57
}
33,626✔
58

59
bool Fixture::operator<(const Fixture& fxi)
12✔
60
{
61
    if (m_address < fxi.m_address)
12✔
62
        return true;
5✔
63
    else
64
        return false;
7✔
65
}
66

67
/*****************************************************************************
68
 * Fixture ID
69
 *****************************************************************************/
70

71
void Fixture::setID(quint32 id)
16,807✔
72
{
73
    m_id = id;
16,807✔
74
    emit changed(m_id);
16,807✔
75
}
16,807✔
76

77
quint32 Fixture::id() const
18,124✔
78
{
79
    return m_id;
18,124✔
80
}
81

82
quint32 Fixture::invalidId()
832,282✔
83
{
84
    return UINT_MAX;
832,282✔
85
}
86

87
/*****************************************************************************
88
 * Name
89
 *****************************************************************************/
90

91
void Fixture::setName(const QString& name)
83✔
92
{
93
    m_name = name;
83✔
94
    emit changed(m_id);
83✔
95
}
83✔
96

97
QString Fixture::name() const
35✔
98
{
99
    return m_name;
35✔
100
}
101

102
/*****************************************************************************
103
 * Fixture type
104
 *****************************************************************************/
105

106
QString Fixture::typeString()
12✔
107
{
108
    if (m_fixtureDef != NULL)
12✔
109
        return m_fixtureDef->typeToString(m_fixtureDef->type());
11✔
110
    else
111
        return QString(KXMLFixtureDimmer);
1✔
112
}
113

114
QLCFixtureDef::FixtureType Fixture::type() const
24✔
115
{
116
    if (m_fixtureDef != NULL)
24✔
117
        return m_fixtureDef->type();
22✔
118
    else
119
        return QLCFixtureDef::Dimmer;
2✔
120
}
121

122
/*****************************************************************************
123
 * Universe
124
 *****************************************************************************/
125

126
void Fixture::setUniverse(quint32 universe)
95✔
127
{
128
    /* The universe part is stored in the highest 7 bits */
129
    m_address = (m_address & 0x01FF) | (universe << 9);
95✔
130

131
    emit changed(m_id);
95✔
132
}
95✔
133

134
quint32 Fixture::universe() const
795,771✔
135
{
136
    /* The universe part is stored in the highest 7 bits */
137
    return (m_address >> 9);
795,771✔
138
}
139

140
void Fixture::setCrossUniverse(bool enable)
×
141
{
142
    m_crossUniverse = enable;
×
143
}
×
144

145
bool Fixture::crossUniverse() const
18✔
146
{
147
    return m_crossUniverse;
18✔
148
}
149

150
/*****************************************************************************
151
 * Address
152
 *****************************************************************************/
153

154
void Fixture::setAddress(quint32 address)
374✔
155
{
156
    /* Don't allow more than 512 channels per universe */
157
    if (address > 511)
374✔
158
        return;
3✔
159

160
    /* The address part is stored in the lowest 9 bits */
161
    m_address = (m_address & 0xFE00) | (address & 0x01FF);
371✔
162

163
    emit changed(m_id);
371✔
164
}
165

166
quint32 Fixture::address() const
795,744✔
167
{
168
    /* The address part is stored in the lowest 9 bits */
169
    return (m_address & 0x01FF);
795,744✔
170
}
171

172
quint32 Fixture::universeAddress() const
7,724✔
173
{
174
    return m_address;
7,724✔
175
}
176

177
/*****************************************************************************
178
 * Channels
179
 *****************************************************************************/
180

181
void Fixture::setChannels(quint32 channels)
224✔
182
{
183
    if (m_fixtureDef == NULL && m_fixtureMode == NULL)
224✔
184
    {
185
        QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
224✔
186
        QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
224✔
187
        setFixtureDefinition(fixtureDef, fixtureMode);
224✔
188
    }
224✔
189
    else
190
    {
191
        if ((quint32)m_fixtureMode->channels().size() != channels)
×
192
        {
193
            QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
×
194
            QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
×
195
            setFixtureDefinition(fixtureDef, fixtureMode);
×
196
        }
197
    }
198

199
    m_channels = channels;
224✔
200

201
    emit changed(m_id);
224✔
202
}
224✔
203

204
quint32 Fixture::channels() const
10,311✔
205
{
206
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
10,311✔
207
        return m_fixtureMode->channels().size();
10,291✔
208
    else
209
        return m_channels;
20✔
210
}
211

212
const QLCChannel* Fixture::channel(quint32 channel) const
797,996✔
213
{
214
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
797,996✔
215
        return m_fixtureMode->channel(channel);
797,993✔
216
    else
217
        return NULL;
3✔
218
}
219

220
quint32 Fixture::channelAddress(quint32 channel) const
7✔
221
{
222
    if (channel < channels())
7✔
223
        return universeAddress() + channel;
5✔
224
    else
225
        return QLCChannel::invalid();
2✔
226
}
227

228
quint32 Fixture::channel(QLCChannel::Group group,
4✔
229
    QLCChannel::PrimaryColour color) const
230
{
231
    if (m_fixtureDef == NULL && m_fixtureMode == NULL)
4✔
232
    {
233
        /* There's just one generic channel object with "Intensity" as
234
           its name that is the same for all channel numbers. So
235
           there's really no point in returning 0 or any otherwise
236
           valid channel number here. Which one of them the user would
237
           want to get? */
238
        return QLCChannel::invalid();
×
239
    }
240
    else
241
    {
242
        /* Search for the channel name (and group) from our list */
243
        for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
21✔
244
        {
245
            const QLCChannel* ch = m_fixtureMode->channel(i);
21✔
246
            Q_ASSERT(ch != NULL);
21✔
247

248
            if (group != QLCChannel::NoGroup && ch->group() != group)
21✔
249
            {
250
                /* Given group name doesn't match */
251
                continue;
15✔
252
            }
253
            else if (group != QLCChannel::Intensity || ch->colour() == color)
6✔
254
            {
255
                /* Found the channel */
256
                return i;
4✔
257
            }
258
        }
259

260
        /* Went thru all channels but a match was not found */
261
        return QLCChannel::invalid();
×
262
    }
263
}
264

265
QSet <quint32> Fixture::channels(QLCChannel::Group group, QLCChannel::PrimaryColour color) const
4✔
266
{
267
    QSet <quint32> set;
4✔
268
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
4✔
269
    {
270
        /* Search for the channel name (and group) from our list */
271
        for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
148✔
272
        {
273
            const QLCChannel* ch = m_fixtureMode->channel(i);
144✔
274
            Q_ASSERT(ch != NULL);
144✔
275

276
            if (group != QLCChannel::NoGroup && ch->group() != group)
144✔
277
            {
278
                /* Given group name doesn't match */
279
                continue;
48✔
280
            }
281
            else if (group != QLCChannel::Intensity || ch->colour() == color)
96✔
282
            {
283
                /* Found the channel */
284
                set << i;
24✔
285
            }
286
        }
287
    }
288

289
    return set;
4✔
UNCOV
290
}
×
291

292
quint32 Fixture::channelNumber(int type, int controlByte, int head) const
310✔
293
{
294
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
310✔
295
        return QLCChannel::invalid();
4✔
296

297
    return m_fixtureMode->heads().at(head).channelNumber(type, controlByte);
306✔
298
}
299

300
quint32 Fixture::masterIntensityChannel() const
40✔
301
{
302
    if (m_fixtureMode == NULL)
40✔
303
        return QLCChannel::invalid();
1✔
304

305
    return m_fixtureMode->masterIntensityChannel();
39✔
306
}
307

308
QVector <quint32> Fixture::rgbChannels(int head) const
15✔
309
{
310
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
15✔
UNCOV
311
        return QVector <quint32> ();
×
312

313
    return m_fixtureMode->heads().at(head).rgbChannels();
15✔
314
}
315

316
QVector <quint32> Fixture::cmyChannels(int head) const
1✔
317
{
318
    if (m_fixtureMode == NULL || head < 0 || head >= m_fixtureMode->heads().size())
1✔
UNCOV
319
        return QVector <quint32> ();
×
320

321
    return m_fixtureMode->heads().at(head).cmyChannels();
1✔
322
}
323

324
QList<SceneValue> Fixture::positionToValues(int type, float degrees, bool isRelative)
2✔
325
{
326
    QList<SceneValue> posList;
2✔
327
    // cache a list of channels processed, to avoid duplicates
328
    QList<quint32> chDone;
2✔
329

330
    if (m_fixtureMode == NULL)
2✔
UNCOV
331
        return posList;
×
332

333
    QLCPhysical phy = fixtureMode()->physical();
2✔
334
    qreal headDegrees = degrees, maxDegrees;
2✔
335
    float msbValue = 0, lsbValue = 0;
2✔
336

337
    if (type == QLCChannel::Pan)
2✔
338
    {
339
        maxDegrees = phy.focusPanMax();
1✔
340
        if (maxDegrees == 0) maxDegrees = 360;
1✔
341

342
        for (int i = 0; i < heads(); i++)
2✔
343
        {
344
            quint32 panMSB = channelNumber(QLCChannel::Pan, QLCChannel::MSB, i);
1✔
345
            if (panMSB == QLCChannel::invalid() || chDone.contains(panMSB))
1✔
346
                continue;
×
347
            quint32 panLSB = channelNumber(QLCChannel::Pan, QLCChannel::LSB, i);
1✔
348

349
            if (isRelative)
1✔
350
            {
351
                // degrees is a relative value upon the current value.
352
                // Recalculate absolute degrees here
353
                float chDegrees = (qreal(phy.focusPanMax()) / 256.0) * channelValueAt(panMSB);
×
354
                headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
355

356
                if (panLSB != QLCChannel::invalid())
×
357
                {
358
                    chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(panLSB);
×
359
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
360
                }
361
            }
362

363
            quint16 degToDmx = (headDegrees * 65535.0) / qreal(phy.focusPanMax());
1✔
364
            posList.append(SceneValue(id(), panMSB, static_cast<uchar>(degToDmx >> 8)));
1✔
365

366
            if (panLSB != QLCChannel::invalid())
1✔
367
                posList.append(SceneValue(id(), panLSB, static_cast<uchar>(degToDmx & 0x00FF)));
1✔
368

369
            qDebug() << "[positionToValues] Pan MSB:" << msbValue << "LSB:" << lsbValue;
1✔
370

371
            chDone.append(panMSB);
1✔
372
        }
373
    }
374
    else if (type == QLCChannel::Tilt)
1✔
375
    {
376
        maxDegrees = phy.focusTiltMax();
1✔
377
        if (maxDegrees == 0) maxDegrees = 270;
1✔
378

379
        for (int i = 0; i < heads(); i++)
2✔
380
        {
381
            quint32 tiltMSB = channelNumber(QLCChannel::Tilt, QLCChannel::MSB, i);
1✔
382
            if (tiltMSB == QLCChannel::invalid() || chDone.contains(tiltMSB))
1✔
383
                continue;
×
384
            quint32 tiltLSB = channelNumber(QLCChannel::Tilt, QLCChannel::LSB, i);
1✔
385

386
            if (isRelative)
1✔
387
            {
388
                // degrees is a relative value upon the current value.
389
                // Recalculate absolute degrees here
390
                float chDegrees = (qreal(phy.focusTiltMax()) / 256.0) * channelValueAt(tiltMSB);
×
391
                headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
392

393
                if (tiltLSB != QLCChannel::invalid())
×
394
                {
395
                    chDegrees = (qreal(phy.focusPanMax()) / 65536.0) * channelValueAt(tiltLSB);
×
396
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
397
                }
398
            }
399

400
            quint16 degToDmx = (headDegrees * 65535.0) / qreal(phy.focusTiltMax());
1✔
401
            posList.append(SceneValue(id(), tiltMSB, static_cast<uchar>(degToDmx >> 8)));
1✔
402

403
            if (tiltLSB != QLCChannel::invalid())
1✔
404
                posList.append(SceneValue(id(), tiltLSB, static_cast<uchar>(degToDmx & 0x00FF)));
1✔
405

406
            qDebug() << "[positionToValues] Tilt MSB:" << msbValue << "LSB:" << lsbValue;
1✔
407

408
            chDone.append(tiltMSB);
1✔
409
        }
410

411
    }
412

413
    return posList;
2✔
414
}
2✔
415

416
QList<SceneValue> Fixture::zoomToValues(float degrees, bool isRelative)
×
417
{
UNCOV
418
    QList<SceneValue> chList;
×
419

420
    if (m_fixtureMode == NULL)
×
UNCOV
421
        return chList;
×
422

423
    QLCPhysical phy = fixtureMode()->physical();
×
424
    if (!isRelative)
×
425
        degrees = qBound(float(phy.lensDegreesMin()), degrees, float(phy.lensDegreesMax()));
×
426

427
    float deltaDegrees = phy.lensDegreesMax() - phy.lensDegreesMin();
×
428
    // delta : 0xFFFF = deg : x
429
    quint16 degToDmx = ((qAbs(degrees) - (isRelative ? 0 : float(phy.lensDegreesMin()))) * 65535.0) / deltaDegrees;
×
UNCOV
430
    qDebug() << "Degrees" << degrees << "DMX" << QString::number(degToDmx, 16);
×
431

432
    for (quint32 i = 0; i < quint32(m_fixtureMode->channels().size()); i++)
×
433
    {
434
        const QLCChannel *ch = m_fixtureMode->channel(i);
×
435

436
        if (ch->group() != QLCChannel::Beam)
×
437
            continue;
×
438

439
        if (ch->preset() != QLCChannel::BeamZoomBigSmall &&
×
440
            ch->preset() != QLCChannel::BeamZoomSmallBig &&
×
441
            ch->preset() != QLCChannel::BeamZoomFine)
×
442
            continue;
×
443

444
        if (isRelative)
×
445
        {
446
            // degrees is a relative value upon the current value.
447
            // Recalculate absolute degrees here
448
            qreal divider = ch->controlByte() == QLCChannel::MSB ? 256.0 : 65536.0;
×
449
            uchar currDmxValue = ch->preset() == QLCChannel::BeamZoomBigSmall ? UCHAR_MAX - channelValueAt(i) : channelValueAt(i);
×
450
            float chDegrees = float((phy.lensDegreesMax() - phy.lensDegreesMin()) / divider) * float(currDmxValue);
×
451

UNCOV
452
            qDebug() << "Relative channel degrees:" << chDegrees << "MSB?" << ch->controlByte();
×
453

454
            quint16 currDmxVal = (chDegrees * 65535.0) / deltaDegrees;
×
455
            if (degrees > 0)
×
456
                degToDmx = qBound(0, currDmxVal + degToDmx, 65535);
×
457
            else
458
                degToDmx = qBound(0, currDmxVal - degToDmx, 65535);
×
459
        }
460

461
        if (ch->controlByte() == QLCChannel::MSB)
×
462
        {
463
            if (ch->preset() == QLCChannel::BeamZoomBigSmall)
×
464
                chList.append(SceneValue(id(), i, static_cast<uchar>(UCHAR_MAX - (degToDmx >> 8))));
×
465
            else
466
                chList.append(SceneValue(id(), i, static_cast<uchar>(degToDmx >> 8)));
×
467
        }
468
        else if (ch->controlByte() == QLCChannel::LSB)
×
469
        {
470
            chList.append(SceneValue(id(), i, static_cast<uchar>(degToDmx & 0x00FF)));
×
471
        }
472
    }
473

UNCOV
474
    return chList;
×
475
}
×
476

477
void Fixture::setExcludeFadeChannels(QList<int> indices)
11✔
478
{
479
    if (indices.count() > (int)channels())
11✔
UNCOV
480
        return;
×
481

482
    m_excludeFadeIndices = indices;
11✔
483
}
484

485
QList<int> Fixture::excludeFadeChannels()
2✔
486
{
487
    return m_excludeFadeIndices;
2✔
488
}
489

490
void Fixture::setChannelCanFade(int idx, bool canFade)
1✔
491
{
492
    if (canFade == false && m_excludeFadeIndices.contains(idx) == false)
1✔
493
    {
494
        m_excludeFadeIndices.append(idx);
1✔
495
        std::sort(m_excludeFadeIndices.begin(), m_excludeFadeIndices.end());
1✔
496
    }
497
    else if (canFade == true && m_excludeFadeIndices.contains(idx) == true)
×
498
    {
499
        m_excludeFadeIndices.removeOne(idx);
×
500
    }
501
}
1✔
502

503
bool Fixture::channelCanFade(int index)
795,211✔
504
{
505
    if (m_excludeFadeIndices.contains(index))
795,211✔
506
        return false;
1✔
507

508
    return true;
795,210✔
509
}
510

511
void Fixture::setForcedHTPChannels(QList<int> indices)
13✔
512
{
513
    if (indices.count() > (int)channels())
13✔
UNCOV
514
        return;
×
515
    m_forcedHTPIndices = indices;
13✔
516
    // cross check: if a channel is forced HTP it must be removed from
517
    // the forced LTP list (if present)
518
    for (int i = 0; i < m_forcedHTPIndices.count(); i++)
16✔
519
        m_forcedLTPIndices.removeAll(m_forcedHTPIndices.at(i));
3✔
520
}
521

522
QList<int> Fixture::forcedHTPChannels()
795,615✔
523
{
524
    return m_forcedHTPIndices;
795,615✔
525
}
526

527
void Fixture::setForcedLTPChannels(QList<int> indices)
13✔
528
{
529
    if (indices.count() > (int)channels())
13✔
UNCOV
530
        return;
×
531
    m_forcedLTPIndices = indices;
13✔
532
    // cross check: if a channel is forced LTP it must be removed from
533
    // the forced HTP list (if present)
534
    for (int i = 0; i < m_forcedLTPIndices.count(); i++)
15✔
535
        m_forcedHTPIndices.removeAll(m_forcedLTPIndices.at(i));
2✔
536
}
537

538
QList<int> Fixture::forcedLTPChannels()
795,614✔
539
{
540
    return m_forcedLTPIndices;
795,614✔
541
}
542

543
void Fixture::setChannelModifier(quint32 idx, ChannelModifier *mod)
×
544
{
545
    if (idx >= channels())
×
UNCOV
546
        return;
×
547

548
    if (mod == NULL)
×
549
    {
550
        m_channelModifiers.remove(idx);
×
551
        return;
×
552
    }
553

UNCOV
554
    qDebug() << Q_FUNC_INFO << idx << mod->name();
×
555
    m_channelModifiers[idx] = mod;
×
556
}
557

558
ChannelModifier *Fixture::channelModifier(quint32 idx)
2,572✔
559
{
560
    return m_channelModifiers.value(idx, NULL);
2,572✔
561
}
562

563
/*********************************************************************
564
 * Channel info
565
 *********************************************************************/
566

567
bool Fixture::setChannelValues(const QByteArray &values)
1✔
568
{
569
    const int addr = address();
1✔
570
    if (addr >= values.size())
1✔
UNCOV
571
        return false;
×
572

573
    const int chNum = qMin(values.size() - addr, (int)channels());
1✔
574
    bool changed = false;
1✔
575

576
    // Most of the times there are no changes,
577
    // so the lock is inside the cycle
578
    for (int i = 0; i < chNum; i++)
7✔
579
    {
580
        if (m_values.at(i) != values.at(i + addr))
6✔
581
        {
582
            changed = true;
6✔
583
            QMutexLocker locker(&m_channelsInfoMutex);
6✔
584
            m_values[i] = values.at(i + addr);
6✔
585
            checkAlias(i, m_values[i]);
6✔
586
        }
6✔
587
    }
588

589
    if (changed == true)
1✔
590
        emit valuesChanged();
1✔
591

592
    return changed;
1✔
593
}
594

595
QByteArray Fixture::channelValues()
11✔
596
{
597
    QMutexLocker locker(&m_channelsInfoMutex);
11✔
598
    return m_values;
11✔
599
}
11✔
600

601
uchar Fixture::channelValueAt(int idx)
×
602
{
603
    QMutexLocker locker(&m_channelsInfoMutex);
×
604
    if (idx >= 0 && idx < m_values.length())
×
605
        return (uchar)m_values.at(idx);
×
UNCOV
606
    return 0;
×
UNCOV
607
}
×
608

609
void Fixture::checkAlias(int chIndex, uchar value)
6✔
610
{
611
    if (chIndex < 0 || chIndex >= m_aliasInfo.count() ||
12✔
612
        m_aliasInfo[chIndex].m_hasAlias == false)
6✔
613
        return;
6✔
614

615
    // If the channel @chIndex has aliases, check
616
    // if replacements are to be done
617
    QLCCapability *cap = m_fixtureMode->channel(chIndex)->searchCapability(value);
×
618
    if (cap == NULL || cap == m_aliasInfo[chIndex].m_currCap)
×
619
        return;
×
620

621
    // first, revert any channel replaced to the original channel set
622
    foreach (AliasInfo alias, m_aliasInfo[chIndex].m_currCap->aliasList())
×
623
    {
624
        QLCFixtureMode *mode = m_fixtureDef->mode(alias.targetMode);
×
625
        if (mode != m_fixtureMode)
×
UNCOV
626
            continue;
×
627

628
        QLCChannel *currChannel = m_fixtureMode->channel(alias.targetChannel);
×
629
        QLCChannel *origChannel = m_fixtureDef->channel(alias.sourceChannel);
×
630

631
        m_fixtureMode->replaceChannel(currChannel, origChannel);
×
632
    }
×
633

634
    // now, apply the current alias changes
635
    foreach (AliasInfo alias, cap->aliasList())
×
636
    {
637
        QLCFixtureMode *mode = m_fixtureDef->mode(alias.targetMode);
×
638
        if (mode != m_fixtureMode)
×
UNCOV
639
            continue;
×
640

641
        QLCChannel *currChannel = m_fixtureMode->channel(alias.sourceChannel);
×
642
        QLCChannel *newChannel = m_fixtureDef->channel(alias.targetChannel);
×
643

644
        m_fixtureMode->replaceChannel(currChannel, newChannel);
×
645
    }
×
646

647
    emit aliasChanged();
×
648

649
    m_aliasInfo[chIndex].m_currCap = cap;
×
650

651
}
652

653
/*****************************************************************************
654
 * Fixture definition
655
 *****************************************************************************/
656

657
void Fixture::setFixtureDefinition(QLCFixtureDef* fixtureDef,
452✔
658
                                   QLCFixtureMode* fixtureMode)
659
{
660
    if (fixtureDef != NULL && fixtureMode != NULL)
452✔
661
    {
662
        int i, chNum;
663

664
        if (m_fixtureDef != NULL && m_fixtureDef != fixtureDef &&
40✔
665
            m_fixtureDef->manufacturer() == KXMLFixtureGeneric &&
940✔
666
            m_fixtureDef->model() == KXMLFixtureGeneric)
459✔
667
        {
668
            delete m_fixtureDef;
3✔
669
        }
670

671
        m_fixtureDef = fixtureDef;
450✔
672
        m_fixtureMode = fixtureMode;
450✔
673
        chNum = fixtureMode->channels().size();
450✔
674

675
        // If there are no head entries in the mode, create one that contains
676
        // all channels. This const_cast is a bit heretic, but it's easier this
677
        // way, than to change everything def & mode related non-const, which would
678
        // be worse than one constness violation here.
679
        if (fixtureMode->heads().size() == 0)
450✔
680
        {
681
            QLCFixtureHead head;
37✔
682
            for (i = 0; i < chNum; i++)
321✔
683
                head.addChannel(i);
284✔
684
            fixtureMode->insertHead(-1, head);
37✔
685
        }
37✔
686

687
        m_aliasInfo.resize(chNum);
450✔
688

689
        for (i = 0; i < chNum; i++)
3,734✔
690
        {
691
            QLCChannel *channel = fixtureMode->channel(i);
3,284✔
692
            const QList <QLCCapability*> capsList = channel->capabilities();
3,284✔
693

694
            // initialize values with the channel default
695
            m_values.append(channel->defaultValue());
3,284✔
696

697
            // look for aliases
698
            m_aliasInfo[i].m_hasAlias = false;
3,284✔
699
            m_aliasInfo[i].m_currCap = capsList.count() ? capsList.at(0) : NULL;
3,284✔
700

701
            foreach (QLCCapability *cap, capsList)
13,410✔
702
            {
703
                if (cap->preset() == QLCCapability::Alias)
10,126✔
704
                    m_aliasInfo[i].m_hasAlias = true;
×
705
            }
3,284✔
706
        }
3,284✔
707

708
        // Cache all head channels
709
        fixtureMode->cacheHeads();
450✔
710
    }
450✔
711
    else
712
    {
713
        m_fixtureDef = NULL;
2✔
714
        m_fixtureMode = NULL;
2✔
715
    }
716

717
    emit changed(m_id);
452✔
718
}
452✔
719

720
QLCFixtureDef* Fixture::fixtureDef() const
33✔
721
{
722
    return m_fixtureDef;
33✔
723
}
724

725
QLCFixtureMode* Fixture::fixtureMode() const
795,314✔
726
{
727
    return m_fixtureMode;
795,314✔
728
}
729

730
int Fixture::heads() const
820✔
731
{
732
    return m_fixtureMode->heads().size();
820✔
733
}
734

735
QLCFixtureHead Fixture::head(int index) const
30✔
736
{
737
    if (index < m_fixtureMode->heads().size())
30✔
738
        return m_fixtureMode->heads().at(index);
30✔
739
    else
740
        return QLCFixtureHead();
×
741
}
742

743
QString Fixture::iconResource(bool svg) const
12✔
744
{
745
    QString prefix = svg ? "qrc" : "";
12✔
746
    QString ext = svg ? "svg" : "png";
12✔
747

748
    switch(type())
12✔
749
    {
750
        case QLCFixtureDef::ColorChanger: return QString("%1:/fixture.%2").arg(prefix).arg(ext);
1✔
751
        case QLCFixtureDef::Dimmer: return QString("%1:/dimmer.%2").arg(prefix).arg(ext);
1✔
752
        case QLCFixtureDef::Effect: return QString("%1:/effect.%2").arg(prefix).arg(ext);
1✔
753
        case QLCFixtureDef::Fan: return QString("%1:/fan.%2").arg(prefix).arg(ext);
×
754
        case QLCFixtureDef::Flower: return QString("%1:/flower.%2").arg(prefix).arg(ext);
1✔
755
        case QLCFixtureDef::Hazer: return QString("%1:/hazer.%2").arg(prefix).arg(ext);
1✔
756
        case QLCFixtureDef::Laser: return QString("%1:/laser.%2").arg(prefix).arg(ext);
1✔
757
        case QLCFixtureDef::MovingHead: return QString("%1:/movinghead.%2").arg(prefix).arg(ext);
1✔
758
        case QLCFixtureDef::Scanner: return QString("%1:/scanner.%2").arg(prefix).arg(ext);
1✔
759
        case QLCFixtureDef::Smoke: return QString("%1:/smoke.%2").arg(prefix).arg(ext);
1✔
760
        case QLCFixtureDef::Strobe: return QString("%1:/strobe.%2").arg(prefix).arg(ext);
1✔
761
        case QLCFixtureDef::LEDBarBeams: return QString("%1:/ledbar_beams.%2").arg(prefix).arg(ext);
1✔
762
        case QLCFixtureDef::LEDBarPixels: return QString("%1:/ledbar_pixels.%2").arg(prefix).arg(ext);
1✔
UNCOV
763
        default: break;
×
764
    }
765

766
    return QString("%1:/other.%2").arg(prefix).arg(ext);
×
767
}
12✔
768

769
QIcon Fixture::getIconFromType() const
×
770
{
771
    return QIcon(iconResource());
×
772
}
773

774
QRectF Fixture::degreesRange(int head) const
7✔
775
{
776
    // TODO: handle fixtures with only pan or tilt
777

778
    if (m_fixtureMode != NULL && head < m_fixtureMode->heads().size())
7✔
779
    {
780
        QLCPhysical physical(m_fixtureMode->physical());
7✔
781
        qreal pan = physical.focusPanMax();
7✔
782
        qreal tilt = physical.focusTiltMax();
7✔
783

784
        if (pan != 0 && tilt != 0)
7✔
785
        {
786
            return QRectF(-pan/2, -tilt/2, pan, tilt);
7✔
787
        }
788
    }
7✔
789

UNCOV
790
    return QRectF();
×
791
}
792

793
/*********************************************************************
794
 * Generic Dimmer
795
 *********************************************************************/
796

797
QLCFixtureDef *Fixture::genericDimmerDef(int channels)
224✔
798
{
799
    QLCFixtureDef *def = new QLCFixtureDef();
224✔
800
    def->setManufacturer(KXMLFixtureGeneric);
448✔
801
    def->setModel(KXMLFixtureGeneric);
448✔
802
    def->setType(QLCFixtureDef::Dimmer);
224✔
803
    def->setAuthor("QLC+");
224✔
804

805
    for (int i = 0; i < channels; i++)
927✔
806
    {
807
        QLCChannel *intensity = new QLCChannel();
703✔
808
        intensity->setGroup(QLCChannel::Intensity);
703✔
809
        intensity->setName(tr("Dimmer #%1").arg(i + 1));
703✔
810
        intensity->addCapability(new QLCCapability(0, UCHAR_MAX, tr("Intensity")));
703✔
811
        def->addChannel(intensity);
703✔
812
    }
813

814
    return def;
224✔
815
}
816

817
QLCFixtureMode *Fixture::genericDimmerMode(QLCFixtureDef *def, int channels)
224✔
818
{
819
    Q_ASSERT(def != NULL);
224✔
820
    QLCFixtureMode *mode = new QLCFixtureMode(def);
224✔
821

822
    mode->setName(QString("%1 Channel").arg(channels));
224✔
823
    QList<QLCChannel *>chList = def->channels();
224✔
824
    for (int i = 0; i < chList.count(); i++)
927✔
825
    {
826
        QLCChannel *ch = chList.at(i);
703✔
827
        mode->insertChannel(ch, i);
703✔
828
        QLCFixtureHead head;
703✔
829
        head.addChannel(i);
703✔
830
        mode->insertHead(-1, head);
703✔
831
    }
703✔
832

833
    QLCPhysical physical;
224✔
834
    physical.setWidth(300 * channels);
224✔
835
    physical.setHeight(300);
224✔
836
    physical.setDepth(300);
224✔
837

838
    mode->setPhysical(physical);
224✔
839
    def->addMode(mode);
224✔
840

841
    return mode;
224✔
842
}
224✔
843

844
/*********************************************************************
845
 * Generic RGB panel
846
 *********************************************************************/
847

848
QString Fixture::componentsToString(Components comp, bool is16bit)
8✔
849
{
850
    QString compStr;
8✔
851

852
    switch (comp)
8✔
853
    {
854
        case BGR:
1✔
855
            compStr = "BGR";
1✔
856
        break;
1✔
857
        case BRG:
1✔
858
            compStr = "BRG";
1✔
859
        break;
1✔
860
        case GBR:
1✔
861
            compStr = "GBR";
1✔
862
        break;
1✔
863
        case GRB:
1✔
864
            compStr = "GRB";
1✔
865
        break;
1✔
866
        case RBG:
1✔
867
            compStr = "RBG";
1✔
868
        break;
1✔
869
        case RGBW:
1✔
870
            compStr = "RGBW";
1✔
871
        break;
1✔
872
        default:
2✔
873
            compStr = "RGB";
2✔
874
        break;
2✔
875
    }
876

877
    if (is16bit)
8✔
878
        compStr += " 16bit";
1✔
879

880
    return compStr;
8✔
881
}
×
882

883
Fixture::Components Fixture::stringToComponents(QString str, bool &is16bit)
×
884
{
885
    QStringList strToken = str.split(' ');
×
886
    is16bit = false;
×
887

888
    if (strToken.count() == 2)
×
889
    {
890
        if (strToken.at(1) == "16bit")
×
891
            is16bit = true;
×
892
    }
893

894
    if (strToken.at(0) == "BGR") return BGR;
×
895
    else if (strToken.at(0) == "BRG") return BRG;
×
896
    else if (strToken.at(0) == "GBR") return GBR;
×
897
    else if (strToken.at(0) == "GRB") return GRB;
×
898
    else if (strToken.at(0) == "RBG") return RBG;
×
899
    else if (strToken.at(0) == "RGBW") return RGBW;
×
900
    else return RGB;
×
UNCOV
901
}
×
902

903
QLCFixtureDef *Fixture::genericRGBPanelDef(int columns, Components components, bool is16bit)
8✔
904
{
905
    QLCFixtureDef *def = new QLCFixtureDef();
8✔
906
    def->setManufacturer(KXMLFixtureGeneric);
16✔
907
    def->setModel(KXMLFixtureRGBPanel);
16✔
908
    def->setType(QLCFixtureDef::LEDBarPixels);
8✔
909
    def->setAuthor("QLC+");
8✔
910

911
    for (int i = 0; i < columns; i++)
88✔
912
    {
913
        QLCChannel *red = new QLCChannel();
80✔
914
        red->setName(QString("Red %1").arg(i + 1));
80✔
915
        red->setGroup(QLCChannel::Intensity);
80✔
916
        red->setColour(QLCChannel::Red);
80✔
917

918
        QLCChannel *green = new QLCChannel();
80✔
919
        green->setName(QString("Green %1").arg(i + 1));
80✔
920
        green->setGroup(QLCChannel::Intensity);
80✔
921
        green->setColour(QLCChannel::Green);
80✔
922

923
        QLCChannel *blue = new QLCChannel();
80✔
924
        blue->setName(QString("Blue %1").arg(i + 1));
80✔
925
        blue->setGroup(QLCChannel::Intensity);
80✔
926
        blue->setColour(QLCChannel::Blue);
80✔
927

928
        QLCChannel *redFine = NULL;
80✔
929
        QLCChannel *greenFine = NULL;
80✔
930
        QLCChannel *blueFine = NULL;
80✔
931

932
        if (is16bit)
80✔
933
        {
934
            redFine = new QLCChannel();
10✔
935
            redFine->setName(QString("Red Fine %1").arg(i + 1));
10✔
936
            redFine->setGroup(QLCChannel::Intensity);
10✔
937
            redFine->setColour(QLCChannel::Red);
10✔
938
            redFine->setControlByte(QLCChannel::LSB);
10✔
939

940
            greenFine = new QLCChannel();
10✔
941
            greenFine->setName(QString("Green Fine %1").arg(i + 1));
10✔
942
            greenFine->setGroup(QLCChannel::Intensity);
10✔
943
            greenFine->setColour(QLCChannel::Green);
10✔
944
            greenFine->setControlByte(QLCChannel::LSB);
10✔
945

946
            blueFine = new QLCChannel();
10✔
947
            blueFine->setName(QString("Blue Fine %1").arg(i + 1));
10✔
948
            blueFine->setGroup(QLCChannel::Intensity);
10✔
949
            blueFine->setColour(QLCChannel::Blue);
10✔
950
            blueFine->setControlByte(QLCChannel::LSB);
10✔
951
        }
952

953
        if (components == BGR)
80✔
954
        {
955
            def->addChannel(blue);
10✔
956
            if (is16bit) def->addChannel(blueFine);
10✔
957
            def->addChannel(green);
10✔
958
            if (is16bit) def->addChannel(greenFine);
10✔
959
            def->addChannel(red);
10✔
960
            if (is16bit) def->addChannel(redFine);
10✔
961
        }
962
        else if (components == BRG)
70✔
963
        {
964
            def->addChannel(blue);
10✔
965
            if (is16bit) def->addChannel(blueFine);
10✔
966
            def->addChannel(red);
10✔
967
            if (is16bit) def->addChannel(redFine);
10✔
968
            def->addChannel(green);
10✔
969
            if (is16bit) def->addChannel(greenFine);
10✔
970
        }
971
        else if (components == GBR)
60✔
972
        {
973
            def->addChannel(green);
10✔
974
            if (is16bit) def->addChannel(greenFine);
10✔
975
            def->addChannel(blue);
10✔
976
            if (is16bit) def->addChannel(blueFine);
10✔
977
            def->addChannel(red);
10✔
978
            if (is16bit) def->addChannel(redFine);
10✔
979
        }
980
        else if (components == GRB)
50✔
981
        {
982
            def->addChannel(green);
10✔
983
            if (is16bit) def->addChannel(greenFine);
10✔
984
            def->addChannel(red);
10✔
985
            if (is16bit) def->addChannel(redFine);
10✔
986
            def->addChannel(blue);
10✔
987
            if (is16bit) def->addChannel(blueFine);
10✔
988
        }
989
        else if (components == RBG)
40✔
990
        {
991
            def->addChannel(red);
10✔
992
            if (is16bit) def->addChannel(redFine);
10✔
993
            def->addChannel(blue);
10✔
994
            if (is16bit) def->addChannel(blueFine);
10✔
995
            def->addChannel(green);
10✔
996
            if (is16bit) def->addChannel(greenFine);
10✔
997
        }
998
        else if (components == RGBW)
30✔
999
        {
1000
            QLCChannel *white = new QLCChannel();
10✔
1001
            white->setName(QString("White %1").arg(i + 1));
10✔
1002
            white->setGroup(QLCChannel::Intensity);
10✔
1003
            white->setColour(QLCChannel::White);
10✔
1004

1005
            def->addChannel(red);
10✔
1006
            if (is16bit) def->addChannel(redFine);
10✔
1007
            def->addChannel(green);
10✔
1008
            if (is16bit) def->addChannel(greenFine);
10✔
1009
            def->addChannel(blue);
10✔
1010
            if (is16bit) def->addChannel(blueFine);
10✔
1011
            def->addChannel(white);
10✔
1012

1013
            if (is16bit)
10✔
1014
            {
1015
                QLCChannel *whiteFine = new QLCChannel();
×
1016
                whiteFine->setName(QString("White Fine %1").arg(i + 1));
×
1017
                whiteFine->setGroup(QLCChannel::Intensity);
×
1018
                whiteFine->setColour(QLCChannel::White);
×
1019
                whiteFine->setControlByte(QLCChannel::LSB);
×
1020
                def->addChannel(whiteFine);
×
1021
            }
1022
        }
1023
        else
1024
        {
1025
            def->addChannel(red);
20✔
1026
            if (is16bit) def->addChannel(redFine);
20✔
1027
            def->addChannel(green);
20✔
1028
            if (is16bit) def->addChannel(greenFine);
20✔
1029
            def->addChannel(blue);
20✔
1030
            if (is16bit) def->addChannel(blueFine);
20✔
1031
        }
1032
    }
1033

1034
    return def;
8✔
1035
}
1036

1037
QLCFixtureMode *Fixture::genericRGBPanelMode(QLCFixtureDef *def, Components components, bool is16bit,
8✔
1038
                                             quint32 width, quint32 height)
1039
{
1040
    Q_ASSERT(def != NULL);
8✔
1041

1042
    QLCFixtureMode *mode = new QLCFixtureMode(def);
8✔
1043
    QString modeName = componentsToString(components, is16bit);
8✔
1044
    mode->setName(modeName);
8✔
1045

1046
    int compNum = components == RGBW ? 4 : 3;
8✔
1047
    if (is16bit)
8✔
1048
        compNum *= 2;
1✔
1049

1050
    QList<QLCChannel *>channels = def->channels();
8✔
1051
    int i = 0;
8✔
1052

1053
    // add channels and heads
1054
    for (int h = 0; h < channels.count() / compNum; h++)
88✔
1055
    {
1056
        QLCFixtureHead head;
80✔
1057

1058
        for (int c = 0; c < compNum; c++, i++)
360✔
1059
        {
1060
            QLCChannel *ch = channels.at(i);
280✔
1061
            mode->insertChannel(ch, i);
280✔
1062
            head.addChannel(i);
280✔
1063
        }
1064

1065
        mode->insertHead(-1, head);
80✔
1066
    }
80✔
1067

1068
    QLCPhysical physical;
8✔
1069
    physical.setWidth(width);
8✔
1070
    physical.setHeight(height);
8✔
1071
    physical.setDepth(height);
8✔
1072
    physical.setLayoutSize(QSize(mode->heads().count(), 1));
8✔
1073

1074
    mode->setPhysical(physical);
8✔
1075
    def->addMode(mode);
8✔
1076

1077
    return mode;
8✔
1078
}
8✔
1079

1080
/*****************************************************************************
1081
 * Load & Save
1082
 *****************************************************************************/
1083

1084
bool Fixture::loader(QXmlStreamReader &root, Doc* doc)
4✔
1085
{
1086
    bool result = false;
4✔
1087

1088
    Fixture* fxi = new Fixture(doc);
4✔
1089
    Q_ASSERT(fxi != NULL);
4✔
1090

1091
    if (fxi->loadXML(root, doc, doc->fixtureDefCache()) == true)
4✔
1092
    {
1093
        if (doc->addFixture(fxi, fxi->id(), fxi->crossUniverse()) == true)
4✔
1094
        {
1095
            /* Success */
1096
            result = true;
4✔
1097
        }
1098
        else
1099
        {
1100
            /* Doc is full */
1101
            qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name()
×
1102
                       << "cannot be created.";
×
1103
            delete fxi;
×
1104
        }
1105
    }
1106
    else
1107
    {
1108
        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
1109
        delete fxi;
×
1110
    }
1111

1112
    return result;
4✔
1113
}
1114

1115
bool Fixture::loadXML(QXmlStreamReader &xmlDoc, Doc *doc,
11✔
1116
                      QLCFixtureDefCache *fixtureDefCache)
1117
{
1118
    QLCFixtureDef* fixtureDef = NULL;
11✔
1119
    QLCFixtureMode* fixtureMode = NULL;
11✔
1120
    QString manufacturer;
11✔
1121
    QString model;
11✔
1122
    QString modeName;
11✔
1123
    QString name;
11✔
1124
    quint32 id = Fixture::invalidId();
11✔
1125
    quint32 universe = 0;
11✔
1126
    quint32 address = 0;
11✔
1127
    quint32 channels = 0;
11✔
1128
    quint32 width = 0, height = 0;
11✔
1129
    QList<int> excludeList;
11✔
1130
    QList<int> forcedHTP;
11✔
1131
    QList<int> forcedLTP;
11✔
1132
    QList<quint32>modifierIndices;
11✔
1133
    QList<ChannelModifier *>modifierPointers;
11✔
1134

1135
    if (xmlDoc.name() != KXMLFixture)
11✔
1136
    {
1137
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
1✔
1138
        return false;
1✔
1139
    }
1140

1141
    while (xmlDoc.readNextStartElement())
90✔
1142
    {
1143
        if (xmlDoc.name() == KXMLQLCFixtureDefManufacturer)
80✔
1144
        {
1145
            manufacturer = xmlDoc.readElementText();
10✔
1146
        }
1147
        else if (xmlDoc.name() == KXMLQLCFixtureDefModel)
70✔
1148
        {
1149
            model = xmlDoc.readElementText();
10✔
1150
        }
1151
        else if (xmlDoc.name() == KXMLQLCFixtureMode)
60✔
1152
        {
1153
            modeName = xmlDoc.readElementText();
10✔
1154
        }
1155
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsWidth)
50✔
1156
        {
1157
            width = xmlDoc.readElementText().toUInt();
×
1158
        }
1159
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsHeight)
50✔
1160
        {
1161
            height = xmlDoc.readElementText().toUInt();
×
1162
        }
1163
        else if (xmlDoc.name() == KXMLFixtureID)
50✔
1164
        {
1165
            id = xmlDoc.readElementText().toUInt();
10✔
1166
        }
1167
        else if (xmlDoc.name() == KXMLFixtureName)
40✔
1168
        {
1169
            name = xmlDoc.readElementText();
10✔
1170
        }
1171
        else if (xmlDoc.name() == KXMLFixtureUniverse)
30✔
1172
        {
1173
            universe = xmlDoc.readElementText().toInt();
10✔
1174
        }
1175
        else if (xmlDoc.name() == KXMLFixtureCrossUniverse)
20✔
1176
        {
1177
            xmlDoc.readElementText();
×
1178
            setCrossUniverse(true);
×
1179
        }
1180
        else if (xmlDoc.name() == KXMLFixtureAddress)
20✔
1181
        {
1182
            address = xmlDoc.readElementText().toInt();
10✔
1183
        }
1184
        else if (xmlDoc.name() == KXMLFixtureChannels)
10✔
1185
        {
1186
            channels = xmlDoc.readElementText().toInt();
10✔
1187
        }
1188
        else if (xmlDoc.name() == KXMLFixtureExcludeFade)
×
1189
        {
1190
            QString list = xmlDoc.readElementText();
×
1191
            QStringList values = list.split(",");
×
1192

1193
            for (int i = 0; i < values.count(); i++)
×
1194
                excludeList.append(values.at(i).toInt());
×
1195
        }
×
1196
        else if (xmlDoc.name() == KXMLFixtureForcedHTP)
×
1197
        {
1198
            QString list = xmlDoc.readElementText();
×
1199
            QStringList values = list.split(",");
×
1200

1201
            for (int i = 0; i < values.count(); i++)
×
1202
                forcedHTP.append(values.at(i).toInt());
×
1203
        }
×
1204
        else if (xmlDoc.name() == KXMLFixtureForcedLTP)
×
1205
        {
1206
            QString list = xmlDoc.readElementText();
×
1207
            QStringList values = list.split(",");
×
1208

1209
            for (int i = 0; i < values.count(); i++)
×
1210
                forcedLTP.append(values.at(i).toInt());
×
1211
        }
×
1212
        else if (xmlDoc.name() == KXMLFixtureChannelModifier)
×
1213
        {
1214
            QXmlStreamAttributes attrs = xmlDoc.attributes();
×
1215
            if (attrs.hasAttribute(KXMLFixtureChannelIndex) &&
×
1216
                attrs.hasAttribute(KXMLFixtureModifierName))
×
1217
            {
1218
                quint32 chIdx = attrs.value(KXMLFixtureChannelIndex).toString().toUInt();
×
1219
                QString modName = attrs.value(KXMLFixtureModifierName).toString();
×
1220
                ChannelModifier *chMod = doc->modifiersCache()->modifier(modName);
×
1221
                if (chMod != NULL)
×
1222
                {
1223
                    modifierIndices.append(chIdx);
×
1224
                    modifierPointers.append(chMod);
×
1225
                }
1226
                xmlDoc.skipCurrentElement();
×
1227
            }
×
UNCOV
1228
        }
×
1229
        else
1230
        {
1231
            qWarning() << Q_FUNC_INFO << "Unknown fixture tag:" << xmlDoc.name();
×
1232
            xmlDoc.skipCurrentElement();
×
1233
        }
1234
    }
1235

1236
    /* Find the given fixture definition, unless its a generic dimmer */
1237
    if (model != KXMLFixtureGeneric && model != KXMLFixtureRGBPanel)
30✔
1238
    {
1239
        fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
10✔
1240
        if (fixtureDef == NULL)
10✔
1241
        {
1242
            // fallback to project local path
1243
            QString man(manufacturer);
7✔
1244
            QString mod(model);
7✔
1245
            QString path = QString("%1%2%3-%4%5")
7✔
1246
                    .arg(doc->getWorkspacePath()).arg(QDir::separator())
14✔
1247
                    .arg(man.replace(" ", "-")).arg(mod.replace(" ", "-")).arg(KExtFixture);
14✔
1248

1249
            qDebug() << "Fixture not found. Fallback to:" << path;
7✔
1250

1251
            if (fixtureDefCache->loadQXF(path, true) == false)
7✔
1252
                qDebug() << "Failed to load definition" << path;
7✔
1253

1254
            fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
7✔
1255
            if (fixtureDef == NULL)
7✔
1256
            {
1257
                doc->appendToErrorLog(QString("No fixture definition found for <b>%1</b> <b>%2</b>")
21✔
1258
                                      .arg(manufacturer).arg(model));
14✔
1259
            }
1260
        }
7✔
1261

1262
        if (fixtureDef != NULL)
10✔
1263
        {
1264
            /* Find the given fixture mode */
1265
            fixtureMode = fixtureDef->mode(modeName);
3✔
1266
            if (fixtureMode == NULL)
3✔
1267
            {
1268
                doc->appendToErrorLog(QString("Fixture mode <b>%1</b> not found for <b>%2</b> <b>%3</b>")
×
1269
                                      .arg(modeName).arg(manufacturer).arg(model));
×
1270

1271
                /* Set this also NULL so that a generic dimmer will be
1272
                   created instead as a backup. */
UNCOV
1273
                fixtureDef = NULL;
×
1274
            }
1275
        }
1276
    }
1277

1278
    /* Number of channels */
1279
    if (channels <= 0)
10✔
1280
    {
1281
        doc->appendToErrorLog(QString("%1 channels of fixture <b>%2</b> are out of bounds")
×
1282
                              .arg(QString::number(channels))
×
1283
                              .arg(name));
×
UNCOV
1284
        channels = 1;
×
1285
    }
1286

1287
    /* Make sure that address is something sensible */
1288
    if (!crossUniverse() && (address > 511 || address + (channels - 1) > 511))
10✔
1289
    {
1290
        doc->appendToErrorLog(QString("Fixture address range %1-%2 is out of DMX bounds")
2✔
1291
                              .arg(QString::number(address))
2✔
1292
                              .arg(QString::number(address + channels - 1)));
2✔
1293
        address = 0;
1✔
1294
    }
1295

1296
    /* Check that the invalid ID is not used */
1297
    if (id == Fixture::invalidId())
10✔
1298
    {
1299
        qWarning() << Q_FUNC_INFO << "Fixture ID" << id << "is not allowed.";
1✔
1300
        return false;
1✔
1301
    }
1302

1303
    if (model == KXMLFixtureGeneric)
9✔
1304
    {
1305
        fixtureDef = genericDimmerDef(channels);
×
1306
        fixtureMode = genericDimmerMode(fixtureDef, channels);
×
1307
    }
1308
    else if (model == KXMLFixtureRGBPanel)
9✔
1309
    {
1310
        bool is16bit = false;
×
1311
        Components components = stringToComponents(modeName, is16bit);
×
1312
        int compNum = components == RGBW ? 4 : 3;
×
1313
        if (is16bit)
×
1314
            compNum *= 2;
×
1315

1316
        fixtureDef = genericRGBPanelDef(channels / compNum, components, is16bit);
×
1317
        fixtureMode = genericRGBPanelMode(fixtureDef, components, is16bit, width, height);
×
1318
    }
1319

1320
    if (fixtureDef != NULL && fixtureMode != NULL)
9✔
1321
    {
1322
        /* Assign fixtureDef & mode only if BOTH are not NULL */
1323
        setFixtureDefinition(fixtureDef, fixtureMode);
2✔
1324
    }
1325
    else
1326
    {
1327
        /* Otherwise set just the channel count */
1328
        setChannels(channels);
7✔
1329
    }
1330

1331
    setAddress(address);
9✔
1332
    setUniverse(universe);
9✔
1333
    setName(name);
9✔
1334
    setExcludeFadeChannels(excludeList);
9✔
1335
    setForcedHTPChannels(forcedHTP);
9✔
1336
    setForcedLTPChannels(forcedLTP);
9✔
1337
    for (int i = 0; i < modifierIndices.count(); i++)
9✔
1338
        setChannelModifier(modifierIndices.at(i), modifierPointers.at(i));
×
1339
    setID(id);
9✔
1340

1341
    return true;
9✔
1342
}
11✔
1343

1344
bool Fixture::saveXML(QXmlStreamWriter *doc) const
4✔
1345
{
1346
    Q_ASSERT(doc != NULL);
4✔
1347

1348
    /* Fixture Instance entry */
1349
    doc->writeStartElement(KXMLFixture);
8✔
1350

1351
    /* Manufacturer */
1352
    if (m_fixtureDef != NULL)
4✔
1353
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, m_fixtureDef->manufacturer());
8✔
1354
    else
1355
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, KXMLFixtureGeneric);
×
1356

1357
    /* Model */
1358
    if (m_fixtureDef != NULL)
4✔
1359
        doc->writeTextElement(KXMLQLCFixtureDefModel, m_fixtureDef->model());
8✔
1360
    else
1361
        doc->writeTextElement(KXMLQLCFixtureDefModel, KXMLFixtureGeneric);
×
1362

1363
    /* Fixture mode */
1364
    if (m_fixtureMode != NULL)
4✔
1365
        doc->writeTextElement(KXMLQLCFixtureMode, m_fixtureMode->name());
8✔
1366
    else
1367
        doc->writeTextElement(KXMLQLCFixtureMode, KXMLFixtureGeneric);
×
1368

1369
    /* RGB Panel physical dimensions */
1370
    if (m_fixtureDef != NULL && m_fixtureDef->model() == KXMLFixtureRGBPanel && m_fixtureMode != NULL)
8✔
1371
    {
1372
        doc->writeTextElement(KXMLQLCPhysicalDimensionsWidth,
×
1373
                              QString::number(m_fixtureMode->physical().width()));
×
1374

1375
        doc->writeTextElement(KXMLQLCPhysicalDimensionsHeight,
×
1376
                              QString::number(m_fixtureMode->physical().height()));
×
1377
    }
1378

1379
    /* ID */
1380
    doc->writeTextElement(KXMLFixtureID, QString::number(id()));
8✔
1381
    /* Name */
1382
    doc->writeTextElement(KXMLFixtureName, m_name);
8✔
1383
    /* Universe */
1384
    doc->writeTextElement(KXMLFixtureUniverse, QString::number(universe()));
8✔
1385
    if (crossUniverse())
4✔
1386
        doc->writeTextElement(KXMLFixtureCrossUniverse, KXMLQLCTrue);
×
1387
    /* Address */
1388
    doc->writeTextElement(KXMLFixtureAddress, QString::number(address()));
8✔
1389
    /* Channel count */
1390
    doc->writeTextElement(KXMLFixtureChannels, QString::number(channels()));
8✔
1391

1392
    if (m_excludeFadeIndices.count() > 0)
4✔
1393
    {
UNCOV
1394
        QString list;
×
1395
        for (int i = 0; i < m_excludeFadeIndices.count(); i++)
×
1396
        {
1397
            if (list.isEmpty() == false)
×
1398
                list.append(QString(","));
×
1399
            list.append(QString("%1").arg(m_excludeFadeIndices.at(i)));
×
1400
        }
1401
        doc->writeTextElement(KXMLFixtureExcludeFade, list);
×
1402
    }
×
1403

1404
    if (m_forcedHTPIndices.count() > 0)
4✔
1405
    {
UNCOV
1406
        QString list;
×
1407
        for (int i = 0; i < m_forcedHTPIndices.count(); i++)
×
1408
        {
1409
            if (list.isEmpty() == false)
×
1410
                list.append(QString(","));
×
1411
            list.append(QString("%1").arg(m_forcedHTPIndices.at(i)));
×
1412
        }
1413
        doc->writeTextElement(KXMLFixtureForcedHTP, list);
×
1414
    }
×
1415

1416
    if (m_forcedLTPIndices.count() > 0)
4✔
1417
    {
UNCOV
1418
        QString list;
×
1419
        for (int i = 0; i < m_forcedLTPIndices.count(); i++)
×
1420
        {
1421
            if (list.isEmpty() == false)
×
1422
                list.append(QString(","));
×
1423
            list.append(QString("%1").arg(m_forcedLTPIndices.at(i)));
×
1424
        }
1425
        doc->writeTextElement(KXMLFixtureForcedLTP, list);
×
1426
    }
×
1427

1428
    {
1429
        QMapIterator<quint32, ChannelModifier *> it(m_channelModifiers);
4✔
1430
        while (it.hasNext())
4✔
1431
        {
1432
            it.next();
×
1433
            quint32 ch = it.key();
×
1434
            ChannelModifier *mod = it.value();
×
1435
            if (mod != NULL)
×
1436
            {
1437
                doc->writeStartElement(KXMLFixtureChannelModifier);
×
1438
                doc->writeAttribute(KXMLFixtureChannelIndex, QString::number(ch));
×
1439
                doc->writeAttribute(KXMLFixtureModifierName, mod->name());
×
1440
                doc->writeEndElement();
×
1441
            }
1442
        }
1443
    }
4✔
1444

1445
    /* End the <Fixture> tag */
1446
    doc->writeEndElement();
4✔
1447

1448
    return true;
4✔
1449
}
1450

1451
/*****************************************************************************
1452
 * Status
1453
 *****************************************************************************/
1454

1455
QString Fixture::status() const
7✔
1456
{
1457
    QString info;
7✔
1458
    QString t;
7✔
1459

1460
    QString title("<TR><TD CLASS='hilite' COLSPAN='3'>%1</TD></TR>");
7✔
1461
    QString subTitle("<TR><TD CLASS='subhi' COLSPAN='3'>%1</TD></TR>");
7✔
1462
    QString genInfo("<TR><TD CLASS='emphasis'>%1</TD><TD COLSPAN='2'>%2</TD></TR>");
7✔
1463

1464
    /********************************************************************
1465
     * General info
1466
     ********************************************************************/
1467

1468
    info += "<TABLE COLS='3' WIDTH='100%'>";
7✔
1469

1470
    // Fixture title
1471
    info += title.arg(name());
7✔
1472

1473
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
7✔
1474
    {
1475
        // Manufacturer
1476
        info += genInfo.arg(tr("Manufacturer")).arg(m_fixtureDef->manufacturer());
2✔
1477
        info += genInfo.arg(tr("Model")).arg(m_fixtureDef->model());
2✔
1478
        info += genInfo.arg(tr("Mode")).arg(m_fixtureMode->name());
2✔
1479
        info += genInfo.arg(tr("Type")).arg(m_fixtureDef->typeToString(m_fixtureDef->type()));
2✔
1480
    }
1481

1482
    // Universe
1483
    info += genInfo.arg(tr("Universe")).arg(universe() + 1);
7✔
1484

1485
    // Address
1486
    QString range = QString("%1 - %2").arg(address() + 1).arg(address() + channels());
7✔
1487
    info += genInfo.arg(tr("Address Range")).arg(range);
7✔
1488

1489
    // Channels
1490
    info += genInfo.arg(tr("Channels")).arg(channels());
7✔
1491

1492
    // Binary address
1493
    QString binaryStr = QString("%1").arg(address() + 1, 10, 2, QChar('0'));
7✔
1494
    QString dipTable("<TABLE COLS='33' cellspacing='0'><TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_top.png\"></TD></TR>");
7✔
1495
    dipTable += "<TR><TD><IMG SRC=\"" ":/ds_border.png\"></TD><TD><IMG SRC=\"" ":/ds_border.png\"></TD>";
7✔
1496
    for (int i = 9; i >= 0; i--)
77✔
1497
    {
1498
        if (binaryStr.at(i) == '0')
70✔
1499
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_off.png\"></TD>";
45✔
1500
        else
1501
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_on.png\"></TD>";
25✔
1502
    }
1503
    dipTable += "<TD><IMG SRC=\"" ":/ds_border.png\"></TD></TR>";
7✔
1504
    dipTable += "<TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_bottom.png\"></TD></TR>";
7✔
1505
    dipTable += "</TABLE>";
7✔
1506

1507
    info += genInfo.arg(tr("Binary Address (DIP)"))
14✔
1508
            .arg(QString("%1").arg(dipTable));
7✔
1509

1510
    /********************************************************************
1511
     * Channels
1512
     ********************************************************************/
1513

1514
    // Title row
1515
    info += QString("<TR><TD CLASS='subhi'>%1</TD>").arg(tr("Channel"));
7✔
1516
    info += QString("<TD CLASS='subhi'>%1</TD>").arg(tr("DMX"));
7✔
1517
    info += QString("<TD CLASS='subhi'>%1</TD></TR>").arg(tr("Name"));
7✔
1518

1519
    // Fill table with the fixture's channels
1520
    for (quint32 ch = 0; ch < channels();        ch++)
28✔
1521
    {
1522
        QString chInfo("<TR><TD>%1</TD><TD>%2</TD><TD>%3</TD></TR>");
21✔
1523
        info += chInfo.arg(ch + 1).arg(address() + ch + 1)
42✔
1524
                .arg(channel(ch)->name());
21✔
1525
    }
21✔
1526

1527
    /********************************************************************
1528
     * Extended device information
1529
     ********************************************************************/
1530

1531
    if (m_fixtureMode != NULL)
7✔
1532
    {
1533
        QLCPhysical physical = m_fixtureMode->physical();
2✔
1534
        info += title.arg(tr("Physical"));
2✔
1535

1536
        float mmInch = 0.0393700787;
2✔
1537
        float kgLbs = 2.20462262;
2✔
1538
        QString mm("%1mm (%2\")");
2✔
1539
        QString kg("%1kg (%2 lbs)");
2✔
1540
        QString W("%1W");
2✔
1541
        info += genInfo.arg(tr("Width")).arg(mm.arg(physical.width()))
4✔
1542
                                        .arg(physical.width() * mmInch, 0, 'g', 4);
2✔
1543
        info += genInfo.arg(tr("Height")).arg(mm.arg(physical.height()))
4✔
1544
                                         .arg(physical.height() * mmInch, 0, 'g', 4);
2✔
1545
        info += genInfo.arg(tr("Depth")).arg(mm.arg(physical.depth()))
4✔
1546
                                        .arg(physical.depth() * mmInch, 0, 'g', 4);
2✔
1547
        info += genInfo.arg(tr("Weight")).arg(kg.arg(physical.weight()))
4✔
1548
                                         .arg(physical.weight() * kgLbs, 0, 'g', 4);
2✔
1549
        info += genInfo.arg(tr("Power consumption")).arg(W.arg(physical.powerConsumption()));
2✔
1550
        info += genInfo.arg(tr("DMX Connector")).arg(physical.dmxConnector());
2✔
1551

1552
        // Bulb
1553
        QString K("%1K");
2✔
1554
        QString lm("%1lm");
2✔
1555
        info += subTitle.arg(tr("Bulb"));
2✔
1556
        info += genInfo.arg(tr("Type")).arg(physical.bulbType());
2✔
1557
        info += genInfo.arg(tr("Luminous Flux")).arg(lm.arg(physical.bulbLumens()));
2✔
1558
        info += genInfo.arg(tr("Colour Temperature")).arg(K.arg(physical.bulbColourTemperature()));
2✔
1559

1560
        // Lens
1561
        QString angle1("%1&deg;");
2✔
1562
        QString angle2("%1&deg; &ndash; %2&deg;");
2✔
1563

1564
        info += subTitle.arg(tr("Lens"));
2✔
1565
        info += genInfo.arg(tr("Name")).arg(physical.lensName());
2✔
1566

1567
        if (physical.lensDegreesMin() == physical.lensDegreesMax())
2✔
1568
        {
1569
            info += genInfo.arg(tr("Beam Angle"))
4✔
1570
                .arg(angle1.arg(physical.lensDegreesMin()));
2✔
1571
        }
1572
        else
1573
        {
1574
            info += genInfo.arg(tr("Beam Angle"))
×
1575
                .arg(angle2.arg(physical.lensDegreesMin())
×
1576
                .arg(physical.lensDegreesMax()));
×
1577
        }
1578

1579
        // Focus
1580
        QString frange("%1&deg;");
2✔
1581
        info += subTitle.arg(tr("Head(s)"));
2✔
1582
        info += genInfo.arg(tr("Type")).arg(physical.focusType());
2✔
1583
        info += genInfo.arg(tr("Pan Range")).arg(frange.arg(physical.focusPanMax()));
2✔
1584
        info += genInfo.arg(tr("Tilt Range")).arg(frange.arg(physical.focusTiltMax()));
2✔
1585
        if (physical.layoutSize() != QSize(1, 1))
2✔
1586
        {
1587
            info += genInfo.arg(tr("Layout"))
×
1588
                           .arg(QString("%1 x %2").arg(physical.layoutSize().width()).arg(physical.layoutSize().height()));
×
1589
        }
1590
    }
2✔
1591

1592
    // HTML document & table closure
1593
    info += "</TABLE>";
7✔
1594

1595
    if (m_fixtureDef != NULL)
7✔
1596
    {
1597
        info += "<HR>";
2✔
1598
        info += "<DIV CLASS='author' ALIGN='right'>";
2✔
1599
        info += tr("Fixture definition author: ") + m_fixtureDef->author();
2✔
1600
        info += "</DIV>";
2✔
1601
    }
1602

1603
    return info;
14✔
1604
}
7✔
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