• 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

68.98
/engine/src/show.cpp
1
/*
2
  Q Light Controller Plus
3
  show.cpp
4

5
  Copyright (c) Massimo Callegari
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QString>
23
#include <QDebug>
24
#include <QFile>
25
#include <QList>
26

27
#include "showrunner.h"
28
#include "function.h"
29
#include "show.h"
30
#include "doc.h"
31

32
#define KXMLQLCShowTimeDivision QStringLiteral("TimeDivision")
33
#define KXMLQLCShowTimeType     QStringLiteral("Type")
34
#define KXMLQLCShowTimeBPM      QStringLiteral("BPM")
35

36
/*****************************************************************************
37
 * Initialization
38
 *****************************************************************************/
39

40
Show::Show(Doc* doc) : Function(doc, Function::ShowType)
9✔
41
    , m_timeDivisionType(Time)
9✔
42
    , m_timeDivisionBPM(120)
9✔
43
    , m_latestTrackId(0)
9✔
44
    , m_latestShowFunctionID(0)
9✔
45
    , m_runner(NULL)
9✔
46
{
47
    setName(tr("New Show"));
9✔
48

49
    // Clear attributes here. I want attributes to be mapped
50
    // exactly like the Show tracks
51
    unregisterAttribute(tr("Intensity"));
9✔
52
}
9✔
53

54
Show::~Show()
10✔
55
{
56
    m_tracks.clear();
9✔
57
}
10✔
58

59
QIcon Show::getIcon() const
×
60
{
61
    return QIcon(":/show.png");
×
62
}
63

64
quint32 Show::totalDuration()
2✔
65
{
66
    quint32 totalDuration = 0;
2✔
67

68
    foreach (Track *track, m_tracks)
4✔
69
    {
70
        foreach (ShowFunction *sf, track->showFunctions())
6✔
71
        {
72
            if (sf->startTime() + sf->duration(doc()) > totalDuration)
2✔
73
                totalDuration = sf->startTime() + sf->duration(doc());
2✔
74
        }
2✔
75
    }
2✔
76

77
    return totalDuration;
2✔
78
}
79

80
/*****************************************************************************
81
 * Copying
82
 *****************************************************************************/
83

84
Function* Show::createCopy(Doc* doc, bool addToDoc)
×
85
{
86
    Q_ASSERT(doc != NULL);
×
87

88
    Function* copy = new Show(doc);
×
89
    if (copy->copyFrom(this) == false)
×
90
    {
91
        delete copy;
×
92
        copy = NULL;
×
93
    }
94
    if (addToDoc == true && doc->addFunction(copy) == false)
×
95
    {
96
        delete copy;
×
97
        copy = NULL;
×
98
    }
99

100
    return copy;
×
101
}
102

103
bool Show::copyFrom(const Function* function)
1✔
104
{
105
    const Show* show = qobject_cast<const Show*> (function);
1✔
106
    if (show == NULL)
1✔
107
        return false;
×
108

109
    m_timeDivisionType = show->m_timeDivisionType;
1✔
110
    m_timeDivisionBPM = show->m_timeDivisionBPM;
1✔
111
    m_latestTrackId = show->m_latestTrackId;
1✔
112
    m_latestShowFunctionID = show->m_latestShowFunctionID;
1✔
113

114
    // create a copy of each track
115
    foreach (Track *track, show->tracks())
3✔
116
    {
117
        quint32 sceneID = track->getSceneID();
1✔
118
        Track* newTrack = new Track(sceneID, this);
1✔
119
        newTrack->setName(track->name());
1✔
120
        addTrack(newTrack);
1✔
121

122
        // create a copy of each sequence/audio in a track
123
        foreach (ShowFunction *sfunc, track->showFunctions())
3✔
124
        {
125
            Function* function = doc()->function(sfunc->functionID());
1✔
126
            if (function == NULL)
1✔
127
                continue;
×
128

129
            /* Attempt to create a copy of the function to Doc */
130
            Function* copy = function->createCopy(doc());
1✔
131
            if (copy != NULL)
1✔
132
            {
133
                copy->setName(tr("Copy of %1").arg(function->name()));
1✔
134
                ShowFunction *showFunc = newTrack->createShowFunction(copy->id());
1✔
135
                showFunc->setStartTime(sfunc->startTime());
1✔
136
                showFunc->setDuration(sfunc->duration());
1✔
137
                showFunc->setColor(sfunc->color());
1✔
138
                showFunc->setLocked(sfunc->isLocked());
1✔
139
            }
140
        }
1✔
141
    }
1✔
142

143
    return Function::copyFrom(function);
1✔
144
}
145

146
/*********************************************************************
147
 * Time division
148
 *********************************************************************/
149

150
void Show::setTimeDivision(Show::TimeDivision type, int BPM)
4✔
151
{
152
    qDebug() << "[setTimeDivision] type:" << type << ", BPM:" << BPM;
4✔
153
    m_timeDivisionType = type;
4✔
154
    m_timeDivisionBPM = BPM;
4✔
155
}
4✔
156

157
Show::TimeDivision Show::timeDivisionType() const
4✔
158
{
159
    return m_timeDivisionType;
4✔
160
}
161

162
int Show::beatsDivision() const
4✔
163
{
164
    switch(m_timeDivisionType)
4✔
165
    {
166
        case BPM_2_4: return 2;
1✔
167
        case BPM_3_4: return 3;
1✔
168
        case BPM_4_4: return 4;
1✔
169
        default: return 0;
1✔
170
    }
171
}
172

173
void Show::setTimeDivisionType(TimeDivision type)
3✔
174
{
175
    m_timeDivisionType = type;
3✔
176
}
3✔
177

178
int Show::timeDivisionBPM() const
4✔
179
{
180
    return m_timeDivisionBPM;
4✔
181
}
182

183
void Show::setTimeDivisionBPM(int BPM)
×
184
{
185
    m_timeDivisionBPM = BPM;
×
186
}
×
187

188
QString Show::tempoToString(Show::TimeDivision type)
5✔
189
{
190
    switch(type)
5✔
191
    {
192
        case Time: return QString("Time"); break;
1✔
193
        case BPM_4_4: return QString("BPM_4_4"); break;
1✔
194
        case BPM_3_4: return QString("BPM_3_4"); break;
2✔
195
        case BPM_2_4: return QString("BPM_2_4"); break;
1✔
196
        case Invalid:
×
197
        default:
198
            return QString("Invalid"); break;
×
199
    }
200
    return QString();
201
}
202

203
Show::TimeDivision Show::stringToTempo(const QString& tempo)
5✔
204
{
205
    if (tempo == "Time")
5✔
206
        return Time;
1✔
207
    else if (tempo == "BPM_4_4")
4✔
208
        return BPM_4_4;
1✔
209
    else if (tempo == "BPM_3_4")
3✔
210
        return BPM_3_4;
1✔
211
    else if (tempo == "BPM_2_4")
2✔
212
        return BPM_2_4;
2✔
213
    else
214
        return Invalid;
×
215
}
216

217
/*****************************************************************************
218
 * Tracks
219
 *****************************************************************************/
220

221
bool Show::addTrack(Track *track, quint32 id)
10✔
222
{
223
    Q_ASSERT(track != NULL);
10✔
224

225
    // No ID given, this method can assign one
226
    if (id == Track::invalidId())
10✔
227
        id = createTrackId();
8✔
228

229
     track->setId(id);
10✔
230
     track->setShowId(this->id());
10✔
231
     m_tracks[id] = track;
10✔
232

233
     registerAttribute(QString("%1-%2").arg(track->name()).arg(track->id()));
10✔
234

235
     return true;
10✔
236
}
237

238
bool Show::removeTrack(quint32 id)
3✔
239
{
240
    if (m_tracks.contains(id) == true)
3✔
241
    {
242
        Track* track = m_tracks.take(id);
2✔
243
        Q_ASSERT(track != NULL);
2✔
244

245
        unregisterAttribute(QString("%1-%2").arg(track->name()).arg(track->id()));
2✔
246

247
        //emit trackRemoved(id);
248
        delete track;
2✔
249

250
        return true;
2✔
251
    }
252
    else
253
    {
254
        qWarning() << Q_FUNC_INFO << "No track found with id" << id;
1✔
255
        return false;
1✔
256
    }
257
}
258

259
Track* Show::track(quint32 id) const
5✔
260
{
261
    return m_tracks.value(id, NULL);
5✔
262
}
263

264
Track* Show::getTrackFromSceneID(quint32 id) const
2✔
265
{
266
    foreach (Track *track, m_tracks)
3✔
267
    {
268
        if (track->getSceneID() == id)
2✔
269
            return track;
1✔
270
    }
2✔
271
    return NULL;
1✔
272
}
273

274
Track *Show::getTrackFromShowFunctionID(quint32 id) const
×
275
{
276
    foreach (Track *track, m_tracks)
×
277
        if (track->showFunction(id) != NULL)
×
278
            return track;
×
279

280
    return NULL;
×
281
}
282

283
int Show::getTracksCount() const
5✔
284
{
285
    return m_tracks.size();
5✔
286
}
287

288
void Show::moveTrack(Track *track, int direction)
3✔
289
{
290
    if (track == NULL)
3✔
291
        return;
×
292

293
    qint32 trkID = track->id();
3✔
294
    if (trkID == 0 && direction == -1)
3✔
295
        return;
1✔
296
    qint32 maxID = -1;
2✔
297
    Track *swapTrack = NULL;
2✔
298
    qint32 swapID = -1;
2✔
299
    if (direction > 0) swapID = INT_MAX;
2✔
300

301
    foreach (quint32 id, m_tracks.keys())
8✔
302
    {
303
        qint32 signedID = (qint32)id;
4✔
304
        if (signedID > maxID) maxID = signedID;
4✔
305
        if (direction == -1 && signedID > swapID && signedID < trkID)
4✔
306
            swapID = signedID;
2✔
307
        else if (direction == 1 && signedID < swapID && signedID > trkID)
2✔
308
            swapID = signedID;
×
309
    }
2✔
310

311
    qDebug() << Q_FUNC_INFO << "Direction:" << direction << ", trackID:" << trkID << ", swapID:" << swapID;
2✔
312
    if (swapID == trkID || (direction > 0 && trkID == maxID))
2✔
313
        return;
×
314

315
    swapTrack = m_tracks[swapID];
2✔
316
    m_tracks[swapID] = track;
2✔
317
    m_tracks[trkID] = swapTrack;
2✔
318
    track->setId(swapID);
2✔
319
    swapTrack->setId(trkID);
2✔
320
}
321

322
QList <Track*> Show::tracks() const
16✔
323
{
324
    return m_tracks.values();
16✔
325
}
326

327
quint32 Show::createTrackId()
8✔
328
{
329
    while (m_tracks.contains(m_latestTrackId) == true ||
18✔
330
           m_latestTrackId == Track::invalidId())
8✔
331
    {
332
        m_latestTrackId++;
2✔
333
    }
334

335
    return m_latestTrackId;
8✔
336
}
337

338
/*********************************************************************
339
 * Show Functions
340
 *********************************************************************/
341

342
quint32 Show::getLatestShowFunctionId()
4✔
343
{
344
    return m_latestShowFunctionID++;
4✔
345
}
346

347
ShowFunction *Show::showFunction(quint32 id) const
2✔
348
{
349
    foreach (Track *track, m_tracks)
3✔
350
    {
351
        ShowFunction *sf = track->showFunction(id);
2✔
352
        if (sf != NULL)
2✔
353
            return sf;
1✔
354
    }
2✔
355

356
    return NULL;
1✔
357
}
358

359
/*****************************************************************************
360
 * Load & Save
361
 *****************************************************************************/
362

363
bool Show::saveXML(QXmlStreamWriter *doc) const
1✔
364
{
365
    Q_ASSERT(doc != NULL);
1✔
366

367
    /* Function tag */
368
    doc->writeStartElement(KXMLQLCFunction);
2✔
369

370
    /* Common attributes */
371
    saveXMLCommon(doc);
1✔
372

373
    doc->writeStartElement(KXMLQLCShowTimeDivision);
2✔
374
    doc->writeAttribute(KXMLQLCShowTimeType, tempoToString(m_timeDivisionType));
2✔
375
    doc->writeAttribute(KXMLQLCShowTimeBPM, QString::number(m_timeDivisionBPM));
2✔
376
    doc->writeEndElement();
1✔
377

378
    foreach (Track *track, m_tracks)
3✔
379
        track->saveXML(doc);
3✔
380

381
    /* End the <Function> tag */
382
    doc->writeEndElement();
1✔
383

384
    return true;
1✔
385
}
386

387
bool Show::loadXML(QXmlStreamReader &root)
1✔
388
{
389
    if (root.name() != KXMLQLCFunction)
1✔
390
    {
391
        qWarning() << Q_FUNC_INFO << "Function node not found";
×
392
        return false;
×
393
    }
394

395
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::ShowType))
2✔
396
    {
397
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
×
398
                   << "is not a show";
×
399
        return false;
×
400
    }
401

402
    while (root.readNextStartElement())
4✔
403
    {
404
        if (root.name() == KXMLQLCShowTimeDivision)
3✔
405
        {
406
            QString type = root.attributes().value(KXMLQLCShowTimeType).toString();
2✔
407
            int bpm = root.attributes().value(KXMLQLCShowTimeBPM).toString().toInt();
2✔
408
            setTimeDivision(stringToTempo(type), bpm);
1✔
409
            root.skipCurrentElement();
1✔
410
        }
1✔
411
        else if (root.name() == KXMLQLCTrack)
2✔
412
        {
413
            Track *trk = new Track(Function::invalidId(), this);
2✔
414
            if (trk->loadXML(root) == true)
2✔
415
                addTrack(trk, trk->id());
2✔
416
        }
417
        else
418
        {
419
            qWarning() << Q_FUNC_INFO << "Unknown Show tag:" << root.name();
×
420
            root.skipCurrentElement();
×
421
        }
422
    }
423

424
    return true;
1✔
425
}
426

427
void Show::postLoad()
×
428
{
429
    foreach (Track* track, m_tracks)
×
430
    {
431
        if (track->postLoad(doc()))
×
432
            doc()->setModified();
×
433
    }
×
434
}
×
435

436
bool Show::contains(quint32 functionId) const
3✔
437
{
438
    Doc *doc = this->doc();
3✔
439
    Q_ASSERT(doc != NULL);
3✔
440

441
    if (functionId == id())
3✔
442
        return true;
1✔
443

444
    foreach (Track* track, m_tracks)
3✔
445
    {
446
        if (track->contains(doc, functionId))
2✔
447
            return true;
1✔
448
    }
2✔
449

450
    return false;
1✔
451
}
452

453
QList<quint32> Show::components() const
1✔
454
{
455
    QList<quint32> ids;
1✔
456

457
    foreach (Track* track, m_tracks)
2✔
458
        ids.append(track->components());
2✔
459

460
    return ids;
1✔
461
}
×
462

463
/*****************************************************************************
464
 * Running
465
 *****************************************************************************/
466

467
void Show::preRun(MasterTimer* timer)
×
468
{
469
    Function::preRun(timer);
×
470
    m_runningChildren.clear();
×
471
    if (m_runner != NULL)
×
472
    {
473
        m_runner->stop();
×
474
        delete m_runner;
×
475
    }
476

477
    m_runner = new ShowRunner(doc(), this->id(), elapsed());
×
478
    int i = 0;
×
479
    foreach (Track *track, m_tracks)
×
480
        m_runner->adjustIntensity(getAttributeValue(i++), track);
×
481

482
    connect(m_runner, SIGNAL(timeChanged(quint32)), this, SIGNAL(timeChanged(quint32)));
×
483
    connect(m_runner, SIGNAL(showFinished()), this, SIGNAL(showFinished()));
×
484
    m_runner->start();
×
485
}
×
486

487
void Show::setPause(bool enable)
×
488
{
489
    if (m_runner != NULL)
×
490
        m_runner->setPause(enable);
×
491
    Function::setPause(enable);
×
492
}
×
493

494
void Show::write(MasterTimer* timer, QList<Universe *> universes)
×
495
{
496
    Q_UNUSED(universes);
497

498
    if (isPaused())
×
499
        return;
×
500

501
    m_runner->write(timer);
×
502
}
503

504
void Show::postRun(MasterTimer* timer, QList<Universe *> universes)
×
505
{
506
    if (m_runner != NULL)
×
507
    {
508
        m_runner->stop();
×
509
        delete m_runner;
×
510
        m_runner = NULL;
×
511
    }
512
    Function::postRun(timer, universes);
×
513
}
×
514

515
void Show::slotChildStopped(quint32 fid)
×
516
{
517
    Q_UNUSED(fid);
518
}
×
519

520
/*****************************************************************************
521
 * Attributes
522
 *****************************************************************************/
523

524
int Show::adjustAttribute(qreal fraction, int attributeId)
×
525
{
526
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
527

528
    if (m_runner != NULL)
×
529
    {
530
        QList<Track*> trkList = m_tracks.values();
×
531
        if (trkList.isEmpty() == false &&
×
532
            attrIndex >= 0 && attrIndex < trkList.count())
×
533
        {
534
            Track *track = trkList.at(attrIndex);
×
535
            if (track != NULL)
×
536
                m_runner->adjustIntensity(getAttributeValue(attrIndex), track);
×
537
        }
538
    }
×
539

540
    return attrIndex;
×
541
}
542

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