• 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

77.39
/engine/src/qlcfixturedefcache.cpp
1
/*
2
  Q Light Controller
3
  qlcfixturedefcache.cpp
4

5
  Copyright (c) Heikki Junnila
6

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

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

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

20
#include <QCoreApplication>
21
#include <QXmlStreamReader>
22
#include <QDebug>
23
#include <QList>
24
#include <QSet>
25

26
#if defined(WIN32) || defined(Q_OS_WIN)
27
#   include <windows.h>
28
#else
29
#   include <unistd.h>
30
#endif
31

32
#include "qlcfixturedefcache.h"
33
#include "avolitesd4parser.h"
34
#include "qlcfixturedef.h"
35
#include "qlcconfig.h"
36
#include "qlcfile.h"
37

38
#define FIXTURES_MAP_NAME QStringLiteral("FixturesMap.xml")
39
#define KXMLQLCFixtureMap QStringLiteral("FixturesMap")
40

41
QLCFixtureDefCache::QLCFixtureDefCache()
200✔
42
{
43
}
200✔
44

45
QLCFixtureDefCache::~QLCFixtureDefCache()
200✔
46
{
47
    clear();
200✔
48
}
200✔
49

50
QLCFixtureDef* QLCFixtureDefCache::fixtureDef(
215✔
51
    const QString& manufacturer, const QString& model) const
52
{
53
    QListIterator <QLCFixtureDef*> it(m_defs);
215✔
54
    while (it.hasNext() == true)
173,069✔
55
    {
56
        QLCFixtureDef* def = it.next();
173,051✔
57
        if (def->manufacturer() == manufacturer && def->model() == model)
173,051✔
58
        {
59
            def->checkLoaded(m_mapAbsolutePath);
197✔
60
            return def;
197✔
61
        }
62
    }
63

64
    return NULL;
18✔
65
}
215✔
66

67
QStringList QLCFixtureDefCache::manufacturers() const
38✔
68
{
69
    QSet <QString> makers;
38✔
70

71
    // Gather a list of manufacturers
72
    QListIterator <QLCFixtureDef*> it(m_defs);
38✔
73
    while (it.hasNext() == true)
34,361✔
74
        makers << it.next()->manufacturer();
34,323✔
75

76
    // Bounce the QSet into a QStringList
77
    QStringList list;
38✔
78
    foreach (QString manuf, makers)
2,929✔
79
        list << manuf;
2,929✔
80

81
    return list;
76✔
82
}
38✔
83

84
QStringList QLCFixtureDefCache::models(const QString& manufacturer) const
83,721✔
85
{
86
    QSet <QString> models;
83,721✔
87
    QListIterator <QLCFixtureDef*> it(m_defs);
83,721✔
88
    while (it.hasNext() == true)
73,004,167✔
89
    {
90
        QLCFixtureDef* def = it.next();
72,920,446✔
91
        if (def->manufacturer() == manufacturer)
72,920,446✔
92
            models << def->model();
2,785,969✔
93
    }
94

95
    // Bounce the QSet into a QStringList
96
    QStringList list;
83,721✔
97
    foreach (QString model, models)
2,869,690✔
98
        list << model;
2,869,690✔
99

100
    return list;
167,442✔
101
}
83,721✔
102

103
QMap<QString, QMap<QString, bool> > QLCFixtureDefCache::fixtureCache() const
2✔
104
{
105
    QMap<QString, QMap<QString, bool> > map;
2✔
106

107
    QListIterator <QLCFixtureDef*> it(m_defs);
2✔
108
    while (it.hasNext() == true)
3,430✔
109
    {
110
        QLCFixtureDef *def = it.next();
3,428✔
111
        map[def->manufacturer()][def->model()] = def->isUser();
3,428✔
112
    }
113

114
    return map;
4✔
115
}
2✔
116

117
bool QLCFixtureDefCache::addFixtureDef(QLCFixtureDef* fixtureDef)
82,284✔
118
{
119
    if (fixtureDef == NULL)
82,284✔
120
        return false;
1✔
121

122
    if (models(fixtureDef->manufacturer()).contains(fixtureDef->model()) == false)
82,283✔
123
    {
124
        m_defs << fixtureDef;
82,280✔
125
        return true;
82,280✔
126
    }
127
    else
128
    {
129
        qWarning() << Q_FUNC_INFO << "Cache already contains"
3✔
130
                   << fixtureDef->name();
3✔
131
        return false;
3✔
132
    }
133
}
134

135
bool QLCFixtureDefCache::storeFixtureDef(const QString& filename, const QString& data)
1✔
136
{
137
    QDir userFolder = userDefinitionDirectory();
1✔
138

139
    QFile file(userFolder.absoluteFilePath(filename));
1✔
140
    if (file.open(QIODevice::WriteOnly | QIODevice::Text) == false)
1✔
141
        return false;
×
142

143
    file.write(data.toUtf8());
1✔
144
    file.close();
1✔
145
#ifdef Q_OS_UNIX
146
    sync();
1✔
147
#endif
148

149
    // reload user definitions
150
    load(userDefinitionDirectory());
1✔
151

152
    return true;
1✔
153
}
1✔
154

155
bool QLCFixtureDefCache::reloadFixtureDef(QLCFixtureDef *fixtureDef)
1✔
156
{
157
    int idx = m_defs.indexOf(fixtureDef);
1✔
158
    if (idx == -1)
1✔
159
        return false;
×
160

161
    QLCFixtureDef *def = m_defs.takeAt(idx);
1✔
162
    QString absPath = def->definitionSourceFile();
1✔
163
    delete def;
1✔
164

165
    QLCFixtureDef *origDef = new QLCFixtureDef();
1✔
166
    origDef->loadXML(absPath);
1✔
167
    m_defs << origDef;
1✔
168

169
    return true;
1✔
170
}
1✔
171

172
bool QLCFixtureDefCache::reloadOrAddFixtureDef(QLCFixtureDef *fixtureDef)
×
173
{
174
    // Check existing definitions first.
175
    for (int i = 0; i < m_defs.size(); i++)
×
176
    {
177
        QLCFixtureDef *def = m_defs.at(i);
×
178
        if (def->manufacturer() == fixtureDef->manufacturer() &&
×
179
            def->model() == fixtureDef->model())
×
180
        {
181
            if (def == fixtureDef)
×
182
            {
183
                // Cache and editor must not share ownership of the same instance.
184
                // Keep a detached cache copy and leave editor-owned instance untouched.
185
                QLCFixtureDef *cacheCopy = new QLCFixtureDef(fixtureDef);
×
186
                cacheCopy->setIsUser(true);
×
187
                cacheCopy->setLoaded(true);
×
188
                m_defs[i] = cacheCopy;
×
189
            }
190
            else
191
            {
192
                // Set as user and perform a deep copy.
193
                def->setIsUser(true);
×
194
                *def = *fixtureDef;
×
195
                def->setLoaded(true);
×
196
            }
197

198
            return true;
×
199
        }
200
    }
201

202
    // Add a new user fixture as a detached cache copy.
203
    QLCFixtureDef *cacheCopy = new QLCFixtureDef(fixtureDef);
×
204
    cacheCopy->setIsUser(true);
×
205
    cacheCopy->setLoaded(true);
×
206
    if (addFixtureDef(cacheCopy) == false)
×
207
    {
208
        delete cacheCopy;
×
209
        return false;
×
210
    }
211

212
    return true;
×
213
}
214

215
bool QLCFixtureDefCache::load(const QDir& dir)
9✔
216
{
217
    qDebug() << Q_FUNC_INFO << dir.path();
9✔
218

219
    if (dir.exists() == false || dir.isReadable() == false)
9✔
220
        return false;
7✔
221

222
    /* Attempt to read all specified files from the given directory */
223
    QStringListIterator it(dir.entryList());
2✔
224
    while (it.hasNext() == true)
3✔
225
    {
226
        QString path(dir.absoluteFilePath(it.next()));
1✔
227

228
        if (path.toLower().endsWith(KExtFixture) == true)
2✔
229
            loadQXF(path, true);
1✔
230
        else if (path.toLower().endsWith(KExtAvolitesFixture) == true)
×
231
            loadD4(path);
×
232
        else
233
            qWarning() << Q_FUNC_INFO << "Unrecognized fixture extension:" << path;
×
234
    }
1✔
235

236
    return true;
2✔
237
}
2✔
238

239
int QLCFixtureDefCache::loadMapManufacturer(QXmlStreamReader *doc, const QString& manufacturer)
6,864✔
240
{
241
    int count = 0;
6,864✔
242
    QString spacedManufacturer = manufacturer;
6,864✔
243
    spacedManufacturer.replace("_", " ");
6,864✔
244

245
    while (doc->readNextStartElement())
89,136✔
246
    {
247
        if (doc->name() == QString("F"))
82,272✔
248
        {
249
            QString defFile = "";
82,272✔
250
            QString model = "";
82,272✔
251

252
            if (doc->attributes().hasAttribute("n"))
164,544✔
253
            {
254
                defFile = QString("%1%2%3%4")
164,544✔
255
                            .arg(manufacturer).arg(QDir::separator())
164,544✔
256
                            .arg(doc->attributes().value("n").toString()).arg(KExtFixture);
246,816✔
257
                //qDebug() << "Manufacturer" << spacedManufacturer << "file" << defFile;
258
            }
259

260
            if (doc->attributes().hasAttribute("m"))
164,544✔
261
                model = doc->attributes().value("m").toString();
164,544✔
262

263
            if (defFile.isEmpty() == false &&
82,272✔
264
                spacedManufacturer.isEmpty() == false &&
164,544✔
265
                model.isEmpty() == false)
82,272✔
266
            {
267
                QLCFixtureDef *fxi = new QLCFixtureDef();
82,272✔
268
                Q_ASSERT(fxi != NULL);
82,272✔
269

270
                fxi->setDefinitionSourceFile(defFile);
82,272✔
271
                fxi->setManufacturer(spacedManufacturer);
82,272✔
272
                fxi->setModel(model);
82,272✔
273

274
                /* Delete the def if it's a duplicate. */
275
                if (addFixtureDef(fxi) == false)
82,272✔
276
                    delete fxi;
×
277
                fxi = NULL;
82,272✔
278
                count++;
82,272✔
279
            }
280
        }
82,272✔
281
        else
282
        {
283
            qWarning() << Q_FUNC_INFO << "Unknown manufacturer tag: " << doc->name();
×
284
        }
285
        doc->skipCurrentElement();
82,272✔
286
    }
287

288
    return count;
6,864✔
289
}
6,864✔
290

291
bool QLCFixtureDefCache::loadMap(const QDir &dir)
48✔
292
{
293
    qDebug() << Q_FUNC_INFO << dir.path();
48✔
294

295
    if (dir.exists() == false || dir.isReadable() == false)
48✔
296
        return false;
×
297

298
    QString mapPath(dir.absoluteFilePath(FIXTURES_MAP_NAME));
96✔
299

300
    if (mapPath.isEmpty() == true)
48✔
301
        return false;
×
302

303
    // cache the map path to be used when composing the fixture
304
    // definition absolute path
305
    m_mapAbsolutePath = dir.absolutePath();
48✔
306

307
    QXmlStreamReader *doc = QLCFile::getXMLReader(mapPath);
48✔
308
    if (doc == NULL || doc->device() == NULL || doc->hasError())
48✔
309
    {
310
        qWarning() << Q_FUNC_INFO << "Unable to read from" << mapPath;
×
311
        return false;
×
312
    }
313

314
    while (!doc->atEnd())
96✔
315
    {
316
        if (doc->readNext() == QXmlStreamReader::DTD)
96✔
317
            break;
48✔
318
    }
319

320
    if (doc->hasError())
48✔
321
    {
322
        QLCFile::releaseXMLReader(doc);
×
323
        return false;
×
324
    }
325

326
    // make sure the doc type is FixtureMap
327
    if (doc->dtdName() != KXMLQLCFixtureMap)
48✔
328
    {
329
        qWarning() << Q_FUNC_INFO << mapPath << "is not a fixture map file";
×
330
        QLCFile::releaseXMLReader(doc);
×
331
        return false;
×
332
    }
333

334
    if (doc->readNextStartElement() == false)
48✔
335
    {
336
        QLCFile::releaseXMLReader(doc);
×
337
        return false;
×
338
    }
339

340
    // make sure the root tag is FixtureMap
341
    if (doc->name() != KXMLQLCFixtureMap)
48✔
342
    {
343
        qWarning() << Q_FUNC_INFO << mapPath << "is not a fixture map file";
×
344
        QLCFile::releaseXMLReader(doc);
×
345
        return false;
×
346
    }
347

348
    int fxCount = 0;
48✔
349
    QString manufacturer = "";
48✔
350

351
    while (doc->readNextStartElement())
6,912✔
352
    {
353
        if (doc->name() == QString("M"))
6,864✔
354
        {
355
            if (doc->attributes().hasAttribute("n"))
13,728✔
356
            {
357
                manufacturer = doc->attributes().value("n").toString();
13,728✔
358
                fxCount += loadMapManufacturer(doc, manufacturer);
6,864✔
359
            }
360
        }
361
        else
362
        {
363
            qWarning() << Q_FUNC_INFO << "Unknown Fixture Map tag: " << doc->name();
×
364
            doc->skipCurrentElement();
×
365
        }
366
    }
367
    qDebug() << fxCount << "fixtures found in map";
48✔
368

369
#if 0
370
    /* Attempt to read all files not in FixtureMap */
371
    QStringList definitionPaths;
372

373
    // Gather a list of manufacturers
374
    QListIterator <QLCFixtureDef*> mfit(m_defs);
375
    while (mfit.hasNext() == true)
376
        definitionPaths << mfit.next()->definitionSourceFile();
377

378
    QStringListIterator it(dir.entryList());
379
    while (it.hasNext() == true)
380
    {
381
        QString path(dir.absoluteFilePath(it.next()));
382
        if (definitionPaths.contains(path))
383
            continue;
384

385
        qWarning() << path << "not in" << FIXTURES_MAP_NAME;
386

387
        if (path.toLower().endsWith(KExtFixture) == true)
388
            loadQXF(path);
389
        else if (path.toLower().endsWith(KExtAvolitesFixture) == true)
390
            loadD4(path);
391
        else
392
            qWarning() << Q_FUNC_INFO << "Unrecognized fixture extension:" << path;
393
    }
394
#endif
395
    return true;
48✔
396
}
48✔
397

398
void QLCFixtureDefCache::clear()
209✔
399
{
400
    while (m_defs.isEmpty() == false)
82,489✔
401
        delete m_defs.takeFirst();
82,280✔
402
}
209✔
403

404
QDir QLCFixtureDefCache::systemDefinitionDirectory()
1✔
405
{
406
    return QLCFile::systemDirectory(QString(FIXTUREDIR), QString(KExtFixture));
2✔
407
}
408

409
QDir QLCFixtureDefCache::userDefinitionDirectory()
4✔
410
{
411
    QStringList filters;
4✔
412
    filters << QString("*%1").arg(KExtFixture);
8✔
413
    filters << QString("*%1").arg(KExtAvolitesFixture);
8✔
414

415
    return QLCFile::userDirectory(QString(USERFIXTUREDIR), QString(FIXTUREDIR), filters);
12✔
416
}
4✔
417

418
bool QLCFixtureDefCache::loadQXF(const QString& path, bool isUser)
10✔
419
{
420
    QLCFixtureDef *fxi = new QLCFixtureDef();
10✔
421
    Q_ASSERT(fxi != NULL);
10✔
422

423
    QFile::FileError error = fxi->loadXML(path);
10✔
424
    if (error == QFile::NoError)
10✔
425
    {
426
        fxi->setIsUser(isUser);
2✔
427
        fxi->setDefinitionSourceFile(path);
2✔
428
        fxi->setLoaded(true);
2✔
429

430
        /* Delete the def if it's a duplicate. */
431
        if (addFixtureDef(fxi) == false)
2✔
432
            delete fxi;
2✔
433
        fxi = NULL;
2✔
434
    }
435
    else
436
    {
437
        qWarning() << Q_FUNC_INFO << "Fixture definition loading from"
8✔
438
                   << path << "failed:" << QLCFile::errorString(error);
8✔
439
        delete fxi;
8✔
440
        fxi = NULL;
8✔
441
        return false;
8✔
442
    }
443
    return true;
2✔
444
}
445

446
bool QLCFixtureDefCache::loadD4(const QString& path)
1✔
447
{
448
    QLCFixtureDef *fxi = new QLCFixtureDef();
1✔
449
    AvolitesD4Parser parser;
1✔
450
    if (parser.loadXML(path, fxi) == false)
1✔
451
    {
452
        qWarning() << Q_FUNC_INFO << "Unable to load D4 fixture from" << path
1✔
453
                   << ":" << parser.lastError();
1✔
454
        delete fxi;
1✔
455
        return false;
1✔
456
    }
457

458
    // a D4 personality is always a user-made fixture
459
    fxi->setIsUser(true);
×
460
    fxi->setDefinitionSourceFile(path);
×
461
    fxi->setLoaded(true);
×
462

463
    /* Delete the def if it's a duplicate. */
464
    if (addFixtureDef(fxi) == false)
×
465
    {
466
        qDebug() << Q_FUNC_INFO << "Deleting duplicate" << path;
×
467
        delete fxi;
×
468
    }
469
    fxi = NULL;
×
470

471
    return true;
×
472
}
1✔
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