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

mcallegari / qlcplus / 24571308339

17 Apr 2026 02:49PM UTC coverage: 34.069% (-0.008%) from 34.077%
24571308339

push

github

mcallegari
qmlui: improve VC Animation widget

- no longer run a copy of the original function
- overrides color and pattern attributes
- fix color buttons number on pattern selection
- fix color tool position

17759 of 52127 relevant lines covered (34.07%)

41189.18 hits per line

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

39.81
/engine/src/rgbmatrix.cpp
1
/*
2
  Q Light Controller Plus
3
  rgbmatrix.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 <QCoreApplication>
24
#include <QElapsedTimer>
25
#include <QDebug>
26
#include <cmath>
27
#include <QDir>
28

29
#include "rgbscriptscache.h"
30
#include "qlcfixturehead.h"
31
#include "fixturegroup.h"
32
#include "genericfader.h"
33
#include "fadechannel.h"
34
#include "rgbmatrix.h"
35
#include "rgbimage.h"
36
#include "doc.h"
37

38
#define KXMLQLCRGBMatrixStartColor      QStringLiteral("MonoColor")
39
#define KXMLQLCRGBMatrixEndColor        QStringLiteral("EndColor")
40
#define KXMLQLCRGBMatrixColor           QStringLiteral("Color")
41
#define KXMLQLCRGBMatrixColorIndex      QStringLiteral("Index")
42

43
#define KXMLQLCRGBMatrixFixtureGroup    QStringLiteral("FixtureGroup")
44
#define KXMLQLCRGBMatrixDimmerControl   QStringLiteral("DimmerControl")
45

46
#define KXMLQLCRGBMatrixProperty        QStringLiteral("Property")
47
#define KXMLQLCRGBMatrixPropertyName    QStringLiteral("Name")
48
#define KXMLQLCRGBMatrixPropertyValue   QStringLiteral("Value")
49

50
#define KXMLQLCRGBMatrixControlMode         QStringLiteral("ControlMode")
51
#define KXMLQLCRGBMatrixControlModeRgb      QStringLiteral("RGB")
52
#define KXMLQLCRGBMatrixControlModeAmber    QStringLiteral("Amber")
53
#define KXMLQLCRGBMatrixControlModeWhite    QStringLiteral("White")
54
#define KXMLQLCRGBMatrixControlModeUV       QStringLiteral("UV")
55
#define KXMLQLCRGBMatrixControlModeDimmer   QStringLiteral("Dimmer")
56
#define KXMLQLCRGBMatrixControlModeShutter  QStringLiteral("Shutter")
57

58
static const int RGBMatrixColorMask = 0x00FFFFFF;
59

60
/****************************************************************************
61
 * Initialization
62
 ****************************************************************************/
63

64
RGBMatrix::RGBMatrix(Doc *doc)
9✔
65
    : Function(doc, Function::RGBMatrixType)
66
    , m_dimmerControl(false)
9✔
67
    , m_fixtureGroupID(FixtureGroup::invalidId())
18✔
68
    , m_group(NULL)
9✔
69
    , m_requestEngineCreation(true)
9✔
70
    , m_runAlgorithm(NULL)
9✔
71
    , m_algorithm(NULL)
9✔
72
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
73
    , m_algorithmMutex(QMutex::Recursive)
74
#endif
75
    , m_stepHandler(new RGBMatrixStep())
9✔
76
    , m_stepsCount(0)
9✔
77
    , m_stepBeatDuration(0)
9✔
78
    , m_applyingStyleAttributes(false)
9✔
79
    , m_controlMode(RGBMatrix::ControlModeRgb)
18✔
80
{
81
    setName(tr("New RGB Matrix"));
9✔
82
    setDuration(500);
9✔
83

84
    m_rgbColors.fill(QColor(), RGBAlgorithmColorDisplayCount);
9✔
85
    setColor(0, Qt::red);
9✔
86

87
    setAlgorithm(RGBAlgorithm::algorithm(doc, "Stripes"));
9✔
88

89
    for (int i = 0; i < ColorAttributeCount; ++i)
54✔
90
    {
91
        registerAttribute(tr("Color %1").arg(i + 1), LastWins | Single, -1.0, 16777215.0,
54✔
92
                          getColor(i).isValid() ? int(getColor(i).rgb() & RGBMatrixColorMask) : -1);
99✔
93
    }
94

95
    int algoCount = RGBAlgorithm::algorithms(doc).count();
9✔
96
    registerAttribute(tr("Pattern"), LastWins | Single, 0.0, algoCount > 0 ? algoCount - 1 : 0, algorithmIndex());
9✔
97
}
9✔
98

99
RGBMatrix::~RGBMatrix()
11✔
100
{
101
    //if (m_runAlgorithm != NULL)
102
    //    delete m_runAlgorithm;
103
    delete m_algorithm;
9✔
104
    delete m_stepHandler;
9✔
105
}
11✔
106

107
QIcon RGBMatrix::getIcon() const
×
108
{
109
    return QIcon(":/rgbmatrix.png");
×
110
}
111

112
int RGBMatrix::algorithmIndex() const
22✔
113
{
114
    if (m_algorithm == NULL || doc() == NULL)
22✔
115
        return 0;
×
116

117
    QStringList algoList = RGBAlgorithm::algorithms(doc());
22✔
118
    int idx = algoList.indexOf(m_algorithm->name());
22✔
119
    return idx >= 0 ? idx : 0;
22✔
120
}
22✔
121

122
void RGBMatrix::setTotalDuration(quint32 msec)
1✔
123
{
124
    QMutexLocker algorithmLocker(&m_algorithmMutex);
1✔
125

126
    if (m_algorithm == NULL)
1✔
127
        return;
×
128

129
    FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
1✔
130
    if (grp == NULL)
1✔
131
        return;
×
132

133
    int steps = m_algorithm->rgbMapStepCount(grp->size());
1✔
134
    setDuration(msec / steps);
1✔
135
}
1✔
136

137
quint32 RGBMatrix::totalDuration()
3✔
138
{
139
    QMutexLocker algorithmLocker(&m_algorithmMutex);
3✔
140

141
    if (m_algorithm == NULL)
3✔
142
        return 0;
×
143

144
    FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
3✔
145
    if (grp == NULL)
3✔
146
        return 0;
1✔
147

148
    //qDebug () << "Algorithm steps:" << m_algorithm->rgbMapStepCount(grp->size());
149
    return m_algorithm->rgbMapStepCount(grp->size()) * duration();
2✔
150
}
3✔
151

152
void RGBMatrix::setDimmerControl(bool dimmerControl)
1✔
153
{
154
    m_dimmerControl = dimmerControl;
1✔
155
}
1✔
156

157
bool RGBMatrix::dimmerControl() const
2✔
158
{
159
    return m_dimmerControl;
2✔
160
}
161

162
/****************************************************************************
163
 * Copying
164
 ****************************************************************************/
165

166
Function* RGBMatrix::createCopy(Doc* doc, bool addToDoc)
1✔
167
{
168
    Q_ASSERT(doc != NULL);
1✔
169

170
    Function *copy = new RGBMatrix(doc);
1✔
171
    if (copy->copyFrom(this) == false)
1✔
172
    {
173
        delete copy;
×
174
        copy = NULL;
×
175
    }
176
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
177
    {
178
        delete copy;
×
179
        copy = NULL;
×
180
    }
181

182
    return copy;
1✔
183
}
184

185
bool RGBMatrix::copyFrom(const Function* function)
1✔
186
{
187
    const RGBMatrix *mtx = qobject_cast<const RGBMatrix*> (function);
1✔
188
    if (mtx == NULL)
1✔
189
        return false;
×
190

191
    setDimmerControl(mtx->dimmerControl());
1✔
192
    setFixtureGroup(mtx->fixtureGroup());
1✔
193

194
    m_rgbColors.clear();
1✔
195
    foreach (QColor col, mtx->getColors())
7✔
196
        m_rgbColors.append(col);
6✔
197

198
    if (mtx->algorithm() != NULL)
1✔
199
        setAlgorithm(mtx->algorithm()->clone());
1✔
200
    else
201
        setAlgorithm(NULL);
×
202

203
    setControlMode(mtx->controlMode());
1✔
204

205
    return Function::copyFrom(function);
1✔
206
}
207

208
/****************************************************************************
209
 * Fixtures
210
 ****************************************************************************/
211

212
quint32 RGBMatrix::fixtureGroup() const
35✔
213
{
214
    return m_fixtureGroupID;
35✔
215
}
216

217
void RGBMatrix::setFixtureGroup(quint32 id)
8✔
218
{
219
    m_fixtureGroupID = id;
8✔
220
    {
221
        QMutexLocker algoLocker(&m_algorithmMutex);
8✔
222
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
8✔
223
    }
8✔
224
    m_stepsCount = algorithmStepsCount();
8✔
225
}
8✔
226

227
QList<quint32> RGBMatrix::components() const
2✔
228
{
229
    if (m_group != NULL)
2✔
230
        return m_group->fixtureList();
1✔
231

232
    return QList<quint32>();
1✔
233
}
234

235
/****************************************************************************
236
 * Algorithm
237
 ****************************************************************************/
238

239
void RGBMatrix::setAlgorithm(RGBAlgorithm *algo)
13✔
240
{
241
    {
242
        QMutexLocker algorithmLocker(&m_algorithmMutex);
13✔
243
        delete m_algorithm;
13✔
244
        m_algorithm = algo;
13✔
245

246
        m_requestEngineCreation = true;
13✔
247

248
        /** If there's been a change of Script algorithm "on the fly",
249
         *  then re-apply the properties currently set in this RGBMatrix */
250
        if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
13✔
251
        {
252
            RGBScript *script = static_cast<RGBScript*> (m_algorithm);
13✔
253
            QMapIterator<QString, QString> it(m_properties);
13✔
254
            while (it.hasNext())
13✔
255
            {
256
                it.next();
×
257
                if (script->setProperty(it.key(), it.value()) == false)
×
258
                {
259
                    /** If the new algorithm doesn't expose a property,
260
                     *  then remove it from the cached list, otherwise
261
                     *  it would be carried around forever (and saved on XML) */
262
                    m_properties.take(it.key());
×
263
                }
264
            }
265

266
            QVector<uint> colors = script->rgbMapGetColors();
13✔
267
            for (int i = 0; i < colors.count(); i++)
13✔
268
                m_rgbColors.replace(i, QColor::fromRgb(colors.at(i)));
×
269
        }
13✔
270
    }
13✔
271
    m_stepsCount = algorithmStepsCount();
13✔
272

273
    if (m_applyingStyleAttributes == false)
13✔
274
        Function::adjustAttribute(algorithmIndex(), PatternAttr);
13✔
275

276
    emit changed(id());
13✔
277
}
13✔
278

279
RGBAlgorithm *RGBMatrix::algorithm() const
18✔
280
{
281
    return m_algorithm;
18✔
282
}
283

284
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
285
QMutex& RGBMatrix::algorithmMutex()
286
{
287
    return m_algorithmMutex;
288
}
289
#else
290
QRecursiveMutex& RGBMatrix::algorithmMutex()
×
291
{
292
    return m_algorithmMutex;
×
293
}
294
#endif
295

296

297
int RGBMatrix::stepsCount() const
2✔
298
{
299
    return m_stepsCount;
2✔
300
}
301

302
int RGBMatrix::algorithmStepsCount()
22✔
303
{
304
    QMutexLocker algorithmLocker(&m_algorithmMutex);
22✔
305

306
    if (m_algorithm == NULL)
22✔
307
        return 0;
×
308

309
    FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
22✔
310
    if (grp != NULL)
22✔
311
        return m_algorithm->rgbMapStepCount(grp->size());
6✔
312

313
    return 0;
16✔
314
}
22✔
315

316
void RGBMatrix::previewMap(int step, RGBMatrixStep *handler)
7✔
317
{
318
    QMutexLocker algorithmLocker(&m_algorithmMutex);
7✔
319
    if (m_algorithm == NULL || handler == NULL)
7✔
320
        return;
×
321

322
    if (m_group == NULL)
7✔
323
        m_group = doc()->fixtureGroup(fixtureGroup());
1✔
324

325
    if (m_group != NULL)
7✔
326
    {
327
        setMapColors(m_algorithm);
6✔
328
        m_algorithm->rgbMap(m_group->size(), handler->stepColor().rgb(), step, handler->m_map);
6✔
329
    }
330
}
7✔
331

332
/****************************************************************************
333
 * Color
334
 ****************************************************************************/
335

336
void RGBMatrix::setColor(int i, QColor c)
25✔
337
{
338
    if (i < 0)
25✔
339
        return;
×
340

341
    if (i >= m_rgbColors.count())
25✔
342
        m_rgbColors.resize(i + 1);
×
343

344
    m_rgbColors.replace(i, c);
25✔
345
    {
346
        QMutexLocker algorithmLocker(&m_algorithmMutex);
25✔
347
        if (m_algorithm != NULL)
25✔
348
        {
349
            m_algorithm->setColors(m_rgbColors);
16✔
350
            updateColorDelta();
16✔
351
        }
352
    }
25✔
353
    setMapColors(m_algorithm);
25✔
354

355
    if (m_applyingStyleAttributes == false && i >= 0 && i < ColorAttributeCount)
25✔
356
        Function::adjustAttribute(c.isValid() ? int(c.rgb() & RGBMatrixColorMask) : -1, Color1Attr + i);
25✔
357

358
    emit changed(id());
25✔
359
}
360

361
QColor RGBMatrix::getColor(int i) const
64✔
362
{
363
    if (i < 0 || i >= m_rgbColors.count())
64✔
364
        return QColor();
×
365

366
    return m_rgbColors.at(i);
64✔
367
}
368

369
QVector<QColor> RGBMatrix::getColors() const
1✔
370
{
371
    return m_rgbColors;
1✔
372
}
373

374
void RGBMatrix::updateColorDelta()
16✔
375
{
376
    if (m_rgbColors.count() > 1)
16✔
377
        m_stepHandler->calculateColorDelta(m_rgbColors[0], m_rgbColors[1], m_algorithm);
16✔
378
}
16✔
379

380
void RGBMatrix::setMapColors(RGBAlgorithm *algorithm)
31✔
381
{
382
    QMutexLocker algorithmLocker(&m_algorithmMutex);
31✔
383
    if (algorithm == NULL)
31✔
384
        return;
9✔
385

386
    if (algorithm->apiVersion() < 3)
22✔
387
        return;
22✔
388

389
    if (m_group == NULL)
×
390
        m_group = doc()->fixtureGroup(fixtureGroup());
×
391

392
    QVector<unsigned int> rawColors;
×
393
    const int acceptColors = algorithm->acceptColors();
×
394
    rawColors.reserve(acceptColors);
×
395
    for (int i = 0; i < acceptColors; i++)
×
396
    {
397
        if (m_rgbColors.count() > i)
×
398
        {
399
            QColor col = m_rgbColors.at(i);
×
400
            rawColors.append(col.isValid() ? col.rgb() : 0);
×
401
        }
402
        else
403
        {
404
            rawColors.append(0);
×
405
        }
406
    }
407

408
    algorithm->rgbMapSetColors(rawColors);
×
409
}
31✔
410

411
/************************************************************************
412
 * Properties
413
 ************************************************************************/
414

415
void RGBMatrix::setProperty(QString propName, QString value)
1✔
416
{
417
    QMutexLocker algoLocker(&m_algorithmMutex);
1✔
418
    m_properties[propName] = value;
1✔
419
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
1✔
420
    {
421
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
1✔
422
        script->setProperty(propName, value);
1✔
423

424
        QVector<uint> colors = script->rgbMapGetColors();
1✔
425
        for (int i = 0; i < colors.count(); i++)
1✔
426
            setColor(i, QColor::fromRgb(colors.at(i)));
×
427
    }
1✔
428
    m_stepsCount = algorithmStepsCount();
1✔
429
}
1✔
430

431
QString RGBMatrix::property(QString propName)
3✔
432
{
433
    QMutexLocker algoLocker(&m_algorithmMutex);
3✔
434

435
    /** If the property is cached, then return it right away */
436
    QMap<QString, QString>::iterator it = m_properties.find(propName);
3✔
437
    if (it != m_properties.end())
3✔
438
        return it.value();
1✔
439

440
    /** Otherwise, let's retrieve it from the Script */
441
    if (m_algorithm != NULL && m_algorithm->type() == RGBAlgorithm::Script)
2✔
442
    {
443
        RGBScript *script = static_cast<RGBScript*> (m_algorithm);
2✔
444
        return script->property(propName);
2✔
445
    }
446

447
    return QString();
×
448
}
3✔
449

450
/****************************************************************************
451
 * Load & Save
452
 ****************************************************************************/
453

454
bool RGBMatrix::loadXML(QXmlStreamReader &root)
3✔
455
{
456
    if (root.name() != KXMLQLCFunction)
3✔
457
    {
458
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
459
        return false;
1✔
460
    }
461

462
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::RGBMatrixType))
4✔
463
    {
464
        qWarning() << Q_FUNC_INFO << "Function is not an RGB matrix";
1✔
465
        return false;
1✔
466
    }
467

468
    /* Load matrix contents */
469
    while (root.readNextStartElement())
12✔
470
    {
471
        if (root.name() == KXMLQLCFunctionSpeed)
11✔
472
        {
473
            loadXMLSpeed(root);
1✔
474
        }
475
        else if (root.name() == KXMLQLCFunctionTempoType)
10✔
476
        {
477
            loadXMLTempoType(root);
×
478
        }
479
        else if (root.name() == KXMLQLCRGBAlgorithm)
10✔
480
        {
481
            setAlgorithm(RGBAlgorithm::loader(doc(), root));
1✔
482
        }
483
        else if (root.name() == KXMLQLCRGBMatrixFixtureGroup)
9✔
484
        {
485
            setFixtureGroup(root.readElementText().toUInt());
1✔
486
        }
487
        else if (root.name() == KXMLQLCFunctionDirection)
8✔
488
        {
489
            loadXMLDirection(root);
1✔
490
        }
491
        else if (root.name() == KXMLQLCFunctionRunOrder)
7✔
492
        {
493
            loadXMLRunOrder(root);
1✔
494
        }
495
        // Legacy support
496
        else if (root.name() == KXMLQLCRGBMatrixStartColor)
6✔
497
        {
498
            setColor(0, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
499
        }
500
        else if (root.name() == KXMLQLCRGBMatrixEndColor)
6✔
501
        {
502
            setColor(1, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
×
503
        }
504
        else if (root.name() == KXMLQLCRGBMatrixColor)
6✔
505
        {
506
            int colorIdx = root.attributes().value(KXMLQLCRGBMatrixColorIndex).toInt();
10✔
507
            setColor(colorIdx, QColor::fromRgb(QRgb(root.readElementText().toUInt())));
5✔
508
        }
509
        else if (root.name() == KXMLQLCRGBMatrixControlMode)
1✔
510
        {
511
            setControlMode(stringToControlMode(root.readElementText()));
1✔
512
        }
513
        else if (root.name() == KXMLQLCRGBMatrixProperty)
×
514
        {
515
            QString name = root.attributes().value(KXMLQLCRGBMatrixPropertyName).toString();
×
516
            QString value = root.attributes().value(KXMLQLCRGBMatrixPropertyValue).toString();
×
517
            setProperty(name, value);
×
518
            root.skipCurrentElement();
×
519
        }
×
520
        else if (root.name() == KXMLQLCRGBMatrixDimmerControl)
×
521
        {
522
            setDimmerControl(root.readElementText().toInt());
×
523
        }
524
        else
525
        {
526
            qWarning() << Q_FUNC_INFO << "Unknown RGB matrix tag:" << root.name();
×
527
            root.skipCurrentElement();
×
528
        }
529
    }
530

531
    return true;
1✔
532
}
533

534
bool RGBMatrix::saveXML(QXmlStreamWriter *doc) const
1✔
535
{
536
    Q_ASSERT(doc != NULL);
1✔
537

538
    /* Function tag */
539
    doc->writeStartElement(KXMLQLCFunction);
2✔
540

541
    /* Common attributes */
542
    saveXMLCommon(doc);
1✔
543

544
    /* Tempo type */
545
    saveXMLTempoType(doc);
1✔
546

547
    /* Speeds */
548
    saveXMLSpeed(doc);
1✔
549

550
    /* Direction */
551
    saveXMLDirection(doc);
1✔
552

553
    /* Run order */
554
    saveXMLRunOrder(doc);
1✔
555

556
    /* Algorithm */
557
    if (m_algorithm != NULL)
1✔
558
        m_algorithm->saveXML(doc);
1✔
559

560
    /* LEGACY - Dimmer Control */
561
    if (dimmerControl())
1✔
562
        doc->writeTextElement(KXMLQLCRGBMatrixDimmerControl, QString::number(dimmerControl()));
×
563

564
    /* Colors */
565
    for (int i = 0; i < m_rgbColors.count(); i++)
6✔
566
    {
567
        if (m_rgbColors.at(i).isValid() == false)
5✔
568
            continue;
×
569

570
        doc->writeStartElement(KXMLQLCRGBMatrixColor);
10✔
571
        doc->writeAttribute(KXMLQLCRGBMatrixColorIndex, QString::number(i));
10✔
572
        doc->writeCharacters(QString::number(m_rgbColors.at(i).rgb()));
5✔
573
        doc->writeEndElement();
5✔
574
    }
575

576
    /* Control Mode */
577
    doc->writeTextElement(KXMLQLCRGBMatrixControlMode, RGBMatrix::controlModeToString(m_controlMode));
2✔
578

579
    /* Fixture Group */
580
    doc->writeTextElement(KXMLQLCRGBMatrixFixtureGroup, QString::number(fixtureGroup()));
2✔
581

582
    /* Properties */
583
    QMapIterator<QString, QString> it(m_properties);
1✔
584
    while (it.hasNext())
1✔
585
    {
586
        it.next();
×
587
        doc->writeStartElement(KXMLQLCRGBMatrixProperty);
×
588
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyName, it.key());
×
589
        doc->writeAttribute(KXMLQLCRGBMatrixPropertyValue, it.value());
×
590
        doc->writeEndElement();
×
591
    }
592

593
    /* End the <Function> tag */
594
    doc->writeEndElement();
1✔
595

596
    return true;
1✔
597
}
1✔
598

599
/****************************************************************************
600
 * Running
601
 ****************************************************************************/
602

603
void RGBMatrix::tap()
×
604
{
605
    if (stopped() == false)
×
606
    {
607
        FixtureGroup *grp = doc()->fixtureGroup(fixtureGroup());
×
608
        // Filter out taps that are too close to each other
609
        if (grp != NULL && uint(m_roundTime.elapsed()) >= (duration() / 4))
×
610
        {
611
            roundCheck();
×
612
            resetElapsed();
×
613
        }
614
    }
615
}
×
616

617
void RGBMatrix::checkEngineCreation()
×
618
{
619
    m_runAlgorithm = m_algorithm;
×
620
    m_requestEngineCreation = false;
×
621
}
×
622

623
void RGBMatrix::preRun(MasterTimer *timer)
×
624
{
625
    {
626
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
627

628
        m_group = doc()->fixtureGroup(m_fixtureGroupID);
×
629
        if (m_group == NULL)
×
630
        {
631
            // No fixture group to control
632
            stop(FunctionParent::master());
×
633
            return;
×
634
        }
635

636
        if (m_algorithm != NULL)
×
637
        {
638
            checkEngineCreation();
×
639

640
            // Copy direction from parent class direction
641
            m_stepHandler->initializeDirection(direction(), m_rgbColors[0], m_rgbColors[1], m_stepsCount, m_runAlgorithm);
×
642

643
            if (m_runAlgorithm->type() == RGBAlgorithm::Script)
×
644
            {
645
                RGBScript *script = static_cast<RGBScript*> (m_runAlgorithm);
×
646
                QMapIterator<QString, QString> it(m_properties);
×
647
                while (it.hasNext())
×
648
                {
649
                    it.next();
×
650
                    script->setProperty(it.key(), it.value());
×
651
                }
652
            }
×
653
            else if (m_runAlgorithm->type() == RGBAlgorithm::Image)
×
654
            {
655
                RGBImage *image = static_cast<RGBImage*> (m_runAlgorithm);
×
656
                if (image->animatedSource())
×
657
                    image->rewindAnimation();
×
658
            }
659
        }
660
    }
×
661

662
    m_roundTime.restart();
×
663

664
    Function::preRun(timer);
×
665
}
666

667
void RGBMatrix::write(MasterTimer *timer, QList<Universe *> universes)
×
668
{
669
    Q_UNUSED(timer);
670

671
    {
672
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
673
        if (m_group == NULL)
×
674
        {
675
            // No fixture group to control
676
            stop(FunctionParent::master());
×
677
            return;
×
678
        }
679

680
        // No time to do anything.
681
        if (duration() == 0)
×
682
            return;
×
683

684
        if (m_algorithm != NULL && m_requestEngineCreation)
×
685
            checkEngineCreation();
×
686

687
        // Invalid/nonexistent script
688
        if (m_runAlgorithm == NULL || m_runAlgorithm->apiVersion() == 0)
×
689
            return;
×
690

691
        if (isPaused() == false)
×
692
        {
693
            // Get a new map every time elapsed is reset to zero
694
            if (elapsed() < MasterTimer::tick())
×
695
            {
696
                if (tempoType() == Beats)
×
697
                    m_stepBeatDuration = beatsToTime(duration(), timer->beatTimeDuration());
×
698

699
                //qDebug() << "RGBMatrix step" << m_stepHandler->currentStepIndex() << ", color:" << QString::number(m_stepHandler->stepColor().rgb(), 16);
700
                m_runAlgorithm->rgbMap(m_group->size(), m_stepHandler->stepColor().rgb(),
×
701
                                       m_stepHandler->currentStepIndex(), m_stepHandler->m_map);
×
702
                updateMapChannels(m_stepHandler->m_map, m_group, universes);
×
703
            }
704
        }
705
    }
×
706

707
    if (isPaused() == false)
×
708
    {
709
        // Increment the ms elapsed time
710
        incrementElapsed();
×
711

712
        /* Check if we need to change direction, stop completely or go to next step
713
         * The cases are:
714
         * 1- time tempo type: act normally, on ms elapsed time
715
         * 2- beat tempo type, beat occurred: check if the elapsed beats is a multiple of
716
         *    the step beat duration. If so, proceed to the next step
717
         * 3- beat tempo type, not beat: if the ms elapsed time reached the step beat
718
         *    duration in ms, and the ms time to the next beat is not less than 1/16 of
719
         *    the step beat duration in ms, then proceed to the next step. If the ms time to the
720
         *    next beat is less than 1/16 of the step beat duration in ms, then defer the step
721
         *    change to case #2, to resync the matrix to the next beat
722
         */
723
        if (tempoType() == Time && elapsed() >= duration())
×
724
        {
725
            roundCheck();
×
726
        }
727
        else if (tempoType() == Beats)
×
728
        {
729
            if (timer->isBeat())
×
730
            {
731
                incrementElapsedBeats();
×
732
                qDebug() << "Elapsed beats:" << elapsedBeats() << ", time elapsed:" << elapsed() << ", step time:" << m_stepBeatDuration;
×
733
                if (elapsedBeats() % duration() == 0)
×
734
                {
735
                    roundCheck();
×
736
                    resetElapsed();
×
737
                }
738
            }
739
            else if (elapsed() >= m_stepBeatDuration && (uint)timer->timeToNextBeat() > m_stepBeatDuration / 16)
×
740
            {
741
                qDebug() << "Elapsed exceeded";
×
742
                roundCheck();
×
743
            }
744
        }
745
    }
746
}
747

748
void RGBMatrix::postRun(MasterTimer *timer, QList<Universe *> universes)
×
749
{
750
    uint fadeout = overrideFadeOutSpeed() == defaultSpeed() ? fadeOutSpeed() : overrideFadeOutSpeed();
×
751

752
    /* If no fade out is needed, dismiss all the requested faders.
753
     * Otherwise, set all the faders to fade out and let Universe dismiss them
754
     * when done */
755
    if (fadeout == 0)
×
756
    {
757
        dismissAllFaders();
×
758
    }
759
    else
760
    {
761
        if (tempoType() == Beats)
×
762
            fadeout = beatsToTime(fadeout, timer->beatTimeDuration());
×
763

764
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
765
        {
766
            if (!fader.isNull())
×
767
                fader->setFadeOut(true, fadeout);
×
768
        }
×
769
    }
770

771
    m_fadersMap.clear();
×
772

773
    {
774
        QMutexLocker algorithmLocker(&m_algorithmMutex);
×
775
        checkEngineCreation();
×
776
        if (m_runAlgorithm != NULL)
×
777
            m_runAlgorithm->postRun();
×
778
    }
×
779

780
    Function::postRun(timer, universes);
×
781
}
×
782

783
void RGBMatrix::roundCheck()
×
784
{
785
    QMutexLocker algorithmLocker(&m_algorithmMutex);
×
786
    if (m_algorithm == NULL)
×
787
        return;
×
788

789
    if (m_stepHandler->checkNextStep(runOrder(), m_rgbColors[0], m_rgbColors[1], m_stepsCount) == false)
×
790
        stop(FunctionParent::master());
×
791

792
    m_roundTime.restart();
×
793

794
    if (tempoType() == Beats)
×
795
        roundElapsed(m_stepBeatDuration);
×
796
    else
797
        roundElapsed(duration());
×
798
}
×
799

800
QSharedPointer<GenericFader> RGBMatrix::getFader(Universe *universe)
×
801
{
802
    // get the universe Fader first. If doesn't exist, create it
803
    if (universe == NULL)
×
804
        return QSharedPointer<GenericFader>();
×
805

806
    QSharedPointer<GenericFader> fader = m_fadersMap.value(universe->id(), QSharedPointer<GenericFader>());
×
807
    if (fader.isNull())
×
808
    {
809
        fader = universe->requestFader();
×
810
        fader->adjustIntensity(getAttributeValue(Intensity));
×
811
        fader->setBlendMode(blendMode());
×
812
        fader->setName(name());
×
813
        fader->setParentFunctionID(id());
×
814
        m_fadersMap[universe->id()] = fader;
×
815
    }
816

817
    return fader;
×
818
}
×
819

820
void RGBMatrix::updateFaderValues(FadeChannel &fc, uchar value, uint fadeTime)
×
821
{
822
    fc.setStart(fc.current());
×
823
    fc.setTarget(value);
×
824
    fc.setElapsed(0);
×
825
    fc.setReady(false);
×
826
    // fade in/out depends on target value
827
    if (value == 0)
×
828
        fc.setFadeTime(fadeOutSpeed());
×
829
    else
830
        fc.setFadeTime(fadeTime);
×
831
}
×
832

833
void RGBMatrix::updateMapChannels(const RGBMap& map, const FixtureGroup *grp, QList<Universe *> universes)
×
834
{
835
    uint fadeTime = (overrideFadeInSpeed() == defaultSpeed()) ? fadeInSpeed() : overrideFadeInSpeed();
×
836

837
    // Create/modify fade channels for ALL heads in the group
838
    QMapIterator<QLCPoint, GroupHead> it(grp->headsMap());
×
839
    while (it.hasNext())
×
840
    {
841
        it.next();
×
842
        QLCPoint pt = it.key();
×
843
        GroupHead grpHead = it.value();
×
844
        Fixture *fxi = doc()->fixture(grpHead.fxi);
×
845
        if (fxi == NULL)
×
846
            continue;
×
847

848
        QLCFixtureHead head = fxi->head(grpHead.head);
×
849

850
        if (pt.y() >= map.count() || pt.x() >= map[pt.y()].count())
×
851
            continue;
×
852

853
        uint col = map[pt.y()][pt.x()];
×
854
        QVector<quint32> channelList;
×
855
        QVector<uchar> valueList;
×
856

857
        if (m_controlMode == ControlModeRgb)
×
858
        {
859
            channelList = head.rgbChannels();
×
860

861
            if (channelList.size() == 3)
×
862
            {
863
                valueList.append(qRed(col));
×
864
                valueList.append(qGreen(col));
×
865
                valueList.append(qBlue(col));
×
866
            }
867
            else
868
            {
869
                channelList = head.cmyChannels();
×
870

871
                if (channelList.size() == 3)
×
872
                {
873
                    // CMY color mixing
874
                    QColor cmyCol(col);
×
875
                    valueList.append(cmyCol.cyan());
×
876
                    valueList.append(cmyCol.magenta());
×
877
                    valueList.append(cmyCol.yellow());
×
878
                }
879
            }
880
        }
881
        else if (m_controlMode == ControlModeShutter)
×
882
        {
883
            channelList = head.shutterChannels();
×
884

885
            if (channelList.size())
×
886
            {
887
                // make sure only one channel is in the list
888
                channelList.resize(1);
×
889
                valueList.append(rgbToGrey(col));
×
890
            }
891
        }
892
        else if (m_controlMode == ControlModeDimmer || m_dimmerControl)
×
893
        {
894
            // Collect all dimmers that affect current head:
895
            // They are the master dimmer (affects whole fixture)
896
            // and per-head dimmer.
897
            //
898
            // If there are no RGB or CMY channels, the least important* dimmer channel
899
            // is used to create grayscale image.
900
            //
901
            // The rest of the dimmer channels are set to full if dimmer control is
902
            // enabled and target color is > 0 (see
903
            // https://www.qlcplus.org/forum/viewtopic.php?f=29&t=11090)
904
            //
905
            // Note: If there is only one head, and only one dimmer channel,
906
            // make it a master dimmer in fixture definition.
907
            //
908
            // *least important - per head dimmer if present,
909
            // otherwise per fixture dimmer if present
910

911
            quint32 masterDim = fxi->masterIntensityChannel();
×
912
            quint32 headDim = head.channelNumber(QLCChannel::Intensity, QLCChannel::MSB);
×
913

914
            if (masterDim != QLCChannel::invalid())
×
915
            {
916
                channelList.append(masterDim);
×
917
                valueList.append(rgbToGrey(col));
×
918
            }
919

920
            if (headDim != QLCChannel::invalid() && headDim != masterDim)
×
921
            {
922
                channelList.append(headDim);
×
923
                valueList.append(rgbToGrey(col) == 0 ? 0 : 255);
×
924
            }
925
        }
×
926
        else
927
        {
928
            if (m_controlMode == ControlModeWhite)
×
929
                channelList.append(head.channelNumber(QLCChannel::White, QLCChannel::MSB));
×
930
            else if (m_controlMode == ControlModeAmber)
×
931
                channelList.append(head.channelNumber(QLCChannel::Amber, QLCChannel::MSB));
×
932
            else if (m_controlMode == ControlModeUV)
×
933
                channelList.append(head.channelNumber(QLCChannel::UV, QLCChannel::MSB));
×
934

935
            valueList.append(rgbToGrey(col));
×
936
        }
937

938
        quint32 absAddress = fxi->universeAddress();
×
939

940
        for (int i = 0; i < channelList.count(); i++)
×
941
        {
942
            if (channelList.at(i) == QLCChannel::invalid())
×
943
                continue;
×
944

945
            quint32 universeIndex = floor((absAddress + channelList.at(i)) / 512);
×
946
            Universe *universe = universes.at(universeIndex);
×
947
            QSharedPointer<GenericFader> fader = getFader(universe);
×
948
            if (fader.isNull())
×
949
                continue;
×
950

951
            const quint32 fixtureID = grpHead.fxi;
×
952
            const quint32 channel = channelList.at(i);
×
953
            const uchar value = valueList.at(i);
×
954
            fader->updateChannel(doc(), universe, fixtureID, channel, [this, value, fadeTime](FadeChannel &fc)
×
955
            {
956
                updateFaderValues(fc, value, fadeTime);
×
957
            });
×
958
        }
×
959
    }
×
960
}
×
961

962
uchar RGBMatrix::rgbToGrey(uint col)
×
963
{
964
    // the weights are taken from
965
    // https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
966
    return (0.299 * qRed(col) + 0.587 * qGreen(col) + 0.114 * qBlue(col));
×
967
}
968

969
/*********************************************************************
970
 * Attributes
971
 *********************************************************************/
972

973
int RGBMatrix::adjustAttribute(qreal fraction, int attributeId)
×
974
{
975
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
976

977
    if (attrIndex == Intensity)
×
978
    {
979
        foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
980
        {
981
            if (!fader.isNull())
×
982
                fader->adjustIntensity(getAttributeValue(Function::Intensity));
×
983
        }
×
984
    }
985
    else if (attrIndex >= Color1Attr && attrIndex <= ColorLastAttr)
×
986
    {
987
        applyColorAttribute(attrIndex - Color1Attr, getAttributeValue(attrIndex));
×
988
    }
989
    else if (attrIndex == PatternAttr)
×
990
    {
991
        applyPatternAttribute(getAttributeValue(PatternAttr));
×
992
    }
993

994
    return attrIndex;
×
995
}
996

997
void RGBMatrix::applyStyleAttributes()
×
998
{
999
    for (int i = 0; i < ColorAttributeCount; ++i)
×
1000
        applyColorAttribute(i, getAttributeValue(Color1Attr + i));
×
1001

1002
    applyPatternAttribute(getAttributeValue(PatternAttr));
×
1003
}
×
1004

1005
void RGBMatrix::applyColorAttribute(int colorIndex, qreal packedColor)
×
1006
{
1007
    if (colorIndex < 0 || colorIndex >= ColorAttributeCount)
×
1008
        return;
×
1009

1010
    int packed = qRound(packedColor);
×
1011
    QColor targetColor = packed < 0 ? QColor() :
1012
                                      QColor::fromRgb(static_cast<QRgb>((packed & RGBMatrixColorMask) | 0xFF000000));
×
1013
    if (getColor(colorIndex) == targetColor)
×
1014
        return;
×
1015

1016
    bool previous = m_applyingStyleAttributes;
×
1017
    m_applyingStyleAttributes = true;
×
1018
    setColor(colorIndex, targetColor);
×
1019
    m_applyingStyleAttributes = previous;
×
1020
}
1021

1022
void RGBMatrix::applyPatternAttribute(qreal patternIndex)
×
1023
{
1024
    if (doc() == NULL)
×
1025
        return;
×
1026

1027
    QStringList algoList = RGBAlgorithm::algorithms(doc());
×
1028
    if (algoList.isEmpty())
×
1029
        return;
×
1030

1031
    int idx = qRound(patternIndex);
×
1032
    if (idx < 0)
×
1033
        idx = 0;
×
1034
    else if (idx >= algoList.count())
×
1035
        idx = algoList.count() - 1;
×
1036

1037
    RGBAlgorithm *algo = RGBAlgorithm::algorithm(doc(), algoList.at(idx));
×
1038
    if (algo == NULL)
×
1039
        return;
×
1040

1041
    if (m_algorithm != NULL && m_algorithm->name() == algo->name())
×
1042
    {
1043
        delete algo;
×
1044
        return;
×
1045
    }
1046

1047
    algo->setColors(getColors());
×
1048

1049
    bool previous = m_applyingStyleAttributes;
×
1050
    m_applyingStyleAttributes = true;
×
1051
    setAlgorithm(algo);
×
1052
    m_applyingStyleAttributes = previous;
×
1053
}
×
1054

1055
/*************************************************************************
1056
 * Blend mode
1057
 *************************************************************************/
1058

1059
void RGBMatrix::setBlendMode(Universe::BlendMode mode)
×
1060
{
1061
    if (mode == blendMode())
×
1062
        return;
×
1063

1064
    foreach (QSharedPointer<GenericFader> fader, m_fadersMap)
×
1065
    {
1066
        if (!fader.isNull())
×
1067
            fader->setBlendMode(mode);
×
1068
    }
×
1069

1070
    Function::setBlendMode(mode);
×
1071
    emit changed(id());
×
1072
}
1073

1074
/*************************************************************************
1075
 * Control Mode
1076
 *************************************************************************/
1077

1078
RGBMatrix::ControlMode RGBMatrix::controlMode() const
2✔
1079
{
1080
    return m_controlMode;
2✔
1081
}
1082

1083
void RGBMatrix::setControlMode(RGBMatrix::ControlMode mode)
3✔
1084
{
1085
    m_controlMode = mode;
3✔
1086
    emit changed(id());
3✔
1087
}
3✔
1088

1089
RGBMatrix::ControlMode RGBMatrix::stringToControlMode(QString mode)
1✔
1090
{
1091
    if (mode == KXMLQLCRGBMatrixControlModeRgb)
1✔
1092
        return ControlModeRgb;
1✔
1093
    else if (mode == KXMLQLCRGBMatrixControlModeAmber)
×
1094
        return ControlModeAmber;
×
1095
    else if (mode == KXMLQLCRGBMatrixControlModeWhite)
×
1096
        return ControlModeWhite;
×
1097
    else if (mode == KXMLQLCRGBMatrixControlModeUV)
×
1098
        return ControlModeUV;
×
1099
    else if (mode == KXMLQLCRGBMatrixControlModeDimmer)
×
1100
        return ControlModeDimmer;
×
1101
    else if (mode == KXMLQLCRGBMatrixControlModeShutter)
×
1102
        return ControlModeShutter;
×
1103

1104
    return ControlModeRgb;
×
1105
}
1106

1107
QString RGBMatrix::controlModeToString(RGBMatrix::ControlMode mode)
1✔
1108
{
1109
    switch(mode)
1✔
1110
    {
1111
        default:
1✔
1112
        case ControlModeRgb:
1113
            return QString(KXMLQLCRGBMatrixControlModeRgb);
1✔
1114
        break;
1115
        case ControlModeAmber:
×
1116
            return QString(KXMLQLCRGBMatrixControlModeAmber);
×
1117
        break;
1118
        case ControlModeWhite:
×
1119
            return QString(KXMLQLCRGBMatrixControlModeWhite);
×
1120
        break;
1121
        case ControlModeUV:
×
1122
            return QString(KXMLQLCRGBMatrixControlModeUV);
×
1123
        break;
1124
        case ControlModeDimmer:
×
1125
            return QString(KXMLQLCRGBMatrixControlModeDimmer);
×
1126
        break;
1127
        case ControlModeShutter:
×
1128
            return QString(KXMLQLCRGBMatrixControlModeShutter);
×
1129
        break;
1130
    }
1131
}
1132

1133

1134
/*************************************************************************
1135
 *************************************************************************
1136
 *                          RGBMatrixStep class
1137
 *************************************************************************
1138
 *************************************************************************/
1139

1140
RGBMatrixStep::RGBMatrixStep()
10✔
1141
    : m_direction(Function::Forward)
10✔
1142
    , m_currentStepIndex(0)
10✔
1143
    , m_stepColor(QColor())
10✔
1144
    , m_crDelta(0)
10✔
1145
    , m_cgDelta(0)
10✔
1146
    , m_cbDelta(0)
10✔
1147
{
1148

1149
}
10✔
1150

1151
void RGBMatrixStep::setCurrentStepIndex(int index)
×
1152
{
1153
    m_currentStepIndex = index;
×
1154
}
×
1155

1156
int RGBMatrixStep::currentStepIndex() const
1✔
1157
{
1158
    return m_currentStepIndex;
1✔
1159
}
1160

1161
void RGBMatrixStep::calculateColorDelta(const QColor& startColor, const QColor& endColor, const RGBAlgorithm *algorithm)
16✔
1162
{
1163
    m_crDelta = 0;
16✔
1164
    m_cgDelta = 0;
16✔
1165
    m_cbDelta = 0;
16✔
1166

1167
    if (endColor.isValid() && algorithm != NULL && algorithm->acceptColors() > 1)
16✔
1168
    {
1169
        m_crDelta = endColor.red() - startColor.red();
10✔
1170
        m_cgDelta = endColor.green() - startColor.green();
10✔
1171
        m_cbDelta = endColor.blue() - startColor.blue();
10✔
1172

1173
        //qDebug() << "Color deltas:" << m_crDelta << m_cgDelta << m_cbDelta;
1174
    }
1175
}
16✔
1176

1177
void RGBMatrixStep::setStepColor(QColor color)
×
1178
{
1179
    m_stepColor = color;
×
1180
}
×
1181

1182
QColor RGBMatrixStep::stepColor() const
6✔
1183
{
1184
    return m_stepColor;
6✔
1185
}
1186

1187
void RGBMatrixStep::updateStepColor(int stepIndex, QColor startColor, int stepsCount)
×
1188
{
1189
    if (stepsCount <= 0)
×
1190
        return;
×
1191

1192
    if (stepsCount == 1)
×
1193
    {
1194
        m_stepColor = startColor;
×
1195
    }
1196
    else
1197
    {
1198
        m_stepColor.setRed(startColor.red() + (m_crDelta * stepIndex / (stepsCount - 1)));
×
1199
        m_stepColor.setGreen(startColor.green() + (m_cgDelta * stepIndex / (stepsCount - 1)));
×
1200
        m_stepColor.setBlue(startColor.blue() + (m_cbDelta * stepIndex / (stepsCount - 1)));
×
1201
    }
1202

1203
    //qDebug() << "RGBMatrix step" << stepIndex << ", color:" << QString::number(m_stepColor.rgb(), 16);
1204
}
1205

1206
void RGBMatrixStep::initializeDirection(Function::Direction direction, const QColor& startColor, const QColor& endColor, int stepsCount, const RGBAlgorithm *algorithm)
×
1207
{
1208
    m_direction = direction;
×
1209

1210
    if (m_direction == Function::Forward)
×
1211
    {
1212
        setCurrentStepIndex(0);
×
1213
        setStepColor(startColor);
×
1214
    }
1215
    else
1216
    {
1217
        setCurrentStepIndex(stepsCount - 1);
×
1218

1219
        if (endColor.isValid())
×
1220
            setStepColor(endColor);
×
1221
        else
1222
            setStepColor(startColor);
×
1223
    }
1224

1225
    calculateColorDelta(startColor, endColor, algorithm);
×
1226
}
×
1227

1228
bool RGBMatrixStep::checkNextStep(Function::RunOrder order,
×
1229
                                  QColor startColor, QColor endColor, int stepsNumber)
1230
{
1231
    if (order == Function::PingPong)
×
1232
    {
1233
        if (m_direction == Function::Forward && (m_currentStepIndex + 1) == stepsNumber)
×
1234
        {
1235
            m_direction = Function::Backward;
×
1236
            m_currentStepIndex = stepsNumber - 2;
×
1237
            if (endColor.isValid())
×
1238
                m_stepColor = endColor;
×
1239

1240
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1241
        }
1242
        else if (m_direction == Function::Backward && (m_currentStepIndex - 1) < 0)
×
1243
        {
1244
            m_direction = Function::Forward;
×
1245
            m_currentStepIndex = 1;
×
1246
            m_stepColor = startColor;
×
1247
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1248
        }
1249
        else
1250
        {
1251
            if (m_direction == Function::Forward)
×
1252
                m_currentStepIndex++;
×
1253
            else
1254
                m_currentStepIndex--;
×
1255
            updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1256
        }
1257
    }
1258
    else if (order == Function::SingleShot)
×
1259
    {
1260
        if (m_direction == Function::Forward)
×
1261
        {
1262
            if (m_currentStepIndex >= stepsNumber - 1)
×
1263
                return false;
×
1264
            else
1265
            {
1266
                m_currentStepIndex++;
×
1267
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1268
            }
1269
        }
1270
        else
1271
        {
1272
            if (m_currentStepIndex <= 0)
×
1273
                return false;
×
1274
            else
1275
            {
1276
                m_currentStepIndex--;
×
1277
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1278
            }
1279
        }
1280
    }
1281
    else
1282
    {
1283
        if (m_direction == Function::Forward)
×
1284
        {
1285
            if (m_currentStepIndex >= stepsNumber - 1)
×
1286
            {
1287
                m_currentStepIndex = 0;
×
1288
                m_stepColor = startColor;
×
1289
            }
1290
            else
1291
            {
1292
                m_currentStepIndex++;
×
1293
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1294
            }
1295
        }
1296
        else
1297
        {
1298
            if (m_currentStepIndex <= 0)
×
1299
            {
1300
                m_currentStepIndex = stepsNumber - 1;
×
1301
                if (endColor.isValid())
×
1302
                    m_stepColor = endColor;
×
1303
            }
1304
            else
1305
            {
1306
                m_currentStepIndex--;
×
1307
                updateStepColor(m_currentStepIndex, startColor, stepsNumber);
×
1308
            }
1309
        }
1310
    }
1311

1312
    return true;
×
1313
}
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