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

mcallegari / qlcplus / 27867971263

20 Jun 2026 10:09AM UTC coverage: 35.268% (-0.1%) from 35.377%
27867971263

push

github

mcallegari
Back to 5.3.0 debug

18433 of 52265 relevant lines covered (35.27%)

41250.48 hits per line

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

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

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

58
bool Fixture::operator<(const Fixture& fxi) const
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,841✔
71
{
72
    m_id = id;
16,841✔
73
    emit changed(m_id);
16,841✔
74
}
16,841✔
75

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

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

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

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

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

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

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

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

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

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

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

133
quint32 Fixture::universe() const
1,200✔
134
{
135
    /* The universe part is stored in the highest 7 bits */
136
    return (m_address >> 9);
1,200✔
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)
407✔
154
{
155
    /* Don't allow more than 512 channels per universe */
156
    if (address > 511)
407✔
157
        return;
3✔
158

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

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

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

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

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

180
void Fixture::setChannels(quint32 channels)
235✔
181
{
182
    if (m_fixtureDef == NULL && m_fixtureMode == NULL)
235✔
183
    {
184
        QLCFixtureDef *fixtureDef = genericDimmerDef(channels);
235✔
185
        QLCFixtureMode *fixtureMode = genericDimmerMode(fixtureDef, channels);
235✔
186
        setFixtureDefinition(fixtureDef, fixtureMode);
235✔
187
    }
235✔
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;
235✔
199

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

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

211
const QLCChannel* Fixture::channel(quint32 channel) const
3,465✔
212
{
213
    if (m_fixtureDef != NULL && m_fixtureMode != NULL)
3,465✔
214
        return m_fixtureMode->channel(channel);
3,462✔
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)
17✔
476
{
477
    if (indices.count() > (int)channels())
17✔
478
        return;
×
479

480
    m_excludeFadeIndices = indices;
17✔
481
}
482

483
QList<int> Fixture::excludeFadeChannels()
8✔
484
{
485
    return m_excludeFadeIndices;
8✔
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) const
635✔
502
{
503
    if (m_excludeFadeIndices.contains(index))
635✔
504
        return false;
1✔
505

506
    return true;
634✔
507
}
508

509
void Fixture::setForcedHTPChannels(QList<int> indices)
26✔
510
{
511
    if (indices.count() > (int)channels())
26✔
512
        return;
×
513
    m_forcedHTPIndices = indices;
26✔
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++)
29✔
517
        m_forcedLTPIndices.removeAll(m_forcedHTPIndices.at(i));
3✔
518
}
519

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

525
void Fixture::setForcedLTPChannels(QList<int> indices)
26✔
526
{
527
    if (indices.count() > (int)channels())
26✔
528
        return;
×
529
    m_forcedLTPIndices = indices;
26✔
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++)
28✔
533
        m_forcedHTPIndices.removeAll(m_forcedLTPIndices.at(i));
2✔
534
}
535

536
QList<int> Fixture::forcedLTPChannels()
1,055✔
537
{
538
    return m_forcedLTPIndices;
1,055✔
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,699✔
557
{
558
    return m_channelModifiers.value(idx, NULL);
2,699✔
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,
485✔
656
                                   QLCFixtureMode* fixtureMode)
657
{
658
    if (fixtureDef != NULL && fixtureMode != NULL)
485✔
659
    {
660
        int i, chNum;
661
        QByteArray prevValues;
478✔
662
        QByteArray newValues;
478✔
663

664
        if (m_fixtureDef != NULL && m_fixtureDef != fixtureDef &&
38✔
665
            m_fixtureDef->manufacturer() == KXMLFixtureGeneric &&
994✔
666
            m_fixtureDef->model() == KXMLFixtureGeneric)
486✔
667
        {
668
            delete m_fixtureDef;
2✔
669
        }
670

671
        m_fixtureDef = fixtureDef;
478✔
672
        m_fixtureMode = fixtureMode;
478✔
673
        chNum = fixtureMode->channels().size();
478✔
674
        newValues.reserve(chNum);
478✔
675

676
        {
677
            QMutexLocker locker(&m_channelsInfoMutex);
478✔
678
            prevValues = m_values;
478✔
679
            m_values.clear();
478✔
680
        }
478✔
681

682
        // If there are no head entries in the mode, create one that contains
683
        // all channels. This const_cast is a bit heretic, but it's easier this
684
        // way, than to change everything def & mode related non-const, which would
685
        // be worse than one constness violation here.
686
        if (fixtureMode->heads().size() == 0)
478✔
687
        {
688
            QLCFixtureHead head;
51✔
689
            for (i = 0; i < chNum; i++)
377✔
690
                head.addChannel(i);
326✔
691
            fixtureMode->insertHead(-1, head);
51✔
692
        }
51✔
693

694
        m_aliasInfo.resize(chNum);
478✔
695

696
        for (i = 0; i < chNum; i++)
3,820✔
697
        {
698
            QLCChannel *channel = fixtureMode->channel(i);
3,342✔
699
            const QList <QLCCapability*> capsList = channel->capabilities();
3,342✔
700

701
            // initialize values with previous values if possible, otherwise defaults
702
            uchar value = channel->defaultValue();
3,342✔
703
            if (i < prevValues.size())
3,342✔
704
                value = (uchar)prevValues.at(i);
236✔
705
            newValues.append(value);
3,342✔
706

707
            // look for aliases
708
            m_aliasInfo[i].m_hasAlias = false;
3,342✔
709
            m_aliasInfo[i].m_currCap = capsList.count() ? capsList.at(0) : NULL;
3,342✔
710

711
            foreach (QLCCapability *cap, capsList)
13,431✔
712
            {
713
                if (cap->preset() == QLCCapability::Alias)
10,089✔
714
                    m_aliasInfo[i].m_hasAlias = true;
×
715
            }
3,342✔
716
        }
3,342✔
717

718
        {
719
            QMutexLocker locker(&m_channelsInfoMutex);
478✔
720
            m_values = newValues;
478✔
721
        }
478✔
722

723
        // Cache all head channels
724
        fixtureMode->cacheHeads();
478✔
725
    }
478✔
726
    else
727
    {
728
        m_fixtureDef = NULL;
7✔
729
        m_fixtureMode = NULL;
7✔
730
        m_aliasInfo.clear();
7✔
731

732
        QMutexLocker locker(&m_channelsInfoMutex);
7✔
733
        m_values.clear();
7✔
734
    }
7✔
735

736
    emit changed(m_id);
485✔
737
}
485✔
738

739
QLCFixtureDef* Fixture::fixtureDef() const
82✔
740
{
741
    return m_fixtureDef;
82✔
742
}
743

744
QLCFixtureMode* Fixture::fixtureMode() const
729✔
745
{
746
    return m_fixtureMode;
729✔
747
}
748

749
int Fixture::heads() const
820✔
750
{
751
    return m_fixtureMode->heads().size();
820✔
752
}
753

754
QLCFixtureHead Fixture::head(int index) const
30✔
755
{
756
    if (index < m_fixtureMode->heads().size())
30✔
757
        return m_fixtureMode->heads().at(index);
30✔
758
    else
759
        return QLCFixtureHead();
×
760
}
761

762
QString Fixture::iconResource(bool svg) const
15✔
763
{
764
    QString prefix = svg ? "qrc" : "";
15✔
765
    QString ext = svg ? "svg" : "png";
15✔
766

767
    switch(type())
15✔
768
    {
769
        case QLCFixtureDef::ColorChanger: return QString("%1:/fixture.%2").arg(prefix).arg(ext);
1✔
770
        case QLCFixtureDef::Dimmer: return QString("%1:/dimmer.%2").arg(prefix).arg(ext);
4✔
771
        case QLCFixtureDef::Effect: return QString("%1:/effect.%2").arg(prefix).arg(ext);
1✔
772
        case QLCFixtureDef::Fan: return QString("%1:/fan.%2").arg(prefix).arg(ext);
×
773
        case QLCFixtureDef::Flower: return QString("%1:/flower.%2").arg(prefix).arg(ext);
1✔
774
        case QLCFixtureDef::Hazer: return QString("%1:/hazer.%2").arg(prefix).arg(ext);
1✔
775
        case QLCFixtureDef::Laser: return QString("%1:/laser.%2").arg(prefix).arg(ext);
1✔
776
        case QLCFixtureDef::MovingHead: return QString("%1:/movinghead.%2").arg(prefix).arg(ext);
1✔
777
        case QLCFixtureDef::Scanner: return QString("%1:/scanner.%2").arg(prefix).arg(ext);
1✔
778
        case QLCFixtureDef::Smoke: return QString("%1:/smoke.%2").arg(prefix).arg(ext);
1✔
779
        case QLCFixtureDef::Strobe: return QString("%1:/strobe.%2").arg(prefix).arg(ext);
1✔
780
        case QLCFixtureDef::LEDBarBeams: return QString("%1:/ledbar_beams.%2").arg(prefix).arg(ext);
1✔
781
        case QLCFixtureDef::LEDBarPixels: return QString("%1:/ledbar_pixels.%2").arg(prefix).arg(ext);
1✔
782
        default: break;
×
783
    }
784

785
    return QString("%1:/other.%2").arg(prefix).arg(ext);
×
786
}
15✔
787

788
QIcon Fixture::getIconFromType() const
3✔
789
{
790
    return QIcon(iconResource());
6✔
791
}
792

793
QRectF Fixture::degreesRange(int head) const
7✔
794
{
795
    // TODO: handle fixtures with only pan or tilt
796

797
    if (m_fixtureMode != NULL && head < m_fixtureMode->heads().size())
7✔
798
    {
799
        QLCPhysical physical(m_fixtureMode->physical());
7✔
800
        qreal pan = physical.focusPanMax();
7✔
801
        qreal tilt = physical.focusTiltMax();
7✔
802

803
        if (pan != 0 && tilt != 0)
7✔
804
        {
805
            return QRectF(-pan/2, -tilt/2, pan, tilt);
7✔
806
        }
807
    }
7✔
808

809
    return QRectF();
×
810
}
811

812
/*********************************************************************
813
 * Generic Dimmer
814
 *********************************************************************/
815

816
QLCFixtureDef *Fixture::genericDimmerDef(int channels)
238✔
817
{
818
    QLCFixtureDef *def = new QLCFixtureDef();
238✔
819
    def->setManufacturer(KXMLFixtureGeneric);
476✔
820
    def->setModel(KXMLFixtureGeneric);
476✔
821
    def->setType(QLCFixtureDef::Dimmer);
238✔
822
    def->setAuthor("QLC+");
238✔
823

824
    for (int i = 0; i < channels; i++)
960✔
825
    {
826
        QLCChannel *intensity = new QLCChannel();
722✔
827
        intensity->setGroup(QLCChannel::Intensity);
722✔
828
        intensity->setName(tr("Dimmer #%1").arg(i + 1));
722✔
829
        intensity->addCapability(new QLCCapability(0, UCHAR_MAX, tr("Intensity")));
722✔
830
        def->addChannel(intensity);
722✔
831
    }
832

833
    return def;
238✔
834
}
835

836
QLCFixtureMode *Fixture::genericDimmerMode(QLCFixtureDef *def, int channels)
238✔
837
{
838
    Q_ASSERT(def != NULL);
238✔
839
    QLCFixtureMode *mode = new QLCFixtureMode(def);
238✔
840

841
    mode->setName(QString("%1 Channel").arg(channels));
238✔
842
    QList<QLCChannel *>chList = def->channels();
238✔
843
    for (int i = 0; i < chList.count(); i++)
960✔
844
    {
845
        QLCChannel *ch = chList.at(i);
722✔
846
        mode->insertChannel(ch, i);
722✔
847
        QLCFixtureHead head;
722✔
848
        head.addChannel(i);
722✔
849
        mode->insertHead(-1, head);
722✔
850
    }
722✔
851

852
    QLCPhysical physical;
238✔
853
    physical.setWidth(300 * channels);
238✔
854
    physical.setHeight(300);
238✔
855
    physical.setDepth(300);
238✔
856

857
    mode->setPhysical(physical);
238✔
858
    def->addMode(mode);
238✔
859

860
    return mode;
238✔
861
}
238✔
862

863
/*********************************************************************
864
 * Generic RGB panel
865
 *********************************************************************/
866

867
QString Fixture::componentsToString(Components comp, bool is16bit)
8✔
868
{
869
    QString compStr;
8✔
870

871
    switch (comp)
8✔
872
    {
873
        case BGR:
1✔
874
            compStr = "BGR";
1✔
875
        break;
1✔
876
        case BRG:
1✔
877
            compStr = "BRG";
1✔
878
        break;
1✔
879
        case GBR:
1✔
880
            compStr = "GBR";
1✔
881
        break;
1✔
882
        case GRB:
1✔
883
            compStr = "GRB";
1✔
884
        break;
1✔
885
        case RBG:
1✔
886
            compStr = "RBG";
1✔
887
        break;
1✔
888
        case RGBW:
1✔
889
            compStr = "RGBW";
1✔
890
        break;
1✔
891
        default:
2✔
892
            compStr = "RGB";
2✔
893
        break;
2✔
894
    }
895

896
    if (is16bit)
8✔
897
        compStr += " 16bit";
1✔
898

899
    return compStr;
8✔
900
}
×
901

902
Fixture::Components Fixture::stringToComponents(const QString& str, bool &is16bit)
×
903
{
904
    QStringList strToken = str.split(' ');
×
905
    is16bit = false;
×
906

907
    if (strToken.count() == 2)
×
908
    {
909
        if (strToken.at(1) == "16bit")
×
910
            is16bit = true;
×
911
    }
912

913
    if (strToken.at(0) == "BGR") return BGR;
×
914
    else if (strToken.at(0) == "BRG") return BRG;
×
915
    else if (strToken.at(0) == "GBR") return GBR;
×
916
    else if (strToken.at(0) == "GRB") return GRB;
×
917
    else if (strToken.at(0) == "RBG") return RBG;
×
918
    else if (strToken.at(0) == "RGBW") return RGBW;
×
919
    else return RGB;
×
920
}
×
921

922
QLCFixtureDef *Fixture::genericRGBPanelDef(int columns, Components components, bool is16bit)
8✔
923
{
924
    QLCFixtureDef *def = new QLCFixtureDef();
8✔
925
    def->setManufacturer(KXMLFixtureGeneric);
16✔
926
    def->setModel(KXMLFixtureRGBPanel);
16✔
927
    def->setType(QLCFixtureDef::LEDBarPixels);
8✔
928
    def->setAuthor("QLC+");
8✔
929

930
    for (int i = 0; i < columns; i++)
88✔
931
    {
932
        QLCChannel *red = new QLCChannel();
80✔
933
        red->setName(QString("Red %1").arg(i + 1));
80✔
934
        red->setGroup(QLCChannel::Intensity);
80✔
935
        red->setColour(QLCChannel::Red);
80✔
936

937
        QLCChannel *green = new QLCChannel();
80✔
938
        green->setName(QString("Green %1").arg(i + 1));
80✔
939
        green->setGroup(QLCChannel::Intensity);
80✔
940
        green->setColour(QLCChannel::Green);
80✔
941

942
        QLCChannel *blue = new QLCChannel();
80✔
943
        blue->setName(QString("Blue %1").arg(i + 1));
80✔
944
        blue->setGroup(QLCChannel::Intensity);
80✔
945
        blue->setColour(QLCChannel::Blue);
80✔
946

947
        QLCChannel *redFine = NULL;
80✔
948
        QLCChannel *greenFine = NULL;
80✔
949
        QLCChannel *blueFine = NULL;
80✔
950

951
        if (is16bit)
80✔
952
        {
953
            redFine = new QLCChannel();
10✔
954
            redFine->setName(QString("Red Fine %1").arg(i + 1));
10✔
955
            redFine->setGroup(QLCChannel::Intensity);
10✔
956
            redFine->setColour(QLCChannel::Red);
10✔
957
            redFine->setControlByte(QLCChannel::LSB);
10✔
958

959
            greenFine = new QLCChannel();
10✔
960
            greenFine->setName(QString("Green Fine %1").arg(i + 1));
10✔
961
            greenFine->setGroup(QLCChannel::Intensity);
10✔
962
            greenFine->setColour(QLCChannel::Green);
10✔
963
            greenFine->setControlByte(QLCChannel::LSB);
10✔
964

965
            blueFine = new QLCChannel();
10✔
966
            blueFine->setName(QString("Blue Fine %1").arg(i + 1));
10✔
967
            blueFine->setGroup(QLCChannel::Intensity);
10✔
968
            blueFine->setColour(QLCChannel::Blue);
10✔
969
            blueFine->setControlByte(QLCChannel::LSB);
10✔
970
        }
971

972
        if (components == BGR)
80✔
973
        {
974
            def->addChannel(blue);
10✔
975
            if (is16bit) def->addChannel(blueFine);
10✔
976
            def->addChannel(green);
10✔
977
            if (is16bit) def->addChannel(greenFine);
10✔
978
            def->addChannel(red);
10✔
979
            if (is16bit) def->addChannel(redFine);
10✔
980
        }
981
        else if (components == BRG)
70✔
982
        {
983
            def->addChannel(blue);
10✔
984
            if (is16bit) def->addChannel(blueFine);
10✔
985
            def->addChannel(red);
10✔
986
            if (is16bit) def->addChannel(redFine);
10✔
987
            def->addChannel(green);
10✔
988
            if (is16bit) def->addChannel(greenFine);
10✔
989
        }
990
        else if (components == GBR)
60✔
991
        {
992
            def->addChannel(green);
10✔
993
            if (is16bit) def->addChannel(greenFine);
10✔
994
            def->addChannel(blue);
10✔
995
            if (is16bit) def->addChannel(blueFine);
10✔
996
            def->addChannel(red);
10✔
997
            if (is16bit) def->addChannel(redFine);
10✔
998
        }
999
        else if (components == GRB)
50✔
1000
        {
1001
            def->addChannel(green);
10✔
1002
            if (is16bit) def->addChannel(greenFine);
10✔
1003
            def->addChannel(red);
10✔
1004
            if (is16bit) def->addChannel(redFine);
10✔
1005
            def->addChannel(blue);
10✔
1006
            if (is16bit) def->addChannel(blueFine);
10✔
1007
        }
1008
        else if (components == RBG)
40✔
1009
        {
1010
            def->addChannel(red);
10✔
1011
            if (is16bit) def->addChannel(redFine);
10✔
1012
            def->addChannel(blue);
10✔
1013
            if (is16bit) def->addChannel(blueFine);
10✔
1014
            def->addChannel(green);
10✔
1015
            if (is16bit) def->addChannel(greenFine);
10✔
1016
        }
1017
        else if (components == RGBW)
30✔
1018
        {
1019
            QLCChannel *white = new QLCChannel();
10✔
1020
            white->setName(QString("White %1").arg(i + 1));
10✔
1021
            white->setGroup(QLCChannel::Intensity);
10✔
1022
            white->setColour(QLCChannel::White);
10✔
1023

1024
            def->addChannel(red);
10✔
1025
            if (is16bit) def->addChannel(redFine);
10✔
1026
            def->addChannel(green);
10✔
1027
            if (is16bit) def->addChannel(greenFine);
10✔
1028
            def->addChannel(blue);
10✔
1029
            if (is16bit) def->addChannel(blueFine);
10✔
1030
            def->addChannel(white);
10✔
1031

1032
            if (is16bit)
10✔
1033
            {
1034
                QLCChannel *whiteFine = new QLCChannel();
×
1035
                whiteFine->setName(QString("White Fine %1").arg(i + 1));
×
1036
                whiteFine->setGroup(QLCChannel::Intensity);
×
1037
                whiteFine->setColour(QLCChannel::White);
×
1038
                whiteFine->setControlByte(QLCChannel::LSB);
×
1039
                def->addChannel(whiteFine);
×
1040
            }
1041
        }
1042
        else
1043
        {
1044
            def->addChannel(red);
20✔
1045
            if (is16bit) def->addChannel(redFine);
20✔
1046
            def->addChannel(green);
20✔
1047
            if (is16bit) def->addChannel(greenFine);
20✔
1048
            def->addChannel(blue);
20✔
1049
            if (is16bit) def->addChannel(blueFine);
20✔
1050
        }
1051
    }
1052

1053
    return def;
8✔
1054
}
1055

1056
QLCFixtureMode *Fixture::genericRGBPanelMode(QLCFixtureDef *def, Components components, bool is16bit,
8✔
1057
                                             quint32 width, quint32 height)
1058
{
1059
    Q_ASSERT(def != NULL);
8✔
1060

1061
    QLCFixtureMode *mode = new QLCFixtureMode(def);
8✔
1062
    QString modeName = componentsToString(components, is16bit);
8✔
1063
    mode->setName(modeName);
8✔
1064

1065
    int compNum = components == RGBW ? 4 : 3;
8✔
1066
    if (is16bit)
8✔
1067
        compNum *= 2;
1✔
1068

1069
    QList<QLCChannel *>channels = def->channels();
8✔
1070
    int i = 0;
8✔
1071

1072
    // add channels and heads
1073
    for (int h = 0; h < channels.count() / compNum; h++)
88✔
1074
    {
1075
        QLCFixtureHead head;
80✔
1076

1077
        for (int c = 0; c < compNum; c++, i++)
360✔
1078
        {
1079
            QLCChannel *ch = channels.at(i);
280✔
1080
            mode->insertChannel(ch, i);
280✔
1081
            head.addChannel(i);
280✔
1082
        }
1083

1084
        mode->insertHead(-1, head);
80✔
1085
    }
80✔
1086

1087
    QLCPhysical physical;
8✔
1088
    physical.setWidth(width);
8✔
1089
    physical.setHeight(height);
8✔
1090
    physical.setDepth(height);
8✔
1091
    physical.setLayoutSize(QSize(mode->heads().count(), 1));
8✔
1092

1093
    mode->setPhysical(physical);
8✔
1094
    def->addMode(mode);
8✔
1095

1096
    return mode;
8✔
1097
}
8✔
1098

1099
/*****************************************************************************
1100
 * Load & Save
1101
 *****************************************************************************/
1102

1103
bool Fixture::loader(QXmlStreamReader &root, Doc* doc)
4✔
1104
{
1105
    bool result = false;
4✔
1106

1107
    Fixture* fxi = new Fixture(doc);
4✔
1108
    Q_ASSERT(fxi != NULL);
4✔
1109

1110
    if (fxi->loadXML(root, doc, doc->fixtureDefCache()) == true)
4✔
1111
    {
1112
        if (doc->addFixture(fxi, fxi->id(), fxi->crossUniverse()) == true)
4✔
1113
        {
1114
            /* Success */
1115
            result = true;
4✔
1116
        }
1117
        else
1118
        {
1119
            /* Doc is full */
1120
            qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name()
×
1121
                       << "cannot be created.";
×
1122
            delete fxi;
×
1123
        }
1124
    }
1125
    else
1126
    {
1127
        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
1128
        delete fxi;
×
1129
    }
1130

1131
    return result;
4✔
1132
}
1133

1134
bool Fixture::loadXML(QXmlStreamReader &xmlDoc, Doc *doc,
11✔
1135
                      QLCFixtureDefCache *fixtureDefCache)
1136
{
1137
    QLCFixtureDef* fixtureDef = NULL;
11✔
1138
    QLCFixtureMode* fixtureMode = NULL;
11✔
1139
    QString manufacturer;
11✔
1140
    QString model;
11✔
1141
    QString modeName;
11✔
1142
    QString name;
11✔
1143
    quint32 id = Fixture::invalidId();
11✔
1144
    quint32 universe = 0;
11✔
1145
    quint32 address = 0;
11✔
1146
    quint32 channels = 0;
11✔
1147
    quint32 width = 0, height = 0;
11✔
1148
    QList<int> excludeList;
11✔
1149
    QList<int> forcedHTP;
11✔
1150
    QList<int> forcedLTP;
11✔
1151
    QList<quint32>modifierIndices;
11✔
1152
    QList<ChannelModifier *>modifierPointers;
11✔
1153

1154
    if (xmlDoc.name() != KXMLFixture)
11✔
1155
    {
1156
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
1✔
1157
        return false;
1✔
1158
    }
1159

1160
    while (xmlDoc.readNextStartElement())
90✔
1161
    {
1162
        if (xmlDoc.name() == KXMLQLCFixtureDefManufacturer)
80✔
1163
        {
1164
            manufacturer = xmlDoc.readElementText();
10✔
1165
        }
1166
        else if (xmlDoc.name() == KXMLQLCFixtureDefModel)
70✔
1167
        {
1168
            model = xmlDoc.readElementText();
10✔
1169
        }
1170
        else if (xmlDoc.name() == KXMLQLCFixtureMode)
60✔
1171
        {
1172
            modeName = xmlDoc.readElementText();
10✔
1173
        }
1174
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsWidth)
50✔
1175
        {
1176
            width = xmlDoc.readElementText().toUInt();
×
1177
        }
1178
        else if (xmlDoc.name() == KXMLQLCPhysicalDimensionsHeight)
50✔
1179
        {
1180
            height = xmlDoc.readElementText().toUInt();
×
1181
        }
1182
        else if (xmlDoc.name() == KXMLFixtureID)
50✔
1183
        {
1184
            id = xmlDoc.readElementText().toUInt();
10✔
1185
        }
1186
        else if (xmlDoc.name() == KXMLFixtureName)
40✔
1187
        {
1188
            name = xmlDoc.readElementText();
10✔
1189
        }
1190
        else if (xmlDoc.name() == KXMLFixtureUniverse)
30✔
1191
        {
1192
            universe = xmlDoc.readElementText().toInt();
10✔
1193
        }
1194
        else if (xmlDoc.name() == KXMLFixtureCrossUniverse)
20✔
1195
        {
1196
            xmlDoc.readElementText();
×
1197
            setCrossUniverse(true);
×
1198
        }
1199
        else if (xmlDoc.name() == KXMLFixtureAddress)
20✔
1200
        {
1201
            address = xmlDoc.readElementText().toInt();
10✔
1202
        }
1203
        else if (xmlDoc.name() == KXMLFixtureChannels)
10✔
1204
        {
1205
            channels = xmlDoc.readElementText().toInt();
10✔
1206
        }
1207
        else if (xmlDoc.name() == KXMLFixtureExcludeFade)
×
1208
        {
1209
            QString list = xmlDoc.readElementText();
×
1210
            QStringList values = list.split(",");
×
1211

1212
            for (int i = 0; i < values.count(); i++)
×
1213
                excludeList.append(values.at(i).toInt());
×
1214
        }
×
1215
        else if (xmlDoc.name() == KXMLFixtureForcedHTP)
×
1216
        {
1217
            QString list = xmlDoc.readElementText();
×
1218
            QStringList values = list.split(",");
×
1219

1220
            for (int i = 0; i < values.count(); i++)
×
1221
                forcedHTP.append(values.at(i).toInt());
×
1222
        }
×
1223
        else if (xmlDoc.name() == KXMLFixtureForcedLTP)
×
1224
        {
1225
            QString list = xmlDoc.readElementText();
×
1226
            QStringList values = list.split(",");
×
1227

1228
            for (int i = 0; i < values.count(); i++)
×
1229
                forcedLTP.append(values.at(i).toInt());
×
1230
        }
×
1231
        else if (xmlDoc.name() == KXMLFixtureChannelModifier)
×
1232
        {
1233
            QXmlStreamAttributes attrs = xmlDoc.attributes();
×
1234
            if (attrs.hasAttribute(KXMLFixtureChannelIndex) &&
×
1235
                attrs.hasAttribute(KXMLFixtureModifierName))
×
1236
            {
1237
                quint32 chIdx = attrs.value(KXMLFixtureChannelIndex).toString().toUInt();
×
1238
                QString modName = attrs.value(KXMLFixtureModifierName).toString();
×
1239
                ChannelModifier *chMod = doc->modifiersCache()->modifier(modName);
×
1240
                if (chMod != NULL)
×
1241
                {
1242
                    modifierIndices.append(chIdx);
×
1243
                    modifierPointers.append(chMod);
×
1244
                }
1245
                xmlDoc.skipCurrentElement();
×
1246
            }
×
1247
        }
×
1248
        else
1249
        {
1250
            qWarning() << Q_FUNC_INFO << "Unknown fixture tag:" << xmlDoc.name();
×
1251
            xmlDoc.skipCurrentElement();
×
1252
        }
1253
    }
1254

1255
    /* Find the given fixture definition, unless its a generic dimmer */
1256
    if (model != KXMLFixtureGeneric && model != KXMLFixtureRGBPanel)
30✔
1257
    {
1258
        fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
10✔
1259
        if (fixtureDef == NULL)
10✔
1260
        {
1261
            // fallback to project local path
1262
            QString man(manufacturer);
7✔
1263
            QString mod(model);
7✔
1264
            QString path = QString("%1%2%3-%4%5")
7✔
1265
                    .arg(doc->workspacePath()).arg(QDir::separator())
14✔
1266
                    .arg(man.replace(" ", "-")).arg(mod.replace(" ", "-")).arg(KExtFixture);
21✔
1267

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

1270
            if (fixtureDefCache->loadQXF(path, true) == false)
7✔
1271
                qDebug() << "Failed to load definition" << path;
7✔
1272

1273
            fixtureDef = fixtureDefCache->fixtureDef(manufacturer, model);
7✔
1274
            if (fixtureDef == NULL)
7✔
1275
            {
1276
                doc->appendToErrorLog(QString("No fixture definition found for <b>%1</b> <b>%2</b>")
21✔
1277
                                      .arg(manufacturer).arg(model));
14✔
1278
            }
1279
        }
7✔
1280

1281
        if (fixtureDef != NULL)
10✔
1282
        {
1283
            /* Find the given fixture mode */
1284
            fixtureMode = fixtureDef->mode(modeName);
3✔
1285
            if (fixtureMode == NULL)
3✔
1286
            {
1287
                doc->appendToErrorLog(QString("Fixture mode <b>%1</b> not found for <b>%2</b> <b>%3</b>")
×
1288
                                      .arg(modeName).arg(manufacturer).arg(model));
×
1289

1290
                /* Set this also NULL so that a generic dimmer will be
1291
                   created instead as a backup. */
1292
                fixtureDef = NULL;
×
1293
            }
1294
        }
1295
    }
1296

1297
    /* Number of channels */
1298
    if (channels <= 0)
10✔
1299
    {
1300
        doc->appendToErrorLog(QString("%1 channels of fixture <b>%2</b> are out of bounds")
×
1301
                              .arg(QString::number(channels))
×
1302
                              .arg(name));
×
1303
        channels = 1;
×
1304
    }
1305

1306
    /* Make sure that address is something sensible */
1307
    if (!crossUniverse() && (address > 511 || address + (channels - 1) > 511))
10✔
1308
    {
1309
        doc->appendToErrorLog(QString("Fixture address range %1-%2 is out of DMX bounds")
2✔
1310
                              .arg(QString::number(address))
2✔
1311
                              .arg(QString::number(address + channels - 1)));
2✔
1312
        address = 0;
1✔
1313
    }
1314

1315
    /* Check that the invalid ID is not used */
1316
    if (id == Fixture::invalidId())
10✔
1317
    {
1318
        qWarning() << Q_FUNC_INFO << "Fixture ID" << id << "is not allowed.";
1✔
1319
        return false;
1✔
1320
    }
1321

1322
    if (model == KXMLFixtureGeneric)
9✔
1323
    {
1324
        fixtureDef = genericDimmerDef(channels);
×
1325
        fixtureMode = genericDimmerMode(fixtureDef, channels);
×
1326
    }
1327
    else if (model == KXMLFixtureRGBPanel)
9✔
1328
    {
1329
        bool is16bit = false;
×
1330
        Components components = stringToComponents(modeName, is16bit);
×
1331
        int compNum = components == RGBW ? 4 : 3;
×
1332
        if (is16bit)
×
1333
            compNum *= 2;
×
1334

1335
        fixtureDef = genericRGBPanelDef(channels / compNum, components, is16bit);
×
1336
        fixtureMode = genericRGBPanelMode(fixtureDef, components, is16bit, width, height);
×
1337
    }
1338

1339
    if (fixtureDef != NULL && fixtureMode != NULL)
9✔
1340
    {
1341
        /* Assign fixtureDef & mode only if BOTH are not NULL */
1342
        setFixtureDefinition(fixtureDef, fixtureMode);
2✔
1343
    }
1344
    else
1345
    {
1346
        /* Otherwise set just the channel count */
1347
        setChannels(channels);
7✔
1348
    }
1349

1350
    setAddress(address);
9✔
1351
    setUniverse(universe);
9✔
1352
    setName(name);
9✔
1353
    setExcludeFadeChannels(excludeList);
9✔
1354
    setForcedHTPChannels(forcedHTP);
9✔
1355
    setForcedLTPChannels(forcedLTP);
9✔
1356
    for (int i = 0; i < modifierIndices.count(); i++)
9✔
1357
        setChannelModifier(modifierIndices.at(i), modifierPointers.at(i));
×
1358
    setID(id);
9✔
1359

1360
    return true;
9✔
1361
}
11✔
1362

1363
bool Fixture::saveXML(QXmlStreamWriter *doc) const
4✔
1364
{
1365
    Q_ASSERT(doc != NULL);
4✔
1366

1367
    /* Fixture Instance entry */
1368
    doc->writeStartElement(KXMLFixture);
8✔
1369

1370
    /* Manufacturer */
1371
    if (m_fixtureDef != NULL)
4✔
1372
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, m_fixtureDef->manufacturer());
8✔
1373
    else
1374
        doc->writeTextElement(KXMLQLCFixtureDefManufacturer, KXMLFixtureGeneric);
×
1375

1376
    /* Model */
1377
    if (m_fixtureDef != NULL)
4✔
1378
        doc->writeTextElement(KXMLQLCFixtureDefModel, m_fixtureDef->model());
8✔
1379
    else
1380
        doc->writeTextElement(KXMLQLCFixtureDefModel, KXMLFixtureGeneric);
×
1381

1382
    /* Fixture mode */
1383
    if (m_fixtureMode != NULL)
4✔
1384
        doc->writeTextElement(KXMLQLCFixtureMode, m_fixtureMode->name());
8✔
1385
    else
1386
        doc->writeTextElement(KXMLQLCFixtureMode, KXMLFixtureGeneric);
×
1387

1388
    /* RGB Panel physical dimensions */
1389
    if (m_fixtureDef != NULL && m_fixtureDef->model() == KXMLFixtureRGBPanel && m_fixtureMode != NULL)
8✔
1390
    {
1391
        doc->writeTextElement(KXMLQLCPhysicalDimensionsWidth,
×
1392
                              QString::number(m_fixtureMode->physical().width()));
×
1393

1394
        doc->writeTextElement(KXMLQLCPhysicalDimensionsHeight,
×
1395
                              QString::number(m_fixtureMode->physical().height()));
×
1396
    }
1397

1398
    /* ID */
1399
    doc->writeTextElement(KXMLFixtureID, QString::number(id()));
8✔
1400
    /* Name */
1401
    doc->writeTextElement(KXMLFixtureName, m_name);
8✔
1402
    /* Universe */
1403
    doc->writeTextElement(KXMLFixtureUniverse, QString::number(universe()));
8✔
1404
    if (crossUniverse())
4✔
1405
        doc->writeTextElement(KXMLFixtureCrossUniverse, KXMLQLCTrue);
×
1406
    /* Address */
1407
    doc->writeTextElement(KXMLFixtureAddress, QString::number(address()));
8✔
1408
    /* Channel count */
1409
    doc->writeTextElement(KXMLFixtureChannels, QString::number(channels()));
8✔
1410

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

1423
    if (m_forcedHTPIndices.count() > 0)
4✔
1424
    {
1425
        QString list;
×
1426
        for (int i = 0; i < m_forcedHTPIndices.count(); i++)
×
1427
        {
1428
            if (list.isEmpty() == false)
×
1429
                list.append(QString(","));
×
1430
            list.append(QString("%1").arg(m_forcedHTPIndices.at(i)));
×
1431
        }
1432
        doc->writeTextElement(KXMLFixtureForcedHTP, list);
×
1433
    }
×
1434

1435
    if (m_forcedLTPIndices.count() > 0)
4✔
1436
    {
1437
        QString list;
×
1438
        for (int i = 0; i < m_forcedLTPIndices.count(); i++)
×
1439
        {
1440
            if (list.isEmpty() == false)
×
1441
                list.append(QString(","));
×
1442
            list.append(QString("%1").arg(m_forcedLTPIndices.at(i)));
×
1443
        }
1444
        doc->writeTextElement(KXMLFixtureForcedLTP, list);
×
1445
    }
×
1446

1447
    {
1448
        QMapIterator<quint32, ChannelModifier *> it(m_channelModifiers);
4✔
1449
        while (it.hasNext())
4✔
1450
        {
1451
            it.next();
×
1452
            quint32 ch = it.key();
×
1453
            ChannelModifier *mod = it.value();
×
1454
            if (mod != NULL)
×
1455
            {
1456
                doc->writeStartElement(KXMLFixtureChannelModifier);
×
1457
                doc->writeAttribute(KXMLFixtureChannelIndex, QString::number(ch));
×
1458
                doc->writeAttribute(KXMLFixtureModifierName, mod->name());
×
1459
                doc->writeEndElement();
×
1460
            }
1461
        }
1462
    }
4✔
1463

1464
    /* End the <Fixture> tag */
1465
    doc->writeEndElement();
4✔
1466

1467
    return true;
4✔
1468
}
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