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

mcallegari / qlcplus / 26356589956

24 May 2026 08:40AM UTC coverage: 35.006% (-0.03%) from 35.037%
26356589956

Pull #2032

github

web-flow
Merge 77d3323dc into 100c1a70b
Pull Request #2032: engine/scene,ui/sceneditor: implement fixture reordering

4 of 56 new or added lines in 2 files covered. (7.14%)

1 existing line in 1 file now uncovered.

18296 of 52266 relevant lines covered (35.01%)

41115.32 hits per line

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

76.12
/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)
218✔
43
    : Function(doc, Function::SceneType)
44
    , m_legacyFadeBus(Bus::invalid())
436✔
45
    , m_flashOverrides(false)
218✔
46
    , m_flashForceLTP(false)
218✔
47
    , m_blendFunctionID(Function::invalidId())
218✔
48
{
49
    setName(tr("New Scene"));
218✔
50
    registerAttribute(tr("ParentIntensity"), Multiply | Single);
218✔
51
}
218✔
52

53
Scene::~Scene()
422✔
54
{
55
}
422✔
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)
575✔
117
{
118
    bool valChanged = false;
575✔
119

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

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

129
        QMap<SceneValue, uchar>::iterator it = m_values.find(scv);
575✔
130
        if (it == m_values.end())
575✔
131
        {
132
            m_values.insert(scv, scv.value);
572✔
133
            valChanged = true;
572✔
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)
575✔
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
    }
575✔
167

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

173
void Scene::setValue(quint32 fxi, quint32 ch, uchar value)
566✔
174
{
175
    setValue(SceneValue(fxi, ch, value));
566✔
176
}
566✔
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) const
427✔
192
{
193
    return m_values.value(SceneValue(fxi, ch, 0), 0);
427✔
194
}
195

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

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

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

210
    QMap <SceneValue, uchar>::const_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✔
219
}
×
220

221
QColor Scene::colorValue(quint32 fxi) const
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>::const_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
    {
368
        m_fixtures.append(fixtureId);
7✔
369
        emit changed(this->id());
7✔
370
    }
371
}
7✔
372

373
bool Scene::removeFixture(quint32 fixtureId)
4✔
374
{
375
    bool result = m_fixtures.removeOne(fixtureId);
4✔
376
    emit changed(this->id());
4✔
377
    return result;
4✔
378
}
379

NEW
380
bool Scene::moveFixture(int from, int to)
×
381
{
NEW
382
    const int length = m_fixtures.count();
×
NEW
383
    if ((from < 0) || (to < 0) || (from >= length) || (to >= length))
×
NEW
384
        return false;
×
385

NEW
386
    m_fixtures.move(from, to);
×
NEW
387
    emit changed(this->id());
×
NEW
388
    return true;
×
389
}
390

391
QList<quint32> Scene::fixtures() const
2✔
392
{
393
    return m_fixtures;
2✔
394
}
395

396
/*********************************************************************
397
 * Fixture Groups
398
 *********************************************************************/
399

400
void Scene::addFixtureGroup(quint32 id)
×
401
{
402
    if (m_fixtureGroups.contains(id) == false)
×
403
        m_fixtureGroups.append(id);
×
404
}
×
405

406
bool Scene::removeFixtureGroup(quint32 id)
×
407
{
408
    return m_fixtureGroups.removeOne(id);
×
409
}
410

411
QList<quint32> Scene::fixtureGroups() const
×
412
{
413
    return m_fixtureGroups;
×
414
}
415

416
/*********************************************************************
417
 * Palettes
418
 *********************************************************************/
419

420
void Scene::addPalette(quint32 id)
×
421
{
422
    if (m_palettes.contains(id) == false)
×
423
        m_palettes.append(id);
×
424
}
×
425

426
bool Scene::removePalette(quint32 id)
×
427
{
428
    return m_palettes.removeOne(id);
×
429
}
430

431
QList<quint32> Scene::palettes() const
126✔
432
{
433
    return m_palettes;
126✔
434
}
435

436
/*****************************************************************************
437
 * Load & Save
438
 *****************************************************************************/
439

440
bool Scene::saveXML(QXmlStreamWriter *doc) const
2✔
441
{
442
    Q_ASSERT(doc != NULL);
2✔
443

444
    /* Function tag */
445
    doc->writeStartElement(KXMLQLCFunction);
4✔
446

447
    /* Common attributes */
448
    saveXMLCommon(doc);
2✔
449

450
    /* Tempo type */
451
    saveXMLTempoType(doc);
2✔
452

453
    /* Speed */
454
    saveXMLSpeed(doc);
2✔
455

456
    /* Channel groups */
457
    if (m_channelGroups.count() > 0)
2✔
458
    {
459
        QString chanGroupsIDs;
×
460
        for (int i = 0; i < m_channelGroups.size(); ++i)
×
461
        {
462
            if (chanGroupsIDs.isEmpty() == false)
×
463
                chanGroupsIDs.append(QString(","));
×
464
            int id = m_channelGroups.at(i);
×
465
            int val = m_channelGroupsLevels.at(i);
×
466
            chanGroupsIDs.append(QString("%1,%2").arg(id).arg(val));
×
467
        }
468
        doc->writeTextElement(KXMLQLCSceneChannelGroupsValues, chanGroupsIDs);
×
469
    }
×
470

471
    /* Scene contents */
472
    // make a copy of the Scene values cause we need to empty it in the process
473
    QList<SceneValue> values = m_values.keys();
2✔
474

475
    // loop through the Scene Fixtures in the order they've been added
476
    foreach (quint32 fxId, m_fixtures)
4✔
477
    {
478
        QStringList currFixValues;
2✔
479
        bool found = false;
2✔
480

481
        // look for the values that match the current Fixture ID
482
        for (int j = 0; j < values.count(); j++)
6✔
483
        {
484
            SceneValue scv = values.at(j);
5✔
485
            if (scv.fxi != fxId)
5✔
486
            {
487
                if (found == true)
1✔
488
                    break;
1✔
489
                else
490
                    continue;
×
491
            }
492

493
            found = true;
4✔
494
            currFixValues.append(QString::number(scv.channel));
4✔
495
            // IMPORTANT: if a Scene is hidden, so used as a container by some Sequences,
496
            // it must be saved with values set to zero
497
            currFixValues.append(QString::number(isVisible() ? scv.value : 0));
4✔
498
            values.removeAt(j);
4✔
499
            j--;
4✔
500
        }
5✔
501

502
        saveXMLFixtureValues(doc, fxId, currFixValues);
2✔
503
    }
4✔
504

505
    /* Save referenced Fixture Groups */
506
    foreach (quint32 groupId, m_fixtureGroups)
2✔
507
    {
508
        doc->writeStartElement(KXMLQLCFixtureGroup);
×
509
        doc->writeAttribute(KXMLQLCFixtureGroupID, QString::number(groupId));
×
510
        doc->writeEndElement();
×
511
    }
2✔
512

513
    /* Save referenced Palettes */
514
    foreach (quint32 pId, m_palettes)
2✔
515
    {
516
        doc->writeStartElement(KXMLQLCPalette);
×
517
        doc->writeAttribute(KXMLQLCPaletteID, QString::number(pId));
×
518
        doc->writeEndElement();
×
519
    }
2✔
520

521
    /* End the <Function> tag */
522
    doc->writeEndElement();
2✔
523

524
    return true;
2✔
525
}
2✔
526

527
bool Scene::saveXMLFixtureValues(QXmlStreamWriter* doc, quint32 fixtureID, QStringList const& values)
2✔
528
{
529
    doc->writeStartElement(KXMLQLCFixtureValues);
4✔
530
    doc->writeAttribute(KXMLQLCFixtureID, QString::number(fixtureID));
4✔
531
    if (values.size() > 0)
2✔
532
        doc->writeCharacters(values.join(","));
2✔
533
    doc->writeEndElement();
2✔
534
    return true;
2✔
535
}
536

537
bool Scene::loadXML(QXmlStreamReader &root)
4✔
538
{
539
    if (root.name() != KXMLQLCFunction)
4✔
540
    {
541
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
542
        return false;
1✔
543
    }
544

545
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::SceneType))
6✔
546
    {
547
        qWarning() << Q_FUNC_INFO << "Function is not a scene";
1✔
548
        return false;
1✔
549
    }
550

551
    /* Load scene contents */
552
    while (root.readNextStartElement())
9✔
553
    {
554
        if (root.name() == KXMLQLCBus)
7✔
555
        {
556
            m_legacyFadeBus = root.readElementText().toUInt();
2✔
557
        }
558
        else if (root.name() == KXMLQLCFunctionSpeed)
5✔
559
        {
560
            loadXMLSpeed(root);
1✔
561
        }
562
        else if (root.name() == KXMLQLCFunctionTempoType)
4✔
563
        {
564
            loadXMLTempoType(root);
×
565
        }
566
        else if (root.name() == KXMLQLCSceneChannelGroups)
4✔
567
        {
568
            QString chGrpIDs = root.readElementText();
×
569
            if (chGrpIDs.isEmpty() == false)
×
570
            {
571
                QStringList grpArray = chGrpIDs.split(",");
×
572
                foreach (QString grp, grpArray)
×
573
                {
574
                    m_channelGroups.append(grp.toUInt());
×
575
                    m_channelGroupsLevels.append(0);
×
576
                }
×
577
            }
×
578
        }
×
579
        else if (root.name() == KXMLQLCSceneChannelGroupsValues)
4✔
580
        {
581
            QString chGrpIDs = root.readElementText();
×
582
            if (chGrpIDs.isEmpty() == false)
×
583
            {
584
                QStringList grpArray = chGrpIDs.split(",");
×
585
                for (int i = 0; i + 1 < grpArray.count(); i+=2)
×
586
                {
587
                    m_channelGroups.append(grpArray.at(i).toUInt());
×
588
                    m_channelGroupsLevels.append(grpArray.at(i + 1).toUInt());
×
589
                }
590
            }
×
591
        }
×
592
        /* "old" style XML */
593
        else if (root.name() == KXMLQLCFunctionValue)
4✔
594
        {
595
            /* Channel value */
596
            SceneValue scv;
3✔
597
            if (scv.loadXML(root) == true)
3✔
598
                setValue(scv);
3✔
599
        }
3✔
600
        /* "new" style XML */
601
        else if (root.name() == KXMLQLCFixtureValues)
1✔
602
        {
603
            quint32 fxi = root.attributes().value(KXMLQLCFixtureID).toString().toUInt();
×
604
            addFixture(fxi);
×
605
            QString strvals = root.readElementText();
×
606
            if (strvals.isEmpty() == false)
×
607
            {
608
                QStringList varray = strvals.split(",");
×
609
                for (int i = 0; i + 1 < varray.count(); i+=2)
×
610
                {
611
                    SceneValue scv;
×
612
                    scv.fxi = fxi;
×
613
                    scv.channel = QString(varray.at(i)).toUInt();
×
614
                    scv.value = uchar(QString(varray.at(i + 1)).toInt());
×
615
                    setValue(scv);
×
616
                }
×
617
            }
×
618
        }
×
619
        else if (root.name() == KXMLQLCFixtureGroup)
1✔
620
        {
621
            quint32 id = root.attributes().value(KXMLQLCFixtureGroupID).toString().toUInt();
×
622
            addFixtureGroup(id);
×
623
            root.skipCurrentElement();
×
624
        }
625
        else if (root.name() == KXMLQLCPalette)
1✔
626
        {
627
            quint32 id = root.attributes().value(KXMLQLCPaletteID).toString().toUInt();
×
628
            addPalette(id);
×
629
            root.skipCurrentElement();
×
630
        }
631
        else
632
        {
633
            qWarning() << Q_FUNC_INFO << "Unknown scene tag:" << root.name();
1✔
634
            root.skipCurrentElement();
1✔
635
        }
636
    }
637

638
    return true;
2✔
639
}
640

641
void Scene::postLoad()
×
642
{
643
    // Map legacy bus speed to fixed speed values
644
    if (m_legacyFadeBus != Bus::invalid())
×
645
    {
646
        quint32 value = Bus::instance()->value(m_legacyFadeBus);
×
647
        setFadeInSpeed((value / MasterTimer::frequency()) * 1000);
×
648
        setFadeOutSpeed((value / MasterTimer::frequency()) * 1000);
×
649
    }
650

651
    // Remove such fixtures and channels that don't exist
652
    QMutableMapIterator <SceneValue, uchar> it(m_values);
×
653
    while (it.hasNext() == true)
×
654
    {
655
        SceneValue value(it.next().key());
×
656
        Fixture* fxi = doc()->fixture(value.fxi);
×
657
        if (fxi == NULL || fxi->channel(value.channel) == NULL)
×
658
            it.remove();
×
659
    }
×
660
}
×
661

662
/****************************************************************************
663
 * Flashing
664
 ****************************************************************************/
665

666
void Scene::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP)
6✔
667
{
668
    if (flashing() == true)
6✔
669
        return;
1✔
670

671
    m_flashOverrides = shouldOverride;
5✔
672
    m_flashForceLTP = forceLTP;
5✔
673

674
    Q_ASSERT(timer != NULL);
5✔
675
    Function::flash(timer, shouldOverride, forceLTP);
5✔
676
    timer->registerDMXSource(this);
5✔
677
}
678

679
void Scene::unFlash(MasterTimer *timer)
5✔
680
{
681
    if (flashing() == false)
5✔
682
        return;
×
683

684
    Q_ASSERT(timer != NULL);
5✔
685
    Function::unFlash(timer);
5✔
686
}
687

688
void Scene::writeDMX(MasterTimer *timer, QList<Universe *> ua)
5✔
689
{
690
    Q_ASSERT(timer != NULL);
5✔
691

692
    if (flashing() == true)
5✔
693
    {
694
        if (m_fadersMap.isEmpty())
2✔
695
        {
696
            // Keep HTP and LTP channels up. Flash is more or less a forceful intervention
697
            // so enforce all values that the user has chosen to flash.
698
            QMap <SceneValue, uchar>::iterator it = m_values.begin();
1✔
699
            for (; it != m_values.end(); it++)
4✔
700
            {
701
                const SceneValue& sv = it.key();
3✔
702

703
                FadeChannel fc(doc(), sv.fxi, sv.channel);
3✔
704
                quint32 universe = fc.universe();
3✔
705
                if (universe == Universe::invalid())
3✔
706
                    continue;
×
707

708
                QSharedPointer<GenericFader> fader = m_fadersMap.value(universe, QSharedPointer<GenericFader>());
3✔
709
                if (fader.isNull())
3✔
710
                {
711
                    fader = ua[universe]->requestFader(m_flashOverrides ? Universe::Flashing : Universe::Auto);
1✔
712

713
                    fader->adjustIntensity(getAttributeValue(Intensity));
1✔
714
                    fader->setBlendMode(blendMode());
1✔
715
                    fader->setName(name());
1✔
716
                    fader->setParentFunctionID(id());
1✔
717
                    m_fadersMap[universe] = fader;
1✔
718
                }
719

720
                if (m_flashForceLTP)
3✔
721
                    fc.addFlag(FadeChannel::ForceLTP);
×
722
                fc.setTarget(sv.value);
3✔
723
                fc.addFlag(FadeChannel::Flashing);
3✔
724
                fader->add(fc);
3✔
725
            }
3✔
726
        }
727
    }
728
    else
729
    {
730
        handleFadersEnd(timer);
3✔
731
        timer->unregisterDMXSource(this);
3✔
732
    }
733
}
5✔
734

735
/****************************************************************************
736
 * Running
737
 ****************************************************************************/
738

739
void Scene::processValue(MasterTimer *timer, QList<Universe*> ua, uint fadeIn, const SceneValue &scv)
529✔
740
{
741
    Fixture *fixture = doc()->fixture(scv.fxi);
529✔
742

743
    if (fixture == NULL)
529✔
744
        return;
×
745

746
    int universeIndex = floor((fixture->universeAddress() + scv.channel) / 512);
529✔
747
    if (universeIndex >= ua.count())
529✔
748
        return;
×
749

750
    Universe *universe = ua.at(universeIndex);
529✔
751

752
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universe->id(), QSharedPointer<GenericFader>());
529✔
753
    if (fader.isNull())
529✔
754
    {
755
        fader = universe->requestFader();
126✔
756
        fader->adjustIntensity(getAttributeValue(Intensity));
126✔
757
        fader->setBlendMode(blendMode());
126✔
758
        fader->setName(name());
126✔
759
        fader->setParentFunctionID(id());
126✔
760
        fader->setParentIntensity(getAttributeValue(ParentIntensity));
126✔
761
        fader->setHandleSecondary(true);
126✔
762
        m_fadersMap[universe->id()] = fader;
126✔
763
    }
764

765
    const bool doBlend = blendFunctionID() != Function::invalidId();
529✔
766
    Scene *blendScene = doBlend ? qobject_cast<Scene *>(doc()->function(blendFunctionID())) : NULL;
529✔
767

768
    fader->updateChannel(doc(), universe, scv.fxi, scv.channel, [&](FadeChannel &fc)
529✔
769
    {
770
        int chIndex = fc.channelIndex(scv.channel);
529✔
771

772
        /** If a blend Function has been set, check if this channel needs to
773
         *  be blended from a previous value. If so, mark it for crossfade
774
         *  and set its current value */
775
        if (blendScene != NULL && blendScene->checkValue(scv))
529✔
776
        {
777
            fc.addFlag(FadeChannel::CrossFade);
413✔
778
            fc.setCurrent(blendScene->value(scv.fxi, scv.channel), chIndex);
413✔
779
            qDebug() << "----- BLEND from Scene" << blendScene->name()
826✔
780
                     << ", fixture:" << scv.fxi << ", channel:" << scv.channel << ", value:" << fc.current();
413✔
781
        }
782

783
        fc.setStart(fc.current(chIndex), chIndex);
529✔
784
        fc.setTarget(scv.value, chIndex);
529✔
785

786
        if (fc.canFade() == false)
529✔
787
        {
788
            fc.setFadeTime(0);
×
789
        }
790
        else
791
        {
792
            if (tempoType() == Beats)
529✔
793
            {
794
                int fadeInTime = beatsToTime(fadeIn, timer->beatTimeDuration());
×
795
                int beatOffset = timer->nextBeatTimeOffset();
×
796

797
                if (fadeInTime - beatOffset > 0)
×
798
                    fc.setFadeTime(fadeInTime - beatOffset);
×
799
                else
800
                    fc.setFadeTime(fadeInTime);
×
801
            }
802
            else
803
            {
804
                fc.setFadeTime(fadeIn);
529✔
805
            }
806
        }
807
    });
529✔
808
}
529✔
809

810
void Scene::handleFadersEnd(MasterTimer *timer)
112✔
811
{
812
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
112✔
813

814
    /* If no fade out is needed, dismiss all the requested faders.
815
     * Otherwise, set all the faders to fade out and let Universe dismiss them
816
     * when done */
817
    if (fadeout == 0)
112✔
818
    {
819
        dismissAllFaders();
107✔
820
    }
821
    else
822
    {
823
        if (tempoType() == Beats)
5✔
824
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
825

826
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
9✔
827
        {
828
            if (!fader.isNull())
4✔
829
                fader->setFadeOut(true, fadeout);
4✔
830
        }
9✔
831
    }
832

833
    m_fadersMap.clear();
112✔
834

835
    // autonomously reset a blend function if set
836
    setBlendFunctionID(Function::invalidId());
112✔
837
}
112✔
838

839
void Scene::write(MasterTimer *timer, QList<Universe*> ua)
409✔
840
{
841
    //qDebug() << Q_FUNC_INFO << elapsed();
842

843
    if (m_values.count() == 0 && m_palettes.count() == 0)
409✔
844
    {
845
        stop(FunctionParent::master());
1✔
846
        return;
1✔
847
    }
848

849
    if (m_fadersMap.isEmpty())
408✔
850
    {
851
        uint fadeIn = overrideFadeInSpeed() == defaultSpeed() ? fadeInSpeed() : overrideFadeInSpeed();
126✔
852

853
        foreach (quint32 paletteID, palettes())
252✔
854
        {
855
            QLCPalette *palette = doc()->palette(paletteID);
×
856
            if (palette == NULL)
×
857
                continue;
×
858

859
            foreach (SceneValue scv, palette->valuesFromFixtureGroups(doc(), fixtureGroups()))
×
860
                processValue(timer, ua, fadeIn, scv);
×
861

862
            foreach (SceneValue scv, palette->valuesFromFixtures(doc(), fixtures()))
×
863
                processValue(timer, ua, fadeIn, scv);
×
864
        }
126✔
865

866
        QMutexLocker locker(&m_valueListMutex);
126✔
867
        QMapIterator <SceneValue, uchar> it(m_values);
126✔
868
        while (it.hasNext() == true)
655✔
869
        {
870
            SceneValue scv(it.next().key());
529✔
871
            processValue(timer, ua, fadeIn, scv);
529✔
872
        }
529✔
873
    }
126✔
874

875
    if (isPaused() == false)
408✔
876
    {
877
        incrementElapsed();
405✔
878
        if (timer->isBeat() && tempoType() == Beats)
405✔
879
            incrementElapsedBeats();
×
880
    }
881
}
882

883
void Scene::postRun(MasterTimer* timer, QList<Universe *> ua)
109✔
884
{
885
    handleFadersEnd(timer);
109✔
886

887
    Function::postRun(timer, ua);
109✔
888
}
109✔
889

890
void Scene::setPause(bool enable)
6✔
891
{
892
    if (!isRunning())
6✔
893
        return;
×
894

895
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
8✔
896
    {
897
        if (!fader.isNull())
2✔
898
            fader->setPaused(enable);
2✔
899
    }
8✔
900
    Function::setPause(enable);
6✔
901
}
902

903
/****************************************************************************
904
 * Intensity
905
 ****************************************************************************/
906

907
int Scene::adjustAttribute(qreal fraction, int attributeId)
260✔
908
{
909
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
260✔
910

911
    if (attrIndex == Intensity)
260✔
912
    {
913
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
138✔
914
        {
915
            if (!fader.isNull())
3✔
916
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
3✔
917
        }
138✔
918
    }
919
    else if (attrIndex == ParentIntensity)
125✔
920
    {
921
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
132✔
922
        {
923
            if (!fader.isNull())
7✔
924
                fader->setParentIntensity(getAttributeValue(ParentIntensity));
7✔
925
        }
132✔
926
    }
927

928
    return attrIndex;
260✔
929
}
930

931
/*************************************************************************
932
 * Blend mode
933
 *************************************************************************/
934

935
void Scene::setBlendMode(Universe::BlendMode mode)
3✔
936
{
937
    if (mode == blendMode())
3✔
938
        return;
1✔
939

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

942
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
2✔
943
    {
944
        if (!fader.isNull())
×
945
            fader->setBlendMode(mode);
×
946
    }
2✔
947

948
    Function::setBlendMode(mode);
2✔
949
}
950

951
quint32 Scene::blendFunctionID() const
942✔
952
{
953
    return m_blendFunctionID;
942✔
954
}
955

956
void Scene::setBlendFunctionID(quint32 fid)
207✔
957
{
958
    m_blendFunctionID = fid;
207✔
959
    if (isRunning() && fid == Function::invalidId())
207✔
960
    {
961
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
112✔
962
        {
963
            if (!fader.isNull())
1✔
964
                fader->resetCrossfade();
1✔
965
        }
112✔
966
    }
967
}
207✔
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