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

mcallegari / qlcplus / 26001149757

17 May 2026 07:57PM UTC coverage: 35.037% (+0.005%) from 35.032%
26001149757

push

github

mcallegari
qmlui: add fixture definition colors auto assignment

18293 of 52211 relevant lines covered (35.04%)

41158.63 hits per line

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

88.39
/engine/src/qlcfixturedef.cpp
1
/*
2
  Q Light Controller Plus
3
  qlcfixturedef.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 <iostream>
24
#include <QString>
25
#include <QDebug>
26
#include <QFile>
27

28
#include "qlcfixturemode.h"
29
#include "qlcfixturedef.h"
30
#include "qlcchannel.h"
31
#include "qlcfile.h"
32
#include "fixture.h"
33

34
QLCFixtureDef::QLCFixtureDef()
82,555✔
35
    : m_isLoaded(false)
82,555✔
36
    , m_isUser(false)
82,555✔
37
    , m_fileAbsolutePath(QString())
82,555✔
38
    , m_type(Dimmer)
82,555✔
39
{
40
}
82,555✔
41

42
QLCFixtureDef::QLCFixtureDef(const QLCFixtureDef* fixtureDef)
1✔
43
    : m_isLoaded(false)
1✔
44
    , m_isUser(false)
1✔
45
    , m_fileAbsolutePath(QString())
1✔
46
    , m_type(Dimmer)
1✔
47
{
48
    if (fixtureDef != NULL)
1✔
49
        *this = *fixtureDef;
1✔
50
}
1✔
51

52
QLCFixtureDef::~QLCFixtureDef()
82,316✔
53
{
54
    while (m_channels.isEmpty() == false)
83,104✔
55
        delete m_channels.takeFirst();
788✔
56

57
    while (m_modes.isEmpty() == false)
82,467✔
58
        delete m_modes.takeFirst();
151✔
59
}
82,316✔
60

61
QLCFixtureDef& QLCFixtureDef::operator=(const QLCFixtureDef& fixture)
1✔
62
{
63
    if (this != &fixture)
1✔
64
    {
65
        m_fileAbsolutePath = fixture.m_fileAbsolutePath;
1✔
66
        m_isUser = fixture.m_isUser;
1✔
67
        m_isLoaded = fixture.m_isLoaded;
1✔
68
        m_manufacturer = fixture.m_manufacturer;
1✔
69
        m_model = fixture.m_model;
1✔
70
        m_type = fixture.m_type;
1✔
71
        m_author = fixture.m_author;
1✔
72

73
        /* Clear all channels */
74
        while (m_channels.isEmpty() == false)
1✔
75
            delete m_channels.takeFirst();
×
76

77
        /* Copy channels from the other fixture */
78
        QListIterator <QLCChannel*> chit(fixture.m_channels);
1✔
79
        while (chit.hasNext() == true)
2✔
80
            m_channels.append(chit.next()->createCopy());
1✔
81

82
        /* Clear all modes */
83
        while (m_modes.isEmpty() == false)
1✔
84
            delete m_modes.takeFirst();
×
85

86
        /* Copy modes from the other fixture */
87
        QListIterator <QLCFixtureMode*> modeit(fixture.m_modes);
1✔
88
        while (modeit.hasNext() == true)
2✔
89
            m_modes.append(new QLCFixtureMode(this, modeit.next()));
1✔
90

91
        m_physical = fixture.physical();
1✔
92
    }
1✔
93

94
    return *this;
1✔
95
}
96

97
QString QLCFixtureDef::definitionSourceFile() const
4✔
98
{
99
    return m_fileAbsolutePath;
4✔
100
}
101

102
void QLCFixtureDef::setDefinitionSourceFile(const QString &absPath)
82,274✔
103
{
104
    m_fileAbsolutePath = absPath;
82,274✔
105
    m_isLoaded = false;
82,274✔
106
}
82,274✔
107

108
/****************************************************************************
109
 * General properties
110
 ****************************************************************************/
111

112
QString QLCFixtureDef::name() const
7✔
113
{
114
    return m_manufacturer + QString(" ") + m_model;
14✔
115
}
116

117
void QLCFixtureDef::setManufacturer(const QString& mfg)
82,582✔
118
{
119
    m_manufacturer = mfg;
82,582✔
120
}
82,582✔
121

122
QString QLCFixtureDef::manufacturer() const
73,213,623✔
123
{
124
    return m_manufacturer;
73,213,623✔
125
}
126

127
void QLCFixtureDef::setModel(const QString& model)
82,582✔
128
{
129
    m_model = model;
82,582✔
130
}
82,582✔
131

132
QString QLCFixtureDef::model() const
2,878,195✔
133
{
134
    return m_model;
2,878,195✔
135
}
136

137
void QLCFixtureDef::setType(const FixtureType type)
300✔
138
{
139
    m_type = type;
300✔
140
}
300✔
141

142
QLCFixtureDef::FixtureType QLCFixtureDef::type() const
52✔
143
{
144
    return m_type;
52✔
145
}
146

147
QLCFixtureDef::FixtureType QLCFixtureDef::stringToType(const QString& type)
55✔
148
{
149
    if (type == "Color Changer") return ColorChanger;
55✔
150
    else if (type == "Dimmer") return Dimmer;
50✔
151
    else if (type == "Effect") return Effect;
50✔
152
    else if (type == "Fan") return Fan;
49✔
153
    else if (type == "Flower") return Flower;
49✔
154
    else if (type == "Hazer") return Hazer;
48✔
155
    else if (type == "Laser") return Laser;
47✔
156
    else if (type == "Moving Head") return MovingHead;
46✔
157
    else if (type == "Scanner") return Scanner;
32✔
158
    else if (type == "Smoke") return Smoke;
14✔
159
    else if (type == "Strobe") return Strobe;
13✔
160
    else if (type == "LED Bar (Beams)") return LEDBarBeams;
10✔
161
    else if (type == "LED Bar (Pixels)") return LEDBarPixels;
4✔
162

163
    return Other;
1✔
164
}
165

166
QString QLCFixtureDef::typeToString(QLCFixtureDef::FixtureType type)
28✔
167
{
168
    switch(type)
28✔
169
    {
170
        case ColorChanger: return "Color Changer";
2✔
171
        case Dimmer: return "Dimmer";
3✔
172
        case Effect: return "Effect";
2✔
173
        case Fan: return "Fan";
×
174
        case Flower: return "Flower";
2✔
175
        case Hazer: return "Hazer";
2✔
176
        case Laser: return "Laser";
2✔
177
        case MovingHead: return "Moving Head";
3✔
178
        case Scanner: return "Scanner";
3✔
179
        case Smoke: return "Smoke";
2✔
180
        case Strobe: return "Strobe";
2✔
181
        case LEDBarBeams: return "LED Bar (Beams)";
2✔
182
        case LEDBarPixels: return "LED Bar (Pixels)";
2✔
183
        default: return "Other";
1✔
184
    }
185
}
186

187
void QLCFixtureDef::setAuthor(const QString& author)
297✔
188
{
189
    m_author = author;
297✔
190
}
297✔
191

192
QString QLCFixtureDef::author() const
1✔
193
{
194
    return m_author;
1✔
195
}
196

197
void QLCFixtureDef::checkLoaded(QString mapPath)
197✔
198
{
199
    // Already loaded ? Nothing to do
200
    if (m_isLoaded == true)
197✔
201
        return;
146✔
202

203
    if (manufacturer() == KXMLFixtureGeneric &&
167✔
204
       (model() == KXMLFixtureGeneric || model() == KXMLFixtureRGBPanel))
57✔
205
    {
206
        m_isLoaded = true;
×
207
        return;
×
208
    }
209
    if (m_fileAbsolutePath.isEmpty())
55✔
210
    {
211
        qWarning() << Q_FUNC_INFO << "Empty file path provided! This is a trouble.";
4✔
212
        return;
4✔
213
    }
214

215
    // check if path is relative (from map) or absolute (user def)
216
    QDir defPath(m_fileAbsolutePath);
51✔
217
    if (defPath.isRelative())
51✔
218
        m_fileAbsolutePath = QString("%1%2%3").arg(mapPath).arg(QDir::separator()).arg(m_fileAbsolutePath);
51✔
219

220
    qDebug() << "Loading fixture definition now... " << m_fileAbsolutePath;
51✔
221
    bool error = loadXML(m_fileAbsolutePath);
51✔
222
    if (error == false)
51✔
223
        m_isLoaded = true;
51✔
224
}
51✔
225

226
void QLCFixtureDef::setLoaded(bool loaded)
2✔
227
{
228
    m_isLoaded = loaded;
2✔
229
}
2✔
230

231
bool QLCFixtureDef::isUser() const
3,428✔
232
{
233
    return m_isUser;
3,428✔
234
}
235

236
void QLCFixtureDef::setIsUser(bool flag)
2✔
237
{
238
    m_isUser = flag;
2✔
239
}
2✔
240

241
/****************************************************************************
242
 * Channels
243
 ****************************************************************************/
244

245
bool QLCFixtureDef::addChannel(QLCChannel* channel)
1,772✔
246
{
247
    if (channel != NULL && m_channels.contains(channel) == false)
1,772✔
248
    {
249
        m_channels.append(channel);
1,770✔
250
        return true;
1,770✔
251
    }
252
    else
253
    {
254
        return false;
2✔
255
    }
256
}
257

258
bool QLCFixtureDef::removeChannel(const QLCChannel* channel)
8✔
259
{
260
    /* First remove the channel from all modes */
261
    QListIterator <QLCFixtureMode*> modeit(m_modes);
8✔
262
    while (modeit.hasNext() == true)
10✔
263
        modeit.next()->removeChannel(channel);
2✔
264

265
    /* Then remove the actual channel from this fixture definition */
266
    QMutableListIterator <QLCChannel*> chit(m_channels);
8✔
267
    while (chit.hasNext() == true)
12✔
268
    {
269
        if (chit.next() == channel)
7✔
270
        {
271
            chit.remove();
3✔
272
            delete channel;
3✔
273
            return true;
3✔
274
        }
275
    }
276

277
    return false;
5✔
278
}
8✔
279

280
QLCChannel* QLCFixtureDef::channel(const QString& name)
1,469✔
281
{
282
    QListIterator <QLCChannel*> it(m_channels);
1,469✔
283

284
    while (it.hasNext() == true)
21,307✔
285
    {
286
        QLCChannel* ch = it.next();
21,304✔
287
        if (ch->name() == name)
21,304✔
288
            return ch;
1,466✔
289
    }
290

291
    return NULL;
3✔
292
}
1,469✔
293

294
QList <QLCChannel*> QLCFixtureDef::channels() const
2,797✔
295
{
296
    return m_channels;
2,797✔
297
}
298

299
/****************************************************************************
300
 * Modes
301
 ****************************************************************************/
302

303
bool QLCFixtureDef::addMode(QLCFixtureMode* mode)
394✔
304
{
305
    if (mode != NULL && m_modes.contains(mode) == false)
394✔
306
    {
307
        m_modes.append(mode);
392✔
308
        return true;
392✔
309
    }
310
    else
311
    {
312
        return false;
2✔
313
    }
314
}
315

316
bool QLCFixtureDef::removeMode(const QLCFixtureMode* mode)
7✔
317
{
318
    QMutableListIterator <QLCFixtureMode*> it(m_modes);
7✔
319
    while (it.hasNext() == true)
9✔
320
    {
321
        if (it.next() == mode)
4✔
322
        {
323
            it.remove();
2✔
324
            delete mode;
2✔
325
            return true;
2✔
326
        }
327
    }
328

329
    return false;
5✔
330
}
331

332
QLCFixtureMode *QLCFixtureDef::mode(const QString& name)
47✔
333
{
334
    QListIterator <QLCFixtureMode*> it(m_modes);
47✔
335

336
    while (it.hasNext() == true)
76✔
337
    {
338
        QLCFixtureMode *mode = it.next();
75✔
339
        if (mode->name() == name)
75✔
340
            return mode;
46✔
341
    }
342

343
    return NULL;
1✔
344
}
47✔
345

346
QList <QLCFixtureMode*> QLCFixtureDef::modes()
197✔
347
{
348
    return m_modes;
197✔
349
}
350

351
/****************************************************************************
352
 * Physical
353
 ****************************************************************************/
354

355
void QLCFixtureDef::setPhysical(const QLCPhysical& physical)
55✔
356
{
357
    m_physical = physical;
55✔
358
}
55✔
359

360
QLCPhysical QLCFixtureDef::physical() const
55✔
361
{
362
    return m_physical;
55✔
363
}
364

365
/****************************************************************************
366
 * XML operations
367
 ****************************************************************************/
368

369
void QLCFixtureDef::clear()
55✔
370
{
371
    while (m_channels.isEmpty() == false)
55✔
372
        delete m_channels.takeFirst();
×
373
    while (m_modes.isEmpty() == false)
55✔
374
        delete m_modes.takeFirst();
×
375

376
    m_manufacturer.clear();
55✔
377
    m_model.clear();
55✔
378
    m_author.clear();
55✔
379
    m_type = Dimmer;
55✔
380
    m_physical = QLCPhysical();
55✔
381
}
55✔
382

383
QFile::FileError QLCFixtureDef::saveXML(const QString& fileName)
2✔
384
{
385
    QFile::FileError error;
386

387
    if (fileName.isEmpty() == true)
2✔
388
        return QFile::OpenError;
×
389

390
    QString tempFileName(fileName);
2✔
391
    tempFileName += ".temp";
2✔
392
    QFile file(tempFileName);
2✔
393
    if (file.open(QIODevice::WriteOnly) == false)
2✔
394
        return file.error();
1✔
395

396
    QXmlStreamWriter doc(&file);
1✔
397
    doc.setAutoFormatting(true);
1✔
398
    doc.setAutoFormattingIndent(1);
1✔
399
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
400
    doc.setCodec("UTF-8");
401
#endif
402
    QLCFile::writeXMLHeader(&doc, KXMLQLCFixtureDefDocument, author());
2✔
403

404
    doc.writeTextElement(KXMLQLCFixtureDefManufacturer, m_manufacturer);
2✔
405
    doc.writeTextElement(KXMLQLCFixtureDefModel, m_model);
2✔
406
    doc.writeTextElement(KXMLQLCFixtureDefType, typeToString(m_type));
2✔
407

408
    /* Channels */
409
    QListIterator <QLCChannel*> chit(m_channels);
1✔
410
    while (chit.hasNext() == true)
2✔
411
        chit.next()->saveXML(&doc);
1✔
412

413
    /* Modes */
414
    QListIterator <QLCFixtureMode*> modeit(m_modes);
1✔
415
    while (modeit.hasNext() == true)
2✔
416
        modeit.next()->saveXML(&doc);
1✔
417

418
    m_physical.saveXML(&doc);
1✔
419

420
    /* End the document and close all the open elements */
421
    error = QFile::NoError;
1✔
422
    doc.writeEndDocument();
1✔
423
    file.close();
1✔
424

425
    // Save to actual requested file name
426
    QFile currFile(fileName);
1✔
427
    if (currFile.exists() && !currFile.remove())
1✔
428
    {
429
        qWarning() << "Could not erase" << fileName;
×
430
        return currFile.error();
×
431
    }
432
    if (!file.rename(fileName))
1✔
433
    {
434
        qWarning() << "Could not rename" << tempFileName << "to" << fileName;
×
435
        return file.error();
×
436
    }
437

438
    return error;
1✔
439
}
2✔
440

441
QFile::FileError QLCFixtureDef::loadXML(const QString& fileName)
66✔
442
{
443
    QFile::FileError error = QFile::NoError;
66✔
444

445
    if (fileName.isEmpty() == true)
66✔
446
        return QFile::OpenError;
1✔
447

448
    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
65✔
449
    if (doc == NULL || doc->device() == NULL || doc->hasError())
65✔
450
    {
451
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
10✔
452
        return QFile::ReadError;
10✔
453
    }
454

455
    while (!doc->atEnd())
110✔
456
    {
457
        if (doc->readNext() == QXmlStreamReader::DTD)
110✔
458
            break;
55✔
459
    }
460
    if (doc->hasError())
55✔
461
    {
462
        QLCFile::releaseXMLReader(doc);
×
463
        return QFile::ResourceError;
×
464
    }
465

466
    if (doc->dtdName() == KXMLQLCFixtureDefDocument)
55✔
467
    {
468
        // qDebug() << Q_FUNC_INFO << "Loading " << fileName;
469
        if (loadXML(*doc) == true)
55✔
470
            error = QFile::NoError;
55✔
471
        else
472
        {
473
            qWarning() << fileName << QString("%1\nLine %2, column %3")
×
474
                        .arg(doc->errorString())
×
475
                        .arg(doc->lineNumber())
×
476
                        .arg(doc->columnNumber());
×
477
            error = QFile::ReadError;
×
478
        }
479
    }
480
    else
481
    {
482
        error = QFile::ReadError;
×
483
        qWarning() << Q_FUNC_INFO << fileName
×
484
                   << "is not a fixture definition file";
×
485
    }
486

487
    QLCFile::releaseXMLReader(doc);
55✔
488

489
    return error;
55✔
490
}
491

492
bool QLCFixtureDef::loadXML(QXmlStreamReader& doc)
55✔
493
{
494
    bool retval = false;
55✔
495

496
    if (doc.readNextStartElement() == false)
55✔
497
        return false;
×
498

499
    if (doc.name() == KXMLQLCFixtureDef)
55✔
500
    {
501
        // Reset current state before parsing to avoid duplicate append if the same
502
        // fixture definition instance is loaded more than once.
503
        clear();
55✔
504

505
        while (doc.readNextStartElement())
1,218✔
506
        {
507
            if (doc.name() == KXMLQLCCreator)
1,163✔
508
            {
509
                loadCreator(doc);
55✔
510
            }
511
            else if (doc.name() == KXMLQLCFixtureDefManufacturer)
1,108✔
512
            {
513
                setManufacturer(doc.readElementText());
55✔
514
            }
515
            else if (doc.name() == KXMLQLCFixtureDefModel)
1,053✔
516
            {
517
                setModel(doc.readElementText());
55✔
518
            }
519
            else if (doc.name() == KXMLQLCFixtureDefType)
998✔
520
            {
521
                setType(stringToType(doc.readElementText()));
55✔
522
            }
523
            else if (doc.name() == KXMLQLCChannel)
943✔
524
            {
525
                QLCChannel* ch = new QLCChannel();
750✔
526
                if (ch->loadXML(doc) == true)
750✔
527
                {
528
                    /* Loading succeeded */
529
                    if (addChannel(ch) == false)
750✔
530
                    {
531
                        /* Channel already exists */
532
                        delete ch;
×
533
                    }
534
                }
535
                else
536
                {
537
                    /* Loading failed */
538
                    delete ch;
×
539
                }
540
            }
541
            else if (doc.name() == KXMLQLCFixtureMode)
193✔
542
            {
543
                QLCFixtureMode* mode = new QLCFixtureMode(this);
138✔
544
                if (mode->loadXML(doc) == true)
138✔
545
                {
546
                    /* Loading succeeded */
547
                    if (addMode(mode) == false)
138✔
548
                    {
549
                        /* Mode already exists */
550
                        delete mode;
×
551
                    }
552
                }
553
                else
554
                {
555
                    /* Loading failed */
556
                    delete mode;
×
557
                }
558
            }
559
            else if (doc.name() == KXMLQLCPhysical)
55✔
560
            {
561
                /* Global physical */
562
                QLCPhysical physical;
55✔
563
                physical.loadXML(doc);
55✔
564
                setPhysical(physical);
55✔
565
            }
55✔
566
            else
567
            {
568
                qWarning() << Q_FUNC_INFO << "Unknown Fixture tag: " << doc.name();
×
569
                doc.skipCurrentElement();
×
570
            }
571
        }
572

573
        retval = true;
55✔
574
    }
575
    else
576
    {
577
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
×
578
        retval = false;
×
579
    }
580

581
    if (retval == true)
55✔
582
        m_isLoaded = true;
55✔
583

584
    if (m_modes.isEmpty())
55✔
585
        return false;
×
586

587
    return retval;
55✔
588
}
589

590
bool QLCFixtureDef::loadCreator(QXmlStreamReader &doc)
55✔
591
{
592
    if (doc.name() != KXMLQLCCreator)
55✔
593
    {
594
        qWarning() << Q_FUNC_INFO << "file creator information not found!";
×
595
        return false;
×
596
    }
597

598
    while (doc.readNextStartElement())
220✔
599
    {
600
        if (doc.name() == KXMLQLCCreatorName)
165✔
601
        {
602
            /* Ignore name */
603
            doc.skipCurrentElement();
55✔
604
        }
605
        else if (doc.name() == KXMLQLCCreatorVersion)
110✔
606
        {
607
            /* Ignore version */
608
            doc.skipCurrentElement();
55✔
609
        }
610
        else if (doc.name() == KXMLQLCCreatorAuthor)
55✔
611
        {
612
            setAuthor(doc.readElementText());
55✔
613
        }
614
        else
615
        {
616
            qWarning() << Q_FUNC_INFO << "unknown creator tag:" << doc.name();
×
617
            doc.skipCurrentElement();
×
618
        }
619
    }
620

621
    return true;
55✔
622
}
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