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

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

82.06
/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
#include "qlcfile.h"
30

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

45
#define OVERRIDE_ATTRIBUTE_START_ID     128
46

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

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

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

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

70
/*****************************************************************************
71
 * Initialization
72
 *****************************************************************************/
73

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

102
}
×
103

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

135
Function::~Function()
452✔
136
{
137
}
452✔
138

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

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

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

163
    return copy;
×
164
}
165

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

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

183
    emit changed(m_id);
18✔
184

185
    return true;
18✔
186
}
187

188
/*****************************************************************************
189
 * ID
190
 *****************************************************************************/
191

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

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

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

209
/*****************************************************************************
210
 * Name
211
 *****************************************************************************/
212

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

218
    m_name = QString(name);
615✔
219

220
    emit nameChanged(m_id);
615✔
221
}
222

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

228
/*****************************************************************************
229
 * Type
230
 *****************************************************************************/
231

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

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

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

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

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

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

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

312
/*********************************************************************
313
 * Visibility
314
 *********************************************************************/
315

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

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

326
/*********************************************************************
327
 * Common
328
 *********************************************************************/
329

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

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

344
    return true;
12✔
345
}
346

347
/*****************************************************************************
348
 * Running order
349
 *****************************************************************************/
350

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

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

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

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

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

398
    doc->writeTextElement(KXMLQLCFunctionRunOrder, runOrderToString(runOrder()));
20✔
399

400
    return true;
10✔
401
}
402

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

414
    setRunOrder(stringToRunOrder(str));
14✔
415

416
    return true;
14✔
417
}
14✔
418

419
/*****************************************************************************
420
 * Direction
421
 *****************************************************************************/
422

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

432
Function::Direction Function::direction() const
18,177✔
433
{
434
    return m_direction;
18,177✔
435
}
436

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

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

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

461
    doc->writeTextElement(KXMLQLCFunctionDirection, directionToString(direction()));
16✔
462

463
    return true;
8✔
464
}
465

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

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

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

480
    return true;
12✔
481
}
12✔
482

483
/*********************************************************************
484
 * Speed type
485
 *********************************************************************/
486

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

492
    m_tempoType = type;
2✔
493

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

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

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

523
    emit changed(m_id);
2✔
524
    emit tempoTypeChanged();
2✔
525
}
526

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

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

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

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

557
    uint value = 0;
4✔
558

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

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

565
    return value + beats;
4✔
566
}
567

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

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

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

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

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

592
bool Function::saveXMLTempoType(QXmlStreamWriter *doc) const
8✔
593
{
594
    Q_ASSERT(doc != NULL);
8✔
595

596
    /* Make this optional to keep projects lighter */
597
    if (tempoType() == Beats)
8✔
598
        doc->writeTextElement(KXMLQLCFunctionTempoType, tempoTypeToString(tempoType()));
×
599

600
    return true;
8✔
601
}
602

603
bool Function::loadXMLTempoType(QXmlStreamReader &root)
×
604
{
605
    if (root.name() != KXMLQLCFunctionTempoType)
×
606
    {
607
        qWarning() << Q_FUNC_INFO << "Tempo type node not found";
×
608
        return false;
×
609
    }
610

611
    QString str = root.readElementText();
×
612
    if (str.isEmpty())
×
613
        return false;
×
614

615
    setTempoType(stringToTempoType(str));
×
616

617
    return true;
×
618
}
×
619

620
/****************************************************************************
621
 * Speed
622
 ****************************************************************************/
623

624
void Function::setFadeInSpeed(uint ms)
33✔
625
{
626
    m_fadeInSpeed = ms;
33✔
627
    emit changed(m_id);
33✔
628
}
33✔
629

630
uint Function::fadeInSpeed() const
982✔
631
{
632
    return m_fadeInSpeed;
982✔
633
}
634

635
void Function::setFadeOutSpeed(uint ms)
33✔
636
{
637
    m_fadeOutSpeed = ms;
33✔
638
    emit changed(m_id);
33✔
639
}
33✔
640

641
uint Function::fadeOutSpeed() const
167✔
642
{
643
    return m_fadeOutSpeed;
167✔
644
}
645

646
void Function::setDuration(uint ms)
131✔
647
{
648
    m_duration = ms;
131✔
649
    emit changed(m_id);
131✔
650
}
131✔
651

652
uint Function::duration() const
946✔
653
{
654
    return m_duration;
946✔
655
}
656

657
quint32 Function::totalDuration()
1✔
658
{
659
    // fall back to duration in case a
660
    // subclass doesn't provide this method
661
    return duration();
1✔
662
}
663

664
void Function::setTotalDuration(quint32 msec)
×
665
{
666
    Q_UNUSED(msec)
667
}
×
668

669
void Function::setOverrideFadeInSpeed(uint ms)
1✔
670
{
671
    m_overrideFadeInSpeed = ms;
1✔
672
}
1✔
673

674
uint Function::overrideFadeInSpeed() const
1,085✔
675
{
676
    return m_overrideFadeInSpeed;
1,085✔
677
}
678

679
void Function::setOverrideFadeOutSpeed(uint ms)
35✔
680
{
681
    m_overrideFadeOutSpeed = ms;
35✔
682
}
35✔
683

684
uint Function::overrideFadeOutSpeed() const
292✔
685
{
686
    return m_overrideFadeOutSpeed;
292✔
687
}
688

689
void Function::setOverrideDuration(uint ms)
1✔
690
{
691
    m_overrideDuration = ms;
1✔
692
}
1✔
693

694
uint Function::overrideDuration() const
155✔
695
{
696
    return m_overrideDuration;
155✔
697
}
698

699
QString Function::speedToString(uint ms)
60✔
700
{
701
    QString str;
60✔
702
    if (ms == infiniteSpeed())
60✔
703
    {
704
        str = QChar(0x221E); // Infinity symbol
17✔
705
    }
706
    else
707
    {
708
        uint h, m, s;
709

710
        h = ms / MS_PER_HOUR;
43✔
711
        ms -= (h * MS_PER_HOUR);
43✔
712

713
        m = ms / MS_PER_MINUTE;
43✔
714
        ms -= (m * MS_PER_MINUTE);
43✔
715

716
        s = ms / MS_PER_SECOND;
43✔
717
        ms -= (s * MS_PER_SECOND);
43✔
718

719
        if (h != 0)
43✔
720
            str += QString("%1h").arg(h, 1, 10, QChar('0'));
4✔
721
        if (m != 0)
43✔
722
            str += QString("%1m").arg(m, str.size() ? 2 : 1, 10, QChar('0'));
5✔
723
        if (s != 0)
43✔
724
            str += QString("%1s").arg(s, str.size() ? 2 : 1, 10, QChar('0'));
6✔
725
        if (ms != 0 || str.size() == 0)
43✔
726
            str += QString("%1ms").arg(ms, str.size() ? 3 : 1, 10, QChar('0'));
40✔
727
    }
728

729
    return str;
60✔
730
}
×
731

732
static uint speedSplit(QString& speedString, QString splitNeedle)
78✔
733
{
734
    QStringList splitResult;
78✔
735
    // Filter out "ms" because "m" and "s" may wrongly use it
736
    splitResult = speedString.split("ms");
78✔
737
    if (splitResult.count() > 1)
78✔
738
        splitResult = splitResult.at(0).split(splitNeedle);
12✔
739
    else
740
        splitResult = speedString.split(splitNeedle);
66✔
741

742
    if (splitResult.count() > 1)
78✔
743
    {
744
        speedString.remove(0, speedString.indexOf(splitNeedle) + 1);
13✔
745
        return splitResult.at(0).toUInt();
13✔
746
    }
747
    return 0;
65✔
748
}
78✔
749

750
uint Function::stringToSpeed(QString speed)
27✔
751
{
752
    uint value = 0;
27✔
753

754
    if (speed == QChar(0x221E)) // Infinity symbol
27✔
755
        return infiniteSpeed();
1✔
756

757
    value += speedSplit(speed, "h") * 1000 * 60 * 60;
26✔
758
    value += speedSplit(speed, "m") * 1000 * 60;
26✔
759
    value += speedSplit(speed, "s") * 1000;
26✔
760

761
    if (speed.contains("."))
26✔
762
    {
763
        // lround avoids toDouble precison issues (.03 transforms to .029)
764
        value += lround(speed.toDouble() * 1000.0);
20✔
765
    }
766
    else
767
    {
768
        if (speed.contains("ms"))
6✔
769
            speed = speed.split("ms").at(0);
3✔
770
        value += speed.toUInt();
6✔
771
    }
772

773
    return speedNormalize(value);
26✔
774
}
775

776
uint Function::speedNormalize(uint speed)
81✔
777
{
778
    if ((int)speed < 0)
81✔
779
        return infiniteSpeed();
6✔
780
    return speed;
75✔
781
}
782

783
uint Function::speedAdd(uint left, uint right)
14✔
784
{
785
    if (speedNormalize(left) == infiniteSpeed()
14✔
786
        || speedNormalize(right) == infiniteSpeed())
14✔
787
        return infiniteSpeed();
3✔
788

789
    return speedNormalize(left + right);
11✔
790
}
791

792
uint Function::speedSubtract(uint left, uint right)
7✔
793
{
794
    if (right >= left)
7✔
795
        return 0;
4✔
796
    if (speedNormalize(right) == infiniteSpeed())
3✔
797
        return 0;
×
798
    if (speedNormalize(left) == infiniteSpeed())
3✔
799
        return infiniteSpeed();
1✔
800

801
    return speedNormalize(left - right);
2✔
802
}
803

804
void Function::tap()
×
805
{
806
}
×
807

808
bool Function::loadXMLSpeed(QXmlStreamReader &speedRoot)
8✔
809
{
810
    if (speedRoot.name() != KXMLQLCFunctionSpeed)
8✔
811
        return false;
×
812

813
    QXmlStreamAttributes attrs = speedRoot.attributes();
8✔
814

815
    m_fadeInSpeed = attrs.value(KXMLQLCFunctionSpeedFadeIn).toString().toUInt();
8✔
816
    m_fadeOutSpeed = attrs.value(KXMLQLCFunctionSpeedFadeOut).toString().toUInt();
8✔
817
    m_duration = attrs.value(KXMLQLCFunctionSpeedDuration).toString().toUInt();
8✔
818

819
    speedRoot.skipCurrentElement();
8✔
820

821
    return true;
8✔
822
}
8✔
823

824
bool Function::saveXMLSpeed(QXmlStreamWriter *doc) const
10✔
825
{
826
    doc->writeStartElement(KXMLQLCFunctionSpeed);
20✔
827
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeIn, QString::number(fadeInSpeed()));
20✔
828
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeOut, QString::number(fadeOutSpeed()));
20✔
829
    doc->writeAttribute(KXMLQLCFunctionSpeedDuration, QString::number(duration()));
20✔
830
    doc->writeEndElement();
10✔
831

832
    return true;
10✔
833
}
834

835
uint Function::infiniteSpeed()
485✔
836
{
837
    return (uint) -2;
485✔
838
}
839

840
uint Function::defaultSpeed()
3,334✔
841
{
842
    return (uint) -1;
3,334✔
843
}
844

845
/*****************************************************************************
846
 * UI State
847
 *****************************************************************************/
848

849
QVariant Function::uiStateValue(QString property)
2✔
850
{
851
    return m_uiState.value(property, QVariant());
4✔
852
}
853

854
void Function::setUiStateValue(QString property, QVariant value)
1✔
855
{
856
    m_uiState[property] = value;
1✔
857
}
1✔
858

859
QMap<QString, QVariant> Function::uiStateMap() const
20✔
860
{
861
    return m_uiState;
20✔
862
}
863

864
/*****************************************************************************
865
 * Fixtures
866
 *****************************************************************************/
867

868
void Function::slotFixtureRemoved(quint32 fid)
1✔
869
{
870
    Q_UNUSED(fid);
871
}
1✔
872

873
/*****************************************************************************
874
 * Load & Save
875
 *****************************************************************************/
876
bool Function::saveXML(QXmlStreamWriter *doc)
×
877
{
878
    Q_UNUSED(doc)
879
    return false;
×
880
}
881

882
bool Function::loadXML(QXmlStreamReader &root)
×
883
{
884
    Q_UNUSED(root)
885
    return false;
×
886
}
887

888
bool Function::loader(QXmlStreamReader &root, Doc* doc)
12✔
889
{
890
    if (root.name() != KXMLQLCFunction)
12✔
891
    {
892
        qWarning("Function node not found!");
2✔
893
        return false;
2✔
894
    }
895

896
    QXmlStreamAttributes attrs = root.attributes();
10✔
897

898
    /* Get common information from the tag's attributes */
899
    quint32 id = attrs.value(KXMLQLCFunctionID).toString().toUInt();
10✔
900
    QString name = attrs.value(KXMLQLCFunctionName).toString();
10✔
901
    Type type = Function::stringToType(attrs.value(KXMLQLCFunctionType).toString());
10✔
902
    QString path;
10✔
903
    bool visible = true;
10✔
904
    Universe::BlendMode blendMode = Universe::NormalBlend;
10✔
905

906
    if (attrs.hasAttribute(KXMLQLCFunctionPath))
10✔
907
        path = attrs.value(KXMLQLCFunctionPath).toString();
×
908
    if (attrs.hasAttribute(KXMLQLCFunctionHidden))
10✔
909
        visible = false;
×
910
    if (attrs.hasAttribute(KXMLQLCFunctionBlendMode))
10✔
911
        blendMode = Universe::stringToBlendMode(attrs.value(KXMLQLCFunctionBlendMode).toString());
×
912

913
    /* Check for ID validity before creating the function */
914
    if (id == Function::invalidId())
10✔
915
    {
916
        qWarning() << Q_FUNC_INFO << "Function ID" << id << "is not allowed.";
×
917
        return false;
×
918
    }
919

920
    /* Create a new function according to the type */
921
    Function* function = NULL;
10✔
922
    if (type == Function::SceneType)
10✔
923
        function = new class Scene(doc);
1✔
924
    else if (type == Function::ChaserType)
9✔
925
        function = new class Chaser(doc);
1✔
926
    else if (type == Function::CollectionType)
8✔
927
        function = new class Collection(doc);
5✔
928
    else if (type == Function::EFXType)
3✔
929
        function = new class EFX(doc);
1✔
930
    else if (type == Function::ScriptType)
2✔
931
        function = new class Script(doc);
×
932
    else if (type == Function::RGBMatrixType)
2✔
933
        function = new class RGBMatrix(doc);
×
934
    else if (type == Function::ShowType)
2✔
935
        function = new class Show(doc);
×
936
    else if (type == Function::SequenceType)
2✔
937
        function = new class Sequence(doc);
×
938
    else if (type == Function::AudioType)
2✔
939
        function = new class Audio(doc);
×
940
    else if (type == Function::VideoType)
2✔
941
        function = new class Video(doc);
×
942
    else
943
        return false;
2✔
944

945
    function->setName(name);
8✔
946
    function->setPath(path);
8✔
947
    function->setVisible(visible);
8✔
948
    function->setBlendMode(blendMode);
8✔
949
    if (function->loadXML(root) == true)
8✔
950
    {
951
        if (doc->addFunction(function, id) == true)
8✔
952
        {
953
            /* Success */
954
            return true;
8✔
955
        }
956
        else
957
        {
958
            qWarning() << "Function" << name << "cannot be created.";
×
959
            delete function;
×
960
            return false;
×
961
        }
962
    }
963
    else
964
    {
965
        qWarning() << "Function" << name << "cannot be loaded.";
×
966
        delete function;
×
967
        return false;
×
968
    }
969
}
10✔
970

971
void Function::postLoad()
×
972
{
973
    /* NOP */
974
}
×
975

976
bool Function::contains(quint32 functionId)
16✔
977
{
978
    Q_UNUSED(functionId);
979
    return false;
16✔
980
}
981

982
QList<quint32> Function::components()
×
983
{
984
    return QList<quint32>();
×
985
}
986

987
/*****************************************************************************
988
 * Flash
989
 *****************************************************************************/
990

991
void Function::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP)
7✔
992
{
993
    Q_UNUSED(timer);
994
    Q_UNUSED(shouldOverride);
995
    Q_UNUSED(forceLTP);
996

997
    if (m_flashing == false)
7✔
998
    {
999
        emit flashing(m_id, true);
6✔
1000
    }
1001

1002
    m_flashing = true;
7✔
1003
}
7✔
1004

1005
void Function::unFlash(MasterTimer *timer)
6✔
1006
{
1007
    Q_UNUSED(timer);
1008

1009
    if (m_flashing == true)
6✔
1010
        emit flashing(m_id, false);
6✔
1011

1012
    m_flashing = false;
6✔
1013
}
6✔
1014

1015
bool Function::flashing() const
23✔
1016
{
1017
    return m_flashing;
23✔
1018
}
1019

1020
/*****************************************************************************
1021
 * Running
1022
 *****************************************************************************/
1023

1024
void Function::preRun(MasterTimer *timer)
169✔
1025
{
1026
    Q_UNUSED(timer);
1027

1028
    qDebug() << "Function preRun. Name:" << m_name << "ID:" << m_id << "type:" << typeToString(type());
169✔
1029
    m_running = true;
169✔
1030

1031
    emit running(m_id);
169✔
1032
}
169✔
1033

1034
void Function::write(MasterTimer *timer, QList<Universe *> universes)
×
1035
{
1036
    Q_UNUSED(timer);
1037
    Q_UNUSED(universes);
1038
}
×
1039

1040
void Function::postRun(MasterTimer *timer, QList<Universe *> universes)
139✔
1041
{
1042
    Q_UNUSED(timer);
1043
    Q_UNUSED(universes);
1044

1045
    qDebug() << "Function postRun. Name:" << m_name << "ID: " << m_id;
139✔
1046

1047
    {
1048
        QMutexLocker locker(&m_stopMutex);
139✔
1049

1050
        resetElapsed();
139✔
1051
        if (m_preserveAttributes == false)
139✔
1052
            resetAttributes();
139✔
1053

1054
        m_functionStopped.wakeAll();
139✔
1055
    }
139✔
1056

1057
    m_paused = false;
139✔
1058
    m_running = false;
139✔
1059
    emit stopped(m_id);
139✔
1060
}
139✔
1061

1062
void Function::dismissAllFaders()
112✔
1063
{
1064
    QMapIterator <quint32, QSharedPointer<GenericFader> > it(m_fadersMap);
112✔
1065
    while (it.hasNext() == true)
213✔
1066
    {
1067
        it.next();
101✔
1068
        QSharedPointer<GenericFader> fader = it.value();
101✔
1069
        if (!fader.isNull())
101✔
1070
            fader->requestDelete();
101✔
1071
    }
101✔
1072

1073
    m_fadersMap.clear();
112✔
1074
}
112✔
1075

1076
bool Function::isRunning() const
18,578✔
1077
{
1078
    return m_running;
18,578✔
1079
}
1080

1081
bool Function::isPaused() const
546✔
1082
{
1083
    return m_paused;
546✔
1084
}
1085

1086
/*****************************************************************************
1087
 * Elapsed ticks while running
1088
 *****************************************************************************/
1089

1090
quint32 Function::elapsed() const
20✔
1091
{
1092
    return m_elapsed;
20✔
1093
}
1094

1095
quint32 Function::elapsedBeats() const
1✔
1096
{
1097
    return m_elapsedBeats;
1✔
1098
}
1099

1100
void Function::resetElapsed()
140✔
1101
{
1102
    qDebug() << Q_FUNC_INFO;
140✔
1103
    m_elapsed = 0;
140✔
1104
    m_elapsedBeats = 0;
140✔
1105
}
140✔
1106

1107
void Function::incrementElapsed()
644✔
1108
{
1109
    // Don't wrap around. UINT_MAX is the maximum fade/hold time.
1110
    if (m_elapsed < UINT_MAX - MasterTimer::tick())
644✔
1111
        m_elapsed += MasterTimer::tick();
642✔
1112
    else
1113
        m_elapsed = UINT_MAX;
2✔
1114
}
644✔
1115

1116
void Function::incrementElapsedBeats()
×
1117
{
1118
    m_elapsedBeats += 1000;
×
1119
}
×
1120

1121
void Function::roundElapsed(quint32 roundTime)
×
1122
{
1123
    if (roundTime == 0)
×
1124
        m_elapsed = 0;
×
1125
    else
1126
        m_elapsed %= roundTime;
×
1127
}
×
1128

1129
/*****************************************************************************
1130
 * Start & Stop
1131
 *****************************************************************************/
1132

1133
void Function::start(MasterTimer* timer, FunctionParent source, quint32 startTime,
154✔
1134
                     uint overrideFadeIn, uint overrideFadeOut, uint overrideDuration, TempoType overrideTempoType)
1135
{
1136
    qDebug() << "Function start(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id() << ", startTime:" << startTime;
154✔
1137

1138
    Q_ASSERT(timer != NULL);
154✔
1139

1140
    {
1141
        QMutexLocker sourcesLocker(&m_sourcesMutex);
154✔
1142
        if (m_sources.contains(source))
154✔
1143
            return;
×
1144
        m_sources.append(source);
154✔
1145
        if (m_sources.size() > 1)
154✔
1146
            return;
×
1147
    }
154✔
1148

1149
    /** If we're in a paused state, then just return to the running state
1150
     *  to let subclasses resuming what they were doing. */
1151
    if (m_paused == true)
154✔
1152
    {
1153
        m_paused = false;
×
1154
        return;
×
1155
    }
1156

1157
    m_elapsed = startTime;
154✔
1158
    m_elapsedBeats = 0;
154✔
1159
    m_overrideFadeInSpeed = overrideFadeIn;
154✔
1160
    m_overrideFadeOutSpeed = overrideFadeOut;
154✔
1161
    m_overrideDuration = overrideDuration;
154✔
1162
    m_overrideTempoType = overrideTempoType == Original ? tempoType() : overrideTempoType;
154✔
1163

1164
    m_stop = false;
154✔
1165
    timer->startFunction(this);
154✔
1166
}
1167

1168
void Function::setPause(bool enable)
10✔
1169
{
1170
    if (enable && isRunning() == false)
10✔
1171
        return;
×
1172

1173
    m_paused = enable;
10✔
1174
}
1175

1176
void Function::stop(FunctionParent source, bool preserveAttributes)
152✔
1177
{
1178
    qDebug() << "Function stop(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id();
152✔
1179

1180
    QMutexLocker sourcesLocker(&m_sourcesMutex);
152✔
1181

1182
    if ((source.id() == id() && source.type() == FunctionParent::Function) ||
313✔
1183
        (source.type() == FunctionParent::Master) ||
313✔
1184
        (source.type() == FunctionParent::ManualVCWidget))
120✔
1185
    {
1186
        m_sources.clear();
35✔
1187
    }
1188
    else
1189
    {
1190
        m_sources.removeAll(source);
117✔
1191
    }
1192

1193
    if (m_sources.size() == 0)
152✔
1194
    {
1195
        m_stop = true;
152✔
1196
        m_preserveAttributes = preserveAttributes;
152✔
1197
    }
1198
}
152✔
1199

1200
bool Function::stopped() const
770✔
1201
{
1202
    return m_stop;
770✔
1203
}
1204

1205
bool Function::startedAsChild() const
×
1206
{
1207
    QMutexLocker sourcesLocker(const_cast<QMutex*>(&m_sourcesMutex));
×
1208
    foreach (FunctionParent source, m_sources)
×
1209
    {
1210
        if (source.type() == FunctionParent::Function && source.id() != id())
×
1211
            return true;
×
1212
    }
×
1213
    return false;
×
1214
}
×
1215

1216
int Function::invalidAttributeId()
730✔
1217
{
1218
    return -1;
730✔
1219
}
1220

1221
bool Function::stopAndWait()
1✔
1222
{
1223
    QMutexLocker locker(&m_stopMutex);
1✔
1224

1225
    stop(FunctionParent::master());
1✔
1226

1227
    QElapsedTimer watchdog;
1✔
1228
    watchdog.restart();
1✔
1229

1230
    // block thread for maximum 2 seconds
1231
    while (m_running == true)
21✔
1232
    {
1233
        if (watchdog.elapsed() > 2000)
21✔
1234
            return false;
1✔
1235

1236
        // wait until the function has stopped
1237
        m_functionStopped.wait(&m_stopMutex, 100);
20✔
1238
    }
1239

1240
    return true;
×
1241
}
1✔
1242

1243
/*****************************************************************************
1244
 * Attributes
1245
 *****************************************************************************/
1246

1247
int Function::registerAttribute(QString name, int flags, qreal min, qreal max, qreal value)
1,154✔
1248
{
1249
    for (int i = 0; i < m_attributes.count(); i++)
3,071✔
1250
    {
1251
        if (m_attributes[i].m_name == name)
1,917✔
1252
        {
1253
            m_attributes[i].m_min = min;
×
1254
            m_attributes[i].m_max = max;
×
1255
            m_attributes[i].m_value = value;
×
1256
            m_attributes[i].m_flags = flags;
×
1257
            m_attributes[i].m_isOverridden = false;
×
1258
            m_attributes[i].m_overrideValue = 0.0;
×
1259
            return i;
×
1260
        }
1261
    }
1262
    Attribute newAttr;
1,154✔
1263
    newAttr.m_name = name;
1,154✔
1264
    newAttr.m_min = min;
1,154✔
1265
    newAttr.m_max = max;
1,154✔
1266
    newAttr.m_value = value;
1,154✔
1267
    newAttr.m_flags = flags;
1,154✔
1268
    newAttr.m_isOverridden = false;
1,154✔
1269
    newAttr.m_overrideValue = 0.0;
1,154✔
1270
    m_attributes.append(newAttr);
1,154✔
1271

1272
    return m_attributes.count() - 1;
1,154✔
1273
}
1,154✔
1274

1275
int Function::requestAttributeOverride(int attributeIndex, qreal value)
247✔
1276
{
1277
    if (attributeIndex < 0 || attributeIndex >= m_attributes.count())
247✔
1278
        return -1;
×
1279

1280
    int attributeID = invalidAttributeId();
247✔
1281

1282
    if (m_attributes.at(attributeIndex).m_flags & Single)
247✔
1283
    {
1284
        QMap <int, AttributeOverride>::iterator it = m_overrideMap.begin();
246✔
1285
        for (; it != m_overrideMap.end(); it++)
361✔
1286
        {
1287
            if (it.value().m_attrIndex == attributeIndex)
120✔
1288
            {
1289
                attributeID = it.key();
5✔
1290
                break;
5✔
1291
            }
1292
        }
1293
    }
1294

1295
    if (attributeID == invalidAttributeId())
247✔
1296
    {
1297
        AttributeOverride override;
1298
        override.m_attrIndex = attributeIndex;
242✔
1299
        override.m_value = 0.0;
242✔
1300

1301
        attributeID = m_lastOverrideAttributeId;
242✔
1302
        m_overrideMap[attributeID] = override;
242✔
1303

1304
        qDebug() << name() << "Override requested for new attribute" << attributeIndex << "value" << value << "new ID" << attributeID;
242✔
1305

1306
        calculateOverrideValue(attributeIndex);
242✔
1307

1308
        m_lastOverrideAttributeId++;
242✔
1309
    }
1310
    else
1311
    {
1312
        qDebug() << name() << "Override requested for existing attribute" << attributeIndex << "value" << value << "single ID" << attributeID;
5✔
1313
    }
1314

1315
    // actually apply the new override value
1316
    adjustAttribute(value, attributeID);
247✔
1317

1318
    return attributeID;
247✔
1319
}
1320

1321
void Function::releaseAttributeOverride(int attributeId)
1✔
1322
{
1323
    if (m_overrideMap.contains(attributeId) == false)
1✔
1324
        return;
×
1325

1326
    int attributeIndex = m_overrideMap[attributeId].m_attrIndex;
1✔
1327

1328
    m_overrideMap.remove(attributeId);
1✔
1329

1330
    calculateOverrideValue(attributeIndex);
1✔
1331

1332
    qDebug() << name() << "Attribute override released" << attributeId;
1✔
1333
}
1334

1335
bool Function::unregisterAttribute(QString name)
12✔
1336
{
1337
    for (int i = 0; i < m_attributes.count(); i++)
13✔
1338
    {
1339
        if (m_attributes[i].m_name == name)
13✔
1340
        {
1341
            m_attributes.removeAt(i);
12✔
1342
            return true;
12✔
1343
        }
1344
    }
1345
    return false;
×
1346
}
1347

1348
bool Function::renameAttribute(int idx, QString newName)
2✔
1349
{
1350
    if (idx < 0 || idx >= m_attributes.count())
2✔
1351
        return false;
1✔
1352
    m_attributes[idx].m_name = newName;
1✔
1353

1354
    return true;
1✔
1355
}
1356

1357
int Function::adjustAttribute(qreal value, int attributeId)
431✔
1358
{
1359
    if (attributeId < 0)
431✔
1360
        return -1;
×
1361

1362
    int attrIndex;
1363

1364
    //qDebug() << name() << "Attribute ID:" << attributeId << ", val:" << value;
1365

1366
    if (attributeId < OVERRIDE_ATTRIBUTE_START_ID)
431✔
1367
    {
1368
        if (attributeId >= m_attributes.count() || m_attributes[attributeId].m_value == value)
179✔
1369
            return -1;
60✔
1370

1371
        // Adjust the original value of an attribute. Only Function editors should do this!
1372
        m_attributes[attributeId].m_value = CLAMP(value, m_attributes[attributeId].m_min, m_attributes[attributeId].m_max);
119✔
1373
        attrIndex = attributeId;
119✔
1374
    }
1375
    else
1376
    {
1377
        if (m_overrideMap.contains(attributeId) == false)
252✔
1378
            return -1;
×
1379

1380
        // Adjust an attribute override value and recalculate the final overridden value
1381
        m_overrideMap[attributeId].m_value = value;
252✔
1382
        attrIndex = m_overrideMap[attributeId].m_attrIndex;
252✔
1383
        calculateOverrideValue(attrIndex);
252✔
1384
    }
1385

1386
    emit attributeChanged(attrIndex, m_attributes[attrIndex].m_isOverridden ?
742✔
1387
                                     m_attributes[attrIndex].m_overrideValue :
252✔
1388
                                     m_attributes[attrIndex].m_value);
119✔
1389

1390
    return attrIndex;
371✔
1391
}
1392

1393
void Function::resetAttributes()
141✔
1394
{
1395
    for (int i = 0; i < m_attributes.count(); i++)
421✔
1396
    {
1397
        m_attributes[i].m_isOverridden = false;
280✔
1398
        m_attributes[i].m_overrideValue = 0.0;
280✔
1399
    }
1400
    m_overrideMap.clear();
141✔
1401
    m_lastOverrideAttributeId = OVERRIDE_ATTRIBUTE_START_ID;
141✔
1402
}
141✔
1403

1404
qreal Function::getAttributeValue(int attributeIndex) const
90,939✔
1405
{
1406
    if (attributeIndex >= m_attributes.count())
90,939✔
1407
        return 0.0;
75✔
1408

1409
    return m_attributes[attributeIndex].m_isOverridden ?
181,728✔
1410
                m_attributes[attributeIndex].m_overrideValue :
252✔
1411
                m_attributes[attributeIndex].m_value;
181,476✔
1412
}
1413

1414
int Function::getAttributeIndex(QString name) const
3✔
1415
{
1416
    for (int i = 0; i < m_attributes.count(); i++)
7✔
1417
    {
1418
        Attribute attr = m_attributes.at(i);
6✔
1419
        if (attr.m_name == name)
6✔
1420
            return i;
2✔
1421
    }
6✔
1422
    return -1;
1✔
1423
}
1424

1425
QList<Attribute> Function::attributes() const
121✔
1426
{
1427
    return m_attributes;
121✔
1428
}
1429

1430
void Function::calculateOverrideValue(int attributeIndex)
495✔
1431
{
1432
    if (attributeIndex >= m_attributes.count())
495✔
1433
        return;
×
1434

1435
    qreal finalValue = 0.0;
495✔
1436
    bool found = false;
495✔
1437
    Attribute origAttr = m_attributes.at(attributeIndex);
495✔
1438

1439
    if (origAttr.m_flags & Multiply)
495✔
1440
        finalValue = origAttr.m_value;
493✔
1441

1442
    foreach (AttributeOverride attr, m_overrideMap)
1,221✔
1443
    {
1444
        if (attr.m_attrIndex != attributeIndex)
726✔
1445
            continue;
232✔
1446

1447
        found = true;
494✔
1448

1449
        if (origAttr.m_flags & Multiply)
494✔
1450
            finalValue = finalValue * attr.m_value;
492✔
1451
        else if (origAttr.m_flags & LastWins)
2✔
1452
            finalValue = attr.m_value;
2✔
1453
    }
495✔
1454

1455
    m_attributes[attributeIndex].m_overrideValue = finalValue;
495✔
1456
    m_attributes[attributeIndex].m_isOverridden = found;
495✔
1457
}
495✔
1458

1459
/*************************************************************************
1460
 * Blend mode
1461
 *************************************************************************/
1462

1463
void Function::setBlendMode(Universe::BlendMode mode)
5✔
1464
{
1465
    m_blendMode = mode;
5✔
1466
}
5✔
1467

1468
Universe::BlendMode Function::blendMode() const
168✔
1469
{
1470
    return m_blendMode;
168✔
1471
}
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