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

mcallegari / qlcplus / 19144422256

06 Nov 2025 05:33PM UTC coverage: 34.256% (-0.1%) from 34.358%
19144422256

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

90.27
/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_latestFixtureId = 0;
486✔
187
    m_latestFixtureGroupId = 0;
486✔
188
    m_latestChannelsGroupId = 0;
486✔
189
    m_latestPaletteId = 0;
486✔
190
    m_addresses.clear();
486✔
191
    m_loadStatus = Cleared;
486✔
192

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

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

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

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

211
    QFileInfo f(filePath);
6✔
212

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

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

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

231
/*****************************************************************************
232
 * Engine components
233
 *****************************************************************************/
234

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

337
    m_mode = mode;
22✔
338

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

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

360
    emit modeChanged(m_mode);
22✔
361
}
362

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

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

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

378
/*********************************************************************
379
 * Clipboard
380
 *********************************************************************/
381

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

387
/*****************************************************************************
388
 * Fixtures
389
 *****************************************************************************/
390

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

402
    return m_latestFixtureId;
16,791✔
403
}
404

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

496
    return true;
411✔
497
}
411✔
498

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

658
    return true;
1✔
659
}
1✔
660

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

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

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

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

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

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

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

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

721
    return totalPowerConsumption;
4✔
722
}
4✔
723

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

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

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

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

756
/*****************************************************************************
757
 * Fixture groups
758
 *****************************************************************************/
759

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

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

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

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

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

785
        return true;
13✔
786
    }
787
}
788

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

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

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

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

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

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

830
    return m_latestFixtureGroupId;
8✔
831
}
832

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

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

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

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

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

858
     return true;
5✔
859
}
860

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

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

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

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

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

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

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

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

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

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

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

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

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

928
    return m_latestChannelsGroupId;
4✔
929
}
930

931
/*********************************************************************
932
 * Palettes
933
 *********************************************************************/
934

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

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

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

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

957
    return true;
2✔
958
}
959

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

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

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

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

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

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

998
    return m_latestPaletteId;
2✔
999
}
1000

1001
/*****************************************************************************
1002
 * Functions
1003
 *****************************************************************************/
1004

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

1016
    return m_latestFunctionId;
247✔
1017
}
1018

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

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

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

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

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

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

1051
        return true;
258✔
1052
    }
1053
}
1054

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

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

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

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

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

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

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

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

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

1118
    return tmpFID;
2✔
1119
}
1120

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

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

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

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

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

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

1217
    return usageList;
7✔
1218
}
×
1219

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

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

1232
/*********************************************************************
1233
 * Monitor Properties
1234
 *********************************************************************/
1235

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

1241
    return m_monitorProps;
1✔
1242
}
1243

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

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

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

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

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

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

1313
    postLoad();
1✔
1314

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

1318
    return true;
1✔
1319
}
1320

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

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

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

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

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

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

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

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

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

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

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

1384
    return true;
1✔
1385
}
1✔
1386

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

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

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

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

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