summaryrefslogtreecommitdiffstats
path: root/kalarm/lib/synchtimer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kalarm/lib/synchtimer.cpp')
-rw-r--r--kalarm/lib/synchtimer.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/kalarm/lib/synchtimer.cpp b/kalarm/lib/synchtimer.cpp
new file mode 100644
index 000000000..1b681d6eb
--- /dev/null
+++ b/kalarm/lib/synchtimer.cpp
@@ -0,0 +1,233 @@
+/*
+ * synchtimer.cpp - timers which synchronise to time boundaries
+ * Program: kalarm
+ * Copyright (C) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include "kalarm.h"
+#include <qtimer.h>
+#include <kdebug.h>
+#include "synchtimer.moc"
+
+
+/*=============================================================================
+= Class: SynchTimer
+= Virtual base class for application-wide timer synchronised to a time boundary.
+=============================================================================*/
+
+SynchTimer::SynchTimer()
+{
+ mTimer = new QTimer(this, "mTimer");
+}
+
+SynchTimer::~SynchTimer()
+{
+ delete mTimer;
+ mTimer = 0;
+}
+
+/******************************************************************************
+* Connect to the timer. The timer is started if necessary.
+*/
+void SynchTimer::connecT(QObject* receiver, const char* member)
+{
+ Connection connection(receiver, member);
+ if (mConnections.find(connection) != mConnections.end())
+ return; // the slot is already connected, so ignore request
+ connect(mTimer, SIGNAL(timeout()), receiver, member);
+ mConnections.append(connection);
+ if (!mTimer->isActive())
+ {
+ connect(mTimer, SIGNAL(timeout()), this, SLOT(slotTimer()));
+ start();
+ }
+}
+
+/******************************************************************************
+* Disconnect from the timer. The timer is stopped if no longer needed.
+*/
+void SynchTimer::disconnecT(QObject* receiver, const char* member)
+{
+ if (mTimer)
+ {
+ mTimer->disconnect(receiver, member);
+ if (member)
+ mConnections.remove(Connection(receiver, member));
+ else
+ {
+ for (QValueList<Connection>::Iterator it = mConnections.begin(); it != mConnections.end(); )
+ {
+ if ((*it).receiver == receiver)
+ it = mConnections.remove(it);
+ else
+ ++it;
+ }
+ }
+ if (mConnections.isEmpty())
+ {
+ mTimer->disconnect();
+ mTimer->stop();
+ }
+ }
+}
+
+
+/*=============================================================================
+= Class: MinuteTimer
+= Application-wide timer synchronised to the minute boundary.
+=============================================================================*/
+
+MinuteTimer* MinuteTimer::mInstance = 0;
+
+MinuteTimer* MinuteTimer::instance()
+{
+ if (!mInstance)
+ mInstance = new MinuteTimer;
+ return mInstance;
+}
+
+/******************************************************************************
+* 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.
+*/
+void MinuteTimer::slotTimer()
+{
+ kdDebug(5950) << "MinuteTimer::slotTimer()" << endl;
+ int interval = 62 - QTime::currentTime().second();
+ mTimer->start(interval * 1000, true); // execute a single shot
+}
+
+
+/*=============================================================================
+= Class: DailyTimer
+= Application-wide timer synchronised to midnight.
+=============================================================================*/
+
+QValueList<DailyTimer*> DailyTimer::mFixedTimers;
+
+DailyTimer::DailyTimer(const QTime& timeOfDay, bool fixed)
+ : mTime(timeOfDay),
+ mFixed(fixed)
+{
+ if (fixed)
+ mFixedTimers.append(this);
+}
+
+DailyTimer::~DailyTimer()
+{
+ if (mFixed)
+ mFixedTimers.remove(this);
+}
+
+DailyTimer* DailyTimer::fixedInstance(const QTime& timeOfDay, bool create)
+{
+ for (QValueList<DailyTimer*>::Iterator it = mFixedTimers.begin(); it != mFixedTimers.end(); ++it)
+ if ((*it)->mTime == timeOfDay)
+ return *it;
+ return create ? new DailyTimer(timeOfDay, true) : 0;
+}
+
+/******************************************************************************
+* 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)
+{
+ DailyTimer* timer = fixedInstance(timeOfDay, false);
+ if (!timer)
+ return;
+ timer->disconnecT(receiver, member);
+ if (!timer->hasConnections())
+ delete timer;
+}
+
+/******************************************************************************
+* Change the time at which the variable timer triggers.
+*/
+void DailyTimer::changeTime(const QTime& newTimeOfDay, bool triggerMissed)
+{
+ 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, true); // trigger immediately
+ else
+ start();
+ }
+ else
+ mTime = newTimeOfDay;
+}
+
+/******************************************************************************
+* 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.
+*/
+void DailyTimer::start()
+{
+ // 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, true); // execute a single shot
+ kdDebug(5950) << "DailyTimer::start(at " << mTime.hour() << ":" << mTime.minute() << "): interval = " << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60 << endl;
+}
+
+/******************************************************************************
+* 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()
+{
+ // 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, true); // execute a single shot
+ kdDebug(5950) << "DailyTimer::slotTimer(at " << mTime.hour() << ":" << mTime.minute() << "): interval = " << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60 << endl;
+}