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

mcallegari / qlcplus / 22259780172

21 Feb 2026 03:58PM UTC coverage: 33.978%. Remained the same
22259780172

push

github

mcallegari
engine: reset startup Function on project change (fix #1963)

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

1 existing line in 1 file now uncovered.

17651 of 51949 relevant lines covered (33.98%)

19782.5 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(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)
196✔
60
    : QObject(parent)
61
    , m_workspacePath("")
196✔
62
    , m_fixtureDefCache(new QLCFixtureDefCache)
196✔
63
    , m_modifiersCache(new QLCModifiersCache)
196✔
64
    , m_rgbScriptsCache(new RGBScriptsCache(this))
196✔
65
    , m_ioPluginCache(new IOPluginCache(this))
196✔
66
    , m_audioPluginCache(new AudioPluginCache(this))
196✔
67
    , m_masterTimer(new MasterTimer(this))
196✔
68
    , m_ioMap(new InputOutputMap(this, universes))
196✔
69
    , m_monitorProps(NULL)
196✔
70
    , m_mode(Design)
196✔
71
    , m_kiosk(false)
196✔
72
    , m_loadStatus(Cleared)
196✔
73
    , m_clipboard(new QLCClipboard(this))
196✔
74
    , m_fixturesListCacheUpToDate(false)
196✔
75
    , m_latestFixtureId(0)
196✔
76
    , m_latestFixtureGroupId(0)
196✔
77
    , m_latestChannelsGroupId(0)
196✔
78
    , m_latestPaletteId(0)
196✔
79
    , m_latestFunctionId(0)
196✔
80
    , m_startupFunctionId(Function::invalidId())
588✔
81
{
82
    Bus::init(this);
196✔
83
    resetModified();
196✔
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);
196✔
89
    m_autosaveTimer.setSingleShot(true);
196✔
90

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

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

99
    clearContents();
196✔
100

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

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

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

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

118
    delete m_rgbScriptsCache;
196✔
119
    m_rgbScriptsCache = NULL;
196✔
120
}
352✔
121

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

126
    m_clipboard->resetContents();
486✔
127

128
    if (m_monitorProps != NULL)
486✔
129
        m_monitorProps->reset();
9✔
130

131
    destroyAudioCapture();
486✔
132

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

144
    // Delete all palettes
145
    QListIterator <quint32> palIt(m_palettes.keys());
486✔
146
    while (palIt.hasNext() == true)
486✔
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());
486✔
155
    while (grpchans.hasNext() == true)
490✔
156
    {
157
        ChannelsGroup* grp = m_channelsGroups.take(grpchans.next());
4✔
158
        emit channelsGroupRemoved(grp->id());
4✔
159
        delete grp;
4✔
160
    }
161

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

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

183
    m_orderedGroups.clear();
486✔
184

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

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

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

202
QString Doc::workspacePath() const
105✔
203
{
204
    return m_workspacePath;
105✔
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,663✔
237
{
238
    return m_fixtureDefCache;
1,663✔
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
69✔
252
{
253
    return m_rgbScriptsCache;
69✔
254
}
255

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

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

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

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

276
QSharedPointer<AudioCapture> Doc::audioInputCapture() const
17✔
277
{
278
    if (!m_inputCapture)
17✔
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;
17✔
290
}
291

292
void Doc::destroyAudioCapture()
486✔
293
{
294
    if (m_inputCapture.isNull() == false)
486✔
295
    {
296
        qDebug() << "Destroying audio capture";
2✔
297
        m_inputCapture.clear();
2✔
298
    }
299
}
486✔
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,260✔
315
{
316
    m_modified = true;
1,260✔
317
    m_autosaveTimer.start();
1,260✔
318
    emit modified(true);
1,260✔
319
}
1,260✔
320

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

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

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

338
    m_mode = mode;
22✔
339

340
    if (mode == Operate)
22✔
341
        m_autosaveTimer.stop();
17✔
342
    else if (m_modified)
5✔
343
        m_autosaveTimer.start();
4✔
344

345
    // Run startup function
346
    if (m_mode == Operate && m_startupFunctionId != Function::invalidId())
22✔
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);
22✔
362
}
363

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

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

374
bool Doc::isKiosk() const
198✔
375
{
376
    return m_kiosk;
198✔
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,791✔
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,216✔
398
           m_latestFixtureId == Fixture::invalidId())
16,791✔
399
    {
400
        m_latestFixtureId++;
16,634✔
401
    }
402

403
    return m_latestFixtureId;
16,791✔
404
}
405

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

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

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

417
    if (m_fixtures.contains(id) == true || id == Fixture::invalidId())
416✔
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,001✔
425
         i < fixture->universeAddress() + fixture->channels(); i++)
3,001✔
426
    {
427
        if (m_addresses.contains(i))
2,590✔
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);
411✔
435
    m_fixtures.insert(id, fixture);
411✔
436
    m_fixturesListCacheUpToDate = false;
411✔
437

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

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

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

452
    if (uni >= inputOutputMap()->universesCount())
411✔
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();
411✔
461

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

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

471
        if (crossUniverse)
2,586✔
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,586✔
479
            universes.at(uni)->setChannelCapability(addr, channel->group(), Universe::HTP);
×
480
        else if (forcedLTP.contains(int(i)))
2,586✔
481
            universes.at(uni)->setChannelCapability(addr, channel->group(), Universe::LTP);
1✔
482
        else
483
            universes.at(uni)->setChannelCapability(addr, channel->group());
2,585✔
484

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

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

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

497
    return true;
411✔
498
}
411✔
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)
1✔
536
{
537
    // Delete all fixture instances
538
    QListIterator <quint32> fxit(m_fixtures.keys());
1✔
539
    while (fxit.hasNext() == true)
4✔
540
    {
541
        Fixture* fxi = m_fixtures.take(fxit.next());
3✔
542
        disconnect(fxi, SIGNAL(changed(quint32)),
3✔
543
                   this, SLOT(slotFixtureChanged(quint32)));
544
        delete fxi;
3✔
545
        m_fixturesListCacheUpToDate = false;
3✔
546
    }
547
    m_latestFixtureId = 0;
1✔
548
    m_addresses.clear();
1✔
549

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

561
        if (fixture->fixtureDef() == NULL ||
5✔
562
            (fixture->fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
5✔
563
             fixture->fixtureDef()->model() == KXMLFixtureGeneric))
3✔
564
        {
565
            // Generic dimmers just need to know the number of channels
566
            newFixture->setChannels(fixture->channels());
1✔
567
        }
568
        else if (fixture->fixtureDef() == NULL ||
2✔
569
            (fixture->fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
2✔
570
             fixture->fixtureDef()->model() == KXMLFixtureRGBPanel))
1✔
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(),
2✔
583
                                                               fixture->fixtureDef()->model());
2✔
584
            QLCFixtureMode *mode = NULL;
1✔
585
            if (def != NULL)
1✔
586
                mode = def->mode(fixture->fixtureMode()->name());
1✔
587
            newFixture->setFixtureDefinition(def, mode);
1✔
588
        }
589

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

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

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

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

608
        /* Keep track of fixture addresses */
609
        for (uint i = newFixture->universeAddress();
2✔
610
             i < newFixture->universeAddress() + newFixture->channels(); i++)
13✔
611
        {
612
            m_addresses[i] = id;
11✔
613
        }
614
        m_latestFixtureId = id;
2✔
615
    }
1✔
616
    return true;
1✔
617
}
1✔
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
73✔
663
{
664
    if (!m_fixturesListCacheUpToDate)
73✔
665
    {
666
        // Sort fixtures by id
667
        QMap <quint32, Fixture*> fixturesMap;
42✔
668
        QHashIterator <quint32, Fixture*> hashIt(m_fixtures);
42✔
669
        while (hashIt.hasNext())
78✔
670
        {
671
            hashIt.next();
36✔
672
            fixturesMap.insert(hashIt.key(), hashIt.value());
36✔
673
        }
674
        const_cast<QList<Fixture*>&>(m_fixturesListCache) = fixturesMap.values();
42✔
675
        const_cast<bool&>(m_fixturesListCacheUpToDate) = true;
42✔
676
    }
42✔
677
    return m_fixturesListCache;
73✔
678
}
679

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

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

690
quint32 Doc::fixtureForAddress(quint32 universeAddress) const
79✔
691
{
692
    return m_addresses.value(universeAddress, Fixture::invalidId());
79✔
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)
15✔
762
{
763
    Q_ASSERT(grp != NULL);
15✔
764

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

769
    if (m_fixtureGroups.contains(id) == true || id == FixtureGroup::invalidId())
15✔
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);
13✔
777
        m_fixtureGroups[id] = grp;
13✔
778

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

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

786
        return true;
13✔
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
35✔
816
{
817
    return m_fixtureGroups.values();
35✔
818
}
819

820
quint32 Doc::createFixtureGroupId()
8✔
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 ||
20✔
826
           m_latestFixtureGroupId == FixtureGroup::invalidId())
8✔
827
    {
828
        m_latestFixtureGroupId++;
4✔
829
    }
830

831
    return m_latestFixtureGroupId;
8✔
832
}
833

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

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

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

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

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

859
     return true;
5✔
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
18✔
911
{
912
    QList <ChannelsGroup*> orderedList;
18✔
913

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

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

929
    return m_latestChannelsGroupId;
4✔
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
        m_palettes[id] = palette;
2✔
953

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

958
    return true;
2✔
959
}
960

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

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

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

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

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

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

999
    return m_latestPaletteId;
2✔
1000
}
1001

1002
/*****************************************************************************
1003
 * Functions
1004
 *****************************************************************************/
1005

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

1017
    return m_latestFunctionId;
247✔
1018
}
1019

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

1024
    if (id == Function::invalidId())
260✔
1025
        id = createFunctionId();
247✔
1026

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

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

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

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

1052
        return true;
258✔
1053
    }
1054
}
1055

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

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

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

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

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

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

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

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

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

1119
    return tmpFID;
2✔
1120
}
1121

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

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

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

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

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

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

1218
    return usageList;
7✔
1219
}
×
1220

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

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

1233
/*********************************************************************
1234
 * Monitor Properties
1235
 *********************************************************************/
1236

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

1242
    return m_monitorProps;
1✔
1243
}
1244

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

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

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

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

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

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

1314
    postLoad();
1✔
1315

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

1319
    return true;
1✔
1320
}
1321

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

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

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

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

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

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

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

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

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

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

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

1385
    return true;
1✔
1386
}
1✔
1387

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

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

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

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

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