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

mcallegari / qlcplus / 8121957695

02 Mar 2024 09:50AM UTC coverage: 32.097% (+0.06%) from 32.036%
8121957695

push

github

web-flow
Merge pull request #1522 from mcallegari/16bitfade

16bit fade rework

145 of 178 new or added lines in 7 files covered. (81.46%)

1 existing line in 1 file now uncovered.

15387 of 47939 relevant lines covered (32.1%)

24361.41 hits per line

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

64.85
/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 127
39

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

45
Universe::Universe(quint32 id, GrandMaster *gm, QObject *parent)
845✔
46
    : QThread(parent)
47
    , m_id(id)
48
    , m_grandMaster(gm)
49
    , m_passthrough(false)
50
    , m_monitor(false)
51
    , m_inputPatch(NULL)
52
    , m_fbPatch(NULL)
53
    , m_channelsMask(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
54
    , m_modifiedZeroValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
55
    , m_usedChannels(0)
56
    , m_totalChannels(0)
57
    , m_totalChannelsChanged(false)
58
    , m_intensityChannelsChanged(false)
59
    , m_preGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
60
    , m_postGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
61
    , m_lastPostGMValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
62
    , m_blackoutValues(new QByteArray(UNIVERSE_SIZE, char(0)))
845✔
63
    , m_passthroughValues()
5,915✔
64
{
65
    m_relativeValues.fill(0, UNIVERSE_SIZE);
845✔
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,096✔
117
{
118
    return m_id;
133,096✔
119
}
120

121
ushort Universe::usedChannels()
53✔
122
{
123
    return m_usedChannels;
53✔
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,964✔
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
    zeroRelativeValues();
264,827✔
302

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

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

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

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

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

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

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

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

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

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

359
/************************************************************************
360
 * Values
361
 ************************************************************************/
362

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

368
    if (m_passthrough)
8✔
369
    {
370
        (*m_postGMValues) = (*m_passthroughValues);
×
371
    }
372
    else
373
    {
374
        m_postGMValues->fill(0);
8✔
375
    }
376
    zeroRelativeValues();
8✔
377
    m_modifiers.fill(NULL, UNIVERSE_SIZE);
8✔
378
    m_passthrough = false; // not releasing m_passthroughValues, see comment in setPassthrough
8✔
379
}
8✔
380

381
void Universe::reset(int address, int range)
3,099,740✔
382
{
383
    if (address >= UNIVERSE_SIZE)
3,099,740✔
384
        return;
×
385

386
    if (address + range > UNIVERSE_SIZE)
3,099,740✔
387
       range = UNIVERSE_SIZE - address;
1✔
388

389
    memset(m_preGMValues->data() + address, 0, range * sizeof(*m_preGMValues->data()));
3,099,740✔
390
    memset(m_blackoutValues->data() + address, 0, range * sizeof(*m_blackoutValues->data()));
3,099,740✔
391
    memset(m_relativeValues.data() + address, 0, range * sizeof(*m_relativeValues.data()));
3,099,740✔
392
    memcpy(m_postGMValues->data() + address, m_modifiedZeroValues->data() + address, range * sizeof(*m_postGMValues->data()));
3,099,740✔
393

394
    applyPassthroughValues(address, range);
3,099,740✔
395
}
396

397
void Universe::applyPassthroughValues(int address, int range)
3,099,740✔
398
{
399
    if (!m_passthrough)
3,099,740✔
400
        return;
3,099,740✔
401

402
    for (int i = address; i < address + range && i < UNIVERSE_SIZE; i++)
×
403
    {
404
        if (static_cast<uchar>(m_postGMValues->at(i)) < static_cast<uchar>(m_passthroughValues->at(i))) // HTP merge
×
405
        {
406
            (*m_postGMValues)[i] = (*m_passthroughValues)[i];
×
407
        }
408
    }
409
}
410

411
void Universe::zeroIntensityChannels()
284,927✔
412
{
413
    updateIntensityChannelsRanges();
284,927✔
414
    int const* channels = m_intensityChannelsRanges.constData();
284,927✔
415
    for (int i = 0; i < m_intensityChannelsRanges.size(); ++i)
3,384,670✔
416
    {
417
        short channel = channels[i] >> 16;
3,099,740✔
418
        short size = channels[i] & 0xffff;
3,099,740✔
419

420
        reset(channel, size);
3,099,740✔
421
    }
422
}
284,927✔
423

424
QHash<int, uchar> Universe::intensityChannels()
1✔
425
{
426
    QHash <int, uchar> intensityList;
1✔
427
    for (int i = 0; i < m_intensityChannels.size(); ++i)
1✔
428
    {
429
        int channel = m_intensityChannels.at(i);
×
430
        intensityList[channel] = m_preGMValues->at(channel);
×
431
    }
432
    return intensityList;
1✔
433
}
434

435
uchar Universe::postGMValue(int address) const
×
436
{
437
    if (address >= m_postGMValues->size())
×
438
        return 0;
×
439

440
    return uchar(m_postGMValues->at(address));
×
441
}
442

443
const QByteArray* Universe::postGMValues() const
4,601✔
444
{
445
    return m_postGMValues.data();
4,601✔
446
}
447

448
void Universe::zeroRelativeValues()
264,835✔
449
{
450
    memset(m_relativeValues.data(), 0, UNIVERSE_SIZE * sizeof(*m_relativeValues.data()));
264,835✔
451
}
264,835✔
452

453
Universe::BlendMode Universe::stringToBlendMode(QString mode)
5✔
454
{
455
    if (mode == KXMLUniverseNormalBlend)
5✔
456
        return NormalBlend;
1✔
457
    else if (mode == KXMLUniverseMaskBlend)
4✔
458
        return MaskBlend;
1✔
459
    else if (mode == KXMLUniverseAdditiveBlend)
3✔
460
        return AdditiveBlend;
1✔
461
    else if (mode == KXMLUniverseSubtractiveBlend)
2✔
462
        return SubtractiveBlend;
1✔
463

464
    return NormalBlend;
1✔
465
}
466

467
QString Universe::blendModeToString(Universe::BlendMode mode)
7✔
468
{
469
    switch(mode)
7✔
470
    {
471
        default:
1✔
472
        case NormalBlend:
473
            return QString(KXMLUniverseNormalBlend);
1✔
474
        break;
475
        case MaskBlend:
1✔
476
            return QString(KXMLUniverseMaskBlend);
1✔
477
        break;
478
        case AdditiveBlend:
4✔
479
            return QString(KXMLUniverseAdditiveBlend);
4✔
480
        break;
481
        case SubtractiveBlend:
1✔
482
            return QString(KXMLUniverseSubtractiveBlend);
1✔
483
        break;
484
    }
485
}
486

487
const QByteArray Universe::preGMValues() const
1,326,100✔
488
{
489
    return *m_preGMValues;
1,326,100✔
490
}
491

492
uchar Universe::preGMValue(int address) const
17,096,500✔
493
{
494
    if (address >= m_preGMValues->size())
17,096,500✔
495
        return 0U;
×
496

497
    return static_cast<uchar>(m_preGMValues->at(address));
17,096,500✔
498
}
499

500
uchar Universe::applyRelative(int channel, uchar value)
17,095,900✔
501
{
502
    if (m_relativeValues[channel] != 0)
17,095,900✔
503
    {
504
        int val = m_relativeValues[channel] + value;
6✔
505
        return CLAMP(val, 0, (int)UCHAR_MAX);
6✔
506
    }
507

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

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

522
    return value;
17,089,600✔
523
}
524

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

530
    return value;
17,095,900✔
531
}
532

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

544
    return value;
17,095,900✔
545
}
546

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

551
    value = applyRelative(channel, value);
17,095,900✔
552

553
    if (value != 0)
17,095,900✔
554
        value = applyGM(channel, value);
17,087,000✔
555

556
    value = applyModifiers(channel, value);
17,095,900✔
557
    value = applyPassthrough(channel, value);
17,095,900✔
558

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

562
/************************************************************************
563
 * Patches
564
 ************************************************************************/
565

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

571
    return false;
1✔
572
}
573

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

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

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

606
    return true;
×
607
}
608

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

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

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

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

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

647
    return false;
648
}
649

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

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

677
    return false;
×
678
}
679

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

783
/************************************************************************
784
 * Channels capabilities
785
 ************************************************************************/
786

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

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

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

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

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

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

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

857
    if (channel >= m_usedChannels)
2,556✔
858
        m_usedChannels = channel + 1;
970✔
859

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

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

869
    m_modifiers[channel] = modifier;
2,556✔
870

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

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

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

885
    updatePostGMValue(channel);
2,556✔
886
}
887

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

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

896
void Universe::updateIntensityChannelsRanges()
284,927✔
897
{
898
    if (!m_intensityChannelsChanged)
284,927✔
899
        return;
284,890✔
900

901
    m_intensityChannelsChanged = false;
37✔
902

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

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

923
    qDebug() << Q_FUNC_INFO << ":" << m_intensityChannelsRanges.size() << "ranges";
37✔
924
}
925

926
/****************************************************************************
927
 * Writing
928
 ****************************************************************************/
929

930
bool Universe::write(int channel, uchar value, bool forceLTP)
10,248,400✔
931
{
932
    Q_ASSERT(channel < UNIVERSE_SIZE);
10,248,400✔
933

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

936
    if (channel >= m_usedChannels)
10,248,400✔
937
        m_usedChannels = channel + 1;
9,372✔
938

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

953
    (*m_preGMValues)[channel] = char(value);
10,248,400✔
954

955
    updatePostGMValue(channel);
10,248,400✔
956

957
    return true;
10,248,400✔
958
}
959

960
bool Universe::writeMultiple(int address, quint32 value, int channelCount)
1,722,370✔
961
{
962
    for (int i = 0; i < channelCount; i++)
3,444,740✔
963
    {
964
        // preserve non HTP channels for blackout
965
        if ((m_channelsMask->at(address + i) & HTP) == 0)
1,722,370✔
966
            (*m_blackoutValues)[address + i] = ((uchar *)&value)[channelCount - 1 - i];
1,722,250✔
967

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

970
        updatePostGMValue(address + i);
1,722,370✔
971
    }
972

973
    return true;
1,722,370✔
974
}
975

976
bool Universe::writeRelative(int channel, uchar value)
6✔
977
{
978
    Q_ASSERT(channel < UNIVERSE_SIZE);
6✔
979

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

982
    if (channel >= m_usedChannels)
6✔
983
        m_usedChannels = channel + 1;
1✔
984

985
    if (value == RELATIVE_ZERO)
6✔
986
        return true;
1✔
987

988
    m_relativeValues[channel] += value - RELATIVE_ZERO;
5✔
989

990
    updatePostGMValue(channel);
5✔
991

992
    return true;
5✔
993
}
994

995
bool Universe::writeBlended(int channel, quint32 value, int channelCount, Universe::BlendMode blend)
1,722,370✔
996
{
997
    if (channel + channelCount - 1 >= m_usedChannels)
1,722,370✔
998
        m_usedChannels = channel + channelCount;
1✔
999

1000
    quint32 currentValue = 0;
1,722,370✔
1001
    for (int i = 0; i < channelCount; i++)
3,444,740✔
1002
        currentValue = (currentValue << 8) + uchar(m_preGMValues->at(channel + i));
1,722,370✔
1003

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

1047
    writeMultiple(channel, value, channelCount);
1,722,370✔
1048

1049
    return true;
1,722,370✔
1050
}
1051

1052
/*********************************************************************
1053
 * Load & Save
1054
 *********************************************************************/
1055

1056
bool Universe::loadXML(QXmlStreamReader &root, int index, InputOutputMap *ioMap)
5✔
1057
{
1058
    if (root.name() != KXMLQLCUniverse)
5✔
1059
    {
1060
        qWarning() << Q_FUNC_INFO << "Universe node not found";
1✔
1061
        return false;
1✔
1062
    }
1063

1064
    int outputIndex = 0;
4✔
1065

1066
    QXmlStreamAttributes attrs = root.attributes();
4✔
1067

1068
    if (attrs.hasAttribute(KXMLQLCUniverseName))
4✔
1069
        setName(attrs.value(KXMLQLCUniverseName).toString());
4✔
1070

1071
    if (attrs.hasAttribute(KXMLQLCUniversePassthrough))
4✔
1072
    {
1073
        if (attrs.value(KXMLQLCUniversePassthrough).toString() == KXMLQLCTrue ||
8✔
1074
            attrs.value(KXMLQLCUniversePassthrough).toString() == "1")
5✔
1075
            setPassthrough(true);
2✔
1076
        else
1077
            setPassthrough(false);
1✔
1078
    }
1079
    else
1080
    {
1081
        setPassthrough(false);
1✔
1082
    }
1083

1084
    while (root.readNextStartElement())
4✔
1085
    {
1086
        QXmlStreamAttributes pAttrs = root.attributes();
×
1087

1088
        if (root.name() == KXMLQLCUniverseInputPatch)
×
1089
        {
1090
            QString plugin = KInputNone;
×
1091
            quint32 inputLine = QLCIOPlugin::invalidLine();
×
1092
            QString inputUID;
×
1093
            QString profile = KInputNone;
×
1094

1095
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1096
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1097
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1098
                inputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1099
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1100
                inputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1101
            if (pAttrs.hasAttribute(KXMLQLCUniverseProfileName))
×
1102
                profile = pAttrs.value(KXMLQLCUniverseProfileName).toString();
×
1103

1104
            // apply the parameters just loaded
1105
            ioMap->setInputPatch(index, plugin, inputUID, inputLine, profile);
×
1106

1107
            QXmlStreamReader::TokenType tType = root.readNext();
×
1108
            if (tType == QXmlStreamReader::Characters)
×
1109
                tType = root.readNext();
×
1110

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

1125
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1126
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1127
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1128
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1129
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1130
                outputLine = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1131

1132
            // apply the parameters just loaded
1133
            ioMap->setOutputPatch(index, plugin, outputUID, outputLine, false, outputIndex);
×
1134

1135
            QXmlStreamReader::TokenType tType = root.readNext();
×
1136
            if (tType == QXmlStreamReader::Characters)
×
1137
                tType = root.readNext();
×
1138

1139
            // check if there is a PluginParameters tag defined
1140
            if (tType == QXmlStreamReader::StartElement)
×
1141
            {
1142
                if (root.name() == KXMLQLCUniversePluginParameters)
×
1143
                    loadXMLPluginParameters(root, OutputPatchTag, outputIndex);
×
1144
                root.skipCurrentElement();
×
1145
            }
1146

1147
            outputIndex++;
×
1148
        }
1149
        else if (root.name() == KXMLQLCUniverseFeedbackPatch)
×
1150
        {
1151
            QString plugin = KOutputNone;
×
1152
            QString outputUID;
×
1153
            quint32 output = QLCIOPlugin::invalidLine();
×
1154

1155
            if (pAttrs.hasAttribute(KXMLQLCUniversePlugin))
×
1156
                plugin = pAttrs.value(KXMLQLCUniversePlugin).toString();
×
1157
            if (pAttrs.hasAttribute(KXMLQLCUniverseLineUID))
×
1158
                outputUID = pAttrs.value(KXMLQLCUniverseLineUID).toString();
×
1159
            if (pAttrs.hasAttribute(KXMLQLCUniverseLine))
×
1160
                output = pAttrs.value(KXMLQLCUniverseLine).toString().toUInt();
×
1161

1162
            // apply the parameters just loaded
1163
            ioMap->setOutputPatch(index, plugin, outputUID, output, true);
×
1164

1165
            QXmlStreamReader::TokenType tType = root.readNext();
×
1166
            if (tType == QXmlStreamReader::Characters)
×
1167
                tType = root.readNext();
×
1168

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

1184
    return true;
4✔
1185
}
1186

1187
bool Universe::loadXMLPluginParameters(QXmlStreamReader &root, PatchTagType currentTag, int patchIndex)
×
1188
{
1189
    if (root.name() != KXMLQLCUniversePluginParameters)
×
1190
    {
1191
        qWarning() << Q_FUNC_INFO << "PluginParameters node not found";
×
1192
        return false;
×
1193
    }
1194

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

1220
    return true;
×
1221
}
1222

1223
bool Universe::saveXML(QXmlStreamWriter *doc) const
6✔
1224
{
1225
    Q_ASSERT(doc != NULL);
6✔
1226

1227
    doc->writeStartElement(KXMLQLCUniverse);
6✔
1228
    doc->writeAttribute(KXMLQLCUniverseName, name());
6✔
1229
    doc->writeAttribute(KXMLQLCUniverseID, QString::number(id()));
6✔
1230

1231
    if (passthrough() == true)
6✔
1232
        doc->writeAttribute(KXMLQLCUniversePassthrough, KXMLQLCTrue);
1✔
1233

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

1250
    /* End the <Universe> tag */
1251
    doc->writeEndElement();
6✔
1252

1253
    return true;
6✔
1254
}
1255

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

1269
    doc->writeStartElement(tag);
×
1270
    doc->writeAttribute(KXMLQLCUniversePlugin, pluginName);
×
1271
    doc->writeAttribute(KXMLQLCUniverseLineUID, lineName);
×
1272
    doc->writeAttribute(KXMLQLCUniverseLine, QString::number(line));
×
1273
    if (!profileName.isEmpty() && profileName != KInputNone)
×
1274
        doc->writeAttribute(KXMLQLCUniverseProfileName, profileName);
×
1275

1276
    savePluginParametersXML(doc, parameters);
×
1277
    doc->writeEndElement();
×
1278
}
1279

1280
bool Universe::savePluginParametersXML(QXmlStreamWriter *doc,
×
1281
                                       QMap<QString, QVariant> parameters) const
1282
{
1283
    Q_ASSERT(doc != NULL);
×
1284

1285
    if (parameters.isEmpty())
×
1286
        return false;
×
1287

1288
    doc->writeStartElement(KXMLQLCUniversePluginParameters);
×
1289
    QMapIterator<QString, QVariant> it(parameters);
×
1290
    while (it.hasNext())
×
1291
    {
1292
        it.next();
×
1293
        QString pName = it.key();
×
1294
        QVariant pValue = it.value();
×
1295
        doc->writeAttribute(pName, pValue.toString());
×
1296
    }
1297
    doc->writeEndElement();
×
1298

1299
    return true;
×
1300
}
1301

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

© 2026 Coveralls, Inc