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

mcallegari / qlcplus / 7252848206

18 Dec 2023 07:26PM UTC coverage: 32.067% (+0.001%) from 32.066%
7252848206

push

github

mcallegari
Code style review #1427

199 of 628 new or added lines in 101 files covered. (31.69%)

8 existing lines in 2 files now uncovered.

15169 of 47304 relevant lines covered (32.07%)

23733.74 hits per line

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

81.54
/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
    m_flashForceLTP = button->flashForceLTP();
2✔
172
    m_flashOverrides = button->flashOverrides();
2✔
173

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

251
    m_hasCustomForegroundColor = true;
3✔
252

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

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

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

264
    m_hasCustomForegroundColor = false;
2✔
265

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

418
    if (fid == m_function)
×
419
        return;
×
420

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

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

442
/*****************************************************************************
443
 * Button state
444
 *****************************************************************************/
445

446
VCButton::ButtonState VCButton::state() const
72✔
447
{
448
    return m_state;
72✔
449
}
450

451
void VCButton::setState(ButtonState state)
59✔
452
{
453
    if (state == m_state)
59✔
454
        return;
16✔
455

456
    m_state = state;
43✔
457

458
    emit stateChanged(m_state);
43✔
459

460
    updateFeedback();
43✔
461

462
    update();
43✔
463
}
464

465
void VCButton::updateState()
×
466
{
467
    ButtonState state = Inactive;
×
468

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

481
    if (m_state != state)
×
482
        setState(state);
×
483
}
×
484

485
/*****************************************************************************
486
 * Key sequence handler
487
 *****************************************************************************/
488

489
void VCButton::setKeySequence(const QKeySequence& keySequence)
12✔
490
{
491
    m_keySequence = QKeySequence(keySequence);
12✔
492
}
12✔
493

494
QKeySequence VCButton::keySequence() const
9✔
495
{
496
    return m_keySequence;
9✔
497
}
498

499
void VCButton::slotKeyPressed(const QKeySequence& keySequence)
5✔
500
{
501
    if (acceptsInput() == false)
5✔
502
        return;
×
503

504
    if (m_keySequence == keySequence)
5✔
505
        pressFunction();
5✔
506
}
507

508
void VCButton::slotKeyReleased(const QKeySequence& keySequence)
3✔
509
{
510
    if (acceptsInput() == false)
3✔
511
        return;
×
512

513
    if (m_keySequence == keySequence)
3✔
514
        releaseFunction();
3✔
515
}
516

517
void VCButton::updateFeedback()
46✔
518
{
519
    if (m_state == Monitoring)
46✔
520
        return;
×
521

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

532
/*****************************************************************************
533
 * External input
534
 *****************************************************************************/
535

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

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

570
/*****************************************************************************
571
 * Button action
572
 *****************************************************************************/
573

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

584
    // Action update
585
    m_action = action;
42✔
586
    updateIcon();
42✔
587

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

595
VCButton::Action VCButton::action() const
11✔
596
{
597
    return m_action;
11✔
598
}
599

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

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

624
void VCButton::setStopAllFadeOutTime(int ms)
2✔
625
{
626
    m_blackoutFadeOutTime = ms;
2✔
627
}
2✔
628

629
int VCButton::stopAllFadeTime() const
3✔
630
{
631
    return m_blackoutFadeOutTime;
3✔
632
}
633

634
/*****************************************************************************
635
 * Intensity adjustment
636
 *****************************************************************************/
637

638
void VCButton::enableStartupIntensity(bool enable)
11✔
639
{
640
    m_startupIntensityEnabled = enable;
11✔
641
}
11✔
642

643
bool VCButton::isStartupIntensityEnabled() const
16✔
644
{
645
    return m_startupIntensityEnabled;
16✔
646
}
647

648
void VCButton::setStartupIntensity(qreal fraction)
179✔
649
{
650
    m_startupIntensity = CLAMP(fraction, qreal(0), qreal(1));
179✔
651
}
179✔
652

653
qreal VCButton::startupIntensity() const
183✔
654
{
655
    return m_startupIntensity;
183✔
656
}
657

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

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

672

673

674
/*****************************************************************************
675
 * Flash Properties
676
 *****************************************************************************/
677

678
bool VCButton::flashOverrides() const
7✔
679
{
680
    return m_flashOverrides;
7✔
681
}
682

683
void VCButton::setFlashOverride(bool shouldOverride)
1✔
684
{
685
    m_flashOverrides = shouldOverride;
1✔
686
}
1✔
687

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

693
void VCButton::setFlashForceLTP(bool forceLTP)
1✔
694
{
695
    m_flashForceLTP = forceLTP;
1✔
696
}
1✔
697

698

699

700
/*****************************************************************************
701
 * Button press / release handlers
702
 *****************************************************************************/
703

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

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

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

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

741
                Chaser *chaser = qobject_cast<Chaser*>(f);
×
742
                chaser->setAction(action);
×
743
            }
744

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

773
FunctionParent VCButton::functionParent() const
6✔
774
{
775
    return FunctionParent(FunctionParent::ManualVCWidget, id());
6✔
776
}
777

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

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

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

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

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

822
    if (fid != m_function)
11✔
823
        return;
1✔
824

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

832
    setState(state ? Active : Inactive);
9✔
833
}
834

835
void VCButton::blink(int ms)
3✔
836
{
837
    slotBlink();
3✔
838
    QTimer::singleShot(ms, this, SLOT(slotBlink()));
3✔
839
}
3✔
840

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

852
void VCButton::slotBlackoutChanged(bool state)
2✔
853
{
854
    setState(state ? Active : Inactive);
2✔
855
}
2✔
856

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

869
/*****************************************************************************
870
 * Custom menu
871
 *****************************************************************************/
872

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

880
    return menu;
2✔
881
}
882

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

892
    VCWidget::adjustIntensity(val);
×
893
}
×
894

895
/*****************************************************************************
896
 * Load & Save
897
 *****************************************************************************/
898

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

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

913
    /* Widget commons */
914
    loadXMLCommon(root);
3✔
915

916
    /* Icon */
917
    setIconPath(m_doc->denormalizeComponentPath(root.attributes().value(KXMLQLCVCButtonIcon).toString()));
3✔
918

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

949
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashOverride))
2✔
950
                setFlashOverride(attrs.value(KXMLQLCVCButtonFlashOverride).toInt());
×
951

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

976
    /* All buttons start raised... */
977
    setState(Inactive);
3✔
978

979
    return true;
3✔
980
}
981

982
bool VCButton::saveXML(QXmlStreamWriter *doc)
1✔
983
{
984
    Q_ASSERT(doc != NULL);
1✔
985

986
    /* VC button entry */
987
    doc->writeStartElement(KXMLQLCVCButton);
1✔
988

989
    saveXMLCommon(doc);
1✔
990

991
    /* Icon */
992
    doc->writeAttribute(KXMLQLCVCButtonIcon, m_doc->normalizeComponentPath(iconPath()));
1✔
993

994
    /* Window state */
995
    saveXMLWindowState(doc);
1✔
996

997
    /* Appearance */
998
    saveXMLAppearance(doc);
1✔
999

1000
    /* Function */
1001
    doc->writeStartElement(KXMLQLCVCButtonFunction);
1✔
1002
    doc->writeAttribute(KXMLQLCVCButtonFunctionID, QString::number(function()));
1✔
1003
    doc->writeEndElement();
1✔
1004

1005
    /* Action */
1006
    doc->writeStartElement(KXMLQLCVCButtonAction);
1✔
1007

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

1020
    /* Key sequence */
1021
    if (m_keySequence.isEmpty() == false)
1✔
1022
        doc->writeTextElement(KXMLQLCVCButtonKey, m_keySequence.toString());
1✔
1023

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

1031
    /* External input */
1032
    saveXMLInput(doc);
1✔
1033

1034
    /* End the <Button> tag */
1035
    doc->writeEndElement();
1✔
1036

1037
    return true;
1✔
1038
}
1039

1040
/*****************************************************************************
1041
 * Event handlers
1042
 *****************************************************************************/
1043

1044
void VCButton::paintEvent(QPaintEvent* e)
8✔
1045
{
1046
    QStyleOptionButton option;
16✔
1047
    option.initFrom(this);
8✔
1048

1049
    /* This should look like a normal button */
1050
    option.features = QStyleOptionButton::None;
8✔
1051

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

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

1062
    /* Icon */
1063
    option.icon = m_icon;
8✔
1064
    option.iconSize = m_iconSize;
8✔
1065

1066
    /* Paint the button */
1067
    QPainter painter(this);
16✔
1068
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1069

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

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

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

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

1112
    if (m_ledStyle == true)
8✔
1113
    {
1114
        painter.setPen(QPen(QColor(160, 160, 160, 255), 2));
×
1115

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

1123
        int dim = rect().width() / 6;
×
1124
        if (dim > 14) dim = 14;
×
1125

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

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

1156
    /* Stop painting here */
1157
    painter.end();
8✔
1158

1159
    /* Draw a selection frame if appropriate */
1160
    VCWidget::paintEvent(e);
8✔
1161
}
8✔
1162

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

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

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

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

1220
void VCButton::mouseReleaseEvent(QMouseEvent* e)
2✔
1221
{
1222
    if (mode() == Doc::Design)
2✔
1223
        VCWidget::mouseReleaseEvent(e);
1✔
1224
    else
1225
        releaseFunction();
1✔
1226
}
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