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

mcallegari / qlcplus / 21760395819

06 Feb 2026 05:52PM UTC coverage: 33.994% (-0.08%) from 34.078%
21760395819

push

github

mcallegari
Back to 5.2.1 debug

17635 of 51877 relevant lines covered (33.99%)

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

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

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

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

182
    emit changed(m_id);
18✔
183

184
    return true;
18✔
185
}
186

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

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

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

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

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

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

217
    m_name = QString(name);
615✔
218

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

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

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

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

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

241
QString Function::typeToString(Type type)
236✔
242
{
243
    switch (type)
236✔
244
    {
245
        case SceneType:      return KSceneString;
137✔
246
        case ChaserType:     return KChaserString;
22✔
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;
3✔
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
30✔
304
{
305
    if (simplified == true)
30✔
306
        return m_path;
30✔
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
36✔
321
{
322
    return m_visible;
36✔
323
}
324

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

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

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

343
    return true;
12✔
344
}
345

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

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

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

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

381
Function::RunOrder Function::stringToRunOrder(const QString& str)
20✔
382
{
383
    if (str == KPingPongString)
20✔
384
        return PingPong;
3✔
385
    else if (str == KSingleShotString)
17✔
386
        return SingleShot;
9✔
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
10✔
394
{
395
    Q_ASSERT(doc != NULL);
10✔
396

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

399
    return true;
10✔
400
}
401

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

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

415
    return true;
14✔
416
}
14✔
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
18,177✔
432
{
433
    return m_direction;
18,177✔
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()));
16✔
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();
12✔
474
    if (str.isEmpty())
12✔
475
        return false;
×
476

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

479
    return true;
12✔
480
}
12✔
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,475✔
527
{
528
    return m_tempoType;
1,475✔
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
bool Function::saveXMLTempoType(QXmlStreamWriter *doc) const
8✔
592
{
593
    Q_ASSERT(doc != NULL);
8✔
594

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

599
    return true;
8✔
600
}
601

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

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

614
    setTempoType(stringToTempoType(str));
×
615

616
    return true;
×
617
}
×
618

619
/****************************************************************************
620
 * Speed
621
 ****************************************************************************/
622

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

728
    return str;
60✔
729
}
×
730

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

818
    speedRoot.skipCurrentElement();
8✔
819

820
    return true;
8✔
821
}
8✔
822

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

831
    return true;
10✔
832
}
833

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

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

844
/*****************************************************************************
845
 * UI State
846
 *****************************************************************************/
847

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

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

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

863
/*****************************************************************************
864
 * Fixtures
865
 *****************************************************************************/
866

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

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

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

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

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

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

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

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

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

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

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

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

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

986
/*****************************************************************************
987
 * Flash
988
 *****************************************************************************/
989

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

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

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

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

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

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

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

1019
/*****************************************************************************
1020
 * Running
1021
 *****************************************************************************/
1022

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1172
    m_paused = enable;
10✔
1173
}
1174

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

1179
    QMutexLocker sourcesLocker(&m_sourcesMutex);
152✔
1180

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

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

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

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

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

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

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

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

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

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

1239
    return true;
×
1240
}
1✔
1241

1242
/*****************************************************************************
1243
 * Attributes
1244
 *****************************************************************************/
1245

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

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

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

1279
    int attributeID = invalidAttributeId();
247✔
1280

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

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

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

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

1305
        calculateOverrideValue(attributeIndex);
242✔
1306

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

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

1317
    return attributeID;
247✔
1318
}
1319

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

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

1327
    m_overrideMap.remove(attributeId);
1✔
1328

1329
    calculateOverrideValue(attributeIndex);
1✔
1330

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

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

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

1353
    return true;
1✔
1354
}
1355

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

1361
    int attrIndex;
1362

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

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

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

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

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

1389
    return attrIndex;
371✔
1390
}
1391

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

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

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

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

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

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

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

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

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

1446
        found = true;
494✔
1447

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

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

1458
/*************************************************************************
1459
 * Blend mode
1460
 *************************************************************************/
1461

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

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