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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

83.19
/engine/src/qlccapability.cpp
1
/*
2
  Q Light Controller Plus
3
  qlccapability.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 <QCoreApplication>
22
#include <QXmlStreamReader>
23
#include <QMetaEnum>
24
#include <QString>
25
#include <QDebug>
26
#include <QFile>
27

28
#include "qlccapability.h"
29
#include "qlcmacros.h"
30
#include "qlcconfig.h"
31
#include "qlcfile.h"
32

33
/************************************************************************
34
 * Initialization
35
 ************************************************************************/
36

37
QLCCapability::QLCCapability(uchar min, uchar max, const QString& name, QObject *parent)
4,336✔
38
    : QObject(parent)
39
    , m_preset(Custom)
4,336✔
40
    , m_min(min)
4,336✔
41
    , m_max(max)
4,336✔
42
    , m_name(name)
43
    , m_warning(NoWarning)
4,336✔
44
{
45
}
4,336✔
46

47
QLCCapability *QLCCapability::createCopy()
9✔
48
{
49
    QLCCapability *copy = new QLCCapability(m_min, m_max, m_name);
9✔
50
    copy->setWarning(m_warning);
9✔
51
    copy->setPreset(preset());
9✔
52
    for (int i = 0; i < m_resources.count(); i++)
9✔
53
        copy->setResource(i, m_resources.at(i));
×
54
    foreach (AliasInfo alias, m_aliases)
9✔
55
        copy->addAlias(alias);
×
56

57
    return copy;
9✔
58
}
59

60
QLCCapability::~QLCCapability()
6,752✔
61
{
62
}
6,752✔
63

64
bool QLCCapability::operator<(const QLCCapability& capability) const
26✔
65
{
66
    if (m_min < capability.m_min)
26✔
67
        return true;
68
    else
69
        return false;
12✔
70
}
71

72
QString QLCCapability::presetToString(QLCCapability::Preset preset)
1✔
73
{
74
    int index = staticMetaObject.indexOfEnumerator("Preset");
1✔
75
    return staticMetaObject.enumerator(index).valueToKey(preset);
1✔
76
}
77

78
QLCCapability::Preset QLCCapability::stringToPreset(const QString &preset)
764✔
79
{
80
    int index = staticMetaObject.indexOfEnumerator("Preset");
764✔
81
    return Preset(staticMetaObject.enumerator(index).keyToValue(preset.toStdString().c_str()));
764✔
82
}
83

84
QLCCapability::Preset QLCCapability::preset() const
11,159✔
85
{
86
    return m_preset;
11,159✔
87
}
88

89
void QLCCapability::setPreset(QLCCapability::Preset preset)
844✔
90
{
91
    if (preset == m_preset)
844✔
92
        return;
93

94
    m_preset = preset;
835✔
95
}
96

97
/* please see
98
https://github.com/mcallegari/qlcplus/wiki/Fixture-definition-presets
99
when changing this function */
100
QLCCapability::PresetType QLCCapability::presetType() const
2,575✔
101
{
102
    switch (m_preset)
2,575✔
103
    {
104
        case StrobeFrequency:
105
        case PulseFrequency:
106
        case RampUpFrequency:
107
        case RampDownFrequency:
108
        case PrismEffectOn:
109
            return SingleValue;
110
        case StrobeFreqRange:
111
        case PulseFreqRange:
112
        case RampUpFreqRange:
113
        case RampDownFreqRange:
114
            return DoubleValue;
115
        case ColorMacro:
116
            return SingleColor;
117
        case ColorDoubleMacro:
118
            return DoubleColor;
119
        case GoboMacro:
120
        case GoboShakeMacro:
121
        case GenericPicture:
122
            return Picture;
123
        default: return None;
124
    }
125
}
126

127
/* please see
128
https://github.com/mcallegari/qlcplus/wiki/Fixture-definition-presets
129
when changing this function */
130
QString QLCCapability::presetUnits() const
×
131
{
132
    switch (m_preset)
×
133
    {
134
        case StrobeFrequency:
×
135
        case PulseFrequency:
136
        case RampUpFrequency:
137
        case RampDownFrequency:
138
        case StrobeFreqRange:
139
        case PulseFreqRange:
140
        case RampUpFreqRange:
141
        case RampDownFreqRange:
142
            return "Hz";
×
143
        break;
144
        case PrismEffectOn:
×
145
            return "Faces";
×
146
        break;
147
        default:
148
        break;
149
    }
150
    return QString();
151
}
152

153
/************************************************************************
154
 * Properties
155
 ************************************************************************/
156

157
uchar QLCCapability::min() const
88,619✔
158
{
159
    return m_min;
88,619✔
160
}
161

162
void QLCCapability::setMin(uchar value)
2,839✔
163
{
164
    if (m_min == value)
2,839✔
165
        return;
166

167
    m_min = value;
2,575✔
168
    emit minChanged();
2,575✔
169
}
170

171
uchar QLCCapability::max() const
3,608✔
172
{
173
    return m_max;
3,608✔
174
}
175

176
void QLCCapability::setMax(uchar value)
2,839✔
177
{
178
    if (m_max == value)
2,839✔
179
        return;
180

181
    m_max = value;
2,578✔
182
    emit maxChanged();
2,578✔
183
}
184

185
uchar QLCCapability::middle() const
41✔
186
{
187
    return int((m_max + m_min) / 2);
41✔
188
}
189

190
QString QLCCapability::name() const
2,648✔
191
{
192
    return m_name;
2,648✔
193
}
194

195
void QLCCapability::setName(const QString& name)
3,066✔
196
{
197
    if (m_name == name)
3,066✔
198
        return;
199

200
    m_name = name;
3,066✔
201
    emit nameChanged();
3,066✔
202
}
203

204
QLCCapability::WarningType QLCCapability::warning() const
×
205
{
206
    return m_warning;
×
207
}
208

209
void QLCCapability::setWarning(QLCCapability::WarningType type)
9✔
210
{
211
    if (m_warning == type)
9✔
212
        return;
213

214
    m_warning = type;
×
215
    emit warningChanged();
×
216
}
217

218
QVariant QLCCapability::resource(int index)
1,027✔
219
{
220
    if (index < 0 || index >= m_resources.count())
1,027✔
221
        return QVariant();
222

223
    return m_resources.at(index);
3✔
224
}
225

226
void QLCCapability::setResource(int index, QVariant value)
730✔
227
{
228
    if (index < 0)
730✔
229
        return;
230
    else if (index < m_resources.count())
730✔
231
        m_resources[index] = value;
×
232
    else
233
        m_resources.append(value);
730✔
234
}
235

236
QVariantList QLCCapability::resources()
×
237
{
238
    return m_resources;
×
239
}
240

241
bool QLCCapability::overlaps(const QLCCapability *cap)
21,565✔
242
{
243
    if (m_min >= cap->min() && m_min <= cap->max())
21,565✔
244
        return true;
245
    else if (m_max >= cap->min() && m_max <= cap->max())
21,554✔
246
        return true;
247
    else if (m_min <= cap->min() && m_max >= cap->min())
21,548✔
248
        return true;
249
    else
250
        return false;
21,545✔
251
}
252

253
/********************************************************************
254
 * Aliases
255
 ********************************************************************/
256

257
QList<AliasInfo> QLCCapability::aliasList()
11✔
258
{
259
    return m_aliases;
11✔
260
}
261

262
void QLCCapability::addAlias(AliasInfo alias)
9✔
263
{
264
    m_aliases.append(alias);
9✔
265
}
9✔
266

267
void QLCCapability::removeAlias(AliasInfo alias)
2✔
268
{
269
    for (int i = 0; i < m_aliases.count(); i++)
2✔
270
    {
271
        AliasInfo info = m_aliases.at(i);
2✔
272

273
        if (alias.targetMode == info.targetMode &&
4✔
274
            alias.sourceChannel == info.sourceChannel &&
4✔
275
            alias.targetChannel == info.targetChannel)
2✔
276
        {
277
            m_aliases.takeAt(i);
2✔
278
            return;
279
        }
280
    }
2✔
281
}
282

283
void QLCCapability::replaceAliases(QList<AliasInfo> list)
1✔
284
{
285
    m_aliases.clear();
1✔
286
    foreach (AliasInfo info, list)
3✔
287
        m_aliases.append(info);
2✔
288
}
1✔
289

290
/************************************************************************
291
 * Save & Load
292
 ************************************************************************/
293

294
bool QLCCapability::saveXML(QXmlStreamWriter *doc)
7✔
295
{
296
    Q_ASSERT(doc != NULL);
297

298
    /* QLCCapability entry */
299
    doc->writeStartElement(KXMLQLCCapability);
7✔
300

301
    /* Min limit attribute */
302
    doc->writeAttribute(KXMLQLCCapabilityMin, QString::number(m_min));
7✔
303

304
    /* Max limit attribute */
305
    doc->writeAttribute(KXMLQLCCapabilityMax, QString::number(m_max));
7✔
306

307
    /* Preset attribute if not custom */
308
    if (m_preset != Custom)
7✔
309
        doc->writeAttribute(KXMLQLCCapabilityPreset, presetToString(m_preset));
1✔
310

311
    /* Resource attributes */
312
    for (int i = 0; i < m_resources.count(); i++)
9✔
313
    {
314
        switch (presetType())
2✔
315
        {
316
            case Picture:
×
317
            {
318
                QString modFilename = resource(i).toString();
×
319
                QDir dir = QDir::cleanPath(QLCFile::systemDirectory(GOBODIR).path());
×
320

321
                if (modFilename.contains(dir.path()))
×
322
                {
323
                    modFilename.remove(dir.path());
×
324
                    // The following line is a dirty workaround for an issue raised on Windows
325
                    // When building with MinGW, dir.path() is something like "C:/QLC+/Gobos"
326
                    // while QDir::separator() returns "\"
327
                    // So, to avoid any string mismatch I remove the first character
328
                    // no matter what it is
329
                    modFilename.remove(0, 1);
×
330
                }
331

332
                doc->writeAttribute(KXMLQLCCapabilityRes1, modFilename);
×
333
            }
×
334
            break;
×
335
            case SingleColor:
×
336
            case DoubleColor:
337
            {
338
                QColor col = resource(i).value<QColor>();
×
339
                if (i == 0 && col.isValid())
×
340
                    doc->writeAttribute(KXMLQLCCapabilityRes1, col.name());
×
341
                else if (i == 1 && col.isValid())
×
342
                    doc->writeAttribute(KXMLQLCCapabilityRes2, col.name());
×
343
            }
344
            break;
×
345
            case SingleValue:
2✔
346
            case DoubleValue:
347
            {
348
                if (i == 0)
2✔
349
                    doc->writeAttribute(KXMLQLCCapabilityRes1, QString::number(resource(i).toFloat()));
1✔
350
                else if (i == 1)
1✔
351
                    doc->writeAttribute(KXMLQLCCapabilityRes2, QString::number(resource(i).toFloat()));
1✔
352
            }
353
            break;
354
            default:
355
            break;
356
        }
357
    }
358

359
    /* Name */
360
    if (m_aliases.isEmpty())
7✔
361
        doc->writeCharacters(m_name);
6✔
362
    else
363
        doc->writeCharacters(QString("%1\n   ").arg(m_name)); // to preserve indentation
1✔
364

365
    /* Aliases */
366
    foreach (AliasInfo info, m_aliases)
8✔
367
    {
368
        doc->writeStartElement(KXMLQLCCapabilityAlias);
1✔
369
        doc->writeAttribute(KXMLQLCCapabilityAliasMode, info.targetMode);
1✔
370
        doc->writeAttribute(KXMLQLCCapabilityAliasSourceName, info.sourceChannel);
1✔
371
        doc->writeAttribute(KXMLQLCCapabilityAliasTargetName, info.targetChannel);
1✔
372
        doc->writeEndElement();
1✔
373
    }
1✔
374

375
    doc->writeEndElement();
7✔
376

377
    return true;
7✔
378
}
379

380
bool QLCCapability::loadXML(QXmlStreamReader &doc)
2,576✔
381
{
382
    uchar min = 0;
383
    uchar max = 0;
384
    QString str;
385

386
    if (doc.name() != KXMLQLCCapability)
5,152✔
387
    {
388
        qWarning() << Q_FUNC_INFO << "Capability node not found";
1✔
389
        return false;
1✔
390
    }
391

392
    /* Get low limit attribute (mandatory) */
393
    QXmlStreamAttributes attrs = doc.attributes();
2,575✔
394
    str = attrs.value(KXMLQLCCapabilityMin).toString();
2,575✔
395
    if (str.isEmpty() == true)
2,575✔
396
    {
397
        qWarning() << Q_FUNC_INFO << "Capability has no minimum limit.";
2✔
398
        return false;
2✔
399
    }
400
    else
401
    {
402
        min = CLAMP(str.toInt(), 0, (int)UCHAR_MAX);
2,573✔
403
    }
404

405
    /* Get high limit attribute (mandatory) */
406
    str = attrs.value(KXMLQLCCapabilityMax).toString();
2,573✔
407
    if (str.isEmpty() == true)
2,573✔
408
    {
409
        qWarning() << Q_FUNC_INFO << "Capability has no maximum limit.";
1✔
410
        return false;
1✔
411
    }
412
    else
413
    {
414
        max = CLAMP(str.toInt(), 0, (int)UCHAR_MAX);
2,572✔
415
    }
416

417
    if (attrs.hasAttribute(KXMLQLCCapabilityPreset))
2,572✔
418
    {
419
        str = attrs.value(KXMLQLCCapabilityPreset).toString();
764✔
420
        setPreset(stringToPreset(str));
764✔
421
    }
422

423
    switch(presetType())
2,572✔
424
    {
425
        case Picture:
81✔
426
        {
427
            QString path = attrs.value(KXMLQLCCapabilityRes1).toString();
81✔
428
            if (QFileInfo(path).isRelative())
81✔
429
            {
430
                QDir dir = QLCFile::systemDirectory(GOBODIR);
162✔
431
                path = dir.path() + QDir::separator() + path;
81✔
432
            }
81✔
433
            setResource(0, path);
81✔
434
        }
81✔
435
        break;
81✔
436
        case SingleColor:
299✔
437
        case DoubleColor:
438
        {
439
            QColor col1 = QColor(attrs.value(KXMLQLCCapabilityRes1).toString());
598✔
440
            QColor col2 = QColor();
299✔
441
            if (attrs.hasAttribute(KXMLQLCCapabilityRes2))
299✔
442
                col2 = QColor(attrs.value(KXMLQLCCapabilityRes2).toString());
270✔
443

444
            if (col1.isValid())
299✔
445
            {
446
                setResource(0, col1);
299✔
447
                if (col2.isValid())
299✔
448
                    setResource(1, col2);
135✔
449
            }
450
        }
451
        break;
299✔
452
        case SingleValue:
141✔
453
        case DoubleValue:
454
        {
455
            float value = attrs.value(KXMLQLCCapabilityRes1).toString().toFloat();
141✔
456
            setResource(0, value);
141✔
457

458
            if (attrs.hasAttribute(KXMLQLCCapabilityRes2))
141✔
459
            {
460
                value = attrs.value(KXMLQLCCapabilityRes2).toString().toFloat();
2✔
461
                setResource(1, value);
2✔
462
            }
463
        }
464
        break;
465
        default:
466
        break;
467
    }
468

469
    /* ************************* LEGACY ATTRIBUTES ************************* */
470

471
    /* Get (optional) resource name for gobo/effect/... */
472
    if (attrs.hasAttribute(KXMLQLCCapabilityResource))
2,572✔
473
    {
474
        QString path = attrs.value(KXMLQLCCapabilityResource).toString();
10✔
475
        if (QFileInfo(path).isRelative())
10✔
476
        {
477
            QDir dir = QLCFile::systemDirectory(GOBODIR);
20✔
478
            path = dir.path() + QDir::separator() + path;
10✔
479
            setPreset(GoboMacro);
10✔
480
        }
10✔
481
        else
482
            setPreset(GenericPicture);
×
483
        setResource(0, path);
10✔
484
    }
10✔
485

486
    /* Get (optional) color resource for color presets */
487
    if (attrs.hasAttribute(KXMLQLCCapabilityColor1))
2,572✔
488
    {
489
        QColor col1 = QColor(attrs.value(KXMLQLCCapabilityColor1).toString());
120✔
490
        QColor col2 = QColor();
60✔
491
        if (attrs.hasAttribute(KXMLQLCCapabilityColor2))
60✔
492
            col2 = QColor(attrs.value(KXMLQLCCapabilityColor2).toString());
×
493

494
        if (col1.isValid())
60✔
495
        {
496
            setResource(0, col1);
60✔
497

498
            if (col2.isValid())
60✔
499
            {
500
                setResource(1, col2);
×
501
                setPreset(ColorDoubleMacro);
×
502
            }
503
            else
504
            {
505
                setPreset(ColorMacro);
60✔
506
            }
507
        }
508
    }
509

510
    if (min <= max)
2,572✔
511
    {
512
        doc.readNext();
2,571✔
513
        setName(doc.text().toString().simplified());
5,142✔
514
        setMin(min);
2,571✔
515
        setMax(max);
2,571✔
516
        if (name().isEmpty())
2,571✔
517
        {
518
            qWarning() << "Empty description provided. This should be fixed in the definition!";
×
519
            return true;
×
520
        }
521
    }
522
    else
523
    {
524
        qWarning() << Q_FUNC_INFO << "Capability min(" << min
2✔
525
                   << ") is greater than max(" << max << ")";
1✔
526
        return false;
1✔
527
    }
528

529
    /* Subtags */
530
    while (doc.readNextStartElement())
2,576✔
531
    {
532
        if (doc.name() == KXMLQLCCapabilityAlias)
10✔
533
        {
534
            AliasInfo alias;
535
            QXmlStreamAttributes attrs = doc.attributes();
5✔
536

537
            alias.targetMode = attrs.value(KXMLQLCCapabilityAliasMode).toString();
5✔
538
            alias.sourceChannel = attrs.value(KXMLQLCCapabilityAliasSourceName).toString();
5✔
539
            alias.targetChannel = attrs.value(KXMLQLCCapabilityAliasTargetName).toString();
5✔
540
            addAlias(alias);
5✔
541

542
            //qDebug() << "Alias found for mode" << alias.targetMode;
543
        }
5✔
544
        else
545
        {
546
            qWarning() << Q_FUNC_INFO << "Unknown capability tag: " << doc.name();
×
547
        }
548
        doc.skipCurrentElement();
5✔
549
    }
550

551
    return true;
552
}
2,576✔
553

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

© 2025 Coveralls, Inc