kalarm/lib

synchtimer.cpp

00001 /*
00002  *  synchtimer.cpp  -  timers which synchronise to time boundaries
00003  *  Program:  kalarm
00004  *  Copyright (C) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"
00022 #include <qtimer.h>
00023 #include <kdebug.h>
00024 #include "synchtimer.moc"
00025 
00026 
00027 /*=============================================================================
00028 =  Class: SynchTimer
00029 =  Virtual base class for application-wide timer synchronised to a time boundary.
00030 =============================================================================*/
00031 
00032 SynchTimer::SynchTimer()
00033 {
00034     mTimer = new QTimer(this);
00035 }
00036 
00037 SynchTimer::~SynchTimer()
00038 {
00039     delete mTimer;
00040     mTimer = 0;
00041 }
00042 
00043 /******************************************************************************
00044 * Connect to the timer. The timer is started if necessary.
00045 */
00046 void SynchTimer::connecT(QObject* receiver, const char* member)
00047 {
00048     Connection connection(receiver, member);
00049     if (mConnections.find(connection) != mConnections.end())
00050         return;           // the slot is already connected, so ignore request
00051     connect(mTimer, SIGNAL(timeout()), receiver, member);
00052     mConnections.append(connection);
00053     if (!mTimer->isActive())
00054     {
00055         connect(mTimer, SIGNAL(timeout()), this, SLOT(slotTimer()));
00056         start();
00057     }
00058 }
00059 
00060 /******************************************************************************
00061 * Disconnect from the timer. The timer is stopped if no longer needed.
00062 */
00063 void SynchTimer::disconnecT(QObject* receiver, const char* member)
00064 {
00065     if (mTimer)
00066     {
00067         mTimer->disconnect(receiver, member);
00068         if (member)
00069             mConnections.remove(Connection(receiver, member));
00070         else
00071         {
00072             for (QValueList<Connection>::Iterator it = mConnections.begin();  it != mConnections.end();  )
00073             {
00074                 if ((*it).receiver == receiver)
00075                     it = mConnections.remove(it);
00076                 else
00077                     ++it;
00078             }
00079         }
00080         if (mConnections.isEmpty())
00081         {
00082             mTimer->disconnect();
00083             mTimer->stop();
00084         }
00085     }
00086 }
00087 
00088 
00089 /*=============================================================================
00090 =  Class: MinuteTimer
00091 =  Application-wide timer synchronised to the minute boundary.
00092 =============================================================================*/
00093 
00094 MinuteTimer* MinuteTimer::mInstance = 0;
00095 
00096 MinuteTimer* MinuteTimer::instance()
00097 {
00098     if (!mInstance)
00099         mInstance = new MinuteTimer;
00100     return mInstance;
00101 }
00102 
00103 /******************************************************************************
00104 * Called when the timer triggers, or to start the timer.
00105 * Timers can under some circumstances wander off from the correct trigger time,
00106 * so rather than setting a 1 minute interval, calculate the correct next
00107 * interval each time it triggers.
00108 */
00109 void MinuteTimer::slotTimer()
00110 {
00111     kdDebug(5950) << "MinuteTimer::slotTimer()" << endl;
00112     int interval = 62 - QTime::currentTime().second();
00113     mTimer->start(interval * 1000, true);     // execute a single shot
00114 }
00115 
00116 
00117 /*=============================================================================
00118 =  Class: DailyTimer
00119 =  Application-wide timer synchronised to midnight.
00120 =============================================================================*/
00121 
00122 QValueList<DailyTimer*> DailyTimer::mFixedTimers;
00123 
00124 DailyTimer::DailyTimer(const QTime& timeOfDay, bool fixed)
00125     : mTime(timeOfDay),
00126       mFixed(fixed)
00127 {
00128     if (fixed)
00129         mFixedTimers.append(this);
00130 }
00131 
00132 DailyTimer::~DailyTimer()
00133 {
00134     if (mFixed)
00135         mFixedTimers.remove(this);
00136 }
00137 
00138 DailyTimer* DailyTimer::fixedInstance(const QTime& timeOfDay, bool create)
00139 {
00140     for (QValueList<DailyTimer*>::Iterator it = mFixedTimers.begin();  it != mFixedTimers.end();  ++it)
00141         if ((*it)->mTime == timeOfDay)
00142             return *it;
00143     return create ? new DailyTimer(timeOfDay, true) : 0;
00144 }
00145 
00146 /******************************************************************************
00147 * Disconnect from the timer signal which triggers at the given fixed time of day.
00148 * If there are no remaining connections to that timer, it is destroyed.
00149 */
00150 void DailyTimer::disconnect(const QTime& timeOfDay, QObject* receiver, const char* member)
00151 {
00152     DailyTimer* timer = fixedInstance(timeOfDay, false);
00153     if (!timer)
00154         return;
00155     timer->disconnecT(receiver, member);
00156     if (!timer->hasConnections())
00157         delete timer;
00158 }
00159 
00160 /******************************************************************************
00161 * Change the time at which the variable timer triggers.
00162 */
00163 void DailyTimer::changeTime(const QTime& newTimeOfDay, bool triggerMissed)
00164 {
00165     if (mFixed)
00166         return;
00167     if (mTimer->isActive())
00168     {
00169         mTimer->stop();
00170         bool triggerNow = false;
00171         if (triggerMissed)
00172         {
00173             QTime now = QTime::currentTime();
00174             if (now >= newTimeOfDay  &&  now < mTime)
00175             {
00176                 // The trigger time is now earlier and it has already arrived today.
00177                 // Trigger a timer event immediately.
00178                 triggerNow = true;
00179             }
00180         }
00181         mTime = newTimeOfDay;
00182         if (triggerNow)
00183             mTimer->start(0, true);    // trigger immediately
00184         else
00185             start();
00186     }
00187     else
00188         mTime = newTimeOfDay;
00189 }
00190 
00191 /******************************************************************************
00192 * Initialise the timer to trigger at the specified time.
00193 * This will either be today or tomorrow, depending on whether the trigger time
00194 * has already passed.
00195 */
00196 void DailyTimer::start()
00197 {
00198     // TIMEZONE = local time
00199     QDateTime now = QDateTime::currentDateTime();
00200     // Find out whether to trigger today or tomorrow.
00201     // In preference, use the last trigger date to determine this, since
00202     // that will avoid possible errors due to daylight savings time changes.
00203     bool today;
00204     if (mLastDate.isValid())
00205         today = (mLastDate < now.date());
00206     else
00207         today = (now.time() < mTime);
00208     QDateTime next;
00209     if (today)
00210         next = QDateTime(now.date(), mTime);
00211     else
00212         next = QDateTime(now.date().addDays(1), mTime);
00213     uint interval = next.toTime_t() - now.toTime_t();
00214     mTimer->start(interval * 1000, true);    // execute a single shot
00215     kdDebug(5950) << "DailyTimer::start(at " << mTime.hour() << ":" << mTime.minute() << "): interval = " << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60 << endl;
00216 }
00217 
00218 /******************************************************************************
00219 * Called when the timer triggers.
00220 * Set the timer to trigger again tomorrow at the specified time.
00221 * Note that if daylight savings time changes occur, this will not be 24 hours
00222 * from now.
00223 */
00224 void DailyTimer::slotTimer()
00225 {
00226     // TIMEZONE = local time
00227     QDateTime now = QDateTime::currentDateTime();
00228     mLastDate = now.date();
00229     QDateTime next = QDateTime(mLastDate.addDays(1), mTime);
00230     uint interval = next.toTime_t() - now.toTime_t();
00231     mTimer->start(interval * 1000, true);    // execute a single shot
00232     kdDebug(5950) << "DailyTimer::slotTimer(at " << mTime.hour() << ":" << mTime.minute() << "): interval = " << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60 << endl;
00233 }
KDE Home | KDE Accessibility Home | Description of Access Keys