• 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

61.18
/ui/src/virtualconsole/vccuelist.cpp
1
/*
2
  Q Light Controller Plus
3
  vccuelist.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 <QStyledItemDelegate>
22
#include <QXmlStreamReader>
23
#include <QXmlStreamWriter>
24
#include <QTreeWidgetItem>
25
#include <QFontMetrics>
26
#include <QProgressBar>
27
#include <QTreeWidget>
28
#include <QHeaderView>
29
#include <QGridLayout>
30
#include <QSettings>
31
#include <QCheckBox>
32
#include <QString>
33
#include <QLabel>
34
#include <QDebug>
35
#include <QTimer>
36

37
#include "vccuelistproperties.h"
38
#include "vcpropertieseditor.h"
39
#include "clickandgoslider.h"
40
#include "chaserrunner.h"
41
#include "mastertimer.h"
42
#include "chaserstep.h"
43
#include "vccuelist.h"
44
#include "qlcmacros.h"
45
#include "function.h"
46
#include "vcwidget.h"
47
#include "apputil.h"
48
#include "chaser.h"
49
#include "qmath.h"
50
#include "doc.h"
51

52
#define COL_NUM      0
53
#define COL_NAME     1
54
#define COL_FADEIN   2
55
#define COL_FADEOUT  3
56
#define COL_DURATION 4
57
#define COL_NOTES    5
58

59
#define PROP_ID  Qt::UserRole
60
#define HYSTERESIS 3 // Hysteresis for next/previous external input
61

62
#define PROGRESS_INTERVAL 200
63
#define UPDATE_TIMEOUT 100
64

65
const quint8 VCCueList::nextInputSourceId = 0;
66
const quint8 VCCueList::previousInputSourceId = 1;
67
const quint8 VCCueList::playbackInputSourceId = 2;
68
const quint8 VCCueList::sideFaderInputSourceId = 3;
69
const quint8 VCCueList::stopInputSourceId = 4;
70

71
const QString progressDisabledStyle =
72
        "QProgressBar { border: 2px solid #C3C3C3; border-radius: 4px; background-color: #DCDCDC; }";
73
const QString progressFadeStyle =
74
        "QProgressBar { border: 2px solid grey; border-radius: 4px; background-color: #C3C3C3; text-align: center; }"
75
        "QProgressBar::chunk { background-color: #63C10B; width: 1px; }";
76
const QString progressHoldStyle =
77
        "QProgressBar { border: 2px solid grey; border-radius: 4px; background-color: #C3C3C3; text-align: center; }"
78
        "QProgressBar::chunk { background-color: #0F9BEC; width: 1px; }";
79

80
const QString cfLabelBlueStyle =
81
        "QLabel { background-color: #4E8DDE; color: white; border: 1px solid; border-radius: 3px; font: bold; }";
82
const QString cfLabelOrangeStyle =
83
        "QLabel { background-color: orange; color: black; border: 1px solid; border-radius: 3px; font: bold; }";
84
const QString cfLabelNoStyle =
85
        "QLabel { border: 1px solid; border-radius: 3px; font: bold; }";
86

87
VCCueList::VCCueList(QWidget *parent, Doc *doc) : VCWidget(parent, doc)
15✔
88
    , m_chaserID(Function::invalidId())
30✔
89
    , m_nextPrevBehavior(DefaultRunFirst)
15✔
90
    , m_playbackLayout(PlayPauseStop)
15✔
91
    , m_timer(NULL)
15✔
92
    , m_primaryIndex(0)
15✔
93
    , m_secondaryIndex(0)
15✔
94
    , m_primaryTop(true)
15✔
95
    , m_slidersMode(None)
15✔
96
{
97
    /* Set the class name "VCCueList" as the object name as well */
98
    setObjectName(VCCueList::staticMetaObject.className());
15✔
99

100
    /* Create a layout for this widget */
101
    QGridLayout *grid = new QGridLayout(this);
15✔
102
    grid->setSpacing(2);
15✔
103

104
    QFontMetrics m_fm = QFontMetrics(this->font());
15✔
105

106
    m_topPercentageLabel = new QLabel("100%");
15✔
107
    m_topPercentageLabel->setAlignment(Qt::AlignHCenter);
15✔
108
#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
109
    m_topPercentageLabel->setFixedWidth(m_fm.width("100%"));
110
#else
111
    m_topPercentageLabel->setFixedWidth(m_fm.horizontalAdvance("100%"));
15✔
112
#endif
113
    grid->addWidget(m_topPercentageLabel, 1, 0, 1, 1);
15✔
114

115
    m_topStepLabel = new QLabel("");
15✔
116
    m_topStepLabel->setStyleSheet(cfLabelNoStyle);
15✔
117
    m_topStepLabel->setAlignment(Qt::AlignCenter);
15✔
118
    m_topStepLabel->setFixedSize(32, 24);
15✔
119
    grid->addWidget(m_topStepLabel, 2, 0, 1, 1);
15✔
120

121
    m_sideFader = new ClickAndGoSlider();
15✔
122
    m_sideFader->setSliderStyleSheet(CNG_DEFAULT_STYLE);
15✔
123
    m_sideFader->setFixedWidth(32);
15✔
124
    m_sideFader->setRange(0, 100);
15✔
125
    m_sideFader->setValue(100);
15✔
126
    grid->addWidget(m_sideFader, 3, 0, 1, 1);
15✔
127

128
    m_bottomStepLabel = new QLabel("");
15✔
129
    m_bottomStepLabel->setStyleSheet(cfLabelNoStyle);
15✔
130
    m_bottomStepLabel->setAlignment(Qt::AlignCenter);
15✔
131
    m_bottomStepLabel->setFixedSize(32, 24);
15✔
132
    grid->addWidget(m_bottomStepLabel, 4, 0, 1, 1);
15✔
133

134
    m_bottomPercentageLabel = new QLabel("0%");
15✔
135
    m_bottomPercentageLabel->setAlignment(Qt::AlignHCenter);
15✔
136
#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
137
    m_bottomPercentageLabel->setFixedWidth(m_fm.width("100%"));
138
#else
139
    m_bottomPercentageLabel->setFixedWidth(m_fm.horizontalAdvance("100%"));
15✔
140
#endif
141
    grid->addWidget(m_bottomPercentageLabel, 5, 0, 1, 1);
15✔
142

143
    connect(m_sideFader, SIGNAL(valueChanged(int)),
15✔
144
            this, SLOT(slotSideFaderValueChanged(int)));
145

146
    slotShowCrossfadePanel(false);
15✔
147

148
    QVBoxLayout *vbox = new QVBoxLayout();
15✔
149

150
    /* Create a list for scenes (cues) */
151
    m_tree = new QTreeWidget(this);
15✔
152
    m_tree->setSelectionMode(QAbstractItemView::SingleSelection);
15✔
153
    //m_tree->setAlternatingRowColors(true);
154
    m_tree->setAllColumnsShowFocus(true);
15✔
155
    m_tree->setRootIsDecorated(false);
15✔
156
    m_tree->setItemsExpandable(false);
15✔
157
    m_tree->header()->setSortIndicatorShown(false);
15✔
158
    m_tree->header()->setMinimumSectionSize(0); // allow columns to be hidden
15✔
159
    m_tree->header()->setSectionsClickable(false);
15✔
160
    m_tree->header()->setSectionsMovable(false);
15✔
161

162
    // Make only the notes column editable
163
    m_tree->setItemDelegateForColumn(COL_NUM, new NoEditDelegate(this));
15✔
164
    m_tree->setItemDelegateForColumn(COL_NAME, new NoEditDelegate(this));
15✔
165
    m_tree->setItemDelegateForColumn(COL_FADEIN, new NoEditDelegate(this));
15✔
166
    m_tree->setItemDelegateForColumn(COL_FADEOUT, new NoEditDelegate(this));
15✔
167
    m_tree->setItemDelegateForColumn(COL_DURATION, new NoEditDelegate(this));
15✔
168

169
    connect(m_tree, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
15✔
170
            this, SLOT(slotItemActivated(QTreeWidgetItem*)));
171
    connect(m_tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
15✔
172
            this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
173
    vbox->addWidget(m_tree);
15✔
174

175
    m_progress = new QProgressBar(this);
15✔
176
    m_progress->setOrientation(Qt::Horizontal);
15✔
177
    m_progress->setStyleSheet(progressDisabledStyle);
15✔
178
    m_progress->setProperty("status", 0);
15✔
179
    m_progress->setFixedHeight(20);
15✔
180
    vbox->addWidget(m_progress);
15✔
181

182
    m_timer = new QTimer(this);
15✔
183
    connect(m_timer, SIGNAL(timeout()),
15✔
184
            this, SLOT(slotProgressTimeout()));
185

186
    m_updateTimer = new QTimer(this);
15✔
187
    connect(m_updateTimer, SIGNAL(timeout()),
15✔
188
            this, SLOT(slotUpdateStepList()));
189
    m_updateTimer->setSingleShot(true);
15✔
190

191
    /* Create control buttons */
192
    QHBoxLayout *hbox = new QHBoxLayout();
15✔
193
    hbox->setSpacing(2);
15✔
194

195
    m_crossfadeButton = new QToolButton(this);
15✔
196
    m_crossfadeButton->setIcon(QIcon(":/slider.png"));
15✔
197
    m_crossfadeButton->setIconSize(QSize(24, 24));
15✔
198
    m_crossfadeButton->setCheckable(true);
15✔
199
    m_crossfadeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
15✔
200
    m_crossfadeButton->setFixedHeight(32);
15✔
201
    m_crossfadeButton->setToolTip(tr("Show/Hide crossfade sliders"));
15✔
202
    m_crossfadeButton->setVisible(false);
15✔
203
    connect(m_crossfadeButton, SIGNAL(toggled(bool)),
15✔
204
            this, SLOT(slotShowCrossfadePanel(bool)));
205
    hbox->addWidget(m_crossfadeButton);
15✔
206

207
    m_playbackButton = new QToolButton(this);
15✔
208
    m_playbackButton->setIcon(QIcon(":/player_play.png"));
15✔
209
    m_playbackButton->setIconSize(QSize(24, 24));
15✔
210
    m_playbackButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
15✔
211
    m_playbackButton->setFixedHeight(32);
15✔
212
    m_playbackButton->setToolTip(tr("Play/Pause Cue list"));
15✔
213
    connect(m_playbackButton, SIGNAL(clicked()), this, SLOT(slotPlayback()));
15✔
214
    hbox->addWidget(m_playbackButton);
15✔
215

216
    m_stopButton = new QToolButton(this);
15✔
217
    m_stopButton->setIcon(QIcon(":/player_stop.png"));
15✔
218
    m_stopButton->setIconSize(QSize(24, 24));
15✔
219
    m_stopButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
15✔
220
    m_stopButton->setFixedHeight(32);
15✔
221
    m_stopButton->setToolTip(tr("Stop Cue list"));
15✔
222
    connect(m_stopButton, SIGNAL(clicked()), this, SLOT(slotStop()));
15✔
223
    hbox->addWidget(m_stopButton);
15✔
224

225
    m_previousButton = new QToolButton(this);
15✔
226
    m_previousButton->setIcon(QIcon(":/back.png"));
15✔
227
    m_previousButton->setIconSize(QSize(24, 24));
15✔
228
    m_previousButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
15✔
229
    m_previousButton->setFixedHeight(32);
15✔
230
    m_previousButton->setToolTip(tr("Go to previous step in the list"));
15✔
231
    connect(m_previousButton, SIGNAL(clicked()), this, SLOT(slotPreviousCue()));
15✔
232
    hbox->addWidget(m_previousButton);
15✔
233

234
    m_nextButton = new QToolButton(this);
15✔
235
    m_nextButton->setIcon(QIcon(":/forward.png"));
15✔
236
    m_nextButton->setIconSize(QSize(24, 24));
15✔
237
    m_nextButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
15✔
238
    m_nextButton->setFixedHeight(32);
15✔
239
    m_nextButton->setToolTip(tr("Go to next step in the list"));
15✔
240
    connect(m_nextButton, SIGNAL(clicked()), this, SLOT(slotNextCue()));
15✔
241
    hbox->addWidget(m_nextButton);
15✔
242

243
    vbox->addItem(hbox);
15✔
244
    grid->addItem(vbox, 0, 1, 6);
15✔
245

246
    setFrameStyle(KVCFrameStyleSunken);
15✔
247
    setType(VCWidget::CueListWidget);
15✔
248
    setCaption(tr("Cue list"));
15✔
249

250
    QSettings settings;
15✔
251
    QVariant var = settings.value(SETTINGS_CUELIST_SIZE);
15✔
252
    if (var.isValid() == true)
15✔
253
        resize(var.toSize());
×
254
    else
255
        resize(QSize(300, 220));
15✔
256

257
    slotModeChanged(m_doc->mode());
15✔
258
    setLiveEdit(m_liveEdit);
15✔
259

260
    connect(m_doc, SIGNAL(functionRemoved(quint32)),
15✔
261
            this, SLOT(slotFunctionRemoved(quint32)));
262
    connect(m_doc, SIGNAL(functionChanged(quint32)),
15✔
263
            this, SLOT(slotFunctionChanged(quint32)));
264
    connect(m_doc, SIGNAL(functionNameChanged(quint32)),
15✔
265
            this, SLOT(slotFunctionNameChanged(quint32)));
266

267
    m_nextLatestValue = 0;
15✔
268
    m_previousLatestValue = 0;
15✔
269
    m_playbackLatestValue = 0;
15✔
270
    m_stopLatestValue = 0;
15✔
271
}
15✔
272

273
VCCueList::~VCCueList()
17✔
274
{
275
}
17✔
276

277
void VCCueList::enableWidgetUI(bool enable)
23✔
278
{
279
    m_tree->setEnabled(enable);
23✔
280
    m_playbackButton->setEnabled(enable);
23✔
281
    m_stopButton->setEnabled(enable);
23✔
282
    m_previousButton->setEnabled(enable);
23✔
283
    m_nextButton->setEnabled(enable);
23✔
284

285
    m_topPercentageLabel->setEnabled(enable);
23✔
286
    m_sideFader->setEnabled(enable);
23✔
287
    m_topStepLabel->setEnabled(enable);
23✔
288
    m_bottomPercentageLabel->setEnabled(enable);
23✔
289
    m_bottomStepLabel->setEnabled(enable);
23✔
290
}
23✔
291

292
/*****************************************************************************
293
 * Clipboard
294
 *****************************************************************************/
295

296
VCWidget *VCCueList::createCopy(VCWidget *parent)
1✔
297
{
298
    Q_ASSERT(parent != NULL);
1✔
299

300
    VCCueList *cuelist = new VCCueList(parent, m_doc);
1✔
301
    if (cuelist->copyFrom(this) == false)
1✔
302
    {
303
        delete cuelist;
×
304
        cuelist = NULL;
×
305
    }
306

307
    return cuelist;
1✔
308
}
309

310
bool VCCueList::copyFrom(const VCWidget *widget)
3✔
311
{
312
    const VCCueList *cuelist = qobject_cast<const VCCueList*> (widget);
3✔
313
    if (cuelist == NULL)
3✔
314
        return false;
1✔
315

316
    /* Function list contents */
317
    setChaser(cuelist->chaserID());
2✔
318

319
    /* Key sequence */
320
    setNextKeySequence(cuelist->nextKeySequence());
2✔
321
    setPreviousKeySequence(cuelist->previousKeySequence());
2✔
322
    setPlaybackKeySequence(cuelist->playbackKeySequence());
2✔
323
    setStopKeySequence(cuelist->stopKeySequence());
2✔
324

325
    /* Sliders mode */
326
    setSideFaderMode(cuelist->sideFaderMode());
2✔
327

328
    /* Common stuff */
329
    return VCWidget::copyFrom(widget);
2✔
330
}
331

332
/*****************************************************************************
333
 * Cue list
334
 *****************************************************************************/
335

336
void VCCueList::setChaser(quint32 id)
14✔
337
{
338
    Function *old = m_doc->function(m_chaserID);
14✔
339
    if (old != NULL)
14✔
340
    {
341
        /* Get rid of old function connections */
342
        disconnect(old, SIGNAL(running(quint32)),
1✔
343
                this, SLOT(slotFunctionRunning(quint32)));
344
        disconnect(old, SIGNAL(stopped(quint32)),
1✔
345
                this, SLOT(slotFunctionStopped(quint32)));
346
        disconnect(old, SIGNAL(currentStepChanged(int)),
1✔
347
                this, SLOT(slotCurrentStepChanged(int)));
348
    }
349

350
    Chaser *chaser = qobject_cast<Chaser*> (m_doc->function(id));
14✔
351
    if (chaser != NULL)
14✔
352
    {
353
        /* Connect to the new function */
354
        connect(chaser, SIGNAL(running(quint32)),
11✔
355
                this, SLOT(slotFunctionRunning(quint32)));
356
        connect(chaser, SIGNAL(stopped(quint32)),
11✔
357
                this, SLOT(slotFunctionStopped(quint32)));
358
        connect(chaser, SIGNAL(currentStepChanged(int)),
11✔
359
                this, SLOT(slotCurrentStepChanged(int)));
360

361
        m_chaserID = id;
11✔
362
    }
363
    else
364
    {
365
        m_chaserID = Function::invalidId();
3✔
366
    }
367

368
    updateStepList();
14✔
369

370
    /* Current status */
371
    if (chaser != NULL && chaser->isRunning())
14✔
372
    {
373
        slotFunctionRunning(m_chaserID);
×
374
        slotCurrentStepChanged(chaser->currentStepIndex());
×
375
    }
376
    else
377
        slotFunctionStopped(m_chaserID);
14✔
378
}
14✔
379

380
quint32 VCCueList::chaserID() const
9✔
381
{
382
    return m_chaserID;
9✔
383
}
384

385
Chaser *VCCueList::chaser()
89✔
386
{
387
    if (m_chaserID == Function::invalidId())
89✔
388
        return NULL;
6✔
389
    Chaser *chaser = qobject_cast<Chaser*>(m_doc->function(m_chaserID));
83✔
390
    return chaser;
83✔
391
}
392

393
void VCCueList::updateStepList()
15✔
394
{
395
    m_listIsUpdating = true;
15✔
396

397
    m_tree->clear();
15✔
398

399
    Chaser *ch = chaser();
15✔
400
    if (ch == NULL)
15✔
401
        return;
3✔
402

403
    QListIterator <ChaserStep> it(ch->steps());
12✔
404
    while (it.hasNext() == true)
59✔
405
    {
406
        ChaserStep step(it.next());
47✔
407

408
        Function *function = m_doc->function(step.fid);
47✔
409
        Q_ASSERT(function != NULL);
47✔
410

411
        QTreeWidgetItem *item = new QTreeWidgetItem(m_tree);
47✔
412
        item->setFlags(item->flags() | Qt::ItemIsEditable);
47✔
413
        int index = m_tree->indexOfTopLevelItem(item) + 1;
47✔
414
        item->setText(COL_NUM, QString("%1").arg(index));
47✔
415
        item->setData(COL_NUM, PROP_ID, function->id());
47✔
416
        item->setText(COL_NAME, function->name());
47✔
417
        if (step.note.isEmpty() == false)
47✔
418
            item->setText(COL_NOTES, step.note);
×
419

420
        switch (ch->fadeInMode())
47✔
421
        {
422
            case Chaser::Common:
×
423
                item->setText(COL_FADEIN, Function::speedToString(ch->fadeInSpeed()));
×
424
                break;
×
425
            case Chaser::PerStep:
×
426
                item->setText(COL_FADEIN, Function::speedToString(step.fadeIn));
×
427
                break;
×
428
            default:
47✔
429
            case Chaser::Default:
430
                item->setText(COL_FADEIN, QString());
47✔
431
        }
432

433
        switch (ch->fadeOutMode())
47✔
434
        {
435
            case Chaser::Common:
×
436
                item->setText(COL_FADEOUT, Function::speedToString(ch->fadeOutSpeed()));
×
437
                break;
×
438
            case Chaser::PerStep:
×
439
                item->setText(COL_FADEOUT, Function::speedToString(step.fadeOut));
×
440
                break;
×
441
            default:
47✔
442
            case Chaser::Default:
443
                item->setText(COL_FADEOUT, QString());
47✔
444
        }
445

446
        switch (ch->durationMode())
47✔
447
        {
448
            case Chaser::Common:
47✔
449
                item->setText(COL_DURATION, Function::speedToString(ch->duration()));
47✔
450
                break;
47✔
451
            case Chaser::PerStep:
×
452
                item->setText(COL_DURATION, Function::speedToString(step.duration));
×
453
                break;
×
454
            default:
×
455
            case Chaser::Default:
456
                item->setText(COL_DURATION, QString());
×
457
        }
458
    }
47✔
459

460
    QTreeWidgetItem *item = m_tree->topLevelItem(0);
12✔
461
    if (item != NULL)
12✔
462
        m_defCol = item->background(COL_NUM);
12✔
463

464
    m_tree->header()->resizeSections(QHeaderView::ResizeToContents);
12✔
465
    m_tree->header()->setSectionHidden(COL_NAME, ch->type() == Function::SequenceType ? true : false);
12✔
466
    m_listIsUpdating = false;
12✔
467
}
12✔
468

469
int VCCueList::getCurrentIndex()
×
470
{
471
    int index = m_tree->indexOfTopLevelItem(m_tree->currentItem());
×
472
    if (index == -1)
×
473
        index = 0;
×
474
    return index;
×
475
}
476

477
int VCCueList::getNextIndex()
×
478
{
479
    Chaser *ch = chaser();
×
480
    if (ch == NULL)
×
481
        return -1;
×
482

483
    if (ch->direction() == Function::Forward)
×
484
        return getNextTreeIndex();
×
485
    else
486
        return getPrevTreeIndex();
×
487
}
488

489
int VCCueList::getPrevIndex()
×
490
{
491
    Chaser *ch = chaser();
×
492
    if (ch == NULL)
×
493
        return -1;
×
494

495
    if (ch->direction() == Function::Forward)
×
496
        return getPrevTreeIndex();
×
497
    else
498
        return getNextTreeIndex();
×
499
}
500

501
int VCCueList::getFirstIndex()
3✔
502
{
503
    Chaser *ch = chaser();
3✔
504
    if (ch == NULL)
3✔
505
        return -1;
×
506

507
    if (ch->direction() == Function::Forward)
3✔
508
        return getFirstTreeIndex();
3✔
509
    else
510
        return getLastTreeIndex();
×
511
}
512

513
int VCCueList::getLastIndex()
×
514
{
515
    Chaser *ch = chaser();
×
516
    if (ch == NULL)
×
517
        return -1;
×
518

519
    if (ch->direction() == Function::Forward)
×
520
        return getLastTreeIndex();
×
521
    else
522
        return getFirstTreeIndex();
×
523
}
524

525
int VCCueList::getNextTreeIndex()
×
526
{
527
    int count = m_tree->topLevelItemCount();
×
528
    if (count > 0)
×
529
        return (getCurrentIndex() + 1) % count;
×
530
    return 0;
×
531
}
532

533
int VCCueList::getPrevTreeIndex()
×
534
{
535
    int currentIndex = getCurrentIndex();
×
536
    if (currentIndex <= 0)
×
537
        return getLastTreeIndex();
×
538
    return currentIndex - 1;
×
539
}
540

541
int VCCueList::getFirstTreeIndex()
3✔
542
{
543
    return 0;
3✔
544
}
545

546
int VCCueList::getLastTreeIndex()
×
547
{
548
    return m_tree->topLevelItemCount() - 1;
×
549
}
550

551
qreal VCCueList::getPrimaryIntensity() const
20✔
552
{
553
    if (sideFaderMode() == Steps)
20✔
554
        return  1.0;
×
555

556
    return m_primaryTop ? qreal(m_sideFader->value() / 100.0) : qreal((100 - m_sideFader->value()) / 100.0);
20✔
557
}
558

559
void VCCueList::notifyFunctionStarting(quint32 fid, qreal intensity)
×
560
{
561
    Q_UNUSED(intensity);
562

563
    if (mode() == Doc::Design)
×
564
        return;
×
565

566
    if (fid == m_chaserID)
×
567
        return;
×
568

569
    stopChaser();
×
570
}
571

572
void VCCueList::slotFunctionRemoved(quint32 fid)
2✔
573
{
574
    if (fid == m_chaserID)
2✔
575
    {
576
        setChaser(Function::invalidId());
1✔
577
        resetIntensityOverrideAttribute();
1✔
578
    }
579
}
2✔
580

581
void VCCueList::slotFunctionChanged(quint32 fid)
5✔
582
{
583
    if (fid == m_chaserID && !m_updateTimer->isActive())
5✔
584
        m_updateTimer->start(UPDATE_TIMEOUT);
1✔
585
}
5✔
586

587
void VCCueList::slotFunctionNameChanged(quint32 fid)
×
588
{
589
    if (fid == m_chaserID)
×
590
        m_updateTimer->start(UPDATE_TIMEOUT);
×
591
    else
592
    {
593
        // fid might be an ID of a ChaserStep of m_chaser
594
        Chaser *ch = chaser();
×
595
        if (ch == NULL)
×
596
            return;
×
597
        foreach (ChaserStep step, ch->steps())
×
598
        {
599
            if (step.fid == fid)
×
600
            {
601
                m_updateTimer->start(UPDATE_TIMEOUT);
×
602
                return;
×
603
            }
604
        }
×
605
    }
606
}
607

608
void VCCueList::slotUpdateStepList()
1✔
609
{
610
    updateStepList();
1✔
611
}
1✔
612

613
void VCCueList::slotPlayback()
2✔
614
{
615
    if (mode() != Doc::Operate)
2✔
616
        return;
×
617

618
    Chaser *ch = chaser();
2✔
619
    if (ch == NULL)
2✔
620
        return;
×
621

622
    if (ch->isRunning())
2✔
623
    {
624
        if (playbackLayout() == PlayPauseStop)
2✔
625
        {
626
            if (ch->isPaused())
2✔
627
            {
628
                m_playbackButton->setStyleSheet(QString("QToolButton{ background: %1; }")
×
629
                                                .arg(m_stopButton->palette().window().color().name()));
×
630
                m_playbackButton->setIcon(QIcon(":/player_pause.png"));
×
631
            }
632
            else
633
            {
634
                m_playbackButton->setStyleSheet("QToolButton{ background: #5B81FF; }");
2✔
635
                m_playbackButton->setIcon(QIcon(":/player_play.png"));
2✔
636
            }
637

638
            // check if the item selection has been changed during pause
639
            int currentTreeIndex = m_tree->indexOfTopLevelItem(m_tree->currentItem());
2✔
640
            if (currentTreeIndex != ch->currentStepIndex())
2✔
641
                playCueAtIndex(currentTreeIndex);
×
642

643
            ch->setPause(!ch->isPaused());
2✔
644
        }
645
        else if (playbackLayout() == PlayStopPause)
×
646
        {
647
            stopChaser();
×
648
            m_stopButton->setStyleSheet(QString("QToolButton{ background: %1; }")
×
649
                                            .arg(m_playbackButton->palette().window().color().name()));
×
650
        }
651
    }
652
    else
653
    {
654
        if (m_tree->currentItem() != NULL)
×
655
            startChaser(getCurrentIndex());
×
656
        else
657
            startChaser();
×
658
    }
659

660
    emit playbackButtonClicked();
2✔
661
}
662

663
void VCCueList::slotStop()
×
664
{
665
    if (mode() != Doc::Operate)
×
666
        return;
×
667

668
    Chaser *ch = chaser();
×
669
    if (ch == NULL)
×
670
        return;
×
671

672
    if (ch->isRunning())
×
673
    {
674
        if (playbackLayout() == PlayPauseStop)
×
675
        {
676
            stopChaser();
×
677
            m_playbackButton->setStyleSheet(QString("QToolButton{ background: %1; }")
×
678
                                            .arg(m_stopButton->palette().window().color().name()));
×
679
            m_progress->setFormat("");
×
680
            m_progress->setValue(0);
×
681

682
            emit progressStateChanged();
×
683
        }
684
        else if (playbackLayout() == PlayStopPause)
×
685
        {
686
            if (ch->isPaused())
×
687
            {
688
                m_stopButton->setStyleSheet(QString("QToolButton{ background: %1; }")
×
689
                                                .arg(m_playbackButton->palette().window().color().name()));
×
690
                m_stopButton->setIcon(QIcon(":/player_pause.png"));
×
691
            }
692
            else
693
            {
694
                m_stopButton->setStyleSheet("QToolButton{ background: #5B81FF; }");
×
695
            }
696
            ch->setPause(!ch->isPaused());
×
697
        }
698
    }
699
    else
700
    {
701
        m_primaryIndex = 0;
×
702
        m_tree->setCurrentItem(m_tree->topLevelItem(getFirstIndex()));
×
703
    }
704

705
    emit stopButtonClicked();
×
706
}
707

708
void VCCueList::slotNextCue()
10✔
709
{
710
    if (mode() != Doc::Operate)
10✔
711
        return;
1✔
712

713
    Chaser *ch = chaser();
9✔
714
    if (ch == NULL)
9✔
715
        return;
×
716

717
    if (ch->isRunning())
9✔
718
    {
719
        if (ch->isPaused())
6✔
720
        {
721
            m_tree->setCurrentItem(m_tree->topLevelItem(getNextIndex()));
×
722
        }
723
        else
724
        {
725
            ChaserAction action;
726
            action.m_action = ChaserNextStep;
6✔
727
            action.m_masterIntensity = intensity();
6✔
728
            action.m_stepIntensity = getPrimaryIntensity();
6✔
729
            action.m_fadeMode = getFadeMode();
6✔
730
            ch->setAction(action);
6✔
731
        }
732
    }
733
    else
734
    {
735
        switch (m_nextPrevBehavior)
3✔
736
        {
737
            case DefaultRunFirst:
3✔
738
                startChaser(getFirstIndex());
3✔
739
            break;
3✔
740
            case RunNext:
×
741
                startChaser(getNextIndex());
×
742
            break;
×
743
            case Select:
×
744
                m_tree->setCurrentItem(m_tree->topLevelItem(getNextIndex()));
×
745
            break;
×
746
            case Nothing:
×
747
            break;
×
748
            default:
×
749
                Q_ASSERT(false);
×
750
        }
751
    }
752
}
753

754
void VCCueList::slotPreviousCue()
8✔
755
{
756
    if (mode() != Doc::Operate)
8✔
757
        return;
1✔
758

759
    Chaser *ch = chaser();
7✔
760
    if (ch == NULL)
7✔
761
        return;
×
762

763
    if (ch->isRunning())
7✔
764
    {
765
        if (ch->isPaused())
7✔
766
        {
767
            m_tree->setCurrentItem(m_tree->topLevelItem(getPrevIndex()));
×
768
        }
769
        else
770
        {
771
            ChaserAction action;
772
            action.m_action = ChaserPreviousStep;
7✔
773
            action.m_masterIntensity = intensity();
7✔
774
            action.m_stepIntensity = getPrimaryIntensity();
7✔
775
            action.m_fadeMode = getFadeMode();
7✔
776
            ch->setAction(action);
7✔
777
        }
778
    }
779
    else
780
    {
781
        switch (m_nextPrevBehavior)
×
782
        {
783
            case DefaultRunFirst:
×
784
                startChaser(getLastIndex());
×
785
            break;
×
786
            case RunNext:
×
787
                startChaser(getPrevIndex());
×
788
            break;
×
789
            case Select:
×
790
                m_tree->setCurrentItem(m_tree->topLevelItem(getPrevIndex()));
×
791
            break;
×
792
            case Nothing:
×
793
            break;
×
794
            default:
×
795
                Q_ASSERT(false);
×
796
        }
797
    }
798
}
799

800
void VCCueList::slotCurrentStepChanged(int stepNumber)
19✔
801
{
802
    // Chaser is being edited, channels count may change.
803
    // Wait for the CueList to update its steps.
804
    if (m_updateTimer->isActive())
19✔
805
        return;
×
806

807
    Q_ASSERT(stepNumber < m_tree->topLevelItemCount() && stepNumber >= 0);
19✔
808
    QTreeWidgetItem *item = m_tree->topLevelItem(stepNumber);
19✔
809
    Q_ASSERT(item != NULL);
19✔
810
    m_tree->scrollToItem(item, QAbstractItemView::PositionAtCenter);
19✔
811
    m_tree->setCurrentItem(item);
19✔
812
    m_primaryIndex = stepNumber;
19✔
813
    if (sideFaderMode() == Steps)
19✔
814
    {
815
        m_bottomStepLabel->setStyleSheet(cfLabelBlueStyle);
×
816
        m_bottomStepLabel->setText(QString("#%1").arg(m_primaryIndex + 1));
×
817

818
        float stepVal;
819
        int stepsCount = m_tree->topLevelItemCount();
×
820
        if (stepsCount < 256) 
×
821
        {
822
            stepVal = 256.0 / (float)stepsCount; //divide up the full 0..255 range
×
823
            stepVal = qFloor((stepVal * 100000.0) + 0.5) / 100000.0; //round to 5 decimals to fix corner cases
×
824
        }
825
        else 
826
        {
827
            stepVal = 1.0;
×
828
        }
829
        
830
        // value->step# truncates down in slotSideFaderValueChanged; so use ceiling for step#->value
831
        float slValue = stepVal * (float)stepNumber;
×
832
        if (slValue > 255)
×
833
            slValue = 255.0;
×
834

835
        int upperBound = 255 - qCeil(slValue);
×
836
        int lowerBound = qFloor(256.0 - slValue - stepVal);
×
837
        // if the Step slider is already in range, then do not set its value
838
        // this means a user interaction is going on, either with the mouse or external controller
839
        if (m_sideFader->value() < lowerBound || m_sideFader->value() >= upperBound)
×
840
        {
841
            m_sideFader->blockSignals(true);
×
842
            m_sideFader->setValue(upperBound);
×
843
            m_topPercentageLabel->setText(QString("%1").arg(qCeil(slValue)));
×
844
            m_sideFader->blockSignals(false);
×
845

846
            //qDebug() << "Slider value:" << m_sideFader->value() << "->" << 255-qCeil(slValue) 
847
            //    << "(disp:" << slValue << ")" << "Step range:" << upperBound << lowerBound 
848
            //    << "(stepSize:" << stepVal << ")" 
849
            //    << "(raw lower:" << (256.0 - slValue - stepVal) << ")";
850
        }
851
    }
852
    else
853
    {
854
        setFaderInfo(m_primaryIndex);
19✔
855
    }
856
    emit stepChanged(m_primaryIndex);
19✔
857
    emit sideFaderValueChanged();
19✔
858
}
859

860
void VCCueList::slotItemActivated(QTreeWidgetItem *item)
5✔
861
{
862
    if (mode() != Doc::Operate)
5✔
863
        return;
1✔
864

865
    playCueAtIndex(m_tree->indexOfTopLevelItem(item));
4✔
866
}
867

868
void VCCueList::slotItemChanged(QTreeWidgetItem *item, int column)
374✔
869
{
870
    if (m_listIsUpdating || column != COL_NOTES)
374✔
871
        return;
374✔
872

873
    Chaser *ch = chaser();
×
874
    if (ch == NULL)
×
875
        return;
×
876

877
    QString itemText = item->text(column);
×
878
    int idx = m_tree->indexOfTopLevelItem(item);
×
879
    ChaserStep step = ch->steps().at(idx);
×
880

881
    step.note = itemText;
×
882
    ch->replaceStep(step, idx);
×
883

884
    emit stepNoteChanged(idx, itemText);
×
885
}
×
886

887
void VCCueList::slotStepNoteChanged(int idx, QString note)
×
888
{
889
    Chaser *ch = chaser();
×
890
    if (ch == NULL)
×
891
        return;
×
892
    ChaserStep step = ch->steps().at(idx);
×
893
    step.note = note;
×
894
    ch->replaceStep(step, idx);
×
895
}
×
896

897
void VCCueList::slotFunctionRunning(quint32 fid)
4✔
898
{
899
    if (fid != m_chaserID)
4✔
900
        return;
×
901

902
    if (playbackLayout() == PlayPauseStop)
4✔
903
        m_playbackButton->setIcon(QIcon(":/player_pause.png"));
4✔
904
    else if (playbackLayout() == PlayStopPause)
×
905
        m_playbackButton->setIcon(QIcon(":/player_stop.png"));
×
906
    m_timer->start(PROGRESS_INTERVAL);
4✔
907
    emit playbackStatusChanged();
4✔
908
    updateFeedback();
4✔
909
}
910

911
void VCCueList::slotFunctionStopped(quint32 fid)
14✔
912
{
913
    if (fid != m_chaserID)
14✔
914
        return;
×
915

916
    m_playbackButton->setIcon(QIcon(":/player_play.png"));
14✔
917
    m_topStepLabel->setText("");
14✔
918
    m_topStepLabel->setStyleSheet(cfLabelNoStyle);
14✔
919
    m_bottomStepLabel->setText("");
14✔
920
    m_bottomStepLabel->setStyleSheet(cfLabelNoStyle);
14✔
921
    // reset any previously set background
922
    QTreeWidgetItem *item = m_tree->topLevelItem(m_secondaryIndex);
14✔
923
    if (item != NULL)
14✔
924
        item->setBackground(COL_NUM, m_defCol);
11✔
925

926
    emit stepChanged(-1);
14✔
927

928
    m_progress->setFormat("");
14✔
929
    m_progress->setValue(0);    
14✔
930

931
    emit progressStateChanged();
14✔
932
    emit sideFaderValueChanged();
14✔
933
    emit playbackStatusChanged();
14✔
934

935
    qDebug() << Q_FUNC_INFO << "Cue stopped";
14✔
936
    updateFeedback();
14✔
937
}
938

939
void VCCueList::slotProgressTimeout()
×
940
{
941
    Chaser *ch = chaser();
×
942
    if (ch == NULL || !ch->isRunning())
×
943
        return;
×
944

945
    ChaserRunnerStep step(ch->currentRunningStep());
×
946
    if (step.m_function != NULL)
×
947
    {
948
        int status = m_progress->property("status").toInt();
×
949
        int newstatus;
950
        if (step.m_fadeIn == Function::defaultSpeed())
×
951
            newstatus = 1;
×
952
        else if (step.m_elapsed > (quint32)step.m_fadeIn)
×
953
            newstatus = 1;
×
954
        else
955
            newstatus = 0;
×
956

957
        if (newstatus != status)
×
958
        {
959
            if (newstatus == 0)
×
960
                m_progress->setStyleSheet(progressFadeStyle);
×
961
            else
962
                m_progress->setStyleSheet(progressHoldStyle);
×
963
            m_progress->setProperty("status", newstatus);
×
964
        }
965
        if (step.m_duration == Function::infiniteSpeed())
×
966
        {
967
            if (newstatus == 0 && step.m_fadeIn != Function::defaultSpeed())
×
968
            {
969
                double progress = ((double)step.m_elapsed / (double)step.m_fadeIn) * (double)m_progress->width();
×
970
                m_progress->setFormat(QString("-%1").arg(Function::speedToString(step.m_fadeIn - step.m_elapsed)));
×
971
                m_progress->setValue(progress);
×
972

973
                emit progressStateChanged();
×
974
            }
975
            else
976
            {
977
                m_progress->setValue(m_progress->maximum());
×
978
                m_progress->setFormat("");
×
979

980
                emit progressStateChanged();
×
981
            }
982
            return;
×
983
        }
984
        else
985
        {
986
            double progress = ((double)step.m_elapsed / (double)step.m_duration) * (double)m_progress->width();
×
987
            m_progress->setFormat(QString("-%1").arg(Function::speedToString(step.m_duration - step.m_elapsed)));
×
988
            m_progress->setValue(progress);
×
989

990
            emit progressStateChanged();
×
991
        }
992
    }
993
    else
994
    {
995
        m_progress->setValue(0);
×
996
    }
997
}
998

999
QString VCCueList::progressText()
×
1000
{
1001
    return m_progress->text();
×
1002
}
1003

1004
double VCCueList::progressPercent()
×
1005
{
1006
    return ((double)m_progress->value() * 100) / (double)m_progress->width();
×
1007
}
1008

1009
void VCCueList::startChaser(int startIndex)
4✔
1010
{
1011
    Chaser *ch = chaser();
4✔
1012
    if (ch == NULL)
4✔
1013
        return;
×
1014

1015
    adjustFunctionIntensity(ch, intensity());
4✔
1016

1017
    ChaserAction action;
1018
    action.m_action = ChaserSetStepIndex;
4✔
1019
    action.m_stepIndex = startIndex;
4✔
1020
    action.m_masterIntensity = intensity();
4✔
1021
    action.m_stepIntensity = getPrimaryIntensity();
4✔
1022
    action.m_fadeMode = getFadeMode();
4✔
1023
    ch->setAction(action);
4✔
1024

1025
    ch->start(m_doc->masterTimer(), functionParent());
4✔
1026
    emit functionStarting(m_chaserID);
4✔
1027
}
1028

1029
void VCCueList::stopChaser()
×
1030
{
1031
    Chaser *ch = chaser();
×
1032
    if (ch == NULL)
×
1033
        return;
×
1034

1035
    ch->stop(functionParent());
×
1036
    resetIntensityOverrideAttribute();
×
1037
}
1038

1039
int VCCueList::getFadeMode()
20✔
1040
{
1041
    if (sideFaderMode() != Crossfade)
20✔
1042
        return Chaser::FromFunction;
20✔
1043

1044
    if (m_sideFader->value() != 0 && m_sideFader->value() != 100)
×
1045
        return Chaser::BlendedCrossfade;
×
1046

1047
    return Chaser::Blended;
×
1048
}
1049

1050
void VCCueList::setNextPrevBehavior(NextPrevBehavior nextPrev)
×
1051
{
1052
    Q_ASSERT(nextPrev == DefaultRunFirst
×
1053
            || nextPrev == RunNext
1054
            || nextPrev == Select
1055
            || nextPrev == Nothing);
1056
    m_nextPrevBehavior = nextPrev;
×
1057
}
×
1058

1059
VCCueList::NextPrevBehavior VCCueList::nextPrevBehavior() const
1✔
1060
{
1061
    return m_nextPrevBehavior;
1✔
1062
}
1063

1064
void VCCueList::setPlaybackLayout(VCCueList::PlaybackLayout layout)
×
1065
{
1066
    if (layout == m_playbackLayout)
×
1067
        return;
×
1068

1069
    if (layout == PlayStopPause)
×
1070
    {
1071
        m_stopButton->setIcon(QIcon(":/player_pause.png"));
×
1072
        m_playbackButton->setToolTip(tr("Play/Stop Cue list"));
×
1073
        m_stopButton->setToolTip(tr("Pause Cue list"));
×
1074
    }
1075
    else if (layout == PlayPauseStop)
×
1076
    {
1077
        m_stopButton->setIcon(QIcon(":/player_stop.png"));
×
1078
        m_playbackButton->setToolTip(tr("Play/Pause Cue list"));
×
1079
        m_stopButton->setToolTip(tr("Stop Cue list"));
×
1080
    }
1081
    else
1082
    {
1083
        qWarning() << "Playback layout" << layout << "doesn't exist!";
×
1084
        layout = PlayPauseStop;
×
1085
    }
1086

1087
    m_playbackLayout = layout;
×
1088
}
1089

1090
VCCueList::PlaybackLayout VCCueList::playbackLayout() const
7✔
1091
{
1092
    return m_playbackLayout;
7✔
1093
}
1094

1095
VCCueList::FaderMode VCCueList::sideFaderMode() const
67✔
1096
{
1097
    return m_slidersMode;
67✔
1098
}
1099

1100
void VCCueList::setSideFaderMode(VCCueList::FaderMode mode)
3✔
1101
{
1102
    m_slidersMode = mode;
3✔
1103

1104
    bool show = (mode == None) ? false : true;
3✔
1105
    m_crossfadeButton->setVisible(show);
3✔
1106
    m_topPercentageLabel->setVisible(show);
3✔
1107
    m_topStepLabel->setVisible(mode == Steps ? false : show);
3✔
1108
    m_sideFader->setVisible(show);
3✔
1109
    m_bottomPercentageLabel->setVisible(mode == Steps ? false : show);
3✔
1110
    m_bottomStepLabel->setVisible(show);
3✔
1111
    m_sideFader->setMaximum(mode == Steps ? 255 : 100);
3✔
1112
    m_sideFader->setValue(mode == Steps ? 255 : 100);
3✔
1113
}
3✔
1114

1115
VCCueList::FaderMode VCCueList::stringToFaderMode(QString modeStr)
×
1116
{
1117
    if (modeStr == "Crossfade")
×
1118
        return Crossfade;
×
1119
    else if (modeStr == "Steps")
×
1120
        return Steps;
×
1121

1122
    return None;
×
1123
}
1124

1125
QString VCCueList::faderModeToString(VCCueList::FaderMode mode)
1✔
1126
{
1127
    if (mode == Crossfade)
1✔
1128
        return "Crossfade";
1✔
1129
    else if (mode == Steps)
×
1130
        return "Steps";
×
1131

1132
    return "None";
×
1133
}
1134

1135
/*****************************************************************************
1136
 * Crossfade
1137
 *****************************************************************************/
1138
void VCCueList::setFaderInfo(int index)
19✔
1139
{
1140
    Chaser *ch = chaser();
19✔
1141
    if (ch == NULL || !ch->isRunning())
19✔
1142
        return;
×
1143

1144
    int tmpIndex = ch->computeNextStep(index);
19✔
1145

1146
    m_topStepLabel->setText(QString("#%1").arg(m_primaryTop ? index + 1 : tmpIndex + 1));
19✔
1147
    m_topStepLabel->setStyleSheet(m_primaryTop ? cfLabelBlueStyle : cfLabelOrangeStyle);
19✔
1148

1149
    m_bottomStepLabel->setText(QString("#%1").arg(m_primaryTop ? tmpIndex + 1 : index + 1));
19✔
1150
    m_bottomStepLabel->setStyleSheet(m_primaryTop ? cfLabelOrangeStyle : cfLabelBlueStyle);
19✔
1151

1152
    // reset any previously set background
1153
    QTreeWidgetItem *item = m_tree->topLevelItem(m_secondaryIndex);
19✔
1154
    if (item != NULL)
19✔
1155
        item->setBackground(COL_NUM, m_defCol);
19✔
1156

1157
    item = m_tree->topLevelItem(tmpIndex);
19✔
1158
    if (item != NULL)
19✔
1159
        item->setBackground(COL_NUM, QColor("#FF8000"));
19✔
1160
    m_secondaryIndex = tmpIndex;
19✔
1161

1162
    emit sideFaderValueChanged();
19✔
1163
}
1164

1165
void VCCueList::slotShowCrossfadePanel(bool enable)
15✔
1166
{
1167
    m_topPercentageLabel->setVisible(enable);
15✔
1168
    m_topStepLabel->setVisible(enable);
15✔
1169
    m_sideFader->setVisible(enable);
15✔
1170
    m_bottomStepLabel->setVisible(enable);
15✔
1171
    m_bottomPercentageLabel->setVisible(enable);
15✔
1172

1173
    emit sideFaderButtonToggled();
15✔
1174
}
15✔
1175

1176
QString VCCueList::topPercentageValue()
×
1177
{
1178
    return m_topPercentageLabel->text();
×
1179
}
1180

1181
QString VCCueList::bottomPercentageValue()
×
1182
{
1183
    return m_bottomPercentageLabel->text();
×
1184
}
1185

1186
QString VCCueList::topStepValue()
×
1187
{
1188
    return m_topStepLabel->text();
×
1189
}
1190

1191
QString VCCueList::bottomStepValue()
×
1192
{
1193
    return m_bottomStepLabel->text();
×
1194
}
1195

1196
int VCCueList::sideFaderValue()
×
1197
{
1198
    return m_sideFader->value();
×
1199
}
1200

1201
bool VCCueList::primaryTop()
×
1202
{
1203
    return m_primaryTop;
×
1204
}
1205

1206
void VCCueList::slotSideFaderButtonChecked(bool enable)
×
1207
{
1208
    m_crossfadeButton->setChecked(enable);
×
1209
    emit sideFaderButtonChecked();
×
1210
}
×
1211

1212
bool VCCueList::isSideFaderVisible()
×
1213
{
1214
    return m_sideFader->isVisible();
×
1215
}
1216

1217
bool VCCueList::sideFaderButtonIsChecked()
×
1218
{
1219
    return m_crossfadeButton->isChecked();
×
1220
}
1221

1222
void VCCueList::slotSetSideFaderValue(int value)
×
1223
{
1224
    m_sideFader->setValue(value);
×
1225
}
×
1226

1227
void VCCueList::slotSideFaderValueChanged(int value)
×
1228
{
1229
    if (sideFaderMode() == Steps)
×
1230
    {
1231
        value = 255 - value;
×
1232
        m_topPercentageLabel->setText(QString("%1").arg(value));
×
1233

1234
        emit sideFaderValueChanged();
×
1235

1236
        Chaser *ch = chaser();
×
1237
        if (ch == NULL || ch->stopped())
×
1238
            return;
×
1239

1240
        int newStep = value; // by default we assume the Chaser has more than 256 steps
×
1241
        if (ch->stepsCount() < 256)
×
1242
        {
1243
            float stepSize = 256.0 / (float)ch->stepsCount();  //divide up the full 0..255 range
×
1244
            stepSize = qFloor((stepSize * 100000.0) + 0.5) / 100000.0; //round to 5 decimals to fix corner cases
×
1245
            if (value >= 256.0 - stepSize)
×
1246
                newStep = ch->stepsCount() - 1;
×
1247
            else
1248
                newStep = qFloor((float)value / stepSize);
×
1249
            //qDebug() << "value:" << value << " new step:" << newStep << " stepSize:" << stepSize;
1250
        }
1251

1252
        if (newStep == ch->currentStepIndex())
×
1253
            return;
×
1254

1255
        ChaserAction action;
1256
        action.m_action = ChaserSetStepIndex;
×
1257
        action.m_stepIndex = newStep;
×
1258
        action.m_masterIntensity = intensity();
×
1259
        action.m_stepIntensity = getPrimaryIntensity();
×
1260
        action.m_fadeMode = getFadeMode();
×
1261
        ch->setAction(action);
×
1262
    }
1263
    else
1264
    {
1265
        m_topPercentageLabel->setText(QString("%1%").arg(value));
×
1266
        m_bottomPercentageLabel->setText(QString("%1%").arg(100 - value));
×
1267

1268
        emit sideFaderValueChanged();
×
1269

1270
        Chaser *ch = chaser();
×
1271
        if (!(ch == NULL || ch->stopped()))
×
1272
        {
1273
            ch->adjustStepIntensity(qreal(value) / 100.0, m_primaryTop ? m_primaryIndex : m_secondaryIndex,
×
1274
                                    Chaser::FadeControlMode(getFadeMode()));
×
1275
            ch->adjustStepIntensity(qreal(100 - value) / 100.0, m_primaryTop ? m_secondaryIndex : m_primaryIndex,
×
1276
                                    Chaser::FadeControlMode(getFadeMode()));
×
1277
            stopStepIfNeeded(ch);
×
1278
        }
1279
    }
1280

1281
    updateFeedback();
×
1282
}
1283

1284
void VCCueList::stopStepIfNeeded(Chaser *ch)
×
1285
{
1286
    if (ch->runningStepsNumber() != 2)
×
1287
        return;
×
1288

1289
    int primaryValue = m_primaryTop ? m_sideFader->value() : 100 - m_sideFader->value();
×
1290
    int secondaryValue = m_primaryTop ? 100 - m_sideFader->value() : m_sideFader->value();
×
1291

1292
    ChaserAction action;
1293
    action.m_action = ChaserStopStep;
×
1294

1295
    if (primaryValue == 0)
×
1296
    {
1297
        m_primaryTop = !m_primaryTop;
×
1298
        action.m_stepIndex = m_primaryIndex;
×
1299
        ch->setAction(action);
×
1300
    }
1301
    else if (secondaryValue == 0)
×
1302
    {
1303
        action.m_stepIndex = m_secondaryIndex;
×
1304
        ch->setAction(action);
×
1305
    }
1306
}
1307

1308
/*****************************************************************************
1309
 * Key Sequences
1310
 *****************************************************************************/
1311

1312
void VCCueList::setNextKeySequence(const QKeySequence& keySequence)
6✔
1313
{
1314
    m_nextKeySequence = QKeySequence(keySequence);
6✔
1315
}
6✔
1316

1317
QKeySequence VCCueList::nextKeySequence() const
8✔
1318
{
1319
    return m_nextKeySequence;
8✔
1320
}
1321

1322
void VCCueList::setPreviousKeySequence(const QKeySequence& keySequence)
6✔
1323
{
1324
    m_previousKeySequence = QKeySequence(keySequence);
6✔
1325
}
6✔
1326

1327
QKeySequence VCCueList::previousKeySequence() const
8✔
1328
{
1329
    return m_previousKeySequence;
8✔
1330
}
1331

1332
void VCCueList::setPlaybackKeySequence(const QKeySequence& keySequence)
6✔
1333
{
1334
    m_playbackKeySequence = QKeySequence(keySequence);
6✔
1335
}
6✔
1336

1337
QKeySequence VCCueList::playbackKeySequence() const
8✔
1338
{
1339
    return m_playbackKeySequence;
8✔
1340
}
1341

1342
void VCCueList::setStopKeySequence(const QKeySequence &keySequence)
3✔
1343
{
1344
    m_stopKeySequence = QKeySequence(keySequence);
3✔
1345
}
3✔
1346

1347
QKeySequence VCCueList::stopKeySequence() const
3✔
1348
{
1349
    return m_stopKeySequence;
3✔
1350
}
1351

1352
void VCCueList::slotKeyPressed(const QKeySequence& keySequence)
7✔
1353
{
1354
    if (acceptsInput() == false)
7✔
1355
        return;
×
1356

1357
    if (m_nextKeySequence == keySequence)
7✔
1358
        slotNextCue();
3✔
1359
    else if (m_previousKeySequence == keySequence)
4✔
1360
        slotPreviousCue();
2✔
1361
    else if (m_playbackKeySequence == keySequence)
2✔
1362
        slotPlayback();
1✔
1363
    else if (m_stopKeySequence == keySequence)
1✔
1364
        slotStop();
×
1365
}
1366

1367
void VCCueList::updateFeedback()
26✔
1368
{
1369
    int fbv = int(SCALE(float(m_sideFader->value()), 
26✔
1370
                        float(m_sideFader->minimum()),
1371
                        float(m_sideFader->maximum()), 
1372
                        float(0), float(UCHAR_MAX)));
26✔
1373
    sendFeedback(fbv, sideFaderInputSourceId);
26✔
1374

1375
    Chaser *ch = chaser();
26✔
1376
    if (ch == NULL)
26✔
1377
        return;
3✔
1378

1379
    sendFeedback(ch->isRunning() ? UCHAR_MAX : 0, playbackInputSourceId);
23✔
1380
}
1381

1382
/*****************************************************************************
1383
 * External Input
1384
 *****************************************************************************/
1385

1386
void VCCueList::slotInputValueChanged(quint32 universe, quint32 channel, uchar value)
12✔
1387
{
1388
    /* Don't let input data through in design mode or if disabled */
1389
    if (acceptsInput() == false)
12✔
1390
        return;
×
1391

1392
    quint32 pagedCh = (page() << 16) | channel;
12✔
1393

1394
    if (checkInputSource(universe, pagedCh, value, sender(), nextInputSourceId))
12✔
1395
    {
1396
        // Use hysteresis for values, in case the cue list is being controlled
1397
        // by a slider. The value has to go to zero before the next non-zero
1398
        // value is accepted as input. And the non-zero values have to visit
1399
        // above $HYSTERESIS before a zero is accepted again.
1400
        if (m_nextLatestValue == 0 && value > 0)
3✔
1401
        {
1402
            slotNextCue();
2✔
1403
            m_nextLatestValue = value;
2✔
1404
        }
1405
        else if (m_nextLatestValue > HYSTERESIS && value == 0)
1✔
1406
        {
1407
            m_nextLatestValue = 0;
1✔
1408
        }
1409

1410
        if (value > HYSTERESIS)
3✔
1411
            m_nextLatestValue = value;
2✔
1412
    }
1413
    else if (checkInputSource(universe, pagedCh, value, sender(), previousInputSourceId))
9✔
1414
    {
1415
        // Use hysteresis for values, in case the cue list is being controlled
1416
        // by a slider. The value has to go to zero before the next non-zero
1417
        // value is accepted as input. And the non-zero values have to visit
1418
        // above $HYSTERESIS before a zero is accepted again.
1419
        if (m_previousLatestValue == 0 && value > 0)
3✔
1420
        {
1421
            slotPreviousCue();
2✔
1422
            m_previousLatestValue = value;
2✔
1423
        }
1424
        else if (m_previousLatestValue > HYSTERESIS && value == 0)
1✔
1425
        {
1426
            m_previousLatestValue = 0;
1✔
1427
        }
1428

1429
        if (value > HYSTERESIS)
3✔
1430
            m_previousLatestValue = value;
2✔
1431
    }
1432
    else if (checkInputSource(universe, pagedCh, value, sender(), playbackInputSourceId))
6✔
1433
    {
1434
        // Use hysteresis for values, in case the cue list is being controlled
1435
        // by a slider. The value has to go to zero before the next non-zero
1436
        // value is accepted as input. And the non-zero values have to visit
1437
        // above $HYSTERESIS before a zero is accepted again.
1438
        if (m_playbackLatestValue == 0 && value > 0)
2✔
1439
        {
1440
            slotPlayback();
1✔
1441
            m_playbackLatestValue = value;
1✔
1442
        }
1443
        else if (m_playbackLatestValue > HYSTERESIS && value == 0)
1✔
1444
        {
1445
            m_playbackLatestValue = 0;
1✔
1446
        }
1447

1448
        if (value > HYSTERESIS)
2✔
1449
            m_playbackLatestValue = value;
1✔
1450
    }
1451
    else if (checkInputSource(universe, pagedCh, value, sender(), stopInputSourceId))
4✔
1452
    {
1453
        // Use hysteresis for values, in case the cue list is being controlled
1454
        // by a slider. The value has to go to zero before the next non-zero
1455
        // value is accepted as input. And the non-zero values have to visit
1456
        // above $HYSTERESIS before a zero is accepted again.
1457
        if (m_stopLatestValue == 0 && value > 0)
×
1458
        {
1459
            slotStop();
×
1460
            m_stopLatestValue = value;
×
1461
        }
1462
        else if (m_stopLatestValue > HYSTERESIS && value == 0)
×
1463
        {
1464
            m_stopLatestValue = 0;
×
1465
        }
1466

1467
        if (value > HYSTERESIS)
×
1468
            m_stopLatestValue = value;
×
1469
    }
1470
    else if (checkInputSource(universe, pagedCh, value, sender(), sideFaderInputSourceId))
4✔
1471
    {
1472
        if (sideFaderMode() == None)
×
1473
            return;
×
1474

1475
        float val = SCALE((float) value, (float) 0, (float) UCHAR_MAX,
×
1476
                          (float) m_sideFader->minimum(),
1477
                          (float) m_sideFader->maximum());
1478
        m_sideFader->setValue(val);
×
1479
    }
1480
}
1481

1482
/*****************************************************************************
1483
 * VCWidget-inherited methods
1484
 *****************************************************************************/
1485

1486
void VCCueList::adjustIntensity(qreal val)
×
1487
{
1488
    Chaser *ch = chaser();
×
1489
    if (ch != NULL)
×
1490
    {
1491
        adjustFunctionIntensity(ch, val);
×
1492
/*
1493
        // Refresh intensity of current steps
1494
        if (!ch->stopped() && sideFaderMode() == Crossfade && m_sideFader->value() != 100)
1495
        {
1496
                ch->adjustStepIntensity((qreal)m_sideFader->value() / 100, m_primaryTop ? m_primaryIndex : m_secondaryIndex);
1497
                ch->adjustStepIntensity((qreal)(100 - m_sideFader->value()) / 100, m_primaryTop ? m_secondaryIndex : m_primaryIndex);
1498
        }
1499
*/
1500
    }
1501

1502
    VCWidget::adjustIntensity(val);
×
1503
}
×
1504

1505
void VCCueList::setCaption(const QString& text)
20✔
1506
{
1507
    VCWidget::setCaption(text);
20✔
1508

1509
    QStringList list;
20✔
1510
    list << "#" << text << tr("Fade In") << tr("Fade Out") << tr("Duration") << tr("Notes");
20✔
1511
    m_tree->setHeaderLabels(list);
20✔
1512
}
20✔
1513

1514
void VCCueList::setFont(const QFont& font)
1✔
1515
{
1516
    VCWidget::setFont(font);
1✔
1517

1518
    QFontMetrics m_fm = QFontMetrics(font);
1✔
1519
#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
1520
    int w = m_fm.width("100%");
1521
#else
1522
    int w = m_fm.horizontalAdvance("100%");
1✔
1523
#endif
1524
    m_topPercentageLabel->setFixedWidth(w);
1✔
1525
    m_bottomPercentageLabel->setFixedWidth(w);
1✔
1526
}
1✔
1527

1528
void VCCueList::slotModeChanged(Doc::Mode mode)
23✔
1529
{
1530
    bool enable = false;
23✔
1531
    if (mode == Doc::Operate)
23✔
1532
    {
1533
        m_progress->setStyleSheet(progressFadeStyle);
6✔
1534
        m_progress->setRange(0, m_progress->width());
6✔
1535
        enable = true;
6✔
1536
        // send the initial feedback for the current step slider
1537
        updateFeedback();
6✔
1538
    }
1539
    else
1540
    {
1541
        m_topStepLabel->setStyleSheet(cfLabelNoStyle);
17✔
1542
        m_topStepLabel->setText("");
17✔
1543
        m_bottomStepLabel->setStyleSheet(cfLabelNoStyle);
17✔
1544
        m_bottomStepLabel->setText("");
17✔
1545
        m_progress->setStyleSheet(progressDisabledStyle);
17✔
1546
        // reset any previously set background
1547
        QTreeWidgetItem *item = m_tree->topLevelItem(m_secondaryIndex);
17✔
1548
        if (item != NULL)
17✔
1549
            item->setBackground(COL_NUM, m_defCol);
2✔
1550
    }
1551

1552
    enableWidgetUI(enable);
23✔
1553

1554
    /* Always start from the beginning */
1555
    m_tree->setCurrentItem(NULL);
23✔
1556

1557
    VCWidget::slotModeChanged(mode);
23✔
1558

1559
    emit sideFaderValueChanged();
23✔
1560
}
23✔
1561

1562
void VCCueList::editProperties()
×
1563
{
1564
    VCCueListProperties prop(this, m_doc);
×
1565
    if (prop.exec() == QDialog::Accepted)
×
1566
        m_doc->setModified();
×
1567
}
×
1568

1569
void VCCueList::playCueAtIndex(int idx)
4✔
1570
{
1571
    if (mode() != Doc::Operate)
4✔
1572
        return;
×
1573

1574
    m_primaryIndex = idx;
4✔
1575

1576
    Chaser *ch = chaser();
4✔
1577
    if (ch == NULL)
4✔
1578
        return;
×
1579

1580
    if (ch->isRunning())
4✔
1581
    {
1582
        ChaserAction action;
1583
        action.m_action = ChaserSetStepIndex;
3✔
1584
        action.m_stepIndex = m_primaryIndex;
3✔
1585
        action.m_masterIntensity = intensity();
3✔
1586
        action.m_stepIntensity = getPrimaryIntensity();
3✔
1587
        action.m_fadeMode = getFadeMode();
3✔
1588
        ch->setAction(action);
3✔
1589
    }
1590
    else
1591
    {
1592
        startChaser(m_primaryIndex);
1✔
1593
    }
1594

1595
    if (sideFaderMode() == Crossfade)
4✔
1596
        setFaderInfo(m_primaryIndex);
×
1597
}
1598

1599
FunctionParent VCCueList::functionParent() const
4✔
1600
{
1601
    return FunctionParent(FunctionParent::ManualVCWidget, id());
4✔
1602
}
1603

1604
/*****************************************************************************
1605
 * Load & Save
1606
 *****************************************************************************/
1607

1608
bool VCCueList::loadXML(QXmlStreamReader &root)
3✔
1609
{
1610
    QList <quint32> legacyStepList;
3✔
1611

1612
    if (root.name() != KXMLQLCVCCueList)
3✔
1613
    {
1614
        qWarning() << Q_FUNC_INFO << "CueList node not found";
1✔
1615
        return false;
1✔
1616
    }
1617

1618
    /* Widget commons */
1619
    loadXMLCommon(root);
2✔
1620

1621
    /* Children */
1622
    while (root.readNextStartElement())
18✔
1623
    {
1624
        if (root.name() == KXMLQLCWindowState)
16✔
1625
        {
1626
            bool visible = false;
1✔
1627
            int x = 0, y = 0, w = 0, h = 0;
1✔
1628
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
1✔
1629
            setGeometry(x, y, w, h);
1✔
1630
        }
1631
        else if (root.name() == KXMLQLCVCWidgetAppearance)
15✔
1632
        {
1633
            loadXMLAppearance(root);
1✔
1634
        }
1635
        else if (root.name() == KXMLQLCVCCueListNext)
14✔
1636
        {
1637
            QString str = loadXMLSources(root, nextInputSourceId);
1✔
1638
            if (str.isEmpty() == false)
1✔
1639
                m_nextKeySequence = stripKeySequence(QKeySequence(str));
1✔
1640
        }
1✔
1641
        else if (root.name() == KXMLQLCVCCueListPrevious)
13✔
1642
        {
1643
            QString str = loadXMLSources(root, previousInputSourceId);
1✔
1644
            if (str.isEmpty() == false)
1✔
1645
                m_previousKeySequence = stripKeySequence(QKeySequence(str));
1✔
1646
        }
1✔
1647
        else if (root.name() == KXMLQLCVCCueListPlayback)
12✔
1648
        {
1649
            QString str = loadXMLSources(root, playbackInputSourceId);
1✔
1650
            if (str.isEmpty() == false)
1✔
1651
                m_playbackKeySequence = stripKeySequence(QKeySequence(str));
1✔
1652
        }
1✔
1653
        else if (root.name() == KXMLQLCVCCueListStop)
11✔
1654
        {
1655
            QString str = loadXMLSources(root, stopInputSourceId);
1✔
1656
            if (str.isEmpty() == false)
1✔
1657
                m_stopKeySequence = stripKeySequence(QKeySequence(str));
1✔
1658
        }
1✔
1659
        else if (root.name() == KXMLQLCVCCueListSlidersMode)
10✔
1660
        {
1661
            setSideFaderMode(stringToFaderMode(root.readElementText()));
×
1662
        }
1663
        else if (root.name() == KXMLQLCVCCueListCrossfadeLeft)
10✔
1664
        {
1665
            loadXMLSources(root, sideFaderInputSourceId);
×
1666
        }
1667
        else if (root.name() == KXMLQLCVCCueListCrossfadeRight) /* Legacy */
10✔
1668
        {
1669
            root.skipCurrentElement();
×
1670
        }
1671
        else if (root.name() == KXMLQLCVCWidgetKey) /* Legacy */
10✔
1672
        {
1673
            setNextKeySequence(QKeySequence(root.readElementText()));
×
1674
        }
1675
        else if (root.name() == KXMLQLCVCCueListChaser)
10✔
1676
        {
1677
            setChaser(root.readElementText().toUInt());
×
1678
        }
1679
        else if (root.name() == KXMLQLCVCCueListPlaybackLayout)
10✔
1680
        {
1681
            PlaybackLayout layout = PlaybackLayout(root.readElementText().toInt());
×
1682
            if (layout != PlayPauseStop && layout != PlayStopPause)
×
1683
            {
1684
                qWarning() << Q_FUNC_INFO << "Playback layout" << layout << "does not exist.";
×
1685
                layout = PlayPauseStop;
×
1686
            }
1687
            setPlaybackLayout(layout);
×
1688
        }
1689
        else if (root.name() == KXMLQLCVCCueListNextPrevBehavior)
10✔
1690
        {
1691
            NextPrevBehavior nextPrev = NextPrevBehavior(root.readElementText().toInt());
×
1692
            if (nextPrev != DefaultRunFirst
×
1693
                    && nextPrev != RunNext
×
1694
                    && nextPrev != Select
×
1695
                    && nextPrev != Nothing)
×
1696
            {
1697
                qWarning() << Q_FUNC_INFO << "Next/Prev behavior" << nextPrev << "does not exist.";
×
1698
                nextPrev = DefaultRunFirst;
×
1699
            }
1700
            setNextPrevBehavior(nextPrev);
×
1701
        }
1702
        else if (root.name() == KXMLQLCVCCueListCrossfade)
10✔
1703
        {
1704
            m_crossfadeButton->setChecked(true);
×
1705
            root.skipCurrentElement();
×
1706
        }
1707
        else if (root.name() == KXMLQLCVCCueListFunction)
10✔
1708
        {
1709
            // Collect legacy file format steps into a list
1710
            legacyStepList << root.readElementText().toUInt();
6✔
1711
        }
1712
        else
1713
        {
1714
            qWarning() << Q_FUNC_INFO << "Unknown cuelist tag:" << root.name().toString();
4✔
1715
            root.skipCurrentElement();
4✔
1716
        }
1717
    }
1718

1719
    if (legacyStepList.isEmpty() == false)
2✔
1720
    {
1721
        /* Construct a new chaser from legacy step functions and use that chaser */
1722
        Chaser *chaser = new Chaser(m_doc);
1✔
1723
        chaser->setName(caption());
1✔
1724

1725
        // Legacy cue lists relied on individual functions' timings and a common hold time
1726
        chaser->setFadeInMode(Chaser::Default);
1✔
1727
        chaser->setFadeOutMode(Chaser::Default);
1✔
1728
        chaser->setDurationMode(Chaser::Common);
1✔
1729

1730
        foreach (quint32 id, legacyStepList)
7✔
1731
        {
1732
            Function *function = m_doc->function(id);
6✔
1733
            if (function == NULL)
6✔
1734
                continue;
2✔
1735

1736
            // Legacy cuelists relied on individual functions' fadein/out speed and
1737
            // infinite duration. So don't touch them at all.
1738
            ChaserStep step(id);
4✔
1739
            chaser->addStep(step);
4✔
1740
        }
5✔
1741

1742
        // Add the chaser to Doc and attach it to the cue list
1743
        m_doc->addFunction(chaser);
1✔
1744
        setChaser(chaser->id());
1✔
1745
    }
1746

1747
    return true;
2✔
1748
}
3✔
1749

1750
bool VCCueList::saveXML(QXmlStreamWriter *doc)
1✔
1751
{
1752
    Q_ASSERT(doc != NULL);
1✔
1753

1754
    /* VC CueList entry */
1755
    doc->writeStartElement(KXMLQLCVCCueList);
2✔
1756

1757
    saveXMLCommon(doc);
1✔
1758

1759
    /* Window state */
1760
    saveXMLWindowState(doc);
1✔
1761

1762
    /* Appearance */
1763
    saveXMLAppearance(doc);
1✔
1764

1765
    /* Chaser */
1766
    doc->writeTextElement(KXMLQLCVCCueListChaser, QString::number(chaserID()));
2✔
1767

1768
    /* Playback layout */
1769
    if (playbackLayout() != PlayPauseStop)
1✔
1770
        doc->writeTextElement(KXMLQLCVCCueListPlaybackLayout, QString::number(playbackLayout()));
×
1771

1772
    /* Next/Prev behavior */
1773
    doc->writeTextElement(KXMLQLCVCCueListNextPrevBehavior, QString::number(nextPrevBehavior()));
2✔
1774

1775
    /* Next cue */
1776
    doc->writeStartElement(KXMLQLCVCCueListNext);
2✔
1777
    if (m_nextKeySequence.toString().isEmpty() == false)
1✔
1778
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_nextKeySequence.toString());
2✔
1779
    saveXMLInput(doc, inputSource(nextInputSourceId));
1✔
1780
    doc->writeEndElement();
1✔
1781

1782
    /* Previous cue */
1783
    doc->writeStartElement(KXMLQLCVCCueListPrevious);
2✔
1784
    if (m_previousKeySequence.toString().isEmpty() == false)
1✔
1785
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_previousKeySequence.toString());
2✔
1786
    saveXMLInput(doc, inputSource(previousInputSourceId));
1✔
1787
    doc->writeEndElement();
1✔
1788

1789
    /* Cue list playback */
1790
    doc->writeStartElement(KXMLQLCVCCueListPlayback);
2✔
1791
    if (m_playbackKeySequence.toString().isEmpty() == false)
1✔
1792
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_playbackKeySequence.toString());
2✔
1793
    saveXMLInput(doc, inputSource(playbackInputSourceId));
1✔
1794
    doc->writeEndElement();
1✔
1795

1796
    /* Cue list stop */
1797
    doc->writeStartElement(KXMLQLCVCCueListStop);
2✔
1798
    if (m_stopKeySequence.toString().isEmpty() == false)
1✔
1799
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_stopKeySequence.toString());
2✔
1800
    saveXMLInput(doc, inputSource(stopInputSourceId));
1✔
1801
    doc->writeEndElement();
1✔
1802

1803
    /* Crossfade cue list */
1804
    if (sideFaderMode() != None)
1✔
1805
        doc->writeTextElement(KXMLQLCVCCueListSlidersMode, faderModeToString(sideFaderMode()));
2✔
1806

1807
    QSharedPointer<QLCInputSource> cf1Src = inputSource(sideFaderInputSourceId);
1✔
1808
    if (!cf1Src.isNull() && cf1Src->isValid())
1✔
1809
    {
1810
        doc->writeStartElement(KXMLQLCVCCueListCrossfadeLeft);
×
1811
        saveXMLInput(doc, cf1Src);
×
1812
        doc->writeEndElement();
×
1813
    }
1814

1815
    /* End the <CueList> tag */
1816
    doc->writeEndElement();
1✔
1817

1818
    return true;
1✔
1819
}
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