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

mcallegari / qlcplus / 19144422256

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

push

github

mcallegari
Back to 5.1.0 debug

17718 of 51723 relevant lines covered (34.26%)

19528.23 hits per line

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

80.85
/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 "virtualconsole.h"
55
#include "chaseraction.h"
56
#include "mastertimer.h"
57
#include "vcsoloframe.h"
58
#include "vcbutton.h"
59
#include "function.h"
60
#include "apputil.h"
61
#include "chaser.h"
62
#include "doc.h"
63

64
const QSize VCButton::defaultSize(QSize(50, 50));
65

66
/*****************************************************************************
67
 * Initialization
68
 *****************************************************************************/
69

70
VCButton::VCButton(QWidget* parent, Doc* doc) : VCWidget(parent, doc)
25✔
71
    , m_iconPath()
25✔
72
    , m_blackoutFadeOutTime(0)
25✔
73
    , m_startupIntensityEnabled(false)
25✔
74
    , m_startupIntensity(1.0)
25✔
75
    , m_flashOverrides(false)
25✔
76
    , m_flashForceLTP(false)
50✔
77
{
78
    /* Set the class name "VCButton" as the object name as well */
79
    setObjectName(VCButton::staticMetaObject.className());
25✔
80

81
    /* No function is initially attached to the button */
82
    m_function = Function::invalidId();
25✔
83

84
    setType(VCWidget::ButtonWidget);
25✔
85
    setCaption(QString());
25✔
86
    setState(Inactive);
25✔
87
    m_action = Action(-1); // avoid use of uninitialized value
25✔
88
    setAction(Toggle);
25✔
89
    setFrameStyle(KVCFrameStyleNone);
25✔
90

91
    /* Menu actions */
92
    m_chooseIconAction = new QAction(QIcon(":/image.png"), tr("Choose..."),
50✔
93
                                     this);
25✔
94
    m_chooseIconAction->setShortcut(QKeySequence("SHIFT+C"));
25✔
95

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

99
    connect(m_chooseIconAction, SIGNAL(triggered(bool)),
25✔
100
            this, SLOT(slotChooseIcon()));
101
    connect(m_resetIconAction, SIGNAL(triggered(bool)),
25✔
102
            this, SLOT(slotResetIcon()));
103

104
    /* Initial size */
105
    QSettings settings;
25✔
106
    QVariant var = settings.value(SETTINGS_BUTTON_SIZE);
25✔
107
    if (var.isValid() == true)
25✔
108
        resize(var.toSize());
×
109
    else
110
        resize(defaultSize);
25✔
111

112
    var = settings.value(SETTINGS_BUTTON_STATUSLED);
25✔
113
    if (var.isValid() == true && var.toBool() == true)
25✔
114
        m_ledStyle = true;
×
115
    else
116
        m_ledStyle = false;
25✔
117

118
    setStyle(AppUtil::saneStyle());
25✔
119

120
    /* Listen to function removals */
121
    connect(m_doc, SIGNAL(functionRemoved(quint32)),
25✔
122
            this, SLOT(slotFunctionRemoved(quint32)));
123
}
25✔
124

125
VCButton::~VCButton()
29✔
126
{
127
}
29✔
128

129
void VCButton::setID(quint32 id)
1✔
130
{
131
    VCWidget::setID(id);
1✔
132

133
    if (caption().isEmpty())
1✔
134
        setCaption(tr("Button %1").arg(id));
×
135
}
1✔
136

137
/*****************************************************************************
138
 * Clipboard
139
 *****************************************************************************/
140

141
VCWidget* VCButton::createCopy(VCWidget* parent)
2✔
142
{
143
    Q_ASSERT(parent != NULL);
2✔
144

145
    VCButton* button = new VCButton(parent, m_doc);
2✔
146
    if (button->copyFrom(this) == false)
2✔
147
    {
148
        delete button;
×
149
        button = NULL;
×
150
    }
151

152
    return button;
2✔
153
}
154

155
bool VCButton::copyFrom(const VCWidget* widget)
2✔
156
{
157
    const VCButton* button = qobject_cast <const VCButton*> (widget);
2✔
158
    if (button == NULL)
2✔
159
        return false;
×
160

161
    /* Copy button-specific stuff */
162
    setIconPath(button->iconPath());
2✔
163
    setKeySequence(button->keySequence());
2✔
164
    setFunction(button->function());
2✔
165
    enableStartupIntensity(button->isStartupIntensityEnabled());
2✔
166
    setStartupIntensity(button->startupIntensity());
2✔
167
    setStopAllFadeOutTime(button->stopAllFadeTime());
2✔
168
    setAction(button->action());
2✔
169
    m_state = button->m_state;
2✔
170

171
    m_flashForceLTP = button->flashForceLTP();
2✔
172
    m_flashOverrides = button->flashOverrides();
2✔
173

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

178
/*****************************************************************************
179
 * Properties
180
 *****************************************************************************/
181

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

189
/*****************************************************************************
190
 * Background color
191
 *****************************************************************************/
192

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

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

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

210
    m_doc->setModified();
5✔
211
}
5✔
212

213
void VCButton::resetBackgroundColor()
2✔
214
{
215
    QColor fg;
2✔
216

217
    m_hasCustomBackgroundColor = false;
2✔
218
    m_backgroundImage = QString();
2✔
219

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

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

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

235
    m_doc->setModified();
2✔
236
}
2✔
237

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

243
/*****************************************************************************
244
 * Foreground color
245
 *****************************************************************************/
246

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

251
    m_hasCustomForegroundColor = true;
3✔
252

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

257
    m_doc->setModified();
3✔
258
}
3✔
259

260
void VCButton::resetForegroundColor()
2✔
261
{
262
    QColor bg;
2✔
263

264
    m_hasCustomForegroundColor = false;
2✔
265

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

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

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

277
    m_doc->setModified();
2✔
278
}
2✔
279

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

285
/*****************************************************************************
286
 * Button icon
287
 *****************************************************************************/
288

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

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

298
    updateIcon();
10✔
299
    m_doc->setModified();
10✔
300
    update();
10✔
301
}
10✔
302

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

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

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

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

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

359
/*****************************************************************************
360
 * Function attachment
361
 *****************************************************************************/
362

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

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

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

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

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

408
    VCWidget::adjustFunctionIntensity(f, finalValue);
7✔
409
}
7✔
410

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

415
    if (mode() == Doc::Design)
×
416
        return;
×
417

418
    if (fid == m_function || m_function == Function::invalidId())
×
419
        return;
×
420

421
    // stop the controlled Function only if actively started
422
    // by this Button or if monitoring the startup Function
423
    if (m_state != Active && m_function != m_doc->startupFunction())
×
424
        return;
×
425

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

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

447
/*****************************************************************************
448
 * Button state
449
 *****************************************************************************/
450

451
VCButton::ButtonState VCButton::state() const
72✔
452
{
453
    return m_state;
72✔
454
}
455

456
void VCButton::setState(ButtonState state)
59✔
457
{
458
    if (state == m_state)
59✔
459
        return;
20✔
460

461
    m_state = state;
39✔
462

463
    emit stateChanged(m_state);
39✔
464

465
    updateFeedback();
39✔
466

467
    update();
39✔
468
}
469

470
void VCButton::updateState()
×
471
{
472
    ButtonState state = Inactive;
×
473

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

486
    if (m_state != state)
×
487
        setState(state);
×
488
}
×
489

490
/*****************************************************************************
491
 * Key sequence handler
492
 *****************************************************************************/
493

494
void VCButton::setKeySequence(const QKeySequence& keySequence)
12✔
495
{
496
    m_keySequence = QKeySequence(keySequence);
12✔
497
}
12✔
498

499
QKeySequence VCButton::keySequence() const
9✔
500
{
501
    return m_keySequence;
9✔
502
}
503

504
void VCButton::slotKeyPressed(const QKeySequence& keySequence)
5✔
505
{
506
    if (acceptsInput() == false)
5✔
507
        return;
×
508

509
    if (m_keySequence == keySequence)
5✔
510
        pressFunction();
5✔
511
}
512

513
void VCButton::slotKeyReleased(const QKeySequence& keySequence)
3✔
514
{
515
    if (acceptsInput() == false)
3✔
516
        return;
×
517

518
    if (m_keySequence == keySequence)
3✔
519
        releaseFunction();
3✔
520
}
521

522
void VCButton::updateFeedback()
42✔
523
{
524
    //if (m_state == Monitoring)
525
    //    return;
526

527
    QSharedPointer<QLCInputSource> src = inputSource();
42✔
528
    if (!src.isNull() && src->isValid() == true)
42✔
529
    {
530
        if (m_state == Inactive)
11✔
531
            sendFeedback(src->feedbackValue(QLCInputFeedback::LowerValue), src, src->feedbackExtraParams(QLCInputFeedback::LowerValue));
5✔
532
        else if (m_state == Monitoring)
6✔
533
            sendFeedback(src->feedbackValue(QLCInputFeedback::MonitorValue), src, src->feedbackExtraParams(QLCInputFeedback::MonitorValue));
×
534
        else
535
            sendFeedback(src->feedbackValue(QLCInputFeedback::UpperValue), src, src->feedbackExtraParams(QLCInputFeedback::UpperValue));
6✔
536
    }
537
}
42✔
538

539
/*****************************************************************************
540
 * External input
541
 *****************************************************************************/
542

543
void VCButton::slotInputValueChanged(quint32 universe, quint32 channel, uchar value)
15✔
544
{
545
    /* Don't let input data through in design mode or if disabled */
546
    if (acceptsInput() == false)
15✔
547
        return;
1✔
548

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

577
/*****************************************************************************
578
 * Button action
579
 *****************************************************************************/
580

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

591
    // Action update
592
    m_action = action;
42✔
593
    updateIcon();
42✔
594

595
    // Update tooltip
596
    if (m_action == Blackout)
42✔
597
        setToolTip(tr("Toggle Blackout"));
1✔
598
    else if (m_action == StopAll)
41✔
599
        setToolTip(tr("Stop ALL functions!"));
1✔
600
}
42✔
601

602
VCButton::Action VCButton::action() const
11✔
603
{
604
    return m_action;
11✔
605
}
606

607
QString VCButton::actionToString(VCButton::Action action)
4✔
608
{
609
    if (action == Flash)
4✔
610
        return QString(KXMLQLCVCButtonActionFlash);
2✔
611
    else if (action == Blackout)
2✔
612
        return QString(KXMLQLCVCButtonActionBlackout);
×
613
    else if (action == StopAll)
2✔
614
        return QString(KXMLQLCVCButtonActionStopAll);
×
615
    else
616
        return QString(KXMLQLCVCButtonActionToggle);
2✔
617
}
618

619
VCButton::Action VCButton::stringToAction(const QString& str)
5✔
620
{
621
    if (str == KXMLQLCVCButtonActionFlash)
5✔
622
        return Flash;
3✔
623
    else if (str == KXMLQLCVCButtonActionBlackout)
2✔
624
        return Blackout;
×
625
    else if (str == KXMLQLCVCButtonActionStopAll)
2✔
626
        return StopAll;
×
627
    else
628
        return Toggle;
2✔
629
}
630

631
void VCButton::setStopAllFadeOutTime(int ms)
2✔
632
{
633
    m_blackoutFadeOutTime = ms;
2✔
634
}
2✔
635

636
int VCButton::stopAllFadeTime() const
3✔
637
{
638
    return m_blackoutFadeOutTime;
3✔
639
}
640

641
/*****************************************************************************
642
 * Intensity adjustment
643
 *****************************************************************************/
644

645
void VCButton::enableStartupIntensity(bool enable)
11✔
646
{
647
    m_startupIntensityEnabled = enable;
11✔
648
}
11✔
649

650
bool VCButton::isStartupIntensityEnabled() const
16✔
651
{
652
    return m_startupIntensityEnabled;
16✔
653
}
654

655
void VCButton::setStartupIntensity(qreal fraction)
179✔
656
{
657
    m_startupIntensity = CLAMP(fraction, qreal(0), qreal(1));
179✔
658
}
179✔
659

660
qreal VCButton::startupIntensity() const
183✔
661
{
662
    return m_startupIntensity;
183✔
663
}
664

665
void VCButton::slotAttributeChanged(int value)
×
666
{
667
#if 0
668
    ClickAndGoSlider *slider = (ClickAndGoSlider *)sender();
669
    int idx = slider->property("attrIdx").toInt();
670

671
    Function* func = m_doc->function(m_function);
672
    if (func != NULL)
673
        func->adjustAttribute((qreal)value / 100, idx);
674
#else
675
    Q_UNUSED(value)
676
#endif
677
}
×
678

679

680

681
/*****************************************************************************
682
 * Flash Properties
683
 *****************************************************************************/
684

685
bool VCButton::flashOverrides() const
7✔
686
{
687
    return m_flashOverrides;
7✔
688
}
689

690
void VCButton::setFlashOverride(bool shouldOverride)
1✔
691
{
692
    m_flashOverrides = shouldOverride;
1✔
693
}
1✔
694

695
bool VCButton::flashForceLTP() const
7✔
696
{
697
    return m_flashForceLTP;
7✔
698
}
699

700
void VCButton::setFlashForceLTP(bool forceLTP)
1✔
701
{
702
    m_flashForceLTP = forceLTP;
1✔
703
}
1✔
704

705

706

707
/*****************************************************************************
708
 * Button press / release handlers
709
 *****************************************************************************/
710

711
void VCButton::pressFunction()
13✔
712
{
713
    /* Don't allow pressing during design mode */
714
    if (mode() == Doc::Design)
13✔
715
        return;
×
716

717
    Function* f = NULL;
13✔
718
    if (m_action == Toggle)
13✔
719
    {
720
        f = m_doc->function(m_function);
6✔
721
        if (f == NULL)
6✔
722
            return;
×
723

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

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

748
                Chaser *chaser = qobject_cast<Chaser*>(f);
×
749
                chaser->setAction(action);
×
750
            }
751

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

780
FunctionParent VCButton::functionParent() const
6✔
781
{
782
    return FunctionParent(FunctionParent::ManualVCWidget, id());
6✔
783
}
784

785
void VCButton::releaseFunction()
6✔
786
{
787
    /* Don't allow operation during design mode */
788
    if (mode() == Doc::Design)
6✔
789
        return;
×
790

791
    if (m_action == Flash && state() == Active)
6✔
792
    {
793
        Function* f = m_doc->function(m_function);
4✔
794
        if (f != NULL)
4✔
795
        {
796
            f->unFlash(m_doc->masterTimer());
4✔
797
            resetIntensityOverrideAttribute();
4✔
798
            setState(Inactive);
4✔
799
        }
800
    }
801
}
802

803
void VCButton::slotFunctionRunning(quint32 fid)
5✔
804
{
805
    if (fid == m_function && m_action == Toggle)
5✔
806
    {
807
        if (state() == Inactive)
3✔
808
            setState(Monitoring);
×
809
        emit functionStarting(m_function);
3✔
810
    }
811
}
5✔
812

813
void VCButton::slotFunctionStopped(quint32 fid)
6✔
814
{
815
    if (fid == m_function && m_action == Toggle)
6✔
816
    {
817
        resetIntensityOverrideAttribute();
3✔
818
        setState(Inactive);
3✔
819
        blink(250);
3✔
820
    }
821
}
6✔
822

823
void VCButton::slotFunctionFlashing(quint32 fid, bool state)
11✔
824
{
825
    // Do not change the state of the button for Blackout or Stop All Functions buttons
826
    if (m_action != Toggle && m_action != Flash)
11✔
827
        return;
×
828

829
    if (fid != m_function)
11✔
830
        return;
1✔
831

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

839
    setState(state ? Active : Inactive);
9✔
840
}
841

842
void VCButton::blink(int ms)
3✔
843
{
844
    slotBlink();
3✔
845
    QTimer::singleShot(ms, this, SLOT(slotBlink()));
3✔
846
}
3✔
847

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

859
void VCButton::slotBlackoutChanged(bool state)
2✔
860
{
861
    setState(state ? Active : Inactive);
2✔
862
}
2✔
863

864
bool VCButton::isChildOfSoloFrame() const
3✔
865
{
866
    QWidget* parent = parentWidget();
3✔
867
    while (parent != NULL)
6✔
868
    {
869
        if (qobject_cast<VCSoloFrame*>(parent) != NULL)
3✔
870
            return true;
×
871
        parent = parent->parentWidget();
3✔
872
    }
873
    return false;
3✔
874
}
875

876
/*****************************************************************************
877
 * Custom menu
878
 *****************************************************************************/
879

880
QMenu* VCButton::customMenu(QMenu* parentMenu)
2✔
881
{
882
    QMenu* menu = new QMenu(parentMenu);
2✔
883
    menu->setTitle(tr("Icon"));
2✔
884
    menu->addAction(m_chooseIconAction);
2✔
885
    menu->addAction(m_resetIconAction);
2✔
886

887
    return menu;
2✔
888
}
889

890
void VCButton::adjustIntensity(qreal val)
×
891
{
892
    if (state() == Active)
×
893
    {
894
        Function* func = m_doc->function(m_function);
×
895
        if (func != NULL)
×
896
            adjustFunctionIntensity(func, val);
×
897
    }
898

899
    VCWidget::adjustIntensity(val);
×
900
}
×
901

902
/*****************************************************************************
903
 * Load & Save
904
 *****************************************************************************/
905

906
bool VCButton::loadXML(QXmlStreamReader &root)
4✔
907
{
908
    bool visible = false;
4✔
909
    int x = 0;
4✔
910
    int y = 0;
4✔
911
    int w = 0;
4✔
912
    int h = 0;
4✔
913

914
    if (root.name() != KXMLQLCVCButton)
4✔
915
    {
916
        qWarning() << Q_FUNC_INFO << "Button node not found";
1✔
917
        return false;
1✔
918
    }
919

920
    /* Widget commons */
921
    loadXMLCommon(root);
3✔
922

923
    /* Icon */
924
    setIconPath(m_doc->denormalizeComponentPath(root.attributes().value(KXMLQLCVCButtonIcon).toString()));
6✔
925

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

956
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashOverride))
2✔
957
                setFlashOverride(attrs.value(KXMLQLCVCButtonFlashOverride).toInt());
×
958

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

983
    /* All buttons start raised... */
984
    setState(Inactive);
3✔
985

986
    return true;
3✔
987
}
988

989
bool VCButton::saveXML(QXmlStreamWriter *doc)
1✔
990
{
991
    Q_ASSERT(doc != NULL);
1✔
992

993
    /* VC button entry */
994
    doc->writeStartElement(KXMLQLCVCButton);
2✔
995

996
    saveXMLCommon(doc);
1✔
997

998
    /* Icon */
999
    doc->writeAttribute(KXMLQLCVCButtonIcon, m_doc->normalizeComponentPath(iconPath()));
2✔
1000

1001
    /* Window state */
1002
    saveXMLWindowState(doc);
1✔
1003

1004
    /* Appearance */
1005
    saveXMLAppearance(doc);
1✔
1006

1007
    /* Function */
1008
    doc->writeStartElement(KXMLQLCVCButtonFunction);
2✔
1009
    doc->writeAttribute(KXMLQLCVCButtonFunctionID, QString::number(function()));
2✔
1010
    doc->writeEndElement();
1✔
1011

1012
    /* Action */
1013
    doc->writeStartElement(KXMLQLCVCButtonAction);
2✔
1014

1015
    if (action() == StopAll && stopAllFadeTime() != 0)
1✔
1016
    {
1017
        doc->writeAttribute(KXMLQLCVCButtonStopAllFadeTime, QString::number(stopAllFadeTime()));
×
1018
    }
1019
    else if (action() == Flash)
1✔
1020
    {
1021
        doc->writeAttribute(KXMLQLCVCButtonFlashOverride, QString::number(flashOverrides()));
2✔
1022
        doc->writeAttribute(KXMLQLCVCButtonFlashForceLTP, QString::number(flashForceLTP()));
2✔
1023
    }
1024
    doc->writeCharacters(actionToString(action()));
1✔
1025
    doc->writeEndElement();
1✔
1026

1027
    /* Key sequence */
1028
    if (m_keySequence.isEmpty() == false)
1✔
1029
        doc->writeTextElement(KXMLQLCVCButtonKey, m_keySequence.toString());
2✔
1030

1031
    /* Intensity adjustment */
1032
    doc->writeStartElement(KXMLQLCVCButtonIntensity);
2✔
1033
    doc->writeAttribute(KXMLQLCVCButtonIntensityAdjust,
2✔
1034
                     isStartupIntensityEnabled() ? KXMLQLCTrue : KXMLQLCFalse);
3✔
1035
    doc->writeCharacters(QString::number(int(startupIntensity() * 100)));
1✔
1036
    doc->writeEndElement();
1✔
1037

1038
    /* External input */
1039
    saveXMLInput(doc);
1✔
1040

1041
    /* End the <Button> tag */
1042
    doc->writeEndElement();
1✔
1043

1044
    return true;
1✔
1045
}
1046

1047
/*****************************************************************************
1048
 * Event handlers
1049
 *****************************************************************************/
1050

1051
void VCButton::paintEvent(QPaintEvent* e)
8✔
1052
{
1053
    QStyleOptionButton option;
8✔
1054
    option.initFrom(this);
8✔
1055

1056
    /* This should look like a normal button */
1057
    option.features = QStyleOptionButton::None;
8✔
1058

1059
    /* Sunken or raised based on state() status */
1060
    if (state() == Inactive)
8✔
1061
        option.state = QStyle::State_Raised;
7✔
1062
    else
1063
        option.state = QStyle::State_Sunken;
1✔
1064

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

1069
    /* Icon */
1070
    option.icon = m_icon;
8✔
1071
    option.iconSize = m_iconSize;
8✔
1072

1073
    /* Paint the button */
1074
    QPainter painter(this);
8✔
1075
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1076

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

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

1100
    /* Paint caption with text wrapping */
1101
    if (caption().isEmpty() == false)
8✔
1102
    {
1103
        style()->drawItemText(&painter,
9✔
1104
                              rect(),
3✔
1105
                              Qt::AlignCenter | Qt::TextWordWrap,
1106
                              palette(),
1107
                              (mode() == Doc::Operate),
3✔
1108
                              caption());
6✔
1109
    }
1110

1111
    /* Flash emblem */
1112
    if (m_action == Flash)
8✔
1113
    {
1114
        QIcon icon(":/flash.png");
2✔
1115
        painter.drawPixmap(rect().width() - 18, 2,
2✔
1116
                           icon.pixmap(QSize(16, 16), QIcon::Normal, QIcon::On));
4✔
1117
    }
2✔
1118

1119
    if (m_ledStyle == true)
8✔
1120
    {
1121
        painter.setPen(QPen(QColor(160, 160, 160, 255), 2));
×
1122

1123
        if (state() == Active)
×
1124
        {
1125
            if (m_flashForceLTP || m_flashOverrides)
×
1126
                painter.setBrush(QBrush(QColor(230, 0, 0, 255)));
×
1127
            else
1128
                painter.setBrush(QBrush(QColor(0, 230, 0, 255)));
×
1129
        }
1130
        else if (state() == Monitoring)
×
1131
            painter.setBrush(QBrush(QColor(255, 170, 0, 255)));
×
1132
        else
1133
            painter.setBrush(QBrush(QColor(110, 110, 110, 255)));
×
1134

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

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

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

1173
    /* Stop painting here */
1174
    painter.end();
8✔
1175

1176
    /* Draw a selection frame if appropriate */
1177
    VCWidget::paintEvent(e);
8✔
1178
}
8✔
1179

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

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

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

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

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

© 2026 Coveralls, Inc