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

mcallegari / qlcplus / 19144422256

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

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

68.86
/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

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

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

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

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

145
/*********************************************************************
146
 * Time division
147
 *********************************************************************/
148

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

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

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

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

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

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

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

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

216
/*****************************************************************************
217
 * Tracks
218
 *****************************************************************************/
219

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

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

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

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

234
     return true;
10✔
235
}
236

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

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

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

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

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

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

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

279
    return NULL;
×
280
}
281

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

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

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

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

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

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

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

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

334
    return m_latestTrackId;
8✔
335
}
336

337
/*********************************************************************
338
 * Show Functions
339
 *********************************************************************/
340

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

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

355
    return NULL;
1✔
356
}
357

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

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

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

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

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

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

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

383
    return true;
1✔
384
}
385

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

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

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

423
    return true;
1✔
424
}
425

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

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

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

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

449
    return false;
1✔
450
}
451

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

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

459
    return ids;
1✔
460
}
×
461

462
/*****************************************************************************
463
 * Running
464
 *****************************************************************************/
465

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

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

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

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

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

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

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

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

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

519
/*****************************************************************************
520
 * Attributes
521
 *****************************************************************************/
522

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

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

539
    return attrIndex;
×
540
}
541

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