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

mcallegari / qlcplus / 14433223734

13 Apr 2025 08:50PM UTC coverage: 31.914% (+0.007%) from 31.907%
14433223734

Pull #1717

github

web-flow
Merge f294afa4e into c523d61b1
Pull Request #1717: engine: fix 3 memory leaks

8 of 9 new or added lines in 4 files covered. (88.89%)

111 existing lines in 2 files now uncovered.

14721 of 46127 relevant lines covered (31.91%)

26421.07 hits per line

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

66.26
/engine/src/inputoutputmap.cpp
1
/*
2
  Q Light Controller Plus
3
  inputoutputmap.cpp
4

5
  Copyright (c) Massimo Callegari
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
#if defined(WIN32) || defined(Q_OS_WIN)
21
#        include <Windows.h>
22
#else
23
#   include <unistd.h>
24
#endif
25

26
#include <QXmlStreamReader>
27
#include <QXmlStreamWriter>
28
#include <QSettings>
29
#include <QDebug>
30
#include <qmath.h>
31

32
#include "inputoutputmap.h"
33
#include "qlcinputchannel.h"
34
#include "qlcinputsource.h"
35
#include "qlcioplugin.h"
36
#include "outputpatch.h"
37
#include "inputpatch.h"
38
#include "qlcconfig.h"
39
#include "universe.h"
40
#include "qlcfile.h"
41
#include "doc.h"
42

43
InputOutputMap::InputOutputMap(Doc *doc, quint32 universes)
200✔
44
  : QObject(doc)
45
  , m_blackout(false)
200✔
46
  , m_universeChanged(false)
200✔
47
  , m_currentBPM(0)
200✔
48
{
49
    m_grandMaster = new GrandMaster(this);
200✔
50
    for (quint32 i = 0; i < universes; i++)
1,000✔
51
        addUniverse();
800✔
52

53
    connect(doc->ioPluginCache(), SIGNAL(pluginConfigurationChanged(QLCIOPlugin*)),
200✔
54
            this, SLOT(slotPluginConfigurationChanged(QLCIOPlugin*)));
55
    connect(doc->masterTimer(), SIGNAL(beat()), this, SLOT(slotMasterTimerBeat()));
200✔
56
}
200✔
57

58
InputOutputMap::~InputOutputMap()
377✔
59
{
60
    removeAllUniverses();
200✔
61
    delete m_grandMaster;
200✔
62
    qDeleteAll(m_profiles);
200✔
63
}
377✔
64

65
Doc* InputOutputMap::doc() const
867✔
66
{
67
    return qobject_cast<Doc*> (parent());
867✔
68
}
69

70
/*****************************************************************************
71
 * Blackout
72
 *****************************************************************************/
73

74
bool InputOutputMap::toggleBlackout()
4✔
75
{
76
    if (m_blackout == true)
4✔
77
        setBlackout(false);
2✔
78
    else
79
        setBlackout(true);
2✔
80

81
    return m_blackout;
4✔
82
}
83

84
bool InputOutputMap::setBlackout(bool blackout)
7✔
85
{
86
    /* Don't do blackout twice */
87
    if (m_blackout == blackout)
7✔
88
        return false;
89

90
    m_blackout = blackout;
5✔
91

92
    // blackout is an atomic setting, so it's safe to do it
93
    // without mutex locking
94
    foreach (Universe *universe, m_universeArray)
25✔
95
    {
96
        for (int i = 0; i < universe->outputPatchesCount(); i++)
32✔
97
        {
98
            OutputPatch *op = universe->outputPatch(i);
12✔
99
            if (op != NULL)
12✔
100
                op->setBlackout(blackout);
12✔
101
        }
102

103
        const QByteArray postGM = universe->postGMValues()->mid(0, universe->usedChannels());
20✔
104
        universe->dumpOutput(postGM, true);
20✔
105
    }
20✔
106

107
    emit blackoutChanged(m_blackout);
5✔
108

109
    return true;
5✔
110
}
111

UNCOV
112
void InputOutputMap::requestBlackout(BlackoutRequest blackout)
×
113
{
UNCOV
114
    if (blackout != BlackoutRequestNone)
×
115
        setBlackout(blackout == BlackoutRequestOn ? true : false);
×
UNCOV
116
}
×
117

118
bool InputOutputMap::blackout() const
9✔
119
{
120
    return m_blackout;
9✔
121
}
122

123
/*****************************************************************************
124
 * Universes
125
 *****************************************************************************/
126

127
quint32 InputOutputMap::invalidUniverse()
1,940✔
128
{
129
    return UINT_MAX;
1,940✔
130
}
131

132
bool InputOutputMap::addUniverse(quint32 id)
803✔
133
{
134
    {
135
        QMutexLocker locker(&m_universeMutex);
803✔
136
        Universe *uni = NULL;
803✔
137

138
        if (id == InputOutputMap::invalidUniverse())
803✔
139
        {
140
            id = universesCount();
801✔
141
        }
142
        else if (id < universesCount())
2✔
143
        {
144
            qWarning() << Q_FUNC_INFO
2✔
145
                << "Universe" << id << "is already present in the list."
1✔
146
                << "The universe list may be unsorted.";
1✔
147
            return false;
148
        }
149
        else if (id > universesCount())
1✔
150
        {
151
            qDebug() << Q_FUNC_INFO
152
                << "Gap between universe" << (universesCount() - 1)
153
                << "and universe" << id << ", filling the gap...";
154
            while (id > universesCount())
4✔
155
            {
156
                uni = new Universe(universesCount(), m_grandMaster);
3✔
157
                connect(doc()->masterTimer(), SIGNAL(tickReady()), uni, SLOT(tick()), Qt::QueuedConnection);
3✔
158
                connect(uni, SIGNAL(universeWritten(quint32,QByteArray)), this, SIGNAL(universeWritten(quint32,QByteArray)));
3✔
159
                m_universeArray.append(uni);
3✔
160
            }
161
        }
162

163
        uni = new Universe(id, m_grandMaster);
802✔
164
        connect(doc()->masterTimer(), SIGNAL(tickReady()), uni, SLOT(tick()), Qt::QueuedConnection);
802✔
165
        connect(uni, SIGNAL(universeWritten(quint32,QByteArray)), this, SIGNAL(universeWritten(quint32,QByteArray)));
802✔
166
        m_universeArray.append(uni);
802✔
167
    }
168

169
    emit universeAdded(id);
802✔
170
    return true;
802✔
171
}
172

173
bool InputOutputMap::removeUniverse(int index)
3✔
174
{
175
    {
176
        QMutexLocker locker(&m_universeMutex);
3✔
177

178
        if (index < 0 || index >= m_universeArray.count())
3✔
179
            return false;
180

181
        if (index != (m_universeArray.size() - 1))
2✔
182
        {
183
            qWarning() << Q_FUNC_INFO << "Removing universe" << index
2✔
184
                << "would create a gap in the universe list, cancelling";
1✔
185
            return false;
1✔
186
        }
187

188
        delete  m_universeArray.takeAt(index);
1✔
189
    }
190

191
    emit universeRemoved(index);
1✔
192
    return true;
1✔
193
}
194

195
bool InputOutputMap::removeAllUniverses()
201✔
196
{
197
    QMutexLocker locker(&m_universeMutex);
201✔
198
    qDeleteAll(m_universeArray);
201✔
199
    m_universeArray.clear();
201✔
200
    return true;
201✔
201
}
202

UNCOV
203
void InputOutputMap::startUniverses()
×
204
{
UNCOV
205
    foreach (Universe *uni, m_universeArray)
×
206
        uni->start();
×
UNCOV
207
}
×
208

209
quint32 InputOutputMap::getUniverseID(int index)
2✔
210
{
211
    if (index >= 0 && index < m_universeArray.count())
2✔
212
        return index;
1✔
213

214
    return invalidUniverse();
1✔
215
}
216

217
QString InputOutputMap::getUniverseNameByIndex(int index)
4✔
218
{
219
    if (index >= 0 && index < m_universeArray.count())
4✔
220
        return m_universeArray.at(index)->name();
3✔
221

222
    return QString();
223
}
224

225
QString InputOutputMap::getUniverseNameByID(quint32 id)
1✔
226
{
227
    return getUniverseNameByIndex(id);
1✔
228
}
229

230
void InputOutputMap::setUniverseName(int index, QString name)
2✔
231
{
232
    if (index < 0 || index >= m_universeArray.count())
2✔
233
        return;
234
    m_universeArray.at(index)->setName(name);
1✔
235
}
236

237
void InputOutputMap::setUniversePassthrough(int index, bool enable)
2✔
238
{
239
    if (index < 0 || index >= m_universeArray.count())
2✔
240
        return;
241
    m_universeArray.at(index)->setPassthrough(enable);
1✔
242
}
243

244
bool InputOutputMap::getUniversePassthrough(int index)
2✔
245
{
246
    if (index < 0 || index >= m_universeArray.count())
2✔
247
        return false;
248
    return m_universeArray.at(index)->passthrough();
1✔
249
}
250

251
void InputOutputMap::setUniverseMonitor(int index, bool enable)
2✔
252
{
253
    if (index < 0 || index >= m_universeArray.count())
2✔
254
        return;
255
    m_universeArray.at(index)->setMonitor(enable);
1✔
256
}
257

258
bool InputOutputMap::getUniverseMonitor(int index)
2✔
259
{
260
    if (index < 0 || index >= m_universeArray.count())
2✔
261
        return false;
262
    return m_universeArray.at(index)->monitor();
1✔
263
}
264

265
bool InputOutputMap::isUniversePatched(int index)
3✔
266
{
267
    if (index < 0 || index >= m_universeArray.count())
3✔
268
        return false;
269

270
    return m_universeArray.at(index)->isPatched();
2✔
271
}
272

273
quint32 InputOutputMap::universesCount() const
1,598✔
274
{
275
    return (quint32)m_universeArray.count();
1,598✔
276
}
277

278
QList<Universe *> InputOutputMap::universes() const
42✔
279
{
280
    return m_universeArray;
42✔
281
}
282

UNCOV
283
Universe *InputOutputMap::universe(quint32 id)
×
284
{
UNCOV
285
    for (int i = 0; i < m_universeArray.size(); i++)
×
286
        if (m_universeArray.at(i)->id() == id)
×
UNCOV
287
            return m_universeArray.at(i);
×
288

289
    return NULL;
290
}
291

292
QList<Universe*> InputOutputMap::claimUniverses()
1,045✔
293
{
294
    m_universeMutex.lock();
1,045✔
295
    return m_universeArray;
1,045✔
296
}
297

298
void InputOutputMap::releaseUniverses(bool changed)
1,045✔
299
{
300
    m_universeChanged = changed;
1,045✔
301
    m_universeMutex.unlock();
1,045✔
302
}
1,045✔
303

304
void InputOutputMap::resetUniverses()
1✔
305
{
306
    {
307
        QMutexLocker locker(&m_universeMutex);
1✔
308
        for (int i = 0; i < m_universeArray.size(); i++)
5✔
309
            m_universeArray.at(i)->reset();
4✔
310
    }
311

312
    /* Reset Grand Master parameters */
313
    setGrandMasterValue(255);
1✔
314
    setGrandMasterValueMode(GrandMaster::Reduce);
1✔
315
    setGrandMasterChannelMode(GrandMaster::Intensity);
1✔
316
}
1✔
317

318
/*********************************************************************
319
 * Grand Master
320
 *********************************************************************/
321

322
void InputOutputMap::setGrandMasterChannelMode(GrandMaster::ChannelMode mode)
2✔
323
{
324
    Q_ASSERT(m_grandMaster != NULL);
325

326
    if (m_grandMaster->channelMode() != mode)
2✔
327
    {
328
        m_grandMaster->setChannelMode(mode);
1✔
329
        m_universeChanged = true;
1✔
330
    }
331
}
2✔
332

333
GrandMaster::ChannelMode InputOutputMap::grandMasterChannelMode()
78✔
334
{
335
    Q_ASSERT(m_grandMaster != NULL);
336

337
    return m_grandMaster->channelMode();
78✔
338
}
339

340
void InputOutputMap::setGrandMasterValueMode(GrandMaster::ValueMode mode)
2✔
341
{
342
    Q_ASSERT(m_grandMaster != NULL);
343

344
    if (m_grandMaster->valueMode() != mode)
2✔
345
    {
346
        m_grandMaster->setValueMode(mode);
1✔
347
        m_universeChanged = true;
1✔
348
    }
349

350
    emit grandMasterValueModeChanged(mode);
2✔
351
}
2✔
352

353
GrandMaster::ValueMode InputOutputMap::grandMasterValueMode()
154✔
354
{
355
    Q_ASSERT(m_grandMaster != NULL);
356

357
    return m_grandMaster->valueMode();
154✔
358
}
359

360
void InputOutputMap::setGrandMasterValue(uchar value)
2✔
361
{
362
    Q_ASSERT(m_grandMaster != NULL);
363

364
    if (m_grandMaster->value() != value)
2✔
365
    {
366
        m_grandMaster->setValue(value);
1✔
367
        m_universeChanged = true;
1✔
368
    }
369

370
    if (m_universeChanged == true)
2✔
371
        emit grandMasterValueChanged(value);
2✔
372
}
2✔
373

374
uchar InputOutputMap::grandMasterValue()
2✔
375
{
376
    Q_ASSERT(m_grandMaster != NULL);
377

378
    return m_grandMaster->value();
2✔
379
}
380

381
/*********************************************************************
382
 * Patch
383
 *********************************************************************/
384

385
void InputOutputMap::flushInputs()
5✔
386
{
387
    QMutexLocker locker(&m_universeMutex);
5✔
388
    foreach (Universe *universe, m_universeArray)
30✔
389
        universe->flushInput();
20✔
390
}
5✔
391

392
bool InputOutputMap::setInputPatch(quint32 universe, const QString &pluginName,
7✔
393
                                   const QString &inputUID, quint32 input,
394
                                   const QString &profileName)
395
{
396
    /* Check that the universe that we're doing mapping for is valid */
397
    if (universe >= universesCount())
7✔
398
    {
399
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
1✔
400
        return false;
1✔
401
    }
402

403
    QMutexLocker locker(&m_universeMutex);
6✔
404
    InputPatch *currInPatch = m_universeArray.at(universe)->inputPatch();
6✔
405
    QLCInputProfile *currProfile = NULL;
406
    if (currInPatch != NULL)
6✔
407
    {
408
        currProfile = currInPatch->profile();
1✔
409
        disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
1✔
410
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
411
        if (currInPatch->plugin()->capabilities() & QLCIOPlugin::Beats)
1✔
412
        {
UNCOV
413
            disconnect(currInPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
414
                       this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
415
        }
416
    }
417
    InputPatch *ip = NULL;
418
    QLCIOPlugin *plugin = doc()->ioPluginCache()->plugin(pluginName);
6✔
419

420
    if (!inputUID.isEmpty() && plugin != NULL)
6✔
421
    {
422
        QStringList inputs = plugin->inputs();
5✔
423
        int lIdx = inputs.indexOf(inputUID);
424
        if (lIdx != -1)
5✔
425
        {
426
            qDebug() << "[IOMAP] Found match on input by name on universe" << universe << "-" << input << "vs" << lIdx;
427
            input = lIdx;
5✔
428
        }
429
        else
430
        {
431
            qDebug() << "[IOMAP] !!No match found!! for input on universe" << universe << "-" << input << inputUID;
432
            qDebug() << plugin->inputs();
433
        }
434
    }
435

436
    if (m_universeArray.at(universe)->setInputPatch(
6✔
437
                plugin, input, profile(profileName)) == true)
438
    {
439
        ip = m_universeArray.at(universe)->inputPatch();
6✔
440
        if (ip != NULL)
6✔
441
        {
442
            connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
5✔
443
                    this, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)));
444
            if (ip->plugin()->capabilities() & QLCIOPlugin::Beats)
5✔
445
            {
UNCOV
446
                connect(ip, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
447
                        this, SLOT(slotPluginBeat(quint32,quint32,uchar,const QString&)));
448
            }
449
        }
450
    }
451
    else
452
    {
453
        return false;
454
    }
455

456
    if (ip != NULL && currProfile != ip->profile())
6✔
457
        emit profileChanged(universe, ip->profileName());
3✔
458

459
    return true;
460
}
461

UNCOV
462
bool InputOutputMap::setInputProfile(quint32 universe, const QString &profileName)
×
463
{
464
    /* Check that the universe that we're doing mapping for is valid */
465
    if (universe >= universesCount())
×
466
    {
UNCOV
467
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
468
        return false;
×
469
    }
470

471
    InputPatch *currInPatch = m_universeArray.at(universe)->inputPatch();
×
UNCOV
472
    if (currInPatch != NULL)
×
UNCOV
473
        currInPatch->set(profile(profileName));
×
474

475
    /* if no input patch is set, then setting a profile is useless,
476
       but there's no reason to cause an error here */
477
    return true;
478
}
479

480
bool InputOutputMap::setOutputPatch(quint32 universe, const QString &pluginName,
21✔
481
                                    const QString &outputUID, quint32 output,
482
                                    bool isFeedback, int index)
483
{
484
    /* Check that the universe that we're doing mapping for is valid */
485
    if (universe >= universesCount())
21✔
486
    {
487
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
2✔
488
        return false;
2✔
489
    }
490

491
    QMutexLocker locker(&m_universeMutex);
19✔
492
    QLCIOPlugin *plugin = doc()->ioPluginCache()->plugin(pluginName);
19✔
493

494
    if (!outputUID.isEmpty() && plugin != NULL)
19✔
495
    {
496
        QStringList inputs = plugin->outputs();
12✔
497
        int lIdx = inputs.indexOf(outputUID);
498
        if (lIdx != -1)
12✔
499
        {
500
            qDebug() << "[IOMAP] Found match on output by name on universe" << universe << "-" << output << "vs" << lIdx;
501
            output = lIdx;
12✔
502
        }
503
        else
504
        {
505
            qDebug() << "[IOMAP] !!No match found!! for output on universe" << universe << "-" << output << outputUID;
506
            qDebug() << plugin->outputs();
507
        }
508
    }
509

510
    if (isFeedback == false)
19✔
511
        return m_universeArray.at(universe)->setOutputPatch(plugin, output, index);
19✔
512
    else
UNCOV
513
        return m_universeArray.at(universe)->setFeedbackPatch(plugin, output);
×
514

515
    return false;
516
}
517

518
int InputOutputMap::outputPatchesCount(quint32 universe) const
4✔
519
{
520
    if (universe >= universesCount())
4✔
521
    {
UNCOV
522
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
UNCOV
523
        return 0;
×
524
    }
525

526
    return m_universeArray.at(universe)->outputPatchesCount();
4✔
527
}
528

529
InputPatch *InputOutputMap::inputPatch(quint32 universe) const
56✔
530
{
531
    if (universe >= universesCount())
56✔
532
    {
533
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
7✔
534
        return NULL;
7✔
535
    }
536
    return m_universeArray.at(universe)->inputPatch();
49✔
537
}
538

539
OutputPatch *InputOutputMap::outputPatch(quint32 universe, int index) const
27✔
540
{
541
    if (universe >= universesCount())
27✔
542
    {
UNCOV
543
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
×
UNCOV
544
        return NULL;
×
545
    }
546
    return m_universeArray.at(universe)->outputPatch(index);
27✔
547
}
548

549
OutputPatch *InputOutputMap::feedbackPatch(quint32 universe) const
2✔
550
{
551
    if (universe >= universesCount())
2✔
552
    {
553
        qWarning() << Q_FUNC_INFO << "Universe" << universe << "out of bounds.";
1✔
554
        return NULL;
1✔
555
    }
556
    return m_universeArray.at(universe)->feedbackPatch();
1✔
557
}
558

559
QStringList InputOutputMap::universeNames() const
23✔
560
{
561
    QStringList list;
562
    for (quint32 i = 0; i < universesCount(); i++)
115✔
563
        list << m_universeArray.at(i)->name();
184✔
564

565
    return list;
23✔
566
}
567

568
quint32 InputOutputMap::inputMapping(const QString &pluginName, quint32 input) const
16✔
569
{
570
    for (quint32 uni = 0; uni < universesCount(); uni++)
70✔
571
    {
572
        const InputPatch* p = m_universeArray.at(uni)->inputPatch();
57✔
573
        if (p != NULL && p->pluginName() == pluginName && p->input() == input)
57✔
574
            return uni;
3✔
575
    }
576

577
    return QLCIOPlugin::invalidLine();
578
}
579

580
quint32 InputOutputMap::outputMapping(const QString &pluginName, quint32 output) const
2✔
581
{
582
    for (quint32 uni = 0; uni < universesCount(); uni++)
9✔
583
    {
584
        Universe *universe = m_universeArray.at(uni);
8✔
585
        for (int i = 0; i < universe->outputPatchesCount(); i++)
15✔
586
        {
587
            const OutputPatch* p = universe->outputPatch(i);
8✔
588
            if (p != NULL && p->pluginName() == pluginName && p->output() == output)
8✔
589
                return uni;
1✔
590
        }
591
    }
592

593
    return QLCIOPlugin::invalidLine();
594
}
595

596
/*****************************************************************************
597
 * Plugins
598
 *****************************************************************************/
599

600
QString InputOutputMap::pluginDescription(const QString &pluginName)
2✔
601
{
602
    QLCIOPlugin* plugin = NULL;
603

604
    if (pluginName.isEmpty() == false)
2✔
605
        plugin = doc()->ioPluginCache()->plugin(pluginName);
2✔
606

607
    if (plugin != NULL)
2✔
608
    {
609
        return plugin->pluginInfo();
1✔
610
    }
611
    else
612
        return "";
1✔
613
}
614

615
void InputOutputMap::removeDuplicates(QStringList &list)
3✔
616
{
617
    if (list.count() == 1)
3✔
618
        return;
619

620
    int c = 2;
621

622
    for (int i = 1; i < list.count(); i++)
12✔
623
    {
624
        for (int j = 0; j < i; j++)
27✔
625
        {
626
            if (list.at(i) == list.at(j))
18✔
627
            {
UNCOV
628
                list.replace(i, QString("%1 %2").arg(list.at(j)).arg(c));
×
UNCOV
629
                c++;
×
630
            }
631
        }
632
    }
633
}
634

635
QStringList InputOutputMap::inputPluginNames()
2✔
636
{
637
    QStringList list;
638
    QListIterator <QLCIOPlugin*> it(doc()->ioPluginCache()->plugins());
2✔
639
    while (it.hasNext() == true)
6✔
640
    {
641
        QLCIOPlugin* plg(it.next());
2✔
642
        if (plg->capabilities() & QLCIOPlugin::Input)
2✔
643
            list << plg->name();
4✔
644
    }
645
    return list;
2✔
646
}
647

648
QStringList InputOutputMap::outputPluginNames()
2✔
649
{
650
    QStringList list;
651
    QListIterator <QLCIOPlugin*> it(doc()->ioPluginCache()->plugins());
2✔
652
    while (it.hasNext() == true)
6✔
653
    {
654
        QLCIOPlugin* plg(it.next());
2✔
655
        if (plg->capabilities() & QLCIOPlugin::Output)
2✔
656
            list << plg->name();
4✔
657
    }
658
    return list;
2✔
659
}
660

661
QStringList InputOutputMap::pluginInputs(const QString& pluginName)
3✔
662
{
663
    QLCIOPlugin* ip = doc()->ioPluginCache()->plugin(pluginName);
3✔
664
    if (ip == NULL)
3✔
665
        return QStringList();
666
    else
667
    {
668
        QStringList iList = ip->inputs();
2✔
669
        removeDuplicates(iList);
2✔
670
        return iList;
671
    }
672
}
673

674
QStringList InputOutputMap::pluginOutputs(const QString& pluginName)
2✔
675
{
676
    QLCIOPlugin* op = doc()->ioPluginCache()->plugin(pluginName);
2✔
677
    if (op == NULL)
2✔
678
        return QStringList();
679
    else
680
    {
681
        QStringList oList = op->outputs();
1✔
682
        removeDuplicates(oList);
1✔
683
        return oList;
684
    }
685
}
686

687
bool InputOutputMap::pluginSupportsFeedback(const QString& pluginName)
1✔
688
{
689
    QLCIOPlugin* outputPlugin = doc()->ioPluginCache()->plugin(pluginName);
1✔
690
    if (outputPlugin != NULL)
1✔
691
        return (outputPlugin->capabilities() & QLCIOPlugin::Feedback) > 0;
1✔
692
    else
693
        return false;
694
}
695

696
void InputOutputMap::configurePlugin(const QString& pluginName)
3✔
697
{
698
    QLCIOPlugin* outputPlugin = doc()->ioPluginCache()->plugin(pluginName);
3✔
699
    if (outputPlugin != NULL)
3✔
700
        outputPlugin->configure();
3✔
701
}
3✔
702

703
bool InputOutputMap::canConfigurePlugin(const QString& pluginName)
4✔
704
{
705
    QLCIOPlugin* outputPlugin = doc()->ioPluginCache()->plugin(pluginName);
4✔
706
    if (outputPlugin != NULL)
4✔
707
        return outputPlugin->canConfigure();
2✔
708
    else
709
        return false;
710
}
711

712
QString InputOutputMap::inputPluginStatus(const QString& pluginName, quint32 input)
9✔
713
{
714
    QLCIOPlugin* inputPlugin = NULL;
715
    QString info;
716

717
    if (pluginName.isEmpty() == false)
9✔
718
        inputPlugin = doc()->ioPluginCache()->plugin(pluginName);
9✔
719

720
    if (inputPlugin != NULL)
9✔
721
    {
722
        info = inputPlugin->inputInfo(input);
4✔
723
    }
724
    else
725
    {
726
        /* Nothing selected */
727
        info += QString("<HTML><HEAD></HEAD><BODY>");
10✔
728
        info += QString("<H3>%1</H3>").arg(tr("Nothing selected"));
15✔
729
        info += QString("</BODY></HTML>");
10✔
730
    }
731

732
    return info;
9✔
UNCOV
733
}
×
734

735
QString InputOutputMap::outputPluginStatus(const QString& pluginName, quint32 output)
9✔
736
{
737
    QLCIOPlugin* outputPlugin = doc()->ioPluginCache()->plugin(pluginName);
9✔
738
    if (outputPlugin != NULL)
9✔
739
    {
740
        return outputPlugin->outputInfo(output);
4✔
741
    }
742
    else
743
    {
744
        QString info;
745
        info += QString("<HTML><HEAD></HEAD><BODY>");
10✔
746
        info += QString("<H3>%1</H3>").arg(tr("Nothing selected"));
15✔
747
        info += QString("</BODY></HTML>");
10✔
748
        return info;
749
    }
5✔
750
}
751

752
bool InputOutputMap::sendFeedBack(quint32 universe, quint32 channel, uchar value, const QVariant &params)
12✔
753
{
754
    if (universe >= universesCount())
12✔
755
        return false;
756

757
    OutputPatch* patch = m_universeArray.at(universe)->feedbackPatch();
9✔
758

759
    if (patch != NULL && patch->isPatched())
9✔
760
    {
UNCOV
761
        patch->plugin()->sendFeedBack(universe, patch->output(), channel, value, params);
×
UNCOV
762
        return true;
×
763
    }
764
    else
765
    {
766
        return false;
9✔
767
    }
768
}
769

770
void InputOutputMap::slotPluginConfigurationChanged(QLCIOPlugin* plugin)
8✔
771
{
772
    QMutexLocker locker(&m_universeMutex);
8✔
773
    bool success = true;
774
    for (quint32 i = 0; i < universesCount(); i++)
40✔
775
    {
776
        Universe *universe = m_universeArray.at(i);
32✔
777
        for (int oi = 0; oi < universe->outputPatchesCount(); oi++)
32✔
778
        {
UNCOV
779
            OutputPatch* op = universe->outputPatch(oi);
×
780

UNCOV
781
            if (op != NULL && op->plugin() == plugin)
×
782
            {
UNCOV
783
                /*success = */ op->reconnect();
×
784
            }
785
        }
786

787
        InputPatch* ip = m_universeArray.at(i)->inputPatch();
32✔
788

789
        if (ip != NULL && ip->plugin() == plugin)
32✔
790
        {
UNCOV
791
            /*success = */ ip->reconnect();
×
792
        }
793

794
        OutputPatch* fp = m_universeArray.at(i)->feedbackPatch();
32✔
795
        if (fp != NULL && fp->plugin() == plugin)
32✔
796
        {
UNCOV
797
            /*success = */ fp->reconnect();
×
798
        }
799
    }
800
    locker.unlock();
8✔
801

802
    emit pluginConfigurationChanged(plugin->name(), success);
8✔
803
}
8✔
804

805
/*****************************************************************************
806
 * Profiles
807
 *****************************************************************************/
808

809
void InputOutputMap::loadProfiles(const QDir& dir)
5✔
810
{
811
    if (dir.exists() == false || dir.isReadable() == false)
5✔
812
        return;
1✔
813

814
    /* Go thru all found file entries and attempt to load an input
815
       profile from each of them. */
816
    QStringListIterator it(dir.entryList());
4✔
817
    while (it.hasNext() == true)
118✔
818
    {
819
        QLCInputProfile* prof;
820
        QString path;
821

822
        path = dir.absoluteFilePath(it.next());
114✔
823
        prof = QLCInputProfile::loader(path);
114✔
824
        if (prof != NULL)
114✔
825
        {
826
            /* Check for duplicates */
827
            if (profile(prof->name()) == NULL)
114✔
828
                addProfile(prof);
76✔
829
            else
830
                delete prof;
38✔
831
        }
832
        else
833
        {
UNCOV
834
            qWarning() << Q_FUNC_INFO << "Unable to find an input profile from" << path;
×
835
        }
836
    }
114✔
837
}
838

839
QStringList InputOutputMap::profileNames()
7✔
840
{
841
    QStringList list;
842
    QListIterator <QLCInputProfile*> it(m_profiles);
7✔
843
    while (it.hasNext() == true)
85✔
844
        list << it.next()->name();
156✔
845
    return list;
7✔
846
}
847

848
QLCInputProfile* InputOutputMap::profile(const QString& name)
122✔
849
{
850
    QListIterator <QLCInputProfile*> it(m_profiles);
122✔
851
    while (it.hasNext() == true)
2,285✔
852
    {
853
        QLCInputProfile* profile = it.next();
2,205✔
854
        if (profile->name() == name)
2,205✔
855
            return profile;
42✔
856
    }
857

858
    return NULL;
859
}
860

861
bool InputOutputMap::addProfile(QLCInputProfile* profile)
79✔
862
{
863
    Q_ASSERT(profile != NULL);
864

865
    /* Don't add the same profile twice */
866
    if (m_profiles.contains(profile) == false)
79✔
867
    {
868
        m_profiles.append(profile);
78✔
869
        return true;
78✔
870
    }
871
    else
872
    {
873
        return false;
874
    }
875
}
876

877
bool InputOutputMap::removeProfile(const QString& name)
2✔
878
{
879
    QMutableListIterator <QLCInputProfile*> it(m_profiles);
2✔
880
    while (it.hasNext() == true)
3✔
881
    {
882
        QLCInputProfile *profile = it.next();
2✔
883
        if (profile->name() == name)
2✔
884
        {
885
            it.remove();
1✔
886
            delete profile;
1✔
887
            return true;
1✔
888
        }
889
    }
890

891
    return false;
892
}
893

894
bool InputOutputMap::inputSourceNames(const QLCInputSource *src,
8✔
895
                                QString& uniName, QString& chName) const
896
{
897
    if (src == NULL || src->isValid() == false)
8✔
898
        return false;
3✔
899

900
    if (src->universe() >= universesCount())
5✔
901
        return false;
902

903
    InputPatch* pat = m_universeArray.at(src->universe())->inputPatch();
4✔
904
    if (pat == NULL)
4✔
905
    {
906
        /* There is no patch for the given universe */
907
        uniName = QString("%1 -UNPATCHED-").arg(src->universe() + 1);
1✔
908

909
        ushort page = src->page();
1✔
910
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
911

912
        if (page != 0)
1✔
UNCOV
913
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
914
        else
915
            chName = QString("%1: ?").arg(channel);
2✔
916
        return true;
1✔
917
    }
918

919
    QLCInputProfile* profile = pat->profile();
3✔
920
    if (profile == NULL)
3✔
921
    {
922
        /* There is no profile. Display plugin name and channel number. */
923
        if (pat->plugin() != NULL)
1✔
924
            uniName = QString("%1: %2").arg(src->universe() + 1).arg(pat->plugin()->name());
1✔
925
        else
UNCOV
926
            uniName = QString("%1: ??").arg(src->universe() + 1);
×
927

928
        ushort page = src->page();
1✔
929
        ushort channel = (src->channel() & 0x0000FFFF) + 1;
1✔
930

931
        if (page != 0)
1✔
UNCOV
932
            chName = QString("%1: ? (Page %2)").arg(channel).arg(page + 1);
×
933
        else
934
            chName = QString("%1: ?").arg(channel);
2✔
935
    }
936
    else
937
    {
938
        QLCInputChannel* ich;
939
        QString name;
940

941
        /* Display profile name for universe */
942
        uniName = QString("%1: %2").arg(src->universe() + 1).arg(profile->name());
2✔
943

944
        /* User can input the channel number by hand, so put something
945
           rational to the channel name in those cases as well. */
946
        ushort page = src->page();
2✔
947
        ushort channel = (src->channel() & 0x0000FFFF);
2✔
948

949
        ich = profile->channel(channel);
2✔
950
        if (ich != NULL)
2✔
951
            name = ich->name();
1✔
952
        else
953
            name = QString("?");
1✔
954

955
        /* Display channel name */
956
        if (page != 0)
2✔
UNCOV
957
            chName = QString("%1: %2 (Page %3)").arg(channel + 1).arg(name).arg(page + 1);
×
958
        else
959
            chName = QString("%1: %2").arg(channel + 1).arg(name);
2✔
960
    }
2✔
961

962
    return true;
963
}
964

UNCOV
965
bool InputOutputMap::inputSourceNames(QSharedPointer<QLCInputSource> const& src,
×
966
                                QString& uniName, QString& chName) const
967
{
968
    return inputSourceNames(src.data(), uniName, chName);
×
969
}
970

971
QDir InputOutputMap::systemProfileDirectory()
1✔
972
{
973
    return QLCFile::systemDirectory(QString(INPUTPROFILEDIR), QString(KExtInputProfile));
1✔
974
}
975

976
QDir InputOutputMap::userProfileDirectory()
1✔
977
{
978
    return QLCFile::userDirectory(QString(USERINPUTPROFILEDIR), QString(INPUTPROFILEDIR),
2✔
979
                                  QStringList() << QString("*%1").arg(KExtInputProfile));
3✔
980
}
981

982
/*********************************************************************
983
 * Beats
984
 *********************************************************************/
985

UNCOV
986
void InputOutputMap::setBeatGeneratorType(InputOutputMap::BeatGeneratorType type)
×
987
{
UNCOV
988
    if (type == m_beatGeneratorType)
×
989
        return;
990

991
    m_beatGeneratorType = type;
×
992
    qDebug() << "[InputOutputMap] setting beat type:" << m_beatGeneratorType;
993

994
    switch (m_beatGeneratorType)
×
995
    {
UNCOV
996
        case Internal:
×
997
            doc()->masterTimer()->setBeatSourceType(MasterTimer::Internal);
×
UNCOV
998
            setBpmNumber(doc()->masterTimer()->bpmNumber());
×
999
        break;
×
1000
        case Plugin:
×
1001
            doc()->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1002
            // reset the current BPM number and detect it from the MIDI beats
1003
            setBpmNumber(0);
×
1004
            m_beatTime.restart();
×
UNCOV
1005
        break;
×
1006
        case Audio:
×
1007
            doc()->masterTimer()->setBeatSourceType(MasterTimer::External);
×
1008
            // reset the current BPM number and detect it from the audio input
1009
            setBpmNumber(0);
×
1010
            m_beatTime.restart();
×
UNCOV
1011
        break;
×
1012
        case Disabled:
×
1013
        default:
1014
            doc()->masterTimer()->setBeatSourceType(MasterTimer::None);
×
1015
            setBpmNumber(0);
×
UNCOV
1016
        break;
×
1017
    }
1018

1019
    emit beatGeneratorTypeChanged();
×
1020
}
1021

1022
InputOutputMap::BeatGeneratorType InputOutputMap::beatGeneratorType() const
×
1023
{
UNCOV
1024
    return m_beatGeneratorType;
×
1025
}
1026

1027
QString InputOutputMap::beatTypeToString(BeatGeneratorType type) const
1✔
1028
{
1029
    switch (type)
1✔
1030
    {
UNCOV
1031
        case Internal:  return "Internal";
×
UNCOV
1032
        case Plugin:    return "Plugin";
×
UNCOV
1033
        case Audio:     return "Audio";
×
1034
        default:        return "Disabled";
1✔
1035
    }
1036
}
1037

UNCOV
1038
InputOutputMap::BeatGeneratorType InputOutputMap::stringToBeatType(QString str)
×
1039
{
UNCOV
1040
    if (str == "Internal")
×
1041
        return Internal;
UNCOV
1042
    else if (str == "Plugin")
×
1043
        return Plugin;
UNCOV
1044
    else if (str == "Audio")
×
1045
        return Audio;
×
1046

1047
    return Disabled;
1048
}
1049

UNCOV
1050
void InputOutputMap::setBpmNumber(int bpm)
×
1051
{
UNCOV
1052
    if (m_beatGeneratorType == Disabled || bpm == m_currentBPM)
×
1053
        return;
1054

1055
    //qDebug() << "[InputOutputMap] set BPM to" << bpm;
UNCOV
1056
    m_currentBPM = bpm;
×
1057

UNCOV
1058
    if (bpm != 0)
×
1059
        doc()->masterTimer()->requestBpmNumber(bpm);
×
1060

1061
    emit bpmNumberChanged(m_currentBPM);
×
1062
}
1063

1064
int InputOutputMap::bpmNumber() const
×
1065
{
UNCOV
1066
    if (m_beatGeneratorType == Disabled)
×
1067
        return 0;
1068

1069
    return m_currentBPM;
×
1070
}
1071

1072
void InputOutputMap::slotMasterTimerBeat()
×
1073
{
UNCOV
1074
    if (m_beatGeneratorType != Internal)
×
1075
        return;
1076

1077
    emit beat();
×
1078
}
1079

1080
void InputOutputMap::slotPluginBeat(quint32 universe, quint32 channel, uchar value, const QString &key)
×
1081
{
1082
    Q_UNUSED(universe)
1083

1084
    // not interested in synthetic release or non-beat event
UNCOV
1085
    if (m_beatGeneratorType != Plugin || value == 0 || key != "beat")
×
UNCOV
1086
        return;
×
1087

1088
    qDebug() << "Plugin beat:" << channel << m_beatTime.elapsed();
1089

1090
    // process the timer as first thing, to avoid wasting time
1091
    // with the operations below
UNCOV
1092
    int elapsed = m_beatTime.elapsed();
×
UNCOV
1093
    m_beatTime.restart();
×
1094

1095
    int bpm = qRound(60000.0 / (float)elapsed);
×
1096
    float currBpmTime = 60000.0 / (float)m_currentBPM;
×
1097
    // here we check if the difference between the current BPM duration
1098
    // and the current time elapsed is within a range of +/-1ms.
1099
    // If it isn't, then the BPM number has really changed, otherwise
1100
    // it's just a tiny time drift
UNCOV
1101
    if (qAbs((float)elapsed - currBpmTime) > 1)
×
UNCOV
1102
        setBpmNumber(bpm);
×
1103

1104
    doc()->masterTimer()->requestBeat();
×
1105
    emit beat();
×
1106
}
1107

1108
void InputOutputMap::slotAudioSpectrum(double *spectrumBands, int size, double maxMagnitude, quint32 power)
×
1109
{
1110
    Q_UNUSED(spectrumBands)
1111
    Q_UNUSED(size)
1112
    Q_UNUSED(maxMagnitude)
1113
    Q_UNUSED(power)
UNCOV
1114
}
×
1115

1116
/*********************************************************************
1117
 * Defaults - !! FALLBACK !!
1118
 *********************************************************************/
1119

UNCOV
1120
void InputOutputMap::loadDefaults()
×
1121
{
1122
    /* ************************ INPUT *********************************** */
1123
    QSettings settings;
×
1124
    QString plugin;
1125
    QString input;
1126
    QString key;
1127

UNCOV
1128
    for (quint32 i = 0; i < universesCount(); i++)
×
1129
    {
1130
        QString profileName;
1131
        bool passthrough;
1132

1133
        /* Plugin name */
UNCOV
1134
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
UNCOV
1135
        plugin = settings.value(key).toString();
×
1136

1137
        /* Plugin input */
1138
        key = QString("/inputmap/universe%1/input/").arg(i);
×
UNCOV
1139
        input = settings.value(key).toString();
×
1140

1141
        /* Input profile */
1142
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
UNCOV
1143
        profileName = settings.value(key).toString();
×
1144

1145
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
1146
        passthrough = settings.value(key).toBool();
×
UNCOV
1147
        if (passthrough == true)
×
1148
            m_universeArray.at(i)->setPassthrough(passthrough);
×
1149

1150
        /* Do the mapping */
1151
        if (plugin != KInputNone && input != KInputNone)
×
UNCOV
1152
            setInputPatch(i, plugin, "", input.toUInt(), profileName);
×
UNCOV
1153
    }
×
1154

1155
    /* ************************ OUTPUT *********************************** */
1156
    QString output;
1157
    QString fb_plugin;
1158
    QString feedback;
1159

UNCOV
1160
    for (quint32 i = 0; i < universesCount(); i++)
×
1161
    {
1162
        /* Plugin name */
1163
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
UNCOV
1164
        plugin = settings.value(key).toString();
×
1165

1166
        /* Plugin output */
1167
        key = QString("/outputmap/universe%1/output/").arg(i);
×
UNCOV
1168
        output = settings.value(key).toString();
×
1169

1170
        /* Feedback plugin name */
1171
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
UNCOV
1172
        fb_plugin = settings.value(key).toString();
×
1173

1174
        /* Feedback line */
1175
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
UNCOV
1176
        feedback = settings.value(key).toString();
×
1177

1178
        if (plugin != KOutputNone && output != KOutputNone)
×
1179
            setOutputPatch(i, plugin, "", output.toUInt());
×
1180

1181
        if (fb_plugin != KOutputNone && feedback != KOutputNone)
×
1182
            setOutputPatch(i, fb_plugin, "", feedback.toUInt(), true);
×
1183
    }
1184
}
×
1185

UNCOV
1186
void InputOutputMap::saveDefaults()
×
1187
{
1188
    /* ************************ INPUT *********************************** */
1189
    QSettings settings;
×
1190
    QString key;
1191

1192
    for (quint32 i = 0; i < universesCount(); i++)
×
1193
    {
UNCOV
1194
        InputPatch* inPatch = inputPatch(i);
×
1195

1196
        /* Plugin name */
1197
        key = QString("/inputmap/universe%1/plugin/").arg(i);
×
UNCOV
1198
        if (inPatch != NULL)
×
UNCOV
1199
            settings.setValue(key, inPatch->pluginName());
×
1200
        else
1201
            settings.setValue(key, KInputNone);
×
1202

1203
        /* Plugin input */
1204
        key = QString("/inputmap/universe%1/input/").arg(i);
×
UNCOV
1205
        if (inPatch != NULL)
×
UNCOV
1206
            settings.setValue(key, QString::number(inPatch->input()));
×
1207
        else
1208
            settings.setValue(key, KInputNone);
×
1209

1210
        /* Input profile */
1211
        key = QString("/inputmap/universe%1/profile/").arg(i);
×
UNCOV
1212
        if (inPatch != NULL)
×
UNCOV
1213
            settings.setValue(key, inPatch->profileName());
×
1214
        else
1215
            settings.setValue(key, KInputNone);
×
1216

1217
        /* Passthrough */
1218
        key = QString("/inputmap/universe%1/passthrough/").arg(i);
×
UNCOV
1219
        bool passthrough = m_universeArray.at(i)->passthrough();
×
UNCOV
1220
        if (passthrough == true)
×
1221
            settings.setValue(key, passthrough);
×
1222
        else
1223
            settings.remove(key);
×
1224
    }
1225

1226
    /* ************************ OUTPUT *********************************** */
1227

UNCOV
1228
    for (quint32 i = 0; i < universesCount(); i++)
×
1229
    {
UNCOV
1230
        OutputPatch* outPatch = outputPatch(i);
×
1231
        OutputPatch* fbPatch = feedbackPatch(i);
×
1232

1233
        key = QString("/outputmap/universe%1/plugin/").arg(i);
×
1234

1235
        /* Plugin name */
1236
        if (outPatch != NULL)
×
UNCOV
1237
            settings.setValue(key, outPatch->pluginName());
×
1238
        else
1239
            settings.setValue(key, KOutputNone);
×
1240

1241
        /* Plugin output */
1242
        key = QString("/outputmap/universe%1/output/").arg(i);
×
UNCOV
1243
        if (outPatch != NULL)
×
UNCOV
1244
            settings.setValue(key, outPatch->output());
×
1245
        else
1246
            settings.setValue(key, KOutputNone);
×
1247

UNCOV
1248
        key = QString("/outputmap/universe%1/feedbackplugin/").arg(i);
×
1249

1250
        /* Feedback plugin name */
1251
        if (fbPatch != NULL)
×
UNCOV
1252
            settings.setValue(key, fbPatch->pluginName());
×
1253
        else
1254
            settings.setValue(key, KOutputNone);
×
1255

1256
        /* Feedback plugin output */
1257
        key = QString("/outputmap/universe%1/feedback/").arg(i);
×
UNCOV
1258
        if (fbPatch != NULL)
×
UNCOV
1259
            settings.setValue(key, QString::number(fbPatch->output()));
×
1260
        else
1261
            settings.setValue(key, KOutputNone);
×
1262
    }
UNCOV
1263
}
×
1264

1265
/*********************************************************************
1266
 * Load & Save
1267
 *********************************************************************/
1268

UNCOV
1269
bool InputOutputMap::loadXML(QXmlStreamReader &root)
×
1270
{
UNCOV
1271
    if (root.name() != KXMLIOMap)
×
1272
    {
UNCOV
1273
        qWarning() << Q_FUNC_INFO << "InputOutputMap node not found";
×
1274
        return false;
×
1275
    }
1276

1277
    /** Reset the current universe list and read the new one */
UNCOV
1278
    removeAllUniverses();
×
1279

UNCOV
1280
    while (root.readNextStartElement())
×
1281
    {
UNCOV
1282
        if (root.name() == KXMLQLCUniverse)
×
1283
        {
UNCOV
1284
            quint32 id = InputOutputMap::invalidUniverse();
×
1285
            if (root.attributes().hasAttribute(KXMLQLCUniverseID))
×
UNCOV
1286
                id = root.attributes().value(KXMLQLCUniverseID).toString().toUInt();
×
1287
            if (addUniverse(id))
×
1288
            {
1289
                Universe *uni = m_universeArray.last();
×
1290
                uni->loadXML(root, m_universeArray.count() - 1, this);
×
1291
            }
1292
        }
1293
        else if (root.name() == KXMLIOBeatGenerator)
×
1294
        {
UNCOV
1295
            QXmlStreamAttributes attrs = root.attributes();
×
1296

UNCOV
1297
            if (attrs.hasAttribute(KXMLIOBeatType))
×
1298
                setBeatGeneratorType(stringToBeatType(attrs.value(KXMLIOBeatType).toString()));
×
1299

1300
            if (attrs.hasAttribute(KXMLIOBeatsPerMinute))
×
1301
                setBpmNumber(attrs.value(KXMLIOBeatsPerMinute).toInt());
×
1302

1303
            root.skipCurrentElement();
×
1304
        }
1305
        else
1306
        {
UNCOV
1307
            qWarning() << Q_FUNC_INFO << "Unknown IO Map tag:" << root.name();
×
UNCOV
1308
            root.skipCurrentElement();
×
1309
        }
1310
    }
1311

1312
    return true;
1313
}
1314

1315
bool InputOutputMap::saveXML(QXmlStreamWriter *doc) const
1✔
1316
{
1317
    Q_ASSERT(doc != NULL);
1318

1319
    /* IO Map Instance entry */
1320
    doc->writeStartElement(KXMLIOMap);
1✔
1321

1322
    doc->writeStartElement(KXMLIOBeatGenerator);
1✔
1323
    doc->writeAttribute(KXMLIOBeatType, beatTypeToString(m_beatGeneratorType));
1✔
1324
    doc->writeAttribute(KXMLIOBeatsPerMinute, QString::number(m_currentBPM));
1✔
1325
    doc->writeEndElement();
1✔
1326

1327
    foreach (Universe *uni, m_universeArray)
5✔
1328
        uni->saveXML(doc);
4✔
1329

1330
    doc->writeEndElement();
1✔
1331

1332
    return true;
1✔
1333
}
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