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

mcallegari / qlcplus / 23157810099

16 Mar 2026 05:44PM UTC coverage: 33.973% (-0.08%) from 34.05%
23157810099

push

github

mcallegari
Back to 5.2.2/4.14.5 debug

17651 of 51956 relevant lines covered (33.97%)

19863.28 hits per line

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

82.13
/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()
3,196✔
204
{
205
    return UINT_MAX;
3,196✔
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
    if (m_fadeInSpeed == ms)
33✔
626
        return;
1✔
627

628
    m_fadeInSpeed = ms;
32✔
629
    emit changed(m_id);
32✔
630
}
631

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

637
void Function::setFadeOutSpeed(uint ms)
33✔
638
{
639
    if (m_fadeOutSpeed == ms)
33✔
640
        return;
4✔
641

642
    m_fadeOutSpeed = ms;
29✔
643
    emit changed(m_id);
29✔
644
}
645

646
uint Function::fadeOutSpeed() const
167✔
647
{
648
    return m_fadeOutSpeed;
167✔
649
}
650

651
void Function::setDuration(uint ms)
131✔
652
{
653
    if (m_duration == ms)
131✔
654
        return;
1✔
655

656
    m_duration = ms;
130✔
657
    emit changed(m_id);
130✔
658
}
659

660
uint Function::duration() const
946✔
661
{
662
    return m_duration;
946✔
663
}
664

665
quint32 Function::totalDuration()
1✔
666
{
667
    // fall back to duration in case a
668
    // subclass doesn't provide this method
669
    return duration();
1✔
670
}
671

672
void Function::setTotalDuration(quint32 msec)
×
673
{
674
    Q_UNUSED(msec)
675
}
×
676

677
void Function::setOverrideFadeInSpeed(uint ms)
1✔
678
{
679
    m_overrideFadeInSpeed = ms;
1✔
680
}
1✔
681

682
uint Function::overrideFadeInSpeed() const
1,085✔
683
{
684
    return m_overrideFadeInSpeed;
1,085✔
685
}
686

687
void Function::setOverrideFadeOutSpeed(uint ms)
35✔
688
{
689
    m_overrideFadeOutSpeed = ms;
35✔
690
}
35✔
691

692
uint Function::overrideFadeOutSpeed() const
292✔
693
{
694
    return m_overrideFadeOutSpeed;
292✔
695
}
696

697
void Function::setOverrideDuration(uint ms)
1✔
698
{
699
    m_overrideDuration = ms;
1✔
700
}
1✔
701

702
uint Function::overrideDuration() const
155✔
703
{
704
    return m_overrideDuration;
155✔
705
}
706

707
QString Function::speedToString(uint ms)
60✔
708
{
709
    QString str;
60✔
710
    if (ms == infiniteSpeed())
60✔
711
    {
712
        str = QChar(0x221E); // Infinity symbol
17✔
713
    }
714
    else
715
    {
716
        uint h, m, s;
717

718
        h = ms / MS_PER_HOUR;
43✔
719
        ms -= (h * MS_PER_HOUR);
43✔
720

721
        m = ms / MS_PER_MINUTE;
43✔
722
        ms -= (m * MS_PER_MINUTE);
43✔
723

724
        s = ms / MS_PER_SECOND;
43✔
725
        ms -= (s * MS_PER_SECOND);
43✔
726

727
        if (h != 0)
43✔
728
            str += QString("%1h").arg(h, 1, 10, QChar('0'));
4✔
729
        if (m != 0)
43✔
730
            str += QString("%1m").arg(m, str.size() ? 2 : 1, 10, QChar('0'));
5✔
731
        if (s != 0)
43✔
732
            str += QString("%1s").arg(s, str.size() ? 2 : 1, 10, QChar('0'));
6✔
733
        if (ms != 0 || str.size() == 0)
43✔
734
            str += QString("%1ms").arg(ms, str.size() ? 3 : 1, 10, QChar('0'));
40✔
735
    }
736

737
    return str;
60✔
738
}
×
739

740
static uint speedSplit(QString& speedString, QString splitNeedle)
78✔
741
{
742
    QStringList splitResult;
78✔
743
    // Filter out "ms" because "m" and "s" may wrongly use it
744
    splitResult = speedString.split("ms");
78✔
745
    if (splitResult.count() > 1)
78✔
746
        splitResult = splitResult.at(0).split(splitNeedle);
12✔
747
    else
748
        splitResult = speedString.split(splitNeedle);
66✔
749

750
    if (splitResult.count() > 1)
78✔
751
    {
752
        speedString.remove(0, speedString.indexOf(splitNeedle) + 1);
13✔
753
        return splitResult.at(0).toUInt();
13✔
754
    }
755
    return 0;
65✔
756
}
78✔
757

758
uint Function::stringToSpeed(QString speed)
27✔
759
{
760
    uint value = 0;
27✔
761

762
    if (speed == QChar(0x221E)) // Infinity symbol
27✔
763
        return infiniteSpeed();
1✔
764

765
    value += speedSplit(speed, "h") * 1000 * 60 * 60;
26✔
766
    value += speedSplit(speed, "m") * 1000 * 60;
26✔
767
    value += speedSplit(speed, "s") * 1000;
26✔
768

769
    if (speed.contains("."))
26✔
770
    {
771
        // lround avoids toDouble precison issues (.03 transforms to .029)
772
        value += lround(speed.toDouble() * 1000.0);
20✔
773
    }
774
    else
775
    {
776
        if (speed.contains("ms"))
6✔
777
            speed = speed.split("ms").at(0);
3✔
778
        value += speed.toUInt();
6✔
779
    }
780

781
    return speedNormalize(value);
26✔
782
}
783

784
uint Function::speedNormalize(uint speed)
81✔
785
{
786
    if ((int)speed < 0)
81✔
787
        return infiniteSpeed();
6✔
788
    return speed;
75✔
789
}
790

791
uint Function::speedAdd(uint left, uint right)
14✔
792
{
793
    if (speedNormalize(left) == infiniteSpeed()
14✔
794
        || speedNormalize(right) == infiniteSpeed())
14✔
795
        return infiniteSpeed();
3✔
796

797
    return speedNormalize(left + right);
11✔
798
}
799

800
uint Function::speedSubtract(uint left, uint right)
7✔
801
{
802
    if (right >= left)
7✔
803
        return 0;
4✔
804
    if (speedNormalize(right) == infiniteSpeed())
3✔
805
        return 0;
×
806
    if (speedNormalize(left) == infiniteSpeed())
3✔
807
        return infiniteSpeed();
1✔
808

809
    return speedNormalize(left - right);
2✔
810
}
811

812
void Function::tap()
×
813
{
814
}
×
815

816
bool Function::loadXMLSpeed(QXmlStreamReader &speedRoot)
8✔
817
{
818
    if (speedRoot.name() != KXMLQLCFunctionSpeed)
8✔
819
        return false;
×
820

821
    QXmlStreamAttributes attrs = speedRoot.attributes();
8✔
822

823
    m_fadeInSpeed = attrs.value(KXMLQLCFunctionSpeedFadeIn).toString().toUInt();
8✔
824
    m_fadeOutSpeed = attrs.value(KXMLQLCFunctionSpeedFadeOut).toString().toUInt();
8✔
825
    m_duration = attrs.value(KXMLQLCFunctionSpeedDuration).toString().toUInt();
8✔
826

827
    speedRoot.skipCurrentElement();
8✔
828

829
    return true;
8✔
830
}
8✔
831

832
bool Function::saveXMLSpeed(QXmlStreamWriter *doc) const
10✔
833
{
834
    doc->writeStartElement(KXMLQLCFunctionSpeed);
20✔
835
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeIn, QString::number(fadeInSpeed()));
20✔
836
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeOut, QString::number(fadeOutSpeed()));
20✔
837
    doc->writeAttribute(KXMLQLCFunctionSpeedDuration, QString::number(duration()));
20✔
838
    doc->writeEndElement();
10✔
839

840
    return true;
10✔
841
}
842

843
uint Function::infiniteSpeed()
485✔
844
{
845
    return (uint) -2;
485✔
846
}
847

848
uint Function::defaultSpeed()
3,334✔
849
{
850
    return (uint) -1;
3,334✔
851
}
852

853
/*****************************************************************************
854
 * UI State
855
 *****************************************************************************/
856

857
QVariant Function::uiStateValue(QString property) const
2✔
858
{
859
    return m_uiState.value(property, QVariant());
4✔
860
}
861

862
void Function::setUiStateValue(QString property, QVariant value)
1✔
863
{
864
    m_uiState[property] = value;
1✔
865
}
1✔
866

867
QMap<QString, QVariant> Function::uiStateMap() const
20✔
868
{
869
    return m_uiState;
20✔
870
}
871

872
/*****************************************************************************
873
 * Fixtures
874
 *****************************************************************************/
875

876
void Function::slotFixtureRemoved(quint32 fid)
1✔
877
{
878
    Q_UNUSED(fid);
879
}
1✔
880

881
/*****************************************************************************
882
 * Load & Save
883
 *****************************************************************************/
884
bool Function::saveXML(QXmlStreamWriter *doc) const
×
885
{
886
    Q_UNUSED(doc)
887
    return false;
×
888
}
889

890
bool Function::loadXML(QXmlStreamReader &root)
×
891
{
892
    Q_UNUSED(root)
893
    return false;
×
894
}
895

896
bool Function::loader(QXmlStreamReader &root, Doc* doc)
12✔
897
{
898
    if (root.name() != KXMLQLCFunction)
12✔
899
    {
900
        qWarning("Function node not found!");
2✔
901
        return false;
2✔
902
    }
903

904
    QXmlStreamAttributes attrs = root.attributes();
10✔
905

906
    /* Get common information from the tag's attributes */
907
    quint32 id = attrs.value(KXMLQLCFunctionID).toString().toUInt();
10✔
908
    QString name = attrs.value(KXMLQLCFunctionName).toString();
10✔
909
    Type type = Function::stringToType(attrs.value(KXMLQLCFunctionType).toString());
10✔
910
    QString path;
10✔
911
    bool visible = true;
10✔
912
    Universe::BlendMode blendMode = Universe::NormalBlend;
10✔
913

914
    if (attrs.hasAttribute(KXMLQLCFunctionPath))
10✔
915
        path = attrs.value(KXMLQLCFunctionPath).toString();
×
916
    if (attrs.hasAttribute(KXMLQLCFunctionHidden))
10✔
917
        visible = false;
×
918
    if (attrs.hasAttribute(KXMLQLCFunctionBlendMode))
10✔
919
        blendMode = Universe::stringToBlendMode(attrs.value(KXMLQLCFunctionBlendMode).toString());
×
920

921
    /* Check for ID validity before creating the function */
922
    if (id == Function::invalidId())
10✔
923
    {
924
        qWarning() << Q_FUNC_INFO << "Function ID" << id << "is not allowed.";
×
925
        return false;
×
926
    }
927

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

953
    function->setName(name);
8✔
954
    function->setPath(path);
8✔
955
    function->setVisible(visible);
8✔
956
    function->setBlendMode(blendMode);
8✔
957
    if (function->loadXML(root) == true)
8✔
958
    {
959
        if (doc->addFunction(function, id) == true)
8✔
960
        {
961
            /* Success */
962
            return true;
8✔
963
        }
964
        else
965
        {
966
            qWarning() << "Function" << name << "cannot be created.";
×
967
            delete function;
×
968
            return false;
×
969
        }
970
    }
971
    else
972
    {
973
        qWarning() << "Function" << name << "cannot be loaded.";
×
974
        delete function;
×
975
        return false;
×
976
    }
977
}
10✔
978

979
void Function::postLoad()
×
980
{
981
    /* NOP */
982
}
×
983

984
bool Function::contains(quint32 functionId) const
16✔
985
{
986
    Q_UNUSED(functionId);
987
    return false;
16✔
988
}
989

990
QList<quint32> Function::components() const
×
991
{
992
    return QList<quint32>();
×
993
}
994

995
/*****************************************************************************
996
 * Flash
997
 *****************************************************************************/
998

999
void Function::flash(MasterTimer *timer, bool shouldOverride, bool forceLTP)
7✔
1000
{
1001
    Q_UNUSED(timer);
1002
    Q_UNUSED(shouldOverride);
1003
    Q_UNUSED(forceLTP);
1004

1005
    if (m_flashing == false)
7✔
1006
    {
1007
        emit flashing(m_id, true);
6✔
1008
    }
1009

1010
    m_flashing = true;
7✔
1011
}
7✔
1012

1013
void Function::unFlash(MasterTimer *timer)
6✔
1014
{
1015
    Q_UNUSED(timer);
1016

1017
    if (m_flashing == true)
6✔
1018
        emit flashing(m_id, false);
6✔
1019

1020
    m_flashing = false;
6✔
1021
}
6✔
1022

1023
bool Function::flashing() const
23✔
1024
{
1025
    return m_flashing;
23✔
1026
}
1027

1028
/*****************************************************************************
1029
 * Running
1030
 *****************************************************************************/
1031

1032
void Function::preRun(MasterTimer *timer)
169✔
1033
{
1034
    Q_UNUSED(timer);
1035

1036
    qDebug() << "Function preRun. Name:" << m_name << "ID:" << m_id << "type:" << typeToString(type());
169✔
1037
    m_running = true;
169✔
1038

1039
    emit running(m_id);
169✔
1040
}
169✔
1041

1042
void Function::write(MasterTimer *timer, QList<Universe *> universes)
×
1043
{
1044
    Q_UNUSED(timer);
1045
    Q_UNUSED(universes);
1046
}
×
1047

1048
void Function::postRun(MasterTimer *timer, QList<Universe *> universes)
139✔
1049
{
1050
    Q_UNUSED(timer);
1051
    Q_UNUSED(universes);
1052

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

1055
    {
1056
        QMutexLocker locker(&m_stopMutex);
139✔
1057

1058
        resetElapsed();
139✔
1059
        if (m_preserveAttributes == false)
139✔
1060
            resetAttributes();
139✔
1061

1062
        m_functionStopped.wakeAll();
139✔
1063
    }
139✔
1064

1065
    m_paused = false;
139✔
1066
    m_running = false;
139✔
1067
    emit stopped(m_id);
139✔
1068
}
139✔
1069

1070
void Function::dismissAllFaders()
112✔
1071
{
1072
    QMapIterator <quint32, QSharedPointer<GenericFader> > it(m_fadersMap);
112✔
1073
    while (it.hasNext() == true)
213✔
1074
    {
1075
        it.next();
101✔
1076
        QSharedPointer<GenericFader> fader = it.value();
101✔
1077
        if (!fader.isNull())
101✔
1078
            fader->requestDelete();
101✔
1079
    }
101✔
1080

1081
    m_fadersMap.clear();
112✔
1082
}
112✔
1083

1084
bool Function::isRunning() const
18,578✔
1085
{
1086
    return m_running;
18,578✔
1087
}
1088

1089
bool Function::isPaused() const
546✔
1090
{
1091
    return m_paused;
546✔
1092
}
1093

1094
/*****************************************************************************
1095
 * Elapsed ticks while running
1096
 *****************************************************************************/
1097

1098
quint32 Function::elapsed() const
20✔
1099
{
1100
    return m_elapsed;
20✔
1101
}
1102

1103
quint32 Function::elapsedBeats() const
1✔
1104
{
1105
    return m_elapsedBeats;
1✔
1106
}
1107

1108
void Function::resetElapsed()
140✔
1109
{
1110
    qDebug() << Q_FUNC_INFO;
140✔
1111
    m_elapsed = 0;
140✔
1112
    m_elapsedBeats = 0;
140✔
1113
}
140✔
1114

1115
void Function::incrementElapsed()
644✔
1116
{
1117
    // Don't wrap around. UINT_MAX is the maximum fade/hold time.
1118
    if (m_elapsed < UINT_MAX - MasterTimer::tick())
644✔
1119
        m_elapsed += MasterTimer::tick();
642✔
1120
    else
1121
        m_elapsed = UINT_MAX;
2✔
1122
}
644✔
1123

1124
void Function::incrementElapsedBeats()
×
1125
{
1126
    m_elapsedBeats += 1000;
×
1127
}
×
1128

1129
void Function::roundElapsed(quint32 roundTime)
×
1130
{
1131
    if (roundTime == 0)
×
1132
        m_elapsed = 0;
×
1133
    else
1134
        m_elapsed %= roundTime;
×
1135
}
×
1136

1137
/*****************************************************************************
1138
 * Start & Stop
1139
 *****************************************************************************/
1140

1141
void Function::start(MasterTimer* timer, FunctionParent source, quint32 startTime,
154✔
1142
                     uint overrideFadeIn, uint overrideFadeOut, uint overrideDuration, TempoType overrideTempoType)
1143
{
1144
    qDebug() << "Function start(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id() << ", startTime:" << startTime;
154✔
1145

1146
    Q_ASSERT(timer != NULL);
154✔
1147

1148
    {
1149
        QMutexLocker sourcesLocker(&m_sourcesMutex);
154✔
1150
        if (m_sources.contains(source))
154✔
1151
            return;
×
1152
        m_sources.append(source);
154✔
1153
        if (m_sources.size() > 1)
154✔
1154
            return;
×
1155
    }
154✔
1156

1157
    /** If we're in a paused state, then just return to the running state
1158
     *  to let subclasses resuming what they were doing. */
1159
    if (m_paused == true)
154✔
1160
    {
1161
        m_paused = false;
×
1162
        return;
×
1163
    }
1164

1165
    m_elapsed = startTime;
154✔
1166
    m_elapsedBeats = 0;
154✔
1167
    m_overrideFadeInSpeed = overrideFadeIn;
154✔
1168
    m_overrideFadeOutSpeed = overrideFadeOut;
154✔
1169
    m_overrideDuration = overrideDuration;
154✔
1170
    m_overrideTempoType = overrideTempoType == Original ? tempoType() : overrideTempoType;
154✔
1171

1172
    m_stop = false;
154✔
1173
    timer->startFunction(this);
154✔
1174
}
1175

1176
void Function::setPause(bool enable)
10✔
1177
{
1178
    if (enable && isRunning() == false)
10✔
1179
        return;
×
1180

1181
    m_paused = enable;
10✔
1182
}
1183

1184
void Function::stop(FunctionParent source, bool preserveAttributes)
152✔
1185
{
1186
    qDebug() << "Function stop(). Name:" << m_name << "ID: " << m_id << "source:" << source.type() << source.id();
152✔
1187

1188
    QMutexLocker sourcesLocker(&m_sourcesMutex);
152✔
1189

1190
    if ((source.id() == id() && source.type() == FunctionParent::Function) ||
313✔
1191
        (source.type() == FunctionParent::Master) ||
313✔
1192
        (source.type() == FunctionParent::ManualVCWidget))
120✔
1193
    {
1194
        m_sources.clear();
35✔
1195
    }
1196
    else
1197
    {
1198
        m_sources.removeAll(source);
117✔
1199
    }
1200

1201
    if (m_sources.size() == 0)
152✔
1202
    {
1203
        m_stop = true;
152✔
1204
        m_preserveAttributes = preserveAttributes;
152✔
1205
    }
1206
}
152✔
1207

1208
bool Function::stopped() const
770✔
1209
{
1210
    return m_stop;
770✔
1211
}
1212

1213
bool Function::startedAsChild() const
×
1214
{
1215
    QMutexLocker sourcesLocker(const_cast<QMutex*>(&m_sourcesMutex));
×
1216
    foreach (FunctionParent source, m_sources)
×
1217
    {
1218
        if (source.type() == FunctionParent::Function && source.id() != id())
×
1219
            return true;
×
1220
    }
×
1221
    return false;
×
1222
}
×
1223

1224
int Function::invalidAttributeId()
730✔
1225
{
1226
    return -1;
730✔
1227
}
1228

1229
bool Function::stopAndWait()
1✔
1230
{
1231
    QMutexLocker locker(&m_stopMutex);
1✔
1232

1233
    stop(FunctionParent::master());
1✔
1234

1235
    QElapsedTimer watchdog;
1✔
1236
    watchdog.restart();
1✔
1237

1238
    // block thread for maximum 2 seconds
1239
    while (m_running == true)
21✔
1240
    {
1241
        if (watchdog.elapsed() > 2000)
21✔
1242
            return false;
1✔
1243

1244
        // wait until the function has stopped
1245
        m_functionStopped.wait(&m_stopMutex, 100);
20✔
1246
    }
1247

1248
    return true;
×
1249
}
1✔
1250

1251
/*****************************************************************************
1252
 * Attributes
1253
 *****************************************************************************/
1254

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

1280
    return m_attributes.count() - 1;
1,154✔
1281
}
1,154✔
1282

1283
int Function::requestAttributeOverride(int attributeIndex, qreal value)
247✔
1284
{
1285
    if (attributeIndex < 0 || attributeIndex >= m_attributes.count())
247✔
1286
        return -1;
×
1287

1288
    int attributeID = invalidAttributeId();
247✔
1289

1290
    if (m_attributes.at(attributeIndex).m_flags & Single)
247✔
1291
    {
1292
        QMap <int, AttributeOverride>::iterator it = m_overrideMap.begin();
246✔
1293
        for (; it != m_overrideMap.end(); it++)
361✔
1294
        {
1295
            if (it.value().m_attrIndex == attributeIndex)
120✔
1296
            {
1297
                attributeID = it.key();
5✔
1298
                break;
5✔
1299
            }
1300
        }
1301
    }
1302

1303
    if (attributeID == invalidAttributeId())
247✔
1304
    {
1305
        AttributeOverride override;
1306
        override.m_attrIndex = attributeIndex;
242✔
1307
        override.m_value = 0.0;
242✔
1308

1309
        attributeID = m_lastOverrideAttributeId;
242✔
1310
        m_overrideMap[attributeID] = override;
242✔
1311

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

1314
        calculateOverrideValue(attributeIndex);
242✔
1315

1316
        m_lastOverrideAttributeId++;
242✔
1317
    }
1318
    else
1319
    {
1320
        qDebug() << name() << "Override requested for existing attribute" << attributeIndex << "value" << value << "single ID" << attributeID;
5✔
1321
    }
1322

1323
    // actually apply the new override value
1324
    adjustAttribute(value, attributeID);
247✔
1325

1326
    return attributeID;
247✔
1327
}
1328

1329
void Function::releaseAttributeOverride(int attributeId)
1✔
1330
{
1331
    if (m_overrideMap.contains(attributeId) == false)
1✔
1332
        return;
×
1333

1334
    int attributeIndex = m_overrideMap[attributeId].m_attrIndex;
1✔
1335

1336
    m_overrideMap.remove(attributeId);
1✔
1337

1338
    calculateOverrideValue(attributeIndex);
1✔
1339

1340
    qDebug() << name() << "Attribute override released" << attributeId;
1✔
1341
}
1342

1343
bool Function::unregisterAttribute(QString name)
12✔
1344
{
1345
    for (int i = 0; i < m_attributes.count(); i++)
13✔
1346
    {
1347
        if (m_attributes[i].m_name == name)
13✔
1348
        {
1349
            m_attributes.removeAt(i);
12✔
1350
            return true;
12✔
1351
        }
1352
    }
1353
    return false;
×
1354
}
1355

1356
bool Function::renameAttribute(int idx, QString newName)
2✔
1357
{
1358
    if (idx < 0 || idx >= m_attributes.count())
2✔
1359
        return false;
1✔
1360
    m_attributes[idx].m_name = newName;
1✔
1361

1362
    return true;
1✔
1363
}
1364

1365
int Function::adjustAttribute(qreal value, int attributeId)
431✔
1366
{
1367
    if (attributeId < 0)
431✔
1368
        return -1;
×
1369

1370
    int attrIndex;
1371

1372
    //qDebug() << name() << "Attribute ID:" << attributeId << ", val:" << value;
1373

1374
    if (attributeId < OVERRIDE_ATTRIBUTE_START_ID)
431✔
1375
    {
1376
        if (attributeId >= m_attributes.count() || m_attributes[attributeId].m_value == value)
179✔
1377
            return -1;
60✔
1378

1379
        // Adjust the original value of an attribute. Only Function editors should do this!
1380
        m_attributes[attributeId].m_value = CLAMP(value, m_attributes[attributeId].m_min, m_attributes[attributeId].m_max);
119✔
1381
        attrIndex = attributeId;
119✔
1382
    }
1383
    else
1384
    {
1385
        if (m_overrideMap.contains(attributeId) == false)
252✔
1386
            return -1;
×
1387

1388
        // Adjust an attribute override value and recalculate the final overridden value
1389
        m_overrideMap[attributeId].m_value = value;
252✔
1390
        attrIndex = m_overrideMap[attributeId].m_attrIndex;
252✔
1391
        calculateOverrideValue(attrIndex);
252✔
1392
    }
1393

1394
    emit attributeChanged(attrIndex, m_attributes[attrIndex].m_isOverridden ?
742✔
1395
                                     m_attributes[attrIndex].m_overrideValue :
252✔
1396
                                     m_attributes[attrIndex].m_value);
119✔
1397

1398
    return attrIndex;
371✔
1399
}
1400

1401
void Function::resetAttributes()
141✔
1402
{
1403
    for (int i = 0; i < m_attributes.count(); i++)
421✔
1404
    {
1405
        m_attributes[i].m_isOverridden = false;
280✔
1406
        m_attributes[i].m_overrideValue = 0.0;
280✔
1407
    }
1408
    m_overrideMap.clear();
141✔
1409
    m_lastOverrideAttributeId = OVERRIDE_ATTRIBUTE_START_ID;
141✔
1410
}
141✔
1411

1412
qreal Function::getAttributeValue(int attributeIndex) const
90,939✔
1413
{
1414
    if (attributeIndex >= m_attributes.count())
90,939✔
1415
        return 0.0;
75✔
1416

1417
    return m_attributes[attributeIndex].m_isOverridden ?
181,728✔
1418
                m_attributes[attributeIndex].m_overrideValue :
252✔
1419
                m_attributes[attributeIndex].m_value;
181,476✔
1420
}
1421

1422
int Function::getAttributeIndex(QString name) const
3✔
1423
{
1424
    for (int i = 0; i < m_attributes.count(); i++)
7✔
1425
    {
1426
        Attribute attr = m_attributes.at(i);
6✔
1427
        if (attr.m_name == name)
6✔
1428
            return i;
2✔
1429
    }
6✔
1430
    return -1;
1✔
1431
}
1432

1433
QList<Attribute> Function::attributes() const
121✔
1434
{
1435
    return m_attributes;
121✔
1436
}
1437

1438
void Function::calculateOverrideValue(int attributeIndex)
495✔
1439
{
1440
    if (attributeIndex >= m_attributes.count())
495✔
1441
        return;
×
1442

1443
    qreal finalValue = 0.0;
495✔
1444
    bool found = false;
495✔
1445
    Attribute origAttr = m_attributes.at(attributeIndex);
495✔
1446

1447
    if (origAttr.m_flags & Multiply)
495✔
1448
        finalValue = origAttr.m_value;
493✔
1449

1450
    foreach (AttributeOverride attr, m_overrideMap)
1,221✔
1451
    {
1452
        if (attr.m_attrIndex != attributeIndex)
726✔
1453
            continue;
232✔
1454

1455
        found = true;
494✔
1456

1457
        if (origAttr.m_flags & Multiply)
494✔
1458
            finalValue = finalValue * attr.m_value;
492✔
1459
        else if (origAttr.m_flags & LastWins)
2✔
1460
            finalValue = attr.m_value;
2✔
1461
    }
495✔
1462

1463
    m_attributes[attributeIndex].m_overrideValue = finalValue;
495✔
1464
    m_attributes[attributeIndex].m_isOverridden = found;
495✔
1465
}
495✔
1466

1467
/*************************************************************************
1468
 * Blend mode
1469
 *************************************************************************/
1470

1471
void Function::setBlendMode(Universe::BlendMode mode)
5✔
1472
{
1473
    m_blendMode = mode;
5✔
1474
}
5✔
1475

1476
Universe::BlendMode Function::blendMode() const
168✔
1477
{
1478
    return m_blendMode;
168✔
1479
}
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