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

mcallegari / qlcplus / 12610312303

04 Jan 2025 12:09PM UTC coverage: 31.65% (-0.01%) from 31.664%
12610312303

Pull #1659

github

mcallegari
ui: fix some more cross universe cases
Pull Request #1659: RGBPanel: implement cross universe and 16bit profiles

99 of 214 new or added lines in 10 files covered. (46.26%)

234 existing lines in 6 files now uncovered.

14141 of 44679 relevant lines covered (31.65%)

26856.33 hits per line

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

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

5
  Copyright (C) Heikki Junnila
6
                Massimo Callegari
7

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

199
    m_channels = channels;
224✔
200

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

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

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

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

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

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

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

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

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

289
    return set;
4✔
290
}
291

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

411
    }
412

413
    return posList;
414
}
415

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

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

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

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

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

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

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

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

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

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

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

474
    return chList;
475
}
476

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

482
    m_excludeFadeIndices = indices;
11✔
483
}
484

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

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

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

508
    return true;
509
}
510

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

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

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

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

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

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

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

558
ChannelModifier *Fixture::channelModifier(quint32 idx)
2,572✔
559
{
560
    if (m_channelModifiers.contains(idx))
2,572✔
561
        return m_channelModifiers[idx];
×
562

563
    return NULL;
564
}
565

566
/*********************************************************************
567
 * Channel info
568
 *********************************************************************/
569

570
bool Fixture::setChannelValues(const QByteArray &values)
1✔
571
{
572
    const int addr = address();
1✔
573
    if (addr >= values.size())
1✔
574
        return false;
575

576
    const int chNum = qMin(values.size() - addr, (int)channels());
2✔
577
    bool changed = false;
578

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

592
    if (changed == true)
1✔
593
        emit valuesChanged();
1✔
594

595
    return changed;
596
}
597

598
QByteArray Fixture::channelValues()
11✔
599
{
600
    QMutexLocker locker(&m_channelsInfoMutex);
11✔
601
    return m_values;
11✔
602
}
603

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

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

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

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

631
        QLCChannel *currChannel = m_fixtureMode->channel(alias.targetChannel);
×
632
        QLCChannel *origChannel = m_fixtureDef->channel(alias.sourceChannel);
×
633

634
        m_fixtureMode->replaceChannel(currChannel, origChannel);
×
635
    }
636

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

644
        QLCChannel *currChannel = m_fixtureMode->channel(alias.sourceChannel);
×
645
        QLCChannel *newChannel = m_fixtureDef->channel(alias.targetChannel);
×
646

647
        m_fixtureMode->replaceChannel(currChannel, newChannel);
×
648
    }
649

650
    emit aliasChanged();
×
651

652
    m_aliasInfo[chIndex].m_currCap = cap;
×
653

654
}
655

656
/*****************************************************************************
657
 * Fixture definition
658
 *****************************************************************************/
659

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

667
        if (m_fixtureDef != NULL && m_fixtureDef != fixtureDef &&
40✔
668
            m_fixtureDef->manufacturer() == KXMLFixtureGeneric &&
929✔
669
            m_fixtureDef->model() == KXMLFixtureGeneric)
459✔
670
        {
671
            delete m_fixtureDef;
3✔
672
        }
673

674
        m_fixtureDef = fixtureDef;
450✔
675
        m_fixtureMode = fixtureMode;
450✔
676
        chNum = fixtureMode->channels().size();
450✔
677

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

690
        m_aliasInfo.resize(chNum);
450✔
691

692
        for (i = 0; i < chNum; i++)
3,734✔
693
        {
694
            QLCChannel *channel = fixtureMode->channel(i);
3,284✔
695
            const QList <QLCCapability*> capsList = channel->capabilities();
6,568✔
696

697
            // initialize values with the channel default
698
            m_values.append(channel->defaultValue());
3,284✔
699

700
            // look for aliases
701
            m_aliasInfo[i].m_hasAlias = false;
3,284✔
702
            m_aliasInfo[i].m_currCap = capsList.count() ? capsList.at(0) : NULL;
6,568✔
703

704
            foreach (QLCCapability *cap, capsList)
23,536✔
705
            {
706
                if (cap->preset() == QLCCapability::Alias)
10,126✔
707
                    m_aliasInfo[i].m_hasAlias = true;
×
708
            }
709
        }
710

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

720
    emit changed(m_id);
452✔
721
}
452✔
722

723
QLCFixtureDef* Fixture::fixtureDef() const
33✔
724
{
725
    return m_fixtureDef;
33✔
726
}
727

728
QLCFixtureMode* Fixture::fixtureMode() const
795,314✔
729
{
730
    return m_fixtureMode;
795,314✔
731
}
732

733
int Fixture::heads() const
820✔
734
{
735
    return m_fixtureMode->heads().size();
820✔
736
}
737

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

746
QString Fixture::iconResource(bool svg) const
12✔
747
{
748
    QString prefix = svg ? "qrc" : "";
33✔
749
    QString ext = svg ? "svg" : "png";
33✔
750

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

769
    return QString("%1:/other.%2").arg(prefix).arg(ext);
×
770
}
771

772
QIcon Fixture::getIconFromType() const
×
773
{
774
    return QIcon(iconResource());
×
775
}
776

777
QRectF Fixture::degreesRange(int head) const
7✔
778
{
779
    // TODO: handle fixtures with only pan or tilt
780

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

787
        if (pan != 0 && tilt != 0)
7✔
788
        {
789
            return QRectF(-pan/2, -tilt/2, pan, tilt);
7✔
790
        }
791
    }
792

793
    return QRectF();
794
}
795

796
/*********************************************************************
797
 * Generic Dimmer
798
 *********************************************************************/
799

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

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

817
    return def;
224✔
818
}
819

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

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

836
    QLCPhysical physical;
448✔
837
    physical.setWidth(300 * channels);
224✔
838
    physical.setHeight(300);
224✔
839
    physical.setDepth(300);
224✔
840

841
    mode->setPhysical(physical);
224✔
842
    def->addMode(mode);
224✔
843

844
    return mode;
224✔
845
}
846

847
/*********************************************************************
848
 * Generic RGB panel
849
 *********************************************************************/
850

851
QString Fixture::componentsToString(Components comp, bool is16bit)
8✔
852
{
853
    QString compStr;
854

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

880
    if (is16bit)
8✔
881
        compStr += " 16bit";
1✔
882

883
    return compStr;
8✔
884
}
885

NEW
886
Fixture::Components Fixture::stringToComponents(QString str, bool &is16bit)
×
887
{
NEW
888
    QStringList strToken = str.split(' ');
×
NEW
889
    is16bit = false;
×
890

NEW
891
    if (strToken.count() == 2)
×
892
    {
NEW
893
        if (strToken.at(1) == "16bit")
×
NEW
894
            is16bit = true;
×
895
    }
896

NEW
897
    if (strToken.at(0) == "BGR") return BGR;
×
NEW
898
    else if (strToken.at(0) == "BRG") return BRG;
×
NEW
899
    else if (strToken.at(0) == "GBR") return GBR;
×
NEW
900
    else if (strToken.at(0) == "GRB") return GRB;
×
NEW
901
    else if (strToken.at(0) == "RBG") return RBG;
×
NEW
902
    else if (strToken.at(0) == "RGBW") return RGBW;
×
NEW
903
    else return RGB;
×
904
}
905

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

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

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

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

931
        QLCChannel *redFine = NULL;
932
        QLCChannel *greenFine = NULL;
933
        QLCChannel *blueFine = NULL;
934

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

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

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

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

1008
            def->addChannel(red);
10✔
1009
            if (is16bit) def->addChannel(redFine);
10✔
1010
            def->addChannel(green);
10✔
1011
            if (is16bit) def->addChannel(greenFine);
10✔
1012
            def->addChannel(blue);
10✔
1013
            if (is16bit) def->addChannel(blueFine);
10✔
1014
            def->addChannel(white);
10✔
1015

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

1037
    return def;
8✔
1038
}
1039

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

1045
    QLCFixtureMode *mode = new QLCFixtureMode(def);
8✔
1046
    QString modeName = componentsToString(components, is16bit);
16✔
1047
    mode->setName(modeName);
8✔
1048

1049
    int compNum = components == RGBW ? 4 : 3;
8✔
1050
    if (is16bit)
8✔
1051
        compNum *= 2;
1✔
1052

1053
    QList<QLCChannel *>channels = def->channels();
16✔
1054
    int i = 0;
1055

1056
    // add channels and heads
1057
    for (int h = 0; h < channels.count() / compNum; h++)
88✔
1058
    {
1059
        QLCFixtureHead head;
160✔
1060

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

1068
        mode->insertHead(-1, head);
80✔
1069
    }
1070

1071
    QLCPhysical physical;
16✔
1072
    physical.setWidth(width);
8✔
1073
    physical.setHeight(height);
8✔
1074
    physical.setDepth(height);
8✔
1075
    physical.setLayoutSize(QSize(mode->heads().count(), 1));
8✔
1076

1077
    mode->setPhysical(physical);
8✔
1078
    def->addMode(mode);
8✔
1079

1080
    return mode;
8✔
1081
}
1082

1083
/*****************************************************************************
1084
 * Load & Save
1085
 *****************************************************************************/
1086

1087
bool Fixture::loader(QXmlStreamReader &root, Doc* doc)
4✔
1088
{
1089
    bool result = false;
1090

1091
    Fixture* fxi = new Fixture(doc);
4✔
1092
    Q_ASSERT(fxi != NULL);
1093

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

1115
    return result;
4✔
1116
}
1117

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

1138
    if (xmlDoc.name() != KXMLFixture)
22✔
1139
    {
1140
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
1✔
1141
        return false;
1✔
1142
    }
1143

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

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

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

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

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

1252
            qDebug() << "Fixture not found. Fallback to:" << path;
1253

1254
            if (fixtureDefCache->loadQXF(path, true) == false)
7✔
1255
                qDebug() << "Failed to load definition" << path;
1256

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

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

1274
                /* Set this also NULL so that a generic dimmer will be
1275
                   created instead as a backup. */
1276
                fixtureDef = NULL;
1277
            }
1278
        }
1279
    }
1280

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

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

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

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

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

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

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

1342
    return true;
1343
}
1344

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

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

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

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

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

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

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

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

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

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

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

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

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

1450
    return true;
4✔
1451
}
1452

1453
/*****************************************************************************
1454
 * Status
1455
 *****************************************************************************/
1456

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

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

1466
    /********************************************************************
1467
     * General info
1468
     ********************************************************************/
1469

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

1472
    // Fixture title
1473
    info += title.arg(name());
14✔
1474

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

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

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

1491
    // Channels
1492
    info += genInfo.arg(tr("Channels")).arg(channels());
14✔
1493

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

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

1512
    /********************************************************************
1513
     * Channels
1514
     ********************************************************************/
1515

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

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

1529
    /********************************************************************
1530
     * Extended device information
1531
     ********************************************************************/
1532

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

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

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

1562
        // Lens
1563
        QString angle1("%1&deg;");
4✔
1564
        QString angle2("%1&deg; &ndash; %2&deg;");
4✔
1565

1566
        info += subTitle.arg(tr("Lens"));
4✔
1567
        info += genInfo.arg(tr("Name")).arg(physical.lensName());
4✔
1568

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

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

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

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

1605
    return info;
7✔
1606
}
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