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

mcallegari / qlcplus / 18357067171

08 Oct 2025 08:16PM UTC coverage: 34.26% (+2.2%) from 32.066%
18357067171

push

github

mcallegari
Merge branch 'master' into filedialog

1282 of 4424 new or added lines in 152 files covered. (28.98%)

1342 existing lines in 152 files now uncovered.

17704 of 51675 relevant lines covered (34.26%)

19430.31 hits per line

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

76.69
/engine/src/scene.cpp
1
/*
2
  Q Light Controller Plus
3
  scene.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7

8
  Licensed under the Apache License, Version 2.0 (the "License");
9
  you may not use this file except in compliance with the License.
10
  You may obtain a copy of the License at
11

12
      http://www.apache.org/licenses/LICENSE-2.0.txt
13

14
  Unless required by applicable law or agreed to in writing, software
15
  distributed under the License is distributed on an "AS IS" BASIS,
16
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
  See the License for the specific language governing permissions and
18
  limitations under the License.
19
*/
20

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QDebug>
24
#include <cmath>
25
#include <QList>
26
#include <QFile>
27

28
#include "qlcfixturedef.h"
29
#include "qlccapability.h"
30

31
#include "genericfader.h"
32
#include "mastertimer.h"
33
#include "universe.h"
34
#include "scene.h"
35
#include "doc.h"
36
#include "bus.h"
37

38
/*****************************************************************************
39
 * Initialization
40
 *****************************************************************************/
41

42
Scene::Scene(Doc* doc)
214✔
43
    : Function(doc, Function::SceneType)
44
    , m_legacyFadeBus(Bus::invalid())
428✔
45
    , m_flashOverrides(false)
214✔
46
    , m_flashForceLTP(false)
214✔
47
    , m_blendFunctionID(Function::invalidId())
214✔
48
{
49
    setName(tr("New Scene"));
214✔
50
    registerAttribute(tr("ParentIntensity"), Multiply | Single);
214✔
51
}
214✔
52

53
Scene::~Scene()
414✔
54
{
55
}
414✔
56

57
QIcon Scene::getIcon() const
×
58
{
59
    return QIcon(":/scene.png");
×
60
}
61

62
quint32 Scene::totalDuration()
2✔
63
{
64
    return (quint32)duration();
2✔
65
}
66

67
/*****************************************************************************
68
 * Copying
69
 *****************************************************************************/
70

71
Function* Scene::createCopy(Doc* doc, bool addToDoc)
3✔
72
{
73
    Q_ASSERT(doc != NULL);
3✔
74

75
    Function* copy = new Scene(doc);
3✔
76
    if (copy->copyFrom(this) == false)
3✔
77
    {
78
        delete copy;
×
79
        copy = NULL;
×
80
    }
81
    if (addToDoc == true && doc->addFunction(copy) == false)
3✔
82
    {
83
        delete copy;
×
84
        copy = NULL;
×
85
    }
86

87
    return copy;
3✔
88
}
89

90
bool Scene::copyFrom(const Function* function)
6✔
91
{
92
    const Scene* scene = qobject_cast<const Scene*> (function);
6✔
93
    if (scene == NULL)
6✔
94
        return false;
1✔
95

96
    m_values.clear();
5✔
97
    m_values = scene->m_values;
5✔
98
    m_fixtures.clear();
5✔
99
    m_fixtures = scene->m_fixtures;
5✔
100
    m_channelGroups.clear();
5✔
101
    m_channelGroups = scene->m_channelGroups;
5✔
102
    m_channelGroupsLevels.clear();
5✔
103
    m_channelGroupsLevels = scene->m_channelGroupsLevels;
5✔
104
    m_fixtureGroups.clear();
5✔
105
    m_fixtureGroups = scene->m_fixtureGroups;
5✔
106
    m_palettes.clear();
5✔
107
    m_palettes = scene->m_palettes;
5✔
108

109
    return Function::copyFrom(function);
5✔
110
}
111

112
/*****************************************************************************
113
 * Values
114
 *****************************************************************************/
115

116
void Scene::setValue(const SceneValue& scv, bool blind, bool checkHTP)
556✔
117
{
118
    bool valChanged = false;
556✔
119

120
    if (!m_fixtures.contains(scv.fxi))
556✔
121
    {
122
        qWarning() << Q_FUNC_INFO << "Setting value for unknown fixture" << scv.fxi << ". Adding it.";
202✔
123
        m_fixtures.append(scv.fxi);
202✔
124
    }
125

126
    {
127
        QMutexLocker locker(&m_valueListMutex);
556✔
128

129
        QMap<SceneValue, uchar>::iterator it = m_values.find(scv);
556✔
130
        if (it == m_values.end())
556✔
131
        {
132
            m_values.insert(scv, scv.value);
553✔
133
            valChanged = true;
553✔
134
        }
135
        else if (it.value() != scv.value)
3✔
136
        {
137
            const_cast<uchar&>(it.key().value) = scv.value;
3✔
138
            it.value() = scv.value;
3✔
139
            valChanged = true;
3✔
140
        }
141

142
        // if the scene is running, we must
143
        // update/add the changed channel
144
        if (blind == false && m_fadersMap.isEmpty() == false)
556✔
145
        {
146
            Fixture *fixture = doc()->fixture(scv.fxi);
2✔
147
            if (fixture != NULL)
2✔
148
            {
149
                quint32 universe = fixture->universe();
2✔
150

151
                FadeChannel fc(doc(), scv.fxi, scv.channel);
2✔
152
                fc.setStart(scv.value);
2✔
153
                fc.setTarget(scv.value);
2✔
154
                fc.setCurrent(scv.value);
2✔
155
                fc.setFadeTime(0);
2✔
156

157
                if (m_fadersMap.contains(universe))
2✔
158
                {
159
                    if (checkHTP == false)
2✔
160
                        m_fadersMap[universe]->replace(fc);
1✔
161
                    else
162
                        m_fadersMap[universe]->add(fc);
1✔
163
                }
164
            }
2✔
165
        }
166
    }
556✔
167

168
    emit changed(this->id());
556✔
169
    if (valChanged)
556✔
170
        emit valueChanged(scv);
556✔
171
}
556✔
172

173
void Scene::setValue(quint32 fxi, quint32 ch, uchar value)
547✔
174
{
175
    setValue(SceneValue(fxi, ch, value));
547✔
176
}
547✔
177

178
void Scene::unsetValue(quint32 fxi, quint32 ch)
5✔
179
{
180
    if (!m_fixtures.contains(fxi))
5✔
181
        qWarning() << Q_FUNC_INFO << "Unsetting value for unknown fixture" << fxi;
1✔
182

183
    {
184
        QMutexLocker locker(&m_valueListMutex);
5✔
185
        m_values.remove(SceneValue(fxi, ch, 0));
5✔
186
    }
5✔
187

188
    emit changed(this->id());
5✔
189
}
5✔
190

191
uchar Scene::value(quint32 fxi, quint32 ch)
421✔
192
{
193
    return m_values.value(SceneValue(fxi, ch, 0), 0);
421✔
194
}
195

196
bool Scene::checkValue(SceneValue val)
409✔
197
{
198
    return m_values.contains(val);
409✔
199
}
200

201
QList <SceneValue> Scene::values() const
222✔
202
{
203
    return m_values.keys();
222✔
204
}
205

206
QList<quint32> Scene::components()
1✔
207
{
208
    QList<quint32> ids;
1✔
209

210
    QMap <SceneValue, uchar>::iterator it = m_values.begin();
1✔
211
    for (; it != m_values.end(); it++)
5✔
212
    {
213
        const SceneValue& scv = it.key();
4✔
214
        if (ids.contains(scv.fxi) == false)
4✔
215
            ids.append(scv.fxi);
3✔
216
    }
217

218
    return ids;
2✔
UNCOV
219
}
×
220

221
QColor Scene::colorValue(quint32 fxi)
3✔
222
{
223
    int rVal = 0, gVal = 0, bVal = 0;
3✔
224
    int cVal = -1, mVal = -1, yVal = -1;
3✔
225
    bool found = false;
3✔
226
    QColor CMYcol;
3✔
227

228
    QMap <SceneValue, uchar>::iterator it = m_values.begin();
3✔
229
    for (; it != m_values.end(); it++)
27✔
230
    {
231
        const SceneValue& scv = it.key();
24✔
232

233
        if (fxi != Fixture::invalidId() && fxi != scv.fxi)
24✔
234
            continue;
16✔
235

236
        Fixture *fixture = doc()->fixture(scv.fxi);
8✔
237
        if (fixture == NULL)
8✔
238
            continue;
×
239

240
        const QLCChannel* channel = fixture->channel(scv.channel);
8✔
241
        if (channel == NULL)
8✔
242
            continue;
×
243

244
        if (channel->group() == QLCChannel::Intensity)
8✔
245
        {
246
            QLCChannel::PrimaryColour col = channel->colour();
7✔
247
            switch (col)
7✔
248
            {
249
                case QLCChannel::Red: rVal = scv.value; found = true; break;
1✔
250
                case QLCChannel::Green: gVal = scv.value; found = true; break;
1✔
251
                case QLCChannel::Blue: bVal = scv.value; found = true; break;
1✔
252
                case QLCChannel::Cyan: cVal = scv.value; break;
1✔
253
                case QLCChannel::Magenta: mVal = scv.value; break;
1✔
254
                case QLCChannel::Yellow: yVal = scv.value; break;
1✔
255
                case QLCChannel::White: rVal = gVal = bVal = scv.value; found = true; break;
×
256
                default: break;
1✔
257
            }
258
        }
259
        else if (channel->group() == QLCChannel::Colour)
1✔
260
        {
261
            QLCCapability *cap = channel->searchCapability(scv.value);
1✔
262
            if (cap &&
2✔
263
                (cap->presetType() == QLCCapability::SingleColor ||
1✔
264
                 cap->presetType() == QLCCapability::DoubleColor))
×
265
            {
266
                QColor col = cap->resource(0).value<QColor>();
1✔
267
                rVal = col.red();
1✔
268
                gVal = col.green();
1✔
269
                bVal = col.blue();
1✔
270
                found = true;
1✔
271
            }
272
        }
273

274
        if (cVal >= 0 && mVal >= 0 && yVal >= 0)
8✔
275
        {
276
            CMYcol.setCmyk(cVal, mVal, yVal, 0);
1✔
277
            rVal = CMYcol.red();
1✔
278
            gVal = CMYcol.green();
1✔
279
            bVal = CMYcol.blue();
1✔
280
            found = true;
1✔
281
        }
282
    }
283

284
    if (found)
3✔
285
        return QColor(rVal, gVal, bVal);
3✔
286

287
    return QColor();
×
288
}
289

290
void Scene::clear()
1✔
291
{
292
    m_values.clear();
1✔
293
    m_fixtures.clear();
1✔
294
    m_fixtureGroups.clear();
1✔
295
    m_palettes.clear();
1✔
296
}
1✔
297

298
/*********************************************************************
299
 * Channel Groups
300
 *********************************************************************/
301

302
void Scene::addChannelGroup(quint32 id)
2✔
303
{
304
    if (m_channelGroups.contains(id) == false)
2✔
305
    {
306
        m_channelGroups.append(id);
1✔
307
        m_channelGroupsLevels.append(0);
1✔
308
    }
309
}
2✔
310

311
void Scene::removeChannelGroup(quint32 id)
2✔
312
{
313
    int idx = m_channelGroups.indexOf(id);
2✔
314
    if (idx != -1)
2✔
315
    {
316
        m_channelGroups.removeAt(idx);
1✔
317
        m_channelGroupsLevels.removeAt(idx);
1✔
318
    }
319
}
2✔
320

321
void Scene::setChannelGroupLevel(quint32 id, uchar level)
1✔
322
{
323
    int idx = m_channelGroups.indexOf(id);
1✔
324
    if (idx >= 0 && idx < m_channelGroupsLevels.count())
1✔
325
        m_channelGroupsLevels[idx] = level;
1✔
326
}
1✔
327

328
QList<uchar> Scene::channelGroupsLevels()
6✔
329
{
330
    return m_channelGroupsLevels;
6✔
331
}
332

333
QList<quint32> Scene::channelGroups()
6✔
334
{
335
    return m_channelGroups;
6✔
336
}
337

338
/*****************************************************************************
339
 * Fixtures
340
 *****************************************************************************/
341

342
void Scene::slotFixtureRemoved(quint32 fxi_id)
4✔
343
{
344
    bool hasChanged = false;
4✔
345

346
    QMutableMapIterator <SceneValue, uchar> it(m_values);
4✔
347
    while (it.hasNext() == true)
10✔
348
    {
349
        SceneValue value(it.next().key());
6✔
350
        if (value.fxi == fxi_id)
6✔
351
        {
352
            it.remove();
2✔
353
            hasChanged = true;
2✔
354
        }
355
    }
6✔
356

357
    if (removeFixture(fxi_id))
4✔
358
        hasChanged = true;
2✔
359

360
    if (hasChanged)
4✔
361
        emit changed(this->id());
2✔
362
}
4✔
363

364
void Scene::addFixture(quint32 fixtureId)
7✔
365
{
366
    if (m_fixtures.contains(fixtureId) == false)
7✔
367
        m_fixtures.append(fixtureId);
7✔
368
}
7✔
369

370
bool Scene::removeFixture(quint32 fixtureId)
4✔
371
{
372
    return m_fixtures.removeOne(fixtureId);
4✔
373
}
374

375
QList<quint32> Scene::fixtures() const
2✔
376
{
377
    return m_fixtures;
2✔
378
}
379

380
/*********************************************************************
381
 * Fixture Groups
382
 *********************************************************************/
383

384
void Scene::addFixtureGroup(quint32 id)
×
385
{
386
    if (m_fixtureGroups.contains(id) == false)
×
387
        m_fixtureGroups.append(id);
×
388
}
×
389

390
bool Scene::removeFixtureGroup(quint32 id)
×
391
{
392
    return m_fixtureGroups.removeOne(id);
×
393
}
394

395
QList<quint32> Scene::fixtureGroups() const
×
396
{
397
    return m_fixtureGroups;
×
398
}
399

400
/*********************************************************************
401
 * Palettes
402
 *********************************************************************/
403

404
void Scene::addPalette(quint32 id)
×
405
{
406
    if (m_palettes.contains(id) == false)
×
407
        m_palettes.append(id);
×
408
}
×
409

410
bool Scene::removePalette(quint32 id)
×
411
{
412
    return m_palettes.removeOne(id);
×
413
}
414

415
QList<quint32> Scene::palettes() const
122✔
416
{
417
    return m_palettes;
122✔
418
}
419

420
/*****************************************************************************
421
 * Load & Save
422
 *****************************************************************************/
423

424
bool Scene::saveXML(QXmlStreamWriter *doc)
2✔
425
{
426
    Q_ASSERT(doc != NULL);
2✔
427

428
    /* Function tag */
429
    doc->writeStartElement(KXMLQLCFunction);
4✔
430

431
    /* Common attributes */
432
    saveXMLCommon(doc);
2✔
433

434
    /* Speed */
435
    saveXMLSpeed(doc);
2✔
436

437
    /* Channel groups */
438
    if (m_channelGroups.count() > 0)
2✔
439
    {
440
        QString chanGroupsIDs;
×
441
        for (int i = 0; i < m_channelGroups.size(); ++i)
×
442
        {
443
            if (chanGroupsIDs.isEmpty() == false)
×
444
                chanGroupsIDs.append(QString(","));
×
445
            int id = m_channelGroups.at(i);
×
446
            int val = m_channelGroupsLevels.at(i);
×
447
            chanGroupsIDs.append(QString("%1,%2").arg(id).arg(val));
×
448
        }
449
        doc->writeTextElement(KXMLQLCSceneChannelGroupsValues, chanGroupsIDs);
×
UNCOV
450
    }
×
451

452
    /* Scene contents */
453
    // make a copy of the Scene values cause we need to empty it in the process
454
    QList<SceneValue> values = m_values.keys();
2✔
455

456
    // loop through the Scene Fixtures in the order they've been added
457
    foreach (quint32 fxId, m_fixtures)
4✔
458
    {
459
        QStringList currFixValues;
2✔
460
        bool found = false;
2✔
461

462
        // look for the values that match the current Fixture ID
463
        for (int j = 0; j < values.count(); j++)
6✔
464
        {
465
            SceneValue scv = values.at(j);
5✔
466
            if (scv.fxi != fxId)
5✔
467
            {
468
                if (found == true)
1✔
469
                    break;
1✔
470
                else
471
                    continue;
×
472
            }
473

474
            found = true;
4✔
475
            currFixValues.append(QString::number(scv.channel));
4✔
476
            // IMPORTANT: if a Scene is hidden, so used as a container by some Sequences,
477
            // it must be saved with values set to zero
478
            currFixValues.append(QString::number(isVisible() ? scv.value : 0));
4✔
479
            values.removeAt(j);
4✔
480
            j--;
4✔
481
        }
5✔
482

483
        saveXMLFixtureValues(doc, fxId, currFixValues);
2✔
484
    }
4✔
485

486
    /* Save referenced Fixture Groups */
487
    foreach (quint32 groupId, m_fixtureGroups)
2✔
488
    {
489
        doc->writeStartElement(KXMLQLCFixtureGroup);
×
490
        doc->writeAttribute(KXMLQLCFixtureGroupID, QString::number(groupId));
×
491
        doc->writeEndElement();
×
492
    }
2✔
493

494
    /* Save referenced Palettes */
495
    foreach (quint32 pId, m_palettes)
2✔
496
    {
497
        doc->writeStartElement(KXMLQLCPalette);
×
498
        doc->writeAttribute(KXMLQLCPaletteID, QString::number(pId));
×
499
        doc->writeEndElement();
×
500
    }
2✔
501

502
    /* End the <Function> tag */
503
    doc->writeEndElement();
2✔
504

505
    return true;
2✔
506
}
2✔
507

508
bool Scene::saveXMLFixtureValues(QXmlStreamWriter* doc, quint32 fixtureID, QStringList const& values)
2✔
509
{
510
    doc->writeStartElement(KXMLQLCFixtureValues);
4✔
511
    doc->writeAttribute(KXMLQLCFixtureID, QString::number(fixtureID));
4✔
512
    if (values.size() > 0)
2✔
513
        doc->writeCharacters(values.join(","));
2✔
514
    doc->writeEndElement();
2✔
515
    return true;
2✔
516
}
517

518
bool Scene::loadXML(QXmlStreamReader &root)
4✔
519
{
520
    if (root.name() != KXMLQLCFunction)
4✔
521
    {
522
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
523
        return false;
1✔
524
    }
525

526
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::SceneType))
6✔
527
    {
528
        qWarning() << Q_FUNC_INFO << "Function is not a scene";
1✔
529
        return false;
1✔
530
    }
531

532
    /* Load scene contents */
533
    while (root.readNextStartElement())
9✔
534
    {
535
        if (root.name() == KXMLQLCBus)
7✔
536
        {
537
            m_legacyFadeBus = root.readElementText().toUInt();
2✔
538
        }
539
        else if (root.name() == KXMLQLCFunctionSpeed)
5✔
540
        {
541
            loadXMLSpeed(root);
1✔
542
        }
543
        else if (root.name() == KXMLQLCSceneChannelGroups)
4✔
544
        {
545
            QString chGrpIDs = root.readElementText();
×
546
            if (chGrpIDs.isEmpty() == false)
×
547
            {
548
                QStringList grpArray = chGrpIDs.split(",");
×
NEW
549
                foreach (QString grp, grpArray)
×
550
                {
551
                    m_channelGroups.append(grp.toUInt());
×
552
                    m_channelGroupsLevels.append(0);
×
UNCOV
553
                }
×
UNCOV
554
            }
×
UNCOV
555
        }
×
556
        else if (root.name() == KXMLQLCSceneChannelGroupsValues)
4✔
557
        {
558
            QString chGrpIDs = root.readElementText();
×
559
            if (chGrpIDs.isEmpty() == false)
×
560
            {
561
                QStringList grpArray = chGrpIDs.split(",");
×
562
                for (int i = 0; i + 1 < grpArray.count(); i+=2)
×
563
                {
564
                    m_channelGroups.append(grpArray.at(i).toUInt());
×
565
                    m_channelGroupsLevels.append(grpArray.at(i + 1).toUInt());
×
566
                }
UNCOV
567
            }
×
UNCOV
568
        }
×
569
        /* "old" style XML */
570
        else if (root.name() == KXMLQLCFunctionValue)
4✔
571
        {
572
            /* Channel value */
573
            SceneValue scv;
3✔
574
            if (scv.loadXML(root) == true)
3✔
575
                setValue(scv);
3✔
576
        }
3✔
577
        /* "new" style XML */
578
        else if (root.name() == KXMLQLCFixtureValues)
1✔
579
        {
580
            quint32 fxi = root.attributes().value(KXMLQLCFixtureID).toString().toUInt();
×
581
            addFixture(fxi);
×
582
            QString strvals = root.readElementText();
×
583
            if (strvals.isEmpty() == false)
×
584
            {
585
                QStringList varray = strvals.split(",");
×
586
                for (int i = 0; i + 1 < varray.count(); i+=2)
×
587
                {
588
                    SceneValue scv;
×
589
                    scv.fxi = fxi;
×
590
                    scv.channel = QString(varray.at(i)).toUInt();
×
591
                    scv.value = uchar(QString(varray.at(i + 1)).toInt());
×
592
                    setValue(scv);
×
UNCOV
593
                }
×
UNCOV
594
            }
×
UNCOV
595
        }
×
596
        else if (root.name() == KXMLQLCFixtureGroup)
1✔
597
        {
598
            quint32 id = root.attributes().value(KXMLQLCFixtureGroupID).toString().toUInt();
×
599
            addFixtureGroup(id);
×
600
            root.skipCurrentElement();
×
601
        }
602
        else if (root.name() == KXMLQLCPalette)
1✔
603
        {
604
            quint32 id = root.attributes().value(KXMLQLCPaletteID).toString().toUInt();
×
605
            addPalette(id);
×
606
            root.skipCurrentElement();
×
607
        }
608
        else
609
        {
610
            qWarning() << Q_FUNC_INFO << "Unknown scene tag:" << root.name();
1✔
611
            root.skipCurrentElement();
1✔
612
        }
613
    }
614

615
    return true;
2✔
616
}
617

618
void Scene::postLoad()
×
619
{
620
    // Map legacy bus speed to fixed speed values
621
    if (m_legacyFadeBus != Bus::invalid())
×
622
    {
623
        quint32 value = Bus::instance()->value(m_legacyFadeBus);
×
624
        setFadeInSpeed((value / MasterTimer::frequency()) * 1000);
×
625
        setFadeOutSpeed((value / MasterTimer::frequency()) * 1000);
×
626
    }
627

628
    // Remove such fixtures and channels that don't exist
629
    QMutableMapIterator <SceneValue, uchar> it(m_values);
×
630
    while (it.hasNext() == true)
×
631
    {
632
        SceneValue value(it.next().key());
×
633
        Fixture* fxi = doc()->fixture(value.fxi);
×
634
        if (fxi == NULL || fxi->channel(value.channel) == NULL)
×
635
            it.remove();
×
UNCOV
636
    }
×
637
}
×
638

639
/****************************************************************************
640
 * Flashing
641
 ****************************************************************************/
642

643
void Scene::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP)
6✔
644
{
645
    if (flashing() == true)
6✔
646
        return;
1✔
647

648
    m_flashOverrides = shouldOverride;
5✔
649
    m_flashForceLTP = forceLTP;
5✔
650

651
    Q_ASSERT(timer != NULL);
5✔
652
    Function::flash(timer, shouldOverride, forceLTP);
5✔
653
    timer->registerDMXSource(this);
5✔
654
}
655

656
void Scene::unFlash(MasterTimer *timer)
5✔
657
{
658
    if (flashing() == false)
5✔
659
        return;
×
660

661
    Q_ASSERT(timer != NULL);
5✔
662
    Function::unFlash(timer);
5✔
663
}
664

665
void Scene::writeDMX(MasterTimer *timer, QList<Universe *> ua)
5✔
666
{
667
    Q_ASSERT(timer != NULL);
5✔
668

669
    if (flashing() == true)
5✔
670
    {
671
        if (m_fadersMap.isEmpty())
2✔
672
        {
673
            // Keep HTP and LTP channels up. Flash is more or less a forceful intervention
674
            // so enforce all values that the user has chosen to flash.
675
            QMap <SceneValue, uchar>::iterator it = m_values.begin();
1✔
676
            for (; it != m_values.end(); it++)
4✔
677
            {
678
                const SceneValue& sv = it.key();
3✔
679

680
                FadeChannel fc(doc(), sv.fxi, sv.channel);
3✔
681
                quint32 universe = fc.universe();
3✔
682
                if (universe == Universe::invalid())
3✔
683
                    continue;
×
684

685
                QSharedPointer<GenericFader> fader = m_fadersMap.value(universe, QSharedPointer<GenericFader>());
3✔
686
                if (fader.isNull())
3✔
687
                {
688
                    fader = ua[universe]->requestFader(m_flashOverrides ? Universe::Flashing : Universe::Auto);
1✔
689

690
                    fader->adjustIntensity(getAttributeValue(Intensity));
1✔
691
                    fader->setBlendMode(blendMode());
1✔
692
                    fader->setName(name());
1✔
693
                    fader->setParentFunctionID(id());
1✔
694
                    m_fadersMap[universe] = fader;
1✔
695
                }
696

697
                if (m_flashForceLTP)
3✔
698
                    fc.addFlag(FadeChannel::ForceLTP);
×
699
                fc.setTarget(sv.value);
3✔
700
                fc.addFlag(FadeChannel::Flashing);
3✔
701
                fader->add(fc);
3✔
702
            }
3✔
703
        }
704
    }
705
    else
706
    {
707
        handleFadersEnd(timer);
3✔
708
        timer->unregisterDMXSource(this);
3✔
709
    }
710
}
5✔
711

712
/****************************************************************************
713
 * Running
714
 ****************************************************************************/
715

716
void Scene::processValue(MasterTimer *timer, QList<Universe*> ua, uint fadeIn, SceneValue &scv)
515✔
717
{
718
    Fixture *fixture = doc()->fixture(scv.fxi);
515✔
719

720
    if (fixture == NULL)
515✔
721
        return;
×
722

723
    int universeIndex = floor((fixture->universeAddress() + scv.channel) / 512);
515✔
724
    if (universeIndex >= ua.count())
515✔
UNCOV
725
        return;
×
726

727
    Universe *universe = ua.at(universeIndex);
515✔
728

729
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universe->id(), QSharedPointer<GenericFader>());
515✔
730
    if (fader.isNull())
515✔
731
    {
732
        fader = universe->requestFader();
122✔
733
        fader->adjustIntensity(getAttributeValue(Intensity));
122✔
734
        fader->setBlendMode(blendMode());
122✔
735
        fader->setName(name());
122✔
736
        fader->setParentFunctionID(id());
122✔
737
        fader->setParentIntensity(getAttributeValue(ParentIntensity));
122✔
738
        fader->setHandleSecondary(true);
122✔
739
        m_fadersMap[universe->id()] = fader;
122✔
740
    }
741

742
    FadeChannel *fc = fader->getChannelFader(doc(), universe, scv.fxi, scv.channel);
515✔
743
    int chIndex = fc->channelIndex(scv.channel);
515✔
744

745
    /** If a blend Function has been set, check if this channel needs to
746
     *  be blended from a previous value. If so, mark it for crossfade
747
     *  and set its current value */
748
    if (blendFunctionID() != Function::invalidId())
515✔
749
    {
750
        Scene *blendScene = qobject_cast<Scene *>(doc()->function(blendFunctionID()));
407✔
751
        if (blendScene != NULL && blendScene->checkValue(scv))
407✔
752
        {
753
            fc->addFlag(FadeChannel::CrossFade);
407✔
754
            fc->setCurrent(blendScene->value(scv.fxi, scv.channel), chIndex);
407✔
755
            qDebug() << "----- BLEND from Scene" << blendScene->name()
814✔
756
                     << ", fixture:" << scv.fxi << ", channel:" << scv.channel << ", value:" << fc->current();
407✔
757
        }
758
    }
759
    else
760
    {
761
        qDebug() << "Scene" << name() << "add channel" << scv.channel << "from" << fc->current(chIndex) << "to" << scv.value;
108✔
762
    }
763

764
    fc->setStart(fc->current(chIndex), chIndex);
515✔
765
    fc->setTarget(scv.value, chIndex);
515✔
766

767
    if (fc->canFade() == false)
515✔
768
    {
769
        fc->setFadeTime(0);
×
770
    }
771
    else
772
    {
773
        if (tempoType() == Beats)
515✔
774
        {
775
            int fadeInTime = beatsToTime(fadeIn, timer->beatTimeDuration());
×
776
            int beatOffset = timer->nextBeatTimeOffset();
×
777

778
            if (fadeInTime - beatOffset > 0)
×
779
                fc->setFadeTime(fadeInTime - beatOffset);
×
780
            else
781
                fc->setFadeTime(fadeInTime);
×
782
        }
783
        else
784
        {
785
            fc->setFadeTime(fadeIn);
515✔
786
        }
787
    }
788
}
515✔
789

790
void Scene::handleFadersEnd(MasterTimer *timer)
111✔
791
{
792
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
111✔
793

794
    /* If no fade out is needed, dismiss all the requested faders.
795
     * Otherwise, set all the faders to fade out and let Universe dismiss them
796
     * when done */
797
    if (fadeout == 0)
111✔
798
    {
799
        dismissAllFaders();
106✔
800
    }
801
    else
802
    {
803
        if (tempoType() == Beats)
5✔
804
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
805

806
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
9✔
807
        {
808
            if (!fader.isNull())
4✔
809
                fader->setFadeOut(true, fadeout);
4✔
810
        }
9✔
811
    }
812

813
    m_fadersMap.clear();
111✔
814

815
    // autonomously reset a blend function if set
816
    setBlendFunctionID(Function::invalidId());
111✔
817
}
111✔
818

819
void Scene::write(MasterTimer *timer, QList<Universe*> ua)
402✔
820
{
821
    //qDebug() << Q_FUNC_INFO << elapsed();
822

823
    if (m_values.count() == 0 && m_palettes.count() == 0)
402✔
824
    {
825
        stop(FunctionParent::master());
1✔
826
        return;
1✔
827
    }
828

829
    if (m_fadersMap.isEmpty())
401✔
830
    {
831
        uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed();
122✔
832

833
        foreach (quint32 paletteID, palettes())
244✔
834
        {
835
            QLCPalette *palette = doc()->palette(paletteID);
×
836
            if (palette == NULL)
×
837
                continue;
×
838

839
            foreach (SceneValue scv, palette->valuesFromFixtureGroups(doc(), fixtureGroups()))
×
840
                processValue(timer, ua, fadeIn, scv);
×
841

842
            foreach (SceneValue scv, palette->valuesFromFixtures(doc(), fixtures()))
×
843
                processValue(timer, ua, fadeIn, scv);
×
844
        }
122✔
845

846
        QMutexLocker locker(&m_valueListMutex);
122✔
847
        QMapIterator <SceneValue, uchar> it(m_values);
122✔
848
        while (it.hasNext() == true)
637✔
849
        {
850
            SceneValue scv(it.next().key());
515✔
851
            processValue(timer, ua, fadeIn, scv);
515✔
852
        }
515✔
853
    }
122✔
854

855
    if (isPaused() == false)
401✔
856
    {
857
        incrementElapsed();
398✔
858
        if (timer->isBeat() && tempoType() == Beats)
398✔
859
            incrementElapsedBeats();
×
860
    }
861
}
862

863
void Scene::postRun(MasterTimer* timer, QList<Universe *> ua)
108✔
864
{
865
    handleFadersEnd(timer);
108✔
866

867
    Function::postRun(timer, ua);
108✔
868
}
108✔
869

870
void Scene::setPause(bool enable)
6✔
871
{
872
    if (!isRunning())
6✔
873
        return;
×
874

875
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
8✔
876
    {
877
        if (!fader.isNull())
2✔
878
            fader->setPaused(enable);
2✔
879
    }
8✔
880
    Function::setPause(enable);
6✔
881
}
882

883
/****************************************************************************
884
 * Intensity
885
 ****************************************************************************/
886

887
int Scene::adjustAttribute(qreal fraction, int attributeId)
248✔
888
{
889
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
248✔
890

891
    if (attrIndex == Intensity)
248✔
892
    {
893
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
134✔
894
        {
895
            if (!fader.isNull())
3✔
896
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
3✔
897
        }
134✔
898
    }
899
    else if (attrIndex == ParentIntensity)
117✔
900
    {
901
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
120✔
902
        {
903
            if (!fader.isNull())
3✔
904
                fader->setParentIntensity(getAttributeValue(ParentIntensity));
3✔
905
        }
120✔
906
    }
907

908
    return attrIndex;
248✔
909
}
910

911
/*************************************************************************
912
 * Blend mode
913
 *************************************************************************/
914

915
void Scene::setBlendMode(Universe::BlendMode mode)
3✔
916
{
917
    if (mode == blendMode())
3✔
918
        return;
1✔
919

920
    qDebug() << "Scene" << name() << "blend mode set to" << Universe::blendModeToString(mode);
2✔
921

922
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
2✔
923
    {
924
        if (!fader.isNull())
×
925
            fader->setBlendMode(mode);
×
926
    }
2✔
927

928
    Function::setBlendMode(mode);
2✔
929
}
930

931
quint32 Scene::blendFunctionID() const
922✔
932
{
933
    return m_blendFunctionID;
922✔
934
}
935

936
void Scene::setBlendFunctionID(quint32 fid)
204✔
937
{
938
    m_blendFunctionID = fid;
204✔
939
    if (isRunning() && fid == Function::invalidId())
204✔
940
    {
941
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
109✔
942
        {
943
            if (!fader.isNull())
×
944
                fader->resetCrossfade();
×
945
        }
109✔
946
    }
947
}
204✔
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