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

mcallegari / qlcplus / 6628997562

24 Oct 2023 03:21PM UTC coverage: 28.085% (+0.05%) from 28.038%
6628997562

Pull #1462

github

web-flow
Merge 231f6a87e into 646f68840
Pull Request #1462: Overriding flash functionality

91 of 91 new or added lines in 9 files covered. (100.0%)

15382 of 54770 relevant lines covered (28.08%)

20312.39 hits per line

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

80.91
/ui/src/virtualconsole/vcbutton.cpp
1
/*
2
  Q Light Controller Plus
3
  vcbutton.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 <QStyleOptionButton>
22
#include <QXmlStreamReader>
23
#include <QXmlStreamWriter>
24
#include <QWidgetAction>
25
#include <QColorDialog>
26
#include <QImageReader>
27
#include <QFileDialog>
28
#include <QPaintEvent>
29
#include <QMouseEvent>
30
#include <QMessageBox>
31
#include <QByteArray>
32
#include <QSettings>
33
#include <QPainter>
34
#include <QString>
35
#include <QDebug>
36
#include <QEvent>
37
#include <QTimer>
38
#include <QBrush>
39
#include <QStyle>
40
#include <QMenu>
41
#include <QSize>
42
#include <QPen>
43

44
#if defined(WIN32) || defined(Q_OS_WIN)
45
 #include <QStyleFactory>
46
#endif
47

48
#include "qlcinputsource.h"
49
#include "qlcmacros.h"
50
#include "qlcfile.h"
51

52
#include "vcbuttonproperties.h"
53
#include "vcpropertieseditor.h"
54
#include "functionselection.h"
55
#include "clickandgoslider.h"
56
#include "qlcinputchannel.h"
57
#include "virtualconsole.h"
58
#include "chaseraction.h"
59
#include "mastertimer.h"
60
#include "vcsoloframe.h"
61
#include "inputpatch.h"
62
#include "vcbutton.h"
63
#include "function.h"
64
#include "fixture.h"
65
#include "apputil.h"
66
#include "chaser.h"
67
#include "doc.h"
68

69
const QSize VCButton::defaultSize(QSize(50, 50));
70

71
/*****************************************************************************
72
 * Initialization
73
 *****************************************************************************/
74

75
VCButton::VCButton(QWidget* parent, Doc* doc) : VCWidget(parent, doc)
25✔
76
    , m_iconPath()
77
    , m_blackoutFadeOutTime(0)
78
    , m_startupIntensityEnabled(false)
79
    , m_startupIntensity(1.0)
80
    , m_flashOverrides(false)
81
    , m_flashForceLTP(false)
25✔
82
{
83
    /* Set the class name "VCButton" as the object name as well */
84
    setObjectName(VCButton::staticMetaObject.className());
25✔
85

86
    /* No function is initially attached to the button */
87
    m_function = Function::invalidId();
25✔
88

89
    setType(VCWidget::ButtonWidget);
25✔
90
    setCaption(QString());
25✔
91
    setState(Inactive);
25✔
92
    m_action = Action(-1); // avoid use of uninitialized value
25✔
93
    setAction(Toggle);
25✔
94
    setFrameStyle(KVCFrameStyleNone);
25✔
95

96
    /* Menu actions */
97
    m_chooseIconAction = new QAction(QIcon(":/image.png"), tr("Choose..."),
50✔
98
                                     this);
25✔
99
    m_chooseIconAction->setShortcut(QKeySequence("SHIFT+C"));
25✔
100

101
    m_resetIconAction = new QAction(QIcon(":/undo.png"), tr("None"), this);
25✔
102
    m_resetIconAction->setShortcut(QKeySequence("SHIFT+ALT+C"));
25✔
103

104
    connect(m_chooseIconAction, SIGNAL(triggered(bool)),
25✔
105
            this, SLOT(slotChooseIcon()));
106
    connect(m_resetIconAction, SIGNAL(triggered(bool)),
25✔
107
            this, SLOT(slotResetIcon()));
108

109
    /* Initial size */
110
    QSettings settings;
50✔
111
    QVariant var = settings.value(SETTINGS_BUTTON_SIZE);
50✔
112
    if (var.isValid() == true)
25✔
113
        resize(var.toSize());
×
114
    else
115
        resize(defaultSize);
25✔
116

117
    var = settings.value(SETTINGS_BUTTON_STATUSLED);
25✔
118
    if (var.isValid() == true && var.toBool() == true)
25✔
119
        m_ledStyle = true;
×
120
    else
121
        m_ledStyle = false;
25✔
122

123
    setStyle(AppUtil::saneStyle());
25✔
124

125
    /* Listen to function removals */
126
    connect(m_doc, SIGNAL(functionRemoved(quint32)),
25✔
127
            this, SLOT(slotFunctionRemoved(quint32)));
128
}
25✔
129

130
VCButton::~VCButton()
29✔
131
{
132
}
29✔
133

134
void VCButton::setID(quint32 id)
1✔
135
{
136
    VCWidget::setID(id);
1✔
137

138
    if (caption().isEmpty())
1✔
139
        setCaption(tr("Button %1").arg(id));
×
140
}
1✔
141

142
/*****************************************************************************
143
 * Clipboard
144
 *****************************************************************************/
145

146
VCWidget* VCButton::createCopy(VCWidget* parent)
2✔
147
{
148
    Q_ASSERT(parent != NULL);
2✔
149

150
    VCButton* button = new VCButton(parent, m_doc);
2✔
151
    if (button->copyFrom(this) == false)
2✔
152
    {
153
        delete button;
×
154
        button = NULL;
×
155
    }
156

157
    return button;
2✔
158
}
159

160
bool VCButton::copyFrom(const VCWidget* widget)
2✔
161
{
162
    const VCButton* button = qobject_cast <const VCButton*> (widget);
2✔
163
    if (button == NULL)
2✔
164
        return false;
×
165

166
    /* Copy button-specific stuff */
167
    setIconPath(button->iconPath());
2✔
168
    setKeySequence(button->keySequence());
2✔
169
    setFunction(button->function());
2✔
170
    enableStartupIntensity(button->isStartupIntensityEnabled());
2✔
171
    setStartupIntensity(button->startupIntensity());
2✔
172
    setStopAllFadeOutTime(button->stopAllFadeTime());
2✔
173
    setAction(button->action());
2✔
174
    m_state = button->m_state;
2✔
175

176
    /* Copy common stuff */
177
    return VCWidget::copyFrom(widget);
2✔
178
}
179

180
/*****************************************************************************
181
 * Properties
182
 *****************************************************************************/
183

184
void VCButton::editProperties()
×
185
{
186
    VCButtonProperties prop(this, m_doc);
×
187
    if (prop.exec() == QDialog::Accepted)
×
188
        m_doc->setModified();
×
189
}
×
190

191
/*****************************************************************************
192
 * Background color
193
 *****************************************************************************/
194

195
void VCButton::setBackgroundImage(const QString& path)
3✔
196
{
197
    m_bgPixmap = QPixmap(path);
3✔
198
    m_backgroundImage = path;
3✔
199
    m_doc->setModified();
3✔
200
    update();
3✔
201
}
3✔
202

203
void VCButton::setBackgroundColor(const QColor& color)
5✔
204
{
205
    QPalette pal = palette();
10✔
206

207
    m_hasCustomBackgroundColor = true;
5✔
208
    m_backgroundImage = QString();
5✔
209
    pal.setColor(QPalette::Button, color);
5✔
210
    setPalette(pal);
5✔
211

212
    m_doc->setModified();
5✔
213
}
5✔
214

215
void VCButton::resetBackgroundColor()
2✔
216
{
217
    QColor fg;
2✔
218

219
    m_hasCustomBackgroundColor = false;
2✔
220
    m_backgroundImage = QString();
2✔
221

222
    /* Store foreground color */
223
    if (m_hasCustomForegroundColor == true)
2✔
224
        fg = palette().color(QPalette::ButtonText);
2✔
225

226
    /* Reset the whole palette to application palette */
227
    setPalette(QApplication::palette());
2✔
228

229
    /* Restore foreground color */
230
    if (fg.isValid() == true)
2✔
231
    {
232
        QPalette pal = palette();
4✔
233
        pal.setColor(QPalette::ButtonText, fg);
2✔
234
        setPalette(pal);
2✔
235
    }
236

237
    m_doc->setModified();
2✔
238
}
2✔
239

240
QColor VCButton::backgroundColor() const
6✔
241
{
242
    return palette().color(QPalette::Button);
6✔
243
}
244

245
/*****************************************************************************
246
 * Foreground color
247
 *****************************************************************************/
248

249
void VCButton::setForegroundColor(const QColor& color)
3✔
250
{
251
    QPalette pal = palette();
6✔
252

253
    m_hasCustomForegroundColor = true;
3✔
254

255
    pal.setColor(QPalette::WindowText, color);
3✔
256
    pal.setColor(QPalette::ButtonText, color);
3✔
257
    setPalette(pal);
3✔
258

259
    m_doc->setModified();
3✔
260
}
3✔
261

262
void VCButton::resetForegroundColor()
2✔
263
{
264
    QColor bg;
2✔
265

266
    m_hasCustomForegroundColor = false;
2✔
267

268
    /* Store background color */
269
    if (m_hasCustomBackgroundColor == true)
2✔
270
        bg = palette().color(QPalette::Button);
2✔
271

272
    /* Reset the whole palette to application palette */
273
    setPalette(QApplication::palette());
2✔
274

275
    /* Restore background color */
276
    if (bg.isValid() == true)
2✔
277
        setBackgroundColor(bg);
2✔
278

279
    m_doc->setModified();
2✔
280
}
2✔
281

282
QColor VCButton::foregroundColor() const
6✔
283
{
284
    return palette().color(QPalette::ButtonText);
6✔
285
}
286

287
/*****************************************************************************
288
 * Button icon
289
 *****************************************************************************/
290

291
QString VCButton::iconPath() const
72✔
292
{
293
    return m_iconPath;
72✔
294
}
295

296
void VCButton::setIconPath(const QString& iconPath)
10✔
297
{
298
    m_iconPath = iconPath;
10✔
299

300
    updateIcon();
10✔
301
    m_doc->setModified();
10✔
302
    update();
10✔
303
}
10✔
304

305
void VCButton::slotChooseIcon()
×
306
{
307
    /* No point coming here if there is no VC */
308
    VirtualConsole *vc = VirtualConsole::instance();
×
309
    if (vc == NULL)
×
310
        return;
×
311

312
    QString formats;
×
313
    QListIterator <QByteArray> it(QImageReader::supportedImageFormats());
×
314
    while (it.hasNext() == true)
×
315
        formats += QString("*.%1 ").arg(QString(it.next()).toLower());
×
316

317
    QString path;
×
318
    path = QFileDialog::getOpenFileName(this, tr("Select button icon"),
×
319
                                        iconPath(), tr("Images (%1)").arg(formats));
×
320
    if (path.isEmpty() == false)
×
321
    {
322
        foreach(VCWidget *widget, vc->selectedWidgets())
×
323
        {
324
            VCButton *button = qobject_cast<VCButton*> (widget);
×
325
            if (button != NULL)
×
326
                button->setIconPath(path);
×
327
        }
328
    }
329
}
330

331
void VCButton::updateIcon()
52✔
332
{
333
    if (m_action == Blackout)
52✔
334
    {
335
        m_icon = QIcon(":/blackout.png");
1✔
336
        m_iconSize = QSize(26, 26);
1✔
337
    }
338
    else if (m_action == StopAll)
51✔
339
    {
340
        m_icon = QIcon(":/panic.png");
1✔
341
        m_iconSize = QSize(26, 26);
1✔
342
    }
343
    else if (iconPath().isEmpty() == false)
50✔
344
    {
345
        m_icon = QIcon(iconPath());
13✔
346
        m_iconSize = QSize(26, 26);
13✔
347
    }
348
    else
349
    {
350
        m_icon = QIcon();
37✔
351
        m_iconSize = QSize(-1, -1);
37✔
352
    }
353
}
52✔
354

355
void VCButton::slotResetIcon()
1✔
356
{
357
    setIconPath(QString());
1✔
358
    update();
1✔
359
}
1✔
360

361
/*****************************************************************************
362
 * Function attachment
363
 *****************************************************************************/
364

365
void VCButton::setFunction(quint32 fid)
17✔
366
{
367
    Function* old = m_doc->function(m_function);
17✔
368
    if (old != NULL)
17✔
369
    {
370
        /* Get rid of old function connections */
371
        disconnect(old, SIGNAL(running(quint32)),
3✔
372
                this, SLOT(slotFunctionRunning(quint32)));
373
        disconnect(old, SIGNAL(stopped(quint32)),
3✔
374
                this, SLOT(slotFunctionStopped(quint32)));
375
        disconnect(old, SIGNAL(flashing(quint32,bool)),
3✔
376
                this, SLOT(slotFunctionFlashing(quint32,bool)));
377
    }
378

379
    Function* function = m_doc->function(fid);
17✔
380
    if (function != NULL)
17✔
381
    {
382
        /* Connect to the new function */
383
        connect(function, SIGNAL(running(quint32)),
13✔
384
                this, SLOT(slotFunctionRunning(quint32)));
385
        connect(function, SIGNAL(stopped(quint32)),
13✔
386
                this, SLOT(slotFunctionStopped(quint32)));
387
        connect(function, SIGNAL(flashing(quint32,bool)),
13✔
388
                this, SLOT(slotFunctionFlashing(quint32,bool)));
389

390
        m_function = fid;
13✔
391
        setToolTip(function->name());
13✔
392
    }
393
    else
394
    {
395
        /* No function attachment */
396
        m_function = Function::invalidId();
4✔
397
        setToolTip(QString());
4✔
398
    }
399
}
17✔
400

401
quint32 VCButton::function() const
14✔
402
{
403
    return m_function;
14✔
404
}
405

406
void VCButton::adjustFunctionIntensity(Function *f, qreal value)
7✔
407
{
408
    qreal finalValue = isStartupIntensityEnabled() ? startupIntensity() * value : value;
7✔
409

410
    VCWidget::adjustFunctionIntensity(f, finalValue);
7✔
411
}
7✔
412

413
void VCButton::notifyFunctionStarting(quint32 fid, qreal intensity)
×
414
{
415
    Q_UNUSED(intensity);
416

417
    if (mode() == Doc::Design)
×
418
        return;
×
419

420
    if (fid == m_function)
×
421
        return;
×
422

423
    if (m_function != Function::invalidId() && action() == VCButton::Toggle)
×
424
    {
425
        Function *f = m_doc->function(m_function);
×
426
        if (f != NULL)
×
427
        {
428
            f->stop(functionParent());
×
429
            resetIntensityOverrideAttribute();
×
430
        }
431
    }
432
}
433

434
void VCButton::slotFunctionRemoved(quint32 fid)
2✔
435
{
436
    /* Invalidate the button's function if it's the one that was removed */
437
    if (fid == m_function)
2✔
438
    {
439
        setFunction(Function::invalidId());
1✔
440
        resetIntensityOverrideAttribute();
1✔
441
    }
442
}
2✔
443

444
/*****************************************************************************
445
 * Button state
446
 *****************************************************************************/
447

448
VCButton::ButtonState VCButton::state() const
72✔
449
{
450
    return m_state;
72✔
451
}
452

453
void VCButton::setState(ButtonState state)
59✔
454
{
455
    if (state == m_state)
59✔
456
        return;
16✔
457

458
    m_state = state;
43✔
459

460
    emit stateChanged(m_state);
43✔
461

462
    updateFeedback();
43✔
463

464
    update();
43✔
465
}
466

467
void VCButton::updateState()
×
468
{
469
    ButtonState state = Inactive;
×
470

471
    if (m_action == Blackout)
×
472
    {
473
        if (m_doc->inputOutputMap()->blackout())
×
474
            state = Active;
×
475
    }
476
    else if (m_action == Toggle)
×
477
    {
478
        Function* function = m_doc->function(m_function);
×
479
        if (function != NULL && function->isRunning())
×
480
            state = Active;
×
481
    }
482

483
    if (m_state != state)
×
484
        setState(state);
×
485
}
×
486

487
/*****************************************************************************
488
 * Key sequence handler
489
 *****************************************************************************/
490

491
void VCButton::setKeySequence(const QKeySequence& keySequence)
12✔
492
{
493
    m_keySequence = QKeySequence(keySequence);
12✔
494
}
12✔
495

496
QKeySequence VCButton::keySequence() const
9✔
497
{
498
    return m_keySequence;
9✔
499
}
500

501
void VCButton::slotKeyPressed(const QKeySequence& keySequence)
5✔
502
{
503
    if (acceptsInput() == false)
5✔
504
        return;
×
505

506
    if (m_keySequence == keySequence)
5✔
507
        pressFunction();
5✔
508
}
509

510
void VCButton::slotKeyReleased(const QKeySequence& keySequence)
3✔
511
{
512
    if (acceptsInput() == false)
3✔
513
        return;
×
514

515
    if (m_keySequence == keySequence)
3✔
516
        releaseFunction();
3✔
517
}
518

519
void VCButton::updateFeedback()
46✔
520
{
521
    if (m_state == Monitoring)
46✔
522
        return;
×
523

524
    QSharedPointer<QLCInputSource> src = inputSource();
92✔
525
    if (!src.isNull() && src->isValid() == true)
46✔
526
    {
527
        if (m_state == Inactive)
11✔
528
            sendFeedback(src->lowerValue());
5✔
529
        else
530
            sendFeedback(src->upperValue());
6✔
531
    }
532
}
533

534
/*****************************************************************************
535
 * External input
536
 *****************************************************************************/
537

538
void VCButton::slotInputValueChanged(quint32 universe, quint32 channel, uchar value)
15✔
539
{
540
    /* Don't let input data through in design mode or if disabled */
541
    if (acceptsInput() == false)
15✔
542
        return;
1✔
543

544
    if (checkInputSource(universe, (page() << 16) | channel, value, sender()))
14✔
545
    {
546
        if (m_action == Flash)
14✔
547
        {
548
            // Keep the button depressed only while the external button is kept down.
549
            // Raise the button when the external button is raised.
550
            if (state() == Inactive && value > 0)
6✔
551
                pressFunction();
2✔
552
            else if (state() == Active && value == 0)
4✔
553
                releaseFunction();
2✔
554
        }
555
        else
556
        {
557
            if (value > 0)
8✔
558
            {
559
                // Only toggle when the external button is pressed.
560
                pressFunction();
5✔
561
            }
562
            else
563
            {
564
                // Work around the "internal" feedback of some controllers
565
                // by updating feedback state after button release.
566
                updateFeedback();
3✔
567
            }
568
        }
569
    }
570
}
571

572
/*****************************************************************************
573
 * Button action
574
 *****************************************************************************/
575

576
void VCButton::setAction(Action action)
42✔
577
{
578
    // Blackout signal connection
579
    if (m_action == Blackout && action != Blackout)
42✔
580
        disconnect(m_doc->inputOutputMap(), SIGNAL(blackoutChanged(bool)),
1✔
581
                this, SLOT(slotBlackoutChanged(bool)));
582
    else if (m_action != Blackout && action == Blackout)
41✔
583
        connect(m_doc->inputOutputMap(), SIGNAL(blackoutChanged(bool)),
1✔
584
                this, SLOT(slotBlackoutChanged(bool)));
585

586
    // Action update
587
    m_action = action;
42✔
588
    updateIcon();
42✔
589

590
    // Update tooltip
591
    if (m_action == Blackout)
42✔
592
        setToolTip(tr("Toggle Blackout"));
1✔
593
    else if (m_action == StopAll)
41✔
594
        setToolTip(tr("Stop ALL functions!"));
1✔
595
}
42✔
596

597
VCButton::Action VCButton::action() const
11✔
598
{
599
    return m_action;
11✔
600
}
601

602
QString VCButton::actionToString(VCButton::Action action)
4✔
603
{
604
    if (action == Flash)
4✔
605
        return QString(KXMLQLCVCButtonActionFlash);
2✔
606
    else if (action == Blackout)
2✔
607
        return QString(KXMLQLCVCButtonActionBlackout);
×
608
    else if (action == StopAll)
2✔
609
        return QString(KXMLQLCVCButtonActionStopAll);
×
610
    else
611
        return QString(KXMLQLCVCButtonActionToggle);
2✔
612
}
613

614
VCButton::Action VCButton::stringToAction(const QString& str)
5✔
615
{
616
    if (str == KXMLQLCVCButtonActionFlash)
5✔
617
        return Flash;
3✔
618
    else if (str == KXMLQLCVCButtonActionBlackout)
2✔
619
        return Blackout;
×
620
    else if (str == KXMLQLCVCButtonActionStopAll)
2✔
621
        return StopAll;
×
622
    else
623
        return Toggle;
2✔
624
}
625

626
void VCButton::setStopAllFadeOutTime(int ms)
2✔
627
{
628
    m_blackoutFadeOutTime = ms;
2✔
629
}
2✔
630

631
int VCButton::stopAllFadeTime() const
3✔
632
{
633
    return m_blackoutFadeOutTime;
3✔
634
}
635

636
/*****************************************************************************
637
 * Intensity adjustment
638
 *****************************************************************************/
639

640
void VCButton::enableStartupIntensity(bool enable)
11✔
641
{
642
    m_startupIntensityEnabled = enable;
11✔
643
}
11✔
644

645
bool VCButton::isStartupIntensityEnabled() const
16✔
646
{
647
    return m_startupIntensityEnabled;
16✔
648
}
649

650
void VCButton::setStartupIntensity(qreal fraction)
179✔
651
{
652
    m_startupIntensity = CLAMP(fraction, qreal(0), qreal(1));
179✔
653
}
179✔
654

655
qreal VCButton::startupIntensity() const
183✔
656
{
657
    return m_startupIntensity;
183✔
658
}
659

660
void VCButton::slotAttributeChanged(int value)
×
661
{
662
#if 0
663
    ClickAndGoSlider *slider = (ClickAndGoSlider *)sender();
664
    int idx = slider->property("attrIdx").toInt();
665

666
    Function* func = m_doc->function(m_function);
667
    if (func != NULL)
668
        func->adjustAttribute((qreal)value / 100, idx);
669
#else
670
    Q_UNUSED(value)
671
#endif
672
}
×
673

674

675

676
/*****************************************************************************
677
 * Flash Properties
678
 *****************************************************************************/
679

680
bool VCButton::flashOverrides()
5✔
681
{
682
    return m_flashOverrides;
5✔
683
}
684

685
void VCButton::setFlashOverride(bool override)
1✔
686
{
687
    m_flashOverrides = override;
1✔
688
}
1✔
689

690
bool VCButton::flashForceLTP()
5✔
691
{
692
    return m_flashForceLTP;
5✔
693
}
694

695
void VCButton::setFlashForceLTP(bool forceLTP)
1✔
696
{
697
    m_flashForceLTP = forceLTP;
1✔
698
}
1✔
699

700

701

702
/*****************************************************************************
703
 * Button press / release handlers
704
 *****************************************************************************/
705

706
void VCButton::pressFunction()
13✔
707
{
708
    /* Don't allow pressing during design mode */
709
    if (mode() == Doc::Design)
13✔
710
        return;
×
711

712
    Function* f = NULL;
13✔
713
    if (m_action == Toggle)
13✔
714
    {
715
        f = m_doc->function(m_function);
6✔
716
        if (f == NULL)
6✔
717
            return;
×
718

719
        // if the button is in a SoloFrame and the function is running but was
720
        // started by a different function (a chaser or collection), turn other
721
        // functions off and start this one.
722
        if (state() == Active && !(isChildOfSoloFrame() && f->startedAsChild()))
6✔
723
        {
724
            f->stop(functionParent());
3✔
725
            resetIntensityOverrideAttribute();
3✔
726
        }
727
        else
728
        {
729
            adjustFunctionIntensity(f, intensity());
3✔
730

731
            // starting a Chaser is a special case, since it is necessary
732
            // to use Chaser Actions to properly start the first
733
            // Chaser step with the right intensity
734
            if (f->type() == Function::ChaserType || f->type() == Function::SequenceType)
3✔
735
            {
736
                ChaserAction action;
737
                action.m_action = ChaserSetStepIndex;
×
738
                action.m_stepIndex = 0;
×
739
                action.m_masterIntensity = intensity();
×
740
                action.m_stepIntensity = 1.0;
×
741
                action.m_fadeMode = Chaser::FromFunction;
×
742

743
                Chaser *chaser = qobject_cast<Chaser*>(f);
×
744
                chaser->setAction(action);
×
745
            }
746

747
            f->start(m_doc->masterTimer(), functionParent());
3✔
748
            setState(Active);
3✔
749
            emit functionStarting(m_function);
3✔
750
        }
751
    }
752
    else if (m_action == Flash && state() == Inactive)
7✔
753
    {
754
        f = m_doc->function(m_function);
4✔
755
        if (f != NULL)
4✔
756
        {
757
            adjustFunctionIntensity(f, intensity());
4✔
758
            f->flash(m_doc->masterTimer(), flashOverrides(), flashForceLTP());
4✔
759
            setState(Active);
4✔
760
        }
761
    }
762
    else if (m_action == Blackout)
3✔
763
    {
764
        m_doc->inputOutputMap()->toggleBlackout();
2✔
765
    }
766
    else if (m_action == StopAll)
1✔
767
    {
768
        if (stopAllFadeTime() == 0)
1✔
769
            m_doc->masterTimer()->stopAllFunctions();
1✔
770
        else
771
            m_doc->masterTimer()->fadeAndStopAll(stopAllFadeTime());
×
772
    }
773
}
774

775
FunctionParent VCButton::functionParent() const
6✔
776
{
777
    return FunctionParent(FunctionParent::ManualVCWidget, id());
6✔
778
}
779

780
void VCButton::releaseFunction()
6✔
781
{
782
    /* Don't allow operation during design mode */
783
    if (mode() == Doc::Design)
6✔
784
        return;
×
785

786
    if (m_action == Flash && state() == Active)
6✔
787
    {
788
        Function* f = m_doc->function(m_function);
4✔
789
        if (f != NULL)
4✔
790
        {
791
            f->unFlash(m_doc->masterTimer());
4✔
792
            resetIntensityOverrideAttribute();
4✔
793
            setState(Inactive);
4✔
794
        }
795
    }
796
}
797

798
void VCButton::slotFunctionRunning(quint32 fid)
5✔
799
{
800
    if (fid == m_function && m_action == Toggle)
5✔
801
    {
802
        if (state() == Inactive)
3✔
803
            setState(Monitoring);
×
804
        emit functionStarting(m_function);
3✔
805
    }
806
}
5✔
807

808
void VCButton::slotFunctionStopped(quint32 fid)
6✔
809
{
810
    if (fid == m_function && m_action == Toggle)
6✔
811
    {
812
        resetIntensityOverrideAttribute();
3✔
813
        setState(Inactive);
3✔
814
        blink(250);
3✔
815
    }
816
}
6✔
817

818
void VCButton::slotFunctionFlashing(quint32 fid, bool state)
11✔
819
{
820
    // Do not change the state of the button for Blackout or Stop All Functions buttons
821
    if (m_action != Toggle && m_action != Flash)
11✔
822
        return;
×
823

824
    if (fid != m_function)
11✔
825
        return;
1✔
826

827
    // if the function was flashed by another button, and the function is still running, keep the button pushed
828
    Function* f = m_doc->function(m_function);
10✔
829
    if (state == false && m_action == Toggle && f != NULL && f->isRunning())
10✔
830
    {
831
        return;
1✔
832
    }
833

834
    setState(state ? Active : Inactive);
9✔
835
}
836

837
void VCButton::blink(int ms)
3✔
838
{
839
    slotBlink();
3✔
840
    QTimer::singleShot(ms, this, SLOT(slotBlink()));
3✔
841
}
3✔
842

843
void VCButton::slotBlink()
4✔
844
{
845
    // This function is called twice with same XOR mask,
846
    // thus creating a brief opposite-color -- normal-color blink
847
    QPalette pal = palette();
8✔
848
    QColor color(pal.color(QPalette::Button));
4✔
849
    color.setRgb(color.red()^0xff, color.green()^0xff, color.blue()^0xff);
4✔
850
    pal.setColor(QPalette::Button, color);
4✔
851
    setPalette(pal);
4✔
852
}
4✔
853

854
void VCButton::slotBlackoutChanged(bool state)
2✔
855
{
856
    setState(state ? Active : Inactive);
2✔
857
}
2✔
858

859
bool VCButton::isChildOfSoloFrame() const
3✔
860
{
861
    QWidget* parent = parentWidget();
3✔
862
    while (parent != NULL)
6✔
863
    {
864
        if (qobject_cast<VCSoloFrame*>(parent) != NULL)
3✔
865
            return true;
×
866
        parent = parent->parentWidget();
3✔
867
    }
868
    return false;
3✔
869
}
870

871
/*****************************************************************************
872
 * Custom menu
873
 *****************************************************************************/
874

875
QMenu* VCButton::customMenu(QMenu* parentMenu)
2✔
876
{
877
    QMenu* menu = new QMenu(parentMenu);
2✔
878
    menu->setTitle(tr("Icon"));
2✔
879
    menu->addAction(m_chooseIconAction);
2✔
880
    menu->addAction(m_resetIconAction);
2✔
881

882
    return menu;
2✔
883
}
884

885
void VCButton::adjustIntensity(qreal val)
×
886
{
887
    if (state() == Active)
×
888
    {
889
        Function* func = m_doc->function(m_function);
×
890
        if (func != NULL)
×
891
            adjustFunctionIntensity(func, val);
×
892
    }
893

894
    VCWidget::adjustIntensity(val);
×
895
}
×
896

897
/*****************************************************************************
898
 * Load & Save
899
 *****************************************************************************/
900

901
bool VCButton::loadXML(QXmlStreamReader &root)
4✔
902
{
903
    bool visible = false;
4✔
904
    int x = 0;
4✔
905
    int y = 0;
4✔
906
    int w = 0;
4✔
907
    int h = 0;
4✔
908

909
    if (root.name() != KXMLQLCVCButton)
4✔
910
    {
911
        qWarning() << Q_FUNC_INFO << "Button node not found";
1✔
912
        return false;
1✔
913
    }
914

915
    /* Widget commons */
916
    loadXMLCommon(root);
3✔
917

918
    /* Icon */
919
    setIconPath(m_doc->denormalizeComponentPath(root.attributes().value(KXMLQLCVCButtonIcon).toString()));
3✔
920

921
    /* Children */
922
    while (root.readNextStartElement())
19✔
923
    {
924
        //qDebug() << "VC Button tag:" << root.name();
925
        if (root.name() == KXMLQLCWindowState)
16✔
926
        {
927
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
2✔
928
            setGeometry(x, y, w, h);
2✔
929
        }
930
        else if (root.name() == KXMLQLCVCWidgetAppearance)
14✔
931
        {
932
            loadXMLAppearance(root);
2✔
933
        }
934
        else if (root.name() == KXMLQLCVCButtonFunction)
12✔
935
        {
936
            QString str = root.attributes().value(KXMLQLCVCButtonFunctionID).toString();
6✔
937
            setFunction(str.toUInt());
2✔
938
            root.skipCurrentElement();
2✔
939
        }
940
        else if (root.name() == KXMLQLCVCWidgetInput)
10✔
941
        {
942
            loadXMLInput(root);
2✔
943
        }
944
        else if (root.name() == KXMLQLCVCButtonAction)
8✔
945
        {
946
            QXmlStreamAttributes attrs = root.attributes();
4✔
947
            setAction(stringToAction(root.readElementText()));
2✔
948
            if (attrs.hasAttribute(KXMLQLCVCButtonStopAllFadeTime))
2✔
949
                setStopAllFadeOutTime(attrs.value(KXMLQLCVCButtonStopAllFadeTime).toString().toInt());
×
950
        }
951
        else if (root.name() == KXMLQLCVCButtonFlashProperties)
6✔
952
        {
953
            QString str = root.attributes().value(KXMLQLCVCButtonFlashOverrides).toString();
×
954
            if (str.isEmpty() == false)
×
955
            {
956
                setFlashOverride((bool)str.toInt());
×
957
            }
958

959
            str = root.attributes().value(KXMLQLCVCButtonFlashForceLTP).toString();
×
960
            if (str.isEmpty() == false)
×
961
            {
962
                setFlashForceLTP((bool)str.toInt());
×
963
            }
964
        }
965
        else if (root.name() == KXMLQLCVCButtonKey)
6✔
966
        {
967
            setKeySequence(stripKeySequence(QKeySequence(root.readElementText())));
2✔
968
        }
969
        else if (root.name() == KXMLQLCVCButtonIntensity)
4✔
970
        {
971
            bool adjust;
972
            if (root.attributes().value(KXMLQLCVCButtonIntensityAdjust).toString() == KXMLQLCTrue)
2✔
973
                adjust = true;
1✔
974
            else
975
                adjust = false;
1✔
976
            setStartupIntensity(qreal(root.readElementText().toInt()) / qreal(100));
2✔
977
            enableStartupIntensity(adjust);
2✔
978
        }
979
        else
980
        {
981
            qWarning() << Q_FUNC_INFO << "Unknown button tag:" << root.name().toString();
2✔
982
            root.skipCurrentElement();
2✔
983
        }
984
    }
985

986
    /* All buttons start raised... */
987
    setState(Inactive);
3✔
988

989
    return true;
3✔
990
}
991

992
bool VCButton::saveXML(QXmlStreamWriter *doc)
1✔
993
{
994
    Q_ASSERT(doc != NULL);
1✔
995

996
    /* VC button entry */
997
    doc->writeStartElement(KXMLQLCVCButton);
1✔
998

999
    saveXMLCommon(doc);
1✔
1000

1001
    /* Icon */
1002
    doc->writeAttribute(KXMLQLCVCButtonIcon, m_doc->normalizeComponentPath(iconPath()));
1✔
1003

1004
    /* Window state */
1005
    saveXMLWindowState(doc);
1✔
1006

1007
    /* Appearance */
1008
    saveXMLAppearance(doc);
1✔
1009

1010
    /* Function */
1011
    doc->writeStartElement(KXMLQLCVCButtonFunction);
1✔
1012
    doc->writeAttribute(KXMLQLCVCButtonFunctionID, QString::number(function()));
1✔
1013
    doc->writeEndElement();
1✔
1014

1015
    /* Action */
1016
    doc->writeStartElement(KXMLQLCVCButtonAction);
1✔
1017

1018
    if (action() == StopAll && stopAllFadeTime() != 0)
1✔
1019
    {
1020
        doc->writeAttribute(KXMLQLCVCButtonStopAllFadeTime, QString::number(stopAllFadeTime()));
×
1021
    }
1022
    doc->writeCharacters(actionToString(action()));
1✔
1023
    doc->writeEndElement();
1✔
1024

1025
    if(action() == Flash)
1✔
1026
    {
1027
        doc->writeStartElement(KXMLQLCVCButtonFlashProperties);
1✔
1028
        doc->writeAttribute(KXMLQLCVCButtonFlashOverrides, QString::number(flashOverrides()));
1✔
1029
        doc->writeAttribute(KXMLQLCVCButtonFlashForceLTP, QString::number(flashForceLTP()));
1✔
1030
        doc->writeEndElement();
1✔
1031
    }
1032

1033
    /* Key sequence */
1034
    if (m_keySequence.isEmpty() == false)
1✔
1035
        doc->writeTextElement(KXMLQLCVCButtonKey, m_keySequence.toString());
1✔
1036

1037
    /* Intensity adjustment */
1038
    doc->writeStartElement(KXMLQLCVCButtonIntensity);
1✔
1039
    doc->writeAttribute(KXMLQLCVCButtonIntensityAdjust,
1✔
1040
                     isStartupIntensityEnabled() ? KXMLQLCTrue : KXMLQLCFalse);
1✔
1041
    doc->writeCharacters(QString::number(int(startupIntensity() * 100)));
1✔
1042
    doc->writeEndElement();
1✔
1043

1044
    /* External input */
1045
    saveXMLInput(doc);
1✔
1046

1047
    /* End the <Button> tag */
1048
    doc->writeEndElement();
1✔
1049

1050
    return true;
1✔
1051
}
1052

1053
/*****************************************************************************
1054
 * Event handlers
1055
 *****************************************************************************/
1056

1057
void VCButton::paintEvent(QPaintEvent* e)
8✔
1058
{
1059
    QStyleOptionButton option;
16✔
1060
    option.initFrom(this);
8✔
1061

1062
    /* This should look like a normal button */
1063
    option.features = QStyleOptionButton::None;
8✔
1064

1065
    /* Sunken or raised based on state() status */
1066
    if (state() == Inactive)
8✔
1067
        option.state = QStyle::State_Raised;
7✔
1068
    else
1069
        option.state = QStyle::State_Sunken;
1✔
1070

1071
    /* Custom icons are always enabled, to see them in full color also in design mode */
1072
    if (m_action == Toggle || m_action == Flash)
8✔
1073
        option.state |= QStyle::State_Enabled;
8✔
1074

1075
    /* Icon */
1076
    option.icon = m_icon;
8✔
1077
    option.iconSize = m_iconSize;
8✔
1078

1079
    /* Paint the button */
1080
    QPainter painter(this);
16✔
1081
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1082

1083
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
8✔
1084

1085
    if (m_backgroundImage.isEmpty() == false)
8✔
1086
    {
1087
        QRect pxRect = m_bgPixmap.rect();
×
1088
        // if the pixmap is bigger than the button, then paint a scaled version of it
1089
        // covering the whole button surface
1090
        // if the pixmap is smaller than the button, draw a centered pixmap
1091
        if (pxRect.contains(rect()))
×
1092
        {
1093
            if (m_ledStyle == true)
×
1094
                painter.drawPixmap(rect(), m_bgPixmap);
×
1095
            else
1096
                painter.drawPixmap(3, 3, width() - 6, height() - 6, m_bgPixmap);
×
1097
        }
1098
        else
1099
        {
1100
            painter.drawPixmap((width() - pxRect.width()) / 2,
×
1101
                               (height() - pxRect.height()) / 2,
×
1102
                               m_bgPixmap);
×
1103
        }
1104
    }
1105

1106
    /* Paint caption with text wrapping */
1107
    if (caption().isEmpty() == false)
8✔
1108
    {
1109
        style()->drawItemText(&painter,
9✔
1110
                              rect(),
3✔
1111
                              Qt::AlignCenter | Qt::TextWordWrap,
3✔
1112
                              palette(),
1113
                              (mode() == Doc::Operate),
3✔
1114
                              caption());
6✔
1115
    }
1116

1117
    /* Flash emblem */
1118
    if (m_action == Flash)
8✔
1119
    {
1120
        QIcon icon(":/flash.png");
2✔
1121
        painter.drawPixmap(rect().width() - 18, 2,
2✔
1122
                           icon.pixmap(QSize(16, 16), QIcon::Normal, QIcon::On));
4✔
1123
    }
1124

1125
    if (m_ledStyle == true)
8✔
1126
    {
1127
        painter.setPen(QPen(QColor(160, 160, 160, 255), 2));
×
1128

1129
        if (state() == Active)
×
1130
            painter.setBrush(QBrush(QColor(0, 230, 0, 255)));
×
1131
        else if (state() == Monitoring)
×
1132
            painter.setBrush(QBrush(QColor(255, 170, 0, 255)));
×
1133
        else
1134
            painter.setBrush(QBrush(QColor(110, 110, 110, 255)));
×
1135

1136
        int dim = rect().width() / 6;
×
1137
        if (dim > 14) dim = 14;
×
1138

1139
        painter.drawEllipse(6, 6, dim, dim);      // Style #1
×
1140
        //painter.drawRoundedRect(-1, -1, dim, dim, 3, 3);   // Style #2
1141
    }
1142
    else
1143
    {
1144
        // Style #3
1145
        painter.setBrush(Qt::NoBrush);
8✔
1146

1147
        if (state() != Inactive)
8✔
1148
        {
1149
            int borderWidth = (rect().width() > 80)?3:2;
1✔
1150
            painter.setPen(QPen(QColor(20, 20, 20, 255), borderWidth * 2));
1✔
1151
            painter.drawRoundedRect(borderWidth, borderWidth,
3✔
1152
                                    rect().width() - borderWidth * 2, rect().height() - (borderWidth * 2),
1✔
1153
                                    borderWidth + 1,  borderWidth + 1);
1✔
1154
            if (state() == Monitoring)
1✔
1155
                painter.setPen(QPen(QColor(255, 170, 0, 255), borderWidth));
×
1156
            else
1157
                painter.setPen(QPen(QColor(0, 230, 0, 255), borderWidth));
1✔
1158
            painter.drawRoundedRect(borderWidth, borderWidth,
3✔
1159
                                    rect().width() - borderWidth * 2, rect().height() - (borderWidth * 2),
1✔
1160
                                    borderWidth, borderWidth);
1161
        }
1162
        else
1163
        {
1164
            painter.setPen(QPen(QColor(160, 160, 160, 255), 3));
7✔
1165
            painter.drawRoundedRect(1, 1, rect().width() - 2, rect().height() - 2, 3, 3);
7✔
1166
        }
1167
    }
1168

1169
    /* Stop painting here */
1170
    painter.end();
8✔
1171

1172
    /* Draw a selection frame if appropriate */
1173
    VCWidget::paintEvent(e);
8✔
1174
}
8✔
1175

1176
void VCButton::mousePressEvent(QMouseEvent* e)
2✔
1177
{
1178
    if (mode() == Doc::Design)
2✔
1179
        VCWidget::mousePressEvent(e);
1✔
1180
    else if (e->button() == Qt::LeftButton)
1✔
1181
        pressFunction();
1✔
1182
#if 0
1183
    else if (e->button() == Qt::RightButton)
1184
    {
1185
        Function* func = m_doc->function(m_function);
1186
        if (func != NULL)
1187
        {
1188
            QString menuStyle = "QMenu { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #B9D9E8, stop:1 #A4C0CE);"
1189
                            "border: 1px solid black; border-radius: 4px; font:bold; }";
1190
            QMenu *menu = new QMenu();
1191
            menu->setStyleSheet(menuStyle);
1192
            int idx = 0;
1193
            foreach(Attribute attr, func->attributes())
1194
            {
1195
                QString slStyle = "QSlider::groove:horizontal { border: 1px solid #999999; margin: 0; border-radius: 2px;"
1196
                        "height: 15px; background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4); }"
1197

1198
                        "QSlider::handle:horizontal {"
1199
                        "background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);"
1200
                        "border: 1px solid #5c5c5c; width: 15px; border-radius: 2px; margin: -1px 0; }"
1201

1202
                        "QSlider::sub-page:horizontal { background: #114EA2; border-radius: 2px; }";
1203

1204
                QWidget *entryWidget = new QWidget();
1205
                QHBoxLayout *hbox = new QHBoxLayout(menu);
1206
                hbox->setMargin(3);
1207
                QLabel *label = new QLabel(attr.m_name);
1208
                label->setAlignment(Qt::AlignLeft);
1209
                label->setFixedWidth(100);
1210
                ClickAndGoSlider *slider = new ClickAndGoSlider(menu);
1211
                slider->setOrientation(Qt::Horizontal);
1212
                slider->setSliderStyleSheet(slStyle);
1213
                slider->setFixedSize(QSize(100, 18));
1214
                slider->setMinimum(0);
1215
                slider->setMaximum(100);
1216
                slider->setValue(attr.m_value * 100);
1217
                slider->setProperty("attrIdx", QVariant(idx));
1218
                connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotAttributeChanged(int)));
1219
                hbox->addWidget(label);
1220
                hbox->addWidget(slider);
1221
                entryWidget->setLayout(hbox);
1222
                QWidgetAction *sliderBoxAction = new QWidgetAction(menu);
1223
                sliderBoxAction->setDefaultWidget(entryWidget);
1224
                menu->addAction(sliderBoxAction);
1225
                idx++;
1226
            }
1227
            menu->exec(QCursor::pos());
1228
        }
1229
    }
1230
#endif
1231
}
2✔
1232

1233
void VCButton::mouseReleaseEvent(QMouseEvent* e)
2✔
1234
{
1235
    if (mode() == Doc::Design)
2✔
1236
        VCWidget::mouseReleaseEvent(e);
1✔
1237
    else
1238
        releaseFunction();
1✔
1239
}
2✔
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

© 2025 Coveralls, Inc