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

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

72.0
/engine/src/qlcinputprofile.cpp
1
/*
2
  Q Light Controller
3
  qlcinputprofile.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 <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <unistd.h>
23
#include <QString>
24
#include <QDebug>
25
#include <QMap>
26

27
#include "qlcinputchannel.h"
28
#include "qlcinputprofile.h"
29
#include "qlcchannel.h"
30
#include "qlcfile.h"
31

32
#define KXMLQLCInputProfileTypeMidi QStringLiteral("MIDI")
33
#define KXMLQLCInputProfileTypeOs2l QStringLiteral("OS2L")
34
#define KXMLQLCInputProfileTypeOsc QStringLiteral("OSC")
35
#define KXMLQLCInputProfileTypeHid QStringLiteral("HID")
36
#define KXMLQLCInputProfileTypeDmx QStringLiteral("DMX")
37
#define KXMLQLCInputProfileTypeEnttec QStringLiteral("Enttec")
38

39
#define KXMLQLCInputProfileValue QStringLiteral("Value")
40
#define KXMLQLCInputProfileLabel QStringLiteral("Label")
41
#define KXMLQLCInputProfileColorRGB QStringLiteral("RGB")
42

43
/****************************************************************************
44
 * Initialization
45
 ****************************************************************************/
46

47
QLCInputProfile::QLCInputProfile()
139✔
48
    : m_manufacturer(QString())
139✔
49
    , m_model(QString())
139✔
50
    , m_path(QString())
139✔
51
    , m_type(MIDI)
139✔
52
    , m_midiSendNoteOff(true)
278✔
53
{
54
}
139✔
55

56
QLCInputProfile::~QLCInputProfile()
257✔
57
{
58
    destroyChannels();
137✔
59
}
257✔
60

61
QLCInputProfile *QLCInputProfile::createCopy()
1✔
62
{
63
    QLCInputProfile *copy = new QLCInputProfile();
1✔
64
    copy->setManufacturer(this->manufacturer());
1✔
65
    copy->setModel(this->model());
1✔
66
    copy->setType(this->type());
1✔
67
    copy->setPath(this->path());
1✔
68
    copy->setMidiSendNoteOff(this->midiSendNoteOff());
1✔
69

70
    /* Copy the other profile's channels */
71
    QMapIterator <quint32,QLCInputChannel*> it(this->channels());
1✔
72
    while (it.hasNext() == true)
5✔
73
    {
74
        it.next();
4✔
75
        copy->insertChannel(it.key(), it.value()->createCopy());
4✔
76
    }
77

78
    /* Copy the other profile's color table */
79
    QMapIterator <uchar, QPair<QString, QColor>> it2(this->colorTable());
1✔
80
    while (it2.hasNext() == true)
1✔
81
    {
82
        it2.next();
×
83
        QPair<QString, QColor> lc = it2.value();
×
84
        copy->addColor(it2.key(), lc.first, lc.second);
×
85
    }
×
86

87
    /* Copy the other profile's MIDI channel tabel */
88
    QMapIterator <uchar, QString> it3(this->midiChannelTable());
1✔
89
    while (it3.hasNext() == true)
1✔
90
    {
91
        it3.next();
×
92
        copy->addMidiChannel(it3.key(), it3.value());
×
93
    }
94

95
    return copy;
1✔
96
}
1✔
97

98
QLCInputProfile& QLCInputProfile::operator=(const QLCInputProfile& profile)
1✔
99
{
100
    if (this != &profile)
1✔
101
    {
102
        /* Copy basic properties */
103
        m_manufacturer = profile.m_manufacturer;
1✔
104
        m_model = profile.m_model;
1✔
105
        m_path = profile.m_path;
1✔
106
        m_type = profile.m_type;
1✔
107
        m_midiSendNoteOff = profile.m_midiSendNoteOff;
1✔
108
        m_globalSettingsMap = profile.m_globalSettingsMap;
1✔
109

110
        /* Destroy all existing channels */
111
        destroyChannels();
1✔
112

113
        /* Copy the other profile's channels */
114
        QMapIterator <quint32, QLCInputChannel*> it(profile.m_channels);
1✔
115
        while (it.hasNext() == true)
5✔
116
        {
117
            it.next();
4✔
118
            insertChannel(it.key(), it.value()->createCopy());
4✔
119
        }
120

121
        /* Copy the other profile's color table */
122
        QMapIterator <uchar, QPair<QString, QColor>> it2(profile.m_colorTable);
1✔
123
        while (it2.hasNext() == true)
1✔
124
        {
125
            it2.next();
×
126
            QPair<QString, QColor> lc = it2.value();
×
127
            addColor(it2.key(), lc.first, lc.second);
×
128
        }
×
129

130
        /* Copy the other profile's MIDI channel tabel */
131
        QMapIterator <uchar, QString> it3(profile.m_midiChannelTable);
1✔
132
        while (it3.hasNext() == true)
1✔
133
        {
134
            it3.next();
×
135
            addMidiChannel(it3.key(), it3.value());
×
136
        }
137
    }
1✔
138

139
    return *this;
1✔
140
}
141

142
/****************************************************************************
143
 * profile information
144
 ****************************************************************************/
145

146
void QLCInputProfile::setManufacturer(const QString& manufacturer)
132✔
147
{
148
    m_manufacturer = manufacturer;
132✔
149
}
132✔
150

151
QString QLCInputProfile::manufacturer() const
7✔
152
{
153
    return m_manufacturer;
7✔
154
}
155

156
void QLCInputProfile::setModel(const QString& model)
128✔
157
{
158
    m_model = model;
128✔
159
}
128✔
160

161
QString QLCInputProfile::model() const
7✔
162
{
163
    return m_model;
7✔
164
}
165

166
QString QLCInputProfile::name() const
2,550✔
167
{
168
    return QString("%1 %2").arg(m_manufacturer).arg(m_model);
2,550✔
169
}
170

171
void QLCInputProfile::setPath(QString path)
1✔
172
{
173
    m_path = path;
1✔
174
}
1✔
175

176
QString QLCInputProfile::path() const
2✔
177
{
178
    return m_path;
2✔
179
}
180

181
void QLCInputProfile::setType(QLCInputProfile::Type type)
120✔
182
{
183
    m_type = type;
120✔
184
}
120✔
185

186
QLCInputProfile::Type QLCInputProfile::type() const
1✔
187
{
188
    return m_type;
1✔
189
}
190

191
QString QLCInputProfile::typeToString(Type type)
1✔
192
{
193
    switch (type)
1✔
194
    {
195
    case MIDI:
1✔
196
        return KXMLQLCInputProfileTypeMidi;
1✔
197
    case OS2L:
×
198
        return KXMLQLCInputProfileTypeOs2l;
×
199
    case OSC:
×
200
        return KXMLQLCInputProfileTypeOsc;
×
201
    case HID:
×
202
        return KXMLQLCInputProfileTypeHid;
×
203
    case DMX:
×
204
        return KXMLQLCInputProfileTypeDmx;
×
205
    case Enttec:
×
206
        return KXMLQLCInputProfileTypeEnttec;
×
207
    default:
×
208
        return QString();
×
209
    }
210
}
211

212
QLCInputProfile::Type QLCInputProfile::stringToType(const QString& str)
119✔
213
{
214
    if (str == KXMLQLCInputProfileTypeMidi)
119✔
215
        return MIDI;
101✔
216
    else if (str == KXMLQLCInputProfileTypeOs2l)
18✔
217
        return OS2L;
×
218
    else if (str == KXMLQLCInputProfileTypeOsc)
18✔
219
        return OSC;
9✔
220
    else if (str == KXMLQLCInputProfileTypeHid)
9✔
221
        return HID;
3✔
222
    else if (str == KXMLQLCInputProfileTypeDmx)
6✔
223
        return DMX;
×
224
    else // if (str == KXMLQLCInputProfileTypeEnttec)
225
        return Enttec;
6✔
226
}
227

228
QList<QLCInputProfile::Type> QLCInputProfile::types()
×
229
{
230
    QList<Type> result;
×
231
    result
232
        << MIDI
×
233
        << OS2L
×
234
        << OSC
×
235
        << HID
×
236
        << DMX
×
237
        << Enttec;
×
238
    return result;
×
239
}
×
240

241
/********************************************************************
242
 * Plugin-specific global settings
243
 ********************************************************************/
244

245
void QLCInputProfile::setMidiSendNoteOff(bool enable)
22✔
246
{
247
    m_midiSendNoteOff = enable;
22✔
248
    m_globalSettingsMap["MIDISendNoteOff"] = QVariant(enable);
22✔
249
}
22✔
250

251
bool QLCInputProfile::midiSendNoteOff() const
2✔
252
{
253
    return m_midiSendNoteOff;
2✔
254
}
255

256
QMap<QString, QVariant> QLCInputProfile::globalSettings() const
5✔
257
{
258
    return m_globalSettingsMap;
5✔
259
}
260

261
/****************************************************************************
262
 * Channels
263
 ****************************************************************************/
264

265
bool QLCInputProfile::insertChannel(quint32 channel,
12,628✔
266
                                    QLCInputChannel* ich)
267
{
268
    if (ich != NULL && m_channels.contains(channel) == false)
12,628✔
269
    {
270
        m_channels.insert(channel, ich);
12,627✔
271
        return true;
12,627✔
272
    }
273
    else
274
    {
275
        return false;
1✔
276
    }
277
}
278

279
bool QLCInputProfile::removeChannel(quint32 channel)
6✔
280
{
281
    if (m_channels.contains(channel) == true)
6✔
282
    {
283
        QLCInputChannel* ich = m_channels.take(channel);
2✔
284
        Q_ASSERT(ich != NULL);
2✔
285
        delete ich;
2✔
286
        return true;
2✔
287
    }
288
    else
289
    {
290
        return false;
4✔
291
    }
292
}
293

294
bool QLCInputProfile::remapChannel(QLCInputChannel* ich, quint32 number)
3✔
295
{
296
    if (ich == NULL)
3✔
297
        return false;
1✔
298

299
    quint32 old = channelNumber(ich);
2✔
300
    if (old != QLCChannel::invalid() && m_channels.contains(number) == false)
2✔
301
    {
302
        m_channels.remove(old);
1✔
303
        insertChannel(number, ich);
1✔
304
        return true;
1✔
305
    }
306
    else
307
    {
308
        return false;
1✔
309
    }
310
}
311

312
QLCInputChannel* QLCInputProfile::channel(quint32 channel) const
75✔
313
{
314
    return m_channels.value(channel, NULL);
75✔
315
}
316

317
quint32 QLCInputProfile::channelNumber(const QLCInputChannel* channel) const
6✔
318
{
319
    if (channel == NULL)
6✔
320
        return QLCChannel::invalid();
1✔
321

322
    QMapIterator <quint32,QLCInputChannel*> it(m_channels);
5✔
323
    while (it.hasNext() == true)
10✔
324
    {
325
        it.next();
9✔
326
        if (it.value() == channel)
9✔
327
            return it.key();
4✔
328
    }
329

330
    return QLCChannel::invalid();
1✔
331
}
5✔
332

333
QMap <quint32,QLCInputChannel*> QLCInputProfile::channels() const
30✔
334
{
335
    return m_channels;
30✔
336
}
337

338
QVariant QLCInputProfile::channelExtraParams(const QLCInputChannel* channel) const
×
339
{
340
    if (channel == NULL)
×
341
        return QVariant();
×
342

343
    switch (m_type)
×
344
    {
345
        case OSC: return channel->name();
×
346
        case MIDI: return channel->lowerChannel();
×
347
        default: return QVariant();
×
348
    }
349
}
350

351
void QLCInputProfile::destroyChannels()
138✔
352
{
353
    /* Delete existing channels but leave the pointers there */
354
    QMutableMapIterator <quint32,QLCInputChannel*> it(m_channels);
138✔
355
    while (it.hasNext() == true)
12,502✔
356
        delete it.next().value();
12,364✔
357

358
    /* Clear the list of freed pointers */
359
    m_channels.clear();
138✔
360
}
138✔
361

362
bool QLCInputProfile::hasColorTable()
1✔
363
{
364
    return m_colorTable.isEmpty() ? false : true;
1✔
365
}
366

367
void QLCInputProfile::addColor(uchar value, QString label, QColor color)
861✔
368
{
369
    QPair<QString, QColor> lc;
861✔
370
    lc.first = label;
861✔
371
    lc.second = color;
861✔
372
    m_colorTable.insert(value, lc);
861✔
373
}
861✔
374

375
void QLCInputProfile::removeColor(uchar value)
×
376
{
377
    m_colorTable.remove(value);
×
378
}
×
379

380
QMap<uchar, QPair<QString, QColor> > QLCInputProfile::colorTable()
1✔
381
{
382
    return m_colorTable;
1✔
383
}
384

385
/********************************************************************
386
 * MIDI Channel table
387
 ********************************************************************/
388

389
bool QLCInputProfile::hasMidiChannelTable()
1✔
390
{
391
    return m_midiChannelTable.isEmpty() ? false : true;
1✔
392
}
393

394
void QLCInputProfile::addMidiChannel(uchar channel, QString label)
57✔
395
{
396
    m_midiChannelTable.insert(channel, label);
57✔
397
}
57✔
398

399
void QLCInputProfile::removeMidiChannel(uchar channel)
×
400
{
401
    m_midiChannelTable.remove(channel);
×
402
}
×
403

404
QMap<uchar, QString> QLCInputProfile::midiChannelTable()
1✔
405
{
406
    return m_midiChannelTable;
1✔
407
}
408

409
/****************************************************************************
410
 * Load & Save
411
 ****************************************************************************/
412

413
QLCInputProfile* QLCInputProfile::loader(const QString& path)
121✔
414
{
415
    QXmlStreamReader *doc = QLCFile::getXMLReader(path);
121✔
416
    if (doc == NULL || doc->device() == NULL || doc->hasError())
121✔
417
    {
418
        qWarning() << Q_FUNC_INFO << "Unable to load input profile from" << path;
2✔
419
        return NULL;
2✔
420
    }
421

422
    QLCInputProfile* profile = new QLCInputProfile();
119✔
423
    if (profile->loadXML(*doc) == false)
119✔
424
    {
425
        qWarning() << path << QString("%1\nLine %2, column %3")
×
426
                    .arg(doc->errorString())
×
427
                    .arg(doc->lineNumber())
×
428
                    .arg(doc->columnNumber());
×
429

430
        delete profile;
×
431
        profile = NULL;
×
432
    }
433
    else
434
    {
435
        profile->m_path = path;
119✔
436
    }
437

438
    QLCFile::releaseXMLReader(doc);
119✔
439

440
    return profile;
119✔
441
}
442

443
bool QLCInputProfile::loadColorTableXML(QXmlStreamReader &tableRoot)
12✔
444
{
445
    if (tableRoot.name() != KXMLQLCInputProfileColorTable)
12✔
446
    {
447
        qWarning() << Q_FUNC_INFO << "Color table node not found";
×
448
        return false;
×
449
    }
450

451
    tableRoot.readNextStartElement();
12✔
452

453
    do
454
    {
455
        if (tableRoot.name() == KXMLQLCInputProfileColor)
861✔
456
        {
457
            /* get value & color */
458
            uchar value = tableRoot.attributes().value(KXMLQLCInputProfileValue).toInt();
1,722✔
459
            QString label = tableRoot.attributes().value(KXMLQLCInputProfileLabel).toString();
1,722✔
460
            QColor color = QColor(tableRoot.attributes().value(KXMLQLCInputProfileColorRGB).toString());
1,722✔
461
            addColor(value, label, color);
861✔
462
        }
861✔
463
        else
464
        {
465
            qWarning() << Q_FUNC_INFO << "Unknown color table tag:" << tableRoot.name().toString();
×
466
        }
467
        tableRoot.skipCurrentElement();
861✔
468
    } while (tableRoot.readNextStartElement());
861✔
469

470
    return true;
12✔
471
}
472

473
bool QLCInputProfile::loadMidiChannelTableXML(QXmlStreamReader &tableRoot)
6✔
474
{
475
    if (tableRoot.name() != KXMLQLCInputProfileMidiChannelTable)
6✔
476
    {
477
        qWarning() << Q_FUNC_INFO << "MIDI channel table node not found";
×
478
        return false;
×
479
    }
480

481
    tableRoot.readNextStartElement();
6✔
482

483
    do
484
    {
485
        if (tableRoot.name() == KXMLQLCInputProfileMidiChannel)
57✔
486
        {
487
            /* get value & color */
488
            uchar value = tableRoot.attributes().value(KXMLQLCInputProfileValue).toInt();
114✔
489
            QString label = tableRoot.attributes().value(KXMLQLCInputProfileLabel).toString();
114✔
490
            addMidiChannel(value, label);
57✔
491
        }
57✔
492
        else
493
        {
494
            qWarning() << Q_FUNC_INFO << "Unknown MIDI channel table tag:" << tableRoot.name().toString();
×
495
        }
496
        tableRoot.skipCurrentElement();
57✔
497
    } while (tableRoot.readNextStartElement());
57✔
498

499
    return true;
6✔
500
}
501

502
bool QLCInputProfile::loadXML(QXmlStreamReader& doc)
122✔
503
{
504
    if (doc.readNextStartElement() == false)
122✔
505
        return false;
2✔
506

507
    if (doc.name() == KXMLQLCInputProfile)
120✔
508
    {
509
        while (doc.readNextStartElement())
13,230✔
510
        {
511
            if (doc.name() == KXMLQLCCreator)
13,110✔
512
            {
513
                /* Ignore this block */
514
                doc.skipCurrentElement();
119✔
515
            }
516
            else if (doc.name() == KXMLQLCInputProfileManufacturer)
12,991✔
517
            {
518
                setManufacturer(doc.readElementText());
120✔
519
            }
520
            else if (doc.name() == KXMLQLCInputProfileModel)
12,871✔
521
            {
522
                setModel(doc.readElementText());
120✔
523
            }
524
            else if (doc.name() == KXMLQLCInputProfileType)
12,751✔
525
            {
526
                setType(stringToType(doc.readElementText()));
119✔
527
            }
528
            else if (doc.name() == KXMLQLCInputProfileMidiSendNoteOff)
12,632✔
529
            {
530
                setMidiSendNoteOff(doc.readElementText() != KXMLQLCFalse);
21✔
531
            }
532
            else if (doc.name() == KXMLQLCInputChannel)
12,611✔
533
            {
534
                QString str = doc.attributes().value(KXMLQLCInputChannelNumber).toString();
25,186✔
535
                if (str.isEmpty() == false)
12,593✔
536
                {
537
                    quint32 ch = str.toInt();
12,593✔
538
                    QLCInputChannel* ich = new QLCInputChannel();
12,593✔
539
                    if (ich->loadXML(doc) == true)
12,593✔
540
                        insertChannel(ch, ich);
12,593✔
541
                    else
542
                        delete ich;
×
543
                }
544
                else
545
                    doc.skipCurrentElement();
×
546
            }
12,593✔
547
            else if (doc.name() == KXMLQLCInputProfileColorTable)
18✔
548
            {
549
                loadColorTableXML(doc);
12✔
550
            }
551
            else if (doc.name() == KXMLQLCInputProfileMidiChannelTable)
6✔
552
            {
553
                loadMidiChannelTableXML(doc);
6✔
554
            }
555
            else
556
            {
557
                qWarning() << Q_FUNC_INFO << "Unknown input profile tag:" << doc.name().toString();
×
558
                doc.skipCurrentElement();
×
559
            }
560
        }
561

562
        return true;
120✔
563
    }
564
    else
565
    {
566
        qWarning() << Q_FUNC_INFO << "Input profile not found";
×
567
        return false;
×
568
    }
569
}
570

571
bool QLCInputProfile::saveXML(const QString& fileName)
2✔
572
{
573
    QFile file(fileName);
2✔
574
    if (file.open(QIODevice::WriteOnly) == false)
2✔
575
    {
576
        qWarning() << Q_FUNC_INFO << "Unable to write to" << fileName;
1✔
577
        return false;
1✔
578
    }
579

580
    QXmlStreamWriter doc(&file);
1✔
581
    doc.setAutoFormatting(true);
1✔
582
    doc.setAutoFormattingIndent(1);
1✔
583
    QLCFile::writeXMLHeader(&doc, KXMLQLCInputProfile);
2✔
584

585
    doc.writeTextElement(KXMLQLCInputProfileManufacturer, m_manufacturer);
2✔
586
    doc.writeTextElement(KXMLQLCInputProfileModel, m_model);
2✔
587
    doc.writeTextElement(KXMLQLCInputProfileType, typeToString(m_type));
2✔
588

589
    if (midiSendNoteOff() == false)
1✔
590
        doc.writeTextElement(KXMLQLCInputProfileMidiSendNoteOff, QString(KXMLQLCFalse));
×
591

592
    /* Write channels to the document */
593
    QMapIterator <quint32, QLCInputChannel*> it(m_channels);
1✔
594
    while (it.hasNext() == true)
4✔
595
    {
596
        it.next();
3✔
597
        it.value()->saveXML(&doc, it.key());
3✔
598
    }
599

600
    if (hasColorTable())
1✔
601
    {
602
        doc.writeStartElement(KXMLQLCInputProfileColorTable);
×
603

604
        QMapIterator <uchar, QPair<QString, QColor>> it(m_colorTable);
×
605
        while (it.hasNext() == true)
×
606
        {
607
            it.next();
×
608
            QPair<QString, QColor> lc = it.value();
×
609
            doc.writeStartElement(KXMLQLCInputProfileColor);
×
610
            doc.writeAttribute(KXMLQLCInputProfileValue, QString::number(it.key()));
×
611
            doc.writeAttribute(KXMLQLCInputProfileLabel, lc.first);
×
612
            doc.writeAttribute(KXMLQLCInputProfileColorRGB, lc.second.name());
×
613
            doc.writeEndElement();
×
614
        }
×
615

616
        doc.writeEndElement();
×
617
    }
×
618

619
    if (hasMidiChannelTable())
1✔
620
    {
621
        doc.writeStartElement(KXMLQLCInputProfileMidiChannelTable);
×
622

623
        QMapIterator <uchar, QString> it(m_midiChannelTable);
×
624
        while (it.hasNext() == true)
×
625
        {
626
            it.next();
×
627
            doc.writeStartElement(KXMLQLCInputProfileMidiChannel);
×
628
            doc.writeAttribute(KXMLQLCInputProfileValue, QString::number(it.key()));
×
629
            doc.writeAttribute(KXMLQLCInputProfileLabel, it.value());
×
630
            doc.writeEndElement();
×
631

632
        }
633
        doc.writeEndElement();
×
634
    }
×
635

636
    m_path = fileName;
1✔
637

638
    /* End the document and close all the open elements */
639
    doc.writeEndDocument();
1✔
640
    file.close();
1✔
641
#ifdef Q_OS_UNIX
642
    sync();
1✔
643
#endif
644
    return true;
1✔
645
}
2✔
646

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