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

mcallegari / qlcplus / 8961243534

05 May 2024 09:23PM UTC coverage: 32.068% (+4.0%) from 28.094%
8961243534

push

github

mcallegari
Merge branch 'master' into qmltoqt6

902 of 2557 new or added lines in 140 files covered. (35.28%)

166 existing lines in 76 files now uncovered.

15395 of 48008 relevant lines covered (32.07%)

22949.67 hits per line

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

63.44
/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 "qlcfile.h"
36
#include "utils.h"
37

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

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

46
Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent)
845✔
47
    : QThread(parent)
48
    , m_id(id)
49
    , m_grandMaster(gm)
50
    , m_passthrough(false)
51
    , m_monitor(false)
52
    , m_inputPatch(NULL)
53
    , m_fbPatch(NULL)
54
    , m_channelsMask(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
55
    , m_modifiedZeroValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
56
    , m_usedChannels(0)
57
    , m_totalChannels(0)
58
    , m_totalChannelsChanged(false)
59
    , m_intensityChannelsChanged(false)
60
    , m_preGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
61
    , m_postGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
62
    , m_lastPostGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
63
    , m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
64
    , m_passthroughValues()
5,915✔
65
{
66
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
845✔
67

68
    m_name = QString("Universe %1").arg(id + 1);
845✔
69

70
    connect(m_grandMaster, SIGNAL(valueChanged(uchar)),
845✔
71
            this, SLOT(slotGMValueChanged()));
72
}
845✔
73

74
Universe::~Universe()
1,662✔
75
{
76
    if (isRunning() == true)
831✔
77
    {
78
        // isRunning is inconsistent with m_running,
79
        // so double check if the thread is really in the run loop
80
        while (m_running == false)
×
81
            usleep(10000);
×
82

83
        m_running = false;
×
84
        wait(1000);
×
85
    }
86

87
    delete m_inputPatch;
831✔
88
    int opCount = m_outputPatchList.count();
831✔
89
    for (int i = 0; i < opCount; i++)
845✔
90
    {
91
        OutputPatch *patch = m_outputPatchList.takeLast();
14✔
92
        delete patch;
14✔
93
    }
94
    delete m_fbPatch;
831✔
95
}
1,662✔
96

97
void Universe::setName(QString name)
7✔
98
{
99
    if (name.isEmpty())
7✔
100
        m_name = QString("Universe %1").arg(m_id + 1);
×
101
    else
102
        m_name = name;
7✔
103
    emit nameChanged();
7✔
104
}
7✔
105

106
QString Universe::name() const
106✔
107
{
108
    return m_name;
106✔
109
}
110

111
void Universe::setID(quint32 id)
2✔
112
{
113
    m_id = id;
2✔
114
}
2✔
115

116
quint32 Universe::id() const
133,094✔
117
{
118
    return m_id;
133,094✔
119
}
120

121
ushort Universe::usedChannels()
73✔
122
{
123
    return m_usedChannels;
73✔
124
}
125

126
ushort Universe::totalChannels()
6✔
127
{
128
    return m_totalChannels;
6✔
129
}
130

131
bool Universe::hasChanged()
10,505,900✔
132
{
133
    bool changed =
134
        memcmp(m_lastPostGMValues->constData(), m_postGMValues->constData(), m_usedChannels) != 0;
10,505,900✔
135
    if (changed)
10,505,900✔
136
        memcpy(m_lastPostGMValues->data(), m_postGMValues->constData(), m_usedChannels);
133,962✔
137
    return changed;
10,505,900✔
138
}
139

140
void Universe::setPassthrough(bool enable)
6✔
141
{
142
    if (enable == m_passthrough)
6✔
143
        return;
2✔
144

145
    qDebug() << "Set universe" << id() << "passthrough to" << enable;
4✔
146

147
    disconnectInputPatch();
4✔
148

149
    if (enable && m_passthroughValues.isNull())
4✔
150
    {
151
        // When passthrough is disabled, we don't release the array, since it's only ~512 B and
152
        // we would have to synchronize with other threads
153

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

160
    m_passthrough = enable;
4✔
161

162
    connectInputPatch();
4✔
163

164
    emit passthroughChanged();
4✔
165
}
166

167
bool Universe::passthrough() const
12✔
168
{
169
    return m_passthrough;
12✔
170
}
171

172
void Universe::setMonitor(bool enable)
1✔
173
{
174
    m_monitor = enable;
1✔
175
}
1✔
176

177
bool Universe::monitor() const
1✔
178
{
179
    return m_monitor;
1✔
180
}
181

182
void Universe::slotGMValueChanged()
10,032✔
183
{
184
    {
185
        for (int i = 0; i < m_intensityChannels.size(); ++i)
5,130,070✔
186
        {
187
            int channel = m_intensityChannels.at(i);
5,120,040✔
188
            updatePostGMValue(channel);
5,120,040✔
189
        }
190
    }
191

192
    if (m_grandMaster->channelMode() == GrandMaster::AllChannels)
10,032✔
193
    {
194
        for (int i = 0; i < m_nonIntensityChannels.size(); ++i)
32✔
195
        {
196
            int channel = m_nonIntensityChannels.at(i);
15✔
197
            updatePostGMValue(channel);
15✔
198
        }
199
    }
200
}
10,032✔
201

202
/************************************************************************
203
 * Faders
204
 ************************************************************************/
205

206
QSharedPointer<GenericFader> Universe::requestFader(Universe::FaderPriority priority)
147✔
207
{
208
    int insertPos = 0;
147✔
209
    QSharedPointer<GenericFader> fader = QSharedPointer<GenericFader>(new GenericFader());
147✔
210
    fader->setPriority(priority);
147✔
211

212
    if (m_faders.isEmpty())
147✔
213
    {
214
        m_faders.append(fader);
20✔
215
    }
216
    else
217
    {
218
        for (int i = m_faders.count() - 1; i >= 0; i--)
127✔
219
        {
220
            QSharedPointer<GenericFader> f = m_faders.at(i);
127✔
221
            if (!f.isNull() && f->priority() <= fader->priority())
127✔
222
            {
223
                insertPos = i + 1;
127✔
224
                break;
127✔
225
            }
226
        }
227

228
        m_faders.insert(insertPos, fader);
127✔
229
    }
230

231
    qDebug() << "[Universe]" << id() << ": Generic fader with priority" << fader->priority()
294✔
232
             << "registered at pos" << insertPos << ", count" << m_faders.count();
147✔
233

234
    return fader;
147✔
235
}
236

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

247
void Universe::requestFaderPriority(QSharedPointer<GenericFader> fader, Universe::FaderPriority priority)
×
248
{
249
    if (m_faders.contains(fader) == false)
×
250
        return;
×
251

252
    int pos = m_faders.indexOf(fader);
×
253
    int newPos = 0;
×
254

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

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

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

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

288
        fader->setPaused(enable);
3✔
289
    }
290
}
8✔
291

292
void Universe::tick()
688✔
293
{
294
    m_semaphore.release(1);
688✔
295
}
688✔
296

297
void Universe::processFaders()
264,827✔
298
{
299
    flushInput();
264,827✔
300
    zeroIntensityChannels();
264,827✔
301

302
    QMutableListIterator<QSharedPointer<GenericFader> > it(m_faders);
264,827✔
303
    while (it.hasNext())
1,191,900✔
304
    {
305
        QSharedPointer<GenericFader> fader = it.next();
927,070✔
306
        if (fader.isNull())
927,070✔
307
            continue;
×
308

309
        // destroy a fader if it's been requested
310
        // and it's not fading out
311
        if (fader->deleteRequested() && !fader->isFadingOut())
927,070✔
312
        {
313
            fader->removeAll();
2✔
314
            it.remove();
2✔
315
            fader.clear();
2✔
316
            continue;
2✔
317
        }
318

319
        if (fader->isEnabled() == false)
927,068✔
320
            continue;
×
321

322
        //qDebug() << "Processing fader" << fader->name() << fader->channelsCount();
323
        fader->write(this);
927,068✔
324
    }
325

326
    bool dataChanged = hasChanged();
264,827✔
327
    const QByteArray postGM = m_postGMValues->mid(0, m_usedChannels);
529,654✔
328
    dumpOutput(postGM, dataChanged);
264,827✔
329

330
    if (dataChanged)
264,827✔
331
        emit universeWritten(id(), postGM);
132,936✔
332
}
264,827✔
333

334
void Universe::run()
×
335
{
336
    m_running = true;
×
337
    int timeout = int(MasterTimer::tick()) * 2;
×
338

339
    qDebug() << "Universe thread started" << id();
×
340

NEW
341
    while (m_running)
×
342
    {
343
        if (m_semaphore.tryAcquire(1, timeout) == false)
×
344
        {
345
            //qWarning() << "Semaphore not acquired on universe" << id();
346
            continue;
×
347
        }
348
#if 0
349
        if (m_faders.count())
350
            qDebug() << "<<<<<<<< UNIVERSE TICK - id" << id() << "faders:" << m_faders.count();
351
#endif
352
        processFaders();
×
353
    }
354

355
    qDebug() << "Universe thread stopped" << id();
×
356
}
×
357

358
/************************************************************************
359
 * Values
360
 ************************************************************************/
361

362
void Universe::reset()
8✔
363
{
364
    m_preGMValues->fill(0);
8✔
365
    m_blackoutValues->fill(0);
8✔
366

367
    if (m_passthrough)
8✔
UNCOV
368
        (*m_postGMValues) = (*m_passthroughValues);
×
369
    else
370
        m_postGMValues->fill(0);
8✔
371

372
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
8✔
373
    m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
8✔
374
}
8✔
375

376
void Universe::reset(int address, int range)
3,099,740✔
377
{
378
    if (address >= UNIVERSE_SIZE)
3,099,740✔
379
        return;
×
380

381
    if (address + range > UNIVERSE_SIZE)
3,099,740✔
382
       range = UNIVERSE_SIZE - address;
1✔
383

384
    memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
3,099,740✔
385
    memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
3,099,740✔
386
    memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
3,099,740✔
387

388
    applyPassthroughValues(address, range);
3,099,740✔
389
}
390

391
void Universe::applyPassthroughValues(int address, int range)
3,099,740✔
392
{
393
    if (!m_passthrough)
3,099,740✔
394
        return;
3,099,740✔
395

396
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
397
    {
398
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
399
        {
400
            (*m_postGMValues)[i] = (*m_passthroughValues)[i];
×
401
        }
402
    }
403
}
404

405
void Universe::zeroIntensityChannels()
284,927✔
406
{
407
    updateIntensityChannelsRanges();
284,927✔
408
    int const* channels = m_intensityChannelsRanges.constData();
284,927✔
409
    for (int i = 0; i < m_intensityChannelsRanges.size(); ++i)
3,384,670✔
410
    {
411
        short channel = channels[i] >> 16;
3,099,740✔
412
        short size = channels[i] & 0xffff;
3,099,740✔
413

414
        reset(channel, size);
3,099,740✔
415
    }
416
}
284,927✔
417

418
QHash<int, uchar> Universe::intensityChannels()
1✔
419
{
420
    QHash <int, uchar> intensityList;
1✔
421
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1✔
422
    {
423
        int channel = m_intensityChannels.at(i);
×
424
        intensityList[channel] = m_preGMValues->at(channel);
×
425
    }
426
    return intensityList;
1✔
427
}
428

429
uchar Universe::postGMValue(int address) const
×
430
{
431
    if (address >= m_postGMValues->size())
×
432
        return 0;
×
433

434
    return uchar(m_postGMValues->at(address));
×
435
}
436

437
const QByteArray* Universe::postGMValues() const
4,621✔
438
{
439
    return m_postGMValues.data();
4,621✔
440
}
441

442
Universe::BlendMode Universe::stringToBlendMode(QString mode)
5✔
443
{
444
    if (mode == KXMLUniverseNormalBlend)
5✔
445
        return NormalBlend;
1✔
446
    else if (mode == KXMLUniverseMaskBlend)
4✔
447
        return MaskBlend;
1✔
448
    else if (mode == KXMLUniverseAdditiveBlend)
3✔
449
        return AdditiveBlend;
1✔
450
    else if (mode == KXMLUniverseSubtractiveBlend)
2✔
451
        return SubtractiveBlend;
1✔
452

453
    return NormalBlend;
1✔
454
}
455

456
QString Universe::blendModeToString(Universe::BlendMode mode)
7✔
457
{
458
    switch(mode)
7✔
459
    {
460
        default:
1✔
461
        case NormalBlend:
462
            return QString(KXMLUniverseNormalBlend);
1✔
463
        break;
464
        case MaskBlend:
1✔
465
            return QString(KXMLUniverseMaskBlend);
1✔
466
        break;
467
        case AdditiveBlend:
4✔
468
            return QString(KXMLUniverseAdditiveBlend);
4✔
469
        break;
470
        case SubtractiveBlend:
1✔
471
            return QString(KXMLUniverseSubtractiveBlend);
1✔
472
        break;
473
    }
474
}
475

476
const QByteArray Universe::preGMValues() const
1,326,100✔
477
{
478
    return *m_preGMValues;
1,326,100✔
479
}
480

481
uchar Universe::preGMValue(int address) const
17,096,500✔
482
{
483
    if (address >= m_preGMValues->size())
17,096,500✔
484
        return 0U;
×
485

486
    return static_cast<uchar>(m_preGMValues->at(address));
17,096,500✔
487
}
488

489
uchar Universe::applyGM(int channel, uchar value)
17,089,600✔
490
{
491
    if ((m_grandMaster->channelMode() == GrandMaster::Intensity && m_channelsMask->at(channel) & Intensity) ||
23,935,500✔
492
        (m_grandMaster->channelMode() == GrandMaster::AllChannels))
6,845,950✔
493
    {
494
        if (m_grandMaster->valueMode() == GrandMaster::Limit)
10,244,700✔
495
            value = MIN(value, m_grandMaster->value());
792✔
496
        else
497
            value = char(floor((double(value) * m_grandMaster->fraction()) + 0.5));
10,243,900✔
498
    }
499

500
    return value;
17,089,600✔
501
}
502

503
uchar Universe::applyModifiers(int channel, uchar value)
17,095,900✔
504
{
505
    if (m_modifiers.at(channel) != NULL)
17,095,900✔
506
        return m_modifiers.at(channel)->getValue(value);
×
507

508
    return value;
17,095,900✔
509
}
510

511
uchar Universe::applyPassthrough(int channel, uchar value)
17,095,900✔
512
{
513
    if (m_passthrough)
17,095,900✔
514
    {
515
        const uchar passthroughValue = static_cast<uchar>(m_passthroughValues->at(channel));
×
516
        if (value < passthroughValue) // HTP merge
×
517
        {
518
            return passthroughValue;
×
519
        }
520
    }
521

522
    return value;
17,095,900✔
523
}
524

525
void Universe::updatePostGMValue(int channel)
17,095,900✔
526
{
527
    uchar value = preGMValue(channel);
17,095,900✔
528

529
    if (value != 0)
17,095,900✔
530
        value = applyGM(channel, value);
17,087,000✔
531

532
    value = applyModifiers(channel, value);
17,095,900✔
533
    value = applyPassthrough(channel, value);
17,095,900✔
534

535
    (*m_postGMValues)[channel] = static_cast<char>(value);
17,095,900✔
536
}
17,095,900✔
537

538
/************************************************************************
539
 * Patches
540
 ************************************************************************/
541

542
bool Universe::isPatched()
2✔
543
{
544
    if (m_inputPatch != NULL || m_outputPatchList.count() || m_fbPatch != NULL)
2✔
545
        return true;
1✔
546

547
    return false;
1✔
548
}
549

550
bool Universe::setInputPatch(QLCIOPlugin *plugin,
6✔
551
                             quint32 input, QLCInputProfile *profile)
552
{
553
    qDebug() << "[Universe] setInputPatch - ID:" << m_id << ", plugin:" << ((plugin == NULL)?"None":plugin->name())
12✔
554
             << ", input:" << input << ", profile:" << ((profile == NULL)?"None":profile->name());
6✔
555
    if (m_inputPatch == NULL)
6✔
556
    {
557
        if (plugin == NULL || input == QLCIOPlugin::invalidLine())
5✔
558
            return true;
1✔
559

560
        m_inputPatch = new InputPatch(m_id, this);
4✔
561
        connectInputPatch();
4✔
562
    }
563
    else
564
    {
565
        if (input == QLCIOPlugin::invalidLine())
1✔
566
        {
567
            disconnectInputPatch();
×
568
            delete m_inputPatch;
×
569
            m_inputPatch = NULL;
×
570
            emit inputPatchChanged();
×
571
            return true;
×
572
        }
573
    }
574

575
    if (m_inputPatch != NULL)
5✔
576
    {
577
        bool result = m_inputPatch->set(plugin, input, profile);
5✔
578
        emit inputPatchChanged();
5✔
579
        return result;
5✔
580
    }
581

582
    return true;
×
583
}
584

585
bool Universe::setOutputPatch(QLCIOPlugin *plugin, quint32 output, int index)
19✔
586
{
587
    if (index < 0)
19✔
588
        return false;
×
589

590
    qDebug() << "[Universe] setOutputPatch - ID:" << m_id
19✔
591
             << ", plugin:" << ((plugin == NULL) ? "None" : plugin->name()) << ", output:" << output;
19✔
592

593
    // replace or delete an existing patch
594
    if (index < m_outputPatchList.count())
19✔
595
    {
596
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
2✔
597
        {
598
            // need to delete an existing patch
599
            OutputPatch *patch = m_outputPatchList.takeAt(index);
2✔
600
            delete patch;
2✔
601
            emit outputPatchesCountChanged();
2✔
602
            return true;
2✔
603
        }
604

605
        OutputPatch *patch = m_outputPatchList.at(index);
×
606
        bool result = patch->set(plugin, output);
×
607
        emit outputPatchChanged();
×
608
        return result;
×
609
    }
610
    else
611
    {
612
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
17✔
613
            return false;
1✔
614

615
        // add a new patch
616
        OutputPatch *patch = new OutputPatch(m_id, this);
16✔
617
        bool result = patch->set(plugin, output);
16✔
618
        m_outputPatchList.append(patch);
16✔
619
        emit outputPatchesCountChanged();
16✔
620
        return result;
16✔
621
    }
622

623
    return false;
624
}
625

626
bool Universe::setFeedbackPatch(QLCIOPlugin *plugin, quint32 output)
×
627
{
628
    qDebug() << Q_FUNC_INFO << "plugin:" << plugin << "output:" << output;
×
629
    if (m_fbPatch == NULL)
×
630
    {
631
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
632
            return false;
×
633

634
        m_fbPatch = new OutputPatch(m_id, this);
×
635
    }
636
    else
637
    {
638
        if (plugin == NULL || output == QLCIOPlugin::invalidLine())
×
639
        {
640
            delete m_fbPatch;
×
641
            m_fbPatch = NULL;
×
NEW
642
            emit hasFeedbackChanged();
×
643
            return true;
×
644
        }
645
    }
646
    if (m_fbPatch != NULL)
×
647
    {
648
        bool result = m_fbPatch->set(plugin, output);
×
NEW
649
        emit hasFeedbackChanged();
×
650
        return result;
×
651
    }
652

653
    return false;
×
654
}
655

NEW
656
bool Universe::hasFeedback() const
×
657
{
658
    return m_fbPatch != NULL ? true : false;
×
659
}
660

661
InputPatch *Universe::inputPatch() const
161✔
662
{
663
    return m_inputPatch;
161✔
664
}
665

666
OutputPatch *Universe::outputPatch(int index) const
48✔
667
{
668
    if (index < 0 || index >= m_outputPatchList.count())
48✔
669
        return NULL;
14✔
670

671
    return m_outputPatchList.at(index);
34✔
672
}
673

674
int Universe::outputPatchesCount() const
83✔
675
{
676
    return m_outputPatchList.count();
83✔
677
}
678

679
OutputPatch *Universe::feedbackPatch() const
49✔
680
{
681
    return m_fbPatch;
49✔
682
}
683

684
void Universe::dumpOutput(const QByteArray &data, bool dataChanged)
264,875✔
685
{
686
    if (m_outputPatchList.count() == 0)
264,875✔
687
        return;
264,835✔
688

689
    foreach (OutputPatch *op, m_outputPatchList)
120✔
690
    {
691
        if (m_totalChannelsChanged == true)
40✔
692
            op->setPluginParameter(PLUGIN_UNIVERSECHANNELS, m_totalChannels);
4✔
693

694
        if (op->blackout())
40✔
695
            op->dump(m_id, *m_blackoutValues, dataChanged);
20✔
696
        else
697
            op->dump(m_id, data, dataChanged);
20✔
698
    }
699
    m_totalChannelsChanged = false;
40✔
700
}
701

702
void Universe::flushInput()
264,847✔
703
{
704
    if (m_inputPatch == NULL)
264,847✔
705
        return;
264,842✔
706

707
    m_inputPatch->flush(m_id);
5✔
708
}
709

710
void Universe::slotInputValueChanged(quint32 universe, quint32 channel, uchar value, const QString &key)
×
711
{
712
    if (m_passthrough)
×
713
    {
714
        if (universe == m_id)
×
715
        {
716
            qDebug() << "write" << channel << value;
×
717

718
            if (channel >= UNIVERSE_SIZE)
×
719
                return;
×
720

721
            if (channel >= m_usedChannels)
×
722
                m_usedChannels = channel + 1;
×
723

724
            (*m_passthroughValues)[channel] = value;
×
725

726
            updatePostGMValue(channel);
×
727
        }
728
    }
729
    else
730
        emit inputValueChanged(universe, channel, value, key);
×
731
}
732

733
void Universe::connectInputPatch()
8✔
734
{
735
    if (m_inputPatch == NULL)
8✔
736
        return;
4✔
737

738
    if (!m_passthrough)
4✔
739
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
4✔
740
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
741
    else
742
        connect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
743
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
744
}
745

746
void Universe::disconnectInputPatch()
4✔
747
{
748
    if (m_inputPatch == NULL)
4✔
749
        return;
4✔
750

751
    if (!m_passthrough)
×
752
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
753
                this, SIGNAL(inputValueChanged(quint32,quint32,uchar,QString)));
754
    else
755
        disconnect(m_inputPatch, SIGNAL(inputValueChanged(quint32,quint32,uchar,const QString&)),
×
756
                this, SLOT(slotInputValueChanged(quint32,quint32,uchar,const QString&)));
757
}
758

759
/************************************************************************
760
 * Channels capabilities
761
 ************************************************************************/
762

763
void Universe::setChannelCapability(ushort channel, QLCChannel::Group group, ChannelType forcedType)
7,205✔
764
{
765
    if (channel >= (ushort)m_channelsMask->length())
7,205✔
766
        return;
×
767

768
    if (Utils::vectorRemove(m_intensityChannels, channel))
7,205✔
769
        m_intensityChannelsChanged = true;
823✔
770
    Utils::vectorRemove(m_nonIntensityChannels, channel);
7,205✔
771

772
    if (forcedType != Undefined)
7,205✔
773
    {
774
        (*m_channelsMask)[channel] = char(forcedType);
5✔
775
        if ((forcedType & HTP) == HTP)
5✔
776
        {
777
            //qDebug() << "--- Channel" << channel << "forced type HTP";
778
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
3✔
779
            m_intensityChannelsChanged = true;
3✔
780
            if (group == QLCChannel::Intensity)
3✔
781
            {
782
                //qDebug() << "--- Channel" << channel << "Intensity + HTP";
783
                (*m_channelsMask)[channel] = char(HTP | Intensity);
×
784
            }
785
        }
786
        else if ((forcedType & LTP) == LTP)
2✔
787
        {
788
            //qDebug() << "--- Channel" << channel << "forced type LTP";
789
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
2✔
790
        }
791
    }
792
    else
793
    {
794
        if (group == QLCChannel::Intensity)
7,200✔
795
        {
796
            //qDebug() << "--- Channel" << channel << "Intensity + HTP";
797
            (*m_channelsMask)[channel] = char(HTP | Intensity);
5,655✔
798
            Utils::vectorSortedAddUnique(m_intensityChannels, channel);
5,655✔
799
            m_intensityChannelsChanged = true;
5,655✔
800
        }
801
        else
802
        {
803
            //qDebug() << "--- Channel" << channel << "LTP";
804
            (*m_channelsMask)[channel] = char(LTP);
1,545✔
805
            Utils::vectorSortedAddUnique(m_nonIntensityChannels, channel);
1,545✔
806
        }
807
    }
808

809
    // qDebug() << Q_FUNC_INFO << "Channel:" << channel << "mask:" << QString::number(m_channelsMask->at(channel), 16);
810
    if (channel >= m_totalChannels)
7,205✔
811
    {
812
        m_totalChannels = channel + 1;
5,619✔
813
        m_totalChannelsChanged = true;
5,619✔
814
    }
815
}
816

817
uchar Universe::channelCapabilities(ushort channel)
517✔
818
{
819
    if (channel >= (ushort)m_channelsMask->length())
517✔
820
        return Undefined;
×
821

822
    return m_channelsMask->at(channel);
517✔
823
}
824

825
void Universe::setChannelDefaultValue(ushort channel, uchar value)
2,556✔
826
{
827
    if (channel >= m_totalChannels)
2,556✔
828
    {
829
        m_totalChannels = channel + 1;
×
830
        m_totalChannelsChanged = true;
×
831
    }
832

833
    if (channel >= m_usedChannels)
2,556✔
834
        m_usedChannels = channel + 1;
976✔
835

836
    (*m_preGMValues)[channel] = value;
2,556✔
837
    updatePostGMValue(channel);
2,556✔
838
}
2,556✔
839

840
void Universe::setChannelModifier(ushort channel, ChannelModifier *modifier)
2,556✔
841
{
842
    if (channel >= (ushort)m_modifiers.count())
2,556✔
843
        return;
×
844

845
    m_modifiers[channel] = modifier;
2,556✔
846

847
    if (modifier != NULL)
2,556✔
848
    {
849
        (*m_modifiedZeroValues)[channel] = modifier->getValue(0);
×
850

851
        if (channel >= m_totalChannels)
×
852
        {
853
            m_totalChannels = channel + 1;
×
854
            m_totalChannelsChanged = true;
×
855
        }
856

857
        if (channel >= m_usedChannels)
×
858
            m_usedChannels = channel + 1;
×
859
    }
860

861
    updatePostGMValue(channel);
2,556✔
862
}
863

864
ChannelModifier *Universe::channelModifier(ushort channel)
×
865
{
866
    if (channel >= (ushort)m_modifiers.count())
×
867
        return NULL;
×
868

869
    return m_modifiers.at(channel);
×
870
}
871

872
void Universe::updateIntensityChannelsRanges()
284,927✔
873
{
874
    if (!m_intensityChannelsChanged)
284,927✔
875
        return;
284,890✔
876

877
    m_intensityChannelsChanged = false;
37✔
878

879
    m_intensityChannelsRanges.clear();
37✔
880
    short currentPos = -1;
37✔
881
    short currentSize = 0;
37✔
882

883
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1,797✔
884
    {
885
        int channel = m_intensityChannels.at(i);
1,760✔
886
        if (currentPos + currentSize == channel)
1,760✔
887
            ++currentSize;
1,179✔
888
        else
889
        {
890
            if (currentPos != -1)
581✔
891
                m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
544✔
892
            currentPos = channel;
581✔
893
            currentSize = 1;
581✔
894
        }
895
    }
896
    if (currentPos != -1)
37✔
897
        m_intensityChannelsRanges.append((currentPos << 16) | currentSize);
37✔
898

899
    qDebug() << Q_FUNC_INFO << ":" << m_intensityChannelsRanges.size() << "ranges";
37✔
900
}
901

902
/****************************************************************************
903
 * Writing
904
 ****************************************************************************/
905

906
bool Universe::write(int address, uchar value, bool forceLTP)
10,248,400✔
907
{
908
    Q_ASSERT(address < UNIVERSE_SIZE);
10,248,400✔
909

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

912
    if (address >= m_usedChannels)
10,248,400✔
913
        m_usedChannels = address + 1;
9,370✔
914

915
    if (m_channelsMask->at(address) & HTP)
10,248,400✔
916
    {
917
        if (forceLTP == false && value < (uchar)m_preGMValues->at(address))
5,122,710✔
918
        {
NEW
919
            qDebug() << "[Universe] HTP check not passed" << address << value;
×
NEW
920
            return false;
×
921
        }
922
    }
923
    else
924
    {
925
        // preserve non HTP channels for blackout
926
        (*m_blackoutValues)[address] = char(value);
5,125,660✔
927
    }
928

929
    (*m_preGMValues)[address] = char(value);
10,248,400✔
930

931
    updatePostGMValue(address);
10,248,400✔
932

933
    return true;
10,248,400✔
934
}
935

936
bool Universe::writeMultiple(int address, quint32 value, int channelCount)
1,722,380✔
937
{
938
    for (int i = 0; i < channelCount; i++)
3,444,750✔
939
    {
940
        //qDebug() << "[Universe]" << id() << ": write channel" << (address + i) << ", value:" << QString::number(((uchar *)&value)[channelCount - 1 - i]);
941

942
        // preserve non HTP channels for blackout
943
        if ((m_channelsMask->at(address + i) & HTP) == 0)
1,722,380✔
944
            (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,250✔
945

946
        (*m_preGMValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,380✔
947

948
        updatePostGMValue(address + i);
1,722,380✔
949
    }
950

951
    return true;
1,722,380✔
952
}
953

954
bool Universe::writeRelative(int address, quint32 value, int channelCount)
6✔
955
{
956
    Q_ASSERT(address < UNIVERSE_SIZE);
6✔
957

958
    //qDebug() << "Write relative channel" << address << "value" << value;
959

960
    if (address + channelCount >= m_usedChannels)
6✔
961
        m_usedChannels = address + channelCount;
6✔
962

963
    if (channelCount == 1)
6✔
964
    {
965
        short newVal = uchar((*m_preGMValues)[address]);
6✔
966
        newVal += short(value) - RELATIVE_ZERO_8BIT;
6✔
967
        (*m_preGMValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
6✔
968
        (*m_blackoutValues)[address] = char(CLAMP(newVal, 0, UCHAR_MAX));
6✔
969
        updatePostGMValue(address);
6✔
970
    }
971
    else
972
    {
NEW
973
        quint32 currentValue = 0;
×
NEW
974
        for (int i = 0; i < channelCount; i++)
×
NEW
975
            currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
×
976

NEW
977
        currentValue += (value - RELATIVE_ZERO_16BIT);
×
978

NEW
979
        for (int i = 0; i < channelCount; i++)
×
980
        {
NEW
981
            (*m_preGMValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
×
NEW
982
            (*m_blackoutValues)[address + i] = ((uchar *)&currentValue)[channelCount - 1 - i];
×
NEW
983
            updatePostGMValue(address + i);
×
984
        }
985
    }
986

987
    return true;
6✔
988
}
989

990
bool Universe::writeBlended(int address, quint32 value, int channelCount, Universe::BlendMode blend)
1,722,370✔
991
{
992
    if (address + channelCount >= m_usedChannels)
1,722,370✔
993
        m_usedChannels = address + channelCount;
116✔
994

995
    quint32 currentValue = 0;
1,722,370✔
996
    for (int i = 0; i < channelCount; i++)
3,444,740✔
997
        currentValue = (currentValue << 8) + uchar(m_preGMValues->at(address + i));
1,722,370✔
998

999
    switch (blend)
1,722,370✔
1000
    {
1001
        case NormalBlend:
1,722,360✔
1002
        {
1003
            if ((m_channelsMask->at(address) & HTP) && value < currentValue)
1,722,360✔
1004
            {
NEW
1005
                qDebug() << "[Universe] HTP check not passed" << address << value;
×
NEW
1006
                return false;
×
1007
            }
1008
        }
1009
        break;
1,722,360✔
1010
        case MaskBlend:
2✔
1011
        {
1012
            if (value)
2✔
1013
            {
1014
                qDebug() << "Current value" << currentValue << "value" << value;
2✔
1015
                if (currentValue)
2✔
1016
                    value = float(currentValue) * (float(value) / pow(255.0, channelCount));
1✔
1017
                else
1018
                    value = 0;
1✔
1019
            }
1020
        }
1021
        break;
2✔
1022
        case AdditiveBlend:
1✔
1023
        {
1024
            //qDebug() << "Universe write additive channel" << channel << ", value:" << currVal << "+" << value;
1025
            value = fmin(float(currentValue + value), pow(255.0, channelCount));
1✔
1026
        }
1027
        break;
1✔
1028
        case SubtractiveBlend:
2✔
1029
        {
1030
            if (value >= currentValue)
2✔
1031
                value = 0;
1✔
1032
            else
1033
                value = currentValue - value;
1✔
1034
        }
1035
        break;
2✔
1036
        default:
1✔
1037
            qDebug() << "[Universe] Blend mode not handled. Implement me!" << blend;
1✔
1038
            return false;
1✔
1039
        break;
1040
    }
1041

1042
    writeMultiple(address, value, channelCount);
1,722,370✔
1043

1044
    return true;
1,722,370✔
1045
}
1046

1047
/*********************************************************************
1048
 * Load & Save
1049
 *********************************************************************/
1050

1051
bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap)
5✔
1052
{
1053
    if (root.name() != KXMLQLCUniverse)
5✔
1054
    {
1055
        qWarning() << Q_FUNC_INFO << "Universe node not found";
1✔
1056
        return false;
1✔
1057
    }
1058

1059
    int outputIndex = 0;
4✔
1060

1061
    QXmlStreamAttributes attrs = root.attributes();
4✔
1062

1063
    if (attrs.hasAttribute(KXMLQLCUniverseName))
4✔
1064
        setName(attrs.value(KXMLQLCUniverseName).toString());
4✔
1065

1066
    if (attrs.hasAttribute(KXMLQLCUniversePassthrough))
4✔
1067
    {
1068
        if (attrs.value(KXMLQLCUniversePassthrough).toString() == KXMLQLCTrue ||
8✔
1069
            attrs.value(KXMLQLCUniversePassthrough).toString() == "1")
5✔
1070
            setPassthrough(true);
2✔
1071
        else
1072
            setPassthrough(false);
1✔
1073
    }
1074
    else
1075
    {
1076
        setPassthrough(false);
1✔
1077
    }
1078

1079
    while (root.readNextStartElement())
4✔
1080
    {
1081
        QXmlStreamAttributes pAttrs = root.attributes();
×
1082

1083
        if (root.name() == KXMLQLCUniverseInputPatch)
×
1084
        {
1085
            QString plugin = KInputNone;
×
1086
            quint32 inputLine = QLCIOPlugin::invalidLine();
×
1087
            QString inputUID;
×
1088
            QString profile = KInputNone;
×
1089

1090
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1091
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1092
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1093
                inputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1094
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1095
                inputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1096
            if (pAttrs.hasAttribute(KXMLQLCUniverseProfileName))
×
1097
                profile = pAttrs.value(KXMLQLCUniverseProfileName).toString();
×
1098

1099
            // apply the parameters just loaded
1100
            ioMap->setInputPatch(index, plugin, inputUID, inputLine, profile);
×
1101

1102
            QXmlStreamReader::TokenType tType = root.readNext();
×
1103
            if (tType == QXmlStreamReader::Characters)
×
1104
                tType = root.readNext();
×
1105

1106
            // check if there is a PluginParameters tag defined
1107
            if (tType == QXmlStreamReader::StartElement)
×
1108
            {
1109
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1110
                    loadXMLPluginParameters(root, InputPatchTag, 0);
×
1111
                root.skipCurrentElement();
×
1112
            }
1113
        }
1114
        else if (root.name() == KXMLQLCUniverseOutputPatch)
×
1115
        {
1116
            QString plugin = KOutputNone;
×
1117
            QString outputUID;
×
1118
            quint32 outputLine = QLCIOPlugin::invalidLine();
×
1119

1120
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1121
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1122
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1123
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1124
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1125
                outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1126

1127
            // apply the parameters just loaded
1128
            ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false, outputIndex);
×
1129

1130
            QXmlStreamReader::TokenType tType = root.readNext();
×
1131
            if (tType == QXmlStreamReader::Characters)
×
1132
                tType = root.readNext();
×
1133

1134
            // check if there is a PluginParameters tag defined
1135
            if (tType == QXmlStreamReader::StartElement)
×
1136
            {
1137
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1138
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
1139
                root.skipCurrentElement();
×
1140
            }
1141

1142
            outputIndex++;
×
1143
        }
1144
        else if (root.name() == KXMLQLCUniverseFeedbackPatch)
×
1145
        {
1146
            QString plugin = KOutputNone;
×
1147
            QString outputUID;
×
1148
            quint32 output = QLCIOPlugin::invalidLine();
×
1149

1150
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1151
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1152
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1153
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1154
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1155
                output = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1156

1157
            // apply the parameters just loaded
1158
            ioMap->setOutputPatch(index, plugin, outputUID, output, true);
×
1159

1160
            QXmlStreamReader::TokenType tType = root.readNext();
×
1161
            if (tType == QXmlStreamReader::Characters)
×
1162
                tType = root.readNext();
×
1163

1164
            // check if there is a PluginParameters tag defined
1165
            if (tType == QXmlStreamReader::StartElement)
×
1166
            {
1167
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1168
                    loadXMLPluginParameters(root, FeedbackPatchTag, 0);
×
1169
                root.skipCurrentElement();
×
1170
            }
1171
        }
1172
        else
1173
        {
1174
            qWarning() << Q_FUNC_INFO << "Unknown Universe tag:" << root.name();
×
1175
            root.skipCurrentElement();
×
1176
        }
1177
    }
1178

1179
    return true;
4✔
1180
}
1181

1182
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1183
{
1184
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1185
    {
1186
        qWarning() << Q_FUNC_INFO << "PluginParameters node not found";
×
1187
        return false;
×
1188
    }
1189

1190
    QXmlStreamAttributes pluginAttrs = root.attributes();
×
1191
    for (int i = 0; i < pluginAttrs.count(); i++)
×
1192
    {
1193
        QXmlStreamAttribute attr = pluginAttrs.at(i);
×
1194
        if (currentTag == InputPatchTag)
×
1195
        {
1196
            InputPatch *ip = inputPatch();
×
1197
            if (ip != NULL)
×
1198
                ip->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1199
        }
1200
        else if (currentTag == OutputPatchTag)
×
1201
        {
1202
            OutputPatch *op = outputPatch(patchIndex);
×
1203
            if (op != NULL)
×
1204
                op->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1205
        }
1206
        else if (currentTag == FeedbackPatchTag)
×
1207
        {
1208
            OutputPatch *fbp = feedbackPatch();
×
1209
            if (fbp != NULL)
×
1210
                fbp->setPluginParameter(attr.name().toString(), attr.value().toString());
×
1211
        }
1212
    }
1213
    root.skipCurrentElement();
×
1214

1215
    return true;
×
1216
}
1217

1218
bool Universe::saveXML(QXmlStreamWriter *doc) const
6✔
1219
{
1220
    Q_ASSERT(doc != NULL);
6✔
1221

1222
    doc->writeStartElement(KXMLQLCUniverse);
6✔
1223
    doc->writeAttribute(KXMLQLCUniverseName, name());
6✔
1224
    doc->writeAttribute(KXMLQLCUniverseID, QString::number(id()));
6✔
1225

1226
    if (passthrough() == true)
6✔
1227
        doc->writeAttribute(KXMLQLCUniversePassthrough, KXMLQLCTrue);
1✔
1228

1229
    if (inputPatch() != NULL)
6✔
1230
    {
1231
        savePatchXML(doc, KXMLQLCUniverseInputPatch, inputPatch()->pluginName(), inputPatch()->inputName(),
×
1232
            inputPatch()->input(), inputPatch()->profileName(), inputPatch()->getPluginParameters());
×
1233
    }
1234
    foreach (OutputPatch *op, m_outputPatchList)
6✔
1235
    {
1236
        savePatchXML(doc, KXMLQLCUniverseOutputPatch, op->pluginName(), op->outputName(),
×
1237
            op->output(), "", op->getPluginParameters());
×
1238
    }
1239
    if (feedbackPatch() != NULL)
6✔
1240
    {
1241
        savePatchXML(doc, KXMLQLCUniverseFeedbackPatch, feedbackPatch()->pluginName(), feedbackPatch()->outputName(),
×
1242
            feedbackPatch()->output(), "", feedbackPatch()->getPluginParameters());
×
1243
    }
1244

1245
    /* End the <Universe> tag */
1246
    doc->writeEndElement();
6✔
1247

1248
    return true;
6✔
1249
}
1250

1251
void Universe::savePatchXML(
×
1252
    QXmlStreamWriter *doc,
1253
    const QString &tag,
1254
    const QString &pluginName,
1255
    const QString &lineName,
1256
    quint32 line,
1257
    QString profileName,
1258
    QMap<QString, QVariant> parameters) const
1259
{
1260
    // sanity check: don't save invalid data
1261
    if (pluginName.isEmpty() || pluginName == KInputNone || line == QLCIOPlugin::invalidLine())
×
1262
        return;
×
1263

1264
    doc->writeStartElement(tag);
×
1265
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1266
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineName);
×
1267
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
1268
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1269
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1270

1271
    savePluginParametersXML(doc, parameters);
×
1272
    doc->writeEndElement();
×
1273
}
1274

1275
bool Universe::savePluginParametersXML(QXmlStreamWriter *doc,
×
1276
                                       QMap<QString, QVariant> parameters) const
1277
{
1278
    Q_ASSERT(doc != NULL);
×
1279

1280
    if (parameters.isEmpty())
×
1281
        return false;
×
1282

1283
    doc->writeStartElement(KXMLQLCUniversePluginParameters);
×
1284
    QMapIterator<QString, QVariant> it(parameters);
×
NEW
1285
    while (it.hasNext())
×
1286
    {
1287
        it.next();
×
1288
        QString pName = it.key();
×
1289
        QVariant pValue = it.value();
×
1290
        doc->writeAttribute(pName, pValue.toString());
×
1291
    }
1292
    doc->writeEndElement();
×
1293

1294
    return true;
×
1295
}
1296

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