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 <tqtimer.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 TQTimer(this, "mTimer"); 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(TQObject* 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, TQT_SIGNAL(timeout()), receiver, member); 00052 mConnections.append(connection); 00053 if (!mTimer->isActive()) 00054 { 00055 connect(mTimer, TQT_SIGNAL(timeout()), this, TQT_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(TQObject* 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 (TQValueList<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 - TQTime::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 TQValueList<DailyTimer*> DailyTimer::mFixedTimers; 00123 00124 DailyTimer::DailyTimer(const TQTime& 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 TQTime& timeOfDay, bool create) 00139 { 00140 for (TQValueList<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 TQTime& timeOfDay, TQObject* 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 TQTime& 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 TQTime now = TQTime::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 TQDateTime now = TQDateTime::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 TQDateTime next; 00209 if (today) 00210 next = TQDateTime(now.date(), mTime); 00211 else 00212 next = TQDateTime(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 TQDateTime now = TQDateTime::currentDateTime(); 00228 mLastDate = now.date(); 00229 TQDateTime next = TQDateTime(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 }