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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 hits per line

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

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

5
  Copyright (c) Heikki Junnila
6
                Massimo Callegari
7

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

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

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

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

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

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

52
#include "vcbuttonproperties.h"
53
#include "vcpropertieseditor.h"
54
#include "virtualconsole.h"
55
#include "chaseraction.h"
56
#include "mastertimer.h"
57
#include "vcsoloframe.h"
58
#include "vcbutton.h"
59
#include "function.h"
60
#include "apputil.h"
61
#include "chaser.h"
62
#include "doc.h"
63

64
const QSize VCButton::defaultSize(QSize(50, 50));
65

66
/*****************************************************************************
67
 * Initialization
68
 *****************************************************************************/
69

70
VCButton::VCButton(QWidget* parent, Doc* doc) : VCWidget(parent, doc)
25✔
71
    , m_iconPath()
25✔
72
    , m_blackoutFadeOutTime(0)
25✔
73
    , m_startupIntensityEnabled(false)
25✔
74
    , m_startupIntensity(1.0)
25✔
75
    , m_flashOverrides(false)
25✔
76
    , m_flashForceLTP(false)
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);
50✔
97
    m_resetIconAction->setShortcut(QKeySequence("SHIFT+ALT+C"));
25✔
98

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

104
    /* Initial size */
105
    QSettings settings;
25✔
106
    QVariant var = settings.value(SETTINGS_BUTTON_SIZE);
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);
50✔
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);
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);
158
    if (button == NULL)
2✔
159
        return false;
160

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

171
    m_flashForceLTP = button->flashForceLTP();
2✔
172
    m_flashOverrides = button->flashOverrides();
2✔
173

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

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

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

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

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

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

205
    m_hasCustomBackgroundColor = true;
5✔
206
    m_backgroundImage = QString();
5✔
207
    pal.setColor(QPalette::Button, color);
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);
4✔
223

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

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

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

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

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

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

251
    m_hasCustomForegroundColor = true;
3✔
252

253
    pal.setColor(QPalette::WindowText, color);
254
    pal.setColor(QPalette::ButtonText, color);
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);
4✔
269

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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;
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)
520
    //    return;
521

522
    QSharedPointer<QLCInputSource> src = inputSource();
46✔
523
    if (!src.isNull() && src->isValid() == true)
46✔
524
    {
525
        if (m_state == Inactive)
11✔
526
            sendFeedback(src->feedbackValue(QLCInputFeedback::LowerValue), src, src->feedbackExtraParams(QLCInputFeedback::LowerValue));
15✔
527
        else if (m_state == Monitoring)
6✔
528
            sendFeedback(src->feedbackValue(QLCInputFeedback::MonitorValue), src, src->feedbackExtraParams(QLCInputFeedback::MonitorValue));
×
529
        else
530
            sendFeedback(src->feedbackValue(QLCInputFeedback::UpperValue), src, src->feedbackExtraParams(QLCInputFeedback::UpperValue));
18✔
531
    }
532
}
46✔
533

534
/*****************************************************************************
535
 * External input
536
 *****************************************************************************/
537

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

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

572
/*****************************************************************************
573
 * Button action
574
 *****************************************************************************/
575

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

586
    // Action update
587
    m_action = action;
42✔
588
    updateIcon();
42✔
589

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

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

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

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

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

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

636
/*****************************************************************************
637
 * Intensity adjustment
638
 *****************************************************************************/
639

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

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

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

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

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

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

674

675

676
/*****************************************************************************
677
 * Flash Properties
678
 *****************************************************************************/
679

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

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

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

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

700

701

702
/*****************************************************************************
703
 * Button press / release handlers
704
 *****************************************************************************/
705

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

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

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

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

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

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

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

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

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

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

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

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

824
    if (fid != m_function)
11✔
825
        return;
826

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

834
    setState(state ? Active : Inactive);
13✔
835
}
836

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

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

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

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

871
/*****************************************************************************
872
 * Custom menu
873
 *****************************************************************************/
874

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

882
    return menu;
2✔
883
}
884

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

894
    VCWidget::adjustIntensity(val);
×
895
}
×
896

897
/*****************************************************************************
898
 * Load & Save
899
 *****************************************************************************/
900

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

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

915
    /* Widget commons */
916
    loadXMLCommon(root);
3✔
917

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

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

951
            if (attrs.hasAttribute(KXMLQLCVCButtonFlashOverride))
2✔
952
                setFlashOverride(attrs.value(KXMLQLCVCButtonFlashOverride).toInt());
×
953

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

978
    /* All buttons start raised... */
979
    setState(Inactive);
3✔
980

981
    return true;
982
}
983

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

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

991
    saveXMLCommon(doc);
1✔
992

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

996
    /* Window state */
997
    saveXMLWindowState(doc);
1✔
998

999
    /* Appearance */
1000
    saveXMLAppearance(doc);
1✔
1001

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

1007
    /* Action */
1008
    doc->writeStartElement(KXMLQLCVCButtonAction);
1✔
1009

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

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

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

1033
    /* External input */
1034
    saveXMLInput(doc);
1✔
1035

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

1039
    return true;
1✔
1040
}
1041

1042
/*****************************************************************************
1043
 * Event handlers
1044
 *****************************************************************************/
1045

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

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

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

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

1064
    /* Icon */
1065
    option.icon = m_icon;
8✔
1066
    option.iconSize = m_iconSize;
8✔
1067

1068
    /* Paint the button */
1069
    QPainter painter(this);
8✔
1070
    painter.setRenderHint(QPainter::Antialiasing);
8✔
1071

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

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

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

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

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

1118
        if (state() == Active)
×
1119
        {
1120
            if (m_flashForceLTP || m_flashOverrides)
×
1121
                painter.setBrush(QBrush(QColor(230, 0, 0, 255)));
×
1122
            else
1123
                painter.setBrush(QBrush(QColor(0, 230, 0, 255)));
×
1124
        }
1125
        else if (state() == Monitoring)
×
1126
            painter.setBrush(QBrush(QColor(255, 170, 0, 255)));
×
1127
        else
1128
            painter.setBrush(QBrush(QColor(110, 110, 110, 255)));
×
1129

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

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

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

1168
    /* Stop painting here */
1169
    painter.end();
8✔
1170

1171
    /* Draw a selection frame if appropriate */
1172
    VCWidget::paintEvent(e);
8✔
1173
}
8✔
1174

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

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

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

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

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