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

mcallegari / qlcplus / 21012146380

14 Jan 2026 10:28PM UTC coverage: 34.177% (+0.005%) from 34.172%
21012146380

Pull #1931

github

web-flow
Merge a8fdb89dd into 99cd794ab
Pull Request #1931: Fixture path documentation fixed and dir configurable

6 of 6 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

17724 of 51860 relevant lines covered (34.18%)

19816.18 hits per line

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

80.53
/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()
198✔
42
{
43
}
198✔
44

45
QLCFixtureDefCache::~QLCFixtureDefCache()
198✔
46
{
47
    clear();
198✔
48
}
198✔
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)
171,547✔
55
    {
56
        QLCFixtureDef* def = it.next();
171,529✔
57
        if (def->manufacturer() == manufacturer && def->model() == model)
171,529✔
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,081✔
74
        makers << it.next()->manufacturer();
34,043✔
75

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

81
    return list;
76✔
82
}
38✔
83

84
QStringList QLCFixtureDefCache::models(const QString& manufacturer) const
83,039✔
85
{
86
    QSet <QString> models;
83,039✔
87
    QListIterator <QLCFixtureDef*> it(m_defs);
83,039✔
88
    while (it.hasNext() == true)
71,819,669✔
89
    {
90
        QLCFixtureDef* def = it.next();
71,736,630✔
91
        if (def->manufacturer() == manufacturer)
71,736,630✔
92
            models << def->model();
2,759,381✔
93
    }
94

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

100
    return list;
166,078✔
101
}
83,039✔
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,402✔
109
    {
110
        QLCFixtureDef *def = it.next();
3,400✔
111
        map[def->manufacturer()][def->model()] = def->isUser();
3,400✔
112
    }
113

114
    return map;
4✔
115
}
2✔
116

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

122
    if (models(fixtureDef->manufacturer()).contains(fixtureDef->model()) == false)
81,611✔
123
    {
124
        m_defs << fixtureDef;
81,608✔
125
        return true;
81,608✔
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(QString filename, 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 upon bundled definitions
175
    QListIterator <QLCFixtureDef*> it(m_defs);
×
176
    while (it.hasNext() == true)
×
177
    {
178
        QLCFixtureDef *def = it.next();
×
179
        if (def->manufacturer() == fixtureDef->manufacturer() &&
×
180
            def->model() == fixtureDef->model())
×
181
        {
182
            // set as user and perform a deep copy
183
            def->setIsUser(true);
×
184
            *def = *fixtureDef;
×
185
            return true;
×
186
        }
187
    }
188

189
    // add a new user fixture
190
    addFixtureDef(fixtureDef);
×
191

192
    return true;
×
193
}
×
194

195
bool QLCFixtureDefCache::load(const QDir& dir)
9✔
196
{
197
    qDebug() << Q_FUNC_INFO << dir.path();
9✔
198

199
    if (dir.exists() == false || dir.isReadable() == false)
9✔
200
        return false;
7✔
201

202
    /* Attempt to read all specified files from the given directory */
203
    QStringListIterator it(dir.entryList());
2✔
204
    while (it.hasNext() == true)
3✔
205
    {
206
        QString path(dir.absoluteFilePath(it.next()));
1✔
207

208
        if (path.toLower().endsWith(KExtFixture) == true)
2✔
209
            loadQXF(path, true);
1✔
210
        else if (path.toLower().endsWith(KExtAvolitesFixture) == true)
×
211
            loadD4(path);
×
212
        else
213
            qWarning() << Q_FUNC_INFO << "Unrecognized fixture extension:" << path;
×
214
    }
1✔
215

216
    return true;
2✔
217
}
2✔
218

219
int QLCFixtureDefCache::loadMapManufacturer(QXmlStreamReader *doc, QString manufacturer)
6,816✔
220
{
221
    int count = 0;
6,816✔
222
    QString spacedManufacturer = manufacturer;
6,816✔
223
    spacedManufacturer.replace("_", " ");
6,816✔
224

225
    while (doc->readNextStartElement())
88,416✔
226
    {
227
        if (doc->name() == QString("F"))
81,600✔
228
        {
229
            QString defFile = "";
81,600✔
230
            QString model = "";
81,600✔
231

232
            if (doc->attributes().hasAttribute("n"))
163,200✔
233
            {
234
                defFile = QString("%1%2%3%4")
163,200✔
235
                            .arg(manufacturer).arg(QDir::separator())
163,200✔
236
                            .arg(doc->attributes().value("n").toString()).arg(KExtFixture);
244,800✔
237
                //qDebug() << "Manufacturer" << spacedManufacturer << "file" << defFile;
238
            }
239

240
            if (doc->attributes().hasAttribute("m"))
163,200✔
241
                model = doc->attributes().value("m").toString();
163,200✔
242

243
            if (defFile.isEmpty() == false &&
81,600✔
244
                spacedManufacturer.isEmpty() == false &&
163,200✔
245
                model.isEmpty() == false)
81,600✔
246
            {
247
                QLCFixtureDef *fxi = new QLCFixtureDef();
81,600✔
248
                Q_ASSERT(fxi != NULL);
81,600✔
249

250
                fxi->setDefinitionSourceFile(defFile);
81,600✔
251
                fxi->setManufacturer(spacedManufacturer);
81,600✔
252
                fxi->setModel(model);
81,600✔
253

254
                /* Delete the def if it's a duplicate. */
255
                if (addFixtureDef(fxi) == false)
81,600✔
256
                    delete fxi;
×
257
                fxi = NULL;
81,600✔
258
                count++;
81,600✔
259
            }
260
        }
81,600✔
261
        else
262
        {
263
            qWarning() << Q_FUNC_INFO << "Unknown manufacturer tag: " << doc->name();
×
264
        }
265
        doc->skipCurrentElement();
81,600✔
266
    }
267

268
    return count;
6,816✔
269
}
6,816✔
270

271
bool QLCFixtureDefCache::loadMap(const QDir &dir)
48✔
272
{
273
    qDebug() << Q_FUNC_INFO << dir.path();
48✔
274

275
    if (dir.exists() == false || dir.isReadable() == false)
48✔
276
        return false;
×
277

278
    QString mapPath(dir.absoluteFilePath(FIXTURES_MAP_NAME));
96✔
279

280
    if (mapPath.isEmpty() == true)
48✔
281
        return false;
×
282

283
    // cache the map path to be used when composing the fixture
284
    // definition absolute path
285
    m_mapAbsolutePath = dir.absolutePath();
48✔
286

287
    QXmlStreamReader *doc = QLCFile::getXMLReader(mapPath);
48✔
288
    if (doc == NULL || doc->device() == NULL || doc->hasError())
48✔
289
    {
290
        qWarning() << Q_FUNC_INFO << "Unable to read from" << mapPath;
×
291
        return false;
×
292
    }
293

294
    while (!doc->atEnd())
96✔
295
    {
296
        if (doc->readNext() == QXmlStreamReader::DTD)
96✔
297
            break;
48✔
298
    }
299

300
    if (doc->hasError())
48✔
301
    {
302
        QLCFile::releaseXMLReader(doc);
×
303
        return false;
×
304
    }
305

306
    // make sure the doc type is FixtureMap
307
    if (doc->dtdName() != KXMLQLCFixtureMap)
48✔
308
    {
309
        qWarning() << Q_FUNC_INFO << mapPath << "is not a fixture map file";
×
310
        QLCFile::releaseXMLReader(doc);
×
311
        return false;
×
312
    }
313

314
    if (doc->readNextStartElement() == false)
48✔
315
    {
316
        QLCFile::releaseXMLReader(doc);
×
317
        return false;
×
318
    }
319

320
    // make sure the root tag is FixtureMap
321
    if (doc->name() != KXMLQLCFixtureMap)
48✔
322
    {
323
        qWarning() << Q_FUNC_INFO << mapPath << "is not a fixture map file";
×
324
        QLCFile::releaseXMLReader(doc);
×
325
        return false;
×
326
    }
327

328
    int fxCount = 0;
48✔
329
    QString manufacturer = "";
48✔
330

331
    while (doc->readNextStartElement())
6,864✔
332
    {
333
        if (doc->name() == QString("M"))
6,816✔
334
        {
335
            if (doc->attributes().hasAttribute("n"))
13,632✔
336
            {
337
                manufacturer = doc->attributes().value("n").toString();
13,632✔
338
                fxCount += loadMapManufacturer(doc, manufacturer);
6,816✔
339
            }
340
        }
341
        else
342
        {
343
            qWarning() << Q_FUNC_INFO << "Unknown Fixture Map tag: " << doc->name();
×
344
            doc->skipCurrentElement();
×
345
        }
346
    }
347
    qDebug() << fxCount << "fixtures found in map";
48✔
348

349
#if 0
350
    /* Attempt to read all files not in FixtureMap */
351
    QStringList definitionPaths;
352

353
    // Gather a list of manufacturers
354
    QListIterator <QLCFixtureDef*> mfit(m_defs);
355
    while (mfit.hasNext() == true)
356
        definitionPaths << mfit.next()->definitionSourceFile();
357

358
    QStringListIterator it(dir.entryList());
359
    while (it.hasNext() == true)
360
    {
361
        QString path(dir.absoluteFilePath(it.next()));
362
        if (definitionPaths.contains(path))
363
            continue;
364

365
        qWarning() << path << "not in" << FIXTURES_MAP_NAME;
366

367
        if (path.toLower().endsWith(KExtFixture) == true)
368
            loadQXF(path);
369
        else if (path.toLower().endsWith(KExtAvolitesFixture) == true)
370
            loadD4(path);
371
        else
372
            qWarning() << Q_FUNC_INFO << "Unrecognized fixture extension:" << path;
373
    }
374
#endif
375
    return true;
48✔
376
}
48✔
377

378
void QLCFixtureDefCache::clear()
207✔
379
{
380
    while (m_defs.isEmpty() == false)
81,815✔
381
        delete m_defs.takeFirst();
81,608✔
382
}
207✔
383

384
static QString FixtureDir()
5✔
385
{
386
    QString fixture_dir(getenv("QLC_FIXTURES_PATH"));
5✔
387
    if (fixture_dir.isEmpty()) fixture_dir = QString(FIXTUREDIR);
5✔
388
    return fixture_dir;
5✔
UNCOV
389
}
×
390

391
QDir QLCFixtureDefCache::systemDefinitionDirectory()
1✔
392
{
393
    return QLCFile::systemDirectory(FixtureDir(), QString(KExtFixture));
2✔
394
}
395

396
QDir QLCFixtureDefCache::userDefinitionDirectory()
4✔
397
{
398
    QStringList filters;
4✔
399
    filters << QString("*%1").arg(KExtFixture);
8✔
400
    filters << QString("*%1").arg(KExtAvolitesFixture);
8✔
401

402
    return QLCFile::userDirectory(QString(USERFIXTUREDIR), FixtureDir(), filters);
12✔
403
}
4✔
404

405
bool QLCFixtureDefCache::loadQXF(const QString& path, bool isUser)
10✔
406
{
407
    QLCFixtureDef *fxi = new QLCFixtureDef();
10✔
408
    Q_ASSERT(fxi != NULL);
10✔
409

410
    QFile::FileError error = fxi->loadXML(path);
10✔
411
    if (error == QFile::NoError)
10✔
412
    {
413
        fxi->setIsUser(isUser);
2✔
414
        fxi->setDefinitionSourceFile(path);
2✔
415
        fxi->setLoaded(true);
2✔
416

417
        /* Delete the def if it's a duplicate. */
418
        if (addFixtureDef(fxi) == false)
2✔
419
            delete fxi;
2✔
420
        fxi = NULL;
2✔
421
    }
422
    else
423
    {
424
        qWarning() << Q_FUNC_INFO << "Fixture definition loading from"
8✔
425
                   << path << "failed:" << QLCFile::errorString(error);
8✔
426
        delete fxi;
8✔
427
        fxi = NULL;
8✔
428
        return false;
8✔
429
    }
430
    return true;
2✔
431
}
432

433
bool QLCFixtureDefCache::loadD4(const QString& path)
1✔
434
{
435
    QLCFixtureDef *fxi = new QLCFixtureDef();
1✔
436
    AvolitesD4Parser parser;
1✔
437
    if (parser.loadXML(path, fxi) == false)
1✔
438
    {
439
        qWarning() << Q_FUNC_INFO << "Unable to load D4 fixture from" << path
1✔
440
                   << ":" << parser.lastError();
1✔
441
        delete fxi;
1✔
442
        return false;
1✔
443
    }
444

445
    // a D4 personality is always a user-made fixture
446
    fxi->setIsUser(true);
×
447
    fxi->setDefinitionSourceFile(path);
×
448
    fxi->setLoaded(true);
×
449

450
    /* Delete the def if it's a duplicate. */
451
    if (addFixtureDef(fxi) == false)
×
452
    {
453
        qDebug() << Q_FUNC_INFO << "Deleting duplicate" << path;
×
454
        delete fxi;
×
455
    }
456
    fxi = NULL;
×
457

458
    return true;
×
459
}
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