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

mcallegari / qlcplus / 15805077468

22 Jun 2025 08:36AM UTC coverage: 31.876% (-0.01%) from 31.89%
15805077468

push

github

mcallegari
plugins/dmxusb: fix RDM discovery and commands while DMX is running

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

3722 existing lines in 175 files now uncovered.

16438 of 51569 relevant lines covered (31.88%)

19266.08 hits per line

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

90.14
/engine/src/doc.cpp
1
/*
2
  Q Light Controller Plus
3
  doc.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 <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QStringList>
24
#include <QString>
25
#include <QDebug>
26
#include <QList>
27
#include <QTime>
28
#include <QDir>
29
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
30
#include <QRandomGenerator>
31
#endif
32

33
#include "qlcfixturemode.h"
34
#include "qlcfixturedef.h"
35

36
#include "monitorproperties.h"
37
#include "audioplugincache.h"
38
#include "rgbscriptscache.h"
39
#include "channelsgroup.h"
40
#include "scriptwrapper.h"
41
#include "collection.h"
42
#include "function.h"
43
#include "universe.h"
44
#include "sequence.h"
45
#include "fixture.h"
46
#include "chaser.h"
47
#include "show.h"
48
#include "doc.h"
49
#include "bus.h"
50

51
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
52
 #if defined(__APPLE__) || defined(Q_OS_MAC)
53
  #include "audiocapture_portaudio.h"
54
 #elif defined(WIN32) || defined (Q_OS_WIN)
55
  #include "audiocapture_wavein.h"
56
 #else
57
  #include "audiocapture_alsa.h"
58
 #endif
59
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
60
 #include "audiocapture_qt5.h"
61
#else
62
 #include "audiocapture_qt6.h"
63
#endif
64

65
Doc::Doc(QObject* parent, int universes)
177✔
66
    : QObject(parent)
67
    , m_workspacePath("")
177✔
68
    , m_fixtureDefCache(new QLCFixtureDefCache)
177✔
69
    , m_modifiersCache(new QLCModifiersCache)
177✔
70
    , m_rgbScriptsCache(new RGBScriptsCache(this))
177✔
71
    , m_ioPluginCache(new IOPluginCache(this))
177✔
72
    , m_audioPluginCache(new AudioPluginCache(this))
177✔
73
    , m_masterTimer(new MasterTimer(this))
177✔
74
    , m_ioMap(new InputOutputMap(this, universes))
177✔
75
    , m_monitorProps(NULL)
177✔
76
    , m_mode(Design)
177✔
77
    , m_kiosk(false)
177✔
78
    , m_loadStatus(Cleared)
177✔
79
    , m_clipboard(new QLCClipboard(this))
177✔
80
    , m_fixturesListCacheUpToDate(false)
177✔
81
    , m_latestFixtureId(0)
177✔
82
    , m_latestFixtureGroupId(0)
177✔
83
    , m_latestChannelsGroupId(0)
177✔
84
    , m_latestPaletteId(0)
177✔
85
    , m_latestFunctionId(0)
177✔
86
    , m_startupFunctionId(Function::invalidId())
531✔
87
{
88
    Bus::init(this);
177✔
89
    resetModified();
177✔
90
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
91
    qsrand(QTime::currentTime().msec());
92
#endif
93
    
94
}
177✔
95

96
Doc::~Doc()
314✔
97
{
98
    delete m_masterTimer;
177✔
99
    m_masterTimer = NULL;
177✔
100

101
    clearContents();
177✔
102

103
    if (isKiosk() == false)
177✔
104
    {
105
        // TODO: is this still needed ??
106
        //m_ioMap->saveDefaults();
107
    }
108
    delete m_ioMap;
177✔
109
    m_ioMap = NULL;
177✔
110

111
    delete m_ioPluginCache;
177✔
112
    m_ioPluginCache = NULL;
177✔
113

114
    delete m_modifiersCache;
177✔
115
    m_modifiersCache = NULL;
177✔
116

117
    delete m_fixtureDefCache;
177✔
118
    m_fixtureDefCache = NULL;
177✔
119

120
    delete m_rgbScriptsCache;
177✔
121
    m_rgbScriptsCache = NULL;
177✔
122
}
314✔
123

124
void Doc::clearContents()
452✔
125
{
126
    emit clearing();
452✔
127

128
    m_clipboard->resetContents();
452✔
129

130
    if (m_monitorProps != NULL)
452✔
131
        m_monitorProps->reset();
9✔
132

133
    destroyAudioCapture();
452✔
134

135
    // Delete all function instances
136
    QListIterator <quint32> funcit(m_functions.keys());
452✔
137
    while (funcit.hasNext() == true)
698✔
138
    {
139
        Function* func = m_functions.take(funcit.next());
246✔
140
        if (func == NULL)
246✔
141
            continue;
×
142
        emit functionRemoved(func->id());
246✔
143
        delete func;
246✔
144
    }
145

146
    // Delete all palettes
147
    QListIterator <quint32> palIt(m_palettes.keys());
452✔
148
    while (palIt.hasNext() == true)
452✔
149
    {
150
        QLCPalette *palette = m_palettes.take(palIt.next());
×
151
        emit paletteRemoved(palette->id());
×
152
        delete palette;
×
153
    }
154

155
    // Delete all channel groups
156
    QListIterator <quint32> grpchans(m_channelsGroups.keys());
452✔
157
    while (grpchans.hasNext() == true)
455✔
158
    {
159
        ChannelsGroup* grp = m_channelsGroups.take(grpchans.next());
3✔
160
        emit channelsGroupRemoved(grp->id());
3✔
161
        delete grp;
3✔
162
    }
163

164
    // Delete all fixture groups
165
    QListIterator <quint32> grpit(m_fixtureGroups.keys());
452✔
166
    while (grpit.hasNext() == true)
463✔
167
    {
168
        FixtureGroup* grp = m_fixtureGroups.take(grpit.next());
11✔
169
        quint32 grpID = grp->id();
11✔
170
        delete grp;
11✔
171
        emit fixtureGroupRemoved(grpID);
11✔
172
    }
173

174
    // Delete all fixture instances
175
    QListIterator <quint32> fxit(m_fixtures.keys());
452✔
176
    while (fxit.hasNext() == true)
17,226✔
177
    {
178
        Fixture* fxi = m_fixtures.take(fxit.next());
16,774✔
179
        quint32 fxID = fxi->id();
16,774✔
180
        delete fxi;
16,774✔
181
        emit fixtureRemoved(fxID);
16,774✔
182
    }
183
    m_fixturesListCacheUpToDate = false;
452✔
184

185
    m_orderedGroups.clear();
452✔
186

187
    m_latestFunctionId = 0;
452✔
188
    m_latestFixtureId = 0;
452✔
189
    m_latestFixtureGroupId = 0;
452✔
190
    m_latestChannelsGroupId = 0;
452✔
191
    m_latestPaletteId = 0;
452✔
192
    m_addresses.clear();
452✔
193
    m_loadStatus = Cleared;
452✔
194

195
    emit cleared();
452✔
196
}
452✔
197

198
void Doc::setWorkspacePath(QString path)
4✔
199
{
200
    m_workspacePath = path;
4✔
201
}
4✔
202

203
QString Doc::workspacePath() const
105✔
204
{
205
    return m_workspacePath;
105✔
206
}
207

208
QString Doc::normalizeComponentPath(const QString& filePath) const
7✔
209
{
210
    if (filePath.isEmpty())
7✔
211
        return filePath;
1✔
212

213
    QFileInfo f(filePath);
6✔
214

215
    if (f.absolutePath().startsWith(workspacePath()))
6✔
216
    {
217
        return QDir(workspacePath()).relativeFilePath(f.absoluteFilePath());
8✔
218
    }
219
    else
220
    {
221
        return f.absoluteFilePath();
2✔
222
    }
223
}
6✔
224

225
QString Doc::denormalizeComponentPath(const QString& filePath) const
8✔
226
{
227
    if (filePath.isEmpty())
8✔
228
        return filePath;
2✔
229

230
    return QFileInfo(QDir(workspacePath()), filePath).absoluteFilePath();
12✔
231
}
232

233
/*****************************************************************************
234
 * Engine components
235
 *****************************************************************************/
236

237
QLCFixtureDefCache* Doc::fixtureDefCache() const
1,643✔
238
{
239
    return m_fixtureDefCache;
1,643✔
240
}
241

242
void Doc::setFixtureDefinitionCache(QLCFixtureDefCache *cache)
×
243
{
244
    m_fixtureDefCache = cache;
×
245
}
×
246

247
QLCModifiersCache* Doc::modifiersCache() const
1✔
248
{
249
    return m_modifiersCache;
1✔
250
}
251

252
RGBScriptsCache* Doc::rgbScriptsCache() const
69✔
253
{
254
    return m_rgbScriptsCache;
69✔
255
}
256

257
IOPluginCache* Doc::ioPluginCache() const
288✔
258
{
259
    return m_ioPluginCache;
288✔
260
}
261

262
AudioPluginCache *Doc::audioPluginCache() const
1✔
263
{
264
    return m_audioPluginCache;
1✔
265
}
266

267
InputOutputMap* Doc::inputOutputMap() const
2,709✔
268
{
269
    return m_ioMap;
2,709✔
270
}
271

272
MasterTimer* Doc::masterTimer() const
1,102✔
273
{
274
    return m_masterTimer;
1,102✔
275
}
276

277
QSharedPointer<AudioCapture> Doc::audioInputCapture()
17✔
278
{
279
    if (!m_inputCapture)
17✔
280
    {
281
        qDebug() << "Creating new audio capture";
2✔
282
        m_inputCapture = QSharedPointer<AudioCapture>(
4✔
283
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
284
#if defined(__APPLE__) || defined(Q_OS_MAC)
285
            new AudioCapturePortAudio()
286
#elif defined(WIN32) || defined (Q_OS_WIN)
287
            new AudioCaptureWaveIn()
288
#else
289
            new AudioCaptureAlsa()
290
#endif
291
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
292
            new AudioCaptureQt6()
293
#else
294
            new AudioCaptureQt6()
2✔
295
#endif
296
            );
2✔
297
    }
298
    return m_inputCapture;
17✔
299
}
300

301
void Doc::destroyAudioCapture()
452✔
302
{
303
    if (m_inputCapture.isNull() == false)
452✔
304
    {
305
        qDebug() << "Destroying audio capture";
2✔
306
        m_inputCapture.clear();
2✔
307
    }
308
}
452✔
309

310
/*****************************************************************************
311
 * Modified status
312
 *****************************************************************************/
313
Doc::LoadStatus Doc::loadStatus() const
2✔
314
{
315
    return m_loadStatus;
2✔
316
}
317

318
bool Doc::isModified() const
29✔
319
{
320
    return m_modified;
29✔
321
}
322

323
void Doc::setModified()
1,245✔
324
{
325
    m_modified = true;
1,245✔
326
    emit modified(true);
1,245✔
327
}
1,245✔
328

329
void Doc::resetModified()
192✔
330
{
331
    m_modified = false;
192✔
332
    emit modified(false);
192✔
333
}
192✔
334

335
/*****************************************************************************
336
 * Main operating mode
337
 *****************************************************************************/
338

339
void Doc::setMode(Doc::Mode mode)
23✔
340
{
341
    /* Don't do mode switching twice */
342
    if (m_mode == mode)
23✔
343
        return;
2✔
344
    m_mode = mode;
21✔
345

346
    // Run startup function
347
    if (m_mode == Operate && m_startupFunctionId != Function::invalidId())
21✔
348
    {
349
        Function *func = function(m_startupFunctionId);
×
350
        if (func != NULL)
×
351
        {
352
            qDebug() << Q_FUNC_INFO << "Starting startup function. (" << m_startupFunctionId << ")";
×
353
            func->start(masterTimer(), FunctionParent::master());
×
354
        }
355
        else
356
        {
357
            qWarning() << Q_FUNC_INFO << "Startup function does not exist, erasing. (" << m_startupFunctionId << ")";
×
358
            m_startupFunctionId = Function::invalidId();
×
359
        }
360
    }
361

362
    emit modeChanged(m_mode);
21✔
363
}
364

365
Doc::Mode Doc::mode() const
323✔
366
{
367
    return m_mode;
323✔
368
}
369

370
void Doc::setKiosk(bool state)
1✔
371
{
372
    m_kiosk = state;
1✔
373
}
1✔
374

375
bool Doc::isKiosk() const
179✔
376
{
377
    return m_kiosk;
179✔
378
}
379

380
/*********************************************************************
381
 * Clipboard
382
 *********************************************************************/
383

384
QLCClipboard *Doc::clipboard()
1✔
385
{
386
    return m_clipboard;
1✔
387
}
388

389
/*****************************************************************************
390
 * Fixtures
391
 *****************************************************************************/
392

393
quint32 Doc::createFixtureId()
16,781✔
394
{
395
    /* This results in an endless loop if there are UINT_MAX-1 fixtures. That,
396
       however, seems a bit unlikely. Are there even 4294967295-1 fixtures in
397
       total in the whole world? */
398
    while (m_fixtures.contains(m_latestFixtureId) == true ||
50,195✔
399
           m_latestFixtureId == Fixture::invalidId())
16,781✔
400
    {
401
        m_latestFixtureId++;
16,633✔
402
    }
403

404
    return m_latestFixtureId;
16,781✔
405
}
406

407
bool Doc::addFixture(Fixture* fixture, quint32 id, bool crossUniverse)
406✔
408
{
409
    Q_ASSERT(fixture != NULL);
406✔
410

411
    quint32 i;
412
    quint32 uni = fixture->universe();
406✔
413

414
    // No ID given, this method can assign one
415
    if (id == Fixture::invalidId())
406✔
416
        id = createFixtureId();
397✔
417

418
    if (m_fixtures.contains(id) == true || id == Fixture::invalidId())
406✔
419
    {
420
        qWarning() << Q_FUNC_INFO << "a fixture with ID" << id << "already exists!";
1✔
421
        return false;
1✔
422
    }
423

424
    /* Check for overlapping address */
425
    for (i = fixture->universeAddress();
2,971✔
426
         i < fixture->universeAddress() + fixture->channels(); i++)
2,971✔
427
    {
428
        if (m_addresses.contains(i))
2,570✔
429
        {
430
            qWarning() << Q_FUNC_INFO << "fixture" << id << "overlapping with fixture" << m_addresses[i] << "@ channel" << i;
4✔
431
            return false;
4✔
432
        }
433
    }
434

435
    fixture->setID(id);
401✔
436
    m_fixtures.insert(id, fixture);
401✔
437
    m_fixturesListCacheUpToDate = false;
401✔
438

439
    /* Patch fixture change signals thru Doc */
440
    connect(fixture, SIGNAL(changed(quint32)),
401✔
441
            this, SLOT(slotFixtureChanged(quint32)));
442

443
    /* Keep track of fixture addresses */
444
    for (i = fixture->universeAddress();
401✔
445
         i < fixture->universeAddress() + fixture->channels(); i++)
2,967✔
446
    {
447
        m_addresses[i] = id;
2,566✔
448
    }
449

450
    if (crossUniverse)
401✔
451
        uni = floor((fixture->universeAddress() + fixture->channels()) / 512);
×
452

453
    if (uni >= inputOutputMap()->universesCount())
401✔
454
    {
455
        for (i = inputOutputMap()->universesCount(); i <= uni; i++)
×
456
            inputOutputMap()->addUniverse(i);
×
457
        inputOutputMap()->startUniverses();
×
458
    }
459

460
    // Add the fixture channels capabilities to the universe they belong
461
    QList<Universe *> universes = inputOutputMap()->claimUniverses();
401✔
462

463
    QList<int> forcedHTP = fixture->forcedHTPChannels();
401✔
464
    QList<int> forcedLTP = fixture->forcedLTPChannels();
401✔
465
    quint32 fxAddress = fixture->address();
401✔
466

467
    for (i = 0; i < fixture->channels(); i++)
2,967✔
468
    {
469
        const QLCChannel *channel(fixture->channel(i));
2,566✔
470
        quint32 addr = fxAddress + i;
2,566✔
471

472
        if (crossUniverse)
2,566✔
473
        {
474
            uni = floor((fixture->universeAddress() + i) / 512);
×
475
            addr = (fixture->universeAddress() + i) - (uni * 512);
×
476
        }
477

478
        // Inform Universe of any HTP/LTP forcing
479
        if (forcedHTP.contains(int(i)))
2,566✔
480
            universes.at(uni)->setChannelCapability(addr, channel->group(), Universe::HTP);
×
481
        else if (forcedLTP.contains(int(i)))
2,566✔
482
            universes.at(uni)->setChannelCapability(addr, channel->group(), Universe::LTP);
1✔
483
        else
484
            universes.at(uni)->setChannelCapability(addr, channel->group());
2,565✔
485

486
        // Apply the default value BEFORE modifiers
487
        universes.at(uni)->setChannelDefaultValue(addr, channel->defaultValue());
2,566✔
488

489
        // Apply a channel modifier, if defined
490
        ChannelModifier *mod = fixture->channelModifier(i);
2,566✔
491
        universes.at(uni)->setChannelModifier(addr, mod);
2,566✔
492
    }
493
    inputOutputMap()->releaseUniverses(true);
401✔
494

495
    emit fixtureAdded(id);
401✔
496
    setModified();
401✔
497

498
    return true;
401✔
499
}
401✔
500

501
bool Doc::deleteFixture(quint32 id)
16✔
502
{
503
    if (m_fixtures.contains(id) == true)
16✔
504
    {
505
        Fixture* fxi = m_fixtures.take(id);
10✔
506
        Q_ASSERT(fxi != NULL);
10✔
507
        m_fixturesListCacheUpToDate = false;
10✔
508

509
        /* Keep track of fixture addresses */
510
        QMutableHashIterator <uint,uint> it(m_addresses);
10✔
511
        while (it.hasNext() == true)
102✔
512
        {
513
            it.next();
92✔
514
            if (it.value() == id)
92✔
515
                it.remove();
47✔
516
        }
517
        if (m_monitorProps != NULL)
10✔
518
            m_monitorProps->removeFixture(id);
×
519

520
        emit fixtureRemoved(id);
10✔
521
        setModified();
10✔
522
        delete fxi;
10✔
523

524
        if (m_fixtures.count() == 0)
10✔
525
            m_latestFixtureId = 0;
6✔
526

527
        return true;
10✔
528
    }
529
    else
530
    {
531
        qWarning() << Q_FUNC_INFO << "No fixture with id" << id;
6✔
532
        return false;
6✔
533
    }
534
}
535

536
bool Doc::replaceFixtures(QList<Fixture*> newFixturesList)
1✔
537
{
538
    // Delete all fixture instances
539
    QListIterator <quint32> fxit(m_fixtures.keys());
1✔
540
    while (fxit.hasNext() == true)
4✔
541
    {
542
        Fixture* fxi = m_fixtures.take(fxit.next());
3✔
543
        disconnect(fxi, SIGNAL(changed(quint32)),
3✔
544
                   this, SLOT(slotFixtureChanged(quint32)));
545
        delete fxi;
3✔
546
        m_fixturesListCacheUpToDate = false;
3✔
547
    }
548
    m_latestFixtureId = 0;
1✔
549
    m_addresses.clear();
1✔
550

551
    foreach (Fixture *fixture, newFixturesList)
3✔
552
    {
553
        quint32 id = fixture->id();
2✔
554
        // create a copy of the original cause remapping will
555
        // destroy it later
556
        Fixture *newFixture = new Fixture(this);
2✔
557
        newFixture->setID(id);
2✔
558
        newFixture->setName(fixture->name());
2✔
559
        newFixture->setAddress(fixture->address());
2✔
560
        newFixture->setUniverse(fixture->universe());
2✔
561

562
        if (fixture->fixtureDef() == NULL ||
5✔
563
            (fixture->fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
5✔
564
             fixture->fixtureDef()->model() == KXMLFixtureGeneric))
3✔
565
        {
566
            // Generic dimmers just need to know the number of channels
567
            newFixture->setChannels(fixture->channels());
1✔
568
        }
569
        else if (fixture->fixtureDef() == NULL ||
2✔
570
            (fixture->fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
2✔
571
             fixture->fixtureDef()->model() == KXMLFixtureRGBPanel))
1✔
572
        {
573
            // RGB Panels definitions are not cached or shared, so
574
            // let's make a deep copy of them
575
            QLCFixtureDef *fixtureDef = new QLCFixtureDef();
×
576
            *fixtureDef = *fixture->fixtureDef();
×
577
            QLCFixtureMode *mode = new QLCFixtureMode(fixtureDef);
×
578
            *mode = *fixture->fixtureMode();
×
579
            newFixture->setFixtureDefinition(fixtureDef, mode);
×
580
        }
581
        else
582
        {
583
            QLCFixtureDef *def = fixtureDefCache()->fixtureDef(fixture->fixtureDef()->manufacturer(),
2✔
584
                                                               fixture->fixtureDef()->model());
2✔
585
            QLCFixtureMode *mode = NULL;
1✔
586
            if (def != NULL)
1✔
587
                mode = def->mode(fixture->fixtureMode()->name());
1✔
588
            newFixture->setFixtureDefinition(def, mode);
1✔
589
        }
590

591
        newFixture->setExcludeFadeChannels(fixture->excludeFadeChannels());
2✔
592
        newFixture->setForcedHTPChannels(fixture->forcedHTPChannels());
2✔
593
        newFixture->setForcedLTPChannels(fixture->forcedLTPChannels());
2✔
594

595
        for (quint32 s = 0; s < fixture->channels(); s++)
13✔
596
        {
597
            ChannelModifier *chMod = fixture->channelModifier(s);
11✔
598
            if (chMod != NULL)
11✔
599
                newFixture->setChannelModifier(s, chMod);
×
600
        }
601

602
        m_fixtures.insert(id, newFixture);
2✔
603
        m_fixturesListCacheUpToDate = false;
2✔
604

605
        /* Patch fixture change signals thru Doc */
606
        connect(newFixture, SIGNAL(changed(quint32)),
2✔
607
                this, SLOT(slotFixtureChanged(quint32)));
608

609
        /* Keep track of fixture addresses */
610
        for (uint i = newFixture->universeAddress();
2✔
611
             i < newFixture->universeAddress() + newFixture->channels(); i++)
13✔
612
        {
613
            m_addresses[i] = id;
11✔
614
        }
615
        m_latestFixtureId = id;
2✔
616
    }
1✔
617
    return true;
1✔
618
}
1✔
619

620
bool Doc::updateFixtureChannelCapabilities(quint32 id, QList<int> forcedHTP, QList<int> forcedLTP)
2✔
621
{
622
    if (m_fixtures.contains(id) == false)
2✔
623
        return false;
1✔
624

625
    Fixture* fixture = m_fixtures[id];
1✔
626
    // get exclusive access to the universes list
627
    QList<Universe *> universes = inputOutputMap()->claimUniverses();
1✔
628
    Universe *universe = universes.at(fixture->universe());
1✔
629
    quint32 fxAddress = fixture->address();
1✔
630

631
    // Set forced HTP channels
632
    fixture->setForcedHTPChannels(forcedHTP);
1✔
633

634
    // Set forced LTP channels
635
    fixture->setForcedLTPChannels(forcedLTP);
1✔
636

637
    // Update the Fixture Universe with the current channel states
638
    for (quint32 i = 0 ; i < fixture->channels(); i++)
7✔
639
    {
640
        const QLCChannel *channel(fixture->channel(i));
6✔
641

642
        // Inform Universe of any HTP/LTP forcing
643
        if (forcedHTP.contains(int(i)))
6✔
644
            universe->setChannelCapability(fxAddress + i, channel->group(), Universe::HTP);
2✔
645
        else if (forcedLTP.contains(int(i)))
4✔
646
            universe->setChannelCapability(fxAddress + i, channel->group(), Universe::LTP);
1✔
647
        else
648
            universe->setChannelCapability(fxAddress + i, channel->group());
3✔
649

650
        // Apply the default value BEFORE modifiers
651
        universe->setChannelDefaultValue(fxAddress + i, channel->defaultValue());
6✔
652

653
        // Apply a channel modifier, if defined
654
        ChannelModifier *mod = fixture->channelModifier(i);
6✔
655
        universe->setChannelModifier(fxAddress + i, mod);
6✔
656
    }
657

658
    inputOutputMap()->releaseUniverses(true);
1✔
659

660
    return true;
1✔
661
}
1✔
662

663
QList<Fixture*> const& Doc::fixtures() const
71✔
664
{
665
    if (!m_fixturesListCacheUpToDate)
71✔
666
    {
667
        // Sort fixtures by id
668
        QMap <quint32, Fixture*> fixturesMap;
40✔
669
        QHashIterator <quint32, Fixture*> hashIt(m_fixtures);
40✔
670
        while (hashIt.hasNext())
75✔
671
        {
672
            hashIt.next();
35✔
673
            fixturesMap.insert(hashIt.key(), hashIt.value());
35✔
674
        }
675
        const_cast<QList<Fixture*>&>(m_fixturesListCache) = fixturesMap.values();
40✔
676
        const_cast<bool&>(m_fixturesListCacheUpToDate) = true;
40✔
677
    }
40✔
678
    return m_fixturesListCache;
71✔
679
}
680

681
int Doc::fixturesCount() const
×
682
{
683
    return m_fixtures.count();
×
684
}
685

686
Fixture* Doc::fixture(quint32 id) const
796,289✔
687
{
688
    return m_fixtures.value(id, NULL);
796,289✔
689
}
690

691
quint32 Doc::fixtureForAddress(quint32 universeAddress) const
79✔
692
{
693
    return m_addresses.value(universeAddress, Fixture::invalidId());
79✔
694
}
695

696
int Doc::totalPowerConsumption(int& fuzzy) const
4✔
697
{
698
    int totalPowerConsumption = 0;
4✔
699

700
    // Make sure fuzzy starts from zero
701
    fuzzy = 0;
4✔
702

703
    QListIterator <Fixture*> fxit(fixtures());
4✔
704
    while (fxit.hasNext() == true)
14✔
705
    {
706
        Fixture* fxi(fxit.next());
10✔
707
        Q_ASSERT(fxi != NULL);
10✔
708

709
        if (fxi->fixtureMode() != NULL)
10✔
710
        {
711
            QLCPhysical phys = fxi->fixtureMode()->physical();
10✔
712
            if (phys.powerConsumption() > 0)
10✔
713
                totalPowerConsumption += phys.powerConsumption();
7✔
714
            else
715
                fuzzy++;
3✔
716
        }
10✔
717
        else
718
        {
719
            fuzzy++;
×
720
        }
721
    }
722

723
    return totalPowerConsumption;
4✔
724
}
4✔
725

726
void Doc::slotFixtureChanged(quint32 id)
4✔
727
{
728
    /* Keep track of fixture addresses */
729
    Fixture* fxi = fixture(id);
4✔
730

731
    // remove it
732
    QMutableHashIterator <uint,uint> it(m_addresses);
4✔
733
    while (it.hasNext() == true)
67✔
734
    {
735
        it.next();
63✔
736
        if (it.value() == id)
63✔
737
        {
738
            qDebug() << Q_FUNC_INFO << " remove: " << it.key() << " val: " << it.value();
19✔
739
            it.remove();
19✔
740
        }
741
    }
742

743
    for (uint i = fxi->universeAddress(); i < fxi->universeAddress() + fxi->channels(); i++)
23✔
744
    {
745
        /*
746
         * setting new universe and address calls this twice,
747
         * with an tmp wrong address after the first call (old address() + new universe()).
748
         * we only add if the channel is free, to prevent messing up things
749
         */
750
        Q_ASSERT(!m_addresses.contains(i));
19✔
751
        m_addresses[i] = id;
19✔
752
    }
753

754
    setModified();
4✔
755
    emit fixtureChanged(id);
4✔
756
}
4✔
757

758
/*****************************************************************************
759
 * Fixture groups
760
 *****************************************************************************/
761

762
bool Doc::addFixtureGroup(FixtureGroup* grp, quint32 id)
15✔
763
{
764
    Q_ASSERT(grp != NULL);
15✔
765

766
    // No ID given, this method can assign one
767
    if (id == FixtureGroup::invalidId())
15✔
768
        id = createFixtureGroupId();
8✔
769

770
    if (m_fixtureGroups.contains(id) == true || id == FixtureGroup::invalidId())
15✔
771
    {
772
        qWarning() << Q_FUNC_INFO << "a fixture group with ID" << id << "already exists!";
2✔
773
        return false;
2✔
774
    }
775
    else
776
    {
777
        grp->setId(id);
13✔
778
        m_fixtureGroups[id] = grp;
13✔
779

780
        /* Patch fixture group change signals thru Doc */
781
        connect(grp, SIGNAL(changed(quint32)),
13✔
782
                this, SLOT(slotFixtureGroupChanged(quint32)));
783

784
        emit fixtureGroupAdded(id);
13✔
785
        setModified();
13✔
786

787
        return true;
13✔
788
    }
789
}
790

791
bool Doc::deleteFixtureGroup(quint32 id)
4✔
792
{
793
    if (m_fixtureGroups.contains(id) == true)
4✔
794
    {
795
        FixtureGroup* grp = m_fixtureGroups.take(id);
2✔
796
        Q_ASSERT(grp != NULL);
2✔
797

798
        emit fixtureGroupRemoved(id);
2✔
799
        setModified();
2✔
800
        delete grp;
2✔
801

802
        return true;
2✔
803
    }
804
    else
805
    {
806
        qWarning() << Q_FUNC_INFO << "No fixture group with id" << id;
2✔
807
        return false;
2✔
808
    }
809
}
810

811
FixtureGroup* Doc::fixtureGroup(quint32 id) const
45✔
812
{
813
    return m_fixtureGroups.value(id, NULL);
45✔
814
}
815

816
QList <FixtureGroup*> Doc::fixtureGroups() const
34✔
817
{
818
    return m_fixtureGroups.values();
34✔
819
}
820

821
quint32 Doc::createFixtureGroupId()
8✔
822
{
823
    /* This results in an endless loop if there are UINT_MAX-1 fixture groups. That,
824
       however, seems a bit unlikely. Are there even 4294967295-1 fixtures in
825
       total in the whole world? */
826
    while (m_fixtureGroups.contains(m_latestFixtureGroupId) == true ||
20✔
827
           m_latestFixtureGroupId == FixtureGroup::invalidId())
8✔
828
    {
829
        m_latestFixtureGroupId++;
4✔
830
    }
831

832
    return m_latestFixtureGroupId;
8✔
833
}
834

835
void Doc::slotFixtureGroupChanged(quint32 id)
25✔
836
{
837
    setModified();
25✔
838
    emit fixtureGroupChanged(id);
25✔
839
}
25✔
840

841
/*********************************************************************
842
 * Channels groups
843
 *********************************************************************/
844
bool Doc::addChannelsGroup(ChannelsGroup *grp, quint32 id)
4✔
845
{
846
    Q_ASSERT(grp != NULL);
4✔
847

848
    // No ID given, this method can assign one
849
    if (id == ChannelsGroup::invalidId())
4✔
850
        id = createChannelsGroupId();
4✔
851

852
     grp->setId(id);
4✔
853
     m_channelsGroups[id] = grp;
4✔
854
     if (m_orderedGroups.contains(id) == false)
4✔
855
        m_orderedGroups.append(id);
4✔
856

857
     emit channelsGroupAdded(id);
4✔
858
     setModified();
4✔
859

860
     return true;
4✔
861
}
862

863
bool Doc::deleteChannelsGroup(quint32 id)
2✔
864
{
865
    if (m_channelsGroups.contains(id) == true)
2✔
866
    {
867
        ChannelsGroup* grp = m_channelsGroups.take(id);
1✔
868
        Q_ASSERT(grp != NULL);
1✔
869

870
        emit channelsGroupRemoved(id);
1✔
871
        setModified();
1✔
872
        delete grp;
1✔
873

874
        int idx = m_orderedGroups.indexOf(id);
1✔
875
        if (idx != -1)
1✔
876
            m_orderedGroups.takeAt(idx);
1✔
877

878
        return true;
1✔
879
    }
880
    else
881
    {
882
        qWarning() << Q_FUNC_INFO << "No channels group with id" << id;
1✔
883
        return false;
1✔
884
    }
885
}
886

887
bool Doc::moveChannelGroup(quint32 id, int direction)
3✔
888
{
889
    if (direction == 0 || m_orderedGroups.contains(id) == false)
3✔
890
        return false;
1✔
891

892
    int idx = m_orderedGroups.indexOf(id);
2✔
893

894
    if (idx + direction < 0 || idx + direction >= m_orderedGroups.count())
2✔
895
        return false;
1✔
896

897
    qDebug() << Q_FUNC_INFO << m_orderedGroups;
1✔
898
    m_orderedGroups.takeAt(idx);
1✔
899
    m_orderedGroups.insert(idx + direction, id);
1✔
900
    qDebug() << Q_FUNC_INFO << m_orderedGroups;
1✔
901

902
    setModified();
1✔
903
    return true;
1✔
904
}
905

906
ChannelsGroup* Doc::channelsGroup(quint32 id) const
2✔
907
{
908
    return m_channelsGroups.value(id, NULL);
2✔
909
}
910

911
QList <ChannelsGroup*> Doc::channelsGroups() const
15✔
912
{
913
    QList <ChannelsGroup*> orderedList;
15✔
914

915
    for (int i = 0; i < m_orderedGroups.count(); i++)
37✔
916
    {
917
        orderedList.append(m_channelsGroups[m_orderedGroups.at(i)]);
22✔
918
    }
919
    return orderedList;
15✔
UNCOV
920
}
×
921

922
quint32 Doc::createChannelsGroupId()
4✔
923
{
924
    while (m_channelsGroups.contains(m_latestChannelsGroupId) == true ||
10✔
925
           m_latestChannelsGroupId == ChannelsGroup::invalidId())
4✔
926
    {
927
        m_latestChannelsGroupId++;
2✔
928
    }
929

930
    return m_latestChannelsGroupId;
4✔
931
}
932

933
/*********************************************************************
934
 * Palettes
935
 *********************************************************************/
936

937
bool Doc::addPalette(QLCPalette *palette, quint32 id)
2✔
938
{
939
    Q_ASSERT(palette != NULL);
2✔
940

941
    // No ID given, this method can assign one
942
    if (id == QLCPalette::invalidId())
2✔
943
        id = createPaletteId();
2✔
944

945
    if (m_palettes.contains(id) == true || id == QLCPalette::invalidId())
2✔
946
    {
947
        qWarning() << Q_FUNC_INFO << "a palette with ID" << id << "already exists!";
×
948
        return false;
×
949
    }
950
    else
951
    {
952
        palette->setID(id);
2✔
953
        m_palettes[id] = palette;
2✔
954

955
        emit paletteAdded(id);
2✔
956
        setModified();
2✔
957
    }
958

959
    return true;
2✔
960
}
961

962
bool Doc::deletePalette(quint32 id)
3✔
963
{
964
    if (m_palettes.contains(id) == true)
3✔
965
    {
966
        QLCPalette *palette = m_palettes.take(id);
2✔
967
        Q_ASSERT(palette != NULL);
2✔
968

969
        emit paletteRemoved(id);
2✔
970
        setModified();
2✔
971
        delete palette;
2✔
972

973
        return true;
2✔
974
    }
975
    else
976
    {
977
        qWarning() << Q_FUNC_INFO << "No palette with id" << id;
1✔
978
        return false;
1✔
979
    }
980
}
981

982
QLCPalette *Doc::palette(quint32 id) const
2✔
983
{
984
    return m_palettes.value(id, NULL);
2✔
985
}
986

987
QList<QLCPalette *> Doc::palettes() const
8✔
988
{
989
    return m_palettes.values();
8✔
990
}
991

992
quint32 Doc::createPaletteId()
2✔
993
{
994
    while (m_palettes.contains(m_latestPaletteId) == true ||
5✔
995
           m_latestPaletteId == FixtureGroup::invalidId())
2✔
996
    {
997
        m_latestPaletteId++;
1✔
998
    }
999

1000
    return m_latestPaletteId;
2✔
1001
}
1002

1003
/*****************************************************************************
1004
 * Functions
1005
 *****************************************************************************/
1006

1007
quint32 Doc::createFunctionId()
243✔
1008
{
1009
    /* This results in an endless loop if there are UINT_MAX-1 functions. That,
1010
       however, seems a bit unlikely. Are there even 4294967295-1 functions in
1011
       total in the whole world? */
1012
    while (m_functions.contains(m_latestFunctionId) == true ||
653✔
1013
           m_latestFunctionId == Fixture::invalidId())
243✔
1014
    {
1015
        m_latestFunctionId++;
167✔
1016
    }
1017

1018
    return m_latestFunctionId;
243✔
1019
}
1020

1021
bool Doc::addFunction(Function* func, quint32 id)
256✔
1022
{
1023
    Q_ASSERT(func != NULL);
256✔
1024

1025
    if (id == Function::invalidId())
256✔
1026
        id = createFunctionId();
243✔
1027

1028
    if (m_functions.contains(id) == true || id == Fixture::invalidId())
256✔
1029
    {
1030
        qWarning() << Q_FUNC_INFO << "a function with ID" << id << "already exists!";
2✔
1031
        return false;
2✔
1032
    }
1033
    else
1034
    {
1035
        // Listen to function changes
1036
        connect(func, SIGNAL(changed(quint32)),
254✔
1037
                this, SLOT(slotFunctionChanged(quint32)));
1038

1039
        // Listen to function name changes
1040
        connect(func, SIGNAL(nameChanged(quint32)),
254✔
1041
                this, SLOT(slotFunctionNameChanged(quint32)));
1042

1043
        // Make the function listen to fixture removals
1044
        connect(this, SIGNAL(fixtureRemoved(quint32)),
254✔
1045
                func, SLOT(slotFixtureRemoved(quint32)));
1046

1047
        // Place the function in the map and assign it the new ID
1048
        m_functions[id] = func;
254✔
1049
        func->setID(id);
254✔
1050
        emit functionAdded(id);
254✔
1051
        setModified();
254✔
1052

1053
        return true;
254✔
1054
    }
1055
}
1056

1057
QList <Function*> Doc::functions() const
49✔
1058
{
1059
    return m_functions.values();
49✔
1060
}
1061

1062
QList<Function *> Doc::functionsByType(Function::Type type) const
1✔
1063
{
1064
    QList <Function*> list;
1✔
1065
    foreach (Function *f, m_functions)
10✔
1066
    {
1067
        if (f != NULL && f->type() == type)
9✔
1068
            list.append(f);
5✔
1069
    }
1✔
1070
    return list;
1✔
UNCOV
1071
}
×
1072

1073
Function *Doc::functionByName(QString name)
×
1074
{
1075
    foreach (Function *f, m_functions)
×
1076
    {
1077
        if (f != NULL && f->name() == name)
×
UNCOV
1078
            return f;
×
UNCOV
1079
    }
×
1080
    return NULL;
×
1081
}
1082

1083
bool Doc::deleteFunction(quint32 id)
10✔
1084
{
1085
    if (m_functions.contains(id) == true)
10✔
1086
    {
1087
        Function* func = m_functions.take(id);
8✔
1088
        Q_ASSERT(func != NULL);
8✔
1089

1090
        if (m_startupFunctionId == id)
8✔
1091
            m_startupFunctionId = Function::invalidId();
×
1092

1093
        emit functionRemoved(id);
8✔
1094
        setModified();
8✔
1095
        delete func;
8✔
1096

1097
        return true;
8✔
1098
    }
1099
    else
1100
    {
1101
        qWarning() << Q_FUNC_INFO << "No function with id" << id;
2✔
1102
        return false;
2✔
1103
    }
1104
}
1105

1106
Function* Doc::function(quint32 id) const
884✔
1107
{
1108
    return m_functions.value(id, NULL);
884✔
1109
}
1110

1111
quint32 Doc::nextFunctionID()
2✔
1112
{
1113
    quint32 tmpFID = m_latestFunctionId;
2✔
1114
    while (m_functions.contains(tmpFID) == true ||
5✔
1115
           tmpFID == Fixture::invalidId())
2✔
1116
    {
1117
        tmpFID++;
1✔
1118
    }
1119

1120
    return tmpFID;
2✔
1121
}
1122

1123
void Doc::setStartupFunction(quint32 fid)
1✔
1124
{
1125
    m_startupFunctionId = fid;
1✔
1126
}
1✔
1127

1128
quint32 Doc::startupFunction()
3✔
1129
{
1130
    return m_startupFunctionId;
3✔
1131
}
1132

1133
QList<quint32> Doc::getUsage(quint32 fid)
7✔
1134
{
1135
    QList<quint32> usageList;
7✔
1136

1137
    foreach (Function *f, m_functions)
70✔
1138
    {
1139
        if (f->id() == fid)
63✔
1140
            continue;
6✔
1141

1142
        switch(f->type())
57✔
1143
        {
1144
            case Function::CollectionType:
7✔
1145
            {
1146
                Collection *c = qobject_cast<Collection *>(f);
7✔
1147
                int pos = c->functions().indexOf(fid);
7✔
1148
                if (pos != -1)
7✔
1149
                {
1150
                    usageList.append(f->id());
2✔
1151
                    usageList.append(pos);
2✔
1152
                }
1153
            }
1154
            break;
7✔
1155
            case Function::ChaserType:
6✔
1156

1157
            {
1158
                Chaser *c = qobject_cast<Chaser *>(f);
6✔
1159
                for (int i = 0; i < c->stepsCount(); i++)
18✔
1160
                {
1161
                    ChaserStep *cs = c->stepAt(i);
12✔
1162
                    if (cs->fid == fid)
12✔
1163
                    {
1164
                        usageList.append(f->id());
2✔
1165
                        usageList.append(i);
2✔
1166
                    }
1167
                }
1168
            }
1169
            break;
6✔
1170
            case Function::SequenceType:
7✔
1171
            {
1172
                Sequence *s = qobject_cast<Sequence *>(f);
7✔
1173
                if (s->boundSceneID() == fid)
7✔
1174
                {
1175
                    usageList.append(f->id());
1✔
1176
                    usageList.append(0);
1✔
1177
                }
1178
            }
1179
            break;
7✔
1180
            case Function::ScriptType:
7✔
1181
            {
1182
                Script *s = qobject_cast<Script *>(f);
7✔
1183
                QList<quint32> l = s->functionList();
7✔
1184
                for (int i = 0; i < l.count(); i+=2)
14✔
1185
                {
1186
                    if (l.at(i) == fid)
7✔
1187
                    {
1188
                        if (i + 1 >= l.count()) {
1✔
1189
                            qDebug() << "Doc::getUsage: Index entry missing on " << f->name();
×
1190
                            break;
×
1191
                        }
1192
                        usageList.append(s->id());
1✔
1193
                        usageList.append(l.at(i + 1)); // line number
1✔
1194
                    }
1195
                }
1196
            }
7✔
1197
            break;
7✔
UNCOV
1198
            case Function::ShowType:
×
1199
            {
UNCOV
1200
                Show *s = qobject_cast<Show *>(f);
×
1201
                foreach (Track *t, s->tracks())
×
1202
                {
1203
                    foreach (ShowFunction *sf, t->showFunctions())
×
1204
                    {
1205
                        if (sf->functionID() == fid)
×
1206
                        {
1207
                            usageList.append(f->id());
×
1208
                            usageList.append(t->id());
×
1209
                        }
UNCOV
1210
                    }
×
UNCOV
1211
                }
×
1212
            }
1213
            break;
×
1214
            default:
30✔
1215
            break;
30✔
1216
        }
1217
    }
7✔
1218

1219
    return usageList;
7✔
UNCOV
1220
}
×
1221

1222
void Doc::slotFunctionChanged(quint32 fid)
111✔
1223
{
1224
    setModified();
111✔
1225
    emit functionChanged(fid);
111✔
1226
}
111✔
1227

1228
void Doc::slotFunctionNameChanged(quint32 fid)
2✔
1229
{
1230
    setModified();
2✔
1231
    emit functionNameChanged(fid);
2✔
1232
}
2✔
1233

1234
/*********************************************************************
1235
 * Monitor Properties
1236
 *********************************************************************/
1237

1238
MonitorProperties *Doc::monitorProperties()
1✔
1239
{
1240
    if (m_monitorProps == NULL)
1✔
1241
        m_monitorProps = new MonitorProperties();
1✔
1242

1243
    return m_monitorProps;
1✔
1244
}
1245

1246
/*****************************************************************************
1247
 * Load & Save
1248
 *****************************************************************************/
1249

1250
bool Doc::loadXML(QXmlStreamReader &doc, bool loadIO)
2✔
1251
{
1252
    clearErrorLog();
2✔
1253

1254
    if (doc.name() != KXMLQLCEngine)
2✔
1255
    {
1256
        qWarning() << Q_FUNC_INFO << "Engine node not found";
1✔
1257
        return false;
1✔
1258
    }
1259

1260
    m_loadStatus = Loading;
1✔
1261
    emit loading();
1✔
1262

1263
    if (doc.attributes().hasAttribute(KXMLQLCStartupFunction))
2✔
1264
    {
1265
        quint32 sID = doc.attributes().value(KXMLQLCStartupFunction).toString().toUInt();
×
1266
        if (sID != Function::invalidId())
×
1267
            setStartupFunction(sID);
×
1268
    }
1269

1270
    while (doc.readNextStartElement())
17✔
1271
    {
1272
        //qDebug() << "Doc tag:" << doc.name();
1273
        if (doc.name() == KXMLFixture)
16✔
1274
        {
1275
            Fixture::loader(doc, this);
3✔
1276
        }
1277
        else if (doc.name() == KXMLQLCFixtureGroup)
13✔
1278
        {
1279
            FixtureGroup::loader(doc, this);
3✔
1280
        }
1281
        else if (doc.name() == KXMLQLCChannelsGroup)
10✔
1282
        {
1283
            ChannelsGroup::loader(doc, this);
×
1284
        }
1285
        else if (doc.name() == KXMLQLCPalette)
10✔
1286
        {
1287
            QLCPalette::loader(doc, this);
×
1288
            doc.skipCurrentElement();
×
1289
        }
1290
        else if (doc.name() == KXMLQLCFunction)
10✔
1291
        {
1292
            //qDebug() << doc.attributes().value("Name").toString();
1293
            Function::loader(doc, this);
4✔
1294
        }
1295
        else if (doc.name() == KXMLQLCBus)
6✔
1296
        {
1297
            /* LEGACY */
1298
            Bus::instance()->loadXML(doc);
5✔
1299
        }
1300
        else if (doc.name() == KXMLIOMap && loadIO)
2✔
1301
        {
1302
            m_ioMap->loadXML(doc);
×
1303
        }
1304
        else if (doc.name() == KXMLQLCMonitorProperties)
1✔
1305
        {
1306
            monitorProperties()->loadXML(doc, this);
×
1307
        }
1308
        else
1309
        {
1310
            qWarning() << Q_FUNC_INFO << "Unknown engine tag:" << doc.name();
1✔
1311
            doc.skipCurrentElement();
1✔
1312
        }
1313
    }
1314

1315
    postLoad();
1✔
1316

1317
    m_loadStatus = Loaded;
1✔
1318
    emit loaded();
1✔
1319

1320
    return true;
1✔
1321
}
1322

1323
bool Doc::saveXML(QXmlStreamWriter *doc)
1✔
1324
{
1325
    Q_ASSERT(doc != NULL);
1✔
1326

1327
    /* Create the master Engine node */
1328
    doc->writeStartElement(KXMLQLCEngine);
2✔
1329

1330
    if (startupFunction() != Function::invalidId())
1✔
1331
        doc->writeAttribute(KXMLQLCStartupFunction, QString::number(startupFunction()));
2✔
1332

1333
    m_ioMap->saveXML(doc);
1✔
1334

1335
    /* Write fixtures into an XML document */
1336
    QListIterator <Fixture*> fxit(fixtures());
1✔
1337
    while (fxit.hasNext() == true)
4✔
1338
    {
1339
        Fixture *fxi(fxit.next());
3✔
1340
        Q_ASSERT(fxi != NULL);
3✔
1341
        fxi->saveXML(doc);
3✔
1342
    }
1343

1344
    /* Write fixture groups into an XML document */
1345
    QListIterator <FixtureGroup*> grpit(fixtureGroups());
1✔
1346
    while (grpit.hasNext() == true)
3✔
1347
    {
1348
        FixtureGroup *grp(grpit.next());
2✔
1349
        Q_ASSERT(grp != NULL);
2✔
1350
        grp->saveXML(doc);
2✔
1351
    }
1352

1353
    /* Write channel groups into an XML document */
1354
    QListIterator <ChannelsGroup*> chanGroups(channelsGroups());
1✔
1355
    while (chanGroups.hasNext() == true)
1✔
1356
    {
1357
        ChannelsGroup *grp(chanGroups.next());
×
UNCOV
1358
        Q_ASSERT(grp != NULL);
×
1359
        grp->saveXML(doc);
×
1360
    }
1361

1362
    /* Write palettes into an XML document */
1363
    QListIterator <QLCPalette*> paletteIt(palettes());
1✔
1364
    while (paletteIt.hasNext() == true)
1✔
1365
    {
1366
        QLCPalette *palette(paletteIt.next());
×
UNCOV
1367
        Q_ASSERT(palette != NULL);
×
1368
        palette->saveXML(doc);
×
1369
    }
1370

1371
    /* Write functions into an XML document */
1372
    QListIterator <Function*> funcit(functions());
1✔
1373
    while (funcit.hasNext() == true)
5✔
1374
    {
1375
        Function *func(funcit.next());
4✔
1376
        Q_ASSERT(func != NULL);
4✔
1377
        func->saveXML(doc);
4✔
1378
    }
1379

1380
    if (m_monitorProps != NULL)
1✔
1381
        m_monitorProps->saveXML(doc, this);
1✔
1382

1383
    /* End the <Engine> tag */
1384
    doc->writeEndElement();
1✔
1385

1386
    return true;
1✔
1387
}
1✔
1388

1389
void Doc::appendToErrorLog(QString error)
8✔
1390
{
1391
    if (m_errorLog.contains(error))
8✔
1392
        return;
5✔
1393

1394
    m_errorLog.append(error);
3✔
1395
    m_errorLog.append("<br>");
3✔
1396
}
1397

1398
void Doc::clearErrorLog()
2✔
1399
{
1400
    m_errorLog = "";
2✔
1401
}
2✔
1402

1403
QString Doc::errorLog()
1✔
1404
{
1405
    return m_errorLog;
1✔
1406
}
1407

1408
void Doc::postLoad()
1✔
1409
{
1410
    QListIterator <Function*> functionit(functions());
1✔
1411
    while (functionit.hasNext() == true)
5✔
1412
    {
1413
        Function* function(functionit.next());
4✔
1414
        Q_ASSERT(function != NULL);
4✔
1415
        function->postLoad();
4✔
1416
    }
1417
}
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