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

mcallegari / qlcplus / 7025156636

28 Nov 2023 10:11PM UTC coverage: 31.979% (-0.02%) from 32.001%
7025156636

push

github

mcallegari
qmlui: start to handle real beat-based Show

15114 of 47262 relevant lines covered (31.98%)

23507.96 hits per line

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

85.17
/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)
439✔
104
    : QObject(doc)
105
    , m_id(Function::invalidId())
878✔
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())
878✔
118
    , m_overrideFadeOutSpeed(defaultSpeed())
878✔
119
    , m_overrideDuration(defaultSpeed())
878✔
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)
439✔
129
{
130
    Q_ASSERT(doc != NULL);
439✔
131
    registerAttribute(tr("Intensity"), Multiply | Single);
439✔
132
}
439✔
133

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

138
Doc* Function::doc() const
2,221✔
139
{
140
    return qobject_cast<Doc*>(parent());
2,221✔
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)
18✔
166
{
167
    if (function == NULL)
18✔
168
        return false;
1✔
169

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

182
    emit changed(m_id);
17✔
183

184
    return true;
17✔
185
}
186

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

191
void Function::setID(quint32 id)
261✔
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;
261✔
196
}
261✔
197

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

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

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

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

217
    m_name = QString(name);
597✔
218

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

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

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

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

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

241
QString Function::typeToString(Type type)
230✔
242
{
243
    switch (type)
230✔
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;
3✔
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
28✔
304
{
305
    if (simplified == true)
28✔
306
        return m_path;
28✔
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
34✔
321
{
322
    return m_visible;
34✔
323
}
324

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

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

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

343
    return true;
11✔
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
475✔
361
{
362
    return m_runOrder;
475✔
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,606✔
432
{
433
    return m_direction;
4,606✔
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
    emit tempoTypeChanged();
2✔
524
}
525

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

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

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

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

556
    uint value = 0;
4✔
557

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

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

564
    return value + beats;
4✔
565
}
566

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

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

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

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

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

591
/****************************************************************************
592
 * Speed
593
 ****************************************************************************/
594

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

700
    return str;
60✔
701
}
702

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

790
    speedRoot.skipCurrentElement();
7✔
791

792
    return true;
7✔
793
}
794

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

803
    return true;
9✔
804
}
805

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

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

816
/*****************************************************************************
817
 * UI State
818
 *****************************************************************************/
819

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

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

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

835
/*****************************************************************************
836
 * Fixtures
837
 *****************************************************************************/
838

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

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

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

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

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

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

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

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

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

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

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

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

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

958
/*****************************************************************************
959
 * Flash
960
 *****************************************************************************/
961

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

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

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

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

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

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

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

991
/*****************************************************************************
992
 * Running
993
 *****************************************************************************/
994

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1144
    m_paused = enable;
10✔
1145
}
1146

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

1151
    QMutexLocker sourcesLocker(&m_sourcesMutex);
296✔
1152

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

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

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

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

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

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

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

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

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

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

1211
    return true;
×
1212
}
1213

1214
/*****************************************************************************
1215
 * Attributes
1216
 *****************************************************************************/
1217

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

1243
    return m_attributes.count() - 1;
1,097✔
1244
}
1245

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

1251
    int attributeID = invalidAttributeId();
241✔
1252

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

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

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

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

1276
        calculateOverrideValue(attributeIndex);
236✔
1277

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

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

1288
    return attributeID;
241✔
1289
}
1290

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

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

1298
    m_overrideMap.remove(attributeId);
1✔
1299

1300
    calculateOverrideValue(attributeIndex);
1✔
1301

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

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

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

1324
    return true;
1✔
1325
}
1326

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

1332
    int attrIndex;
1333

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

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

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

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

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

1360
    return attrIndex;
360✔
1361
}
1362

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

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

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

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

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

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

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

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

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

1417
        found = true;
477✔
1418

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

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

1429
/*************************************************************************
1430
 * Blend mode
1431
 *************************************************************************/
1432

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

1438
Universe::BlendMode Function::blendMode() const
163✔
1439
{
1440
    return m_blendMode;
163✔
1441
}
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