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

mcallegari / qlcplus / 14809507932

03 May 2025 09:13AM UTC coverage: 31.879% (+0.03%) from 31.845%
14809507932

push

github

web-flow
Merge pull request #1745 from mcallegari/qmluiqt6

Port QML UI to Qt6

2 of 9 new or added lines in 3 files covered. (22.22%)

3720 existing lines in 174 files now uncovered.

16422 of 51513 relevant lines covered (31.88%)

19079.9 hits per line

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

90.23
/engine/src/doc.cpp
1
/*
2
  Q Light Controller Plus
3
  doc.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7

8
  Licensed under the Apache License, Version 2.0 (the "License");
9
  you may not use this file except in compliance with the License.
10
  You may obtain a copy of the License at
11

12
      http://www.apache.org/licenses/LICENSE-2.0.txt
13

14
  Unless required by applicable law or agreed to in writing, software
15
  distributed under the License is distributed on an "AS IS" BASIS,
16
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
  See the License for the specific language governing permissions and
18
  limitations under the License.
19
*/
20

21
#include <QXmlStreamReader>
22
#include <QXmlStreamWriter>
23
#include <QStringList>
24
#include <QString>
25
#include <QDebug>
26
#include <QList>
27
#include <QTime>
28
#include <QDir>
29
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
30
#include <QRandomGenerator>
31
#endif
32

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

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

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

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

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

101
    clearContents();
177✔
102

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

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

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

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

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

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

128
    m_clipboard->resetContents();
452✔
129

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

133
    destroyAudioCapture();
452✔
134

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

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

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

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

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

185
    m_orderedGroups.clear();
452✔
186

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

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

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

203
QString Doc::getWorkspacePath() const
23✔
204
{
205
    return m_wsPath;
23✔
206
}
207

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

213
    QFileInfo f(filePath);
6✔
214

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

404
    return m_latestFixtureId;
16,781✔
405
}
406

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

498
    return true;
401✔
499
}
401✔
500

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

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

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

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

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

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

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

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

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

595
        m_fixtures.insert(id, newFixture);
2✔
596
        m_fixturesListCacheUpToDate = false;
2✔
597

598
        /* Patch fixture change signals thru Doc */
599
        connect(newFixture, SIGNAL(changed(quint32)),
2✔
600
                this, SLOT(slotFixtureChanged(quint32)));
601

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

613
bool Doc::updateFixtureChannelCapabilities(quint32 id, QList<int> forcedHTP, QList<int> forcedLTP)
2✔
614
{
615
    if (m_fixtures.contains(id) == false)
2✔
616
        return false;
1✔
617

618
    Fixture* fixture = m_fixtures[id];
1✔
619
    // get exclusive access to the universes list
620
    QList<Universe *> universes = inputOutputMap()->claimUniverses();
1✔
621
    Universe *universe = universes.at(fixture->universe());
1✔
622
    quint32 fxAddress = fixture->address();
1✔
623

624
    // Set forced HTP channels
625
    fixture->setForcedHTPChannels(forcedHTP);
1✔
626

627
    // Set forced LTP channels
628
    fixture->setForcedLTPChannels(forcedLTP);
1✔
629

630
    // Update the Fixture Universe with the current channel states
631
    for (quint32 i = 0 ; i < fixture->channels(); i++)
7✔
632
    {
633
        const QLCChannel *channel(fixture->channel(i));
6✔
634

635
        // Inform Universe of any HTP/LTP forcing
636
        if (forcedHTP.contains(int(i)))
6✔
637
            universe->setChannelCapability(fxAddress + i, channel->group(), Universe::HTP);
2✔
638
        else if (forcedLTP.contains(int(i)))
4✔
639
            universe->setChannelCapability(fxAddress + i, channel->group(), Universe::LTP);
1✔
640
        else
641
            universe->setChannelCapability(fxAddress + i, channel->group());
3✔
642

643
        // Apply the default value BEFORE modifiers
644
        universe->setChannelDefaultValue(fxAddress + i, channel->defaultValue());
6✔
645

646
        // Apply a channel modifier, if defined
647
        ChannelModifier *mod = fixture->channelModifier(i);
6✔
648
        universe->setChannelModifier(fxAddress + i, mod);
6✔
649
    }
650

651
    inputOutputMap()->releaseUniverses(true);
1✔
652

653
    return true;
1✔
654
}
1✔
655

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

674
int Doc::fixturesCount() const
×
675
{
676
    return m_fixtures.count();
×
677
}
678

679
Fixture* Doc::fixture(quint32 id) const
796,289✔
680
{
681
    return m_fixtures.value(id, NULL);
796,289✔
682
}
683

684
quint32 Doc::fixtureForAddress(quint32 universeAddress) const
79✔
685
{
686
    return m_addresses.value(universeAddress, Fixture::invalidId());
79✔
687
}
688

689
int Doc::totalPowerConsumption(int& fuzzy) const
4✔
690
{
691
    int totalPowerConsumption = 0;
4✔
692

693
    // Make sure fuzzy starts from zero
694
    fuzzy = 0;
4✔
695

696
    QListIterator <Fixture*> fxit(fixtures());
4✔
697
    while (fxit.hasNext() == true)
14✔
698
    {
699
        Fixture* fxi(fxit.next());
10✔
700
        Q_ASSERT(fxi != NULL);
10✔
701

702
        if (fxi->fixtureMode() != NULL)
10✔
703
        {
704
            QLCPhysical phys = fxi->fixtureMode()->physical();
10✔
705
            if (phys.powerConsumption() > 0)
10✔
706
                totalPowerConsumption += phys.powerConsumption();
7✔
707
            else
708
                fuzzy++;
3✔
709
        }
10✔
710
        else
711
        {
712
            fuzzy++;
×
713
        }
714
    }
715

716
    return totalPowerConsumption;
4✔
717
}
4✔
718

719
void Doc::slotFixtureChanged(quint32 id)
4✔
720
{
721
    /* Keep track of fixture addresses */
722
    Fixture* fxi = fixture(id);
4✔
723

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

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

747
    setModified();
4✔
748
    emit fixtureChanged(id);
4✔
749
}
4✔
750

751
/*****************************************************************************
752
 * Fixture groups
753
 *****************************************************************************/
754

755
bool Doc::addFixtureGroup(FixtureGroup* grp, quint32 id)
15✔
756
{
757
    Q_ASSERT(grp != NULL);
15✔
758

759
    // No ID given, this method can assign one
760
    if (id == FixtureGroup::invalidId())
15✔
761
        id = createFixtureGroupId();
8✔
762

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

773
        /* Patch fixture group change signals thru Doc */
774
        connect(grp, SIGNAL(changed(quint32)),
13✔
775
                this, SLOT(slotFixtureGroupChanged(quint32)));
776

777
        emit fixtureGroupAdded(id);
13✔
778
        setModified();
13✔
779

780
        return true;
13✔
781
    }
782
}
783

784
bool Doc::deleteFixtureGroup(quint32 id)
4✔
785
{
786
    if (m_fixtureGroups.contains(id) == true)
4✔
787
    {
788
        FixtureGroup* grp = m_fixtureGroups.take(id);
2✔
789
        Q_ASSERT(grp != NULL);
2✔
790

791
        emit fixtureGroupRemoved(id);
2✔
792
        setModified();
2✔
793
        delete grp;
2✔
794

795
        return true;
2✔
796
    }
797
    else
798
    {
799
        qWarning() << Q_FUNC_INFO << "No fixture group with id" << id;
2✔
800
        return false;
2✔
801
    }
802
}
803

804
FixtureGroup* Doc::fixtureGroup(quint32 id) const
45✔
805
{
806
    return m_fixtureGroups.value(id, NULL);
45✔
807
}
808

809
QList <FixtureGroup*> Doc::fixtureGroups() const
34✔
810
{
811
    return m_fixtureGroups.values();
34✔
812
}
813

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

825
    return m_latestFixtureGroupId;
8✔
826
}
827

828
void Doc::slotFixtureGroupChanged(quint32 id)
25✔
829
{
830
    setModified();
25✔
831
    emit fixtureGroupChanged(id);
25✔
832
}
25✔
833

834
/*********************************************************************
835
 * Channels groups
836
 *********************************************************************/
837
bool Doc::addChannelsGroup(ChannelsGroup *grp, quint32 id)
4✔
838
{
839
    Q_ASSERT(grp != NULL);
4✔
840

841
    // No ID given, this method can assign one
842
    if (id == ChannelsGroup::invalidId())
4✔
843
        id = createChannelsGroupId();
4✔
844

845
     grp->setId(id);
4✔
846
     m_channelsGroups[id] = grp;
4✔
847
     if (m_orderedGroups.contains(id) == false)
4✔
848
        m_orderedGroups.append(id);
4✔
849

850
     emit channelsGroupAdded(id);
4✔
851
     setModified();
4✔
852

853
     return true;
4✔
854
}
855

856
bool Doc::deleteChannelsGroup(quint32 id)
2✔
857
{
858
    if (m_channelsGroups.contains(id) == true)
2✔
859
    {
860
        ChannelsGroup* grp = m_channelsGroups.take(id);
1✔
861
        Q_ASSERT(grp != NULL);
1✔
862

863
        emit channelsGroupRemoved(id);
1✔
864
        setModified();
1✔
865
        delete grp;
1✔
866

867
        int idx = m_orderedGroups.indexOf(id);
1✔
868
        if (idx != -1)
1✔
869
            m_orderedGroups.takeAt(idx);
1✔
870

871
        return true;
1✔
872
    }
873
    else
874
    {
875
        qWarning() << Q_FUNC_INFO << "No channels group with id" << id;
1✔
876
        return false;
1✔
877
    }
878
}
879

880
bool Doc::moveChannelGroup(quint32 id, int direction)
3✔
881
{
882
    if (direction == 0 || m_orderedGroups.contains(id) == false)
3✔
883
        return false;
1✔
884

885
    int idx = m_orderedGroups.indexOf(id);
2✔
886

887
    if (idx + direction < 0 || idx + direction >= m_orderedGroups.count())
2✔
888
        return false;
1✔
889

890
    qDebug() << Q_FUNC_INFO << m_orderedGroups;
1✔
891
    m_orderedGroups.takeAt(idx);
1✔
892
    m_orderedGroups.insert(idx + direction, id);
1✔
893
    qDebug() << Q_FUNC_INFO << m_orderedGroups;
1✔
894

895
    setModified();
1✔
896
    return true;
1✔
897
}
898

899
ChannelsGroup* Doc::channelsGroup(quint32 id) const
2✔
900
{
901
    return m_channelsGroups.value(id, NULL);
2✔
902
}
903

904
QList <ChannelsGroup*> Doc::channelsGroups() const
15✔
905
{
906
    QList <ChannelsGroup*> orderedList;
15✔
907

908
    for (int i = 0; i < m_orderedGroups.count(); i++)
37✔
909
    {
910
        orderedList.append(m_channelsGroups[m_orderedGroups.at(i)]);
22✔
911
    }
912
    return orderedList;
15✔
913
}
×
914

915
quint32 Doc::createChannelsGroupId()
4✔
916
{
917
    while (m_channelsGroups.contains(m_latestChannelsGroupId) == true ||
10✔
918
           m_latestChannelsGroupId == ChannelsGroup::invalidId())
4✔
919
    {
920
        m_latestChannelsGroupId++;
2✔
921
    }
922

923
    return m_latestChannelsGroupId;
4✔
924
}
925

926
/*********************************************************************
927
 * Palettes
928
 *********************************************************************/
929

930
bool Doc::addPalette(QLCPalette *palette, quint32 id)
2✔
931
{
932
    Q_ASSERT(palette != NULL);
2✔
933

934
    // No ID given, this method can assign one
935
    if (id == QLCPalette::invalidId())
2✔
936
        id = createPaletteId();
2✔
937

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

948
        emit paletteAdded(id);
2✔
949
        setModified();
2✔
950
    }
951

952
    return true;
2✔
953
}
954

955
bool Doc::deletePalette(quint32 id)
3✔
956
{
957
    if (m_palettes.contains(id) == true)
3✔
958
    {
959
        QLCPalette *palette = m_palettes.take(id);
2✔
960
        Q_ASSERT(palette != NULL);
2✔
961

962
        emit paletteRemoved(id);
2✔
963
        setModified();
2✔
964
        delete palette;
2✔
965

966
        return true;
2✔
967
    }
968
    else
969
    {
970
        qWarning() << Q_FUNC_INFO << "No palette with id" << id;
1✔
971
        return false;
1✔
972
    }
973
}
974

975
QLCPalette *Doc::palette(quint32 id) const
2✔
976
{
977
    return m_palettes.value(id, NULL);
2✔
978
}
979

980
QList<QLCPalette *> Doc::palettes() const
8✔
981
{
982
    return m_palettes.values();
8✔
983
}
984

985
quint32 Doc::createPaletteId()
2✔
986
{
987
    while (m_palettes.contains(m_latestPaletteId) == true ||
5✔
988
           m_latestPaletteId == FixtureGroup::invalidId())
2✔
989
    {
990
        m_latestPaletteId++;
1✔
991
    }
992

993
    return m_latestPaletteId;
2✔
994
}
995

996
/*****************************************************************************
997
 * Functions
998
 *****************************************************************************/
999

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

1011
    return m_latestFunctionId;
243✔
1012
}
1013

1014
bool Doc::addFunction(Function* func, quint32 id)
256✔
1015
{
1016
    Q_ASSERT(func != NULL);
256✔
1017

1018
    if (id == Function::invalidId())
256✔
1019
        id = createFunctionId();
243✔
1020

1021
    if (m_functions.contains(id) == true || id == Fixture::invalidId())
256✔
1022
    {
1023
        qWarning() << Q_FUNC_INFO << "a function with ID" << id << "already exists!";
2✔
1024
        return false;
2✔
1025
    }
1026
    else
1027
    {
1028
        // Listen to function changes
1029
        connect(func, SIGNAL(changed(quint32)),
254✔
1030
                this, SLOT(slotFunctionChanged(quint32)));
1031

1032
        // Listen to function name changes
1033
        connect(func, SIGNAL(nameChanged(quint32)),
254✔
1034
                this, SLOT(slotFunctionNameChanged(quint32)));
1035

1036
        // Make the function listen to fixture removals
1037
        connect(this, SIGNAL(fixtureRemoved(quint32)),
254✔
1038
                func, SLOT(slotFixtureRemoved(quint32)));
1039

1040
        // Place the function in the map and assign it the new ID
1041
        m_functions[id] = func;
254✔
1042
        func->setID(id);
254✔
1043
        emit functionAdded(id);
254✔
1044
        setModified();
254✔
1045

1046
        return true;
254✔
1047
    }
1048
}
1049

1050
QList <Function*> Doc::functions() const
49✔
1051
{
1052
    return m_functions.values();
49✔
1053
}
1054

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

1066
Function *Doc::functionByName(QString name)
×
1067
{
1068
    foreach (Function *f, m_functions)
×
1069
    {
1070
        if (f != NULL && f->name() == name)
×
UNCOV
1071
            return f;
×
UNCOV
1072
    }
×
1073
    return NULL;
×
1074
}
1075

1076
bool Doc::deleteFunction(quint32 id)
10✔
1077
{
1078
    if (m_functions.contains(id) == true)
10✔
1079
    {
1080
        Function* func = m_functions.take(id);
8✔
1081
        Q_ASSERT(func != NULL);
8✔
1082

1083
        if (m_startupFunctionId == id)
8✔
1084
            m_startupFunctionId = Function::invalidId();
×
1085

1086
        emit functionRemoved(id);
8✔
1087
        setModified();
8✔
1088
        delete func;
8✔
1089

1090
        return true;
8✔
1091
    }
1092
    else
1093
    {
1094
        qWarning() << Q_FUNC_INFO << "No function with id" << id;
2✔
1095
        return false;
2✔
1096
    }
1097
}
1098

1099
Function* Doc::function(quint32 id) const
884✔
1100
{
1101
    return m_functions.value(id, NULL);
884✔
1102
}
1103

1104
quint32 Doc::nextFunctionID()
2✔
1105
{
1106
    quint32 tmpFID = m_latestFunctionId;
2✔
1107
    while (m_functions.contains(tmpFID) == true ||
5✔
1108
           tmpFID == Fixture::invalidId())
2✔
1109
    {
1110
        tmpFID++;
1✔
1111
    }
1112

1113
    return tmpFID;
2✔
1114
}
1115

1116
void Doc::setStartupFunction(quint32 fid)
1✔
1117
{
1118
    m_startupFunctionId = fid;
1✔
1119
}
1✔
1120

1121
quint32 Doc::startupFunction()
3✔
1122
{
1123
    return m_startupFunctionId;
3✔
1124
}
1125

1126
QList<quint32> Doc::getUsage(quint32 fid)
7✔
1127
{
1128
    QList<quint32> usageList;
7✔
1129

1130
    foreach (Function *f, m_functions)
70✔
1131
    {
1132
        if (f->id() == fid)
63✔
1133
            continue;
6✔
1134

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

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

1212
    return usageList;
7✔
1213
}
×
1214

1215
void Doc::slotFunctionChanged(quint32 fid)
111✔
1216
{
1217
    setModified();
111✔
1218
    emit functionChanged(fid);
111✔
1219
}
111✔
1220

1221
void Doc::slotFunctionNameChanged(quint32 fid)
2✔
1222
{
1223
    setModified();
2✔
1224
    emit functionNameChanged(fid);
2✔
1225
}
2✔
1226

1227
/*********************************************************************
1228
 * Monitor Properties
1229
 *********************************************************************/
1230

1231
MonitorProperties *Doc::monitorProperties()
1✔
1232
{
1233
    if (m_monitorProps == NULL)
1✔
1234
        m_monitorProps = new MonitorProperties();
1✔
1235

1236
    return m_monitorProps;
1✔
1237
}
1238

1239
/*****************************************************************************
1240
 * Load & Save
1241
 *****************************************************************************/
1242

1243
bool Doc::loadXML(QXmlStreamReader &doc, bool loadIO)
2✔
1244
{
1245
    clearErrorLog();
2✔
1246

1247
    if (doc.name() != KXMLQLCEngine)
2✔
1248
    {
1249
        qWarning() << Q_FUNC_INFO << "Engine node not found";
1✔
1250
        return false;
1✔
1251
    }
1252

1253
    m_loadStatus = Loading;
1✔
1254
    emit loading();
1✔
1255

1256
    if (doc.attributes().hasAttribute(KXMLQLCStartupFunction))
2✔
1257
    {
1258
        quint32 sID = doc.attributes().value(KXMLQLCStartupFunction).toString().toUInt();
×
1259
        if (sID != Function::invalidId())
×
1260
            setStartupFunction(sID);
×
1261
    }
1262

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

1308
    postLoad();
1✔
1309

1310
    m_loadStatus = Loaded;
1✔
1311
    emit loaded();
1✔
1312

1313
    return true;
1✔
1314
}
1315

1316
bool Doc::saveXML(QXmlStreamWriter *doc)
1✔
1317
{
1318
    Q_ASSERT(doc != NULL);
1✔
1319

1320
    /* Create the master Engine node */
1321
    doc->writeStartElement(KXMLQLCEngine);
2✔
1322

1323
    if (startupFunction() != Function::invalidId())
1✔
1324
        doc->writeAttribute(KXMLQLCStartupFunction, QString::number(startupFunction()));
2✔
1325

1326
    m_ioMap->saveXML(doc);
1✔
1327

1328
    /* Write fixtures into an XML document */
1329
    QListIterator <Fixture*> fxit(fixtures());
1✔
1330
    while (fxit.hasNext() == true)
4✔
1331
    {
1332
        Fixture *fxi(fxit.next());
3✔
1333
        Q_ASSERT(fxi != NULL);
3✔
1334
        fxi->saveXML(doc);
3✔
1335
    }
1336

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

1346
    /* Write channel groups into an XML document */
1347
    QListIterator <ChannelsGroup*> chanGroups(channelsGroups());
1✔
1348
    while (chanGroups.hasNext() == true)
1✔
1349
    {
1350
        ChannelsGroup *grp(chanGroups.next());
×
UNCOV
1351
        Q_ASSERT(grp != NULL);
×
1352
        grp->saveXML(doc);
×
1353
    }
1354

1355
    /* Write palettes into an XML document */
1356
    QListIterator <QLCPalette*> paletteIt(palettes());
1✔
1357
    while (paletteIt.hasNext() == true)
1✔
1358
    {
1359
        QLCPalette *palette(paletteIt.next());
×
UNCOV
1360
        Q_ASSERT(palette != NULL);
×
1361
        palette->saveXML(doc);
×
1362
    }
1363

1364
    /* Write functions into an XML document */
1365
    QListIterator <Function*> funcit(functions());
1✔
1366
    while (funcit.hasNext() == true)
5✔
1367
    {
1368
        Function *func(funcit.next());
4✔
1369
        Q_ASSERT(func != NULL);
4✔
1370
        func->saveXML(doc);
4✔
1371
    }
1372

1373
    if (m_monitorProps != NULL)
1✔
1374
        m_monitorProps->saveXML(doc, this);
1✔
1375

1376
    /* End the <Engine> tag */
1377
    doc->writeEndElement();
1✔
1378

1379
    return true;
1✔
1380
}
1✔
1381

1382
void Doc::appendToErrorLog(QString error)
8✔
1383
{
1384
    if (m_errorLog.contains(error))
8✔
1385
        return;
5✔
1386

1387
    m_errorLog.append(error);
3✔
1388
    m_errorLog.append("<br>");
3✔
1389
}
1390

1391
void Doc::clearErrorLog()
2✔
1392
{
1393
    m_errorLog = "";
2✔
1394
}
2✔
1395

1396
QString Doc::errorLog()
1✔
1397
{
1398
    return m_errorLog;
1✔
1399
}
1400

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