Unity 8
timeformatter.cpp
1 /*
2  * Copyright 2013 Canonical Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17  */
18 
19 #include "timeformatter.h"
20 
21 #include <gio/gio.h>
22 #include <QDateTime>
23 
24 struct TimeFormatterPrivate
25 {
26  TimeFormatter *formatter;
27 
28  QString format;
29  QString timeString;
30  qint64 time;
31 
32  GDBusConnection *system_bus;
33  guint subscription_id;
34  GCancellable *cancellable;
35 };
36 
37 static void
38 timedate1_properties_changed (GDBusConnection *connection,
39  const gchar *sender_name,
40  const gchar *object_path,
41  const gchar *interface_name,
42  const gchar *signal_name,
43  GVariant *parameters,
44  gpointer user_data)
45 {
46  Q_UNUSED(connection);
47  Q_UNUSED(sender_name);
48  Q_UNUSED(object_path);
49  Q_UNUSED(interface_name);
50  Q_UNUSED(signal_name);
51 
52  TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
53  GVariant *changed;
54  GVariantIter *iter;
55  const gchar *name;
56 
57  if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
58  return;
59 
60  g_variant_get (parameters, "(s@a{sv}as)", nullptr, &changed, &iter);
61 
62  if (g_variant_lookup (changed, "Timezone", "s", nullptr)) {
63  priv->formatter->update();
64  }
65  else {
66  while (g_variant_iter_next (iter, "&s", &name)) {
67  if (g_str_equal (name, "Timezone")) {
68  priv->formatter->update();
69  break;
70  }
71  }
72  }
73 
74  g_variant_unref (changed);
75  g_variant_iter_free (iter);
76 }
77 
78 static void
79 got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
80 {
81  Q_UNUSED(object);
82 
83  TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
84  GError *error = nullptr;
85 
86  priv->system_bus = g_bus_get_finish (result, &error);
87  if (priv->system_bus == nullptr) {
88  if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
89  qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
90  g_error_free (error);
91  return;
92  }
93 
94  /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
95  * interface from any sender. In practice, this signal will only be sent
96  * from timedated (we can trust other processes on the system bus to behave
97  * nicely). That way, we don't have to watch timedated's well-known name
98  * and keep the process alive.
99  */
100  priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
101  nullptr, /* sender */
102  "org.freedesktop.DBus.Properties",
103  "PropertiesChanged",
104  nullptr,
105  "org.freedesktop.timedate1",
106  G_DBUS_SIGNAL_FLAGS_NONE,
107  timedate1_properties_changed,
108  priv, nullptr);
109 }
110 
111 TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
112 {
113  priv = new TimeFormatterPrivate;
114  priv->formatter = this;
115  priv->time = 0;
116  priv->format = QStringLiteral("yyyy-MM-dd hh:mm");
117  priv->system_bus = nullptr;
118  priv->subscription_id = 0;
119  priv->cancellable = g_cancellable_new ();
120 
121  g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122 }
123 
124 TimeFormatter::TimeFormatter(const QString &initialFormat, QObject *parent): TimeFormatter(parent)
125 {
126  priv->format = initialFormat;
127 }
128 
129 TimeFormatter::~TimeFormatter()
130 {
131  if (priv->system_bus) {
132  g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
133  g_object_unref (priv->system_bus);
134  }
135 
136  g_cancellable_cancel (priv->cancellable);
137  g_object_unref (priv->cancellable);
138 }
139 
140 QString TimeFormatter::format() const
141 {
142  return priv->format;
143 }
144 
145 QString TimeFormatter::timeString() const
146 {
147  return priv->timeString;
148 }
149 
150 qint64 TimeFormatter::time() const
151 {
152  return priv->time;
153 }
154 
155 void TimeFormatter::setFormat(const QString &format)
156 {
157  if (priv->format != format) {
158  priv->format = format;
159  Q_EMIT formatChanged(priv->format);
160  update();
161  }
162 }
163 
164 void TimeFormatter::setTime(qint64 time)
165 {
166  if (priv->time != time) {
167  priv->time = time;
168  Q_EMIT timeChanged(priv->time);
169  update();
170  }
171 }
172 
173 void TimeFormatter::update()
174 {
175  priv->timeString = formatTime();
176  Q_EMIT timeStringChanged(priv->timeString);
177 }
178 
179 QString TimeFormatter::formatTime() const
180 {
181  return QDateTime::fromMSecsSinceEpoch(time() / 1000).toString(format());
182 }
183 
184 GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
185 : TimeFormatter(QStringLiteral("%d-%m-%Y %I:%M%p"), parent)
186 {
187 }
188 
189 QString GDateTimeFormatter::formatTime() const
190 {
191  gchar* time_string;
192  GDateTime* datetime;
193  QByteArray formatBytes = format().toUtf8();
194 
195  datetime = g_date_time_new_from_unix_local(time());
196  if (!datetime) {
197  return QLatin1String("");
198  }
199 
200  time_string = g_date_time_format(datetime, formatBytes.constData());
201  QString formattedTime(QString::fromUtf8(time_string));
202 
203  g_free(time_string);
204  g_date_time_unref(datetime);
205  return formattedTime;
206 }