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

mcallegari / qlcplus / 20862400268

09 Jan 2026 06:59PM UTC coverage: 34.176% (+0.006%) from 34.17%
20862400268

push

github

mcallegari
qmlui: fix fixture editor definition reference (fix #1920)

17720 of 51850 relevant lines covered (34.18%)

19820.0 hits per line

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

88.59
/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()
81,883✔
35
    : m_isLoaded(false)
81,883✔
36
    , m_isUser(false)
81,883✔
37
    , m_fileAbsolutePath(QString())
81,883✔
38
    , m_type(Dimmer)
81,883✔
39
{
40
}
81,883✔
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()
81,645✔
53
{
54
    while (m_channels.isEmpty() == false)
82,445✔
55
        delete m_channels.takeFirst();
800✔
56

57
    while (m_modes.isEmpty() == false)
81,797✔
58
        delete m_modes.takeFirst();
152✔
59
}
81,645✔
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)
81,602✔
103
{
104
    m_fileAbsolutePath = absPath;
81,602✔
105
    m_isLoaded = false;
81,602✔
106
}
81,602✔
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)
81,910✔
118
{
119
    m_manufacturer = mfg;
81,910✔
120
}
81,910✔
121

122
QString QLCFixtureDef::manufacturer() const
72,027,308✔
123
{
124
    return m_manufacturer;
72,027,308✔
125
}
126

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

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

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

142
QLCFixtureDef::FixtureType QLCFixtureDef::type()
50✔
143
{
144
    return m_type;
50✔
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";
2✔
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";
4✔
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()
3✔
193
{
194
    return m_author;
3✔
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,400✔
232
{
233
    return m_isUser;
3,400✔
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,783✔
246
{
247
    if (channel != NULL && m_channels.contains(channel) == false)
1,783✔
248
    {
249
        m_channels.append(channel);
1,781✔
250
        return true;
1,781✔
251
    }
252
    else
253
    {
254
        return false;
2✔
255
    }
256
}
257

258
bool QLCFixtureDef::removeChannel(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,808✔
295
{
296
    return m_channels;
2,808✔
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(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)
46✔
333
{
334
    QListIterator <QLCFixtureMode*> it(m_modes);
46✔
335

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

343
    return NULL;
1✔
344
}
46✔
345

346
QList <QLCFixtureMode*> QLCFixtureDef::modes()
198✔
347
{
348
    return m_modes;
198✔
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
56✔
361
{
362
    return m_physical;
56✔
363
}
364

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

369
QFile::FileError QLCFixtureDef::saveXML(const QString& fileName)
2✔
370
{
371
    QFile::FileError error;
372

373
    if (fileName.isEmpty() == true)
2✔
374
        return QFile::OpenError;
×
375

376
    QString tempFileName(fileName);
2✔
377
    tempFileName += ".temp";
2✔
378
    QFile file(tempFileName);
2✔
379
    if (file.open(QIODevice::WriteOnly) == false)
2✔
380
        return file.error();
1✔
381

382
    QXmlStreamWriter doc(&file);
1✔
383
    doc.setAutoFormatting(true);
1✔
384
    doc.setAutoFormattingIndent(1);
1✔
385
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
386
    doc.setCodec("UTF-8");
387
#endif
388
    QLCFile::writeXMLHeader(&doc, KXMLQLCFixtureDefDocument, author());
2✔
389

390
    doc.writeTextElement(KXMLQLCFixtureDefManufacturer, m_manufacturer);
2✔
391
    doc.writeTextElement(KXMLQLCFixtureDefModel, m_model);
2✔
392
    doc.writeTextElement(KXMLQLCFixtureDefType, typeToString(m_type));
2✔
393

394
    /* Channels */
395
    QListIterator <QLCChannel*> chit(m_channels);
1✔
396
    while (chit.hasNext() == true)
2✔
397
        chit.next()->saveXML(&doc);
1✔
398

399
    /* Modes */
400
    QListIterator <QLCFixtureMode*> modeit(m_modes);
1✔
401
    while (modeit.hasNext() == true)
2✔
402
        modeit.next()->saveXML(&doc);
1✔
403

404
    m_physical.saveXML(&doc);
1✔
405

406
    /* End the document and close all the open elements */
407
    error = QFile::NoError;
1✔
408
    doc.writeEndDocument();
1✔
409
    file.close();
1✔
410

411
    // Save to actual requested file name
412
    QFile currFile(fileName);
1✔
413
    if (currFile.exists() && !currFile.remove())
1✔
414
    {
415
        qWarning() << "Could not erase" << fileName;
×
416
        return currFile.error();
×
417
    }
418
    if (!file.rename(fileName))
1✔
419
    {
420
        qWarning() << "Could not rename" << tempFileName << "to" << fileName;
×
421
        return file.error();
×
422
    }
423

424
    return error;
1✔
425
}
2✔
426

427
QFile::FileError QLCFixtureDef::loadXML(const QString& fileName)
66✔
428
{
429
    QFile::FileError error = QFile::NoError;
66✔
430

431
    if (fileName.isEmpty() == true)
66✔
432
        return QFile::OpenError;
1✔
433

434
    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
65✔
435
    if (doc == NULL || doc->device() == NULL || doc->hasError())
65✔
436
    {
437
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
10✔
438
        return QFile::ReadError;
10✔
439
    }
440

441
    while (!doc->atEnd())
110✔
442
    {
443
        if (doc->readNext() == QXmlStreamReader::DTD)
110✔
444
            break;
55✔
445
    }
446
    if (doc->hasError())
55✔
447
    {
448
        QLCFile::releaseXMLReader(doc);
×
449
        return QFile::ResourceError;
×
450
    }
451

452
    if (doc->dtdName() == KXMLQLCFixtureDefDocument)
55✔
453
    {
454
        // qDebug() << Q_FUNC_INFO << "Loading " << fileName;
455
        if (loadXML(*doc) == true)
55✔
456
            error = QFile::NoError;
55✔
457
        else
458
        {
459
            qWarning() << fileName << QString("%1\nLine %2, column %3")
×
460
                        .arg(doc->errorString())
×
461
                        .arg(doc->lineNumber())
×
462
                        .arg(doc->columnNumber());
×
463
            error = QFile::ReadError;
×
464
        }
465
    }
466
    else
467
    {
468
        error = QFile::ReadError;
×
469
        qWarning() << Q_FUNC_INFO << fileName
×
470
                   << "is not a fixture definition file";
×
471
    }
472

473
    QLCFile::releaseXMLReader(doc);
55✔
474

475
    return error;
55✔
476
}
477

478
bool QLCFixtureDef::loadXML(QXmlStreamReader& doc)
55✔
479
{
480
    bool retval = false;
55✔
481

482
    if (doc.readNextStartElement() == false)
55✔
483
        return false;
×
484

485
    if (doc.name() == KXMLQLCFixtureDef)
55✔
486
    {
487
        while (doc.readNextStartElement())
1,218✔
488
        {
489
            if (doc.name() == KXMLQLCCreator)
1,163✔
490
            {
491
                loadCreator(doc);
55✔
492
            }
493
            else if (doc.name() == KXMLQLCFixtureDefManufacturer)
1,108✔
494
            {
495
                setManufacturer(doc.readElementText());
55✔
496
            }
497
            else if (doc.name() == KXMLQLCFixtureDefModel)
1,053✔
498
            {
499
                setModel(doc.readElementText());
55✔
500
            }
501
            else if (doc.name() == KXMLQLCFixtureDefType)
998✔
502
            {
503
                setType(stringToType(doc.readElementText()));
55✔
504
            }
505
            else if (doc.name() == KXMLQLCChannel)
943✔
506
            {
507
                QLCChannel* ch = new QLCChannel();
750✔
508
                if (ch->loadXML(doc) == true)
750✔
509
                {
510
                    /* Loading succeeded */
511
                    if (addChannel(ch) == false)
750✔
512
                    {
513
                        /* Channel already exists */
514
                        delete ch;
×
515
                    }
516
                }
517
                else
518
                {
519
                    /* Loading failed */
520
                    delete ch;
×
521
                }
522
            }
523
            else if (doc.name() == KXMLQLCFixtureMode)
193✔
524
            {
525
                QLCFixtureMode* mode = new QLCFixtureMode(this);
138✔
526
                if (mode->loadXML(doc) == true)
138✔
527
                {
528
                    /* Loading succeeded */
529
                    if (addMode(mode) == false)
138✔
530
                    {
531
                        /* Mode already exists */
532
                        delete mode;
×
533
                    }
534
                }
535
                else
536
                {
537
                    /* Loading failed */
538
                    delete mode;
×
539
                }
540
            }
541
            else if (doc.name() == KXMLQLCPhysical)
55✔
542
            {
543
                /* Global physical */
544
                QLCPhysical physical;
55✔
545
                physical.loadXML(doc);
55✔
546
                setPhysical(physical);
55✔
547
            }
55✔
548
            else
549
            {
550
                qWarning() << Q_FUNC_INFO << "Unknown Fixture tag: " << doc.name();
×
551
                doc.skipCurrentElement();
×
552
            }
553
        }
554

555
        retval = true;
55✔
556
    }
557
    else
558
    {
559
        qWarning() << Q_FUNC_INFO << "Fixture node not found";
×
560
        retval = false;
×
561
    }
562

563
    if (retval == true)
55✔
564
        m_isLoaded = true;
55✔
565

566
    if (m_modes.isEmpty())
55✔
567
        return false;
×
568

569
    return retval;
55✔
570
}
571

572
bool QLCFixtureDef::loadCreator(QXmlStreamReader &doc)
55✔
573
{
574
    if (doc.name() != KXMLQLCCreator)
55✔
575
    {
576
        qWarning() << Q_FUNC_INFO << "file creator information not found!";
×
577
        return false;
×
578
    }
579

580
    while (doc.readNextStartElement())
220✔
581
    {
582
        if (doc.name() == KXMLQLCCreatorName)
165✔
583
        {
584
            /* Ignore name */
585
            doc.skipCurrentElement();
55✔
586
        }
587
        else if (doc.name() == KXMLQLCCreatorVersion)
110✔
588
        {
589
            /* Ignore version */
590
            doc.skipCurrentElement();
55✔
591
        }
592
        else if (doc.name() == KXMLQLCCreatorAuthor)
55✔
593
        {
594
            setAuthor(doc.readElementText());
55✔
595
        }
596
        else
597
        {
598
            qWarning() << Q_FUNC_INFO << "unknown creator tag:" << doc.name();
×
599
            doc.skipCurrentElement();
×
600
        }
601
    }
602

603
    return true;
55✔
604
}
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