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

mcallegari / qlcplus / 27867971263

20 Jun 2026 10:09AM UTC coverage: 35.268% (-0.1%) from 35.377%
27867971263

push

github

mcallegari
Back to 5.3.0 debug

18433 of 52265 relevant lines covered (35.27%)

41250.48 hits per line

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

71.04
/engine/src/fixtureremapper.cpp
1
/*
2
  Q Light Controller Plus
3
  fixtureremapper.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 <QMapIterator>
21
#include <algorithm>
22
#include <QDebug>
23

24
#include "fixtureremapper.h"
25
#include "monitorproperties.h"
26
#include "qlcfixturemode.h"
27
#include "qlcfixturedef.h"
28
#include "channelmodifier.h"
29
#include "channelsgroup.h"
30
#include "fixturegroup.h"
31
#include "qlcchannel.h"
32
#include "efxfixture.h"
33
#include "chaserstep.h"
34
#include "grouphead.h"
35
#include "sequence.h"
36
#include "fixture.h"
37
#include "scene.h"
38
#include "efx.h"
39
#include "doc.h"
40

41
FixtureRemapper::FixtureRemapper()
10✔
42
{
43
}
10✔
44

45
void FixtureRemapper::reset()
1✔
46
{
47
    m_sourceList.clear();
1✔
48
    m_targetList.clear();
1✔
49
}
1✔
50

51
void FixtureRemapper::addChannelRemap(quint32 srcFxiId, quint32 srcCh,
29✔
52
                                      quint32 tgtFxiId, quint32 tgtCh)
53
{
54
    m_sourceList.append(SceneValue(srcFxiId, srcCh));
29✔
55
    m_targetList.append(SceneValue(tgtFxiId, tgtCh));
29✔
56
}
29✔
57

58
QList<QPair<quint32, quint32>> FixtureRemapper::autoConnectFixtures(Fixture *src, Fixture *tgt)
8✔
59
{
60
    QList<QPair<quint32, quint32>> channelPairs;
8✔
61
    if (src == nullptr || tgt == nullptr)
8✔
62
        return channelPairs;
×
63

64
    const QLCFixtureDef *srcDef = src->fixtureDef();
8✔
65
    const QLCFixtureDef *tgtDef = tgt->fixtureDef();
8✔
66
    const QLCFixtureMode *srcMode = src->fixtureMode();
8✔
67
    const QLCFixtureMode *tgtMode = tgt->fixtureMode();
8✔
68

69
    bool oneToOne = false;
8✔
70

71
    // Same definition and mode → direct index mapping
72
    if (srcDef != nullptr && tgtDef != nullptr &&
8✔
73
        srcMode != nullptr && tgtMode != nullptr &&
16✔
74
        srcDef->name() == tgtDef->name() &&
32✔
75
        srcMode->name() == tgtMode->name())
15✔
76
    {
77
        oneToOne = true;
7✔
78
    }
79
    // Both are untyped generic dimmers → direct index mapping
80
    else if (srcDef == nullptr && tgtDef == nullptr &&
1✔
81
             srcMode == nullptr && tgtMode == nullptr)
×
82
    {
83
        oneToOne = true;
×
84
    }
85

86
    if (oneToOne)
8✔
87
    {
88
        tgt->setForcedHTPChannels(src->forcedHTPChannels());
7✔
89
        tgt->setForcedLTPChannels(src->forcedLTPChannels());
7✔
90
    }
91

92
    for (quint32 s = 0; s < src->channels(); s++)
33✔
93
    {
94
        if (oneToOne)
25✔
95
        {
96
            if (s < tgt->channels())
22✔
97
            {
98
                addChannelRemap(src->id(), s, tgt->id(), s);
22✔
99
                channelPairs.append(qMakePair(s, s));
22✔
100

101
                if (!src->channelCanFade(s))
22✔
102
                    tgt->setChannelCanFade(s, false);
×
103

104
                ChannelModifier *chMod = src->channelModifier(s);
22✔
105
                if (chMod != nullptr)
22✔
106
                    tgt->setChannelModifier(s, chMod);
×
107
            }
108
        }
109
        else
110
        {
111
            const QLCChannel *srcCh = src->channel(s);
3✔
112
            if (srcCh == nullptr)
3✔
113
                continue;
×
114

115
            for (quint32 t = 0; t < tgt->channels(); t++)
6✔
116
            {
117
                const QLCChannel *tgtCh = tgt->channel(t);
6✔
118
                if (tgtCh == nullptr)
6✔
119
                    continue;
×
120

121
                if (tgtCh->group() != srcCh->group())
6✔
122
                    continue;
×
123
                if (tgtCh->controlByte() != srcCh->controlByte())
6✔
124
                    continue;
×
125
                if (tgtCh->group() == QLCChannel::Intensity &&
12✔
126
                    tgtCh->colour() != srcCh->colour())
6✔
127
                    continue;
3✔
128

129
                addChannelRemap(src->id(), s, tgt->id(), t);
3✔
130
                channelPairs.append(qMakePair(s, t));
3✔
131

132
                if (!src->channelCanFade(s))
3✔
133
                    tgt->setChannelCanFade(t, false);
×
134

135
                break;
3✔
136
            }
137
        }
138
    }
139

140
    return channelPairs;
8✔
141
}
×
142

143
const QList<SceneValue>& FixtureRemapper::sourceList() const
10✔
144
{
145
    return m_sourceList;
10✔
146
}
147

148
const QList<SceneValue>& FixtureRemapper::targetList() const
9✔
149
{
150
    return m_targetList;
9✔
151
}
152

153
// static
154
QList<SceneValue> FixtureRemapper::remapSceneValues(const QList<SceneValue> &funcList,
5✔
155
                                                    const QList<SceneValue> &srcList,
156
                                                    const QList<SceneValue> &tgtList)
157
{
158
    QList<SceneValue> newValuesList;
5✔
159
    foreach (SceneValue val, funcList)
14✔
160
    {
161
        for (int v = 0; v < srcList.count(); v++)
36✔
162
        {
163
            if (val == srcList.at(v))
27✔
164
            {
165
                SceneValue tgtVal = tgtList.at(v);
8✔
166
                newValuesList.append(SceneValue(tgtVal.fxi, tgtVal.channel, val.value));
8✔
167
            }
8✔
168
        }
169
    }
14✔
170
    std::sort(newValuesList.begin(), newValuesList.end());
5✔
171
    return newValuesList;
5✔
172
}
×
173

174
void FixtureRemapper::remapEFX(EFX *efx, Doc *doc)
×
175
{
176
    QList<EFXFixture*> fixListCopy;
×
177
    foreach (EFXFixture *efxFix, efx->fixtures())
×
178
    {
179
        EFXFixture *ef = new EFXFixture(efx);
×
180
        ef->copyFrom(efxFix);
×
181
        fixListCopy.append(ef);
×
182
    }
×
183

184
    efx->removeAllFixtures();
×
185
    QList<quint32> remappedFixtures;
×
186

187
    foreach (EFXFixture *efxFix, fixListCopy)
×
188
    {
189
        quint32 fxID = efxFix->head().fxi;
×
190
        for (int i = 0; i < m_sourceList.count(); i++)
×
191
        {
192
            const SceneValue &srcVal = m_sourceList.at(i);
×
193
            const SceneValue &tgtVal = m_targetList.at(i);
×
194

195
            if (srcVal.fxi != fxID)
×
196
                continue;
×
197
            if (remappedFixtures.contains(tgtVal.fxi))
×
198
                continue;
×
199

200
            Fixture *docFix = doc->fixture(tgtVal.fxi);
×
201
            if (docFix == nullptr)
×
202
                continue;
×
203

204
            const QLCChannel *chan = docFix->channel(tgtVal.channel);
×
205
            if (chan == nullptr)
×
206
                continue;
×
207

208
            if (chan->group() == QLCChannel::Pan ||
×
209
                chan->group() == QLCChannel::Tilt)
×
210
            {
211
                EFXFixture *ef = new EFXFixture(efx);
×
212
                ef->copyFrom(efxFix);
×
213
                ef->setHead(GroupHead(tgtVal.fxi, 0));
×
214
                if (!efx->addFixture(ef))
×
215
                    delete ef;
×
216
                remappedFixtures.append(tgtVal.fxi);
×
217
            }
218
        }
219
    }
×
220

221
    qDeleteAll(fixListCopy);
×
222
}
×
223

224
void FixtureRemapper::applyRemap(Doc *doc, const QList<Fixture *> &targetFixtures)
5✔
225
{
226
    Q_ASSERT(doc != nullptr);
5✔
227

228
    // 1 – Replace fixtures in the document
229
    doc->replaceFixtures(targetFixtures);
5✔
230

231
    // 2 – Remap fixture groups
232
    foreach (FixtureGroup *group, doc->fixtureGroups())
11✔
233
    {
234
        QMap<QLCPoint, GroupHead> grpHash = group->headsMap();
1✔
235
        group->reset();
1✔
236

237
        QMapIterator<QLCPoint, GroupHead> it(grpHash);
1✔
238
        while (it.hasNext())
2✔
239
        {
240
            it.next();
1✔
241
            QLCPoint pt(it.key());
1✔
242
            GroupHead head(it.value());
1✔
243

244
            if (!head.isValid())
1✔
245
                continue;
×
246

247
            for (int i = 0; i < m_sourceList.count(); i++)
1✔
248
            {
249
                if (m_sourceList.at(i).fxi == head.fxi)
1✔
250
                {
251
                    head.fxi = m_targetList.at(i).fxi;
1✔
252
                    group->resignHead(pt);
1✔
253
                    group->assignHead(pt, head);
1✔
254
                    break;
1✔
255
                }
256
            }
257
        }
1✔
258
    }
6✔
259

260
    // 3 – Remap channel groups
261
    foreach (ChannelsGroup *grp, doc->channelsGroups())
11✔
262
    {
263
        QList<SceneValue> grpChannels = grp->getChannels();
1✔
264
        grp->resetChannels();
1✔
265
        QList<SceneValue> newList = remapSceneValues(grpChannels, m_sourceList, m_targetList);
1✔
266
        foreach (SceneValue val, newList)
3✔
267
            grp->addChannel(val.fxi, val.channel);
3✔
268
    }
6✔
269

270
    // 4 – Remap functions
271
    foreach (Function *func, doc->functions())
13✔
272
    {
273
        switch (func->type())
3✔
274
        {
275
            case Function::SceneType:
2✔
276
            {
277
                Scene *s = qobject_cast<Scene*>(func);
2✔
278
                QList<SceneValue> newList = remapSceneValues(s->values(), m_sourceList, m_targetList);
2✔
279
                s->clear();
2✔
280
                foreach (const SceneValue &sv, newList)
4✔
281
                {
282
                    s->addFixture(sv.fxi);
2✔
283
                    s->setValue(sv);
2✔
284
                }
2✔
285
            }
2✔
286
            break;
2✔
287

288
            case Function::SequenceType:
1✔
289
            {
290
                Sequence *s = qobject_cast<Sequence*>(func);
1✔
291
                for (int idx = 0; idx < s->stepsCount(); idx++)
2✔
292
                {
293
                    ChaserStep *cs = s->stepAt(idx);
1✔
294
                    QList<SceneValue> newList = remapSceneValues(cs->values, m_sourceList, m_targetList);
1✔
295
                    cs->values.clear();
1✔
296
                    cs->values = newList;
1✔
297
                }
1✔
298
            }
299
            break;
1✔
300

301
            case Function::EFXType:
×
302
                remapEFX(qobject_cast<EFX*>(func), doc);
×
303
            break;
×
304

305
            default:
×
306
            break;
×
307
        }
308
    }
5✔
309

310
    // 5 – Remap monitor properties
311
    MonitorProperties *props = doc->monitorProperties();
5✔
312
    if (props != nullptr)
5✔
313
    {
314
        QMap<quint32, FixturePreviewItem> remappedItems;
5✔
315

316
        foreach (quint32 fxID, props->fixtureItemsID())
11✔
317
        {
318
            for (int v = 0; v < m_sourceList.count(); v++)
1✔
319
            {
320
                if (m_sourceList.at(v).fxi == fxID)
1✔
321
                {
322
                    remappedItems[m_targetList.at(v).fxi] = props->fixtureProperties(fxID);
1✔
323
                    break;
1✔
324
                }
325
            }
326
            props->removeFixture(fxID);
1✔
327
        }
5✔
328

329
        QMapIterator<quint32, FixturePreviewItem> it(remappedItems);
5✔
330
        while (it.hasNext())
6✔
331
        {
332
            it.next();
1✔
333
            props->setFixtureProperties(it.key(), it.value());
1✔
334
        }
335
    }
5✔
336
}
5✔
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