• 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

90.16
/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(6, 0, 0)
52
 #include "audiocapture_qt5.h"
53
#else
54
 #include "audiocapture_qt6.h"
55
#endif
56

57
#define AUTOSAVE_TIMEOUT    30 // seconds
58

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

88
    m_autosaveTimer.setInterval(AUTOSAVE_TIMEOUT * 1000);
210✔
89
    m_autosaveTimer.setSingleShot(true);
210✔
90

91
    connect(&m_autosaveTimer, SIGNAL(timeout()), this, SIGNAL(needAutosave()));
210✔
92
}
210✔
93

94
Doc::~Doc()
368✔
95
{
96
    delete m_masterTimer;
210✔
97
    m_masterTimer = NULL;
210✔
98

99
    clearContents();
210✔
100

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

109
    delete m_ioPluginCache;
210✔
110
    m_ioPluginCache = NULL;
210✔
111

112
    delete m_modifiersCache;
210✔
113
    m_modifiersCache = NULL;
210✔
114

115
    delete m_fixtureDefCache;
210✔
116
    m_fixtureDefCache = NULL;
210✔
117

118
    delete m_rgbScriptsCache;
210✔
119
    m_rgbScriptsCache = NULL;
210✔
120
}
368✔
121

122
void Doc::clearContents()
512✔
123
{
124
    emit clearing();
512✔
125

126
    m_clipboard->resetContents();
512✔
127

128
    if (m_monitorProps != NULL)
512✔
129
        m_monitorProps->reset();
15✔
130

131
    destroyAudioCapture();
512✔
132

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

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

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

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

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

183
    m_orderedGroups.clear();
512✔
184

185
    m_latestFunctionId = 0;
512✔
186
    m_startupFunctionId = Function::invalidId();
512✔
187
    m_latestFixtureId = 0;
512✔
188
    m_latestFixtureGroupId = 0;
512✔
189
    m_latestChannelsGroupId = 0;
512✔
190
    m_latestPaletteId = 0;
512✔
191
    m_addresses.clear();
512✔
192
    m_loadStatus = Cleared;
512✔
193

194
    emit cleared();
512✔
195
}
512✔
196

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

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

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

212
    QFileInfo f(filePath);
6✔
213

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

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

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

232
/*****************************************************************************
233
 * Engine components
234
 *****************************************************************************/
235

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

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

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

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

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

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

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

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

276
QSharedPointer<AudioCapture> Doc::audioInputCapture() const
48✔
277
{
278
    if (!m_inputCapture)
48✔
279
    {
280
        qDebug() << "Creating new audio capture";
2✔
281
        m_inputCapture = QSharedPointer<AudioCapture>(
4✔
282
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
283
            new AudioCaptureQt5()
284
#else
285
            new AudioCaptureQt6()
2✔
286
#endif
287
            );
2✔
288
    }
289
    return m_inputCapture;
48✔
290
}
291

292
void Doc::destroyAudioCapture()
512✔
293
{
294
    if (m_inputCapture.isNull() == false)
512✔
295
    {
296
        qDebug() << "Destroying audio capture";
2✔
297
        m_inputCapture.clear();
2✔
298
    }
299
}
512✔
300

301
/*****************************************************************************
302
 * Modified status
303
 *****************************************************************************/
304
Doc::LoadStatus Doc::loadStatus() const
2✔
305
{
306
    return m_loadStatus;
2✔
307
}
308

309
bool Doc::isModified() const
29✔
310
{
311
    return m_modified;
29✔
312
}
313

314
void Doc::setModified()
1,309✔
315
{
316
    m_modified = true;
1,309✔
317
    m_autosaveTimer.start();
1,309✔
318
    emit modified(true);
1,309✔
319
}
1,309✔
320

321
void Doc::resetModified()
225✔
322
{
323
    m_modified = false;
225✔
324
    m_autosaveTimer.stop();
225✔
325
    emit modified(false);
225✔
326
}
225✔
327

328
/*****************************************************************************
329
 * Main operating mode
330
 *****************************************************************************/
331

332
void Doc::setMode(Doc::Mode mode)
28✔
333
{
334
    /* Don't do mode switching twice */
335
    if (m_mode == mode)
28✔
336
        return;
3✔
337

338
    m_mode = mode;
25✔
339

340
    if (mode == Operate)
25✔
341
        m_autosaveTimer.stop();
19✔
342
    else if (m_modified)
6✔
343
        m_autosaveTimer.start();
5✔
344

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

361
    emit modeChanged(m_mode);
25✔
362
}
363

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

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

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

379
/*********************************************************************
380
 * Clipboard
381
 *********************************************************************/
382

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

388
/*****************************************************************************
389
 * Fixtures
390
 *****************************************************************************/
391

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

403
    return m_latestFixtureId;
16,810✔
404
}
405

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

410
    quint32 i;
411
    quint32 uni = fixture->universe();
435✔
412

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

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

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

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

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

442
    /* Keep track of fixture addresses */
443
    for (i = fixture->universeAddress();
430✔
444
         i < fixture->universeAddress() + fixture->channels(); i++)
3,074✔
445
    {
446
        m_addresses[i] = id;
2,644✔
447
    }
448

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

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

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

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

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

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

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

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

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

494
    emit fixtureAdded(id);
430✔
495
    setModified();
430✔
496

497
    return true;
430✔
498
}
430✔
499

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

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

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

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

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

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

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

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

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

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

601
        m_fixtures.insert(id, newFixture);
8✔
602
        m_fixturesListCacheUpToDate = false;
8✔
603

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

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

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

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

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

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

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

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

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

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

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

659
    return true;
1✔
660
}
1✔
661

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

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

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

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

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

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

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

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

722
    return totalPowerConsumption;
4✔
723
}
4✔
724

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

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

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

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

757
/*****************************************************************************
758
 * Fixture groups
759
 *****************************************************************************/
760

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

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

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

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

783
        emit fixtureGroupAdded(id);
14✔
784
        setModified();
14✔
785

786
        return true;
14✔
787
    }
788
}
789

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

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

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

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

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

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

831
    return m_latestFixtureGroupId;
9✔
832
}
833

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

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

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

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

856
     emit channelsGroupAdded(id);
6✔
857
     setModified();
6✔
858

859
     return true;
6✔
860
}
861

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

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

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

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

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

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

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

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

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

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

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

914
    for (int i = 0; i < m_orderedGroups.count(); i++)
48✔
915
    {
916
        orderedList.append(m_channelsGroups[m_orderedGroups.at(i)]);
25✔
917
    }
918
    return orderedList;
23✔
919
}
×
920

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

929
    return m_latestChannelsGroupId;
5✔
930
}
931

932
/*********************************************************************
933
 * Palettes
934
 *********************************************************************/
935

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

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

944
    if (m_palettes.contains(id) == true || id == QLCPalette::invalidId())
2✔
945
    {
946
        qWarning() << Q_FUNC_INFO << "a palette with ID" << id << "already exists!";
×
947
        return false;
×
948
    }
949
    else
950
    {
951
        palette->setID(id);
2✔
952
        palette->setTemporary(false);
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()
255✔
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 ||
682✔
1013
           m_latestFunctionId == Fixture::invalidId())
255✔
1014
    {
1015
        m_latestFunctionId++;
172✔
1016
    }
1017

1018
    return m_latestFunctionId;
255✔
1019
}
1020

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

1025
    if (id == Function::invalidId())
268✔
1026
        id = createFunctionId();
255✔
1027

1028
    if (m_functions.contains(id) == true || id == Fixture::invalidId())
268✔
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)),
266✔
1037
                this, SLOT(slotFunctionChanged(quint32)));
1038

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

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

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

1053
        return true;
266✔
1054
    }
1055
}
1056

1057
QList <Function*> Doc::functions() const
54✔
1058
{
1059
    return m_functions.values();
54✔
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✔
1071
}
×
1072

1073
Function *Doc::functionByName(QString name)
×
1074
{
1075
    foreach (Function *f, m_functions)
×
1076
    {
1077
        if (f != NULL && f->name() == name)
×
1078
            return f;
×
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
944✔
1107
{
1108
    return m_functions.value(id, NULL);
944✔
1109
}
1110

1111
quint32 Doc::nextFunctionID() const
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() const
2✔
1129
{
1130
    return m_startupFunctionId;
2✔
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✔
1198
            case Function::ShowType:
×
1199
            {
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
                        }
1210
                    }
×
1211
                }
×
1212
            }
1213
            break;
×
1214
            default:
30✔
1215
            break;
30✔
1216
        }
1217
    }
7✔
1218

1219
    return usageList;
7✔
1220
}
×
1221

1222
void Doc::slotFunctionChanged(quint32 fid)
116✔
1223
{
1224
    setModified();
116✔
1225
    emit functionChanged(fid);
116✔
1226
}
116✔
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()
7✔
1239
{
1240
    if (m_monitorProps == NULL)
7✔
1241
        m_monitorProps = new MonitorProperties();
2✔
1242

1243
    return m_monitorProps;
7✔
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) const
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()));
×
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());
×
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());
×
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() const
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