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

mcallegari / qlcplus / 24747863392

21 Apr 2026 09:40PM UTC coverage: 34.048% (-0.02%) from 34.071%
24747863392

push

github

mcallegari
qmlui: improve Sequence editing and values dumping

0 of 32 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

17759 of 52159 relevant lines covered (34.05%)

41163.92 hits per line

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

63.64
/engine/src/sequence.cpp
1
/*
2
  Q Light Controller Plus
3
  sequence.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 <QDebug>
23
#include <algorithm>
24

25
#include "sequence.h"
26

27
#define KXMLQLCSequenceBoundScene QStringLiteral("BoundScene")
28

29
Sequence::Sequence(Doc* doc)
10✔
30
    : Chaser(doc)
31
    , m_boundSceneID(Function::invalidId())
20✔
32
    , m_needFixup(true)
10✔
33
{
34
    m_type = Function::SequenceType;
10✔
35
    setName(tr("New Sequence"));
10✔
36
}
10✔
37

38
Sequence::~Sequence()
17✔
39
{
40
}
17✔
41

42
QIcon Sequence::getIcon() const
×
43
{
44
    return QIcon(":/sequence.png");
×
45
}
46

47
Function *Sequence::createCopy(Doc *doc, bool addToDoc)
1✔
48
{
49
    Q_ASSERT(doc != NULL);
1✔
50

51
    Function* copy = new Sequence(doc);
1✔
52
    if (copy->copyFrom(this) == false)
1✔
53
    {
54
        delete copy;
×
55
        copy = NULL;
×
56
    }
57
    if (addToDoc == true && doc->addFunction(copy) == false)
1✔
58
    {
59
        delete copy;
×
60
        copy = NULL;
×
61
    }
62

63
    return copy;
1✔
64
}
65

66
bool Sequence::copyFrom(const Function *function)
1✔
67
{
68
    const Sequence* sequence = qobject_cast<const Sequence*> (function);
1✔
69
    if (sequence == NULL)
1✔
70
        return false;
×
71

72
    // Copy sequence stuff
73
    m_steps = sequence->m_steps;
1✔
74
    m_fadeInMode = sequence->m_fadeInMode;
1✔
75
    m_fadeOutMode = sequence->m_fadeOutMode;
1✔
76
    m_holdMode = sequence->m_holdMode;
1✔
77
    m_boundSceneID = sequence->m_boundSceneID;
1✔
78

79
    // Copy common function stuff
80
    return Function::copyFrom(function);
1✔
81
}
82

83
void Sequence::setBoundSceneID(quint32 sceneID)
6✔
84
{
85
    m_boundSceneID = sceneID;
6✔
86
}
6✔
87

88
quint32 Sequence::boundSceneID() const
23✔
89
{
90
    return m_boundSceneID;
23✔
91
}
92

93
QList<quint32> Sequence::components() const
×
94
{
95
    QList<quint32> ids;
×
96
    if (m_boundSceneID != Function::invalidId())
×
97
        ids.append(m_boundSceneID);
×
98
    return ids;
×
99
}
×
100

NEW
101
void Sequence::applyDumpValues(const QList<SceneValue> &dumpedValues, int targetStepIndex)
×
102
{
NEW
103
    Scene *scene = qobject_cast<Scene *>(doc()->function(boundSceneID()));
×
NEW
104
    if (scene == nullptr)
×
NEW
105
        return;
×
106

NEW
107
    QList<SceneValue> sceneValues = scene->values();
×
NEW
108
    std::sort(sceneValues.begin(), sceneValues.end());
×
109

NEW
110
    QList<SceneValue> zeroSceneValues = sceneValues;
×
NEW
111
    for (SceneValue &scv : zeroSceneValues)
×
NEW
112
        scv.value = 0;
×
113

114
    // Keep step values aligned with the bound Scene channels, preserving
115
    // existing step values where channels already exist.
NEW
116
    for (int i = 0; i < stepsCount(); i++)
×
117
    {
NEW
118
        ChaserStep step = steps().at(i);
×
NEW
119
        QList<SceneValue> oldValues = step.values;
×
120

NEW
121
        step.values = zeroSceneValues;
×
NEW
122
        for (const SceneValue &oldScv : oldValues)
×
123
        {
NEW
124
            int index = step.values.indexOf(oldScv);
×
NEW
125
            if (index != -1)
×
NEW
126
                step.values.replace(index, oldScv);
×
127
        }
128

NEW
129
        replaceStep(step, i);
×
NEW
130
    }
×
131

NEW
132
    if (targetStepIndex < 0 || targetStepIndex >= stepsCount())
×
133
    {
NEW
134
        ChaserStep newStep(boundSceneID());
×
NEW
135
        newStep.values = zeroSceneValues;
×
NEW
136
        for (const SceneValue &scv : dumpedValues)
×
NEW
137
            newStep.setValue(scv);
×
NEW
138
        addStep(newStep);
×
NEW
139
    }
×
140
    else
141
    {
NEW
142
        ChaserStep step = steps().at(targetStepIndex);
×
NEW
143
        for (const SceneValue &scv : dumpedValues)
×
NEW
144
            step.setValue(scv);
×
NEW
145
        replaceStep(step, targetStepIndex);
×
NEW
146
    }
×
NEW
147
}
×
148

149
/*****************************************************************************
150
 * Save & Load
151
 *****************************************************************************/
152

153
bool Sequence::saveXML(QXmlStreamWriter *doc) const
1✔
154
{
155
    Q_ASSERT(doc != NULL);
1✔
156

157
    /* Function tag */
158
    doc->writeStartElement(KXMLQLCFunction);
2✔
159

160
    /* Common attributes */
161
    saveXMLCommon(doc);
1✔
162

163
    doc->writeAttribute(KXMLQLCSequenceBoundScene, QString::number(boundSceneID()));
2✔
164

165
    /* Tempo type */
166
    saveXMLTempoType(doc);
1✔
167

168
    /* Speed */
169
    saveXMLSpeed(doc);
1✔
170

171
    /* Direction */
172
    saveXMLDirection(doc);
1✔
173

174
    /* Run order */
175
    saveXMLRunOrder(doc);
1✔
176

177
    /* Speed modes */
178
    doc->writeStartElement(KXMLQLCChaserSpeedModes);
2✔
179
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeIn, speedModeToString(fadeInMode()));
2✔
180
    doc->writeAttribute(KXMLQLCFunctionSpeedFadeOut, speedModeToString(fadeOutMode()));
2✔
181
    doc->writeAttribute(KXMLQLCFunctionSpeedDuration, speedModeToString(durationMode()));
2✔
182
    doc->writeEndElement();
1✔
183

184
    /* Steps */
185
    for (int i = 0; i < m_steps.count(); i++)
4✔
186
        m_steps.at(i).saveXML(doc, i, true);
3✔
187

188
    /* End the <Function> tag */
189
    doc->writeEndElement();
1✔
190

191
    return true;
1✔
192
}
193

194
bool Sequence::loadXML(QXmlStreamReader &root)
4✔
195
{
196
    if (root.name() != KXMLQLCFunction)
4✔
197
    {
198
        qWarning() << Q_FUNC_INFO << "Function node not found";
1✔
199
        return false;
1✔
200
    }
201

202
    QXmlStreamAttributes funcAttrs = root.attributes();
3✔
203

204
    if (funcAttrs.value(KXMLQLCFunctionType).toString() != typeToString(Function::SequenceType))
6✔
205
    {
206
        qWarning() << Q_FUNC_INFO << funcAttrs.value(KXMLQLCFunctionType).toString()
3✔
207
                   << "is not a Sequence";
1✔
208
        return false;
1✔
209
    }
210

211
    if (funcAttrs.hasAttribute(KXMLQLCSequenceBoundScene) == false)
2✔
212
    {
213
        qWarning() << Q_FUNC_INFO << "Sequence doesn't have a bound Scene ID";
×
214
        return false;
×
215
    }
216

217
    setBoundSceneID(funcAttrs.value(KXMLQLCSequenceBoundScene).toString().toUInt());
2✔
218

219
    Scene *scene = qobject_cast<Scene *>(doc()->function(boundSceneID()));
2✔
220
    QList<SceneValue> sceneValues;
2✔
221
    if (scene != NULL)
2✔
222
    {
223
        sceneValues = scene->values();
1✔
224
        std::sort(sceneValues.begin(), sceneValues.end());
1✔
225
        m_needFixup = false;
1✔
226
    }
227

228
    /* Load Sequence contents */
229
    while (root.readNextStartElement())
16✔
230
    {
231
        if (root.name() == KXMLQLCFunctionSpeed)
14✔
232
        {
233
            loadXMLSpeed(root);
2✔
234
        }
235
        else if (root.name() == KXMLQLCFunctionDirection)
12✔
236
        {
237
            loadXMLDirection(root);
2✔
238
        }
239
        else if (root.name() == KXMLQLCFunctionRunOrder)
10✔
240
        {
241
            loadXMLRunOrder(root);
2✔
242
        }
243
        else if (root.name() == KXMLQLCFunctionTempoType)
8✔
244
        {
245
            loadXMLTempoType(root);
×
246
        }
247
        else if (root.name() == KXMLQLCChaserSpeedModes)
8✔
248
        {
249
            loadXMLSpeedModes(root);
2✔
250
        }
251
        else if (root.name() == KXMLQLCFunctionStep)
6✔
252
        {
253
            //! @todo stepNumber is useless if the steps are in the wrong order
254
            ChaserStep step;
6✔
255
            int stepNumber = -1;
6✔
256

257
            if (sceneValues.isEmpty() == false)
6✔
258
                step.values = sceneValues;
3✔
259

260
            if (step.loadXML(root, stepNumber, doc()) == true)
6✔
261
            {
262
                step.fid = boundSceneID();
6✔
263

264
                if (stepNumber >= m_steps.size())
6✔
265
                    m_steps.append(step);
6✔
266
                else
267
                    m_steps.insert(stepNumber, step);
×
268
            }
269
        }
6✔
270
        else
271
        {
272
            qWarning() << Q_FUNC_INFO << "Unknown Sequence tag:" << root.name();
×
273
            root.skipCurrentElement();
×
274
        }
275
    }
276

277
    return true;
2✔
278
}
3✔
279

280
void Sequence::postLoad()
1✔
281
{
282
    if (m_needFixup == false)
1✔
283
        return;
×
284

285
    Doc* doc = this->doc();
1✔
286
    Q_ASSERT(doc != NULL);
1✔
287

288
    Scene *scene = qobject_cast<Scene *>(doc->function(boundSceneID()));
1✔
289
    QList<SceneValue> sceneValues;
1✔
290
    if (scene != NULL)
1✔
291
    {
292
        sceneValues = scene->values();
1✔
293

294
        if (sceneValues.count() == 0)
1✔
295
        {
296
            qDebug() << "The bound Scene is empty ! This should never happen. Trying to fix it...";
×
297
            if (stepsCount())
×
298
            {
299
                foreach (SceneValue value, m_steps.at(0).values)
×
300
                {
301
                    value.value = 0;
×
302
                    if (doc->fixture(value.fxi) != NULL)
×
303
                        scene->setValue(value);
×
304
                }
×
305
            }
306
            m_needFixup = false;
×
307
            return;
×
308
        }
309

310
        std::sort(sceneValues.begin(), sceneValues.end());
1✔
311
    }
312

313
    int stepIndex = 0;
1✔
314

315
    QMutableListIterator <ChaserStep> it(m_steps);
1✔
316
    while (it.hasNext() == true)
4✔
317
    {
318
        ChaserStep step(it.next());
3✔
319
        if (sceneValues.count() == step.values.count())
3✔
320
        {
321
            stepIndex++;
×
322
            continue;
×
323
        }
324

325
        QList <SceneValue> tmpList = step.values;
3✔
326
        step.values = sceneValues;
3✔
327
        for (int i = 0; i < tmpList.count(); i++)
21✔
328
        {
329
            int tmpIndex = step.values.indexOf(tmpList.at(i));
18✔
330
            if (tmpIndex == -1)
18✔
331
                continue;
×
332
            step.values.replace(tmpIndex, tmpList.at(i));
18✔
333
        }
334

335
        replaceStep(step, stepIndex);
3✔
336
        stepIndex++;
3✔
337
    }
3✔
338
    m_needFixup = false;
1✔
339

340
    qDebug() << "Sequence" << name() << "steps fixed. Values:" << sceneValues.count();
1✔
341
}
1✔
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