synchtimer.cpp 7.72 KB
Newer Older
1
/*
2
 *  synchtimer.cpp  -  timers which synchronize to time boundaries
3
 *  Program:  kalarm
Volker Krause's avatar
Volker Krause committed
4
 *  Copyright © 2004,2005,2007-2009 by David Jarvie <djarvie@kde.org>
5 6 7 8 9 10 11 12 13 14 15
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
David Jarvie's avatar
David Jarvie committed
16 17 18
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 20 21
 */

#include "kalarm.h"
Laurent Montel's avatar
Laurent Montel committed
22
#include "synchtimer.h"
David Jarvie's avatar
David Jarvie committed
23

24
#include <QTimer>
Laurent Montel's avatar
Laurent Montel committed
25 26
#include <QDateTime>
#include <QTime>
David Jarvie's avatar
Tidy  
David Jarvie committed
27
#include <qdebug.h>
28 29 30

/*=============================================================================
=  Class: SynchTimer
31
=  Virtual base class for application-wide timer synchronized to a time boundary.
32 33 34 35
=============================================================================*/

SynchTimer::SynchTimer()
{
36 37
    mTimer = new QTimer(this);
    mTimer->setSingleShot(true);
38 39 40 41
}

SynchTimer::~SynchTimer()
{
42 43
    delete mTimer;
    mTimer = 0;
44 45 46 47 48 49 50
}

/******************************************************************************
* Connect to the timer. The timer is started if necessary.
*/
void SynchTimer::connecT(QObject* receiver, const char* member)
{
51 52 53 54 55 56 57
    Connection connection(receiver, member);
    if (mConnections.contains(connection))
        return;           // the slot is already connected, so ignore request
    connect(mTimer, SIGNAL(timeout()), receiver, member);
    mConnections.append(connection);
    if (!mTimer->isActive())
    {
Laurent Montel's avatar
Laurent Montel committed
58
        connect(mTimer, &QTimer::timeout, this, &SynchTimer::slotTimer);
59 60
        start();
    }
61 62 63 64 65 66 67
}

/******************************************************************************
* Disconnect from the timer. The timer is stopped if no longer needed.
*/
void SynchTimer::disconnecT(QObject* receiver, const char* member)
{
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
    if (mTimer)
    {
        mTimer->disconnect(receiver, member);
        if (member)
        {
            int i = mConnections.indexOf(Connection(receiver, member));
            if (i >= 0)
                mConnections.removeAt(i);
        }
        else
        {
            for (int i = 0;  i < mConnections.count();  )
            {
                if (mConnections[i].receiver == receiver)
                    mConnections.removeAt(i);
                else
                    ++i;
            }
        }
        if (mConnections.isEmpty())
        {
            mTimer->disconnect();
            mTimer->stop();
        }
    }
93 94 95 96 97
}


/*=============================================================================
=  Class: MinuteTimer
98
=  Application-wide timer synchronized to the minute boundary.
99 100 101 102 103 104
=============================================================================*/

MinuteTimer* MinuteTimer::mInstance = 0;

MinuteTimer* MinuteTimer::instance()
{
105 106 107
    if (!mInstance)
        mInstance = new MinuteTimer;
    return mInstance;
108 109 110
}

/******************************************************************************
111 112 113 114
* Called when the timer triggers, or to start the timer.
* Timers can under some circumstances wander off from the correct trigger time,
* so rather than setting a 1 minute interval, calculate the correct next
* interval each time it triggers.
115
*/
116
void MinuteTimer::slotTimer()
117
{
Laurent Montel's avatar
Laurent Montel committed
118
    qDebug();
119 120
    int interval = 62 - QTime::currentTime().second();
    mTimer->start(interval * 1000);     // execute a single shot
121 122 123 124 125
}


/*=============================================================================
=  Class: DailyTimer
126
=  Application-wide timer synchronized to midnight.
127 128
=============================================================================*/

David Jarvie's avatar
David Jarvie committed
129
QList<DailyTimer*> DailyTimer::mFixedTimers;
130

131
DailyTimer::DailyTimer(const QTime& timeOfDay, bool fixed)
132 133
    : mTime(timeOfDay),
      mFixed(fixed)
134
{
135 136
    if (fixed)
        mFixedTimers.append(this);
137 138
}

139
DailyTimer::~DailyTimer()
140
{
141 142
    if (mFixed)
        mFixedTimers.removeAt(mFixedTimers.indexOf(this));
143 144
}

145
DailyTimer* DailyTimer::fixedInstance(const QTime& timeOfDay, bool create)
146
{
147 148 149 150
    for (int i = 0, end = mFixedTimers.count();  i < end;  ++i)
        if (mFixedTimers[i]->mTime == timeOfDay)
            return mFixedTimers[i];
    return create ? new DailyTimer(timeOfDay, true) : 0;
151 152
}

153 154 155 156 157
/******************************************************************************
* Disconnect from the timer signal which triggers at the given fixed time of day.
* If there are no remaining connections to that timer, it is destroyed.
*/
void DailyTimer::disconnect(const QTime& timeOfDay, QObject* receiver, const char* member)
158
{
159 160 161 162 163 164
    DailyTimer* timer = fixedInstance(timeOfDay, false);
    if (!timer)
        return;
    timer->disconnecT(receiver, member);
    if (!timer->hasConnections())
        delete timer;
165 166 167
}

/******************************************************************************
168
* Change the time at which the variable timer triggers.
169
*/
170
void DailyTimer::changeTime(const QTime& newTimeOfDay, bool triggerMissed)
171
{
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    if (mFixed)
        return;
    if (mTimer->isActive())
    {
        mTimer->stop();
        bool triggerNow = false;
        if (triggerMissed)
        {
            QTime now = QTime::currentTime();
            if (now >= newTimeOfDay  &&  now < mTime)
            {
                // The trigger time is now earlier and it has already arrived today.
                // Trigger a timer event immediately.
                triggerNow = true;
            }
        }
        mTime = newTimeOfDay;
        if (triggerNow)
            mTimer->start(0);    // trigger immediately
        else
            start();
    }
    else
        mTime = newTimeOfDay;
196 197 198
}

/******************************************************************************
199 200 201
* Initialise the timer to trigger at the specified time.
* This will either be today or tomorrow, depending on whether the trigger time
* has already passed.
202
*/
203
void DailyTimer::start()
204
{
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
    // TIMEZONE = local time
    QDateTime now = QDateTime::currentDateTime();
    // Find out whether to trigger today or tomorrow.
    // In preference, use the last trigger date to determine this, since
    // that will avoid possible errors due to daylight savings time changes.
    bool today;
    if (mLastDate.isValid())
        today = (mLastDate < now.date());
    else
        today = (now.time() < mTime);
    QDateTime next;
    if (today)
        next = QDateTime(now.date(), mTime);
    else
        next = QDateTime(now.date().addDays(1), mTime);
    uint interval = next.toTime_t() - now.toTime_t();
    mTimer->start(interval * 1000);    // execute a single shot
Laurent Montel's avatar
Laurent Montel committed
222
    qDebug() << "at" << mTime.hour() << ":" << mTime.minute() << ": interval =" << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60;
223 224 225 226 227 228 229 230 231 232
}

/******************************************************************************
* Called when the timer triggers.
* Set the timer to trigger again tomorrow at the specified time.
* Note that if daylight savings time changes occur, this will not be 24 hours
* from now.
*/
void DailyTimer::slotTimer()
{
233 234 235 236 237 238
    // TIMEZONE = local time
    QDateTime now = QDateTime::currentDateTime();
    mLastDate = now.date();
    QDateTime next = QDateTime(mLastDate.addDays(1), mTime);
    uint interval = next.toTime_t() - now.toTime_t();
    mTimer->start(interval * 1000);    // execute a single shot
Laurent Montel's avatar
Laurent Montel committed
239
    qDebug() << "at" << mTime.hour() << ":" << mTime.minute() << ": interval =" << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60;
240
}
241

242
// vim: et sw=4: