• 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

94.93
/engine/src/collection.cpp
1
/*
2
  Q Light Controller Plus
3
  collection.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 <QString>
22
#include <QDebug>
23
#include <QFile>
24
#include <QList>
25
#include <QMutexLocker>
26
#include <QXmlStreamReader>
27
#include <QXmlStreamWriter>
28

29
#include "mastertimer.h"
30
#include "collection.h"
31
#include "function.h"
32
#include "doc.h"
33

34
/*****************************************************************************
35
 * Initialization
36
 *****************************************************************************/
37

38
Collection::Collection(Doc* doc)
28✔
39
    : Function(doc, Function::CollectionType)
28✔
40
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
41
    , m_functionListMutex(QMutex::Recursive)
42
#endif
43
{
44
    setName(tr("New Collection"));
28✔
45

46
    // Listen to member Function removals
47
    connect(doc, SIGNAL(functionRemoved(quint32)),
28✔
48
            this, SLOT(slotFunctionRemoved(quint32)));
49
}
28✔
50

51
Collection::~Collection()
43✔
52
{
53
}
43✔
54

55
QIcon Collection::getIcon() const
×
56
{
57
    return QIcon(":/collection.png");
×
58
}
59

60
quint32 Collection::totalDuration()
1✔
61
{
62
    quint32 totalDuration = 0;
1✔
63

64
    foreach (QVariant fid, functions())
4✔
65
    {
66
        Function* function = doc()->function(fid.toUInt());
2✔
67
        if (function == nullptr)
2✔
68
            continue;
×
69
        totalDuration += function->totalDuration();
2✔
70
    }
3✔
71

72
    return totalDuration;
1✔
73
}
74

75
/*****************************************************************************
76
 * Copying
77
 *****************************************************************************/
78

79
Function* Collection::createCopy(Doc* doc, bool addToDoc)
1✔
80
{
81
    Q_ASSERT(doc != NULL);
1✔
82

83
    Function* copy = new Collection(doc);
1✔
84
    if (copy->copyFrom(this) == false)
1✔
85
    {
86
        delete copy;
×
87
        copy = NULL;
×
88
    }
89
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
90
    {
91
        delete copy;
×
92
        copy = NULL;
×
93
    }
94

95
    return copy;
1✔
96
}
97

98
bool Collection::copyFrom(const Function* function)
4✔
99
{
100
    const Collection* coll = qobject_cast<const Collection*> (function);
4✔
101
    if (coll == NULL)
4✔
102
        return false;
1✔
103

104
    m_functions.clear();
3✔
105
    m_functions = coll->m_functions;
3✔
106

107
    return Function::copyFrom(function);
3✔
108
}
109

110
/*****************************************************************************
111
 * Contents
112
 *****************************************************************************/
113

114
bool Collection::addFunction(quint32 fid, int insertIndex)
49✔
115
{
116
    if (fid != this->id() && m_functions.contains(fid) == false)
49✔
117
    {
118
        {
119
            QMutexLocker locker(&m_functionListMutex);
47✔
120
            if (insertIndex == -1)
47✔
121
                m_functions.append(fid);
47✔
122
            else
123
                m_functions.insert(insertIndex, fid);
×
124
        }
47✔
125

126
        emit changed(this->id());
47✔
127
        emit functionsChanged();
47✔
128
        return true;
47✔
129
    }
130
    else
131
    {
132
        return false;
2✔
133
    }
134
}
135

136
bool Collection::removeFunction(quint32 fid)
51✔
137
{
138
    int num = 0;
51✔
139
    {
140
        QMutexLocker locker(&m_functionListMutex);
51✔
141
        num = m_functions.removeAll(fid);
51✔
142
    }
51✔
143

144
    if (num > 0)
51✔
145
    {
146
        emit changed(this->id());
10✔
147
        emit functionsChanged();
10✔
148
        return true;
10✔
149
    }
150
    else
151
    {
152
        return false;
41✔
153
    }
154
}
155

156
QList<quint32> Collection::functions() const
60✔
157
{
158
    QMutexLocker locker(&m_functionListMutex);
60✔
159
    return m_functions;
60✔
160
}
60✔
161

162
void Collection::slotFunctionRemoved(quint32 fid)
48✔
163
{
164
    removeFunction(fid);
48✔
165
}
48✔
166

167
/*****************************************************************************
168
 * Load & Save
169
 *****************************************************************************/
170

171
bool Collection::saveXML(QXmlStreamWriter *doc) const
2✔
172
{
173
    int i = 0;
2✔
174

175
    Q_ASSERT(doc != NULL);
2✔
176

177
    /* Function tag */
178
    doc->writeStartElement(KXMLQLCFunction);
4✔
179

180
    /* Common attributes */
181
    saveXMLCommon(doc);
2✔
182

183
    /* Steps */
184
    QListIterator <quint32> it(m_functions);
2✔
185
    while (it.hasNext() == true)
6✔
186
    {
187
        /* Step tag */
188
        doc->writeStartElement(KXMLQLCFunctionStep);
8✔
189

190
        /* Step number */
191
        doc->writeAttribute(KXMLQLCFunctionNumber, QString::number(i++));
8✔
192

193
        /* Step Function ID */
194
        doc->writeCharacters(QString::number(it.next()));
4✔
195
        doc->writeEndElement();
4✔
196
    }
197

198
    /* End the <Function> tag */
199
    doc->writeEndElement();
2✔
200

201
    return true;
2✔
202
}
2✔
203

204
bool Collection::loadXML(QXmlStreamReader &root)
10✔
205
{
206
    if (root.name() != KXMLQLCFunction)
10✔
207
    {
208
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
209
        return false;
1✔
210
    }
211

212
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::CollectionType))
18✔
213
    {
214
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
3✔
215
                   << "is not a collection";
1✔
216
        return false;
1✔
217
    }
218

219
    /* Load collection contents */
220
    while (root.readNextStartElement())
28✔
221
    {
222
        if (root.name() == KXMLQLCFunctionStep)
20✔
223
            addFunction(root.readElementText().toUInt());
18✔
224
        else
225
        {
226
            qWarning() << Q_FUNC_INFO << "Unknown collection tag:" << root.name();
2✔
227
            root.skipCurrentElement();
2✔
228
        }
229
    }
230

231
    return true;
8✔
232
}
233

234
void Collection::postLoad()
5✔
235
{
236
    Doc* doc = qobject_cast <Doc*> (parent());
5✔
237
    Q_ASSERT(doc != NULL);
5✔
238

239
    /* Check that all member functions exist (nonexistent functions can
240
       be present only when a corrupted file has been loaded) */
241
    QMutableListIterator<quint32> it(m_functions);
5✔
242
    while (it.hasNext() == true)
18✔
243
    {
244
        /* Remove any nonexistent member functions */
245
        Function* function = doc->function(it.next());
13✔
246

247
        if (function == NULL || function->contains(id())) // forbid self-containment
13✔
248
            it.remove();
13✔
249
    }
250
}
5✔
251

252
bool Collection::contains(quint32 functionId) const
7✔
253
{
254
    Doc* doc = qobject_cast <Doc*> (parent());
7✔
255
    Q_ASSERT(doc != NULL);
7✔
256

257
    foreach (quint32 fid, m_functions)
15✔
258
    {
259
        Function* function = doc->function(fid);
12✔
260
        // contains() can be called during init, function may be NULL
261
        if (function == NULL)
12✔
262
            continue;
×
263

264
        if (function->id() == functionId)
12✔
265
            return true;
3✔
266
        if (function->contains(functionId))
9✔
267
            return true;
1✔
268
    }
7✔
269

270
    return false;
3✔
271
}
272

273
QList<quint32> Collection::components() const
1✔
274
{
275
    return m_functions;
1✔
276
}
277

278
/*****************************************************************************
279
 * Running
280
 *****************************************************************************/
281

282
FunctionParent Collection::functionParent() const
6✔
283
{
284
    return FunctionParent(FunctionParent::Function, id());
6✔
285
}
286

287
void Collection::preRun(MasterTimer *timer)
2✔
288
{
289
    Doc *doc = this->doc();
2✔
290
    Q_ASSERT(doc != NULL);
2✔
291
    {
292
        QMutexLocker locker(&m_functionListMutex);
2✔
293
        m_runningChildren.clear();
2✔
294
        foreach (quint32 fid, m_functions)
6✔
295
        {
296
            Function *function = doc->function(fid);
4✔
297
            Q_ASSERT(function != NULL);
4✔
298

299
            m_intensityOverrideIds << function->requestAttributeOverride(Function::Intensity, getAttributeValue(Function::Intensity));
4✔
300

301
            // Append the IDs of all functions started by this collection
302
            // to a set so that we can track which of them are still controlled
303
            // by this collection which are not.
304
            m_runningChildren << function->id();
4✔
305

306
            // Listen to the children's stopped signals so that this Collection
307
            // can give up its rights to stop the function later.
308
            connect(function, SIGNAL(stopped(quint32)),
4✔
309
                    this, SLOT(slotChildStopped(quint32)));
310

311
            // Listen to the children's stopped signals so that this collection
312
            // can give up its rights to stop the function later.
313
            connect(function, SIGNAL(running(quint32)),
4✔
314
                    this, SLOT(slotChildStarted(quint32)));
315

316
            //function->adjustAttribute(getAttributeValue(Function::Intensity), Function::Intensity);
317
            function->start(timer, functionParent(), 0, overrideFadeInSpeed(), overrideFadeOutSpeed(), overrideDuration());
4✔
318
        }
2✔
319
        m_tick = 1;
2✔
320
    }
2✔
321
    Function::preRun(timer);
2✔
322
}
2✔
323

324
void Collection::setPause(bool enable)
2✔
325
{
326
    Doc *doc = this->doc();
2✔
327
    Q_ASSERT(doc != NULL);
2✔
328
    foreach (quint32 fid, m_runningChildren)
6✔
329
    {
330
        Function *function = doc->function(fid);
4✔
331
        Q_ASSERT(function != NULL);
4✔
332
        function->setPause(enable);
4✔
333
    }
2✔
334
    Function::setPause(enable);
2✔
335
}
2✔
336

337
void Collection::write(MasterTimer *timer, QList<Universe *> universes)
8✔
338
{
339
    Q_UNUSED(timer);
340
    Q_UNUSED(universes);
341

342
    if (isPaused())
8✔
343
        return;
×
344

345
    // During first tick, children may be stopped & started.
346
    if (m_tick == 1)
8✔
347
        m_tick = 2;
2✔
348
    else if (m_tick == 2)
6✔
349
    {
350
        m_tick = 0;
2✔
351
        Doc *doc = this->doc();
2✔
352
        Q_ASSERT(doc != NULL);
2✔
353

354
        QMutexLocker locker(&m_functionListMutex);
2✔
355
        foreach (quint32 fid, m_runningChildren)
6✔
356
        {
357
            Function *function = doc->function(fid);
4✔
358
            Q_ASSERT(function != NULL);
4✔
359

360
            // First tick may correspond to this collection starting the function
361
            // Now that first tick is over, stop listening to running signal
362
            disconnect(function, SIGNAL(running(quint32)),
4✔
363
                    this, SLOT(slotChildStarted(quint32)));
364
        }
2✔
365
    }
2✔
366

367
    incrementElapsed();
8✔
368

369
    {
370
        QMutexLocker locker(&m_functionListMutex);
8✔
371
        if (m_runningChildren.size() > 0)
8✔
372
            return;
7✔
373
    }
8✔
374

375
    stop(functionParent());
1✔
376
}
377

378
void Collection::postRun(MasterTimer* timer, QList<Universe *> universes)
1✔
379
{
380
    Doc* doc = qobject_cast <Doc*> (parent());
1✔
381
    Q_ASSERT(doc != NULL);
1✔
382

383
    {
384
        QMutexLocker locker(&m_functionListMutex);
1✔
385
        /** Stop the member functions only if they have been started by this
386
            collection. */
387
        QSetIterator <quint32> it(m_runningChildren);
1✔
388
        while (it.hasNext() == true)
2✔
389
        {
390
            Function* function = doc->function(it.next());
1✔
391
            Q_ASSERT(function != NULL);
1✔
392
            function->stop(functionParent());
1✔
393
        }
394

395
        m_runningChildren.clear();
1✔
396

397
        for (int i = 0; i < m_functions.count(); i++)
3✔
398
        {
399
            Function* function = doc->function(m_functions.at(i));
2✔
400
            Q_ASSERT(function != NULL);
2✔
401

402
            disconnect(function, SIGNAL(stopped(quint32)),
2✔
403
                    this, SLOT(slotChildStopped(quint32)));
404
            if (m_tick == 2)
2✔
405
            {
406
                disconnect(function, SIGNAL(running(quint32)),
×
407
                        this, SLOT(slotChildStarted(quint32)));
408
            }
409
        }
410

411
        m_intensityOverrideIds.clear();
1✔
412
    }
1✔
413

414
    Function::postRun(timer, universes);
1✔
415
}
1✔
416

417
void Collection::slotChildStopped(quint32 fid)
3✔
418
{
419
    QMutexLocker locker(&m_functionListMutex);
3✔
420
    m_runningChildren.remove(fid);
3✔
421
}
3✔
422

423
void Collection::slotChildStarted(quint32 fid)
4✔
424
{
425
    QMutexLocker locker(&m_functionListMutex);
4✔
426
    m_runningChildren << fid;
4✔
427
}
4✔
428

429
int Collection::adjustAttribute(qreal fraction, int attributeId)
1✔
430
{
431
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
1✔
432

433
    if (isRunning() && attrIndex == Intensity)
1✔
434
    {
435
        Doc* document = doc();
1✔
436
        Q_ASSERT(document != NULL);
1✔
437

438
        QMutexLocker locker(&m_functionListMutex);
1✔
439

440
        for (int i = 0; i < m_functions.count(); i++)
3✔
441
        {
442
            Function* function = document->function(m_functions.at(i));
2✔
443
            Q_ASSERT(function != NULL);
2✔
444
            function->adjustAttribute(getAttributeValue(Function::Intensity), m_intensityOverrideIds.at(i));
2✔
445
        }
446
    }
1✔
447

448
    return attrIndex;
1✔
449
}
450

451
void Collection::setBlendMode(Universe::BlendMode mode)
6✔
452
{
453
    if (mode == blendMode())
6✔
454
        return;
5✔
455

456
    qDebug() << "Collection" << name() << "blend mode set to" << Universe::blendModeToString(mode);
1✔
457

458
    if (isRunning())
1✔
459
    {
460
        for (int i = 0; i < m_functions.count(); i++)
3✔
461
        {
462
            Function* function = doc()->function(m_functions.at(i));
2✔
463
            Q_ASSERT(function != NULL);
2✔
464
            function->setBlendMode(mode);
2✔
465
        }
466
    }
467

468
    Function::setBlendMode(mode);
1✔
469
}
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