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

mcallegari / qlcplus / 19409821768

16 Nov 2025 06:06PM UTC coverage: 34.233% (-0.01%) from 34.243%
19409821768

push

github

mcallegari
vc/soloframe: add an option to exclude functions monitored by buttons to be stopped

3 of 38 new or added lines in 9 files covered. (7.89%)

2 existing lines in 2 files now uncovered.

17724 of 51774 relevant lines covered (34.23%)

19630.45 hits per line

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

80.71
/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

NEW
411
void VCButton::notifyFunctionStarting(quint32 fid, qreal intensity, bool excludeMonitored)
×
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

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

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

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

450
/*****************************************************************************
451
 * Button state
452
 *****************************************************************************/
453

454
VCButton::ButtonState VCButton::state() const
72✔
455
{
456
    return m_state;
72✔
457
}
458

459
void VCButton::setState(ButtonState state)
59✔
460
{
461
    if (state == m_state)
59✔
462
        return;
19✔
463

464
    m_state = state;
40✔
465

466
    emit stateChanged(m_state);
40✔
467

468
    updateFeedback();
40✔
469

470
    update();
40✔
471
}
472

473
void VCButton::updateState()
×
474
{
475
    ButtonState state = Inactive;
×
476

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

489
    if (m_state != state)
×
490
        setState(state);
×
491
}
×
492

493
/*****************************************************************************
494
 * Key sequence handler
495
 *****************************************************************************/
496

497
void VCButton::setKeySequence(const QKeySequence& keySequence)
12✔
498
{
499
    m_keySequence = QKeySequence(keySequence);
12✔
500
}
12✔
501

502
QKeySequence VCButton::keySequence() const
9✔
503
{
504
    return m_keySequence;
9✔
505
}
506

507
void VCButton::slotKeyPressed(const QKeySequence& keySequence)
5✔
508
{
509
    if (acceptsInput() == false)
5✔
510
        return;
×
511

512
    if (m_keySequence == keySequence)
5✔
513
        pressFunction();
5✔
514
}
515

516
void VCButton::slotKeyReleased(const QKeySequence& keySequence)
3✔
517
{
518
    if (acceptsInput() == false)
3✔
519
        return;
×
520

521
    if (m_keySequence == keySequence)
3✔
522
        releaseFunction();
3✔
523
}
524

525
void VCButton::updateFeedback()
43✔
526
{
527
    //if (m_state == Monitoring)
528
    //    return;
529

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

542
/*****************************************************************************
543
 * External input
544
 *****************************************************************************/
545

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

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

580
/*****************************************************************************
581
 * Button action
582
 *****************************************************************************/
583

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

594
    // Action update
595
    m_action = action;
42✔
596
    updateIcon();
42✔
597

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

605
VCButton::Action VCButton::action() const
11✔
606
{
607
    return m_action;
11✔
608
}
609

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

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

634
void VCButton::setStopAllFadeOutTime(int ms)
2✔
635
{
636
    m_blackoutFadeOutTime = ms;
2✔
637
}
2✔
638

639
int VCButton::stopAllFadeTime() const
3✔
640
{
641
    return m_blackoutFadeOutTime;
3✔
642
}
643

644
/*****************************************************************************
645
 * Intensity adjustment
646
 *****************************************************************************/
647

648
void VCButton::enableStartupIntensity(bool enable)
11✔
649
{
650
    m_startupIntensityEnabled = enable;
11✔
651
}
11✔
652

653
bool VCButton::isStartupIntensityEnabled() const
16✔
654
{
655
    return m_startupIntensityEnabled;
16✔
656
}
657

658
void VCButton::setStartupIntensity(qreal fraction)
179✔
659
{
660
    m_startupIntensity = CLAMP(fraction, qreal(0), qreal(1));
179✔
661
}
179✔
662

663
qreal VCButton::startupIntensity() const
183✔
664
{
665
    return m_startupIntensity;
183✔
666
}
667

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

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

682

683

684
/*****************************************************************************
685
 * Flash Properties
686
 *****************************************************************************/
687

688
bool VCButton::flashOverrides() const
7✔
689
{
690
    return m_flashOverrides;
7✔
691
}
692

693
void VCButton::setFlashOverride(bool shouldOverride)
1✔
694
{
695
    m_flashOverrides = shouldOverride;
1✔
696
}
1✔
697

698
bool VCButton::flashForceLTP() const
7✔
699
{
700
    return m_flashForceLTP;
7✔
701
}
702

703
void VCButton::setFlashForceLTP(bool forceLTP)
1✔
704
{
705
    m_flashForceLTP = forceLTP;
1✔
706
}
1✔
707

708

709

710
/*****************************************************************************
711
 * Button press / release handlers
712
 *****************************************************************************/
713

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

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

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

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

751
                Chaser *chaser = qobject_cast<Chaser*>(f);
×
752
                chaser->setAction(action);
×
753
            }
754

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

783
FunctionParent VCButton::functionParent() const
6✔
784
{
785
    return FunctionParent(FunctionParent::ManualVCWidget, id());
6✔
786
}
787

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

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

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

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

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

832
    if (fid != m_function)
11✔
833
        return;
1✔
834

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

842
    setState(state ? Active : Inactive);
9✔
843
}
844

845
void VCButton::blink(int ms)
3✔
846
{
847
    slotBlink();
3✔
848
    QTimer::singleShot(ms, this, SLOT(slotBlink()));
3✔
849
}
3✔
850

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

862
void VCButton::slotBlackoutChanged(bool state)
2✔
863
{
864
    setState(state ? Active : Inactive);
2✔
865
}
2✔
866

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

879
/*****************************************************************************
880
 * Custom menu
881
 *****************************************************************************/
882

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

890
    return menu;
2✔
891
}
892

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

902
    VCWidget::adjustIntensity(val);
×
903
}
×
904

905
/*****************************************************************************
906
 * Load & Save
907
 *****************************************************************************/
908

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

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

923
    /* Widget commons */
924
    loadXMLCommon(root);
3✔
925

926
    /* Icon */
927
    setIconPath(m_doc->denormalizeComponentPath(root.attributes().value(KXMLQLCVCButtonIcon).toString()));
6✔
928

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

959
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashOverride))
2✔
960
                setFlashOverride(attrs.value(KXMLQLCVCButtonFlashOverride).toInt());
×
961

962
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashForceLTP))
2✔
963
                setFlashForceLTP(attrs.value(KXMLQLCVCButtonFlashForceLTP).toInt());
×
964
        }
2✔
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)
4✔
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);
2✔
998

999
    saveXMLCommon(doc);
1✔
1000

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

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

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

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

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

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

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

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

1041
    /* External input */
1042
    saveXMLInput(doc);
1✔
1043

1044
    /* End the <Button> tag */
1045
    doc->writeEndElement();
1✔
1046

1047
    return true;
1✔
1048
}
1049

1050
/*****************************************************************************
1051
 * Event handlers
1052
 *****************************************************************************/
1053

1054
void VCButton::paintEvent(QPaintEvent* e)
8✔
1055
{
1056
    QStyleOptionButton option;
8✔
1057
    option.initFrom(this);
8✔
1058

1059
    /* This should look like a normal button */
1060
    option.features = QStyleOptionButton::None;
8✔
1061

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

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

1072
    /* Icon */
1073
    option.icon = m_icon;
8✔
1074
    option.iconSize = m_iconSize;
8✔
1075

1076
    /* Paint the button */
1077
    QPainter painter(this);
8✔
1078
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1079

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

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

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

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

1122
    if (m_ledStyle == true)
8✔
1123
    {
1124
        painter.setPen(QPen(QColor(160, 160, 160, 255), 2));
×
1125

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

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

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

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

1176
    /* Stop painting here */
1177
    painter.end();
8✔
1178

1179
    /* Draw a selection frame if appropriate */
1180
    VCWidget::paintEvent(e);
8✔
1181
}
8✔
1182

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

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

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

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

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