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

mcallegari / qlcplus / 6683238402

29 Oct 2023 12:10PM UTC coverage: 28.07%. Remained the same
6683238402

push

github

mcallegari
engine: fix build

15385 of 54809 relevant lines covered (28.07%)

20267.63 hits per line

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

63.31
/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)
90
    , m_playbackLayout(PlayPauseStop)
91
    , m_timer(NULL)
92
    , m_primaryIndex(0)
93
    , m_secondaryIndex(0)
94
    , m_primaryTop(true)
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());
30✔
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;
30✔
251
    QVariant var = settings.value(SETTINGS_CUELIST_SIZE);
30✔
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()
88✔
386
{
387
    if (m_chaserID == Function::invalidId())
88✔
388
        return NULL;
6✔
389
    Chaser *chaser = qobject_cast<Chaser*>(m_doc->function(m_chaserID));
82✔
390
    return chaser;
82✔
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());
24✔
404
    while (it.hasNext() == true)
59✔
405
    {
406
        ChaserStep step(it.next());
94✔
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
    }
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
}
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

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

666
    Chaser *ch = chaser();
×
667
    if (ch == NULL)
×
668
        return;
×
669

670
    if (ch->isRunning())
×
671
    {
672
        if (playbackLayout() == PlayPauseStop)
×
673
        {
674
            stopChaser();
×
675
            m_playbackButton->setStyleSheet(QString("QToolButton{ background: %1; }")
×
676
                                            .arg(m_stopButton->palette().window().color().name()));
×
677
            m_progress->setFormat("");
×
678
            m_progress->setValue(0);
×
679
        }
680
        else if (playbackLayout() == PlayStopPause)
×
681
        {
682
            if (ch->isPaused())
×
683
            {
684
                m_stopButton->setStyleSheet(QString("QToolButton{ background: %1; }")
×
685
                                                .arg(m_playbackButton->palette().window().color().name()));
×
686
                m_stopButton->setIcon(QIcon(":/player_pause.png"));
×
687
            }
688
            else
689
            {
690
                m_stopButton->setStyleSheet("QToolButton{ background: #5B81FF; }");
×
691
            }
692
            ch->setPause(!ch->isPaused());
×
693
        }
694
    }
695
    else
696
    {
697
        m_primaryIndex = 0;
×
698
        m_tree->setCurrentItem(m_tree->topLevelItem(getFirstIndex()));
×
699
    }
700
}
701

702
void VCCueList::slotNextCue()
10✔
703
{
704
    if (mode() != Doc::Operate)
10✔
705
        return;
1✔
706

707
    Chaser *ch = chaser();
9✔
708
    if (ch == NULL)
9✔
709
        return;
×
710

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

748
void VCCueList::slotPreviousCue()
8✔
749
{
750
    if (mode() != Doc::Operate)
8✔
751
        return;
1✔
752

753
    Chaser *ch = chaser();
7✔
754
    if (ch == NULL)
7✔
755
        return;
×
756

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

794
void VCCueList::slotCurrentStepChanged(int stepNumber)
19✔
795
{
796
    // Chaser is being edited, channels count may change.
797
    // Wait for the CueList to update its steps.
798
    if (m_updateTimer->isActive())
19✔
799
        return;
×
800

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

812
        float stepVal;
813
        int stepsCount = m_tree->topLevelItemCount();
×
814
        if (stepsCount < 256)
×
815
            stepVal = 255.0 / (float)stepsCount;
×
816
        else
817
            stepVal = 1.0;
×
818
        int slValue = (stepVal * (float)stepNumber);
×
819
        if (slValue > 255)
×
820
            slValue = 255;
×
821

822
        int upperBound = 255 - slValue;
×
823
        int lowerBound = qFloor(upperBound - stepVal);
×
824
        //qDebug() << "Slider value:" << m_slider1->value() << "Step range:" << (255 - slValue) << (255 - slValue - stepVal);
825
        // if the Step slider is already in range, then do not set its value
826
        // this means a user interaction is going on, either with the mouse or external controller
827
        if (m_sideFader->value() < lowerBound || m_sideFader->value() >= upperBound)
×
828
        {
829
            m_sideFader->blockSignals(true);
×
830
            m_sideFader->setValue(upperBound);
×
831
            m_topPercentageLabel->setText(QString("%1").arg(slValue));
×
832
            m_sideFader->blockSignals(false);
×
833
        }
834
    }
835
    else
836
    {
837
        setFaderInfo(m_primaryIndex);
19✔
838
    }
839
    emit stepChanged(m_primaryIndex);
19✔
840
}
841

842
void VCCueList::slotItemActivated(QTreeWidgetItem *item)
5✔
843
{
844
    if (mode() != Doc::Operate)
5✔
845
        return;
1✔
846

847
    playCueAtIndex(m_tree->indexOfTopLevelItem(item));
4✔
848
}
849

850
void VCCueList::slotItemChanged(QTreeWidgetItem *item, int column)
374✔
851
{
852
    if (m_listIsUpdating || column != COL_NOTES)
374✔
853
        return;
374✔
854

855
    Chaser *ch = chaser();
×
856
    if (ch == NULL)
×
857
        return;
×
858

859
    QString itemText = item->text(column);
×
860
    int idx = m_tree->indexOfTopLevelItem(item);
×
861
    ChaserStep step = ch->steps().at(idx);
×
862

863
    step.note = itemText;
×
864
    ch->replaceStep(step, idx);
×
865
}
866

867
void VCCueList::slotFunctionRunning(quint32 fid)
4✔
868
{
869
    if (fid != m_chaserID)
4✔
870
        return;
×
871

872
    if (playbackLayout() == PlayPauseStop)
4✔
873
        m_playbackButton->setIcon(QIcon(":/player_pause.png"));
4✔
874
    else if (playbackLayout() == PlayStopPause)
×
875
        m_playbackButton->setIcon(QIcon(":/player_stop.png"));
×
876
    m_timer->start(PROGRESS_INTERVAL);
4✔
877
    updateFeedback();
4✔
878
}
879

880
void VCCueList::slotFunctionStopped(quint32 fid)
14✔
881
{
882
    if (fid != m_chaserID)
14✔
883
        return;
×
884

885
    m_playbackButton->setIcon(QIcon(":/player_play.png"));
14✔
886
    m_topStepLabel->setText("");
14✔
887
    m_topStepLabel->setStyleSheet(cfLabelNoStyle);
14✔
888
    m_bottomStepLabel->setText("");
14✔
889
    m_bottomStepLabel->setStyleSheet(cfLabelNoStyle);
14✔
890
    // reset any previously set background
891
    QTreeWidgetItem *item = m_tree->topLevelItem(m_secondaryIndex);
14✔
892
    if (item != NULL)
14✔
893
        item->setBackground(COL_NUM, m_defCol);
11✔
894

895
    emit stepChanged(-1);
14✔
896

897
    m_progress->setFormat("");
14✔
898
    m_progress->setValue(0);
14✔
899

900
    qDebug() << Q_FUNC_INFO << "Cue stopped";
14✔
901
    updateFeedback();
14✔
902
}
903

904
void VCCueList::slotProgressTimeout()
×
905
{
906
    Chaser *ch = chaser();
×
907
    if (ch == NULL || !ch->isRunning())
×
908
        return;
×
909

910
    ChaserRunnerStep step(ch->currentRunningStep());
×
911
    if (step.m_function != NULL)
×
912
    {
913
        int status = m_progress->property("status").toInt();
×
914
        int newstatus;
915
        if (step.m_fadeIn == Function::defaultSpeed())
×
916
            newstatus = 1;
×
917
        else if (step.m_elapsed > (quint32)step.m_fadeIn)
×
918
            newstatus = 1;
×
919
        else
920
            newstatus = 0;
×
921

922
        if (newstatus != status)
×
923
        {
924
            if (newstatus == 0)
×
925
                m_progress->setStyleSheet(progressFadeStyle);
×
926
            else
927
                m_progress->setStyleSheet(progressHoldStyle);
×
928
            m_progress->setProperty("status", newstatus);
×
929
        }
930
        if (step.m_duration == Function::infiniteSpeed())
×
931
        {
932
            if (newstatus == 0 && step.m_fadeIn != Function::defaultSpeed())
×
933
            {
934
                double progress = ((double)step.m_elapsed / (double)step.m_fadeIn) * (double)m_progress->width();
×
935
                m_progress->setFormat(QString("-%1").arg(Function::speedToString(step.m_fadeIn - step.m_elapsed)));
×
936
                m_progress->setValue(progress);
×
937
            }
938
            else
939
            {
940
                m_progress->setValue(m_progress->maximum());
×
941
                m_progress->setFormat("");
×
942
            }
943
            return;
×
944
        }
945
        else
946
        {
947
            double progress = ((double)step.m_elapsed / (double)step.m_duration) * (double)m_progress->width();
×
948
            m_progress->setFormat(QString("-%1").arg(Function::speedToString(step.m_duration - step.m_elapsed)));
×
949
            m_progress->setValue(progress);
×
950
        }
951
    }
952
    else
953
    {
954
        m_progress->setValue(0);
×
955
    }
956
}
957

958
void VCCueList::startChaser(int startIndex)
4✔
959
{
960
    Chaser *ch = chaser();
4✔
961
    if (ch == NULL)
4✔
962
        return;
×
963

964
    adjustFunctionIntensity(ch, intensity());
4✔
965

966
    ChaserAction action;
967
    action.m_action = ChaserSetStepIndex;
4✔
968
    action.m_stepIndex = startIndex;
4✔
969
    action.m_masterIntensity = intensity();
4✔
970
    action.m_stepIntensity = getPrimaryIntensity();
4✔
971
    action.m_fadeMode = getFadeMode();
4✔
972
    ch->setAction(action);
4✔
973

974
    ch->start(m_doc->masterTimer(), functionParent());
4✔
975
    emit functionStarting(m_chaserID);
4✔
976
}
977

978
void VCCueList::stopChaser()
×
979
{
980
    Chaser *ch = chaser();
×
981
    if (ch == NULL)
×
982
        return;
×
983

984
    ch->stop(functionParent());
×
985
    resetIntensityOverrideAttribute();
×
986
}
987

988
int VCCueList::getFadeMode()
20✔
989
{
990
    if (sideFaderMode() != Crossfade)
20✔
991
        return Chaser::FromFunction;
20✔
992

993
    if (m_sideFader->value() != 0 && m_sideFader->value() != 100)
×
994
        return Chaser::BlendedCrossfade;
×
995

996
    return Chaser::Blended;
×
997
}
998

999
void VCCueList::setNextPrevBehavior(NextPrevBehavior nextPrev)
×
1000
{
1001
    Q_ASSERT(nextPrev == DefaultRunFirst
×
1002
            || nextPrev == RunNext
1003
            || nextPrev == Select
1004
            || nextPrev == Nothing);
1005
    m_nextPrevBehavior = nextPrev;
×
1006
}
×
1007

1008
VCCueList::NextPrevBehavior VCCueList::nextPrevBehavior() const
1✔
1009
{
1010
    return m_nextPrevBehavior;
1✔
1011
}
1012

1013
void VCCueList::setPlaybackLayout(VCCueList::PlaybackLayout layout)
×
1014
{
1015
    if (layout == m_playbackLayout)
×
1016
        return;
×
1017

1018
    if (layout == PlayStopPause)
×
1019
    {
1020
        m_stopButton->setIcon(QIcon(":/player_pause.png"));
×
1021
        m_playbackButton->setToolTip(tr("Play/Stop Cue list"));
×
1022
        m_stopButton->setToolTip(tr("Pause Cue list"));
×
1023
    }
1024
    else if (layout == PlayPauseStop)
×
1025
    {
1026
        m_stopButton->setIcon(QIcon(":/player_stop.png"));
×
1027
        m_playbackButton->setToolTip(tr("Play/Pause Cue list"));
×
1028
        m_stopButton->setToolTip(tr("Stop Cue list"));
×
1029
    }
1030
    else
1031
    {
1032
        qWarning() << "Playback layout" << layout << "doesn't exist!";
×
1033
        layout = PlayPauseStop;
×
1034
    }
1035

1036
    m_playbackLayout = layout;
×
1037
}
1038

1039
VCCueList::PlaybackLayout VCCueList::playbackLayout() const
7✔
1040
{
1041
    return m_playbackLayout;
7✔
1042
}
1043

1044
VCCueList::FaderMode VCCueList::sideFaderMode() const
67✔
1045
{
1046
    return m_slidersMode;
67✔
1047
}
1048

1049
void VCCueList::setSideFaderMode(VCCueList::FaderMode mode)
3✔
1050
{
1051
    m_slidersMode = mode;
3✔
1052

1053
    bool show = (mode == None) ? false : true;
3✔
1054
    m_crossfadeButton->setVisible(show);
3✔
1055
    m_topPercentageLabel->setVisible(show);
3✔
1056
    m_topStepLabel->setVisible(mode == Steps ? false : show);
3✔
1057
    m_sideFader->setVisible(show);
3✔
1058
    m_bottomPercentageLabel->setVisible(mode == Steps ? false : show);
3✔
1059
    m_bottomStepLabel->setVisible(show);
3✔
1060
    m_sideFader->setMaximum(mode == Steps ? 255 : 100);
3✔
1061
    m_sideFader->setValue(mode == Steps ? 255 : 100);
3✔
1062
}
3✔
1063

1064
VCCueList::FaderMode VCCueList::stringToFaderMode(QString modeStr)
×
1065
{
1066
    if (modeStr == "Crossfade")
×
1067
        return Crossfade;
×
1068
    else if (modeStr == "Steps")
×
1069
        return Steps;
×
1070

1071
    return None;
×
1072
}
1073

1074
QString VCCueList::faderModeToString(VCCueList::FaderMode mode)
1✔
1075
{
1076
    if (mode == Crossfade)
1✔
1077
        return "Crossfade";
1✔
1078
    else if (mode == Steps)
×
1079
        return "Steps";
×
1080

1081
    return "None";
×
1082
}
1083

1084
/*****************************************************************************
1085
 * Crossfade
1086
 *****************************************************************************/
1087
void VCCueList::setFaderInfo(int index)
19✔
1088
{
1089
    Chaser *ch = chaser();
19✔
1090
    if (ch == NULL || !ch->isRunning())
19✔
1091
        return;
×
1092

1093
    int tmpIndex = ch->computeNextStep(index);
19✔
1094

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

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

1101
    // reset any previously set background
1102
    QTreeWidgetItem *item = m_tree->topLevelItem(m_secondaryIndex);
19✔
1103
    if (item != NULL)
19✔
1104
        item->setBackground(COL_NUM, m_defCol);
19✔
1105

1106
    item = m_tree->topLevelItem(tmpIndex);
19✔
1107
    if (item != NULL)
19✔
1108
        item->setBackground(COL_NUM, QColor("#FF8000"));
19✔
1109
    m_secondaryIndex = tmpIndex;
19✔
1110
}
1111

1112
void VCCueList::slotShowCrossfadePanel(bool enable)
15✔
1113
{
1114
    m_topPercentageLabel->setVisible(enable);
15✔
1115
    m_topStepLabel->setVisible(enable);
15✔
1116
    m_sideFader->setVisible(enable);
15✔
1117
    m_bottomStepLabel->setVisible(enable);
15✔
1118
    m_bottomPercentageLabel->setVisible(enable);
15✔
1119
}
15✔
1120

1121
void VCCueList::slotSideFaderValueChanged(int value)
×
1122
{
1123
    if (sideFaderMode() == Steps)
×
1124
    {
1125
        value = 255 - value;
×
1126
        m_topPercentageLabel->setText(QString("%1").arg(value));
×
1127

1128
        Chaser *ch = chaser();
×
1129
        if (ch == NULL || ch->stopped())
×
1130
            return;
×
1131

1132
        int newStep = value; // by default we assume the Chaser has more than 256 steps
×
1133
        if (ch->stepsCount() < 256)
×
1134
        {
1135
            float stepSize = 255.0 / (float)ch->stepsCount();
×
1136
            if(value >= 255.0 - stepSize)
×
1137
                newStep = ch->stepsCount() - 1;
×
1138
            else
1139
                newStep = qFloor((float)value / stepSize);
×
1140
        }
1141
        //qDebug() << "value:" << value << "steps:" << ch->stepsCount() << "new step:" << newStep;
1142

1143
        if (newStep == ch->currentStepIndex())
×
1144
            return;
×
1145

1146
        ChaserAction action;
1147
        action.m_action = ChaserSetStepIndex;
×
1148
        action.m_stepIndex = newStep;
×
1149
        action.m_masterIntensity = intensity();
×
1150
        action.m_stepIntensity = getPrimaryIntensity();
×
1151
        action.m_fadeMode = getFadeMode();
×
1152
        ch->setAction(action);
×
1153
    }
1154
    else
1155
    {
1156
        m_topPercentageLabel->setText(QString("%1%").arg(value));
×
1157
        m_bottomPercentageLabel->setText(QString("%1%").arg(100 - value));
×
1158

1159
        Chaser *ch = chaser();
×
1160
        if (!(ch == NULL || ch->stopped()))
×
1161
        {
1162
            ch->adjustStepIntensity(qreal(value) / 100.0, m_primaryTop ? m_primaryIndex : m_secondaryIndex,
×
1163
                                    Chaser::FadeControlMode(getFadeMode()));
×
1164
            ch->adjustStepIntensity(qreal(100 - value) / 100.0, m_primaryTop ? m_secondaryIndex : m_primaryIndex,
×
1165
                                    Chaser::FadeControlMode(getFadeMode()));
×
1166
            stopStepIfNeeded(ch);
×
1167
        }
1168
    }
1169

1170
    updateFeedback();
×
1171
}
1172

1173
void VCCueList::stopStepIfNeeded(Chaser *ch)
×
1174
{
1175
    if (ch->runningStepsNumber() != 2)
×
1176
        return;
×
1177

1178
    int primaryValue = m_primaryTop ? m_sideFader->value() : 100 - m_sideFader->value();
×
1179
    int secondaryValue = m_primaryTop ? 100 - m_sideFader->value() : m_sideFader->value();
×
1180

1181
    ChaserAction action;
1182
    action.m_action = ChaserStopStep;
×
1183

1184
    if (primaryValue == 0)
×
1185
    {
1186
        m_primaryTop = !m_primaryTop;
×
1187
        action.m_stepIndex = m_primaryIndex;
×
1188
        ch->setAction(action);
×
1189
    }
1190
    else if (secondaryValue == 0)
×
1191
    {
1192
        action.m_stepIndex = m_secondaryIndex;
×
1193
        ch->setAction(action);
×
1194
    }
1195
}
1196

1197
/*****************************************************************************
1198
 * Key Sequences
1199
 *****************************************************************************/
1200

1201
void VCCueList::setNextKeySequence(const QKeySequence& keySequence)
6✔
1202
{
1203
    m_nextKeySequence = QKeySequence(keySequence);
6✔
1204
}
6✔
1205

1206
QKeySequence VCCueList::nextKeySequence() const
8✔
1207
{
1208
    return m_nextKeySequence;
8✔
1209
}
1210

1211
void VCCueList::setPreviousKeySequence(const QKeySequence& keySequence)
6✔
1212
{
1213
    m_previousKeySequence = QKeySequence(keySequence);
6✔
1214
}
6✔
1215

1216
QKeySequence VCCueList::previousKeySequence() const
8✔
1217
{
1218
    return m_previousKeySequence;
8✔
1219
}
1220

1221
void VCCueList::setPlaybackKeySequence(const QKeySequence& keySequence)
6✔
1222
{
1223
    m_playbackKeySequence = QKeySequence(keySequence);
6✔
1224
}
6✔
1225

1226
QKeySequence VCCueList::playbackKeySequence() const
8✔
1227
{
1228
    return m_playbackKeySequence;
8✔
1229
}
1230

1231
void VCCueList::setStopKeySequence(const QKeySequence &keySequence)
3✔
1232
{
1233
    m_stopKeySequence = QKeySequence(keySequence);
3✔
1234
}
3✔
1235

1236
QKeySequence VCCueList::stopKeySequence() const
3✔
1237
{
1238
    return m_stopKeySequence;
3✔
1239
}
1240

1241
void VCCueList::slotKeyPressed(const QKeySequence& keySequence)
7✔
1242
{
1243
    if (acceptsInput() == false)
7✔
1244
        return;
×
1245

1246
    if (m_nextKeySequence == keySequence)
7✔
1247
        slotNextCue();
3✔
1248
    else if (m_previousKeySequence == keySequence)
4✔
1249
        slotPreviousCue();
2✔
1250
    else if (m_playbackKeySequence == keySequence)
2✔
1251
        slotPlayback();
1✔
1252
    else if (m_stopKeySequence == keySequence)
1✔
1253
        slotStop();
×
1254
}
1255

1256
void VCCueList::updateFeedback()
25✔
1257
{
1258
    int fbv = int(SCALE(float(m_sideFader->value()), 
25✔
1259
                        float(m_sideFader->minimum()),
1260
                        float(m_sideFader->maximum()), 
1261
                        float(0), float(UCHAR_MAX)));
25✔
1262
    sendFeedback(fbv, sideFaderInputSourceId);
25✔
1263

1264
    Chaser *ch = chaser();
25✔
1265
    if (ch == NULL)
25✔
1266
        return;
3✔
1267

1268
    sendFeedback(ch->isRunning() ? UCHAR_MAX : 0, playbackInputSourceId);
22✔
1269
}
1270

1271
/*****************************************************************************
1272
 * External Input
1273
 *****************************************************************************/
1274

1275
void VCCueList::slotInputValueChanged(quint32 universe, quint32 channel, uchar value)
12✔
1276
{
1277
    /* Don't let input data through in design mode or if disabled */
1278
    if (acceptsInput() == false)
12✔
1279
        return;
×
1280

1281
    quint32 pagedCh = (page() << 16) | channel;
12✔
1282

1283
    if (checkInputSource(universe, pagedCh, value, sender(), nextInputSourceId))
12✔
1284
    {
1285
        // Use hysteresis for values, in case the cue list is being controlled
1286
        // by a slider. The value has to go to zero before the next non-zero
1287
        // value is accepted as input. And the non-zero values have to visit
1288
        // above $HYSTERESIS before a zero is accepted again.
1289
        if (m_nextLatestValue == 0 && value > 0)
3✔
1290
        {
1291
            slotNextCue();
2✔
1292
            m_nextLatestValue = value;
2✔
1293
        }
1294
        else if (m_nextLatestValue > HYSTERESIS && value == 0)
1✔
1295
        {
1296
            m_nextLatestValue = 0;
1✔
1297
        }
1298

1299
        if (value > HYSTERESIS)
3✔
1300
            m_nextLatestValue = value;
2✔
1301
    }
1302
    else if (checkInputSource(universe, pagedCh, value, sender(), previousInputSourceId))
9✔
1303
    {
1304
        // Use hysteresis for values, in case the cue list is being controlled
1305
        // by a slider. The value has to go to zero before the next non-zero
1306
        // value is accepted as input. And the non-zero values have to visit
1307
        // above $HYSTERESIS before a zero is accepted again.
1308
        if (m_previousLatestValue == 0 && value > 0)
3✔
1309
        {
1310
            slotPreviousCue();
2✔
1311
            m_previousLatestValue = value;
2✔
1312
        }
1313
        else if (m_previousLatestValue > HYSTERESIS && value == 0)
1✔
1314
        {
1315
            m_previousLatestValue = 0;
1✔
1316
        }
1317

1318
        if (value > HYSTERESIS)
3✔
1319
            m_previousLatestValue = value;
2✔
1320
    }
1321
    else if (checkInputSource(universe, pagedCh, value, sender(), playbackInputSourceId))
6✔
1322
    {
1323
        // Use hysteresis for values, in case the cue list is being controlled
1324
        // by a slider. The value has to go to zero before the next non-zero
1325
        // value is accepted as input. And the non-zero values have to visit
1326
        // above $HYSTERESIS before a zero is accepted again.
1327
        if (m_playbackLatestValue == 0 && value > 0)
2✔
1328
        {
1329
            slotPlayback();
1✔
1330
            m_playbackLatestValue = value;
1✔
1331
        }
1332
        else if (m_playbackLatestValue > HYSTERESIS && value == 0)
1✔
1333
        {
1334
            m_playbackLatestValue = 0;
1✔
1335
        }
1336

1337
        if (value > HYSTERESIS)
2✔
1338
            m_playbackLatestValue = value;
1✔
1339
    }
1340
    else if (checkInputSource(universe, pagedCh, value, sender(), stopInputSourceId))
4✔
1341
    {
1342
        // Use hysteresis for values, in case the cue list is being controlled
1343
        // by a slider. The value has to go to zero before the next non-zero
1344
        // value is accepted as input. And the non-zero values have to visit
1345
        // above $HYSTERESIS before a zero is accepted again.
1346
        if (m_stopLatestValue == 0 && value > 0)
×
1347
        {
1348
            slotStop();
×
1349
            m_stopLatestValue = value;
×
1350
        }
1351
        else if (m_stopLatestValue > HYSTERESIS && value == 0)
×
1352
        {
1353
            m_stopLatestValue = 0;
×
1354
        }
1355

1356
        if (value > HYSTERESIS)
×
1357
            m_stopLatestValue = value;
×
1358
    }
1359
    else if (checkInputSource(universe, pagedCh, value, sender(), sideFaderInputSourceId))
4✔
1360
    {
1361
        if (sideFaderMode() == None)
×
1362
            return;
×
1363

1364
        float val = SCALE((float) value, (float) 0, (float) UCHAR_MAX,
×
1365
                          (float) m_sideFader->minimum(),
1366
                          (float) m_sideFader->maximum());
1367
        m_sideFader->setValue(val);
×
1368
    }
1369
}
1370

1371
/*****************************************************************************
1372
 * VCWidget-inherited methods
1373
 *****************************************************************************/
1374

1375
void VCCueList::adjustIntensity(qreal val)
×
1376
{
1377
    Chaser *ch = chaser();
×
1378
    if (ch != NULL)
×
1379
    {
1380
        adjustFunctionIntensity(ch, val);
×
1381
/*
1382
        // Refresh intensity of current steps
1383
        if (!ch->stopped() && sideFaderMode() == Crossfade && m_sideFader->value() != 100)
1384
        {
1385
                ch->adjustStepIntensity((qreal)m_sideFader->value() / 100, m_primaryTop ? m_primaryIndex : m_secondaryIndex);
1386
                ch->adjustStepIntensity((qreal)(100 - m_sideFader->value()) / 100, m_primaryTop ? m_secondaryIndex : m_primaryIndex);
1387
        }
1388
*/
1389
    }
1390

1391
    VCWidget::adjustIntensity(val);
×
1392
}
×
1393

1394
void VCCueList::setCaption(const QString& text)
20✔
1395
{
1396
    VCWidget::setCaption(text);
20✔
1397

1398
    QStringList list;
40✔
1399
    list << "#" << text << tr("Fade In") << tr("Fade Out") << tr("Duration") << tr("Notes");
20✔
1400
    m_tree->setHeaderLabels(list);
20✔
1401
}
20✔
1402

1403
void VCCueList::setFont(const QFont& font)
1✔
1404
{
1405
    VCWidget::setFont(font);
1✔
1406

1407
    QFontMetrics m_fm = QFontMetrics(font);
2✔
1408
#if (QT_VERSION < QT_VERSION_CHECK(5, 11, 0))
1409
    int w = m_fm.width("100%");
1410
#else
1411
    int w = m_fm.horizontalAdvance("100%");
1✔
1412
#endif
1413
    m_topPercentageLabel->setFixedWidth(w);
1✔
1414
    m_bottomPercentageLabel->setFixedWidth(w);
1✔
1415
}
1✔
1416

1417
void VCCueList::slotModeChanged(Doc::Mode mode)
23✔
1418
{
1419
    bool enable = false;
23✔
1420
    if (mode == Doc::Operate)
23✔
1421
    {
1422
        m_progress->setStyleSheet(progressFadeStyle);
6✔
1423
        m_progress->setRange(0, m_progress->width());
6✔
1424
        enable = true;
6✔
1425
        // send the initial feedback for the current step slider
1426
        updateFeedback();
6✔
1427
    }
1428
    else
1429
    {
1430
        m_topStepLabel->setStyleSheet(cfLabelNoStyle);
17✔
1431
        m_topStepLabel->setText("");
17✔
1432
        m_bottomStepLabel->setStyleSheet(cfLabelNoStyle);
17✔
1433
        m_bottomStepLabel->setText("");
17✔
1434
        m_progress->setStyleSheet(progressDisabledStyle);
17✔
1435
        // reset any previously set background
1436
        QTreeWidgetItem *item = m_tree->topLevelItem(m_secondaryIndex);
17✔
1437
        if (item != NULL)
17✔
1438
            item->setBackground(COL_NUM, m_defCol);
2✔
1439
    }
1440

1441
    enableWidgetUI(enable);
23✔
1442

1443
    /* Always start from the beginning */
1444
    m_tree->setCurrentItem(NULL);
23✔
1445

1446
    VCWidget::slotModeChanged(mode);
23✔
1447
}
23✔
1448

1449
void VCCueList::editProperties()
×
1450
{
1451
    VCCueListProperties prop(this, m_doc);
×
1452
    if (prop.exec() == QDialog::Accepted)
×
1453
        m_doc->setModified();
×
1454
}
×
1455

1456
void VCCueList::playCueAtIndex(int idx)
4✔
1457
{
1458
    if (mode() != Doc::Operate)
4✔
1459
        return;
×
1460

1461
    m_primaryIndex = idx;
4✔
1462

1463
    Chaser *ch = chaser();
4✔
1464
    if (ch == NULL)
4✔
1465
        return;
×
1466

1467
    if (ch->isRunning())
4✔
1468
    {
1469
        ChaserAction action;
1470
        action.m_action = ChaserSetStepIndex;
3✔
1471
        action.m_stepIndex = m_primaryIndex;
3✔
1472
        action.m_masterIntensity = intensity();
3✔
1473
        action.m_stepIntensity = getPrimaryIntensity();
3✔
1474
        action.m_fadeMode = getFadeMode();
3✔
1475
        ch->setAction(action);
3✔
1476
    }
1477
    else
1478
    {
1479
        startChaser(m_primaryIndex);
1✔
1480
    }
1481

1482
    if (sideFaderMode() == Crossfade)
4✔
1483
        setFaderInfo(m_primaryIndex);
×
1484
}
1485

1486
FunctionParent VCCueList::functionParent() const
4✔
1487
{
1488
    return FunctionParent(FunctionParent::ManualVCWidget, id());
4✔
1489
}
1490

1491
/*****************************************************************************
1492
 * Load & Save
1493
 *****************************************************************************/
1494

1495
bool VCCueList::loadXML(QXmlStreamReader &root)
3✔
1496
{
1497
    QList <quint32> legacyStepList;
6✔
1498

1499
    if (root.name() != KXMLQLCVCCueList)
3✔
1500
    {
1501
        qWarning() << Q_FUNC_INFO << "CueList node not found";
1✔
1502
        return false;
1✔
1503
    }
1504

1505
    /* Widget commons */
1506
    loadXMLCommon(root);
2✔
1507

1508
    /* Children */
1509
    while (root.readNextStartElement())
18✔
1510
    {
1511
        if (root.name() == KXMLQLCWindowState)
16✔
1512
        {
1513
            bool visible = false;
1✔
1514
            int x = 0, y = 0, w = 0, h = 0;
1✔
1515
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
1✔
1516
            setGeometry(x, y, w, h);
1✔
1517
        }
1518
        else if (root.name() == KXMLQLCVCWidgetAppearance)
15✔
1519
        {
1520
            loadXMLAppearance(root);
1✔
1521
        }
1522
        else if (root.name() == KXMLQLCVCCueListNext)
14✔
1523
        {
1524
            QString str = loadXMLSources(root, nextInputSourceId);
2✔
1525
            if (str.isEmpty() == false)
1✔
1526
                m_nextKeySequence = stripKeySequence(QKeySequence(str));
1✔
1527
        }
1528
        else if (root.name() == KXMLQLCVCCueListPrevious)
13✔
1529
        {
1530
            QString str = loadXMLSources(root, previousInputSourceId);
2✔
1531
            if (str.isEmpty() == false)
1✔
1532
                m_previousKeySequence = stripKeySequence(QKeySequence(str));
1✔
1533
        }
1534
        else if (root.name() == KXMLQLCVCCueListPlayback)
12✔
1535
        {
1536
            QString str = loadXMLSources(root, playbackInputSourceId);
2✔
1537
            if (str.isEmpty() == false)
1✔
1538
                m_playbackKeySequence = stripKeySequence(QKeySequence(str));
1✔
1539
        }
1540
        else if (root.name() == KXMLQLCVCCueListStop)
11✔
1541
        {
1542
            QString str = loadXMLSources(root, stopInputSourceId);
2✔
1543
            if (str.isEmpty() == false)
1✔
1544
                m_stopKeySequence = stripKeySequence(QKeySequence(str));
1✔
1545
        }
1546
        else if (root.name() == KXMLQLCVCCueListSlidersMode)
10✔
1547
        {
1548
            setSideFaderMode(stringToFaderMode(root.readElementText()));
×
1549
        }
1550
        else if (root.name() == KXMLQLCVCCueListCrossfadeLeft)
10✔
1551
        {
1552
            loadXMLSources(root, sideFaderInputSourceId);
×
1553
        }
1554
        else if (root.name() == KXMLQLCVCCueListCrossfadeRight) /* Legacy */
10✔
1555
        {
1556
            root.skipCurrentElement();
×
1557
        }
1558
        else if (root.name() == KXMLQLCVCWidgetKey) /* Legacy */
10✔
1559
        {
1560
            setNextKeySequence(QKeySequence(root.readElementText()));
×
1561
        }
1562
        else if (root.name() == KXMLQLCVCCueListChaser)
10✔
1563
        {
1564
            setChaser(root.readElementText().toUInt());
×
1565
        }
1566
        else if (root.name() == KXMLQLCVCCueListPlaybackLayout)
10✔
1567
        {
1568
            PlaybackLayout layout = PlaybackLayout(root.readElementText().toInt());
×
1569
            if (layout != PlayPauseStop && layout != PlayStopPause)
×
1570
            {
1571
                qWarning() << Q_FUNC_INFO << "Playback layout" << layout << "does not exist.";
×
1572
                layout = PlayPauseStop;
×
1573
            }
1574
            setPlaybackLayout(layout);
×
1575
        }
1576
        else if (root.name() == KXMLQLCVCCueListNextPrevBehavior)
10✔
1577
        {
1578
            NextPrevBehavior nextPrev = NextPrevBehavior(root.readElementText().toInt());
×
1579
            if (nextPrev != DefaultRunFirst
×
1580
                    && nextPrev != RunNext
×
1581
                    && nextPrev != Select
×
1582
                    && nextPrev != Nothing)
×
1583
            {
1584
                qWarning() << Q_FUNC_INFO << "Next/Prev behavior" << nextPrev << "does not exist.";
×
1585
                nextPrev = DefaultRunFirst;
×
1586
            }
1587
            setNextPrevBehavior(nextPrev);
×
1588
        }
1589
        else if (root.name() == KXMLQLCVCCueListCrossfade)
10✔
1590
        {
1591
            m_crossfadeButton->setChecked(true);
×
1592
            root.skipCurrentElement();
×
1593
        }
1594
        else if (root.name() == KXMLQLCVCCueListFunction)
10✔
1595
        {
1596
            // Collect legacy file format steps into a list
1597
            legacyStepList << root.readElementText().toUInt();
6✔
1598
        }
1599
        else
1600
        {
1601
            qWarning() << Q_FUNC_INFO << "Unknown cuelist tag:" << root.name().toString();
4✔
1602
            root.skipCurrentElement();
4✔
1603
        }
1604
    }
1605

1606
    if (legacyStepList.isEmpty() == false)
2✔
1607
    {
1608
        /* Construct a new chaser from legacy step functions and use that chaser */
1609
        Chaser *chaser = new Chaser(m_doc);
1✔
1610
        chaser->setName(caption());
1✔
1611

1612
        // Legacy cue lists relied on individual functions' timings and a common hold time
1613
        chaser->setFadeInMode(Chaser::Default);
1✔
1614
        chaser->setFadeOutMode(Chaser::Default);
1✔
1615
        chaser->setDurationMode(Chaser::Common);
1✔
1616

1617
        foreach (quint32 id, legacyStepList)
13✔
1618
        {
1619
            Function *function = m_doc->function(id);
6✔
1620
            if (function == NULL)
6✔
1621
                continue;
2✔
1622

1623
            // Legacy cuelists relied on individual functions' fadein/out speed and
1624
            // infinite duration. So don't touch them at all.
1625
            ChaserStep step(id);
8✔
1626
            chaser->addStep(step);
4✔
1627
        }
1628

1629
        // Add the chaser to Doc and attach it to the cue list
1630
        m_doc->addFunction(chaser);
1✔
1631
        setChaser(chaser->id());
1✔
1632
    }
1633

1634
    return true;
2✔
1635
}
1636

1637
bool VCCueList::saveXML(QXmlStreamWriter *doc)
1✔
1638
{
1639
    Q_ASSERT(doc != NULL);
1✔
1640

1641
    /* VC CueList entry */
1642
    doc->writeStartElement(KXMLQLCVCCueList);
1✔
1643

1644
    saveXMLCommon(doc);
1✔
1645

1646
    /* Window state */
1647
    saveXMLWindowState(doc);
1✔
1648

1649
    /* Appearance */
1650
    saveXMLAppearance(doc);
1✔
1651

1652
    /* Chaser */
1653
    doc->writeTextElement(KXMLQLCVCCueListChaser, QString::number(chaserID()));
1✔
1654

1655
    /* Playback layout */
1656
    if (playbackLayout() != PlayPauseStop)
1✔
1657
        doc->writeTextElement(KXMLQLCVCCueListPlaybackLayout, QString::number(playbackLayout()));
×
1658

1659
    /* Next/Prev behavior */
1660
    doc->writeTextElement(KXMLQLCVCCueListNextPrevBehavior, QString::number(nextPrevBehavior()));
1✔
1661

1662
    /* Next cue */
1663
    doc->writeStartElement(KXMLQLCVCCueListNext);
1✔
1664
    if (m_nextKeySequence.toString().isEmpty() == false)
1✔
1665
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_nextKeySequence.toString());
1✔
1666
    saveXMLInput(doc, inputSource(nextInputSourceId));
1✔
1667
    doc->writeEndElement();
1✔
1668

1669
    /* Previous cue */
1670
    doc->writeStartElement(KXMLQLCVCCueListPrevious);
1✔
1671
    if (m_previousKeySequence.toString().isEmpty() == false)
1✔
1672
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_previousKeySequence.toString());
1✔
1673
    saveXMLInput(doc, inputSource(previousInputSourceId));
1✔
1674
    doc->writeEndElement();
1✔
1675

1676
    /* Cue list playback */
1677
    doc->writeStartElement(KXMLQLCVCCueListPlayback);
1✔
1678
    if (m_playbackKeySequence.toString().isEmpty() == false)
1✔
1679
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_playbackKeySequence.toString());
1✔
1680
    saveXMLInput(doc, inputSource(playbackInputSourceId));
1✔
1681
    doc->writeEndElement();
1✔
1682

1683
    /* Cue list stop */
1684
    doc->writeStartElement(KXMLQLCVCCueListStop);
1✔
1685
    if (m_stopKeySequence.toString().isEmpty() == false)
1✔
1686
        doc->writeTextElement(KXMLQLCVCWidgetKey, m_stopKeySequence.toString());
1✔
1687
    saveXMLInput(doc, inputSource(stopInputSourceId));
1✔
1688
    doc->writeEndElement();
1✔
1689

1690
    /* Crossfade cue list */
1691
    if (sideFaderMode() != None)
1✔
1692
        doc->writeTextElement(KXMLQLCVCCueListSlidersMode, faderModeToString(sideFaderMode()));
1✔
1693

1694
    QSharedPointer<QLCInputSource> cf1Src = inputSource(sideFaderInputSourceId);
1✔
1695
    if (!cf1Src.isNull() && cf1Src->isValid())
1✔
1696
    {
1697
        doc->writeStartElement(KXMLQLCVCCueListCrossfadeLeft);
×
1698
        saveXMLInput(doc, cf1Src);
×
1699
        doc->writeEndElement();
×
1700
    }
1701

1702
    /* End the <CueList> tag */
1703
    doc->writeEndElement();
1✔
1704

1705
    return true;
2✔
1706
}
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