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

mcallegari / qlcplus / 6683238402

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

push

github

mcallegari
engine: fix build

15385 of 54809 relevant lines covered (28.07%)

20267.63 hits per line

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

81.25
/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()
72
    , m_blackoutFadeOutTime(0)
73
    , m_startupIntensityEnabled(false)
74
    , m_startupIntensity(1.0)
25✔
75
{
76
    /* Set the class name "VCButton" as the object name as well */
77
    setObjectName(VCButton::staticMetaObject.className());
25✔
78

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

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

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

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

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

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

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

116
    setStyle(AppUtil::saneStyle());
25✔
117

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

123
VCButton::~VCButton()
29✔
124
{
125
}
29✔
126

127
void VCButton::setID(quint32 id)
1✔
128
{
129
    VCWidget::setID(id);
1✔
130

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

135
/*****************************************************************************
136
 * Clipboard
137
 *****************************************************************************/
138

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

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

150
    return button;
2✔
151
}
152

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

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

169
    /* Copy common stuff */
170
    return VCWidget::copyFrom(widget);
2✔
171
}
172

173
/*****************************************************************************
174
 * Properties
175
 *****************************************************************************/
176

177
void VCButton::editProperties()
×
178
{
179
    VCButtonProperties prop(this, m_doc);
×
180
    if (prop.exec() == QDialog::Accepted)
×
181
        m_doc->setModified();
×
182
}
×
183

184
/*****************************************************************************
185
 * Background color
186
 *****************************************************************************/
187

188
void VCButton::setBackgroundImage(const QString& path)
3✔
189
{
190
    m_bgPixmap = QPixmap(path);
3✔
191
    m_backgroundImage = path;
3✔
192
    m_doc->setModified();
3✔
193
    update();
3✔
194
}
3✔
195

196
void VCButton::setBackgroundColor(const QColor& color)
5✔
197
{
198
    QPalette pal = palette();
10✔
199

200
    m_hasCustomBackgroundColor = true;
5✔
201
    m_backgroundImage = QString();
5✔
202
    pal.setColor(QPalette::Button, color);
5✔
203
    setPalette(pal);
5✔
204

205
    m_doc->setModified();
5✔
206
}
5✔
207

208
void VCButton::resetBackgroundColor()
2✔
209
{
210
    QColor fg;
2✔
211

212
    m_hasCustomBackgroundColor = false;
2✔
213
    m_backgroundImage = QString();
2✔
214

215
    /* Store foreground color */
216
    if (m_hasCustomForegroundColor == true)
2✔
217
        fg = palette().color(QPalette::ButtonText);
2✔
218

219
    /* Reset the whole palette to application palette */
220
    setPalette(QApplication::palette());
2✔
221

222
    /* Restore foreground color */
223
    if (fg.isValid() == true)
2✔
224
    {
225
        QPalette pal = palette();
4✔
226
        pal.setColor(QPalette::ButtonText, fg);
2✔
227
        setPalette(pal);
2✔
228
    }
229

230
    m_doc->setModified();
2✔
231
}
2✔
232

233
QColor VCButton::backgroundColor() const
6✔
234
{
235
    return palette().color(QPalette::Button);
6✔
236
}
237

238
/*****************************************************************************
239
 * Foreground color
240
 *****************************************************************************/
241

242
void VCButton::setForegroundColor(const QColor& color)
3✔
243
{
244
    QPalette pal = palette();
6✔
245

246
    m_hasCustomForegroundColor = true;
3✔
247

248
    pal.setColor(QPalette::WindowText, color);
3✔
249
    pal.setColor(QPalette::ButtonText, color);
3✔
250
    setPalette(pal);
3✔
251

252
    m_doc->setModified();
3✔
253
}
3✔
254

255
void VCButton::resetForegroundColor()
2✔
256
{
257
    QColor bg;
2✔
258

259
    m_hasCustomForegroundColor = false;
2✔
260

261
    /* Store background color */
262
    if (m_hasCustomBackgroundColor == true)
2✔
263
        bg = palette().color(QPalette::Button);
2✔
264

265
    /* Reset the whole palette to application palette */
266
    setPalette(QApplication::palette());
2✔
267

268
    /* Restore background color */
269
    if (bg.isValid() == true)
2✔
270
        setBackgroundColor(bg);
2✔
271

272
    m_doc->setModified();
2✔
273
}
2✔
274

275
QColor VCButton::foregroundColor() const
6✔
276
{
277
    return palette().color(QPalette::ButtonText);
6✔
278
}
279

280
/*****************************************************************************
281
 * Button icon
282
 *****************************************************************************/
283

284
QString VCButton::iconPath() const
72✔
285
{
286
    return m_iconPath;
72✔
287
}
288

289
void VCButton::setIconPath(const QString& iconPath)
10✔
290
{
291
    m_iconPath = iconPath;
10✔
292

293
    updateIcon();
10✔
294
    m_doc->setModified();
10✔
295
    update();
10✔
296
}
10✔
297

298
void VCButton::slotChooseIcon()
×
299
{
300
    /* No point coming here if there is no VC */
301
    VirtualConsole *vc = VirtualConsole::instance();
×
302
    if (vc == NULL)
×
303
        return;
×
304

305
    QString formats;
×
306
    QListIterator <QByteArray> it(QImageReader::supportedImageFormats());
×
307
    while (it.hasNext() == true)
×
308
        formats += QString("*.%1 ").arg(QString(it.next()).toLower());
×
309

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

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

348
void VCButton::slotResetIcon()
1✔
349
{
350
    setIconPath(QString());
1✔
351
    update();
1✔
352
}
1✔
353

354
/*****************************************************************************
355
 * Function attachment
356
 *****************************************************************************/
357

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

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

383
        m_function = fid;
13✔
384
        setToolTip(function->name());
13✔
385
    }
386
    else
387
    {
388
        /* No function attachment */
389
        m_function = Function::invalidId();
4✔
390
        setToolTip(QString());
4✔
391
    }
392
}
17✔
393

394
quint32 VCButton::function() const
14✔
395
{
396
    return m_function;
14✔
397
}
398

399
void VCButton::adjustFunctionIntensity(Function *f, qreal value)
7✔
400
{
401
    qreal finalValue = isStartupIntensityEnabled() ? startupIntensity() * value : value;
7✔
402

403
    VCWidget::adjustFunctionIntensity(f, finalValue);
7✔
404
}
7✔
405

406
void VCButton::notifyFunctionStarting(quint32 fid, qreal intensity)
×
407
{
408
    Q_UNUSED(intensity);
409

410
    if (mode() == Doc::Design)
×
411
        return;
×
412

413
    if (fid == m_function)
×
414
        return;
×
415

416
    if (m_function != Function::invalidId() && action() == VCButton::Toggle)
×
417
    {
418
        Function *f = m_doc->function(m_function);
×
419
        if (f != NULL)
×
420
        {
421
            f->stop(functionParent());
×
422
            resetIntensityOverrideAttribute();
×
423
        }
424
    }
425
}
426

427
void VCButton::slotFunctionRemoved(quint32 fid)
2✔
428
{
429
    /* Invalidate the button's function if it's the one that was removed */
430
    if (fid == m_function)
2✔
431
    {
432
        setFunction(Function::invalidId());
1✔
433
        resetIntensityOverrideAttribute();
1✔
434
    }
435
}
2✔
436

437
/*****************************************************************************
438
 * Button state
439
 *****************************************************************************/
440

441
VCButton::ButtonState VCButton::state() const
72✔
442
{
443
    return m_state;
72✔
444
}
445

446
void VCButton::setState(ButtonState state)
59✔
447
{
448
    if (state == m_state)
59✔
449
        return;
16✔
450

451
    m_state = state;
43✔
452

453
    emit stateChanged(m_state);
43✔
454

455
    updateFeedback();
43✔
456

457
    update();
43✔
458
}
459

460
void VCButton::updateState()
×
461
{
462
    ButtonState state = Inactive;
×
463

464
    if (m_action == Blackout)
×
465
    {
466
        if (m_doc->inputOutputMap()->blackout())
×
467
            state = Active;
×
468
    }
469
    else if (m_action == Toggle)
×
470
    {
471
        Function* function = m_doc->function(m_function);
×
472
        if (function != NULL && function->isRunning())
×
473
            state = Active;
×
474
    }
475

476
    if (m_state != state)
×
477
        setState(state);
×
478
}
×
479

480
/*****************************************************************************
481
 * Key sequence handler
482
 *****************************************************************************/
483

484
void VCButton::setKeySequence(const QKeySequence& keySequence)
12✔
485
{
486
    m_keySequence = QKeySequence(keySequence);
12✔
487
}
12✔
488

489
QKeySequence VCButton::keySequence() const
9✔
490
{
491
    return m_keySequence;
9✔
492
}
493

494
void VCButton::slotKeyPressed(const QKeySequence& keySequence)
5✔
495
{
496
    if (acceptsInput() == false)
5✔
497
        return;
×
498

499
    if (m_keySequence == keySequence)
5✔
500
        pressFunction();
5✔
501
}
502

503
void VCButton::slotKeyReleased(const QKeySequence& keySequence)
3✔
504
{
505
    if (acceptsInput() == false)
3✔
506
        return;
×
507

508
    if (m_keySequence == keySequence)
3✔
509
        releaseFunction();
3✔
510
}
511

512
void VCButton::updateFeedback()
46✔
513
{
514
    if (m_state == Monitoring)
46✔
515
        return;
×
516

517
    QSharedPointer<QLCInputSource> src = inputSource();
92✔
518
    if (!src.isNull() && src->isValid() == true)
46✔
519
    {
520
        if (m_state == Inactive)
11✔
521
            sendFeedback(src->lowerValue());
5✔
522
        else
523
            sendFeedback(src->upperValue());
6✔
524
    }
525
}
526

527
/*****************************************************************************
528
 * External input
529
 *****************************************************************************/
530

531
void VCButton::slotInputValueChanged(quint32 universe, quint32 channel, uchar value)
15✔
532
{
533
    /* Don't let input data through in design mode or if disabled */
534
    if (acceptsInput() == false)
15✔
535
        return;
1✔
536

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

565
/*****************************************************************************
566
 * Button action
567
 *****************************************************************************/
568

569
void VCButton::setAction(Action action)
42✔
570
{
571
    // Blackout signal connection
572
    if (m_action == Blackout && action != Blackout)
42✔
573
        disconnect(m_doc->inputOutputMap(), SIGNAL(blackoutChanged(bool)),
1✔
574
                this, SLOT(slotBlackoutChanged(bool)));
575
    else if (m_action != Blackout && action == Blackout)
41✔
576
        connect(m_doc->inputOutputMap(), SIGNAL(blackoutChanged(bool)),
1✔
577
                this, SLOT(slotBlackoutChanged(bool)));
578

579
    // Action update
580
    m_action = action;
42✔
581
    updateIcon();
42✔
582

583
    // Update tooltip
584
    if (m_action == Blackout)
42✔
585
        setToolTip(tr("Toggle Blackout"));
1✔
586
    else if (m_action == StopAll)
41✔
587
        setToolTip(tr("Stop ALL functions!"));
1✔
588
}
42✔
589

590
VCButton::Action VCButton::action() const
10✔
591
{
592
    return m_action;
10✔
593
}
594

595
QString VCButton::actionToString(VCButton::Action action)
4✔
596
{
597
    if (action == Flash)
4✔
598
        return QString(KXMLQLCVCButtonActionFlash);
2✔
599
    else if (action == Blackout)
2✔
600
        return QString(KXMLQLCVCButtonActionBlackout);
×
601
    else if (action == StopAll)
2✔
602
        return QString(KXMLQLCVCButtonActionStopAll);
×
603
    else
604
        return QString(KXMLQLCVCButtonActionToggle);
2✔
605
}
606

607
VCButton::Action VCButton::stringToAction(const QString& str)
5✔
608
{
609
    if (str == KXMLQLCVCButtonActionFlash)
5✔
610
        return Flash;
3✔
611
    else if (str == KXMLQLCVCButtonActionBlackout)
2✔
612
        return Blackout;
×
613
    else if (str == KXMLQLCVCButtonActionStopAll)
2✔
614
        return StopAll;
×
615
    else
616
        return Toggle;
2✔
617
}
618

619
void VCButton::setStopAllFadeOutTime(int ms)
2✔
620
{
621
    m_blackoutFadeOutTime = ms;
2✔
622
}
2✔
623

624
int VCButton::stopAllFadeTime() const
3✔
625
{
626
    return m_blackoutFadeOutTime;
3✔
627
}
628

629
/*****************************************************************************
630
 * Intensity adjustment
631
 *****************************************************************************/
632

633
void VCButton::enableStartupIntensity(bool enable)
11✔
634
{
635
    m_startupIntensityEnabled = enable;
11✔
636
}
11✔
637

638
bool VCButton::isStartupIntensityEnabled() const
16✔
639
{
640
    return m_startupIntensityEnabled;
16✔
641
}
642

643
void VCButton::setStartupIntensity(qreal fraction)
179✔
644
{
645
    m_startupIntensity = CLAMP(fraction, qreal(0), qreal(1));
179✔
646
}
179✔
647

648
qreal VCButton::startupIntensity() const
183✔
649
{
650
    return m_startupIntensity;
183✔
651
}
652

653
void VCButton::slotAttributeChanged(int value)
×
654
{
655
#if 0
656
    ClickAndGoSlider *slider = (ClickAndGoSlider *)sender();
657
    int idx = slider->property("attrIdx").toInt();
658

659
    Function* func = m_doc->function(m_function);
660
    if (func != NULL)
661
        func->adjustAttribute((qreal)value / 100, idx);
662
#else
663
    Q_UNUSED(value)
664
#endif
665
}
×
666

667
/*****************************************************************************
668
 * Button press / release handlers
669
 *****************************************************************************/
670

671
void VCButton::pressFunction()
13✔
672
{
673
    /* Don't allow pressing during design mode */
674
    if (mode() == Doc::Design)
13✔
675
        return;
×
676

677
    Function* f = NULL;
13✔
678
    if (m_action == Toggle)
13✔
679
    {
680
        f = m_doc->function(m_function);
6✔
681
        if (f == NULL)
6✔
682
            return;
×
683

684
        // if the button is in a SoloFrame and the function is running but was
685
        // started by a different function (a chaser or collection), turn other
686
        // functions off and start this one.
687
        if (state() == Active && !(isChildOfSoloFrame() && f->startedAsChild()))
6✔
688
        {
689
            f->stop(functionParent());
3✔
690
            resetIntensityOverrideAttribute();
3✔
691
        }
692
        else
693
        {
694
            adjustFunctionIntensity(f, intensity());
3✔
695

696
            // starting a Chaser is a special case, since it is necessary
697
            // to use Chaser Actions to properly start the first
698
            // Chaser step with the right intensity
699
            if (f->type() == Function::ChaserType || f->type() == Function::SequenceType)
3✔
700
            {
701
                ChaserAction action;
702
                action.m_action = ChaserSetStepIndex;
×
703
                action.m_stepIndex = 0;
×
704
                action.m_masterIntensity = intensity();
×
705
                action.m_stepIntensity = 1.0;
×
706
                action.m_fadeMode = Chaser::FromFunction;
×
707

708
                Chaser *chaser = qobject_cast<Chaser*>(f);
×
709
                chaser->setAction(action);
×
710
            }
711

712
            f->start(m_doc->masterTimer(), functionParent());
3✔
713
            setState(Active);
3✔
714
            emit functionStarting(m_function);
3✔
715
        }
716
    }
717
    else if (m_action == Flash && state() == Inactive)
7✔
718
    {
719
        f = m_doc->function(m_function);
4✔
720
        if (f != NULL)
4✔
721
        {
722
            adjustFunctionIntensity(f, intensity());
4✔
723
            f->flash(m_doc->masterTimer());
4✔
724
            setState(Active);
4✔
725
        }
726
    }
727
    else if (m_action == Blackout)
3✔
728
    {
729
        m_doc->inputOutputMap()->toggleBlackout();
2✔
730
    }
731
    else if (m_action == StopAll)
1✔
732
    {
733
        if (stopAllFadeTime() == 0)
1✔
734
            m_doc->masterTimer()->stopAllFunctions();
1✔
735
        else
736
            m_doc->masterTimer()->fadeAndStopAll(stopAllFadeTime());
×
737
    }
738
}
739

740
FunctionParent VCButton::functionParent() const
6✔
741
{
742
    return FunctionParent(FunctionParent::ManualVCWidget, id());
6✔
743
}
744

745
void VCButton::releaseFunction()
6✔
746
{
747
    /* Don't allow operation during design mode */
748
    if (mode() == Doc::Design)
6✔
749
        return;
×
750

751
    if (m_action == Flash && state() == Active)
6✔
752
    {
753
        Function* f = m_doc->function(m_function);
4✔
754
        if (f != NULL)
4✔
755
        {
756
            f->unFlash(m_doc->masterTimer());
4✔
757
            resetIntensityOverrideAttribute();
4✔
758
            setState(Inactive);
4✔
759
        }
760
    }
761
}
762

763
void VCButton::slotFunctionRunning(quint32 fid)
5✔
764
{
765
    if (fid == m_function && m_action == Toggle)
5✔
766
    {
767
        if (state() == Inactive)
3✔
768
            setState(Monitoring);
×
769
        emit functionStarting(m_function);
3✔
770
    }
771
}
5✔
772

773
void VCButton::slotFunctionStopped(quint32 fid)
6✔
774
{
775
    if (fid == m_function && m_action == Toggle)
6✔
776
    {
777
        resetIntensityOverrideAttribute();
3✔
778
        setState(Inactive);
3✔
779
        blink(250);
3✔
780
    }
781
}
6✔
782

783
void VCButton::slotFunctionFlashing(quint32 fid, bool state)
11✔
784
{
785
    // Do not change the state of the button for Blackout or Stop All Functions buttons
786
    if (m_action != Toggle && m_action != Flash)
11✔
787
        return;
×
788

789
    if (fid != m_function)
11✔
790
        return;
1✔
791

792
    // if the function was flashed by another button, and the function is still running, keep the button pushed
793
    Function* f = m_doc->function(m_function);
10✔
794
    if (state == false && m_action == Toggle && f != NULL && f->isRunning())
10✔
795
    {
796
        return;
1✔
797
    }
798

799
    setState(state ? Active : Inactive);
9✔
800
}
801

802
void VCButton::blink(int ms)
3✔
803
{
804
    slotBlink();
3✔
805
    QTimer::singleShot(ms, this, SLOT(slotBlink()));
3✔
806
}
3✔
807

808
void VCButton::slotBlink()
4✔
809
{
810
    // This function is called twice with same XOR mask,
811
    // thus creating a brief opposite-color -- normal-color blink
812
    QPalette pal = palette();
8✔
813
    QColor color(pal.color(QPalette::Button));
4✔
814
    color.setRgb(color.red()^0xff, color.green()^0xff, color.blue()^0xff);
4✔
815
    pal.setColor(QPalette::Button, color);
4✔
816
    setPalette(pal);
4✔
817
}
4✔
818

819
void VCButton::slotBlackoutChanged(bool state)
2✔
820
{
821
    setState(state ? Active : Inactive);
2✔
822
}
2✔
823

824
bool VCButton::isChildOfSoloFrame() const
3✔
825
{
826
    QWidget* parent = parentWidget();
3✔
827
    while (parent != NULL)
6✔
828
    {
829
        if (qobject_cast<VCSoloFrame*>(parent) != NULL)
3✔
830
            return true;
×
831
        parent = parent->parentWidget();
3✔
832
    }
833
    return false;
3✔
834
}
835

836
/*****************************************************************************
837
 * Custom menu
838
 *****************************************************************************/
839

840
QMenu* VCButton::customMenu(QMenu* parentMenu)
2✔
841
{
842
    QMenu* menu = new QMenu(parentMenu);
2✔
843
    menu->setTitle(tr("Icon"));
2✔
844
    menu->addAction(m_chooseIconAction);
2✔
845
    menu->addAction(m_resetIconAction);
2✔
846

847
    return menu;
2✔
848
}
849

850
void VCButton::adjustIntensity(qreal val)
×
851
{
852
    if (state() == Active)
×
853
    {
854
        Function* func = m_doc->function(m_function);
×
855
        if (func != NULL)
×
856
            adjustFunctionIntensity(func, val);
×
857
    }
858

859
    VCWidget::adjustIntensity(val);
×
860
}
×
861

862
/*****************************************************************************
863
 * Load & Save
864
 *****************************************************************************/
865

866
bool VCButton::loadXML(QXmlStreamReader &root)
4✔
867
{
868
    bool visible = false;
4✔
869
    int x = 0;
4✔
870
    int y = 0;
4✔
871
    int w = 0;
4✔
872
    int h = 0;
4✔
873

874
    if (root.name() != KXMLQLCVCButton)
4✔
875
    {
876
        qWarning() << Q_FUNC_INFO << "Button node not found";
1✔
877
        return false;
1✔
878
    }
879

880
    /* Widget commons */
881
    loadXMLCommon(root);
3✔
882

883
    /* Icon */
884
    setIconPath(m_doc->denormalizeComponentPath(root.attributes().value(KXMLQLCVCButtonIcon).toString()));
3✔
885

886
    /* Children */
887
    while (root.readNextStartElement())
19✔
888
    {
889
        //qDebug() << "VC Button tag:" << root.name();
890
        if (root.name() == KXMLQLCWindowState)
16✔
891
        {
892
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
2✔
893
            setGeometry(x, y, w, h);
2✔
894
        }
895
        else if (root.name() == KXMLQLCVCWidgetAppearance)
14✔
896
        {
897
            loadXMLAppearance(root);
2✔
898
        }
899
        else if (root.name() == KXMLQLCVCButtonFunction)
12✔
900
        {
901
            QString str = root.attributes().value(KXMLQLCVCButtonFunctionID).toString();
6✔
902
            setFunction(str.toUInt());
2✔
903
            root.skipCurrentElement();
2✔
904
        }
905
        else if (root.name() == KXMLQLCVCWidgetInput)
10✔
906
        {
907
            loadXMLInput(root);
2✔
908
        }
909
        else if (root.name() == KXMLQLCVCButtonAction)
8✔
910
        {
911
            QXmlStreamAttributes attrs = root.attributes();
4✔
912
            setAction(stringToAction(root.readElementText()));
2✔
913
            if (attrs.hasAttribute(KXMLQLCVCButtonStopAllFadeTime))
2✔
914
                setStopAllFadeOutTime(attrs.value(KXMLQLCVCButtonStopAllFadeTime).toString().toInt());
×
915
        }
916
        else if (root.name() == KXMLQLCVCButtonKey)
6✔
917
        {
918
            setKeySequence(stripKeySequence(QKeySequence(root.readElementText())));
2✔
919
        }
920
        else if (root.name() == KXMLQLCVCButtonIntensity)
4✔
921
        {
922
            bool adjust;
923
            if (root.attributes().value(KXMLQLCVCButtonIntensityAdjust).toString() == KXMLQLCTrue)
2✔
924
                adjust = true;
1✔
925
            else
926
                adjust = false;
1✔
927
            setStartupIntensity(qreal(root.readElementText().toInt()) / qreal(100));
2✔
928
            enableStartupIntensity(adjust);
2✔
929
        }
930
        else
931
        {
932
            qWarning() << Q_FUNC_INFO << "Unknown button tag:" << root.name().toString();
2✔
933
            root.skipCurrentElement();
2✔
934
        }
935
    }
936

937
    /* All buttons start raised... */
938
    setState(Inactive);
3✔
939

940
    return true;
3✔
941
}
942

943
bool VCButton::saveXML(QXmlStreamWriter *doc)
1✔
944
{
945
    Q_ASSERT(doc != NULL);
1✔
946

947
    /* VC button entry */
948
    doc->writeStartElement(KXMLQLCVCButton);
1✔
949

950
    saveXMLCommon(doc);
1✔
951

952
    /* Icon */
953
    doc->writeAttribute(KXMLQLCVCButtonIcon, m_doc->normalizeComponentPath(iconPath()));
1✔
954

955
    /* Window state */
956
    saveXMLWindowState(doc);
1✔
957

958
    /* Appearance */
959
    saveXMLAppearance(doc);
1✔
960

961
    /* Function */
962
    doc->writeStartElement(KXMLQLCVCButtonFunction);
1✔
963
    doc->writeAttribute(KXMLQLCVCButtonFunctionID, QString::number(function()));
1✔
964
    doc->writeEndElement();
1✔
965

966
    /* Action */
967
    doc->writeStartElement(KXMLQLCVCButtonAction);
1✔
968

969
    if (action() == StopAll && stopAllFadeTime() != 0)
1✔
970
    {
971
        doc->writeAttribute(KXMLQLCVCButtonStopAllFadeTime, QString::number(stopAllFadeTime()));
×
972
    }
973
    doc->writeCharacters(actionToString(action()));
1✔
974
    doc->writeEndElement();
1✔
975

976
    /* Key sequence */
977
    if (m_keySequence.isEmpty() == false)
1✔
978
        doc->writeTextElement(KXMLQLCVCButtonKey, m_keySequence.toString());
1✔
979

980
    /* Intensity adjustment */
981
    doc->writeStartElement(KXMLQLCVCButtonIntensity);
1✔
982
    doc->writeAttribute(KXMLQLCVCButtonIntensityAdjust,
1✔
983
                     isStartupIntensityEnabled() ? KXMLQLCTrue : KXMLQLCFalse);
1✔
984
    doc->writeCharacters(QString::number(int(startupIntensity() * 100)));
1✔
985
    doc->writeEndElement();
1✔
986

987
    /* External input */
988
    saveXMLInput(doc);
1✔
989

990
    /* End the <Button> tag */
991
    doc->writeEndElement();
1✔
992

993
    return true;
1✔
994
}
995

996
/*****************************************************************************
997
 * Event handlers
998
 *****************************************************************************/
999

1000
void VCButton::paintEvent(QPaintEvent* e)
8✔
1001
{
1002
    QStyleOptionButton option;
16✔
1003
    option.initFrom(this);
8✔
1004

1005
    /* This should look like a normal button */
1006
    option.features = QStyleOptionButton::None;
8✔
1007

1008
    /* Sunken or raised based on state() status */
1009
    if (state() == Inactive)
8✔
1010
        option.state = QStyle::State_Raised;
7✔
1011
    else
1012
        option.state = QStyle::State_Sunken;
1✔
1013

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

1018
    /* Icon */
1019
    option.icon = m_icon;
8✔
1020
    option.iconSize = m_iconSize;
8✔
1021

1022
    /* Paint the button */
1023
    QPainter painter(this);
16✔
1024
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1025

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

1028
    if (m_backgroundImage.isEmpty() == false)
8✔
1029
    {
1030
        QRect pxRect = m_bgPixmap.rect();
×
1031
        // if the pixmap is bigger than the button, then paint a scaled version of it
1032
        // covering the whole button surface
1033
        // if the pixmap is smaller than the button, draw a centered pixmap
1034
        if (pxRect.contains(rect()))
×
1035
        {
1036
            if (m_ledStyle == true)
×
1037
                painter.drawPixmap(rect(), m_bgPixmap);
×
1038
            else
1039
                painter.drawPixmap(3, 3, width() - 6, height() - 6, m_bgPixmap);
×
1040
        }
1041
        else
1042
        {
1043
            painter.drawPixmap((width() - pxRect.width()) / 2,
×
1044
                               (height() - pxRect.height()) / 2,
×
1045
                               m_bgPixmap);
×
1046
        }
1047
    }
1048

1049
    /* Paint caption with text wrapping */
1050
    if (caption().isEmpty() == false)
8✔
1051
    {
1052
        style()->drawItemText(&painter,
9✔
1053
                              rect(),
3✔
1054
                              Qt::AlignCenter | Qt::TextWordWrap,
3✔
1055
                              palette(),
1056
                              (mode() == Doc::Operate),
3✔
1057
                              caption());
6✔
1058
    }
1059

1060
    /* Flash emblem */
1061
    if (m_action == Flash)
8✔
1062
    {
1063
        QIcon icon(":/flash.png");
2✔
1064
        painter.drawPixmap(rect().width() - 18, 2,
2✔
1065
                           icon.pixmap(QSize(16, 16), QIcon::Normal, QIcon::On));
4✔
1066
    }
1067

1068
    if (m_ledStyle == true)
8✔
1069
    {
1070
        painter.setPen(QPen(QColor(160, 160, 160, 255), 2));
×
1071

1072
        if (state() == Active)
×
1073
            painter.setBrush(QBrush(QColor(0, 230, 0, 255)));
×
1074
        else if (state() == Monitoring)
×
1075
            painter.setBrush(QBrush(QColor(255, 170, 0, 255)));
×
1076
        else
1077
            painter.setBrush(QBrush(QColor(110, 110, 110, 255)));
×
1078

1079
        int dim = rect().width() / 6;
×
1080
        if (dim > 14) dim = 14;
×
1081

1082
        painter.drawEllipse(6, 6, dim, dim);      // Style #1
×
1083
        //painter.drawRoundedRect(-1, -1, dim, dim, 3, 3);   // Style #2
1084
    }
1085
    else
1086
    {
1087
        // Style #3
1088
        painter.setBrush(Qt::NoBrush);
8✔
1089

1090
        if (state() != Inactive)
8✔
1091
        {
1092
            int borderWidth = (rect().width() > 80)?3:2;
1✔
1093
            painter.setPen(QPen(QColor(20, 20, 20, 255), borderWidth * 2));
1✔
1094
            painter.drawRoundedRect(borderWidth, borderWidth,
3✔
1095
                                    rect().width() - borderWidth * 2, rect().height() - (borderWidth * 2),
1✔
1096
                                    borderWidth + 1,  borderWidth + 1);
1✔
1097
            if (state() == Monitoring)
1✔
1098
                painter.setPen(QPen(QColor(255, 170, 0, 255), borderWidth));
×
1099
            else
1100
                painter.setPen(QPen(QColor(0, 230, 0, 255), borderWidth));
1✔
1101
            painter.drawRoundedRect(borderWidth, borderWidth,
3✔
1102
                                    rect().width() - borderWidth * 2, rect().height() - (borderWidth * 2),
1✔
1103
                                    borderWidth, borderWidth);
1104
        }
1105
        else
1106
        {
1107
            painter.setPen(QPen(QColor(160, 160, 160, 255), 3));
7✔
1108
            painter.drawRoundedRect(1, 1, rect().width() - 2, rect().height() - 2, 3, 3);
7✔
1109
        }
1110
    }
1111

1112
    /* Stop painting here */
1113
    painter.end();
8✔
1114

1115
    /* Draw a selection frame if appropriate */
1116
    VCWidget::paintEvent(e);
8✔
1117
}
8✔
1118

1119
void VCButton::mousePressEvent(QMouseEvent* e)
2✔
1120
{
1121
    if (mode() == Doc::Design)
2✔
1122
        VCWidget::mousePressEvent(e);
1✔
1123
    else if (e->button() == Qt::LeftButton)
1✔
1124
        pressFunction();
1✔
1125
#if 0
1126
    else if (e->button() == Qt::RightButton)
1127
    {
1128
        Function* func = m_doc->function(m_function);
1129
        if (func != NULL)
1130
        {
1131
            QString menuStyle = "QMenu { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #B9D9E8, stop:1 #A4C0CE);"
1132
                            "border: 1px solid black; border-radius: 4px; font:bold; }";
1133
            QMenu *menu = new QMenu();
1134
            menu->setStyleSheet(menuStyle);
1135
            int idx = 0;
1136
            foreach(Attribute attr, func->attributes())
1137
            {
1138
                QString slStyle = "QSlider::groove:horizontal { border: 1px solid #999999; margin: 0; border-radius: 2px;"
1139
                        "height: 15px; background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4); }"
1140

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

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

1147
                QWidget *entryWidget = new QWidget();
1148
                QHBoxLayout *hbox = new QHBoxLayout(menu);
1149
                hbox->setMargin(3);
1150
                QLabel *label = new QLabel(attr.m_name);
1151
                label->setAlignment(Qt::AlignLeft);
1152
                label->setFixedWidth(100);
1153
                ClickAndGoSlider *slider = new ClickAndGoSlider(menu);
1154
                slider->setOrientation(Qt::Horizontal);
1155
                slider->setSliderStyleSheet(slStyle);
1156
                slider->setFixedSize(QSize(100, 18));
1157
                slider->setMinimum(0);
1158
                slider->setMaximum(100);
1159
                slider->setValue(attr.m_value * 100);
1160
                slider->setProperty("attrIdx", QVariant(idx));
1161
                connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotAttributeChanged(int)));
1162
                hbox->addWidget(label);
1163
                hbox->addWidget(slider);
1164
                entryWidget->setLayout(hbox);
1165
                QWidgetAction *sliderBoxAction = new QWidgetAction(menu);
1166
                sliderBoxAction->setDefaultWidget(entryWidget);
1167
                menu->addAction(sliderBoxAction);
1168
                idx++;
1169
            }
1170
            menu->exec(QCursor::pos());
1171
        }
1172
    }
1173
#endif
1174
}
2✔
1175

1176
void VCButton::mouseReleaseEvent(QMouseEvent* e)
2✔
1177
{
1178
    if (mode() == Doc::Design)
2✔
1179
        VCWidget::mouseReleaseEvent(e);
1✔
1180
    else
1181
        releaseFunction();
1✔
1182
}
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