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

mcallegari / qlcplus / 21760395819

06 Feb 2026 05:52PM UTC coverage: 33.994% (-0.08%) from 34.078%
21760395819

push

github

mcallegari
Back to 5.2.1 debug

17635 of 51877 relevant lines covered (33.99%)

19809.79 hits per line

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

71.43
/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)
16,833✔
44
    : QObject(parent)
45
    , m_id(Fixture::invalidId())
33,666✔
46
    , m_address(0)
16,833✔
47
    , m_crossUniverse(false)
16,833✔
48
    , m_channels(0)
16,833✔
49
    , m_fixtureDef(NULL)
16,833✔
50
    , m_fixtureMode(NULL)
16,833✔
51
{
52
}
16,833✔
53

54
Fixture::~Fixture()
33,645✔
55
{
56
}
33,645✔
57

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

66
/*****************************************************************************
67
 * Fixture ID
68
 *****************************************************************************/
69

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

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

81
quint32 Fixture::invalidId()
831,757✔
82
{
83
    return UINT_MAX;
831,757✔
84
}
85

86
/*****************************************************************************
87
 * Name
88
 *****************************************************************************/
89

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

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

101
/*****************************************************************************
102
 * Fixture type
103
 *****************************************************************************/
104

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

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

121
/*****************************************************************************
122
 * Universe
123
 *****************************************************************************/
124

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

130
    emit changed(m_id);
94✔
131
}
94✔
132

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

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

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

149
/*****************************************************************************
150
 * Address
151
 *****************************************************************************/
152

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

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

162
    emit changed(m_id);
379✔
163
}
164

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

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

176
/*****************************************************************************
177
 * Channels
178
 *****************************************************************************/
179

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

198
    m_channels = channels;
233✔
199

200
    emit changed(m_id);
233✔
201
}
233✔
202

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

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

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

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

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

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

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

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

288
    return set;
4✔
289
}
×
290

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

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

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

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

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

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

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

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

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

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

332
    QLCPhysical phy = fixtureMode()->physical();
2✔
333
    qreal headDegrees = degrees, maxDegrees;
2✔
334

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

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

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

354
                if (panLSB != QLCChannel::invalid())
×
355
                {
356
                    chDegrees = (maxDegrees / 65536.0) * channelValueAt(panLSB);
×
357
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
358
                }
359
            }
360

361
            quint16 degToDmx = (headDegrees * 65535.0) / maxDegrees;
1✔
362
            posList.append(SceneValue(id(), panMSB, static_cast<uchar>(degToDmx >> 8)));
1✔
363

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

367
            qDebug() << "[positionToValues] Pan MSB idx:" << panMSB << "LSB idx:" << panLSB << "value" << QString::number(degToDmx, 16);
1✔
368

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

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

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

391
                if (tiltLSB != QLCChannel::invalid())
×
392
                {
393
                    chDegrees = (maxDegrees / 65536.0) * channelValueAt(tiltLSB);
×
394
                    headDegrees = qBound(0.0, chDegrees + headDegrees, maxDegrees);
×
395
                }
396
            }
397

398
            quint16 degToDmx = (headDegrees * 65535.0) / maxDegrees;
1✔
399
            posList.append(SceneValue(id(), tiltMSB, static_cast<uchar>(degToDmx >> 8)));
1✔
400

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

404
            qDebug() << "[positionToValues] Tilt MSB idx:" << tiltMSB << "LSB idx:" << tiltLSB << "value" << QString::number(degToDmx, 16);
1✔
405

406
            chDone.append(tiltMSB);
1✔
407
        }
408

409
    }
410

411
    return posList;
2✔
412
}
2✔
413

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

418
    if (m_fixtureMode == NULL)
×
419
        return chList;
×
420

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

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

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

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

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

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

450
            qDebug() << "Relative channel degrees:" << chDegrees << "MSB?" << ch->controlByte();
×
451

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

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

472
    return chList;
×
473
}
×
474

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

480
    m_excludeFadeIndices = indices;
11✔
481
}
482

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

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

501
bool Fixture::channelCanFade(int index)
795,213✔
502
{
503
    if (m_excludeFadeIndices.contains(index))
795,213✔
504
        return false;
1✔
505

506
    return true;
795,212✔
507
}
508

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

520
QList<int> Fixture::forcedHTPChannels()
795,627✔
521
{
522
    return m_forcedHTPIndices;
795,627✔
523
}
524

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

536
QList<int> Fixture::forcedLTPChannels()
795,626✔
537
{
538
    return m_forcedLTPIndices;
795,626✔
539
}
540

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

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

552
    qDebug() << Q_FUNC_INFO << idx << mod->name();
×
553
    m_channelModifiers[idx] = mod;
×
554
}
555

556
ChannelModifier *Fixture::channelModifier(quint32 idx)
2,603✔
557
{
558
    return m_channelModifiers.value(idx, NULL);
2,603✔
559
}
560

561
/*********************************************************************
562
 * Channel info
563
 *********************************************************************/
564

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

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

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

587
    if (changed == true)
1✔
588
        emit valuesChanged();
1✔
589

590
    return changed;
1✔
591
}
592

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

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

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

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

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

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

629
        m_fixtureMode->replaceChannel(currChannel, origChannel);
×
630
    }
×
631

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

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

642
        m_fixtureMode->replaceChannel(currChannel, newChannel);
×
643
    }
×
644

645
    emit aliasChanged();
×
646

647
    m_aliasInfo[chIndex].m_currCap = cap;
×
648

649
}
650

651
/*****************************************************************************
652
 * Fixture definition
653
 *****************************************************************************/
654

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

662
        if (m_fixtureDef != NULL && m_fixtureDef != fixtureDef &&
38✔
663
            m_fixtureDef->manufacturer() == KXMLFixtureGeneric &&
954✔
664
            m_fixtureDef->model() == KXMLFixtureGeneric)
466✔
665
        {
666
            delete m_fixtureDef;
2✔
667
        }
668

669
        m_fixtureDef = fixtureDef;
458✔
670
        m_fixtureMode = fixtureMode;
458✔
671
        chNum = fixtureMode->channels().size();
458✔
672

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

685
        m_aliasInfo.resize(chNum);
458✔
686

687
        for (i = 0; i < chNum; i++)
3,741✔
688
        {
689
            QLCChannel *channel = fixtureMode->channel(i);
3,283✔
690
            const QList <QLCCapability*> capsList = channel->capabilities();
3,283✔
691

692
            // initialize values with the channel default
693
            m_values.append(channel->defaultValue());
3,283✔
694

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

699
            foreach (QLCCapability *cap, capsList)
13,329✔
700
            {
701
                if (cap->preset() == QLCCapability::Alias)
10,046✔
702
                    m_aliasInfo[i].m_hasAlias = true;
×
703
            }
3,283✔
704
        }
3,283✔
705

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

715
    emit changed(m_id);
460✔
716
}
460✔
717

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

723
QLCFixtureMode* Fixture::fixtureMode() const
795,316✔
724
{
725
    return m_fixtureMode;
795,316✔
726
}
727

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

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

741
QString Fixture::iconResource(bool svg) const
13✔
742
{
743
    QString prefix = svg ? "qrc" : "";
13✔
744
    QString ext = svg ? "svg" : "png";
13✔
745

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

764
    return QString("%1:/other.%2").arg(prefix).arg(ext);
×
765
}
13✔
766

767
QIcon Fixture::getIconFromType() const
1✔
768
{
769
    return QIcon(iconResource());
2✔
770
}
771

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

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

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

788
    return QRectF();
×
789
}
790

791
/*********************************************************************
792
 * Generic Dimmer
793
 *********************************************************************/
794

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

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

812
    return def;
233✔
813
}
814

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

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

831
    QLCPhysical physical;
233✔
832
    physical.setWidth(300 * channels);
233✔
833
    physical.setHeight(300);
233✔
834
    physical.setDepth(300);
233✔
835

836
    mode->setPhysical(physical);
233✔
837
    def->addMode(mode);
233✔
838

839
    return mode;
233✔
840
}
233✔
841

842
/*********************************************************************
843
 * Generic RGB panel
844
 *********************************************************************/
845

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

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

875
    if (is16bit)
8✔
876
        compStr += " 16bit";
1✔
877

878
    return compStr;
8✔
879
}
×
880

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

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

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

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

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

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

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

926
        QLCChannel *redFine = NULL;
80✔
927
        QLCChannel *greenFine = NULL;
80✔
928
        QLCChannel *blueFine = NULL;
80✔
929

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

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

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

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

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

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

1032
    return def;
8✔
1033
}
1034

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

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

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

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

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

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

1063
        mode->insertHead(-1, head);
80✔
1064
    }
80✔
1065

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

1072
    mode->setPhysical(physical);
8✔
1073
    def->addMode(mode);
8✔
1074

1075
    return mode;
8✔
1076
}
8✔
1077

1078
/*****************************************************************************
1079
 * Load & Save
1080
 *****************************************************************************/
1081

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

1086
    Fixture* fxi = new Fixture(doc);
4✔
1087
    Q_ASSERT(fxi != NULL);
4✔
1088

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

1110
    return result;
4✔
1111
}
1112

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1339
    return true;
9✔
1340
}
11✔
1341

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

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

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

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

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

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

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

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

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

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

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

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

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

1446
    return true;
4✔
1447
}
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