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

mcallegari / qlcplus / 6810307726

09 Nov 2023 10:11AM UTC coverage: 28.086% (+0.02%) from 28.067%
6810307726

Pull #1477

github

web-flow
Merge c8f4c4dcb into 56a703a42
Pull Request #1477: Webaccess cuelist side fader

8 of 39 new or added lines in 1 file covered. (20.51%)

279 existing lines in 9 files now uncovered.

15420 of 54903 relevant lines covered (28.09%)

20284.04 hits per line

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

85.15
/engine/src/function.cpp
1
/*
2
  Q Light Controller Plus
3
  function.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 <QElapsedTimer>
24
#include <QString>
25
#include <QDebug>
26
#include <math.h>
27

28
#include "qlcmacros.h"
29

30
#include "scriptwrapper.h"
31
#include "mastertimer.h"
32
#include "collection.h"
33
#include "rgbmatrix.h"
34
#include "function.h"
35
#include "sequence.h"
36
#include "chaser.h"
37
#include "audio.h"
38
#include "video.h"
39
#include "scene.h"
40
#include "show.h"
41
#include "efx.h"
42
#include "doc.h"
43

44
#define OVERRIDE_ATTRIBUTE_START_ID     128
45

46
const QString KSceneString      (      "Scene" );
47
const QString KChaserString     (     "Chaser" );
48
const QString KEFXString        (        "EFX" );
49
const QString KCollectionString ( "Collection" );
50
const QString KScriptString     (     "Script" );
51
const QString KRGBMatrixString  (  "RGBMatrix" );
52
const QString KShowString       (       "Show" );
53
const QString KSequenceString   (   "Sequence" );
54
const QString KAudioString      (      "Audio" );
55
const QString KVideoString      (      "Video" );
56
const QString KUndefinedString  (  "Undefined" );
57

58
const QString KLoopString       (       "Loop" );
59
const QString KPingPongString   (   "PingPong" );
60
const QString KSingleShotString ( "SingleShot" );
61
const QString KRandomString     (     "Random" );
62

63
const QString KBackwardString   (   "Backward" );
64
const QString KForwardString    (    "Forward" );
65

66
const QString KTimeTypeString   (       "Time" );
67
const QString KBeatsTypeString  (      "Beats" );
68

69
/*****************************************************************************
70
 * Initialization
71
 *****************************************************************************/
72

73
Function::Function(QObject *parent)
×
74
    : QObject(parent)
75
    , m_id(Function::invalidId())
×
76
    , m_type(Undefined)
77
    , m_path(QString())
78
    , m_visible(true)
79
    , m_runOrder(Loop)
80
    , m_direction(Forward)
81
    , m_tempoType(Time)
82
    , m_overrideTempoType(Original)
83
    , m_beatResyncNeeded(false)
84
    , m_fadeInSpeed(0)
85
    , m_fadeOutSpeed(0)
86
    , m_duration(0)
87
    , m_overrideFadeInSpeed(defaultSpeed())
×
88
    , m_overrideFadeOutSpeed(defaultSpeed())
×
89
    , m_overrideDuration(defaultSpeed())
×
90
    , m_flashing(false)
91
    , m_elapsed(0)
92
    , m_elapsedBeats(0)
93
    , m_stop(true)
94
    , m_running(false)
95
    , m_paused(false)
96
    , m_lastOverrideAttributeId(OVERRIDE_ATTRIBUTE_START_ID)
97
    , m_preserveAttributes(false)
98
    , m_blendMode(Universe::NormalBlend)
×
99
{
100

101
}
×
102

103
Function::Function(Doc* doc, Type t)
426✔
104
    : QObject(doc)
105
    , m_id(Function::invalidId())
852✔
106
    , m_type(t)
107
    , m_path(QString())
108
    , m_visible(true)
109
    , m_runOrder(Loop)
110
    , m_direction(Forward)
111
    , m_tempoType(Time)
112
    , m_overrideTempoType(Original)
113
    , m_beatResyncNeeded(false)
114
    , m_fadeInSpeed(0)
115
    , m_fadeOutSpeed(0)
116
    , m_duration(0)
117
    , m_overrideFadeInSpeed(defaultSpeed())
852✔
118
    , m_overrideFadeOutSpeed(defaultSpeed())
852✔
119
    , m_overrideDuration(defaultSpeed())
852✔
120
    , m_flashing(false)
121
    , m_elapsed(0)
122
    , m_elapsedBeats(0)
123
    , m_stop(true)
124
    , m_running(false)
125
    , m_paused(false)
126
    , m_lastOverrideAttributeId(OVERRIDE_ATTRIBUTE_START_ID)
127
    , m_preserveAttributes(false)
128
    , m_blendMode(Universe::NormalBlend)
426✔
129
{
130
    Q_ASSERT(doc != NULL);
426✔
131
    registerAttribute(tr("Intensity"), Multiply | Single);
426✔
132
}
426✔
133

134
Function::~Function()
426✔
135
{
136
}
426✔
137

138
Doc* Function::doc() const
2,210✔
139
{
140
    return qobject_cast<Doc*>(parent());
2,210✔
141
}
142

143
/*****************************************************************************
144
 * Copying
145
 *****************************************************************************/
146
Function *Function::createCopy(Doc *doc, bool addToDoc)
×
147
{
148
    Q_ASSERT(doc != NULL);
×
149

150
    Function* copy = new Function(doc, type());
×
151
    if (copy->copyFrom(this) == false)
×
152
    {
153
        delete copy;
×
154
        copy = NULL;
×
155
    }
156
    if (addToDoc == true && doc->addFunction(copy) == false)
×
157
    {
158
        delete copy;
×
159
        copy = NULL;
×
160
    }
161

162
    return copy;
×
163
}
164

165
bool Function::copyFrom(const Function* function)
16✔
166
{
167
    if (function == NULL)
16✔
168
        return false;
1✔
169

170
    m_name = function->name();
15✔
171
    m_runOrder = function->runOrder();
15✔
172
    m_direction = function->direction();
15✔
173
    m_tempoType = function->tempoType();
15✔
174
    m_fadeInSpeed = function->fadeInSpeed();
15✔
175
    m_fadeOutSpeed = function->fadeOutSpeed();
15✔
176
    m_duration = function->duration();
15✔
177
    m_path = function->path(true);
15✔
178
    m_visible = function->isVisible();
15✔
179
    m_blendMode = function->blendMode();
15✔
180
    m_uiState = function->uiStateMap();
15✔
181

182
    emit changed(m_id);
15✔
183

184
    return true;
15✔
185
}
186

187
/*****************************************************************************
188
 * ID
189
 *****************************************************************************/
190

191
void Function::setID(quint32 id)
252✔
192
{
193
    /* Don't set doc modified status or emit changed signal, because this
194
       function is called only once during function creation. */
195
    m_id = id;
252✔
196
}
252✔
197

198
quint32 Function::id() const
3,027✔
199
{
200
    return m_id;
3,027✔
201
}
202

203
quint32 Function::invalidId()
2,552✔
204
{
205
    return UINT_MAX;
2,552✔
206
}
207

208
/*****************************************************************************
209
 * Name
210
 *****************************************************************************/
211

212
void Function::setName(const QString& name)
581✔
213
{
214
    if (m_name == name)
581✔
215
        return;
×
216

217
    m_name = QString(name);
581✔
218

219
    emit nameChanged(m_id);
581✔
220
}
221

222
QString Function::name() const
1,005✔
223
{
224
    return m_name;
1,005✔
225
}
226

227
/*****************************************************************************
228
 * Type
229
 *****************************************************************************/
230

231
Function::Type Function::type() const
843✔
232
{
233
    return m_type;
843✔
234
}
235

236
QString Function::typeString() const
3✔
237
{
238
    return Function::typeToString(type());
3✔
239
}
240

241
QString Function::typeToString(Type type)
228✔
242
{
243
    switch (type)
228✔
244
    {
245
        case SceneType:      return KSceneString;
134✔
246
        case ChaserType:     return KChaserString;
21✔
247
        case EFXType:        return KEFXString;
13✔
248
        case CollectionType: return KCollectionString;
19✔
249
        case ScriptType:     return KScriptString;
2✔
250
        case RGBMatrixType:  return KRGBMatrixString;
4✔
251
        case ShowType:       return KShowString;
1✔
252
        case SequenceType:   return KSequenceString;
5✔
253
        case AudioType:      return KAudioString;
1✔
254
        case VideoType:      return KVideoString;
1✔
255
        case Undefined:
27✔
256
        default:
257
            return KUndefinedString;
27✔
258
    }
259
}
260

261
Function::Type Function::stringToType(const QString& string)
23✔
262
{
263
    if (string == KSceneString)
23✔
264
        return SceneType;
2✔
265
    else if (string == KChaserString)
21✔
266
        return ChaserType;
2✔
267
    else if (string == KEFXString)
19✔
268
        return EFXType;
2✔
269
    else if (string == KCollectionString)
17✔
270
        return CollectionType;
6✔
271
    else if (string == KScriptString)
11✔
272
        return ScriptType;
1✔
273
    else if (string == KRGBMatrixString)
10✔
274
        return RGBMatrixType;
1✔
275
    else if (string == KShowString)
9✔
276
        return ShowType;
1✔
277
    else if (string == KSequenceString)
8✔
278
        return SequenceType;
1✔
279
    else if (string == KAudioString)
7✔
280
        return AudioType;
1✔
281
    else if (string == KVideoString)
6✔
282
        return VideoType;
1✔
283
    else
284
        return Undefined;
5✔
285
}
286

287
QIcon Function::getIcon() const
×
288
{
289
    return QIcon(":/function.png");
×
290
}
291

292
/*********************************************************************
293
 * Path
294
 *********************************************************************/
295
void Function::setPath(QString path)
8✔
296
{
297
    if (path.contains(typeToString(type())))
8✔
298
        path.remove(typeToString(type()) + "/");
×
299
    //qDebug() << "Function " << name() << "path set to:" << path;
300
    m_path = path;
8✔
301
}
8✔
302

303
QString Function::path(bool simplified) const
25✔
304
{
305
    if (simplified == true)
25✔
306
        return m_path;
25✔
307
    else
308
        return QString("%1/%2").arg(typeToString(type())).arg(m_path);
×
309
}
310

311
/*********************************************************************
312
 * Visibility
313
 *********************************************************************/
314

315
void Function::setVisible(bool visible)
9✔
316
{
317
    m_visible = visible;
9✔
318
}
9✔
319

320
bool Function::isVisible() const
31✔
321
{
322
    return m_visible;
31✔
323
}
324

325
/*********************************************************************
326
 * Common
327
 *********************************************************************/
328

329
bool Function::saveXMLCommon(QXmlStreamWriter *doc) const
10✔
330
{
331
    Q_ASSERT(doc != NULL);
10✔
332

333
    doc->writeAttribute(KXMLQLCFunctionID, QString::number(id()));
10✔
334
    doc->writeAttribute(KXMLQLCFunctionType, Function::typeToString(type()));
10✔
335
    doc->writeAttribute(KXMLQLCFunctionName, name());
10✔
336
    if (isVisible() == false)
10✔
337
        doc->writeAttribute(KXMLQLCFunctionHidden, "True");
×
338
    if (path(true).isEmpty() == false)
10✔
339
        doc->writeAttribute(KXMLQLCFunctionPath, path(true));
×
340
    if (blendMode() != Universe::NormalBlend)
10✔
341
        doc->writeAttribute(KXMLQLCFunctionBlendMode, Universe::blendModeToString(blendMode()));
×
342

343
    return true;
10✔
344
}
345

346
/*****************************************************************************
347
 * Running order
348
 *****************************************************************************/
349

350
void Function::setRunOrder(const Function::RunOrder& order)
56✔
351
{
352
    if (order == Loop || order == SingleShot || order == PingPong || order == Random)
56✔
353
        m_runOrder = order;
55✔
354
    else
355
        m_runOrder = Loop;
1✔
356
    emit runOrderChanged();
56✔
357
    emit changed(m_id);
56✔
358
}
56✔
359

360
Function::RunOrder Function::runOrder() const
473✔
361
{
362
    return m_runOrder;
473✔
363
}
364

365
QString Function::runOrderToString(const RunOrder& order)
15✔
366
{
367
    switch (order)
15✔
368
    {
369
    default:
6✔
370
    case Loop:
371
        return KLoopString;
6✔
372
    case PingPong:
3✔
373
        return KPingPongString;
3✔
374
    case SingleShot:
5✔
375
        return KSingleShotString;
5✔
376
    case Random:
1✔
377
        return KRandomString;
1✔
378
    }
379
}
380

381
Function::RunOrder Function::stringToRunOrder(const QString& str)
19✔
382
{
383
    if (str == KPingPongString)
19✔
384
        return PingPong;
3✔
385
    else if (str == KSingleShotString)
16✔
386
        return SingleShot;
8✔
387
    else if (str == KRandomString)
8✔
388
        return Random;
1✔
389
    else
390
        return Loop;
7✔
391
}
392

393
bool Function::saveXMLRunOrder(QXmlStreamWriter *doc) const
9✔
394
{
395
    Q_ASSERT(doc != NULL);
9✔
396

397
    doc->writeTextElement(KXMLQLCFunctionRunOrder, runOrderToString(runOrder()));
9✔
398

399
    return true;
9✔
400
}
401

402
bool Function::loadXMLRunOrder(QXmlStreamReader &root)
13✔
403
{
404
    if (root.name() != KXMLQLCFunctionRunOrder)
13✔
405
    {
406
        qWarning() << Q_FUNC_INFO << "RunOrder node not found";
×
407
        return false;
×
408
    }
409
    QString str = root.readElementText();
26✔
410
    if (str.isEmpty())
13✔
411
        return false;
×
412

413
    setRunOrder(stringToRunOrder(str));
13✔
414

415
    return true;
13✔
416
}
417

418
/*****************************************************************************
419
 * Direction
420
 *****************************************************************************/
421

422
void Function::setDirection(const Function::Direction& dir)
60✔
423
{
424
    if (dir == Forward || dir == Backward)
60✔
425
        m_direction = dir;
59✔
426
    else
427
        m_direction = Forward;
1✔
428
    emit changed(m_id);
60✔
429
}
60✔
430

431
Function::Direction Function::direction() const
4,604✔
432
{
433
    return m_direction;
4,604✔
434
}
435

436
QString Function::directionToString(const Direction& dir)
16✔
437
{
438
    switch (dir)
16✔
439
    {
440
        default:
8✔
441
        case Forward:
442
            return KForwardString;
8✔
443
        case Backward:
8✔
444
            return KBackwardString;
8✔
445
    }
446
}
447

448
Function::Direction Function::stringToDirection(const QString& str)
28✔
449
{
450
    if (str == KBackwardString)
28✔
451
        return Backward;
16✔
452
    else
453
        return Forward;
12✔
454
}
455

456
bool Function::saveXMLDirection(QXmlStreamWriter *doc) const
8✔
457
{
458
    Q_ASSERT(doc != NULL);
8✔
459

460
    doc->writeTextElement(KXMLQLCFunctionDirection, directionToString(direction()));
8✔
461

462
    return true;
8✔
463
}
464

465
bool Function::loadXMLDirection(QXmlStreamReader &root)
12✔
466
{
467
    if (root.name() != KXMLQLCFunctionDirection)
12✔
468
    {
469
        qWarning() << Q_FUNC_INFO << "Direction node not found";
×
470
        return false;
×
471
    }
472

473
    QString str = root.readElementText();
24✔
474
    if (str.isEmpty())
12✔
475
        return false;
×
476

477
    setDirection(stringToDirection(str));
12✔
478

479
    return true;
12✔
480
}
481

482
/*********************************************************************
483
 * Speed type
484
 *********************************************************************/
485

486
void Function::setTempoType(const Function::TempoType &type)
3✔
487
{
488
    if (type == m_tempoType)
3✔
489
        return;
1✔
490

491
    m_tempoType = type;
2✔
492

493
    /* Retrieve the current BPM value known by the Master Timer */
494
    float bpmNum = doc()->masterTimer()->bpmNumber();
2✔
495
    /* Calculate the duration in ms of a single beat */
496
    float beatTime = 60000.0 / bpmNum;
2✔
497

498
    switch (type)
2✔
499
    {
500
        /* Beats -> Time */
501
        case Time:
1✔
502
            setFadeInSpeed(beatsToTime(fadeInSpeed(), beatTime));
1✔
503
            setDuration(beatsToTime(duration(), beatTime));
1✔
504
            setFadeOutSpeed(beatsToTime(fadeOutSpeed(), beatTime));
1✔
505
            disconnect(doc()->masterTimer(), SIGNAL(bpmNumberChanged(int)),
1✔
506
                       this, SLOT(slotBPMChanged(int)));
507
        break;
1✔
508

509
        /* Time -> Beats */
510
        case Beats:
1✔
511
            setFadeInSpeed(timeToBeats(fadeInSpeed(), beatTime));
1✔
512
            setDuration(timeToBeats(duration(), beatTime));
1✔
513
            setFadeOutSpeed(timeToBeats(fadeOutSpeed(), beatTime));
1✔
514
            connect(doc()->masterTimer(), SIGNAL(bpmNumberChanged(int)),
1✔
515
                    this, SLOT(slotBPMChanged(int)));
516
        break;
1✔
517
        default:
×
518
            qDebug() << "Error. Unhandled tempo type" << type;
×
519
        break;
×
520
    }
521

522
    emit changed(m_id);
2✔
523
}
524

525
Function::TempoType Function::tempoType() const
1,369✔
526
{
527
    return m_tempoType;
1,369✔
528
}
529

530
QString Function::tempoTypeToString(const Function::TempoType &type)
2✔
531
{
532
    switch (type)
2✔
533
    {
534
        default:
1✔
535
        case Time:
536
            return KTimeTypeString;
1✔
537
        case Beats:
1✔
538
            return KBeatsTypeString;
1✔
539
    }
540
}
541

542
Function::TempoType Function::stringToTempoType(const QString &str)
2✔
543
{
544
    if (str == KTimeTypeString)
2✔
545
        return Time;
1✔
546
    else
547
        return Beats;
1✔
548
}
549

550
uint Function::timeToBeats(uint time, int beatDuration)
5✔
551
{
552
    if (time == 0 || time == infiniteSpeed())
5✔
553
        return time;
1✔
554

555
    uint value = 0;
4✔
556

557
    float beats = (float)time / (float)beatDuration;
4✔
558
    value = floor(beats) * 1000;
4✔
559

560
    beats -= floor(beats);
4✔
561
    beats = floor((beats * 1000) / 125) * 125;
4✔
562

563
    return value + beats;
4✔
564
}
565

566
uint Function::beatsToTime(uint beats, int beatDuration)
5✔
567
{
568
    if (beats == 0 || beats == infiniteSpeed())
5✔
569
        return beats;
1✔
570

571
    return ((float)beats / 1000.0) * beatDuration;
4✔
572
}
573

574
Function::TempoType Function::overrideTempoType() const
2✔
575
{
576
    return m_overrideTempoType;
2✔
577
}
578

579
void Function::setOverrideTempoType(Function::TempoType type)
1✔
580
{
581
    m_overrideTempoType = type;
1✔
582
}
1✔
583

584
void Function::slotBPMChanged(int bpmNumber)
×
585
{
586
    Q_UNUSED(bpmNumber)
587
    m_beatResyncNeeded = true;
×
588
}
×
589

590
/****************************************************************************
591
 * Speed
592
 ****************************************************************************/
593

594
void Function::setFadeInSpeed(uint ms)
33✔
595
{
596
    m_fadeInSpeed = ms;
33✔
597
    emit changed(m_id);
33✔
598
}
33✔
599

600
uint Function::fadeInSpeed() const
981✔
601
{
602
    return m_fadeInSpeed;
981✔
603
}
604

605
void Function::setFadeOutSpeed(uint ms)
33✔
606
{
607
    m_fadeOutSpeed = ms;
33✔
608
    emit changed(m_id);
33✔
609
}
33✔
610

611
uint Function::fadeOutSpeed() const
161✔
612
{
613
    return m_fadeOutSpeed;
161✔
614
}
615

616
void Function::setDuration(uint ms)
129✔
617
{
618
    m_duration = ms;
129✔
619
    emit changed(m_id);
129✔
620
}
129✔
621

622
uint Function::duration() const
913✔
623
{
624
    return m_duration;
913✔
625
}
626

627
quint32 Function::totalDuration()
1✔
628
{
629
    // fall back to duration in case a
630
    // subclass doesn't provide this method
631
    return duration();
1✔
632
}
633

634
void Function::setTotalDuration(quint32 msec)
×
635
{
636
    Q_UNUSED(msec)
637
}
×
638

639
void Function::setOverrideFadeInSpeed(uint ms)
1✔
640
{
641
    m_overrideFadeInSpeed = ms;
1✔
642
}
1✔
643

644
uint Function::overrideFadeInSpeed() const
1,085✔
645
{
646
    return m_overrideFadeInSpeed;
1,085✔
647
}
648

649
void Function::setOverrideFadeOutSpeed(uint ms)
34✔
650
{
651
    m_overrideFadeOutSpeed = ms;
34✔
652
}
34✔
653

654
uint Function::overrideFadeOutSpeed() const
286✔
655
{
656
    return m_overrideFadeOutSpeed;
286✔
657
}
658

659
void Function::setOverrideDuration(uint ms)
1✔
660
{
661
    m_overrideDuration = ms;
1✔
662
}
1✔
663

664
uint Function::overrideDuration() const
152✔
665
{
666
    return m_overrideDuration;
152✔
667
}
668

669
QString Function::speedToString(uint ms)
60✔
670
{
671
    QString str;
60✔
672
    if (ms == infiniteSpeed())
60✔
673
    {
674
        str = QChar(0x221E); // Infinity symbol
17✔
675
    }
676
    else
677
    {
678
        uint h, m, s;
679

680
        h = ms / MS_PER_HOUR;
43✔
681
        ms -= (h * MS_PER_HOUR);
43✔
682

683
        m = ms / MS_PER_MINUTE;
43✔
684
        ms -= (m * MS_PER_MINUTE);
43✔
685

686
        s = ms / MS_PER_SECOND;
43✔
687
        ms -= (s * MS_PER_SECOND);
43✔
688

689
        if (h != 0)
43✔
690
            str += QString("%1h").arg(h, 1, 10, QChar('0'));
4✔
691
        if (m != 0)
43✔
692
            str += QString("%1m").arg(m, str.size() ? 2 : 1, 10, QChar('0'));
5✔
693
        if (s != 0)
43✔
694
            str += QString("%1s").arg(s, str.size() ? 2 : 1, 10, QChar('0'));
6✔
695
        if (ms != 0 || str.size() == 0)
43✔
696
            str += QString("%1ms").arg(ms, str.size() ? 3 : 1, 10, QChar('0'));
40✔
697
    }
698

699
    return str;
60✔
700
}
701

702
static uint speedSplit(QString& speedString, QString splitNeedle)
78✔
703
{
704
    QStringList splitResult;
156✔
705
    // Filter out "ms" because "m" and "s" may wrongly use it
706
    splitResult = speedString.split("ms");
78✔
707
    if (splitResult.count() > 1)
78✔
708
        splitResult = splitResult.at(0).split(splitNeedle);
12✔
709
    else
710
        splitResult = speedString.split(splitNeedle);
66✔
711

712
    if (splitResult.count() > 1)
78✔
713
    {
714
        speedString.remove(0, speedString.indexOf(splitNeedle) + 1);
13✔
715
        return splitResult.at(0).toUInt();
13✔
716
    }
717
    return 0;
65✔
718
}
719

720
uint Function::stringToSpeed(QString speed)
27✔
721
{
722
    uint value = 0;
27✔
723

724
    if (speed == QChar(0x221E)) // Infinity symbol
27✔
725
        return infiniteSpeed();
1✔
726

727
    value += speedSplit(speed, "h") * 1000 * 60 * 60;
26✔
728
    value += speedSplit(speed, "m") * 1000 * 60;
26✔
729
    value += speedSplit(speed, "s") * 1000;
26✔
730

731
    if (speed.contains("."))
26✔
732
    {
733
        // lround avoids toDouble precison issues (.03 transforms to .029)
734
        value += lround(speed.toDouble() * 1000.0);
20✔
735
    }
736
    else
737
    {
738
        if (speed.contains("ms"))
6✔
739
            speed = speed.split("ms").at(0);
3✔
740
        value += speed.toUInt();
6✔
741
    }
742

743
    return speedNormalize(value);
26✔
744
}
745

746
uint Function::speedNormalize(uint speed)
81✔
747
{
748
    if ((int)speed < 0)
81✔
749
        return infiniteSpeed();
6✔
750
    return speed;
75✔
751
}
752

753
uint Function::speedAdd(uint left, uint right)
14✔
754
{
755
    if (speedNormalize(left) == infiniteSpeed()
14✔
756
        || speedNormalize(right) == infiniteSpeed())
14✔
757
        return infiniteSpeed();
3✔
758

759
    return speedNormalize(left + right);
11✔
760
}
761

762
uint Function::speedSubtract(uint left, uint right)
7✔
763
{
764
    if (right >= left)
7✔
765
        return 0;
4✔
766
    if (speedNormalize(right) == infiniteSpeed())
3✔
767
        return 0;
×
768
    if (speedNormalize(left) == infiniteSpeed())
3✔
769
        return infiniteSpeed();
1✔
770

771
    return speedNormalize(left - right);
2✔
772
}
773

774
void Function::tap()
×
775
{
776
}
×
777

778
bool Function::loadXMLSpeed(QXmlStreamReader &speedRoot)
7✔
779
{
780
    if (speedRoot.name() != KXMLQLCFunctionSpeed)
7✔
781
        return false;
×
782

783
    QXmlStreamAttributes attrs = speedRoot.attributes();
7✔
784

785
    m_fadeInSpeed = attrs.value(KXMLQLCFunctionSpeedFadeIn).toString().toUInt();
7✔
786
    m_fadeOutSpeed = attrs.value(KXMLQLCFunctionSpeedFadeOut).toString().toUInt();
7✔
787
    m_duration = attrs.value(KXMLQLCFunctionSpeedDuration).toString().toUInt();
7✔
788

789
    speedRoot.skipCurrentElement();
7✔
790

791
    return true;
7✔
792
}
793

794
bool Function::saveXMLSpeed(QXmlStreamWriter *doc) const
9✔
795
{
796
    doc->writeStartElement(KXMLQLCFunctionSpeed);
9✔
797
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeIn, QString::number(fadeInSpeed()));
9✔
798
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeOut, QString::number(fadeOutSpeed()));
9✔
799
    doc->writeAttribute(KXMLQLCFunctionSpeedDuration, QString::number(duration()));
9✔
800
    doc->writeEndElement();
9✔
801

802
    return true;
9✔
803
}
804

805
uint Function::infiniteSpeed()
451✔
806
{
807
    return (uint) -2;
451✔
808
}
809

810
uint Function::defaultSpeed()
3,234✔
811
{
812
    return (uint) -1;
3,234✔
813
}
814

815
/*****************************************************************************
816
 * UI State
817
 *****************************************************************************/
818

819
QVariant Function::uiStateValue(QString property)
2✔
820
{
821
    return m_uiState.value(property, QVariant());
4✔
822
}
823

824
void Function::setUiStateValue(QString property, QVariant value)
1✔
825
{
826
    m_uiState[property] = value;
1✔
827
}
1✔
828

829
QMap<QString, QVariant> Function::uiStateMap() const
17✔
830
{
831
    return m_uiState;
17✔
832
}
833

834
/*****************************************************************************
835
 * Fixtures
836
 *****************************************************************************/
837

838
void Function::slotFixtureRemoved(quint32 fid)
1✔
839
{
840
    Q_UNUSED(fid);
841
}
1✔
842

843
/*****************************************************************************
844
 * Load & Save
845
 *****************************************************************************/
846
bool Function::saveXML(QXmlStreamWriter *doc)
×
847
{
848
    Q_UNUSED(doc)
849
    return false;
×
850
}
851

852
bool Function::loadXML(QXmlStreamReader &root)
×
853
{
854
    Q_UNUSED(root)
855
    return false;
×
856
}
857

858
bool Function::loader(QXmlStreamReader &root, Doc* doc)
12✔
859
{
860
    if (root.name() != KXMLQLCFunction)
12✔
861
    {
862
        qWarning("Function node not found!");
2✔
863
        return false;
2✔
864
    }
865

866
    QXmlStreamAttributes attrs = root.attributes();
20✔
867

868
    /* Get common information from the tag's attributes */
869
    quint32 id = attrs.value(KXMLQLCFunctionID).toString().toUInt();
10✔
870
    QString name = attrs.value(KXMLQLCFunctionName).toString();
30✔
871
    Type type = Function::stringToType(attrs.value(KXMLQLCFunctionType).toString());
10✔
872
    QString path;
20✔
873
    bool visible = true;
10✔
874
    Universe::BlendMode blendMode = Universe::NormalBlend;
10✔
875

876
    if (attrs.hasAttribute(KXMLQLCFunctionPath))
10✔
877
        path = attrs.value(KXMLQLCFunctionPath).toString();
×
878
    if (attrs.hasAttribute(KXMLQLCFunctionHidden))
10✔
879
        visible = false;
×
880
    if (attrs.hasAttribute(KXMLQLCFunctionBlendMode))
10✔
881
        blendMode = Universe::stringToBlendMode(attrs.value(KXMLQLCFunctionBlendMode).toString());
×
882

883
    /* Check for ID validity before creating the function */
884
    if (id == Function::invalidId())
10✔
885
    {
886
        qWarning() << Q_FUNC_INFO << "Function ID" << id << "is not allowed.";
×
887
        return false;
×
888
    }
889

890
    /* Create a new function according to the type */
891
    Function* function = NULL;
10✔
892
    if (type == Function::SceneType)
10✔
893
        function = new class Scene(doc);
1✔
894
    else if (type == Function::ChaserType)
9✔
895
        function = new class Chaser(doc);
1✔
896
    else if (type == Function::CollectionType)
8✔
897
        function = new class Collection(doc);
5✔
898
    else if (type == Function::EFXType)
3✔
899
        function = new class EFX(doc);
1✔
900
    else if (type == Function::ScriptType)
2✔
901
        function = new class Script(doc);
×
902
    else if (type == Function::RGBMatrixType)
2✔
903
        function = new class RGBMatrix(doc);
×
904
    else if (type == Function::ShowType)
2✔
905
        function = new class Show(doc);
×
906
    else if (type == Function::SequenceType)
2✔
907
        function = new class Sequence(doc);
×
908
    else if (type == Function::AudioType)
2✔
909
        function = new class Audio(doc);
×
910
    else if (type == Function::VideoType)
2✔
911
        function = new class Video(doc);
×
912
    else
913
        return false;
2✔
914

915
    function->setName(name);
8✔
916
    function->setPath(path);
8✔
917
    function->setVisible(visible);
8✔
918
    function->setBlendMode(blendMode);
8✔
919
    if (function->loadXML(root) == true)
8✔
920
    {
921
        if (doc->addFunction(function, id) == true)
8✔
922
        {
923
            /* Success */
924
            return true;
8✔
925
        }
926
        else
927
        {
928
            qWarning() << "Function" << name << "cannot be created.";
×
929
            delete function;
×
930
            return false;
×
931
        }
932
    }
933
    else
934
    {
935
        qWarning() << "Function" << name << "cannot be loaded.";
×
936
        delete function;
×
937
        return false;
×
938
    }
939
}
940

941
void Function::postLoad()
×
942
{
943
    /* NOP */
944
}
×
945

946
bool Function::contains(quint32 functionId)
12✔
947
{
948
    Q_UNUSED(functionId);
949
    return false;
12✔
950
}
951

952
QList<quint32> Function::components()
×
953
{
954
    return QList<quint32>();
×
955
}
956

957
/*****************************************************************************
958
 * Flash
959
 *****************************************************************************/
960

961
void Function::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP)
7✔
962
{
963
    Q_UNUSED(timer);
964
    Q_UNUSED(shouldOverride);
965
    Q_UNUSED(forceLTP);
966

967
    if (m_flashing == false)
7✔
968
    {
969
        emit flashing(m_id, true);
6✔
970
    }
971

972
    m_flashing = true;
7✔
973
}
7✔
974

975
void Function::unFlash(MasterTimer *timer)
6✔
976
{
977
    Q_UNUSED(timer);
978

979
    if (m_flashing == true)
6✔
980
        emit flashing(m_id, false);
6✔
981

982
    m_flashing = false;
6✔
983
}
6✔
984

985
bool Function::flashing() const
23✔
986
{
987
    return m_flashing;
23✔
988
}
989

990
/*****************************************************************************
991
 * Running
992
 *****************************************************************************/
993

994
void Function::preRun(MasterTimer *timer)
165✔
995
{
996
    Q_UNUSED(timer);
997

998
    qDebug() << "Function preRun. Name:" << m_name << "ID:" << m_id << "type:" << typeToString(type());
165✔
999
    m_running = true;
165✔
1000

1001
    emit running(m_id);
165✔
1002
}
165✔
1003

1004
void Function::write(MasterTimer *timer, QList<Universe *> universes)
×
1005
{
1006
    Q_UNUSED(timer);
1007
    Q_UNUSED(universes);
UNCOV
1008
}
×
1009

1010
void Function::postRun(MasterTimer *timer, QList<Universe *> universes)
137✔
1011
{
1012
    Q_UNUSED(timer);
1013
    Q_UNUSED(universes);
1014

1015
    qDebug() << "Function postRun. Name:" << m_name << "ID: " << m_id;
137✔
1016

1017
    {
1018
        QMutexLocker locker(&m_stopMutex);
274✔
1019

1020
        resetElapsed();
137✔
1021
        if (m_preserveAttributes == false)
137✔
1022
            resetAttributes();
137✔
1023

1024
        m_functionStopped.wakeAll();
137✔
1025
    }
1026

1027
    m_paused = false;
137✔
1028
    m_running = false;
137✔
1029
    emit stopped(m_id);
137✔
1030
}
137✔
1031

1032
void Function::dismissAllFaders()
110✔
1033
{
1034
    QMapIterator <quint32, QSharedPointer<GenericFader> > it(m_fadersMap);
220✔
1035
    while (it.hasNext() == true)
209✔
1036
    {
1037
        it.next();
99✔
1038
        QSharedPointer<GenericFader> fader = it.value();
198✔
1039
        if (!fader.isNull())
99✔
1040
            fader->requestDelete();
99✔
1041
    }
1042

1043
    m_fadersMap.clear();
110✔
1044
}
110✔
1045

1046
bool Function::isRunning() const
4,917✔
1047
{
1048
    return m_running;
4,917✔
1049
}
1050

1051
bool Function::isPaused() const
486✔
1052
{
1053
    return m_paused;
486✔
1054
}
1055

1056
/*****************************************************************************
1057
 * Elapsed ticks while running
1058
 *****************************************************************************/
1059

1060
quint32 Function::elapsed() const
19✔
1061
{
1062
    return m_elapsed;
19✔
1063
}
1064

1065
quint32 Function::elapsedBeats() const
1✔
1066
{
1067
    return m_elapsedBeats;
1✔
1068
}
1069

1070
void Function::resetElapsed()
138✔
1071
{
1072
    qDebug() << Q_FUNC_INFO;
138✔
1073
    m_elapsed = 0;
138✔
1074
    m_elapsedBeats = 0;
138✔
1075
}
138✔
1076

1077
void Function::incrementElapsed()
584✔
1078
{
1079
    // Don't wrap around. UINT_MAX is the maximum fade/hold time.
1080
    if (m_elapsed < UINT_MAX - MasterTimer::tick())
584✔
1081
        m_elapsed += MasterTimer::tick();
582✔
1082
    else
1083
        m_elapsed = UINT_MAX;
2✔
1084
}
584✔
1085

UNCOV
1086
void Function::incrementElapsedBeats()
×
1087
{
UNCOV
1088
    m_elapsedBeats += 1000;
×
1089
}
×
1090

UNCOV
1091
void Function::roundElapsed(quint32 roundTime)
×
1092
{
1093
    if (roundTime == 0)
×
UNCOV
1094
        m_elapsed = 0;
×
1095
    else
UNCOV
1096
        m_elapsed %= roundTime;
×
UNCOV
1097
}
×
1098

1099
/*****************************************************************************
1100
 * Start & Stop
1101
 *****************************************************************************/
1102

1103
void Function::start(MasterTimer* timer, FunctionParent source, quint32 startTime,
150✔
1104
                     uint overrideFadeIn, uint overrideFadeOut, uint overrideDuration, TempoType overrideTempoType)
1105
{
1106
    qDebug() << "Function start(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id() << ", startTime:" << startTime;
150✔
1107

1108
    Q_ASSERT(timer != NULL);
150✔
1109

1110
    {
1111
        QMutexLocker sourcesLocker(&m_sourcesMutex);
150✔
1112
        if (m_sources.contains(source))
150✔
UNCOV
1113
            return;
×
1114
        m_sources.append(source);
150✔
1115
        if (m_sources.size() > 1)
150✔
UNCOV
1116
            return;
×
1117
    }
1118

1119
    /** If we're in a paused state, then just return to the running state
1120
     *  to let subclasses resuming what they were doing. */
1121
    if (m_paused == true)
150✔
1122
    {
UNCOV
1123
        m_paused = false;
×
UNCOV
1124
        return;
×
1125
    }
1126

1127
    m_elapsed = startTime;
150✔
1128
    m_elapsedBeats = 0;
150✔
1129
    m_overrideFadeInSpeed = overrideFadeIn;
150✔
1130
    m_overrideFadeOutSpeed = overrideFadeOut;
150✔
1131
    m_overrideDuration = overrideDuration;
150✔
1132
    m_overrideTempoType = overrideTempoType == Original ? tempoType() : overrideTempoType;
150✔
1133

1134
    m_stop = false;
150✔
1135
    timer->startFunction(this);
150✔
1136
}
1137

1138
void Function::setPause(bool enable)
10✔
1139
{
1140
    if (enable && isRunning() == false)
10✔
UNCOV
1141
        return;
×
1142

1143
    m_paused = enable;
10✔
1144
}
1145

1146
void Function::stop(FunctionParent source, bool preserveAttributes)
148✔
1147
{
1148
    qDebug() << "Function stop(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id();
148✔
1149

1150
    QMutexLocker sourcesLocker(&m_sourcesMutex);
296✔
1151

1152
    if ((source.id() == id() && source.type() == FunctionParent::Function) ||
305✔
1153
        (source.type() == FunctionParent::Master) ||
305✔
1154
        (source.type() == FunctionParent::ManualVCWidget))
116✔
1155
    {
1156
        m_sources.clear();
35✔
1157
    }
1158
    else
1159
    {
1160
        m_sources.removeAll(source);
113✔
1161
    }
1162

1163
    if (m_sources.size() == 0)
148✔
1164
    {
1165
        m_stop = true;
148✔
1166
        m_preserveAttributes = preserveAttributes;
148✔
1167
    }
1168
}
148✔
1169

1170
bool Function::stopped() const
680✔
1171
{
1172
    return m_stop;
680✔
1173
}
1174

UNCOV
1175
bool Function::startedAsChild() const
×
1176
{
1177
    QMutexLocker sourcesLocker(const_cast<QMutex*>(&m_sourcesMutex));
×
UNCOV
1178
    foreach (FunctionParent source, m_sources)
×
1179
    {
UNCOV
1180
        if (source.type() == FunctionParent::Function && source.id() != id())
×
UNCOV
1181
            return true;
×
1182
    }
UNCOV
1183
    return false;
×
1184
}
1185

1186
int Function::invalidAttributeId()
718✔
1187
{
1188
    return -1;
718✔
1189
}
1190

1191
bool Function::stopAndWait()
1✔
1192
{
1193
    QMutexLocker locker(&m_stopMutex);
2✔
1194

1195
    stop(FunctionParent::master());
1✔
1196

1197
    QElapsedTimer watchdog;
1✔
1198
    watchdog.restart();
1✔
1199

1200
    // block thread for maximum 2 seconds
1201
    while (m_running == true)
21✔
1202
    {
1203
        if (watchdog.elapsed() > 2000)
21✔
1204
            return false;
1✔
1205

1206
        // wait until the function has stopped
1207
        m_functionStopped.wait(&m_stopMutex, 100);
20✔
1208
    }
1209

UNCOV
1210
    return true;
×
1211
}
1212

1213
/*****************************************************************************
1214
 * Attributes
1215
 *****************************************************************************/
1216

1217
int Function::registerAttribute(QString name, int flags, qreal min, qreal max, qreal value)
1,071✔
1218
{
1219
    for (int i = 0; i < m_attributes.count(); i++)
2,811✔
1220
    {
1221
        if (m_attributes[i].m_name == name)
1,740✔
1222
        {
1223
            m_attributes[i].m_min = min;
×
1224
            m_attributes[i].m_max = max;
×
1225
            m_attributes[i].m_value = value;
×
UNCOV
1226
            m_attributes[i].m_flags = flags;
×
UNCOV
1227
            m_attributes[i].m_isOverridden = false;
×
UNCOV
1228
            m_attributes[i].m_overrideValue = 0.0;
×
UNCOV
1229
            return i;
×
1230
        }
1231
    }
1232
    Attribute newAttr;
1,071✔
1233
    newAttr.m_name = name;
1,071✔
1234
    newAttr.m_min = min;
1,071✔
1235
    newAttr.m_max = max;
1,071✔
1236
    newAttr.m_value = value;
1,071✔
1237
    newAttr.m_flags = flags;
1,071✔
1238
    newAttr.m_isOverridden = false;
1,071✔
1239
    newAttr.m_overrideValue = 0.0;
1,071✔
1240
    m_attributes.append(newAttr);
1,071✔
1241

1242
    return m_attributes.count() - 1;
1,071✔
1243
}
1244

1245
int Function::requestAttributeOverride(int attributeIndex, qreal value)
241✔
1246
{
1247
    if (attributeIndex < 0 || attributeIndex >= m_attributes.count())
241✔
UNCOV
1248
        return -1;
×
1249

1250
    int attributeID = invalidAttributeId();
241✔
1251

1252
    if (m_attributes.at(attributeIndex).m_flags & Single)
241✔
1253
    {
1254
        foreach (int id, m_overrideMap.keys())
709✔
1255
        {
1256
            if (m_overrideMap[id].m_attrIndex == attributeIndex)
117✔
1257
            {
1258
                attributeID = id;
5✔
1259
                break;
5✔
1260
            }
1261
        }
1262
    }
1263

1264
    if (attributeID == invalidAttributeId())
241✔
1265
    {
1266
        AttributeOverride override;
1267
        override.m_attrIndex = attributeIndex;
236✔
1268
        override.m_value = 0.0;
236✔
1269

1270
        attributeID = m_lastOverrideAttributeId;
236✔
1271
        m_overrideMap[attributeID] = override;
236✔
1272

1273
        qDebug() << name() << "Override requested for new attribute" << attributeIndex << "value" << value << "new ID" << attributeID;
236✔
1274

1275
        calculateOverrideValue(attributeIndex);
236✔
1276

1277
        m_lastOverrideAttributeId++;
236✔
1278
    }
1279
    else
1280
    {
1281
        qDebug() << name() << "Override requested for existing attribute" << attributeIndex << "value" << value << "single ID" << attributeID;
5✔
1282
    }
1283

1284
    // actually apply the new override value
1285
    adjustAttribute(value, attributeID);
241✔
1286

1287
    return attributeID;
241✔
1288
}
1289

1290
void Function::releaseAttributeOverride(int attributeId)
1✔
1291
{
1292
    if (m_overrideMap.contains(attributeId) == false)
1✔
UNCOV
1293
        return;
×
1294

1295
    int attributeIndex = m_overrideMap[attributeId].m_attrIndex;
1✔
1296

1297
    m_overrideMap.remove(attributeId);
1✔
1298

1299
    calculateOverrideValue(attributeIndex);
1✔
1300

1301
    qDebug() << name() << "Attribute override released" << attributeId;
1✔
1302
}
1303

1304
bool Function::unregisterAttribute(QString name)
1✔
1305
{
1306
    for (int i = 0; i < m_attributes.count(); i++)
2✔
1307
    {
1308
        if (m_attributes[i].m_name == name)
2✔
1309
        {
1310
            m_attributes.removeAt(i);
1✔
1311
            return true;
1✔
1312
        }
1313
    }
UNCOV
1314
    return false;
×
1315
}
1316

1317
bool Function::renameAttribute(int idx, QString newName)
2✔
1318
{
1319
    if (idx < 0 || idx >= m_attributes.count())
2✔
1320
        return false;
1✔
1321
    m_attributes[idx].m_name = newName;
1✔
1322

1323
    return true;
1✔
1324
}
1325

1326
int Function::adjustAttribute(qreal value, int attributeId)
425✔
1327
{
1328
    if (attributeId < 0)
425✔
UNCOV
1329
        return -1;
×
1330

1331
    int attrIndex;
1332

1333
    //qDebug() << name() << "Attribute ID:" << attributeId << ", val:" << value;
1334

1335
    if (attributeId < OVERRIDE_ATTRIBUTE_START_ID)
425✔
1336
    {
1337
        if (attributeId >= m_attributes.count() || m_attributes[attributeId].m_value == value)
179✔
1338
            return -1;
60✔
1339

1340
        // Adjust the original value of an attribute. Only Function editors should do this !
1341
        m_attributes[attributeId].m_value = CLAMP(value, m_attributes[attributeId].m_min, m_attributes[attributeId].m_max);
119✔
1342
        attrIndex = attributeId;
119✔
1343
    }
1344
    else
1345
    {
1346
        if (m_overrideMap.contains(attributeId) == false || m_overrideMap[attributeId].m_value == value)
246✔
1347
            return -1;
5✔
1348

1349
        // Adjust an attribute override value and recalculate the final overridden value
1350
        m_overrideMap[attributeId].m_value = value;
241✔
1351
        attrIndex = m_overrideMap[attributeId].m_attrIndex;
241✔
1352
        calculateOverrideValue(attrIndex);
241✔
1353
    }
1354

1355
    emit attributeChanged(attrIndex, m_attributes[attrIndex].m_isOverridden ?
720✔
1356
                                     m_attributes[attrIndex].m_overrideValue :
241✔
1357
                                     m_attributes[attrIndex].m_value);
119✔
1358

1359
    return attrIndex;
360✔
1360
}
1361

1362
void Function::resetAttributes()
139✔
1363
{
1364
    for (int i = 0; i < m_attributes.count(); i++)
415✔
1365
    {
1366
        m_attributes[i].m_isOverridden = false;
276✔
1367
        m_attributes[i].m_overrideValue = 0.0;
276✔
1368
    }
1369
    m_overrideMap.clear();
139✔
1370
    m_lastOverrideAttributeId = OVERRIDE_ATTRIBUTE_START_ID;
139✔
1371
}
139✔
1372

1373
qreal Function::getAttributeValue(int attributeIndex) const
23,090✔
1374
{
1375
    if (attributeIndex >= m_attributes.count())
23,090✔
1376
        return 0.0;
74✔
1377

1378
    return m_attributes[attributeIndex].m_isOverridden ?
23,016✔
1379
                m_attributes[attributeIndex].m_overrideValue :
244✔
1380
                m_attributes[attributeIndex].m_value;
23,016✔
1381
}
1382

1383
int Function::getAttributeIndex(QString name) const
3✔
1384
{
1385
    for (int i = 0; i < m_attributes.count(); i++)
7✔
1386
    {
1387
        Attribute attr = m_attributes.at(i);
6✔
1388
        if(attr.m_name == name)
6✔
1389
            return i;
2✔
1390
    }
1391
    return -1;
1✔
1392
}
1393

1394
QList<Attribute> Function::attributes() const
116✔
1395
{
1396
    return m_attributes;
116✔
1397
}
1398

1399
void Function::calculateOverrideValue(int attributeIndex)
478✔
1400
{
1401
    if (attributeIndex >= m_attributes.count())
478✔
UNCOV
1402
        return;
×
1403

1404
    qreal finalValue = 0.0;
478✔
1405
    bool found = false;
478✔
1406
    Attribute origAttr = m_attributes.at(attributeIndex);
478✔
1407

1408
    if (origAttr.m_flags & Multiply)
478✔
1409
        finalValue = origAttr.m_value;
476✔
1410

1411
    foreach (AttributeOverride attr, m_overrideMap.values())
2,358✔
1412
    {
1413
        if (attr.m_attrIndex != attributeIndex)
701✔
1414
            continue;
224✔
1415

1416
        found = true;
477✔
1417

1418
        if (origAttr.m_flags & Multiply)
477✔
1419
            finalValue = finalValue * attr.m_value;
475✔
1420
        else if (origAttr.m_flags & LastWins)
2✔
1421
            finalValue = attr.m_value;
2✔
1422
    }
1423

1424
    m_attributes[attributeIndex].m_overrideValue = finalValue;
478✔
1425
    m_attributes[attributeIndex].m_isOverridden = found;
478✔
1426
}
1427

1428
/*************************************************************************
1429
 * Blend mode
1430
 *************************************************************************/
1431

1432
void Function::setBlendMode(Universe::BlendMode mode)
5✔
1433
{
1434
    m_blendMode = mode;
5✔
1435
}
5✔
1436

1437
Universe::BlendMode Function::blendMode() const
160✔
1438
{
1439
    return m_blendMode;
160✔
1440
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc