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

mcallegari / qlcplus / 14452115210

14 Apr 2025 05:40PM UTC coverage: 31.855% (+0.001%) from 31.854%
14452115210

Pull #1708

github

web-flow
Merge 772292420 into 02d6f8913
Pull Request #1708: simplify work with containers

55 of 80 new or added lines in 19 files covered. (68.75%)

3 existing lines in 3 files now uncovered.

14671 of 46056 relevant lines covered (31.85%)

26461.66 hits per line

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

65.78
/engine/src/universe.cpp
1
/*
2
  Q Light Controller Plus
3
  universe.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
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QDebug>
23
#include <math.h>
24

25
#include "channelmodifier.h"
26
#include "inputoutputmap.h"
27
#include "genericfader.h"
28
#include "qlcioplugin.h"
29
#include "outputpatch.h"
30
#include "grandmaster.h"
31
#include "mastertimer.h"
32
#include "inputpatch.h"
33
#include "qlcmacros.h"
34
#include "universe.h"
35
#include "function.h"
36
#include "qlcfile.h"
37
#include "utils.h"
38

39
#define RELATIVE_ZERO_8BIT   0x7F
40
#define RELATIVE_ZERO_16BIT  0x7F00
41

42
#define KXMLUniverseNormalBlend "Normal"
43
#define KXMLUniverseMaskBlend "Mask"
44
#define KXMLUniverseAdditiveBlend "Additive"
45
#define KXMLUniverseSubtractiveBlend "Subtractive"
46

47
Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent)
850✔
48
    : QThread(parent)
49
    , m_id(id)
850✔
50
    , m_grandMaster(gm)
850✔
51
    , m_passthrough(false)
850✔
52
    , m_monitor(false)
850✔
53
    , m_inputPatch(NULL)
850✔
54
    , m_fbPatch(NULL)
850✔
55
    , m_channelsMask(new QByteArray(UNIVERSE_SIZE, char(0)))
850✔
56
    , m_modifiedZeroValues(new QByteArray(UNIVERSE_SIZE, char(0)))
850✔
57
    , m_running(false)
850✔
58
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
59
    , m_fadersMutex(QMutex::Recursive)
60
#endif
61
    , m_usedChannels(0)
850✔
62
    , m_totalChannels(0)
850✔
63
    , m_totalChannelsChanged(false)
850✔
64
    , m_intensityChannelsChanged(false)
850✔
65
    , m_preGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
850✔
66
    , m_postGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
850✔
67
    , m_lastPostGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
850✔
68
    , m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0)))
850✔
69
    , m_passthroughValues()
3,400✔
70
{
71
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
850✔
72

73
    m_name = QString("Universe %1").arg(id + 1);
850✔
74

75
    connect(m_grandMaster, SIGNAL(valueChanged(uchar)),
850✔
76
            this, SLOT(slotGMValueChanged()));
77
}
850✔
78

79
Universe::~Universe()
1,670✔
80
{
81
    if (isRunning() == true)
835✔
82
    {
83
        // isRunning is inconsistent with m_running,
84
        // so double check if the thread is really in the run loop
85
        while (m_running == false)
×
86
            usleep(10000);
×
87

88
        m_running = false;
×
89
        wait(1000);
×
90
    }
91

92
    delete m_inputPatch;
835✔
93
    int opCount = m_outputPatchList.count();
94
    for (int i = 0; i < opCount; i++)
849✔
95
    {
96
        OutputPatch *patch = m_outputPatchList.takeLast();
14✔
97
        delete patch;
14✔
98
    }
99
    delete m_fbPatch;
835✔
100
}
3,340✔
101

102
void Universe::setName(QString name)
7✔
103
{
104
    if (name.isEmpty())
7✔
105
        m_name = QString("Universe %1").arg(m_id + 1);
×
106
    else
107
        m_name = name;
7✔
108
    emit nameChanged();
7✔
109
}
7✔
110

111
QString Universe::name() const
106✔
112
{
113
    return m_name;
106✔
114
}
115

116
void Universe::setID(quint32 id)
2✔
117
{
118
    m_id = id;
2✔
119
}
2✔
120

121
quint32 Universe::id() const
133,583✔
122
{
123
    return m_id;
133,583✔
124
}
125

126
ushort Universe::usedChannels()
73✔
127
{
128
    return m_usedChannels;
73✔
129
}
130

131
ushort Universe::totalChannels()
6✔
132
{
133
    return m_totalChannels;
6✔
134
}
135

136
bool Universe::hasChanged()
10,505,885✔
137
{
138
    bool changed =
139
        memcmp(m_lastPostGMValues->constData(), m_postGMValues->constData(), m_usedChannels) != 0;
10,505,885✔
140
    if (changed)
10,505,885✔
141
        memcpy(m_lastPostGMValues->data(), m_postGMValues->constData(), m_usedChannels);
142
    return changed;
10,505,885✔
143
}
144

145
void Universe::setPassthrough(bool enable)
6✔
146
{
147
    if (enable == m_passthrough)
6✔
148
        return;
149

150
    qDebug() << "Set universe" << id() << "passthrough to" << enable;
151

152
    disconnectInputPatch();
4✔
153

154
    if (enable && m_passthroughValues.isNull())
4✔
155
    {
156
        // When passthrough is disabled, we don't release the array, since it's only ~512 B and
157
        // we would have to synchronize with other threads
158

159
        // When enabling passthrough, make sure the array is allocated BEFORE m_passthrough is set to
160
        // true. That way we only have to check for m_passthrough, and do not need to check
161
        // m_passthroughValues.isNull()
162
        m_passthroughValues.reset(new QByteArray(UNIVERSE_SIZE, char(0)));
4✔
163
    }
164

165
    m_passthrough = enable;
4✔
166

167
    connectInputPatch();
4✔
168

169
    emit passthroughChanged();
4✔
170
}
171

172
bool Universe::passthrough() const
12✔
173
{
174
    return m_passthrough;
12✔
175
}
176

177
void Universe::setMonitor(bool enable)
1✔
178
{
179
    m_monitor = enable;
1✔
180
}
1✔
181

182
bool Universe::monitor() const
1✔
183
{
184
    return m_monitor;
1✔
185
}
186

187
void Universe::slotGMValueChanged()
10,032✔
188
{
189
    {
190
        for (int i = 0; i < m_intensityChannels.size(); ++i)
5,130,071✔
191
        {
192
            int channel = m_intensityChannels.at(i);
5,120,039✔
193
            updatePostGMValue(channel);
5,120,039✔
194
        }
195
    }
196

197
    if (m_grandMaster->channelMode() == GrandMaster::AllChannels)
10,032✔
198
    {
199
        for (int i = 0; i < m_nonIntensityChannels.size(); ++i)
32✔
200
        {
201
            int channel = m_nonIntensityChannels.at(i);
15✔
202
            updatePostGMValue(channel);
15✔
203
        }
204
    }
205
}
10,032✔
206

207
/************************************************************************
208
 * Faders
209
 ************************************************************************/
210

211
QSharedPointer<GenericFader> Universe::requestFader(Universe::FaderPriority priority)
150✔
212
{
213
    int insertPos = 0;
214
    QSharedPointer<GenericFader> fader = QSharedPointer<GenericFader>(new GenericFader());
150✔
215
    fader->setPriority(priority);
150✔
216

217
    {
218
        QMutexLocker fadersLocker(&m_fadersMutex);
150✔
219
        for (int i = m_faders.count() - 1; i >= 0; i--)
150✔
220
        {
221
            const QSharedPointer<GenericFader>& f = m_faders.at(i);
222
            if (!f.isNull() && f->priority() <= fader->priority())
129✔
223
            {
224
                insertPos = i + 1;
129✔
225
                break;
129✔
226
            }
227
        }
228

229
        m_faders.insert(insertPos, fader);
150✔
230

231
        qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority()
232
                 << "registered at pos" << insertPos << ", count" << m_faders.count();
233
    }
234
    return fader;
150✔
235
}
236

237
void Universe::dismissFader(QSharedPointer<GenericFader> fader)
2✔
238
{
239
    QMutexLocker fadersLocker(&m_fadersMutex);
2✔
240
    int index = m_faders.indexOf(fader);
2✔
241
    if (index >= 0)
2✔
242
    {
243
        m_faders.takeAt(index);
4✔
244
        fader.clear();
245
    }
246
}
2✔
247

248
void Universe::requestFaderPriority(QSharedPointer<GenericFader> fader, Universe::FaderPriority priority)
×
249
{
250
    QMutexLocker fadersLocker(&m_fadersMutex);
×
251
    int pos = m_faders.indexOf(fader);
×
252
    int newPos = 0;
253

NEW
254
    if (pos == -1)
×
255
        return;
256

UNCOV
257
    for (int i = m_faders.count() - 1; i >= 0; i--)
×
258
    {
259
        QSharedPointer<GenericFader> f = m_faders.at(i);
260
        if (!f.isNull() && f->priority() <= priority)
×
261
        {
262
            newPos = i;
263
            fader->setPriority(priority);
×
264
            break;
265
        }
266
    }
267

268
    if (newPos != pos)
×
269
    {
270
        m_faders.move(pos, newPos);
271
        qDebug() << "[Universe]" << id() << ": Generic fader moved from" << pos
272
                 << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count();
273
    }
274
}
275

276
QList<QSharedPointer<GenericFader> > Universe::faders()
×
277
{
278
    return m_faders;
×
279
}
280

281
void Universe::setFaderPause(quint32 functionID, bool enable)
8✔
282
{
283
    QMutexLocker fadersLocker(&m_fadersMutex);
8✔
284
    QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
8✔
285
    while (it.hasNext())
17✔
286
    {
287
        QSharedPointer<GenericFader> fader = it.next();
288
        if (fader.isNull() || fader->parentFunctionID() != functionID)
9✔
289
            continue;
290

291
        fader->setPaused(enable);
3✔
292
    }
293
}
8✔
294

295
void Universe::setFaderFadeOut(int fadeTime)
×
296
{
297
    QMutexLocker fadersLocker(&m_fadersMutex);
×
298
    foreach (QSharedPointer<GenericFader> fader, m_faders)
×
299
    {
300
        if (!fader.isNull() && fader->parentFunctionID() != Function::invalidId())
×
301
            fader->setFadeOut(true, uint(fadeTime));
×
302
    }
303
}
×
304

305
void Universe::tick()
688✔
306
{
307
    m_semaphore.release(1);
688✔
308
}
688✔
309

310
void Universe::processFaders()
264,856✔
311
{
312
    flushInput();
264,856✔
313
    zeroIntensityChannels();
264,856✔
314

315
    {
316
        QMutexLocker fadersLocker(&m_fadersMutex);
264,856✔
317
        QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
264,856✔
318
        while (it.hasNext())
1,191,957✔
319
        {
320
            QSharedPointer<GenericFader> fader = it.next(); //m_faders.at(i);
321
            if (fader.isNull())
927,101✔
322
                continue;
×
323

324
            // destroy a fader if it's been requested
325
            // and it's not fading out
326
            if (fader->deleteRequested() && !fader->isFadingOut())
927,101✔
327
            {
328
                fader->removeAll();
4✔
329
                it.remove();
4✔
330
                fader.clear();
331
                continue;
4✔
332
            }
333

334
            if (fader->isEnabled() == false)
927,097✔
335
                continue;
×
336

337
            //qDebug() << "Processing fader" << fader->name() << fader->channelsCount();
338
            fader->write(this);
927,097✔
339
        }
340
    }
341

342
    bool dataChanged = hasChanged();
264,856✔
343
    const QByteArray postGM = m_postGMValues->mid(0, m_usedChannels);
264,856✔
344
    dumpOutput(postGM, dataChanged);
264,856✔
345

346
    if (dataChanged)
264,856✔
347
        emit universeWritten(id(), postGM);
132,939✔
348
}
264,856✔
349

350
void Universe::run()
×
351
{
352
    m_running = true;
×
353
    int timeout = int(MasterTimer::tick()) * 2;
×
354

355
    qDebug() << "Universe thread started" << id();
356

357
    while (m_running)
×
358
    {
359
        if (m_semaphore.tryAcquire(1, timeout) == false)
×
360
        {
361
            //qWarning() << "Semaphore not acquired on universe" << id();
362
            continue;
×
363
        }
364
#if 0
365
        if (m_faders.count())
366
            qDebug() << "<<<<<<<< UNIVERSE TICK - id" << id() << "faders:" << m_faders.count();
367
#endif
368
        processFaders();
×
369
    }
370

371
    qDebug() << "Universe thread stopped" << id();
372
}
×
373

374
/************************************************************************
375
 * Values
376
 ************************************************************************/
377

378
void Universe::reset()
9✔
379
{
380
    m_preGMValues->fill(0);
9✔
381
    m_blackoutValues->fill(0);
9✔
382

383
    if (m_passthrough)
9✔
384
        (*m_postGMValues) = (*m_passthroughValues);
×
385
    else
386
        m_postGMValues->fill(0);
9✔
387

388
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
9✔
389
    m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
9✔
390
}
9✔
391

392
void Universe::reset(int address, int range)
3,099,770✔
393
{
394
    if (address >= UNIVERSE_SIZE)
3,099,770✔
395
        return;
396

397
    if (address + range > UNIVERSE_SIZE)
3,099,770✔
398
       range = UNIVERSE_SIZE - address;
1✔
399

400
    memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
3,099,770✔
401
    memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
3,099,770✔
402
    memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
3,099,770✔
403

404
    applyPassthroughValues(address, range);
3,099,770✔
405
}
406

407
void Universe::applyPassthroughValues(int address, int range)
3,099,770✔
408
{
409
    if (!m_passthrough)
3,099,770✔
410
        return;
411

412
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
413
    {
414
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
415
        {
416
            (*m_postGMValues)[i] = (*m_passthroughValues)[i];
×
417
        }
418
    }
419
}
420

421
void Universe::zeroIntensityChannels()
284,956✔
422
{
423
    updateIntensityChannelsRanges();
284,956✔
424
    int const* channels = m_intensityChannelsRanges.constData();
425
    for (int i = 0; i < m_intensityChannelsRanges.size(); ++i)
3,384,725✔
426
    {
427
        short channel = channels[i] >> 16;
3,099,769✔
428
        short size = channels[i] & 0xffff;
3,099,769✔
429

430
        reset(channel, size);
3,099,769✔
431
    }
432
}
284,956✔
433

434
QHash<int, uchar> Universe::intensityChannels()
1✔
435
{
436
    QHash <int, uchar> intensityList;
437
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1✔
438
    {
439
        int channel = m_intensityChannels.at(i);
×
440
        intensityList[channel] = m_preGMValues->at(channel);
×
441
    }
442
    return intensityList;
1✔
443
}
×
444

445
uchar Universe::postGMValue(int address) const
×
446
{
447
    if (address >= m_postGMValues->size())
×
448
        return 0;
449

450
    return uchar(m_postGMValues->at(address));
×
451
}
452

453
const QByteArray* Universe::postGMValues() const
4,633✔
454
{
455
    return m_postGMValues.data();
4,633✔
456
}
457

458
Universe::BlendMode Universe::stringToBlendMode(QString mode)
5✔
459
{
460
    if (mode == KXMLUniverseNormalBlend)
5✔
461
        return NormalBlend;
462
    else if (mode == KXMLUniverseMaskBlend)
4✔
463
        return MaskBlend;
464
    else if (mode == KXMLUniverseAdditiveBlend)
3✔
465
        return AdditiveBlend;
466
    else if (mode == KXMLUniverseSubtractiveBlend)
2✔
467
        return SubtractiveBlend;
1✔
468

469
    return NormalBlend;
470
}
471

472
QString Universe::blendModeToString(Universe::BlendMode mode)
4✔
473
{
474
    switch(mode)
4✔
475
    {
476
        default:
1✔
477
        case NormalBlend:
478
            return QString(KXMLUniverseNormalBlend);
1✔
479
        break;
480
        case MaskBlend:
1✔
481
            return QString(KXMLUniverseMaskBlend);
1✔
482
        break;
483
        case AdditiveBlend:
1✔
484
            return QString(KXMLUniverseAdditiveBlend);
1✔
485
        break;
486
        case SubtractiveBlend:
1✔
487
            return QString(KXMLUniverseSubtractiveBlend);
1✔
488
        break;
489
    }
490
}
491

492
const QByteArray Universe::preGMValues() const
1,326,130✔
493
{
494
    return *m_preGMValues;
1,326,130✔
495
}
496

497
uchar Universe::preGMValue(int address) const
17,096,600✔
498
{
499
    if (address >= m_preGMValues->size())
17,096,600✔
500
        return 0U;
501

502
    return static_cast<uchar>(m_preGMValues->at(address));
17,096,600✔
503
}
504

505
uchar Universe::applyGM(int channel, uchar value)
17,089,613✔
506
{
507
    if ((m_grandMaster->channelMode() == GrandMaster::Intensity && m_channelsMask->at(channel) & Intensity) ||
23,935,604✔
508
        (m_grandMaster->channelMode() == GrandMaster::AllChannels))
6,845,991✔
509
    {
510
        if (m_grandMaster->valueMode() == GrandMaster::Limit)
10,244,676✔
511
            value = MIN(value, m_grandMaster->value());
792✔
512
        else
513
            value = char(floor((double(value) * m_grandMaster->fraction()) + 0.5));
10,243,884✔
514
    }
515

516
    return value;
17,089,613✔
517
}
518

519
uchar Universe::applyModifiers(int channel, uchar value)
17,095,995✔
520
{
521
    if (m_modifiers.at(channel) != NULL)
17,095,995✔
522
        return m_modifiers.at(channel)->getValue(value);
×
523

524
    return value;
525
}
526

527
uchar Universe::applyPassthrough(int channel, uchar value)
17,095,995✔
528
{
529
    if (m_passthrough)
17,095,995✔
530
    {
531
        const uchar passthroughValue = static_cast<uchar>(m_passthroughValues->at(channel));
×
532
        if (value < passthroughValue) // HTP merge
533
        {
534
            return passthroughValue;
535
        }
536
    }
537

538
    return value;
539
}
540

541
void Universe::updatePostGMValue(int channel)
17,095,995✔
542
{
543
    uchar value = preGMValue(channel);
17,095,995✔
544

545
    if (value != 0)
17,095,995✔
546
        value = applyGM(channel, value);
17,087,053✔
547

548
    value = applyModifiers(channel, value);
17,095,995✔
549
    value = applyPassthrough(channel, value);
17,095,995✔
550

551
    (*m_postGMValues)[channel] = static_cast<char>(value);
17,095,995✔
552
}
17,095,995✔
553

554
/************************************************************************
555
 * Patches
556
 ************************************************************************/
557

558
bool Universe::isPatched()
2✔
559
{
560
    if (m_inputPatch != NULL || m_outputPatchList.count() || m_fbPatch != NULL)
2✔
561
        return true;
1✔
562

563
    return false;
564
}
565

566
bool Universe::setInputPatch(QLCIOPlugin *plugin,
6✔
567
                             quint32 input, QLCInputProfile *profile)
568
{
569
    qDebug() << "[Universe] setInputPatch - ID:" << m_id << ", plugin:" << ((plugin == NULL)?"None":plugin->name())
570
             << ", input:" << input << ", profile:" << ((profile == NULL)?"None":profile->name());
571
    if (m_inputPatch == NULL)
6✔
572
    {
573
        if (plugin == NULL || input == QLCIOPlugin::invalidLine())
5✔
574
            return true;
575

576
        m_inputPatch = new InputPatch(m_id, this);
4✔
577
        connectInputPatch();
4✔
578
    }
579
    else
580
    {
581
        if (input == QLCIOPlugin::invalidLine())
1✔
582
        {
583
            disconnectInputPatch();
×
584
            delete m_inputPatch;
×
585
            m_inputPatch = NULL;
×
586
            emit inputPatchChanged();
×
587
            return true;
×
588
        }
589
    }
590

591
    if (m_inputPatch != NULL)
5✔
592
    {
593
        bool result = m_inputPatch->set(plugin, input, profile);
5✔
594
        emit inputPatchChanged();
5✔
595
        return result;
5✔
596
    }
597

598
    return true;
599
}
600

601
bool Universe::setOutputPatch(QLCIOPlugin *plugin, quint32 output, int index)
19✔
602
{
603
    if (index < 0)
19✔
604
        return false;
605

606
    qDebug() << "[Universe] setOutputPatch - ID:" << m_id
607
             << ", plugin:" << ((plugin == NULL) ? "None" : plugin->name()) << ", output:" << output;
608

609
    // replace or delete an existing patch
610
    if (index < m_outputPatchList.count())
19✔
611
    {
612
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
2✔
613
        {
614
            // need to delete an existing patch
615
            OutputPatch *patch = m_outputPatchList.takeAt(index);
2✔
616
            delete patch;
2✔
617
            emit outputPatchesCountChanged();
2✔
618
            return true;
2✔
619
        }
620

621
        OutputPatch *patch = m_outputPatchList.at(index);
×
622
        bool result = patch->set(plugin, output);
×
623
        emit outputPatchChanged();
×
624
        return result;
×
625
    }
626
    else
627
    {
628
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
17✔
629
            return false;
630

631
        // add a new patch
632
        OutputPatch *patch = new OutputPatch(m_id, this);
16✔
633
        bool result = patch->set(plugin, output);
16✔
634
        m_outputPatchList.append(patch);
16✔
635
        emit outputPatchesCountChanged();
16✔
636
        return result;
16✔
637
    }
638

639
    return false;
640
}
641

642
bool Universe::setFeedbackPatch(QLCIOPlugin *plugin, quint32 output)
×
643
{
644
    qDebug() << Q_FUNC_INFO << "plugin:" << plugin << "output:" << output;
645
    if (m_fbPatch == NULL)
×
646
    {
647
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
648
            return false;
649

650
        m_fbPatch = new OutputPatch(m_id, this);
×
651
    }
652
    else
653
    {
654
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
655
        {
656
            delete m_fbPatch;
×
657
            m_fbPatch = NULL;
×
658
            emit hasFeedbackChanged();
×
659
            return true;
×
660
        }
661
    }
662
    if (m_fbPatch != NULL)
×
663
    {
664
        bool result = m_fbPatch->set(plugin, output);
×
665
        emit hasFeedbackChanged();
×
666
        return result;
×
667
    }
668

669
    return false;
670
}
671

672
bool Universe::hasFeedback() const
×
673
{
674
    return m_fbPatch != NULL ? true : false;
×
675
}
676

677
InputPatch *Universe::inputPatch() const
161✔
678
{
679
    return m_inputPatch;
161✔
680
}
681

682
OutputPatch *Universe::outputPatch(int index) const
48✔
683
{
684
    if (index < 0 || index >= m_outputPatchList.count())
48✔
685
        return NULL;
686

687
    return m_outputPatchList.at(index);
34✔
688
}
689

690
int Universe::outputPatchesCount() const
83✔
691
{
692
    return m_outputPatchList.count();
83✔
693
}
694

695
OutputPatch *Universe::feedbackPatch() const
49✔
696
{
697
    return m_fbPatch;
49✔
698
}
699

700
void Universe::dumpOutput(const QByteArray &data, bool dataChanged)
264,904✔
701
{
702
    if (m_outputPatchList.count() == 0)
264,904✔
703
        return;
704

705
    foreach (OutputPatch *op, m_outputPatchList)
80✔
706
    {
707
        if (m_totalChannelsChanged == true)
40✔
708
            op->setPluginParameter(PLUGIN_UNIVERSECHANNELS, m_totalChannels);
4✔
709

710
        if (op->blackout())
40✔
711
            op->dump(m_id, *m_blackoutValues, dataChanged);
20✔
712
        else
713
            op->dump(m_id, data, dataChanged);
20✔
714
    }
715
    m_totalChannelsChanged = false;
40✔
716
}
717

718
void Universe::flushInput()
264,876✔
719
{
720
    if (m_inputPatch == NULL)
264,876✔
721
        return;
722

723
    m_inputPatch->flush(m_id);
5✔
724
}
725

726
void Universe::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString &key)
×
727
{
728
    if (m_passthrough)
×
729
    {
730
        if (universe == m_id)
×
731
        {
732
            qDebug() << "write" << channel << value;
733

734
            if (channel >= UNIVERSE_SIZE)
×
735
                return;
736

737
            if (channel >= m_usedChannels)
×
738
                m_usedChannels = channel + 1;
×
739

740
            (*m_passthroughValues)[channel] = value;
×
741

742
            updatePostGMValue(channel);
×
743
        }
744
    }
745
    else
746
        emit inputValueChanged(universe, channel, value, key);
×
747
}
748

749
void Universe::connectInputPatch()
8✔
750
{
751
    if (m_inputPatch == NULL)
8✔
752
        return;
753

754
    if (!m_passthrough)
4✔
755
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
4✔
756
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
757
    else
758
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
759
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
760
}
761

762
void Universe::disconnectInputPatch()
4✔
763
{
764
    if (m_inputPatch == NULL)
4✔
765
        return;
766

767
    if (!m_passthrough)
×
768
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
769
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
770
    else
771
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
772
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
773
}
774

775
/************************************************************************
776
 * Channels capabilities
777
 ************************************************************************/
778

779
void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, ChannelType forcedType)
7,221✔
780
{
781
    if (channel >= (ushort)m_channelsMask->length())
7,221✔
782
        return;
783

784
    if (Utils::vectorRemove(m_intensityChannels, channel))
7,221✔
785
        m_intensityChannelsChanged = true;
823✔
786
    Utils::vectorRemove(m_nonIntensityChannels, channel);
7,221✔
787

788
    if (forcedType != Undefined)
7,221✔
789
    {
790
        (*m_channelsMask)[channel] = char(forcedType);
5✔
791
        if ((forcedType & HTP) == HTP)
5✔
792
        {
793
            //qDebug() << "--- Channel" << channel << "forced type HTP";
794
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
3✔
795
            m_intensityChannelsChanged = true;
3✔
796
            if (group == QLCChannel::Intensity)
3✔
797
            {
798
                //qDebug() << "--- Channel" << channel << "Intensity + HTP";
799
                (*m_channelsMask)[channel] = char(HTP | Intensity);
×
800
            }
801
        }
802
        else if ((forcedType & LTP) == LTP)
2✔
803
        {
804
            //qDebug() << "--- Channel" << channel << "forced type LTP";
805
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
2✔
806
        }
807
    }
808
    else
809
    {
810
        if (group == QLCChannel::Intensity)
7,216✔
811
        {
812
            //qDebug() << "--- Channel" << channel << "Intensity + HTP";
813
            (*m_channelsMask)[channel] = char(HTP | Intensity);
5,656✔
814
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
5,656✔
815
            m_intensityChannelsChanged = true;
5,656✔
816
        }
817
        else
818
        {
819
            //qDebug() << "--- Channel" << channel << "LTP";
820
            (*m_channelsMask)[channel] = char(LTP);
1,560✔
821
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
1,560✔
822
        }
823
    }
824

825
    // qDebug() << Q_FUNC_INFO << "Channel:" << channel << "mask:" << QString::number(m_channelsMask->at(channel), 16);
826
    if (channel >= m_totalChannels)
7,221✔
827
    {
828
        m_totalChannels = channel + 1;
5,635✔
829
        m_totalChannelsChanged = true;
5,635✔
830
    }
831
}
832

833
uchar Universe::channelCapabilities(ushort channel)
517✔
834
{
835
    if (channel >= (ushort)m_channelsMask->length())
517✔
836
        return Undefined;
837

838
    return m_channelsMask->at(channel);
517✔
839
}
840

841
void Universe::setChannelDefaultValue(ushort channel, uchar value)
2,572✔
842
{
843
    if (channel >= m_totalChannels)
2,572✔
844
    {
845
        m_totalChannels = channel + 1;
×
846
        m_totalChannelsChanged = true;
×
847
    }
848

849
    if (channel >= m_usedChannels)
2,572✔
850
        m_usedChannels = channel + 1;
992✔
851

852
    (*m_preGMValues)[channel] = value;
2,572✔
853
    updatePostGMValue(channel);
2,572✔
854
}
2,572✔
855

856
void Universe::setChannelModifier(ushort channel, ChannelModifier *modifier)
2,572✔
857
{
858
    if (channel >= (ushort)m_modifiers.count())
2,572✔
859
        return;
860

861
    m_modifiers[channel] = modifier;
2,572✔
862

863
    if (modifier != NULL)
2,572✔
864
    {
865
        (*m_modifiedZeroValues)[channel] = modifier->getValue(0);
×
866

867
        if (channel >= m_totalChannels)
×
868
        {
869
            m_totalChannels = channel + 1;
×
870
            m_totalChannelsChanged = true;
×
871
        }
872

873
        if (channel >= m_usedChannels)
×
874
            m_usedChannels = channel + 1;
×
875
    }
876

877
    updatePostGMValue(channel);
2,572✔
878
}
879

880
ChannelModifier *Universe::channelModifier(ushort channel)
×
881
{
882
    if (channel >= (ushort)m_modifiers.count())
×
883
        return NULL;
884

885
    return m_modifiers.at(channel);
×
886
}
887

888
void Universe::updateIntensityChannelsRanges()
284,956✔
889
{
890
    if (!m_intensityChannelsChanged)
284,956✔
891
        return;
892

893
    m_intensityChannelsChanged = false;
38✔
894

895
    m_intensityChannelsRanges.clear();
38✔
896
    short currentPos = -1;
897
    short currentSize = 0;
898

899
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1,799✔
900
    {
901
        int channel = m_intensityChannels.at(i);
1,761✔
902
        if (currentPos + currentSize == channel)
1,761✔
903
            ++currentSize;
1,179✔
904
        else
905
        {
906
            if (currentPos != -1)
582✔
907
                m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
544✔
908
            currentPos = channel;
582✔
909
            currentSize = 1;
910
        }
911
    }
912
    if (currentPos != -1)
38✔
913
        m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
38✔
914

915
    qDebug() << Q_FUNC_INFO << ":" << m_intensityChannelsRanges.size() << "ranges";
916
}
917

918
/****************************************************************************
919
 * Writing
920
 ****************************************************************************/
921

922
bool Universe::write(int address, uchar value, bool forceLTP)
10,248,379✔
923
{
924
    Q_ASSERT(address < UNIVERSE_SIZE);
925

926
    //qDebug() << "[Universe]" << id() << ": write channel" << address << ", value:" << value;
927

928
    if (address >= m_usedChannels)
10,248,379✔
929
        m_usedChannels = address + 1;
9,371✔
930

931
    if (m_channelsMask->at(address) & HTP)
10,248,379✔
932
    {
933
        if (forceLTP == false && value < (uchar)m_preGMValues->at(address))
5,122,713✔
934
        {
935
            qDebug() << "[Universe] HTP check not passed" << address << value;
936
            return false;
937
        }
938
    }
939
    else
940
    {
941
        // preserve non HTP channels for blackout
942
        (*m_blackoutValues)[address] = char(value);
5,125,666✔
943
    }
944

945
    (*m_preGMValues)[address] = char(value);
10,248,379✔
946

947
    updatePostGMValue(address);
10,248,379✔
948

949
    return true;
10,248,379✔
950
}
951

952
bool Universe::writeMultiple(int address, quint32 value, int channelCount)
1,722,406✔
953
{
954
    for (int i = 0; i < channelCount; i++)
3,444,812✔
955
    {
956
        //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]);
957

958
        // preserve non HTP channels for blackout
959
        if ((m_channelsMask->at(address + i) & HTP) == 0)
1,722,406✔
960
            (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,275✔
961

962
        (*m_preGMValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,406✔
963

964
        updatePostGMValue(address + i);
1,722,406✔
965
    }
966

967
    return true;
1,722,406✔
968
}
969

970
bool Universe::writeRelative(int address, quint32 value, int channelCount)
9✔
971
{
972
    Q_ASSERT(address < UNIVERSE_SIZE);
973

974
    //qDebug() << "Write relative channel" << address << "value" << value;
975

976
    if (address + channelCount >= m_usedChannels)
9✔
977
        m_usedChannels = address + channelCount;
9✔
978

979
    if (channelCount == 1)
9✔
980
    {
981
        short newVal = uchar((*m_preGMValues)[address]);
6✔
982
        newVal += short(value) - RELATIVE_ZERO_8BIT;
6✔
983
        (*m_preGMValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
12✔
984
        (*m_blackoutValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
6✔
985
        updatePostGMValue(address);
6✔
986
    }
987
    else
988
    {
989
        quint32 currentValue = 0;
3✔
990
        for (int i = 0; i < channelCount; i++)
9✔
991
            currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
6✔
992

993
        currentValue = qint32(CLAMP((qint32)currentValue + (qint32)value - RELATIVE_ZERO_16BIT, 0, 0xFFFF));
3✔
994

995
        for (int i = 0; i < channelCount; i++)
9✔
996
        {
997
            (*m_preGMValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
998
            (*m_blackoutValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
999
            updatePostGMValue(address + i);
6✔
1000
        }
1001
    }
1002

1003
    return true;
9✔
1004
}
1005

1006
bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend)
1,722,399✔
1007
{
1008
    if (address + channelCount >= m_usedChannels)
1,722,399✔
1009
        m_usedChannels = address + channelCount;
116✔
1010

1011
    quint32 currentValue = 0;
1012
    for (int i = 0; i < channelCount; i++)
3,444,798✔
1013
        currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
1,722,399✔
1014

1015
    switch (blend)
1,722,399✔
1016
    {
1017
        case NormalBlend:
1,722,393✔
1018
        {
1019
            if ((m_channelsMask->at(address) & HTP) && value < currentValue)
1,722,393✔
1020
            {
1021
                qDebug() << "[Universe] HTP check not passed" << address << value;
1022
                return false;
1023
            }
1024
        }
1025
        break;
1026
        case MaskBlend:
2✔
1027
        {
1028
            if (value)
2✔
1029
            {
1030
                qDebug() << "Current value" << currentValue << "value" << value;
1031
                if (currentValue)
2✔
1032
                    value = float(currentValue) * (float(value) / pow(255.0, channelCount));
1✔
1033
                else
1034
                    value = 0;
1035
            }
1036
        }
1037
        break;
1038
        case AdditiveBlend:
1039
        {
1040
            //qDebug() << "Universe write additive channel" << channel << ", value:" << currVal << "+" << value;
1041
            value = fmin(float(currentValue + value), pow(255.0, channelCount));
1✔
1042
        }
1043
        break;
1✔
1044
        case SubtractiveBlend:
2✔
1045
        {
1046
            if (value >= currentValue)
2✔
1047
                value = 0;
1048
            else
1049
                value = currentValue - value;
1✔
1050
        }
1051
        break;
1052
        default:
1053
            qDebug() << "[Universe] Blend mode not handled. Implement me!" << blend;
1054
            return false;
1055
        break;
1056
    }
1057

1058
    writeMultiple(address, value, channelCount);
1,722,398✔
1059

1060
    return true;
1061
}
1062

1063
/*********************************************************************
1064
 * Load & Save
1065
 *********************************************************************/
1066

1067
bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap)
5✔
1068
{
1069
    if (root.name() != KXMLQLCUniverse)
10✔
1070
    {
1071
        qWarning() << Q_FUNC_INFO << "Universe node not found";
1✔
1072
        return false;
1✔
1073
    }
1074

1075
    int outputIndex = 0;
1076

1077
    QXmlStreamAttributes attrs = root.attributes();
4✔
1078

1079
    if (attrs.hasAttribute(KXMLQLCUniverseName))
4✔
1080
        setName(attrs.value(KXMLQLCUniverseName).toString());
4✔
1081

1082
    if (attrs.hasAttribute(KXMLQLCUniversePassthrough))
4✔
1083
    {
1084
        if (attrs.value(KXMLQLCUniversePassthrough).toString() == KXMLQLCTrue ||
8✔
1085
            attrs.value(KXMLQLCUniversePassthrough).toString() == "1")
5✔
1086
            setPassthrough(true);
2✔
1087
        else
1088
            setPassthrough(false);
1✔
1089
    }
1090
    else
1091
    {
1092
        setPassthrough(false);
1✔
1093
    }
1094

1095
    while (root.readNextStartElement())
4✔
1096
    {
1097
        QXmlStreamAttributes pAttrs = root.attributes();
×
1098

1099
        if (root.name() == KXMLQLCUniverseInputPatch)
×
1100
        {
1101
            QString plugin = KInputNone;
1102
            quint32 inputLine = QLCIOPlugin::invalidLine();
1103
            QString inputUID;
1104
            QString profile = KInputNone;
1105

1106
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1107
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1108
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1109
                inputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1110
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1111
                inputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1112
            if (pAttrs.hasAttribute(KXMLQLCUniverseProfileName))
×
1113
                profile = pAttrs.value(KXMLQLCUniverseProfileName).toString();
×
1114

1115
            // apply the parameters just loaded
1116
            ioMap->setInputPatch(index, plugin, inputUID, inputLine, profile);
×
1117

1118
            QXmlStreamReader::TokenType tType = root.readNext();
×
1119
            if (tType == QXmlStreamReader::Characters)
×
1120
                tType = root.readNext();
×
1121

1122
            // check if there is a PluginParameters tag defined
1123
            if (tType == QXmlStreamReader::StartElement)
×
1124
            {
1125
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1126
                    loadXMLPluginParameters(root, InputPatchTag, 0);
×
1127
                root.skipCurrentElement();
×
1128
            }
1129
        }
×
1130
        else if (root.name() == KXMLQLCUniverseOutputPatch)
×
1131
        {
1132
            QString plugin = KOutputNone;
1133
            QString outputUID;
1134
            quint32 outputLine = QLCIOPlugin::invalidLine();
1135

1136
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1137
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1138
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1139
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1140
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1141
                outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1142

1143
            // apply the parameters just loaded
1144
            ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false, outputIndex);
×
1145

1146
            QXmlStreamReader::TokenType tType = root.readNext();
×
1147
            if (tType == QXmlStreamReader::Characters)
×
1148
                tType = root.readNext();
×
1149

1150
            // check if there is a PluginParameters tag defined
1151
            if (tType == QXmlStreamReader::StartElement)
×
1152
            {
1153
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1154
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
1155
                root.skipCurrentElement();
×
1156
            }
1157

1158
            outputIndex++;
×
1159
        }
×
1160
        else if (root.name() == KXMLQLCUniverseFeedbackPatch)
×
1161
        {
1162
            QString plugin = KOutputNone;
1163
            QString outputUID;
1164
            quint32 output = QLCIOPlugin::invalidLine();
1165

1166
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1167
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1168
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1169
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1170
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1171
                output = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1172

1173
            // apply the parameters just loaded
1174
            ioMap->setOutputPatch(index, plugin, outputUID, output, true);
×
1175

1176
            QXmlStreamReader::TokenType tType = root.readNext();
×
1177
            if (tType == QXmlStreamReader::Characters)
×
1178
                tType = root.readNext();
×
1179

1180
            // check if there is a PluginParameters tag defined
1181
            if (tType == QXmlStreamReader::StartElement)
×
1182
            {
1183
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1184
                    loadXMLPluginParameters(root, FeedbackPatchTag, 0);
×
1185
                root.skipCurrentElement();
×
1186
            }
1187
        }
×
1188
        else
1189
        {
1190
            qWarning() << Q_FUNC_INFO << "Unknown Universe tag:" << root.name();
×
1191
            root.skipCurrentElement();
×
1192
        }
1193
    }
1194

1195
    return true;
1196
}
1197

1198
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1199
{
1200
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1201
    {
1202
        qWarning() << Q_FUNC_INFO << "PluginParameters node not found";
×
1203
        return false;
×
1204
    }
1205

1206
    QXmlStreamAttributes pluginAttrs = root.attributes();
×
1207
    for (int i = 0; i < pluginAttrs.count(); i++)
×
1208
    {
1209
        QXmlStreamAttribute attr = pluginAttrs.at(i);
×
1210
        if (currentTag == InputPatchTag)
×
1211
        {
1212
            InputPatch *ip = inputPatch();
×
1213
            if (ip != NULL)
×
1214
                ip->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1215
        }
1216
        else if (currentTag == OutputPatchTag)
×
1217
        {
1218
            OutputPatch *op = outputPatch(patchIndex);
×
1219
            if (op != NULL)
×
1220
                op->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1221
        }
1222
        else if (currentTag == FeedbackPatchTag)
×
1223
        {
1224
            OutputPatch *fbp = feedbackPatch();
×
1225
            if (fbp != NULL)
×
1226
                fbp->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1227
        }
1228
    }
×
1229
    root.skipCurrentElement();
×
1230

1231
    return true;
1232
}
1233

1234
bool Universe::saveXML(QXmlStreamWriter *doc) const
6✔
1235
{
1236
    Q_ASSERT(doc != NULL);
1237

1238
    doc->writeStartElement(KXMLQLCUniverse);
6✔
1239
    doc->writeAttribute(KXMLQLCUniverseName, name());
6✔
1240
    doc->writeAttribute(KXMLQLCUniverseID, QString::number(id()));
6✔
1241

1242
    if (passthrough() == true)
6✔
1243
        doc->writeAttribute(KXMLQLCUniversePassthrough, KXMLQLCTrue);
1✔
1244

1245
    if (inputPatch() != NULL)
6✔
1246
    {
1247
        savePatchXML(doc, KXMLQLCUniverseInputPatch, inputPatch()->pluginName(), inputPatch()->inputName(),
×
1248
            inputPatch()->input(), inputPatch()->profileName(), inputPatch()->getPluginParameters());
×
1249
    }
1250
    foreach (OutputPatch *op, m_outputPatchList)
6✔
1251
    {
1252
        savePatchXML(doc, KXMLQLCUniverseOutputPatch, op->pluginName(), op->outputName(),
×
1253
            op->output(), "", op->getPluginParameters());
×
1254
    }
1255
    if (feedbackPatch() != NULL)
6✔
1256
    {
1257
        savePatchXML(doc, KXMLQLCUniverseFeedbackPatch, feedbackPatch()->pluginName(), feedbackPatch()->outputName(),
×
1258
            feedbackPatch()->output(), "", feedbackPatch()->getPluginParameters());
×
1259
    }
1260

1261
    /* End the <Universe> tag */
1262
    doc->writeEndElement();
6✔
1263

1264
    return true;
6✔
1265
}
1266

1267
void Universe::savePatchXML(
×
1268
    QXmlStreamWriter *doc,
1269
    const QString &tag,
1270
    const QString &pluginName,
1271
    const QString &lineName,
1272
    quint32 line,
1273
    QString profileName,
1274
    QMap<QString, QVariant> parameters) const
1275
{
1276
    // sanity check: don't save invalid data
1277
    if (pluginName.isEmpty() || pluginName == KInputNone || line == QLCIOPlugin::invalidLine())
×
1278
        return;
1279

1280
    doc->writeStartElement(tag);
×
1281
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1282
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineName);
×
1283
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
1284
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1285
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1286

1287
    savePluginParametersXML(doc, parameters);
×
1288
    doc->writeEndElement();
×
1289
}
1290

1291
bool Universe::savePluginParametersXML(QXmlStreamWriter *doc,
×
1292
                                       QMap<QString, QVariant> parameters) const
1293
{
1294
    Q_ASSERT(doc != NULL);
1295

1296
    if (parameters.isEmpty())
×
1297
        return false;
1298

1299
    doc->writeStartElement(KXMLQLCUniversePluginParameters);
×
1300
    QMapIterator<QString, QVariant> it(parameters);
×
1301
    while (it.hasNext())
×
1302
    {
1303
        it.next();
×
1304
        QString pName = it.key();
1305
        QVariant pValue = it.value();
×
1306
        doc->writeAttribute(pName, pValue.toString());
×
1307
    }
×
1308
    doc->writeEndElement();
×
1309

1310
    return true;
1311
}
1312

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