/*
* Copyright 2013 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, 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, see .
*
* Authors:
* Charles Kerr
*/
#include
#include
namespace unity {
namespace indicator {
namespace datetime {
/***
****
***/
namespace
{
void clearTimer(guint& tag)
{
if (tag)
{
g_source_remove(tag);
tag = 0;
}
}
guint calculate_milliseconds_until_next_minute(const DateTime& now)
{
auto next = g_date_time_add_minutes(now.get(), 1);
auto start_of_next = g_date_time_add_seconds (next, -g_date_time_get_seconds(next));
const auto interval_usec = g_date_time_difference(start_of_next, now.get());
const guint interval_msec = (interval_usec + 999) / 1000;
g_date_time_unref(start_of_next);
g_date_time_unref(next);
g_assert (interval_msec <= 60000);
return interval_msec;
}
} // unnamed namespace
class LiveClock::Impl
{
public:
Impl(LiveClock& owner, const std::shared_ptr& tzd):
m_owner(owner),
m_timezones(tzd)
{
if (m_timezones)
{
m_timezones->timezone.changed().connect([this](const std::string& z) {setTimezone(z);});
setTimezone(m_timezones->timezone.get());
}
restart_minute_timer();
}
~Impl()
{
clearTimer(m_timer);
g_clear_pointer(&m_timezone, g_time_zone_unref);
}
DateTime localtime() const
{
g_assert(m_timezone != nullptr);
auto gdt = g_date_time_new_now(m_timezone);
DateTime ret(gdt);
g_date_time_unref(gdt);
return ret;
}
private:
void setTimezone(const std::string& str)
{
g_clear_pointer(&m_timezone, g_time_zone_unref);
m_timezone = g_time_zone_new(str.c_str());
m_owner.minuteChanged();
}
/***
****
***/
void restart_minute_timer()
{
clearTimer(m_timer);
// maybe emit change signals
const auto now = localtime();
if (!DateTime::is_same_minute(m_prev_datetime, now))
m_owner.minuteChanged();
if (!DateTime::is_same_day(m_prev_datetime, now))
m_owner.dateChanged();
// queue up a timer to fire at the next minute
m_prev_datetime = now;
auto interval_msec = calculate_milliseconds_until_next_minute(now);
interval_msec += 50; // add a small margin to ensure the callback
// fires /after/ next is reached
m_timer = g_timeout_add_full(G_PRIORITY_HIGH,
interval_msec,
on_minute_timer_reached,
this,
nullptr);
}
static gboolean on_minute_timer_reached(gpointer gself)
{
static_cast(gself)->restart_minute_timer();
return G_SOURCE_REMOVE;
}
protected:
LiveClock& m_owner;
GTimeZone* m_timezone = nullptr;
std::shared_ptr m_timezones;
DateTime m_prev_datetime;
unsigned int m_timer = 0;
};
LiveClock::LiveClock(const std::shared_ptr& tzd):
p(new Impl(*this, tzd))
{
}
LiveClock::~LiveClock() =default;
DateTime LiveClock::localtime() const
{
return p->localtime();
}
/***
****
***/
} // namespace datetime
} // namespace indicator
} // namespace unity