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

mcallegari / qlcplus / 6804335593

08 Nov 2023 09:41PM UTC coverage: 28.09% (+0.02%) from 28.067%
6804335593

Pull #1462

github

web-flow
Merge 215da7e17 into 63469273b
Pull Request #1462: Overriding flash functionality

27 of 36 new or added lines in 5 files covered. (75.0%)

2 existing lines in 1 file now uncovered.

15405 of 54842 relevant lines covered (28.09%)

20306.6 hits per line

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

81.47
/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)
75
    , m_flashOverrides(false)
76
    , m_flashForceLTP(false)
25✔
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;
50✔
106
    QVariant var = settings.value(SETTINGS_BUTTON_SIZE);
50✔
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
    /* Copy common stuff */
172
    return VCWidget::copyFrom(widget);
2✔
173
}
174

175
/*****************************************************************************
176
 * Properties
177
 *****************************************************************************/
178

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

186
/*****************************************************************************
187
 * Background color
188
 *****************************************************************************/
189

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

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

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

207
    m_doc->setModified();
5✔
208
}
5✔
209

210
void VCButton::resetBackgroundColor()
2✔
211
{
212
    QColor fg;
2✔
213

214
    m_hasCustomBackgroundColor = false;
2✔
215
    m_backgroundImage = QString();
2✔
216

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

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

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

232
    m_doc->setModified();
2✔
233
}
2✔
234

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

240
/*****************************************************************************
241
 * Foreground color
242
 *****************************************************************************/
243

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

248
    m_hasCustomForegroundColor = true;
3✔
249

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

254
    m_doc->setModified();
3✔
255
}
3✔
256

257
void VCButton::resetForegroundColor()
2✔
258
{
259
    QColor bg;
2✔
260

261
    m_hasCustomForegroundColor = false;
2✔
262

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

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

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

274
    m_doc->setModified();
2✔
275
}
2✔
276

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

282
/*****************************************************************************
283
 * Button icon
284
 *****************************************************************************/
285

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

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

295
    updateIcon();
10✔
296
    m_doc->setModified();
10✔
297
    update();
10✔
298
}
10✔
299

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

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

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

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

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

356
/*****************************************************************************
357
 * Function attachment
358
 *****************************************************************************/
359

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

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

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

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

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

405
    VCWidget::adjustFunctionIntensity(f, finalValue);
7✔
406
}
7✔
407

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

412
    if (mode() == Doc::Design)
×
413
        return;
×
414

415
    if (fid == m_function)
×
416
        return;
×
417

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

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

439
/*****************************************************************************
440
 * Button state
441
 *****************************************************************************/
442

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

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

453
    m_state = state;
43✔
454

455
    emit stateChanged(m_state);
43✔
456

457
    updateFeedback();
43✔
458

459
    update();
43✔
460
}
461

462
void VCButton::updateState()
×
463
{
464
    ButtonState state = Inactive;
×
465

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

478
    if (m_state != state)
×
479
        setState(state);
×
480
}
×
481

482
/*****************************************************************************
483
 * Key sequence handler
484
 *****************************************************************************/
485

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

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

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

501
    if (m_keySequence == keySequence)
5✔
502
        pressFunction();
5✔
503
}
504

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

510
    if (m_keySequence == keySequence)
3✔
511
        releaseFunction();
3✔
512
}
513

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

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

529
/*****************************************************************************
530
 * External input
531
 *****************************************************************************/
532

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

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

567
/*****************************************************************************
568
 * Button action
569
 *****************************************************************************/
570

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

581
    // Action update
582
    m_action = action;
42✔
583
    updateIcon();
42✔
584

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

592
VCButton::Action VCButton::action() const
11✔
593
{
594
    return m_action;
11✔
595
}
596

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

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

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

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

631
/*****************************************************************************
632
 * Intensity adjustment
633
 *****************************************************************************/
634

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

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

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

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

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

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

669

670

671
/*****************************************************************************
672
 * Flash Properties
673
 *****************************************************************************/
674

675
bool VCButton::flashOverrides()
5✔
676
{
677
    return m_flashOverrides;
5✔
678
}
679

680
void VCButton::setFlashOverride(bool shouldOverride)
1✔
681
{
682
    m_flashOverrides = shouldOverride;
1✔
683
}
1✔
684

685
bool VCButton::flashForceLTP()
5✔
686
{
687
    return m_flashForceLTP;
5✔
688
}
689

690
void VCButton::setFlashForceLTP(bool forceLTP)
1✔
691
{
692
    m_flashForceLTP = forceLTP;
1✔
693
}
1✔
694

695

696

697
/*****************************************************************************
698
 * Button press / release handlers
699
 *****************************************************************************/
700

701
void VCButton::pressFunction()
13✔
702
{
703
    /* Don't allow pressing during design mode */
704
    if (mode() == Doc::Design)
13✔
705
        return;
×
706

707
    Function* f = NULL;
13✔
708
    if (m_action == Toggle)
13✔
709
    {
710
        f = m_doc->function(m_function);
6✔
711
        if (f == NULL)
6✔
712
            return;
×
713

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

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

738
                Chaser *chaser = qobject_cast<Chaser*>(f);
×
739
                chaser->setAction(action);
×
740
            }
741

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

770
FunctionParent VCButton::functionParent() const
6✔
771
{
772
    return FunctionParent(FunctionParent::ManualVCWidget, id());
6✔
773
}
774

775
void VCButton::releaseFunction()
6✔
776
{
777
    /* Don't allow operation during design mode */
778
    if (mode() == Doc::Design)
6✔
779
        return;
×
780

781
    if (m_action == Flash && state() == Active)
6✔
782
    {
783
        Function* f = m_doc->function(m_function);
4✔
784
        if (f != NULL)
4✔
785
        {
786
            f->unFlash(m_doc->masterTimer());
4✔
787
            resetIntensityOverrideAttribute();
4✔
788
            setState(Inactive);
4✔
789
        }
790
    }
791
}
792

793
void VCButton::slotFunctionRunning(quint32 fid)
5✔
794
{
795
    if (fid == m_function && m_action == Toggle)
5✔
796
    {
797
        if (state() == Inactive)
3✔
798
            setState(Monitoring);
×
799
        emit functionStarting(m_function);
3✔
800
    }
801
}
5✔
802

803
void VCButton::slotFunctionStopped(quint32 fid)
6✔
804
{
805
    if (fid == m_function && m_action == Toggle)
6✔
806
    {
807
        resetIntensityOverrideAttribute();
3✔
808
        setState(Inactive);
3✔
809
        blink(250);
3✔
810
    }
811
}
6✔
812

813
void VCButton::slotFunctionFlashing(quint32 fid, bool state)
11✔
814
{
815
    // Do not change the state of the button for Blackout or Stop All Functions buttons
816
    if (m_action != Toggle && m_action != Flash)
11✔
817
        return;
×
818

819
    if (fid != m_function)
11✔
820
        return;
1✔
821

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

829
    setState(state ? Active : Inactive);
9✔
830
}
831

832
void VCButton::blink(int ms)
3✔
833
{
834
    slotBlink();
3✔
835
    QTimer::singleShot(ms, this, SLOT(slotBlink()));
3✔
836
}
3✔
837

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

849
void VCButton::slotBlackoutChanged(bool state)
2✔
850
{
851
    setState(state ? Active : Inactive);
2✔
852
}
2✔
853

854
bool VCButton::isChildOfSoloFrame() const
3✔
855
{
856
    QWidget* parent = parentWidget();
3✔
857
    while (parent != NULL)
6✔
858
    {
859
        if (qobject_cast<VCSoloFrame*>(parent) != NULL)
3✔
860
            return true;
×
861
        parent = parent->parentWidget();
3✔
862
    }
863
    return false;
3✔
864
}
865

866
/*****************************************************************************
867
 * Custom menu
868
 *****************************************************************************/
869

870
QMenu* VCButton::customMenu(QMenu* parentMenu)
2✔
871
{
872
    QMenu* menu = new QMenu(parentMenu);
2✔
873
    menu->setTitle(tr("Icon"));
2✔
874
    menu->addAction(m_chooseIconAction);
2✔
875
    menu->addAction(m_resetIconAction);
2✔
876

877
    return menu;
2✔
878
}
879

880
void VCButton::adjustIntensity(qreal val)
×
881
{
882
    if (state() == Active)
×
883
    {
884
        Function* func = m_doc->function(m_function);
×
885
        if (func != NULL)
×
886
            adjustFunctionIntensity(func, val);
×
887
    }
888

889
    VCWidget::adjustIntensity(val);
×
890
}
×
891

892
/*****************************************************************************
893
 * Load & Save
894
 *****************************************************************************/
895

896
bool VCButton::loadXML(QXmlStreamReader &root)
4✔
897
{
898
    bool visible = false;
4✔
899
    int x = 0;
4✔
900
    int y = 0;
4✔
901
    int w = 0;
4✔
902
    int h = 0;
4✔
903

904
    if (root.name() != KXMLQLCVCButton)
4✔
905
    {
906
        qWarning() << Q_FUNC_INFO << "Button node not found";
1✔
907
        return false;
1✔
908
    }
909

910
    /* Widget commons */
911
    loadXMLCommon(root);
3✔
912

913
    /* Icon */
914
    setIconPath(m_doc->denormalizeComponentPath(root.attributes().value(KXMLQLCVCButtonIcon).toString()));
3✔
915

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

946
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashOverride))
2✔
NEW
947
                setFlashOverride(attrs.value(KXMLQLCVCButtonFlashOverride).toInt());
×
948

949
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashForceLTP))
2✔
NEW
950
                setFlashForceLTP(attrs.value(KXMLQLCVCButtonFlashForceLTP).toInt());
×
951
        }
952
        else if (root.name() == KXMLQLCVCButtonKey)
6✔
953
        {
954
            setKeySequence(stripKeySequence(QKeySequence(root.readElementText())));
2✔
955
        }
956
        else if (root.name() == KXMLQLCVCButtonIntensity)
4✔
957
        {
958
            bool adjust;
959
            if (root.attributes().value(KXMLQLCVCButtonIntensityAdjust).toString() == KXMLQLCTrue)
2✔
960
                adjust = true;
1✔
961
            else
962
                adjust = false;
1✔
963
            setStartupIntensity(qreal(root.readElementText().toInt()) / qreal(100));
2✔
964
            enableStartupIntensity(adjust);
2✔
965
        }
966
        else
967
        {
968
            qWarning() << Q_FUNC_INFO << "Unknown button tag:" << root.name().toString();
2✔
969
            root.skipCurrentElement();
2✔
970
        }
971
    }
972

973
    /* All buttons start raised... */
974
    setState(Inactive);
3✔
975

976
    return true;
3✔
977
}
978

979
bool VCButton::saveXML(QXmlStreamWriter *doc)
1✔
980
{
981
    Q_ASSERT(doc != NULL);
1✔
982

983
    /* VC button entry */
984
    doc->writeStartElement(KXMLQLCVCButton);
1✔
985

986
    saveXMLCommon(doc);
1✔
987

988
    /* Icon */
989
    doc->writeAttribute(KXMLQLCVCButtonIcon, m_doc->normalizeComponentPath(iconPath()));
1✔
990

991
    /* Window state */
992
    saveXMLWindowState(doc);
1✔
993

994
    /* Appearance */
995
    saveXMLAppearance(doc);
1✔
996

997
    /* Function */
998
    doc->writeStartElement(KXMLQLCVCButtonFunction);
1✔
999
    doc->writeAttribute(KXMLQLCVCButtonFunctionID, QString::number(function()));
1✔
1000
    doc->writeEndElement();
1✔
1001

1002
    /* Action */
1003
    doc->writeStartElement(KXMLQLCVCButtonAction);
1✔
1004

1005
    if (action() == StopAll && stopAllFadeTime() != 0)
1✔
1006
    {
1007
        doc->writeAttribute(KXMLQLCVCButtonStopAllFadeTime, QString::number(stopAllFadeTime()));
×
1008
    }
1009
    else if (action() == Flash)
1✔
1010
    {
1011
        doc->writeAttribute(KXMLQLCVCButtonFlashOverride, QString::number(flashOverrides()));
1✔
1012
        doc->writeAttribute(KXMLQLCVCButtonFlashForceLTP, QString::number(flashForceLTP()));
1✔
1013
    }
1014
    doc->writeCharacters(actionToString(action()));
1✔
1015
    doc->writeEndElement();
1✔
1016

1017
    /* Key sequence */
1018
    if (m_keySequence.isEmpty() == false)
1✔
1019
        doc->writeTextElement(KXMLQLCVCButtonKey, m_keySequence.toString());
1✔
1020

1021
    /* Intensity adjustment */
1022
    doc->writeStartElement(KXMLQLCVCButtonIntensity);
1✔
1023
    doc->writeAttribute(KXMLQLCVCButtonIntensityAdjust,
1✔
1024
                     isStartupIntensityEnabled() ? KXMLQLCTrue : KXMLQLCFalse);
1✔
1025
    doc->writeCharacters(QString::number(int(startupIntensity() * 100)));
1✔
1026
    doc->writeEndElement();
1✔
1027

1028
    /* External input */
1029
    saveXMLInput(doc);
1✔
1030

1031
    /* End the <Button> tag */
1032
    doc->writeEndElement();
1✔
1033

1034
    return true;
1✔
1035
}
1036

1037
/*****************************************************************************
1038
 * Event handlers
1039
 *****************************************************************************/
1040

1041
void VCButton::paintEvent(QPaintEvent* e)
8✔
1042
{
1043
    QStyleOptionButton option;
16✔
1044
    option.initFrom(this);
8✔
1045

1046
    /* This should look like a normal button */
1047
    option.features = QStyleOptionButton::None;
8✔
1048

1049
    /* Sunken or raised based on state() status */
1050
    if (state() == Inactive)
8✔
1051
        option.state = QStyle::State_Raised;
7✔
1052
    else
1053
        option.state = QStyle::State_Sunken;
1✔
1054

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

1059
    /* Icon */
1060
    option.icon = m_icon;
8✔
1061
    option.iconSize = m_iconSize;
8✔
1062

1063
    /* Paint the button */
1064
    QPainter painter(this);
16✔
1065
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1066

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

1069
    if (m_backgroundImage.isEmpty() == false)
8✔
1070
    {
1071
        QRect pxRect = m_bgPixmap.rect();
×
1072
        // if the pixmap is bigger than the button, then paint a scaled version of it
1073
        // covering the whole button surface
1074
        // if the pixmap is smaller than the button, draw a centered pixmap
1075
        if (pxRect.contains(rect()))
×
1076
        {
1077
            if (m_ledStyle == true)
×
1078
                painter.drawPixmap(rect(), m_bgPixmap);
×
1079
            else
1080
                painter.drawPixmap(3, 3, width() - 6, height() - 6, m_bgPixmap);
×
1081
        }
1082
        else
1083
        {
1084
            painter.drawPixmap((width() - pxRect.width()) / 2,
×
1085
                               (height() - pxRect.height()) / 2,
×
1086
                               m_bgPixmap);
×
1087
        }
1088
    }
1089

1090
    /* Paint caption with text wrapping */
1091
    if (caption().isEmpty() == false)
8✔
1092
    {
1093
        style()->drawItemText(&painter,
9✔
1094
                              rect(),
3✔
1095
                              Qt::AlignCenter | Qt::TextWordWrap,
3✔
1096
                              palette(),
1097
                              (mode() == Doc::Operate),
3✔
1098
                              caption());
6✔
1099
    }
1100

1101
    /* Flash emblem */
1102
    if (m_action == Flash)
8✔
1103
    {
1104
        QIcon icon(":/flash.png");
2✔
1105
        painter.drawPixmap(rect().width() - 18, 2,
2✔
1106
                           icon.pixmap(QSize(16, 16), QIcon::Normal, QIcon::On));
4✔
1107
    }
1108

1109
    if (m_ledStyle == true)
8✔
1110
    {
1111
        painter.setPen(QPen(QColor(160, 160, 160, 255), 2));
×
1112

1113
        if (state() == Active)
×
1114
            painter.setBrush(QBrush(QColor(0, 230, 0, 255)));
×
1115
        else if (state() == Monitoring)
×
1116
            painter.setBrush(QBrush(QColor(255, 170, 0, 255)));
×
1117
        else
1118
            painter.setBrush(QBrush(QColor(110, 110, 110, 255)));
×
1119

1120
        int dim = rect().width() / 6;
×
1121
        if (dim > 14) dim = 14;
×
1122

1123
        painter.drawEllipse(6, 6, dim, dim);      // Style #1
×
1124
        //painter.drawRoundedRect(-1, -1, dim, dim, 3, 3);   // Style #2
1125
    }
1126
    else
1127
    {
1128
        // Style #3
1129
        painter.setBrush(Qt::NoBrush);
8✔
1130

1131
        if (state() != Inactive)
8✔
1132
        {
1133
            int borderWidth = (rect().width() > 80)?3:2;
1✔
1134
            painter.setPen(QPen(QColor(20, 20, 20, 255), borderWidth * 2));
1✔
1135
            painter.drawRoundedRect(borderWidth, borderWidth,
3✔
1136
                                    rect().width() - borderWidth * 2, rect().height() - (borderWidth * 2),
1✔
1137
                                    borderWidth + 1,  borderWidth + 1);
1✔
1138
            if (state() == Monitoring)
1✔
1139
                painter.setPen(QPen(QColor(255, 170, 0, 255), borderWidth));
×
1140
            else
1141
                painter.setPen(QPen(QColor(0, 230, 0, 255), borderWidth));
1✔
1142
            painter.drawRoundedRect(borderWidth, borderWidth,
3✔
1143
                                    rect().width() - borderWidth * 2, rect().height() - (borderWidth * 2),
1✔
1144
                                    borderWidth, borderWidth);
1145
        }
1146
        else
1147
        {
1148
            painter.setPen(QPen(QColor(160, 160, 160, 255), 3));
7✔
1149
            painter.drawRoundedRect(1, 1, rect().width() - 2, rect().height() - 2, 3, 3);
7✔
1150
        }
1151
    }
1152

1153
    /* Stop painting here */
1154
    painter.end();
8✔
1155

1156
    /* Draw a selection frame if appropriate */
1157
    VCWidget::paintEvent(e);
8✔
1158
}
8✔
1159

1160
void VCButton::mousePressEvent(QMouseEvent* e)
2✔
1161
{
1162
    if (mode() == Doc::Design)
2✔
1163
        VCWidget::mousePressEvent(e);
1✔
1164
    else if (e->button() == Qt::LeftButton)
1✔
1165
        pressFunction();
1✔
1166
#if 0
1167
    else if (e->button() == Qt::RightButton)
1168
    {
1169
        Function* func = m_doc->function(m_function);
1170
        if (func != NULL)
1171
        {
1172
            QString menuStyle = "QMenu { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #B9D9E8, stop:1 #A4C0CE);"
1173
                            "border: 1px solid black; border-radius: 4px; font:bold; }";
1174
            QMenu *menu = new QMenu();
1175
            menu->setStyleSheet(menuStyle);
1176
            int idx = 0;
1177
            foreach(Attribute attr, func->attributes())
1178
            {
1179
                QString slStyle = "QSlider::groove:horizontal { border: 1px solid #999999; margin: 0; border-radius: 2px;"
1180
                        "height: 15px; background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #B1B1B1, stop:1 #c4c4c4); }"
1181

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

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

1188
                QWidget *entryWidget = new QWidget();
1189
                QHBoxLayout *hbox = new QHBoxLayout(menu);
1190
                hbox->setMargin(3);
1191
                QLabel *label = new QLabel(attr.m_name);
1192
                label->setAlignment(Qt::AlignLeft);
1193
                label->setFixedWidth(100);
1194
                ClickAndGoSlider *slider = new ClickAndGoSlider(menu);
1195
                slider->setOrientation(Qt::Horizontal);
1196
                slider->setSliderStyleSheet(slStyle);
1197
                slider->setFixedSize(QSize(100, 18));
1198
                slider->setMinimum(0);
1199
                slider->setMaximum(100);
1200
                slider->setValue(attr.m_value * 100);
1201
                slider->setProperty("attrIdx", QVariant(idx));
1202
                connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotAttributeChanged(int)));
1203
                hbox->addWidget(label);
1204
                hbox->addWidget(slider);
1205
                entryWidget->setLayout(hbox);
1206
                QWidgetAction *sliderBoxAction = new QWidgetAction(menu);
1207
                sliderBoxAction->setDefaultWidget(entryWidget);
1208
                menu->addAction(sliderBoxAction);
1209
                idx++;
1210
            }
1211
            menu->exec(QCursor::pos());
1212
        }
1213
    }
1214
#endif
1215
}
2✔
1216

1217
void VCButton::mouseReleaseEvent(QMouseEvent* e)
2✔
1218
{
1219
    if (mode() == Doc::Design)
2✔
1220
        VCWidget::mouseReleaseEvent(e);
1✔
1221
    else
1222
        releaseFunction();
1✔
1223
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc