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

mcallegari / qlcplus / 13633248611

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

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

65.96
/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
        if (m_faders.isEmpty())
150✔
220
        {
221
            m_faders.append(fader);
21✔
222
        }
223
        else
224
        {
225
            for (int i = m_faders.count() - 1; i >= 0; i--)
129✔
226
            {
227
                QSharedPointer<GenericFader> f = m_faders.at(i);
228
                if (!f.isNull() && f->priority() <= fader->priority())
129✔
229
                {
230
                    insertPos = i + 1;
129✔
231
                    break;
232
                }
233
            }
234

235
            m_faders.insert(insertPos, fader);
129✔
236
        }
237

238
        qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority()
239
                 << "registered at pos" << insertPos << ", count" << m_faders.count();
240
    }
241
    return fader;
150✔
242
}
243

244
void Universe::dismissFader(QSharedPointer<GenericFader> fader)
2✔
245
{
246
    QMutexLocker fadersLocker(&m_fadersMutex);
2✔
247
    int index = m_faders.indexOf(fader);
2✔
248
    if (index >= 0)
2✔
249
    {
250
        m_faders.takeAt(index);
4✔
251
        fader.clear();
252
    }
253
}
2✔
254

255
void Universe::requestFaderPriority(QSharedPointer<GenericFader> fader, Universe::FaderPriority priority)
×
256
{
257
    QMutexLocker fadersLocker(&m_fadersMutex);
×
258
    if (m_faders.contains(fader) == false)
×
259
        return;
260

261
    int pos = m_faders.indexOf(fader);
262
    int newPos = 0;
263

264
    for (int i = m_faders.count() - 1; i >= 0; i--)
×
265
    {
266
        QSharedPointer<GenericFader> f = m_faders.at(i);
267
        if (!f.isNull() && f->priority() <= priority)
×
268
        {
269
            newPos = i;
270
            fader->setPriority(priority);
×
271
            break;
272
        }
273
    }
274

275
    if (newPos != pos)
×
276
    {
277
        m_faders.move(pos, newPos);
278
        qDebug() << "[Universe]" << id() << ": Generic fader moved from" << pos
279
                 << "to" << m_faders.indexOf(fader) << ". Count:" << m_faders.count();
280
    }
281
}
282

283
QList<QSharedPointer<GenericFader> > Universe::faders()
×
284
{
285
    return m_faders;
×
286
}
287

288
void Universe::setFaderPause(quint32 functionID, bool enable)
8✔
289
{
290
    QMutexLocker fadersLocker(&m_fadersMutex);
8✔
291
    QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
8✔
292
    while (it.hasNext())
17✔
293
    {
294
        QSharedPointer<GenericFader> fader = it.next();
295
        if (fader.isNull() || fader->parentFunctionID() != functionID)
9✔
296
            continue;
297

298
        fader->setPaused(enable);
3✔
299
    }
300
}
8✔
301

302
void Universe::setFaderFadeOut(int fadeTime)
×
303
{
304
    QMutexLocker fadersLocker(&m_fadersMutex);
×
305
    foreach (QSharedPointer<GenericFader> fader, m_faders)
×
306
    {
307
        if (!fader.isNull() && fader->parentFunctionID() != Function::invalidId())
×
308
            fader->setFadeOut(true, uint(fadeTime));
×
309
    }
310
}
×
311

312
void Universe::tick()
688✔
313
{
314
    m_semaphore.release(1);
688✔
315
}
688✔
316

317
void Universe::processFaders()
264,856✔
318
{
319
    flushInput();
264,856✔
320
    zeroIntensityChannels();
264,856✔
321

322
    {
323
        QMutexLocker fadersLocker(&m_fadersMutex);
264,856✔
324
        QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
264,856✔
325
        while (it.hasNext())
1,191,957✔
326
        {
327
            QSharedPointer<GenericFader> fader = it.next(); //m_faders.at(i);
328
            if (fader.isNull())
927,101✔
329
                continue;
×
330

331
            // destroy a fader if it's been requested
332
            // and it's not fading out
333
            if (fader->deleteRequested() && !fader->isFadingOut())
927,101✔
334
            {
335
                fader->removeAll();
4✔
336
                it.remove();
4✔
337
                fader.clear();
338
                continue;
4✔
339
            }
340

341
            if (fader->isEnabled() == false)
927,097✔
342
                continue;
×
343

344
            //qDebug() << "Processing fader" << fader->name() << fader->channelsCount();
345
            fader->write(this);
927,097✔
346
        }
347
    }
348

349
    bool dataChanged = hasChanged();
264,856✔
350
    const QByteArray postGM = m_postGMValues->mid(0, m_usedChannels);
264,856✔
351
    dumpOutput(postGM, dataChanged);
264,856✔
352

353
    if (dataChanged)
264,856✔
354
        emit universeWritten(id(), postGM);
132,939✔
355
}
264,856✔
356

357
void Universe::run()
×
358
{
359
    m_running = true;
×
360
    int timeout = int(MasterTimer::tick()) * 2;
×
361

362
    qDebug() << "Universe thread started" << id();
363

364
    while (m_running)
×
365
    {
366
        if (m_semaphore.tryAcquire(1, timeout) == false)
×
367
        {
368
            //qWarning() << "Semaphore not acquired on universe" << id();
369
            continue;
×
370
        }
371
#if 0
372
        if (m_faders.count())
373
            qDebug() << "<<<<<<<< UNIVERSE TICK - id" << id() << "faders:" << m_faders.count();
374
#endif
375
        processFaders();
×
376
    }
377

378
    qDebug() << "Universe thread stopped" << id();
379
}
×
380

381
/************************************************************************
382
 * Values
383
 ************************************************************************/
384

385
void Universe::reset()
9✔
386
{
387
    m_preGMValues->fill(0);
9✔
388
    m_blackoutValues->fill(0);
9✔
389

390
    if (m_passthrough)
9✔
391
        (*m_postGMValues) = (*m_passthroughValues);
×
392
    else
393
        m_postGMValues->fill(0);
9✔
394

395
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
9✔
396
    m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
9✔
397
}
9✔
398

399
void Universe::reset(int address, int range)
3,099,770✔
400
{
401
    if (address >= UNIVERSE_SIZE)
3,099,770✔
402
        return;
403

404
    if (address + range > UNIVERSE_SIZE)
3,099,770✔
405
       range = UNIVERSE_SIZE - address;
1✔
406

407
    memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
3,099,770✔
408
    memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
3,099,770✔
409
    memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
3,099,770✔
410

411
    applyPassthroughValues(address, range);
3,099,770✔
412
}
413

414
void Universe::applyPassthroughValues(int address, int range)
3,099,770✔
415
{
416
    if (!m_passthrough)
3,099,770✔
417
        return;
418

419
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
420
    {
421
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
422
        {
423
            (*m_postGMValues)[i] = (*m_passthroughValues)[i];
×
424
        }
425
    }
426
}
427

428
void Universe::zeroIntensityChannels()
284,956✔
429
{
430
    updateIntensityChannelsRanges();
284,956✔
431
    int const* channels = m_intensityChannelsRanges.constData();
432
    for (int i = 0; i < m_intensityChannelsRanges.size(); ++i)
3,384,725✔
433
    {
434
        short channel = channels[i] >> 16;
3,099,769✔
435
        short size = channels[i] & 0xffff;
3,099,769✔
436

437
        reset(channel, size);
3,099,769✔
438
    }
439
}
284,956✔
440

441
QHash<int, uchar> Universe::intensityChannels()
1✔
442
{
443
    QHash <int, uchar> intensityList;
444
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1✔
445
    {
446
        int channel = m_intensityChannels.at(i);
×
447
        intensityList[channel] = m_preGMValues->at(channel);
×
448
    }
449
    return intensityList;
1✔
450
}
×
451

452
uchar Universe::postGMValue(int address) const
×
453
{
454
    if (address >= m_postGMValues->size())
×
455
        return 0;
456

457
    return uchar(m_postGMValues->at(address));
×
458
}
459

460
const QByteArray* Universe::postGMValues() const
4,633✔
461
{
462
    return m_postGMValues.data();
4,633✔
463
}
464

465
Universe::BlendMode Universe::stringToBlendMode(QString mode)
5✔
466
{
467
    if (mode == KXMLUniverseNormalBlend)
5✔
468
        return NormalBlend;
469
    else if (mode == KXMLUniverseMaskBlend)
4✔
470
        return MaskBlend;
471
    else if (mode == KXMLUniverseAdditiveBlend)
3✔
472
        return AdditiveBlend;
473
    else if (mode == KXMLUniverseSubtractiveBlend)
2✔
474
        return SubtractiveBlend;
1✔
475

476
    return NormalBlend;
477
}
478

479
QString Universe::blendModeToString(Universe::BlendMode mode)
4✔
480
{
481
    switch(mode)
4✔
482
    {
483
        default:
1✔
484
        case NormalBlend:
485
            return QString(KXMLUniverseNormalBlend);
1✔
486
        break;
487
        case MaskBlend:
1✔
488
            return QString(KXMLUniverseMaskBlend);
1✔
489
        break;
490
        case AdditiveBlend:
1✔
491
            return QString(KXMLUniverseAdditiveBlend);
1✔
492
        break;
493
        case SubtractiveBlend:
1✔
494
            return QString(KXMLUniverseSubtractiveBlend);
1✔
495
        break;
496
    }
497
}
498

499
const QByteArray Universe::preGMValues() const
1,326,130✔
500
{
501
    return *m_preGMValues;
1,326,130✔
502
}
503

504
uchar Universe::preGMValue(int address) const
17,096,600✔
505
{
506
    if (address >= m_preGMValues->size())
17,096,600✔
507
        return 0U;
508

509
    return static_cast<uchar>(m_preGMValues->at(address));
17,096,600✔
510
}
511

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

523
    return value;
17,089,613✔
524
}
525

526
uchar Universe::applyModifiers(int channel, uchar value)
17,095,995✔
527
{
528
    if (m_modifiers.at(channel) != NULL)
17,095,995✔
529
        return m_modifiers.at(channel)->getValue(value);
×
530

531
    return value;
532
}
533

534
uchar Universe::applyPassthrough(int channel, uchar value)
17,095,995✔
535
{
536
    if (m_passthrough)
17,095,995✔
537
    {
538
        const uchar passthroughValue = static_cast<uchar>(m_passthroughValues->at(channel));
×
539
        if (value < passthroughValue) // HTP merge
540
        {
541
            return passthroughValue;
542
        }
543
    }
544

545
    return value;
546
}
547

548
void Universe::updatePostGMValue(int channel)
17,095,995✔
549
{
550
    uchar value = preGMValue(channel);
17,095,995✔
551

552
    if (value != 0)
17,095,995✔
553
        value = applyGM(channel, value);
17,087,053✔
554

555
    value = applyModifiers(channel, value);
17,095,995✔
556
    value = applyPassthrough(channel, value);
17,095,995✔
557

558
    (*m_postGMValues)[channel] = static_cast<char>(value);
17,095,995✔
559
}
17,095,995✔
560

561
/************************************************************************
562
 * Patches
563
 ************************************************************************/
564

565
bool Universe::isPatched()
2✔
566
{
567
    if (m_inputPatch != NULL || m_outputPatchList.count() || m_fbPatch != NULL)
2✔
568
        return true;
1✔
569

570
    return false;
571
}
572

573
bool Universe::setInputPatch(QLCIOPlugin *plugin,
6✔
574
                             quint32 input, QLCInputProfile *profile)
575
{
576
    qDebug() << "[Universe] setInputPatch - ID:" << m_id << ", plugin:" << ((plugin == NULL)?"None":plugin->name())
577
             << ", input:" << input << ", profile:" << ((profile == NULL)?"None":profile->name());
578
    if (m_inputPatch == NULL)
6✔
579
    {
580
        if (plugin == NULL || input == QLCIOPlugin::invalidLine())
5✔
581
            return true;
582

583
        m_inputPatch = new InputPatch(m_id, this);
4✔
584
        connectInputPatch();
4✔
585
    }
586
    else
587
    {
588
        if (input == QLCIOPlugin::invalidLine())
1✔
589
        {
590
            disconnectInputPatch();
×
591
            delete m_inputPatch;
×
592
            m_inputPatch = NULL;
×
593
            emit inputPatchChanged();
×
594
            return true;
×
595
        }
596
    }
597

598
    if (m_inputPatch != NULL)
5✔
599
    {
600
        bool result = m_inputPatch->set(plugin, input, profile);
5✔
601
        emit inputPatchChanged();
5✔
602
        return result;
5✔
603
    }
604

605
    return true;
606
}
607

608
bool Universe::setOutputPatch(QLCIOPlugin *plugin, quint32 output, int index)
19✔
609
{
610
    if (index < 0)
19✔
611
        return false;
612

613
    qDebug() << "[Universe] setOutputPatch - ID:" << m_id
614
             << ", plugin:" << ((plugin == NULL) ? "None" : plugin->name()) << ", output:" << output;
615

616
    // replace or delete an existing patch
617
    if (index < m_outputPatchList.count())
19✔
618
    {
619
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
2✔
620
        {
621
            // need to delete an existing patch
622
            OutputPatch *patch = m_outputPatchList.takeAt(index);
2✔
623
            delete patch;
2✔
624
            emit outputPatchesCountChanged();
2✔
625
            return true;
2✔
626
        }
627

628
        OutputPatch *patch = m_outputPatchList.at(index);
×
629
        bool result = patch->set(plugin, output);
×
630
        emit outputPatchChanged();
×
631
        return result;
×
632
    }
633
    else
634
    {
635
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
17✔
636
            return false;
637

638
        // add a new patch
639
        OutputPatch *patch = new OutputPatch(m_id, this);
16✔
640
        bool result = patch->set(plugin, output);
16✔
641
        m_outputPatchList.append(patch);
16✔
642
        emit outputPatchesCountChanged();
16✔
643
        return result;
16✔
644
    }
645

646
    return false;
647
}
648

649
bool Universe::setFeedbackPatch(QLCIOPlugin *plugin, quint32 output)
×
650
{
651
    qDebug() << Q_FUNC_INFO << "plugin:" << plugin << "output:" << output;
652
    if (m_fbPatch == NULL)
×
653
    {
654
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
655
            return false;
656

657
        m_fbPatch = new OutputPatch(m_id, this);
×
658
    }
659
    else
660
    {
661
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
662
        {
663
            delete m_fbPatch;
×
664
            m_fbPatch = NULL;
×
665
            emit hasFeedbackChanged();
×
666
            return true;
×
667
        }
668
    }
669
    if (m_fbPatch != NULL)
×
670
    {
671
        bool result = m_fbPatch->set(plugin, output);
×
672
        emit hasFeedbackChanged();
×
673
        return result;
×
674
    }
675

676
    return false;
677
}
678

679
bool Universe::hasFeedback() const
×
680
{
681
    return m_fbPatch != NULL ? true : false;
×
682
}
683

684
InputPatch *Universe::inputPatch() const
161✔
685
{
686
    return m_inputPatch;
161✔
687
}
688

689
OutputPatch *Universe::outputPatch(int index) const
48✔
690
{
691
    if (index < 0 || index >= m_outputPatchList.count())
48✔
692
        return NULL;
693

694
    return m_outputPatchList.at(index);
34✔
695
}
696

697
int Universe::outputPatchesCount() const
83✔
698
{
699
    return m_outputPatchList.count();
83✔
700
}
701

702
OutputPatch *Universe::feedbackPatch() const
49✔
703
{
704
    return m_fbPatch;
49✔
705
}
706

707
void Universe::dumpOutput(const QByteArray &data, bool dataChanged)
264,904✔
708
{
709
    if (m_outputPatchList.count() == 0)
264,904✔
710
        return;
711

712
    foreach (OutputPatch *op, m_outputPatchList)
80✔
713
    {
714
        if (m_totalChannelsChanged == true)
40✔
715
            op->setPluginParameter(PLUGIN_UNIVERSECHANNELS, m_totalChannels);
4✔
716

717
        if (op->blackout())
40✔
718
            op->dump(m_id, *m_blackoutValues, dataChanged);
20✔
719
        else
720
            op->dump(m_id, data, dataChanged);
20✔
721
    }
722
    m_totalChannelsChanged = false;
40✔
723
}
724

725
void Universe::flushInput()
264,876✔
726
{
727
    if (m_inputPatch == NULL)
264,876✔
728
        return;
729

730
    m_inputPatch->flush(m_id);
5✔
731
}
732

733
void Universe::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString &key)
×
734
{
735
    if (m_passthrough)
×
736
    {
737
        if (universe == m_id)
×
738
        {
739
            qDebug() << "write" << channel << value;
740

741
            if (channel >= UNIVERSE_SIZE)
×
742
                return;
743

744
            if (channel >= m_usedChannels)
×
745
                m_usedChannels = channel + 1;
×
746

747
            (*m_passthroughValues)[channel] = value;
×
748

749
            updatePostGMValue(channel);
×
750
        }
751
    }
752
    else
753
        emit inputValueChanged(universe, channel, value, key);
×
754
}
755

756
void Universe::connectInputPatch()
8✔
757
{
758
    if (m_inputPatch == NULL)
8✔
759
        return;
760

761
    if (!m_passthrough)
4✔
762
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
4✔
763
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
764
    else
765
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
766
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
767
}
768

769
void Universe::disconnectInputPatch()
4✔
770
{
771
    if (m_inputPatch == NULL)
4✔
772
        return;
773

774
    if (!m_passthrough)
×
775
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
776
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
777
    else
778
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
779
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
780
}
781

782
/************************************************************************
783
 * Channels capabilities
784
 ************************************************************************/
785

786
void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, ChannelType forcedType)
7,221✔
787
{
788
    if (channel >= (ushort)m_channelsMask->length())
7,221✔
789
        return;
790

791
    if (Utils::vectorRemove(m_intensityChannels, channel))
7,221✔
792
        m_intensityChannelsChanged = true;
823✔
793
    Utils::vectorRemove(m_nonIntensityChannels, channel);
7,221✔
794

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

832
    // qDebug() << Q_FUNC_INFO << "Channel:" << channel << "mask:" << QString::number(m_channelsMask->at(channel), 16);
833
    if (channel >= m_totalChannels)
7,221✔
834
    {
835
        m_totalChannels = channel + 1;
5,635✔
836
        m_totalChannelsChanged = true;
5,635✔
837
    }
838
}
839

840
uchar Universe::channelCapabilities(ushort channel)
517✔
841
{
842
    if (channel >= (ushort)m_channelsMask->length())
517✔
843
        return Undefined;
844

845
    return m_channelsMask->at(channel);
517✔
846
}
847

848
void Universe::setChannelDefaultValue(ushort channel, uchar value)
2,572✔
849
{
850
    if (channel >= m_totalChannels)
2,572✔
851
    {
852
        m_totalChannels = channel + 1;
×
853
        m_totalChannelsChanged = true;
×
854
    }
855

856
    if (channel >= m_usedChannels)
2,572✔
857
        m_usedChannels = channel + 1;
992✔
858

859
    (*m_preGMValues)[channel] = value;
2,572✔
860
    updatePostGMValue(channel);
2,572✔
861
}
2,572✔
862

863
void Universe::setChannelModifier(ushort channel, ChannelModifier *modifier)
2,572✔
864
{
865
    if (channel >= (ushort)m_modifiers.count())
2,572✔
866
        return;
867

868
    m_modifiers[channel] = modifier;
2,572✔
869

870
    if (modifier != NULL)
2,572✔
871
    {
872
        (*m_modifiedZeroValues)[channel] = modifier->getValue(0);
×
873

874
        if (channel >= m_totalChannels)
×
875
        {
876
            m_totalChannels = channel + 1;
×
877
            m_totalChannelsChanged = true;
×
878
        }
879

880
        if (channel >= m_usedChannels)
×
881
            m_usedChannels = channel + 1;
×
882
    }
883

884
    updatePostGMValue(channel);
2,572✔
885
}
886

887
ChannelModifier *Universe::channelModifier(ushort channel)
×
888
{
889
    if (channel >= (ushort)m_modifiers.count())
×
890
        return NULL;
891

892
    return m_modifiers.at(channel);
×
893
}
894

895
void Universe::updateIntensityChannelsRanges()
284,956✔
896
{
897
    if (!m_intensityChannelsChanged)
284,956✔
898
        return;
899

900
    m_intensityChannelsChanged = false;
38✔
901

902
    m_intensityChannelsRanges.clear();
38✔
903
    short currentPos = -1;
904
    short currentSize = 0;
905

906
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1,799✔
907
    {
908
        int channel = m_intensityChannels.at(i);
1,761✔
909
        if (currentPos + currentSize == channel)
1,761✔
910
            ++currentSize;
1,179✔
911
        else
912
        {
913
            if (currentPos != -1)
582✔
914
                m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
544✔
915
            currentPos = channel;
582✔
916
            currentSize = 1;
917
        }
918
    }
919
    if (currentPos != -1)
38✔
920
        m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
38✔
921

922
    qDebug() << Q_FUNC_INFO << ":" << m_intensityChannelsRanges.size() << "ranges";
923
}
924

925
/****************************************************************************
926
 * Writing
927
 ****************************************************************************/
928

929
bool Universe::write(int address, uchar value, bool forceLTP)
10,248,379✔
930
{
931
    Q_ASSERT(address < UNIVERSE_SIZE);
932

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

935
    if (address >= m_usedChannels)
10,248,379✔
936
        m_usedChannels = address + 1;
9,371✔
937

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

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

954
    updatePostGMValue(address);
10,248,379✔
955

956
    return true;
10,248,379✔
957
}
958

959
bool Universe::writeMultiple(int address, quint32 value, int channelCount)
1,722,406✔
960
{
961
    for (int i = 0; i < channelCount; i++)
3,444,812✔
962
    {
963
        //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]);
964

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

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

971
        updatePostGMValue(address + i);
1,722,406✔
972
    }
973

974
    return true;
1,722,406✔
975
}
976

977
bool Universe::writeRelative(int address, quint32 value, int channelCount)
9✔
978
{
979
    Q_ASSERT(address < UNIVERSE_SIZE);
980

981
    //qDebug() << "Write relative channel" << address << "value" << value;
982

983
    if (address + channelCount >= m_usedChannels)
9✔
984
        m_usedChannels = address + channelCount;
9✔
985

986
    if (channelCount == 1)
9✔
987
    {
988
        short newVal = uchar((*m_preGMValues)[address]);
6✔
989
        newVal += short(value) - RELATIVE_ZERO_8BIT;
6✔
990
        (*m_preGMValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
12✔
991
        (*m_blackoutValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
6✔
992
        updatePostGMValue(address);
6✔
993
    }
994
    else
995
    {
996
        quint32 currentValue = 0;
3✔
997
        for (int i = 0; i < channelCount; i++)
9✔
998
            currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
6✔
999

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

1002
        for (int i = 0; i < channelCount; i++)
9✔
1003
        {
1004
            (*m_preGMValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
1005
            (*m_blackoutValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
6✔
1006
            updatePostGMValue(address + i);
6✔
1007
        }
1008
    }
1009

1010
    return true;
9✔
1011
}
1012

1013
bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend)
1,722,399✔
1014
{
1015
    if (address + channelCount >= m_usedChannels)
1,722,399✔
1016
        m_usedChannels = address + channelCount;
116✔
1017

1018
    quint32 currentValue = 0;
1019
    for (int i = 0; i < channelCount; i++)
3,444,798✔
1020
        currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
1,722,399✔
1021

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

1065
    writeMultiple(address, value, channelCount);
1,722,398✔
1066

1067
    return true;
1068
}
1069

1070
/*********************************************************************
1071
 * Load & Save
1072
 *********************************************************************/
1073

1074
bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap)
5✔
1075
{
1076
    if (root.name() != KXMLQLCUniverse)
10✔
1077
    {
1078
        qWarning() << Q_FUNC_INFO << "Universe node not found";
1✔
1079
        return false;
1✔
1080
    }
1081

1082
    int outputIndex = 0;
1083

1084
    QXmlStreamAttributes attrs = root.attributes();
4✔
1085

1086
    if (attrs.hasAttribute(KXMLQLCUniverseName))
4✔
1087
        setName(attrs.value(KXMLQLCUniverseName).toString());
4✔
1088

1089
    if (attrs.hasAttribute(KXMLQLCUniversePassthrough))
4✔
1090
    {
1091
        if (attrs.value(KXMLQLCUniversePassthrough).toString() == KXMLQLCTrue ||
8✔
1092
            attrs.value(KXMLQLCUniversePassthrough).toString() == "1")
5✔
1093
            setPassthrough(true);
2✔
1094
        else
1095
            setPassthrough(false);
1✔
1096
    }
1097
    else
1098
    {
1099
        setPassthrough(false);
1✔
1100
    }
1101

1102
    while (root.readNextStartElement())
4✔
1103
    {
1104
        QXmlStreamAttributes pAttrs = root.attributes();
×
1105

1106
        if (root.name() == KXMLQLCUniverseInputPatch)
×
1107
        {
1108
            QString plugin = KInputNone;
1109
            quint32 inputLine = QLCIOPlugin::invalidLine();
1110
            QString inputUID;
1111
            QString profile = KInputNone;
1112

1113
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1114
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1115
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1116
                inputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1117
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1118
                inputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1119
            if (pAttrs.hasAttribute(KXMLQLCUniverseProfileName))
×
1120
                profile = pAttrs.value(KXMLQLCUniverseProfileName).toString();
×
1121

1122
            // apply the parameters just loaded
1123
            ioMap->setInputPatch(index, plugin, inputUID, inputLine, profile);
×
1124

1125
            QXmlStreamReader::TokenType tType = root.readNext();
×
1126
            if (tType == QXmlStreamReader::Characters)
×
1127
                tType = root.readNext();
×
1128

1129
            // check if there is a PluginParameters tag defined
1130
            if (tType == QXmlStreamReader::StartElement)
×
1131
            {
1132
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1133
                    loadXMLPluginParameters(root, InputPatchTag, 0);
×
1134
                root.skipCurrentElement();
×
1135
            }
1136
        }
×
1137
        else if (root.name() == KXMLQLCUniverseOutputPatch)
×
1138
        {
1139
            QString plugin = KOutputNone;
1140
            QString outputUID;
1141
            quint32 outputLine = QLCIOPlugin::invalidLine();
1142

1143
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1144
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1145
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1146
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1147
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1148
                outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1149

1150
            // apply the parameters just loaded
1151
            ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false, outputIndex);
×
1152

1153
            QXmlStreamReader::TokenType tType = root.readNext();
×
1154
            if (tType == QXmlStreamReader::Characters)
×
1155
                tType = root.readNext();
×
1156

1157
            // check if there is a PluginParameters tag defined
1158
            if (tType == QXmlStreamReader::StartElement)
×
1159
            {
1160
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1161
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
1162
                root.skipCurrentElement();
×
1163
            }
1164

1165
            outputIndex++;
×
1166
        }
×
1167
        else if (root.name() == KXMLQLCUniverseFeedbackPatch)
×
1168
        {
1169
            QString plugin = KOutputNone;
1170
            QString outputUID;
1171
            quint32 output = QLCIOPlugin::invalidLine();
1172

1173
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1174
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1175
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1176
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1177
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1178
                output = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1179

1180
            // apply the parameters just loaded
1181
            ioMap->setOutputPatch(index, plugin, outputUID, output, true);
×
1182

1183
            QXmlStreamReader::TokenType tType = root.readNext();
×
1184
            if (tType == QXmlStreamReader::Characters)
×
1185
                tType = root.readNext();
×
1186

1187
            // check if there is a PluginParameters tag defined
1188
            if (tType == QXmlStreamReader::StartElement)
×
1189
            {
1190
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1191
                    loadXMLPluginParameters(root, FeedbackPatchTag, 0);
×
1192
                root.skipCurrentElement();
×
1193
            }
1194
        }
×
1195
        else
1196
        {
1197
            qWarning() << Q_FUNC_INFO << "Unknown Universe tag:" << root.name();
×
1198
            root.skipCurrentElement();
×
1199
        }
1200
    }
1201

1202
    return true;
1203
}
1204

1205
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1206
{
1207
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1208
    {
1209
        qWarning() << Q_FUNC_INFO << "PluginParameters node not found";
×
1210
        return false;
×
1211
    }
1212

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

1238
    return true;
1239
}
1240

1241
bool Universe::saveXML(QXmlStreamWriter *doc) const
6✔
1242
{
1243
    Q_ASSERT(doc != NULL);
1244

1245
    doc->writeStartElement(KXMLQLCUniverse);
6✔
1246
    doc->writeAttribute(KXMLQLCUniverseName, name());
6✔
1247
    doc->writeAttribute(KXMLQLCUniverseID, QString::number(id()));
6✔
1248

1249
    if (passthrough() == true)
6✔
1250
        doc->writeAttribute(KXMLQLCUniversePassthrough, KXMLQLCTrue);
1✔
1251

1252
    if (inputPatch() != NULL)
6✔
1253
    {
1254
        savePatchXML(doc, KXMLQLCUniverseInputPatch, inputPatch()->pluginName(), inputPatch()->inputName(),
×
1255
            inputPatch()->input(), inputPatch()->profileName(), inputPatch()->getPluginParameters());
×
1256
    }
1257
    foreach (OutputPatch *op, m_outputPatchList)
6✔
1258
    {
1259
        savePatchXML(doc, KXMLQLCUniverseOutputPatch, op->pluginName(), op->outputName(),
×
1260
            op->output(), "", op->getPluginParameters());
×
1261
    }
1262
    if (feedbackPatch() != NULL)
6✔
1263
    {
1264
        savePatchXML(doc, KXMLQLCUniverseFeedbackPatch, feedbackPatch()->pluginName(), feedbackPatch()->outputName(),
×
1265
            feedbackPatch()->output(), "", feedbackPatch()->getPluginParameters());
×
1266
    }
1267

1268
    /* End the <Universe> tag */
1269
    doc->writeEndElement();
6✔
1270

1271
    return true;
6✔
1272
}
1273

1274
void Universe::savePatchXML(
×
1275
    QXmlStreamWriter *doc,
1276
    const QString &tag,
1277
    const QString &pluginName,
1278
    const QString &lineName,
1279
    quint32 line,
1280
    QString profileName,
1281
    QMap<QString, QVariant> parameters) const
1282
{
1283
    // sanity check: don't save invalid data
1284
    if (pluginName.isEmpty() || pluginName == KInputNone || line == QLCIOPlugin::invalidLine())
×
1285
        return;
1286

1287
    doc->writeStartElement(tag);
×
1288
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1289
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineName);
×
1290
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
1291
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1292
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1293

1294
    savePluginParametersXML(doc, parameters);
×
1295
    doc->writeEndElement();
×
1296
}
1297

1298
bool Universe::savePluginParametersXML(QXmlStreamWriter *doc,
×
1299
                                       QMap<QString, QVariant> parameters) const
1300
{
1301
    Q_ASSERT(doc != NULL);
1302

1303
    if (parameters.isEmpty())
×
1304
        return false;
1305

1306
    doc->writeStartElement(KXMLQLCUniversePluginParameters);
×
1307
    QMapIterator<QString, QVariant> it(parameters);
×
1308
    while (it.hasNext())
×
1309
    {
1310
        it.next();
×
1311
        QString pName = it.key();
1312
        QVariant pValue = it.value();
×
1313
        doc->writeAttribute(pName, pValue.toString());
×
1314
    }
×
1315
    doc->writeEndElement();
×
1316

1317
    return true;
1318
}
1319

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc