• 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

0.0
/ui/src/virtualconsole/vcclock.cpp
1
/*
2
  Q Light Controller Plus
3
  vcclock.cpp
4

5
  Copyright (c) Massimo Callegari
6

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

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

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

20
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QDateTime>
23
#include <QStyle>
24
#include <QtGui>
25

26
#include "vcclockproperties.h"
27
#include "vcclock.h"
28
#include "doc.h"
29

30
#define HYSTERESIS 3 // Hysteresis for pause/reset external input
31

32
#define KXMLQLCVCClockType      QString("Type")
33
#define KXMLQLCVCClockHours     QString("Hours")
34
#define KXMLQLCVCClockMinutes   QString("Minutes")
35
#define KXMLQLCVCClockSeconds   QString("Seconds")
36

37
#define KXMLQLCVCClockSchedule      QString("Schedule")
38
#define KXMLQLCVCClockScheduleFunc  QString("Function")
39
#define KXMLQLCVCClockScheduleTime  QString("Time")
40

41
#define KXMLQLCVCClockPlay  QString("PlayPause")
42
#define KXMLQLCVCClockReset QString("Reset")
43

44
const quint8 VCClock::playInputSourceId = 0;
45
const quint8 VCClock::resetInputSourceId = 1;
46

47
VCClock::VCClock(QWidget* parent, Doc* doc)
×
48
    : VCWidget(parent, doc)
49
    , m_clocktype(Clock)
×
50
    , m_scheduleIndex(-1)
×
51
    , m_hh(0)
×
52
    , m_mm(0)
×
53
    , m_ss(0)
×
54
    , m_targetTime(0)
×
55
    , m_currentTime(0)
×
56
    , m_isPaused(true)
×
57
{
58
    /* Set the class name "VCClock" as the object name as well */
59
    setObjectName(VCClock::staticMetaObject.className());
×
60

61
    setType(VCWidget::ClockWidget);
×
62
    setCaption("");
×
63
    resize(QSize(150, 50));
×
64
    QFont font = qApp->font();
×
65
    font.setBold(true);
66
    font.setPixelSize(28);
×
67
    setFont(font);
×
68

69
    QTimer *timer = new QTimer(this);
×
70
    connect(timer, SIGNAL(timeout()), this, SLOT(slotUpdateTime()));
×
71
    timer->start(1000);
×
72
}
×
73

74
VCClock::~VCClock()
×
75
{
76
}
×
77

78
void VCClock::slotModeChanged(Doc::Mode mode)
×
79
{
80
    qDebug() << Q_FUNC_INFO;
81

82
    if (mode == Doc::Operate)
×
83
    {
84
        m_scheduleIndex = -1;
×
85

86
        if (m_scheduleList.count() > 0)
×
87
        {
88
            QTime currTime = QDateTime::currentDateTime().time();
×
89

90
            // find the index of the next scheduled event to run
91
            for (int i = 0; i < m_scheduleList.count(); i++)
×
92
            {
93
                VCClockSchedule sch = m_scheduleList.at(i);
94
                if (sch.time().time() >= currTime)
×
95
                {
96
                    m_scheduleIndex = i;
×
97
                    qDebug() << "VC Clock set to play index:" << i;
98
                    break;
99
                }
100
            }
101
            // if no event is found after the current time, it means the next schedule 
102
            // will happen the day after so it's the first in the list
103
            if (m_scheduleIndex == -1)
×
104
                m_scheduleIndex = 0;
×
105
        }
106
    }
107
    VCWidget::slotModeChanged(mode);
×
108
}
×
109

110
/*********************************************************************
111
 * Type
112
 *********************************************************************/
113

114
void VCClock::setClockType(VCClock::ClockType type)
×
115
{
116
    m_clocktype = type;
×
117
    updateFeedback();
×
118
    update();
×
119
}
×
120

121
VCClock::ClockType VCClock::clockType() const
×
122
{
123
    return m_clocktype;
×
124
}
125

126
QString VCClock::typeToString(VCClock::ClockType type)
×
127
{
128
    if (type == Stopwatch)
×
129
        return "Stopwatch";
×
130
    else if (type == Countdown)
×
131
        return "Countdown";
×
132
    else
133
        return "Clock";
×
134
}
135

136
VCClock::ClockType VCClock::stringToType(QString str)
×
137
{
138
    if (str == "Stopwatch")
×
139
        return Stopwatch;
140
    else if (str == "Countdown")
×
141
        return Countdown;
142
    else
143
        return Clock;
×
144
}
145

146
void VCClock::addSchedule(VCClockSchedule schedule)
×
147
{
148
    qDebug() << Q_FUNC_INFO << "--- ID:" << schedule.function() << ", time:" << schedule.time().time().toString();
149
    if (schedule.function() != Function::invalidId())
×
150
        m_scheduleList.append(schedule);
×
151
    std::sort(m_scheduleList.begin(), m_scheduleList.end());
×
152
}
×
153

154
void VCClock::removeSchedule(int index)
×
155
{
156
    if (index < 0 || index > m_scheduleList.count())
×
157
        return;
158

159
    m_scheduleList.removeAt(index);
×
160
}
161

162
void VCClock::removeAllSchedule()
×
163
{
164
    m_scheduleList.clear();
×
165
}
×
166

167
QList<VCClockSchedule> VCClock::schedules()
×
168
{
169
    return m_scheduleList;
×
170
}
171

172
FunctionParent VCClock::functionParent() const
×
173
{
174
    return FunctionParent(FunctionParent::AutoVCWidget, id());
×
175
}
176

177
void VCClock::setCountdown(int h, int m, int s)
×
178
{
179
    m_hh = h;
×
180
    m_mm = m;
×
181
    m_ss = s;
×
182
    m_targetTime = (m_hh * 3600) + (m_mm * 60) + m_ss;
×
183
    m_currentTime = m_targetTime;
×
184
}
×
185

186
void VCClock::slotUpdateTime()
×
187
{
188
    if (mode() == Doc::Operate)
×
189
    {
190
        if (m_isPaused == false)
×
191
        {
192
            if (m_clocktype == Stopwatch)
×
193
                m_currentTime++;
×
194
            else if (m_clocktype == Countdown && m_currentTime > 0)
×
195
                m_currentTime--;
×
196

197
            emit timeChanged(m_currentTime);
×
198
        }
199
        else
200
        {
201
            if (m_clocktype == Clock)
×
202
            {
203
                if (m_scheduleIndex != -1 && m_scheduleIndex < m_scheduleList.count())
×
204
                {
205
                    QTime currTime = QDateTime::currentDateTime().time();
×
206
                    VCClockSchedule sch = m_scheduleList.at(m_scheduleIndex);
×
207
                    //qDebug() << "--- > currTime:" << currTime.toString() << ", schTime:" << sch.time().time().toString();
208
                    if (sch.time().time().toString() == currTime.toString())
×
209
                    {
210
                        quint32 fid = sch.function();
211
                        Function *func = m_doc->function(fid);
×
212
                        if (func != NULL)
×
213
                        {
214
                            func->start(m_doc->masterTimer(), functionParent());
×
215
                            qDebug() << "VC Clock starting function:" << func->name();
216
                        }
217
                        m_scheduleIndex++;
×
218
                        if (m_scheduleIndex == m_scheduleList.count())
×
219
                            m_scheduleIndex = 0;
×
220
                    }
221
                }
222
            }
223
        }
224
    }
225
    updateFeedback();
×
226
    update();
×
227
}
×
228

229
void VCClock::resetTimer()
×
230
{
231
    if (clockType() == Stopwatch)
×
232
        m_currentTime = 0;
×
233
    else if (clockType() == Countdown)
×
234
        m_currentTime = m_targetTime;
×
235

236
    emit timeChanged(m_currentTime);
×
237

238
    updateFeedback();
×
239
    update();
×
240
}
×
241

242
void VCClock::playPauseTimer()
×
243
{
244
    if (clockType() == Stopwatch || clockType() == Countdown)
×
245
        m_isPaused = !m_isPaused;
×
246

247
    updateFeedback();
×
248
    update();
×
249
}
×
250

251
/*****************************************************************************
252
 * Key Sequences
253
 *****************************************************************************/
254

255
void VCClock::setPlayKeySequence(const QKeySequence& keySequence)
×
256
{
257
    m_playKeySequence = QKeySequence(keySequence);
×
258
}
×
259

260
QKeySequence VCClock::playKeySequence() const
×
261
{
262
    return m_playKeySequence;
×
263
}
264

265
void VCClock::setResetKeySequence(const QKeySequence& keySequence)
×
266
{
267
    m_resetKeySequence = QKeySequence(keySequence);
×
268
}
×
269

270
QKeySequence VCClock::resetKeySequence() const
×
271
{
272
    return m_resetKeySequence;
×
273
}
274

275
void VCClock::slotKeyPressed(const QKeySequence& keySequence)
×
276
{
277
    if (acceptsInput() == false)
×
278
        return;
279

280
    if (m_playKeySequence == keySequence)
×
281
        playPauseTimer();
×
282
    else if (m_resetKeySequence == keySequence)
×
283
        resetTimer();
×
284
}
285

286
void VCClock::updateFeedback()
×
287
{
288
    if (clockType() == Stopwatch)
×
289
    {
290
        sendFeedback(!m_isPaused ? UCHAR_MAX : 0, playInputSourceId);
×
291
        sendFeedback(m_currentTime == 0 ? UCHAR_MAX : 0, resetInputSourceId);
×
292
    }
293
    else if (clockType() == Countdown)
×
294
    {
295
        sendFeedback(!m_isPaused ? UCHAR_MAX : 0, playInputSourceId);
×
296
        sendFeedback(m_currentTime == m_targetTime ? UCHAR_MAX : 0, resetInputSourceId);
×
297
    }
298
    else
299
    {
300
        sendFeedback(0, playInputSourceId);
×
301
        sendFeedback(0, resetInputSourceId);
×
302
    }
303
}
×
304

305
/*****************************************************************************
306
 * External Input
307
 *****************************************************************************/
308

309
void VCClock::slotInputValueChanged(quint32 universe, quint32 channel, uchar value)
×
310
{
311
    /* Don't let input data through in design mode or if disabled */
312
    if (acceptsInput() == false)
×
313
        return;
314

315
    quint32 pagedCh = (page() << 16) | channel;
×
316

317
    if (checkInputSource(universe, pagedCh, value, sender(), playInputSourceId))
×
318
    {
319
        // Use hysteresis for values, in case the timer is being controlled
320
        // by a slider. The value has to go to zero before the next non-zero
321
        // value is accepted as input. And the non-zero values have to visit
322
        // above $HYSTERESIS before a zero is accepted again.
323
        if (m_playLatestValue == 0 && value > 0)
×
324
        {
325
            playPauseTimer();
×
326
            m_playLatestValue = value;
×
327
        }
328
        else if (m_playLatestValue > HYSTERESIS && value == 0)
×
329
        {
330
            m_playLatestValue = 0;
×
331
        }
332

333
        if (value > HYSTERESIS)
×
334
            m_playLatestValue = value;
×
335
    }
336
    else if (checkInputSource(universe, pagedCh, value, sender(), resetInputSourceId))
×
337
    {
338
        // Use hysteresis for values, in case the timer is being controlled
339
        // by a slider. The value has to go to zero before the next non-zero
340
        // value is accepted as input. And the non-zero values have to visit
341
        // above $HYSTERESIS before a zero is accepted again.
342
        if (m_resetLatestValue == 0 && value > 0)
×
343
        {
344
            resetTimer();
×
345
            m_resetLatestValue = value;
×
346
        }
347
        else if (m_resetLatestValue > HYSTERESIS && value == 0)
×
348
        {
349
            m_resetLatestValue = 0;
×
350
        }
351

352
        if (value > HYSTERESIS)
×
353
            m_resetLatestValue = value;
×
354
    }
355
}
356

357
/*****************************************************************************
358
 * Clipboard
359
 *****************************************************************************/
360

361
VCWidget* VCClock::createCopy(VCWidget* parent)
×
362
{
363
    Q_ASSERT(parent != NULL);
364

365
    VCClock* clock = new VCClock(parent, m_doc);
×
366
    if (clock->copyFrom(this) == false)
×
367
    {
368
        delete clock;
×
369
        clock = NULL;
370
    }
371

372
    return clock;
×
373
}
374

375
bool VCClock::copyFrom(const VCWidget* widget)
×
376
{
377
    const VCClock* clock = qobject_cast<const VCClock*> (widget);
378
    if (clock == NULL)
×
379
        return false;
380

381
    // TODO: copy schedules
382

383
    /* Clock type */
384
    setClockType(clock->clockType());
×
385

386
    /* Key sequence */
387
    setPlayKeySequence(clock->playKeySequence());
×
388
    setResetKeySequence(clock->resetKeySequence());
×
389

390
    /* Common stuff */
391
    return VCWidget::copyFrom(widget);
×
392
}
393

394
/*****************************************************************************
395
 * Properties
396
 *****************************************************************************/
397

398
void VCClock::editProperties()
×
399
{
400
    VCClockProperties vccp(this, m_doc);
×
401
    if (vccp.exec() == QDialog::Rejected)
×
402
        return;
403

404
    m_doc->setModified();
×
405
}
×
406

407
/*****************************************************************************
408
 * Load & Save
409
 *****************************************************************************/
410

411
bool VCClock::loadXML(QXmlStreamReader &root)
×
412
{
413
    if (root.name() != KXMLQLCVCClock)
×
414
    {
415
        qWarning() << Q_FUNC_INFO << "Clock node not found";
×
416
        return false;
×
417
    }
418

419
    QXmlStreamAttributes attrs = root.attributes();
×
420

421
    if (attrs.hasAttribute(KXMLQLCVCClockType))
×
422
    {
423
        setClockType(stringToType(attrs.value(KXMLQLCVCClockType).toString()));
×
424
        if (clockType() == Countdown)
×
425
        {
426
            int h = 0, m = 0, s = 0;
427
            if (attrs.hasAttribute(KXMLQLCVCClockHours))
×
428
                h = attrs.value(KXMLQLCVCClockHours).toString().toInt();
×
429
            if (attrs.hasAttribute(KXMLQLCVCClockMinutes))
×
430
                m = attrs.value(KXMLQLCVCClockMinutes).toString().toInt();
×
431
            if (attrs.hasAttribute(KXMLQLCVCClockSeconds))
×
432
                s = attrs.value(KXMLQLCVCClockSeconds).toString().toInt();
×
433
            setCountdown(h, m ,s);
×
434
        }
435
    }
436

437
    /* Widget commons */
438
    loadXMLCommon(root);
×
439

440
    /* Children */
441
    while (root.readNextStartElement())
×
442
    {
443
        if (root.name() == KXMLQLCWindowState)
×
444
        {
445
            int x = 0, y = 0, w = 0, h = 0;
×
446
            bool visible = false;
×
447
            loadXMLWindowState(root, &x, &y, &w, &h, &visible);
×
448
            setGeometry(x, y, w, h);
×
449
        }
450
        else if (root.name() == KXMLQLCVCWidgetAppearance)
×
451
        {
452
            loadXMLAppearance(root);
×
453
        }
454
        else if (root.name() == KXMLQLCVCClockSchedule)
×
455
        {
456
            VCClockSchedule sch;
457
            if (sch.loadXML(root) == true)
×
458
                addSchedule(sch);
×
459
        }
460
        else if (root.name() == KXMLQLCVCClockPlay)
×
461
        {
462
            QString str = loadXMLSources(root, playInputSourceId);
×
463
            if (str.isEmpty() == false)
×
464
                m_playKeySequence = stripKeySequence(QKeySequence(str));
×
465
        }else if (root.name() == KXMLQLCVCClockReset)
×
466
        {
467
            QString str = loadXMLSources(root, resetInputSourceId);
×
468
            if (str.isEmpty() == false)
×
469
                m_resetKeySequence = stripKeySequence(QKeySequence(str));
×
470
        }
×
471
        else
472
        {
473
            qWarning() << Q_FUNC_INFO << "Unknown clock tag:" << root.name().toString();
×
474
            root.skipCurrentElement();
×
475
        }
476
    }
477

478
    return true;
479
}
480

481
bool VCClock::saveXML(QXmlStreamWriter *doc)
×
482
{
483
    Q_ASSERT(doc != NULL);
484

485
    /* VC Clock entry */
486
    doc->writeStartElement(KXMLQLCVCClock);
×
487

488
    /* Type */
489
    ClockType type = clockType();
×
490
    doc->writeAttribute(KXMLQLCVCClockType, typeToString(type));
×
491
    if (type == Countdown)
×
492
    {
493
        doc->writeAttribute(KXMLQLCVCClockHours, QString::number(getHours()));
×
494
        doc->writeAttribute(KXMLQLCVCClockMinutes, QString::number(getMinutes()));
×
495
        doc->writeAttribute(KXMLQLCVCClockSeconds, QString::number(getSeconds()));
×
496
    }
497

498
    saveXMLCommon(doc);
×
499

500
    /* Window state */
501
    saveXMLWindowState(doc);
×
502

503
    /* Appearance */
504
    saveXMLAppearance(doc);
×
505

506
    foreach (VCClockSchedule sch, schedules())
×
507
        sch.saveXML(doc);
×
508

509
    if (type != Clock)
×
510
    {
511
        /* Play/Pause */
512
        doc->writeStartElement(KXMLQLCVCClockPlay);
×
513
        if (m_playKeySequence.toString().isEmpty() == false)
×
514
            doc->writeTextElement(KXMLQLCVCWidgetKey, m_playKeySequence.toString());
×
515
        saveXMLInput(doc, inputSource(playInputSourceId));
×
516
        doc->writeEndElement();
×
517

518
        /* Reset */
519
        doc->writeStartElement(KXMLQLCVCClockReset);
×
520
        if (m_resetKeySequence.toString().isEmpty() == false)
×
521
            doc->writeTextElement(KXMLQLCVCWidgetKey, m_resetKeySequence.toString());
×
522
        saveXMLInput(doc, inputSource(resetInputSourceId));
×
523
        doc->writeEndElement();
×
524
    }
525

526
    /* End the <Clock> tag */
527
    doc->writeEndElement();
×
528

529
    return true;
×
530
}
531

532
/****************************************************************************
533
 * Drawing
534
 ****************************************************************************/
535

536
void VCClock::paintEvent(QPaintEvent* e)
×
537
{
538
    QPainter painter(this);
×
539

540
    if (clockType() == Clock)
×
541
    {
542
        QDateTime currTime = QDateTime::currentDateTime();
×
543
        style()->drawItemText(&painter, rect(), Qt::AlignCenter | Qt::TextWordWrap, palette(),
×
544
                              true, currTime.time().toString(), foregroundRole());
×
545
    }
×
546
    else
547
    {
548
        quint32 secTime = m_currentTime;
×
549
        uint h, m;
550

551
        h = secTime / 3600;
×
552
        secTime -= (h * 3600);
553

554
        m = secTime / 60;
×
555
        secTime -= (m * 60);
556
        style()->drawItemText(&painter, rect(), Qt::AlignCenter | Qt::TextWordWrap, palette(),
×
557
                              true, QString("%1:%2:%3").arg(h, 2, 10, QChar('0'))
×
558
                              .arg(m, 2, 10, QChar('0')).arg(secTime, 2, 10, QChar('0')), foregroundRole());
×
559
    }
560
    painter.end();
×
561

562
    VCWidget::paintEvent(e);
×
563
}
×
564

565
void VCClock::mousePressEvent(QMouseEvent *e)
×
566
{
567
    if (mode() == Doc::Design)
×
568
    {
569
        VCWidget::mousePressEvent(e);
×
570
        return;
×
571
    }
572

573
    if (e->button() == Qt::RightButton)
×
574
    {
575
        resetTimer();
×
576
    }
577
    else if (e->button() == Qt::LeftButton)
×
578
    {
579
        playPauseTimer();
×
580
    }
581
    VCWidget::mousePressEvent(e);
×
582
}
583

584
/*********************************************************************
585
 * VCClockSchedule Class methods
586
 *********************************************************************/
587

588
bool VCClockSchedule::operator <(const VCClockSchedule &sch) const
×
589
{
590
    if (sch.time() < time())
×
591
        return false;
×
592
    return true;
593
}
594

595
bool VCClockSchedule::loadXML(QXmlStreamReader &root)
×
596
{
597
    if (root.name() != KXMLQLCVCClockSchedule)
×
598
    {
599
        qWarning() << Q_FUNC_INFO << "Clock Schedule node not found";
×
600
        return false;
×
601
    }
602

603
    QXmlStreamAttributes attrs = root.attributes();
×
604

605
    if (attrs.hasAttribute(KXMLQLCVCClockScheduleFunc))
×
606
    {
607
        setFunction(attrs.value(KXMLQLCVCClockScheduleFunc).toString().toUInt());
×
608
        if (attrs.hasAttribute(KXMLQLCVCClockScheduleTime))
×
609
        {
610
            QDateTime dt;
×
611
            dt.setTime(QTime::fromString(attrs.value(KXMLQLCVCClockScheduleTime).toString(), "HH:mm:ss"));
×
612
            setTime(dt);
×
613
        }
×
614
    }
615
    root.skipCurrentElement();
×
616

617
    return true;
618
}
619

620
bool VCClockSchedule::saveXML(QXmlStreamWriter *doc)
×
621
{
622
    /* Schedule tag */
623
    doc->writeStartElement(KXMLQLCVCClockSchedule);
×
624

625
    /* Schedule function */
626
    doc->writeAttribute(KXMLQLCVCClockScheduleFunc, QString::number(function()));
×
627
    /* Schedule time */
628
    doc->writeAttribute(KXMLQLCVCClockScheduleTime, time().time().toString());
×
629

630
    doc->writeEndElement();
×
631

632
    return true;
×
633
}
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