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

mcallegari / qlcplus / 6796513551

08 Nov 2023 09:52AM UTC coverage: 28.09% (+0.02%) from 28.067%
6796513551

Pull #1462

github

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

28 of 42 new or added lines in 5 files covered. (66.67%)

2 existing lines in 1 file now uncovered.

15407 of 54849 relevant lines covered (28.09%)

20304.0 hits per line

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

80.91
/ui/src/virtualconsole/vcbutton.cpp
1
/*
2
  Q Light Controller Plus
3
  vcbutton.cpp
4

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7

8
  Licensed under the Apache License, Version 2.0 (the "License");
9
  you may not use this file except in compliance with the License.
10
  You may obtain a copy of the License at
11

12
      http://www.apache.org/licenses/LICENSE-2.0.txt
13

14
  Unless required by applicable law or agreed to in writing, software
15
  distributed under the License is distributed on an "AS IS" BASIS,
16
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
  See the License for the specific language governing permissions and
18
  limitations under the License.
19
*/
20

21
#include <QStyleOptionButton>
22
#include <QXmlStreamReader>
23
#include <QXmlStreamWriter>
24
#include <QWidgetAction>
25
#include <QColorDialog>
26
#include <QImageReader>
27
#include <QFileDialog>
28
#include <QPaintEvent>
29
#include <QMouseEvent>
30
#include <QMessageBox>
31
#include <QByteArray>
32
#include <QSettings>
33
#include <QPainter>
34
#include <QString>
35
#include <QDebug>
36
#include <QEvent>
37
#include <QTimer>
38
#include <QBrush>
39
#include <QStyle>
40
#include <QMenu>
41
#include <QSize>
42
#include <QPen>
43

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

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

52
#include "vcbuttonproperties.h"
53
#include "vcpropertieseditor.h"
54
#include "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
        else if (root.name() == KXMLQLCVCButtonFlashProperties)
6✔
947
        {
NEW
948
            QString str = root.attributes().value(KXMLQLCVCButtonFlashOverrides).toString();
×
NEW
949
            if (str.isEmpty() == false)
×
950
            {
NEW
951
                setFlashOverride((bool)str.toInt());
×
952
            }
953

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

981
    /* All buttons start raised... */
982
    setState(Inactive);
3✔
983

984
    return true;
3✔
985
}
986

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

991
    /* VC button entry */
992
    doc->writeStartElement(KXMLQLCVCButton);
1✔
993

994
    saveXMLCommon(doc);
1✔
995

996
    /* Icon */
997
    doc->writeAttribute(KXMLQLCVCButtonIcon, m_doc->normalizeComponentPath(iconPath()));
1✔
998

999
    /* Window state */
1000
    saveXMLWindowState(doc);
1✔
1001

1002
    /* Appearance */
1003
    saveXMLAppearance(doc);
1✔
1004

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

1010
    /* Action */
1011
    doc->writeStartElement(KXMLQLCVCButtonAction);
1✔
1012

1013
    if (action() == StopAll && stopAllFadeTime() != 0)
1✔
1014
    {
1015
        doc->writeAttribute(KXMLQLCVCButtonStopAllFadeTime, QString::number(stopAllFadeTime()));
×
1016
    }
1017
    doc->writeCharacters(actionToString(action()));
1✔
1018
    doc->writeEndElement();
1✔
1019

1020
    if(action() == Flash)
1✔
1021
    {
1022
        doc->writeStartElement(KXMLQLCVCButtonFlashProperties);
1✔
1023
        doc->writeAttribute(KXMLQLCVCButtonFlashOverrides, QString::number(flashOverrides()));
1✔
1024
        doc->writeAttribute(KXMLQLCVCButtonFlashForceLTP, QString::number(flashForceLTP()));
1✔
1025
        doc->writeEndElement();
1✔
1026
    }
1027

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

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

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

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

1045
    return true;
1✔
1046
}
1047

1048
/*****************************************************************************
1049
 * Event handlers
1050
 *****************************************************************************/
1051

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

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

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

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

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

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

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

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

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

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

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

1124
        if (state() == Active)
×
1125
            painter.setBrush(QBrush(QColor(0, 230, 0, 255)));
×
1126
        else if (state() == Monitoring)
×
1127
            painter.setBrush(QBrush(QColor(255, 170, 0, 255)));
×
1128
        else
1129
            painter.setBrush(QBrush(QColor(110, 110, 110, 255)));
×
1130

1131
        int dim = rect().width() / 6;
×
1132
        if (dim > 14) dim = 14;
×
1133

1134
        painter.drawEllipse(6, 6, dim, dim);      // Style #1
×
1135
        //painter.drawRoundedRect(-1, -1, dim, dim, 3, 3);   // Style #2
1136
    }
1137
    else
1138
    {
1139
        // Style #3
1140
        painter.setBrush(Qt::NoBrush);
8✔
1141

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

1164
    /* Stop painting here */
1165
    painter.end();
8✔
1166

1167
    /* Draw a selection frame if appropriate */
1168
    VCWidget::paintEvent(e);
8✔
1169
}
8✔
1170

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

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

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

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

1228
void VCButton::mouseReleaseEvent(QMouseEvent* e)
2✔
1229
{
1230
    if (mode() == Doc::Design)
2✔
1231
        VCWidget::mouseReleaseEvent(e);
1✔
1232
    else
1233
        releaseFunction();
1✔
1234
}
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