aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Gould <ted@gould.cx>2013-10-28 17:09:05 -0700
committerTed Gould <ted@gould.cx>2013-10-28 17:09:05 -0700
commit26283de417c1c3cb3456f1a893fe339056517d56 (patch)
treeb6851a0f12bbeae649854fb5a4a8a85a54d800f1
parent0fa8138427d8469eb2f6a90b0291dbdc4507c8fb (diff)
parent19ba64b479fd14d5192f0ec3dcc37fe33bde238b (diff)
downloadayatana-indicator-datetime-26283de417c1c3cb3456f1a893fe339056517d56.tar.gz
ayatana-indicator-datetime-26283de417c1c3cb3456f1a893fe339056517d56.tar.bz2
ayatana-indicator-datetime-26283de417c1c3cb3456f1a893fe339056517d56.zip
Merge trunk
-rw-r--r--README8
-rw-r--r--configure.ac18
-rw-r--r--data/com.canonical.indicator.datetime3
-rw-r--r--data/gnome-indicator-datetime-panel.desktop.in1
-rw-r--r--debian/changelog122
-rw-r--r--debian/control3
-rwxr-xr-xdebian/rules4
-rw-r--r--src/Makefile.am87
-rw-r--r--src/clock-live.c281
-rw-r--r--src/clock-live.h73
-rw-r--r--src/clock.c110
-rw-r--r--src/clock.h76
-rw-r--r--src/datetime-prefs-locations.c9
-rw-r--r--src/datetime-prefs.c20
-rw-r--r--src/main.c30
-rw-r--r--src/planner-eds.c482
-rw-r--r--src/planner-eds.h2
-rw-r--r--src/planner.c53
-rw-r--r--src/planner.h44
-rw-r--r--src/service.c1090
-rw-r--r--src/service.h15
-rw-r--r--src/settings-shared.h9
-rw-r--r--src/timezone-file.c44
-rw-r--r--src/timezone-geoclue.c55
-rw-r--r--src/timezone.c46
-rw-r--r--src/timezone.h10
-rw-r--r--src/utils.c104
-rw-r--r--src/utils.h14
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/Makefile.am.strings2
-rw-r--r--tests/planner-mock.c178
-rw-r--r--tests/planner-mock.h58
-rw-r--r--tests/test-utils.cc112
33 files changed, 2540 insertions, 629 deletions
diff --git a/README b/README
index 5dc9764..b31db05 100644
--- a/README
+++ b/README
@@ -7,11 +7,17 @@ ACTIONS
Parameter: None
* "activate-planner"
- Description: opens up a calendar appointment editor.
+ Description: opens an appointment editor.
State: None
Parameter: int64, a time_t hinting which day/time to show in the planner,
or 0 for the current day
+ * "activate-appointment"
+ Description: opens an appointment editor to the specified appointment.
+ State: None
+ Parameter: string, an opaque uid to specify which appointment to use.
+ This uid comes from the menuitems' target values.
+
* "set-location"
Description: Set the current location. This will try to set the current
timezone to the new location's timezone.
diff --git a/configure.ac b/configure.ac
index c165a3b..040f222 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,15 +51,21 @@ GEOCLUE_REQUIRED_VERSION=0.12.0
ICAL_REQUIRED_VERSION=0.48
ECAL_REQUIRED_VERSION=3.5
EDS_REQUIRED_VERSION=3.5
+LIBNOTIFY_REQUIRED_VERSION=0.7.6
+URL_DISPATCHER_1_REQUIRED_VERSION=1
+JSON_GLIB_REQUIRED_VERSION=0.16.2
GTK3_REQUIRED_VERSION=3.1.4
-PKG_CHECK_MODULES(SERVICE, glib-2.0 >= $GLIB_REQUIRED_VERSION
- gio-2.0 >= $GIO_REQUIRED_VERSION
- geoclue >= $GEOCLUE_REQUIRED_VERSION
- libical >= $ICAL_REQUIRED_VERSION
- libecal-1.2 >= $ECAL_REQUIRED_VERSION
- libedataserver-1.2 >= EDS_REQUIRED_VERSION)
+PKG_CHECK_MODULES(SERVICE, [glib-2.0 >= $GLIB_REQUIRED_VERSION
+ gio-2.0 >= $GIO_REQUIRED_VERSION
+ geoclue >= $GEOCLUE_REQUIRED_VERSION
+ libical >= $ICAL_REQUIRED_VERSION
+ libecal-1.2 >= $ECAL_REQUIRED_VERSION
+ libedataserver-1.2 >= $EDS_REQUIRED_VERSION
+ libnotify >= $LIBNOTIFY_REQUIRED_VERSION
+ url-dispatcher-1 >= $URL_DISPATCHER_1_REQUIRED_VERSION
+ json-glib-1.0 >= $JSON_GLIB_REQUIRED_VERSION])
###########################
# Control Center panel
diff --git a/data/com.canonical.indicator.datetime b/data/com.canonical.indicator.datetime
index b26b5ec..7fa1e34 100644
--- a/data/com.canonical.indicator.datetime
+++ b/data/com.canonical.indicator.datetime
@@ -12,3 +12,6 @@ ObjectPath=/com/canonical/indicator/datetime/desktop_greeter
[phone]
ObjectPath=/com/canonical/indicator/datetime/phone
+[phone_greeter]
+ObjectPath=/com/canonical/indicator/datetime/desktop_greeter
+
diff --git a/data/gnome-indicator-datetime-panel.desktop.in b/data/gnome-indicator-datetime-panel.desktop.in
index 68571db..b7202b4 100644
--- a/data/gnome-indicator-datetime-panel.desktop.in
+++ b/data/gnome-indicator-datetime-panel.desktop.in
@@ -10,3 +10,4 @@ Type=Application
Categories=GNOME;GTK;Utility;DesktopSettings;Settings;X-GNOME-SystemSettings;X-GNOME-Settings-Panel;
X-GNOME-Settings-Panel=indicator-datetime
OnlyShowIn=Unity;
+X-Ubuntu-Gettext-Domain=indicator-datetime
diff --git a/debian/changelog b/debian/changelog
index 81abd59..fc74793 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,127 @@
-indicator-datetime (13.10.0-0ubuntu1) UNRELEASED; urgency=low
+indicator-datetime (13.10.0+13.10.20131016.2-0ubuntu1) saucy; urgency=low
+ [ Charles Kerr ]
+ * Remove the g_error() call that caused an abort() in the call to
+ on_name_lost() in our bus handler. (LP: #1238737)
+
+ [ Didier Roche ]
+ * remove invalid click recommends and downgrade to Suggests
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 274
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Oct 2013 15:30:10 +0000
+
+indicator-datetime (13.10.0+13.10.20131016-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Use the Unity Mobile icon name for the alarm clock icon.
+ * cache our internal GTimeZone instead of constantly re-creating it.
+ (LP: #1238043)
+ * Changes the phone profile's "Clock" menuitem in two ways: 1. instead
+ of using a stock icon, try to use the clock app's icon. 2. when
+ clicked, launch the clock app. (LP: #1227106)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 271
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Oct 2013 02:35:10 +0000
+
+indicator-datetime (13.10.0+13.10.20131011-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * start tracking failure-to-connect-to-bus errors on indicator-
+ datetime. (LP: #1227519)
+ * When an unsupported date format is being used, improve the error
+ message to also include the unsupported date format so that users
+ can include it in their bug reports. (LP: #1196059)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 267
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 11 Oct 2013 04:27:40 +0000
+
+indicator-datetime (13.10.0+13.10.20131004-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Change the tablet time format string to remove leading zeroes from
+ the hour component of the time format (ie, H:MM rather than HH:MM).
+ (LP: #1234483)
+ * on the phone, show appointments at 1AM as '1 AM', clock times as
+ '1:00 AM'. (LP: #1220297)
+ * Update settings URL to settings:///system. (LP: #1231444)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 264
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 04 Oct 2013 02:29:26 +0000
+
+indicator-datetime (13.10.0+13.10.20130930-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Use url-dispatcher instead of invoking system-settings directly.
+ (LP: #1230819)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 260
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 30 Sep 2013 06:32:59 +0000
+
+indicator-datetime (13.10.0+13.10.20130924.2-0ubuntu1) saucy; urgency=low
+
+ [ Timo Jyrinki ]
+ * Add X-Ubuntu-Gettext-Domain in .desktop file to use translations
+ (LP: #1223498). (LP: #1223498)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 258
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 24 Sep 2013 08:58:52 +0000
+
+indicator-datetime (13.10.0+13.10.20130913-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Make the EDS planner nonblocking. (LP: #1204532)
+ * Minor changes that eliminate unnecessary temporary strings.
+ * This change adds a GSettings* arg to the utils functions so that
+ they don't have to churn through temporary GSettings objects. These
+ functions are usually called in a loop, causing a lot of GSettings
+ temporaries, even though the calling code already has an instance
+ for that GSettings schema.
+ * name says it all; this branch is for improving indicator-datetime
+ test coverage.
+ * Remove code duplication between timezone-file and timezone-geoclue.
+ * Adds a title in the header's action state. (LP: #1223635)
+
+ [ Ted Gould ]
+ * Fix GeoClue signal handler to have proper prototype. (LP: #1195874)
+ * Protect against invalid begin or end dates. (LP: #1216263)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 256
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 13 Sep 2013 15:16:38 +0000
+
+indicator-datetime (13.10.0+13.10.20130903-0ubuntu1) saucy; urgency=low
+
+ [ Jeremy Bicha ]
+ * Really don't install *.la file.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 247
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 03 Sep 2013 02:08:17 +0000
+
+indicator-datetime (13.10.0+13.10.20130828.2-0ubuntu1) saucy; urgency=low
+
+ [ Ted Gould ]
* Updating version to make the Conflicts/Replace nicer
* Split out gnome-control-center panel into it's own package
- -- Ted Gould <ted@ubuntu.com> Tue, 27 Aug 2013 10:26:47 -0500
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 245
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 28 Aug 2013 10:09:19 +0000
indicator-datetime (12.10.3+13.10.20130822.1-0ubuntu1) saucy; urgency=low
diff --git a/debian/control b/debian/control
index ae07129..548f7b0 100644
--- a/debian/control
+++ b/debian/control
@@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 9),
libxorg-gtest-dev,
libgtest-dev,
libglib2.0-dev (>= 2.35.4),
+ libnotify-dev (>= 0.7.6),
libido3-0.1-dev (>= 0.2.90),
libgeoclue-dev (>= 0.12.0),
libecal1.2-dev (>= 3.5),
@@ -22,6 +23,7 @@ Build-Depends: debhelper (>= 9),
libgconf2-dev (>= 2.31),
libgnome-control-center-dev,
libtimezonemap1-dev,
+ liburl-dispatcher1-dev,
Standards-Version: 3.9.3
Homepage: https://launchpad.net/indicator-datetime
# If you aren't a member of ~indicator-applet-developers but need to upload
@@ -40,6 +42,7 @@ Depends: ${shlibs:Depends},
Recommends: indicator-applet | indicator-renderer,
evolution-data-server,
gnome-control-center-datetime | ubuntu-system-settings,
+Suggests: click,
Conflicts: indicator-datetime (<< 13.10.0)
Replaces: indicator-datetime (<< 13.10.0)
Description: Simple clock
diff --git a/debian/rules b/debian/rules
index a350b13..25854b8 100755
--- a/debian/rules
+++ b/debian/rules
@@ -12,10 +12,8 @@ override_dh_auto_configure:
dh_auto_configure -- --disable-static --disable-silent-rules
override_dh_install:
- find debian/indicator-datetime -name \*.la -delete
- find debian/indicator-datetime -name \*.a -delete
cd po; intltool-update --pot --verbose
- dh_install --fail-missing
+ dh_install -X.la --fail-missing
# Language packs
for d in $$(find debian/indicator-datetime -type f \( -name "*.desktop" -o -name "*.directory" \) ); do \
sed -ri '/^(Name|GenericName|Comment|X-GNOME-FullName)\[/d' $$d; \
diff --git a/src/Makefile.am b/src/Makefile.am
index 2f247c1..be7eb4d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,44 +1,71 @@
-if BUILD_CCPANEL
-ccpaneldir = $(libdir)/control-center-1/panels/
-ccpanel_LTLIBRARIES = libindicator-datetime.la
-endif
+SHARED_CFLAGS = \
+ -Wall \
+ -Wextra -Wno-missing-field-initializers \
+ -Werror \
+ $(SERVICE_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ -DTIMEZONE_FILE="\"/etc/timezone\"" \
+ -DG_LOG_DOMAIN=\"Indicator-Datetime\"
+
+###
+###
+###
+
+noinst_LIBRARIES = libindicator-datetime-service.a
+
+libindicator_datetime_service_a_CFLAGS = \
+ $(SHARED_CFLAGS)
+
+libindicator_datetime_service_a_SOURCES = \
+ clock.c \
+ clock.h \
+ clock-live.c \
+ clock-live.h \
+ planner.c \
+ planner.h \
+ planner-eds.c \
+ planner-eds.h \
+ service.c \
+ service.h \
+ timezone.c \
+ timezone.h \
+ timezone-file.c \
+ timezone-file.h \
+ timezone-geoclue.c \
+ timezone-geoclue.h \
+ utils.c \
+ utils.h \
+ dbus-shared.h \
+ settings-shared.h
+
+###
+###
+###
libexec_PROGRAMS = indicator-datetime-service
indicator_datetime_service_SOURCES = \
- planner.c \
- planner.h \
- planner-eds.c \
- planner-eds.h \
- service.c \
- service.h \
- main.c \
- timezone.c \
- timezone.h \
- timezone-file.c \
- timezone-file.h \
- timezone-geoclue.c \
- timezone-geoclue.h \
- utils.c \
- utils.h \
- dbus-shared.h \
- settings-shared.h
+ main.c
indicator_datetime_service_CFLAGS = \
- -Wall \
- -Wextra -Wno-missing-field-initializers \
- -Werror \
- $(SERVICE_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- -DTIMEZONE_FILE="\"/etc/timezone\"" \
- -DG_LOG_DOMAIN=\"Indicator-Datetime\"
+ $(SHARED_CFLAGS)
+
indicator_datetime_service_LDADD = \
- $(SERVICE_LIBS)
+ libindicator-datetime-service.a \
+ $(SERVICE_LIBS)
+
indicator_datetime_service_LDFLAGS = \
- $(COVERAGE_LDFLAGS)
+ $(COVERAGE_LDFLAGS)
+
+###
+###
+###
if BUILD_CCPANEL
+ccpaneldir = $(libdir)/control-center-1/panels/
+ccpanel_LTLIBRARIES = libindicator-datetime.la
+
libindicator_datetime_la_SOURCES =\
datetime-prefs.c \
datetime-prefs-locations.c \
diff --git a/src/clock-live.c b/src/clock-live.c
new file mode 100644
index 0000000..4153747
--- /dev/null
+++ b/src/clock-live.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "config.h"
+
+#include "clock-live.h"
+#include "settings-shared.h"
+#include "timezone-file.h"
+#include "timezone-geoclue.h"
+
+/***
+**** private struct
+***/
+
+struct _IndicatorDatetimeClockLivePriv
+{
+ GSettings * settings;
+
+ GSList * timezones; /* IndicatorDatetimeTimezone */
+ gchar ** timezones_strv;
+ GTimeZone * localtime_zone;
+};
+
+typedef IndicatorDatetimeClockLivePriv priv_t;
+
+/***
+**** GObject boilerplate
+***/
+
+static void indicator_datetime_clock_interface_init (
+ IndicatorDatetimeClockInterface * iface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ IndicatorDatetimeClockLive,
+ indicator_datetime_clock_live,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_DATETIME_CLOCK,
+ indicator_datetime_clock_interface_init));
+
+/***
+**** Timezones
+***/
+
+static void
+on_current_timezone_changed (IndicatorDatetimeClockLive * self)
+{
+ priv_t * p = self->priv;
+
+ /* Invalidate the timezone information.
+ These fields will be lazily regenerated by rebuild_timezones() */
+ g_clear_pointer (&p->timezones_strv, g_strfreev);
+ g_clear_pointer (&p->localtime_zone, g_time_zone_unref);
+
+ indicator_datetime_clock_emit_changed (INDICATOR_DATETIME_CLOCK (self));
+}
+
+static void
+set_detect_location_enabled (IndicatorDatetimeClockLive * self, gboolean enabled)
+{
+ GSList * l;
+ priv_t * p = self->priv;
+ gboolean changed = FALSE;
+
+ /* clear out the old timezone objects */
+ if (p->timezones != NULL)
+ {
+ for (l=p->timezones; l!=NULL; l=l->next)
+ {
+ g_signal_handlers_disconnect_by_func (l->data, on_current_timezone_changed, self);
+ g_object_unref (l->data);
+ }
+
+ g_slist_free (p->timezones);
+ p->timezones = NULL;
+ changed = TRUE;
+ }
+
+ /* maybe add new timezone objects */
+ if (enabled)
+ {
+ p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_geoclue_new ());
+ p->timezones = g_slist_append (p->timezones, indicator_datetime_timezone_file_new (TIMEZONE_FILE));
+
+ for (l=p->timezones; l!=NULL; l=l->next)
+ {
+ g_signal_connect_swapped (l->data, "notify::timezone",
+ G_CALLBACK(on_current_timezone_changed), self);
+ }
+
+ changed = TRUE;
+ }
+
+ if (changed)
+ on_current_timezone_changed (self);
+}
+
+/* When the 'auto-detect timezone' boolean setting changes,
+ start or stop watching geoclue and /etc/timezone */
+static void
+on_detect_location_changed (IndicatorDatetimeClockLive * self)
+{
+ const gboolean enabled = g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_DETECTED_S);
+ set_detect_location_enabled (self, enabled);
+}
+
+/***
+**** IndicatorDatetimeClock virtual functions
+***/
+
+static void
+rebuild_timezones (IndicatorDatetimeClockLive * self)
+{
+ priv_t * p;
+ GHashTable * hash;
+ GSList * l;
+ int i;
+ GHashTableIter iter;
+ gpointer key;
+
+ p = self->priv;
+
+ /* Build a hashtable of timezone strings.
+ This will weed out duplicates. */
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ for (l=p->timezones; l!=NULL; l=l->next)
+ {
+ const gchar * tz = indicator_datetime_timezone_get_timezone (l->data);
+ if (tz && *tz)
+ g_hash_table_add (hash, (gpointer) tz);
+ }
+
+ /* rebuild p->timezone_strv */
+ g_strfreev (p->timezones_strv);
+ p->timezones_strv = g_new0 (gchar*, g_hash_table_size(hash) + 1);
+ i = 0;
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ p->timezones_strv[i++] = g_strdup (key);
+
+ /* rebuild localtime_zone */
+ g_clear_pointer (&p->localtime_zone, g_time_zone_unref);
+ p->localtime_zone = g_time_zone_new (p->timezones_strv ? p->timezones_strv[0] : NULL);
+
+ /* cleanup */
+ g_hash_table_unref (hash);
+}
+
+static const gchar **
+my_get_timezones (IndicatorDatetimeClock * clock)
+{
+ IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock);
+ priv_t * p = self->priv;
+
+ if (G_UNLIKELY (p->timezones_strv == NULL))
+ rebuild_timezones (self);
+
+ return (const gchar **) p->timezones_strv;
+}
+
+static GDateTime *
+my_get_localtime (IndicatorDatetimeClock * clock)
+{
+ IndicatorDatetimeClockLive * self = INDICATOR_DATETIME_CLOCK_LIVE (clock);
+ priv_t * p = self->priv;
+
+ if (G_UNLIKELY (p->localtime_zone == NULL))
+ rebuild_timezones (self);
+
+ return g_date_time_new_now (p->localtime_zone);
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE(o);
+ p = self->priv;
+
+ if (p->settings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->settings, self);
+ g_clear_object (&p->settings);
+ }
+
+ set_detect_location_enabled (self, FALSE);
+
+ G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorDatetimeClockLive * self;
+ priv_t * p;
+
+ self = INDICATOR_DATETIME_CLOCK_LIVE(o);
+ p = self->priv;
+
+ g_clear_pointer (&p->localtime_zone, g_time_zone_unref);
+ g_strfreev (p->timezones_strv);
+
+ G_OBJECT_CLASS (indicator_datetime_clock_live_parent_class)->dispose (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_datetime_clock_live_class_init (IndicatorDatetimeClockLiveClass * klass)
+{
+ GObjectClass * object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+
+ g_type_class_add_private (klass,
+ sizeof (IndicatorDatetimeClockLivePriv));
+}
+
+static void
+indicator_datetime_clock_interface_init (IndicatorDatetimeClockInterface * iface)
+{
+ iface->get_localtime = my_get_localtime;
+ iface->get_timezones = my_get_timezones;
+}
+
+static void
+indicator_datetime_clock_live_init (IndicatorDatetimeClockLive * self)
+{
+ IndicatorDatetimeClockLivePriv * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE,
+ IndicatorDatetimeClockLivePriv);
+ self->priv = p;
+
+ p->settings = g_settings_new (SETTINGS_INTERFACE);
+ g_signal_connect (p->settings, "changed::" SETTINGS_SHOW_DETECTED_S,
+ G_CALLBACK(on_detect_location_changed), self);
+
+
+ on_detect_location_changed (self);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorDatetimeClock *
+indicator_datetime_clock_live_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_CLOCK_LIVE, NULL);
+
+ return INDICATOR_DATETIME_CLOCK (o);
+}
diff --git a/src/clock-live.h b/src/clock-live.h
new file mode 100644
index 0000000..4425f5b
--- /dev/null
+++ b/src/clock-live.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INDICATOR_DATETIME_CLOCK_LIVE__H__
+#define __INDICATOR_DATETIME_CLOCK_LIVE__H__
+
+#include <glib-object.h> /* parent class */
+
+#include "clock.h"
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_CLOCK_LIVE \
+ (indicator_datetime_clock_live_get_type())
+
+#define INDICATOR_DATETIME_CLOCK_LIVE(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), \
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \
+ IndicatorDatetimeClockLive))
+
+#define INDICATOR_DATETIME_CLOCK_LIVE_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), \
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE, \
+ IndicatorDatetimeClockLiveClass))
+
+#define INDICATOR_IS_DATETIME_CLOCK_LIVE(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), \
+ INDICATOR_TYPE_DATETIME_CLOCK_LIVE))
+
+typedef struct _IndicatorDatetimeClockLive
+ IndicatorDatetimeClockLive;
+typedef struct _IndicatorDatetimeClockLivePriv
+ IndicatorDatetimeClockLivePriv;
+typedef struct _IndicatorDatetimeClockLiveClass
+ IndicatorDatetimeClockLiveClass;
+
+/**
+ * An IndicatorDatetimeClock which gives live clock times
+ * from timezones determined by geoclue and /etc/timezone
+ */
+struct _IndicatorDatetimeClockLive
+{
+ GObject parent_instance;
+
+ IndicatorDatetimeClockLivePriv * priv;
+};
+
+struct _IndicatorDatetimeClockLiveClass
+{
+ GObjectClass parent_class;
+};
+
+IndicatorDatetimeClock * indicator_datetime_clock_live_new (void);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_CLOCK_LIVE__H__ */
diff --git a/src/clock.c b/src/clock.c
new file mode 100644
index 0000000..2c2fec2
--- /dev/null
+++ b/src/clock.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "clock.h"
+
+enum
+{
+ SIGNAL_CHANGED,
+ SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+G_DEFINE_INTERFACE (IndicatorDatetimeClock,
+ indicator_datetime_clock,
+ G_TYPE_OBJECT);
+
+static void
+indicator_datetime_clock_default_init (IndicatorDatetimeClockInterface * klass)
+{
+ signals[SIGNAL_CHANGED] = g_signal_new (
+ "changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IndicatorDatetimeClockInterface, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/***
+**** PUBLIC API
+***/
+
+/**
+ * Get a strv of timezones.
+ *
+ * Return value: (element-type char*)
+ * (transfer full):
+ * array of timezone strings
+ */
+const gchar **
+indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * self)
+{
+ const gchar ** timezones;
+ IndicatorDatetimeClockInterface * iface;
+
+ g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL);
+ iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self);
+
+ if (iface->get_timezones != NULL)
+ timezones = iface->get_timezones (self);
+ else
+ timezones = NULL;
+
+ return timezones;
+}
+
+/**
+ * Get the current time.
+ *
+ * Return value: (element-type GDateTime*)
+ * (transfer full):
+ * the current time.
+ */
+GDateTime *
+indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * self)
+{
+ GDateTime * now;
+ IndicatorDatetimeClockInterface * iface;
+
+ g_return_val_if_fail (INDICATOR_IS_DATETIME_CLOCK(self), NULL);
+ iface = INDICATOR_DATETIME_CLOCK_GET_INTERFACE(self);
+
+ if (iface->get_localtime != NULL)
+ now = iface->get_localtime (self);
+ else
+ now = NULL;
+
+ return now;
+}
+
+/**
+ * Emits the "changed" signal.
+ *
+ * This should only be called by subclasses.
+ */
+void
+indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * self)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_CLOCK (self));
+
+ g_signal_emit (self, signals[SIGNAL_CHANGED], 0, NULL);
+}
diff --git a/src/clock.h b/src/clock.h
new file mode 100644
index 0000000..40cdf1c
--- /dev/null
+++ b/src/clock.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INDICATOR_DATETIME_CLOCK__H__
+#define __INDICATOR_DATETIME_CLOCK__H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_CLOCK \
+ (indicator_datetime_clock_get_type ())
+
+#define INDICATOR_DATETIME_CLOCK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ INDICATOR_TYPE_DATETIME_CLOCK, \
+ IndicatorDatetimeClock))
+
+#define INDICATOR_IS_DATETIME_CLOCK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_DATETIME_CLOCK))
+
+#define INDICATOR_DATETIME_CLOCK_GET_INTERFACE(inst) \
+ (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \
+ INDICATOR_TYPE_DATETIME_CLOCK, \
+ IndicatorDatetimeClockInterface))
+
+typedef struct _IndicatorDatetimeClock
+ IndicatorDatetimeClock;
+
+typedef struct _IndicatorDatetimeClockInterface
+ IndicatorDatetimeClockInterface;
+
+struct _IndicatorDatetimeClockInterface
+{
+ GTypeInterface parent_iface;
+
+ /* signals */
+ void (*changed) (IndicatorDatetimeClock * self);
+
+ /* virtual functions */
+ const gchar** (*get_timezones) (IndicatorDatetimeClock * self);
+ GDateTime* (*get_localtime) (IndicatorDatetimeClock * self);
+};
+
+GType indicator_datetime_clock_get_type (void);
+
+/***
+****
+***/
+
+const gchar ** indicator_datetime_clock_get_timezones (IndicatorDatetimeClock * clock);
+
+GDateTime * indicator_datetime_clock_get_localtime (IndicatorDatetimeClock * clock);
+
+void indicator_datetime_clock_emit_changed (IndicatorDatetimeClock * clock);
+
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_CLOCK__H__ */
diff --git a/src/datetime-prefs-locations.c b/src/datetime-prefs-locations.c
index bc044a2..f953ec7 100644
--- a/src/datetime-prefs-locations.c
+++ b/src/datetime-prefs-locations.c
@@ -96,7 +96,9 @@ time_location_array_new_from_model (GtkTreeModel * model)
COL_ZONE, &zone,
COL_VISIBLE_NAME, &name,
-1);
- list = g_slist_prepend (list, time_location_new (zone, name, pos++, now));
+
+ if (zone && name)
+ list = g_slist_prepend (list, time_location_new (zone, name, pos++, now));
g_free (name);
g_free (zone);
@@ -417,6 +419,7 @@ update_times (GtkWidget * dlg)
g_signal_handlers_block_by_func (store, save_when_idle, dlg);
+ GSettings * settings = g_settings_new (SETTINGS_INTERFACE);
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
GDateTime * now = g_date_time_new_now_local ();
@@ -428,7 +431,7 @@ update_times (GtkWidget * dlg)
if (strzone && *strzone) {
GTimeZone * tz = g_time_zone_new (strzone);
GDateTime * now_tz = g_date_time_to_timezone (now, tz);
- gchar * format = generate_full_format_string_at_time (now, now_tz);
+ gchar * format = generate_full_format_string_at_time (now, now_tz, settings);
gchar * time_str = g_date_time_format (now_tz, format);
gchar * old_time_str;
@@ -447,6 +450,8 @@ update_times (GtkWidget * dlg)
g_date_time_unref (now);
}
+ g_object_unref (settings);
+
g_signal_handlers_unblock_by_func (store, save_when_idle, dlg);
return TRUE;
diff --git a/src/datetime-prefs.c b/src/datetime-prefs.c
index 25c9b2a..9b48ca9 100644
--- a/src/datetime-prefs.c
+++ b/src/datetime-prefs.c
@@ -70,6 +70,7 @@ struct _IndicatorDatetimePanelPrivate
gboolean user_edited_time;
gboolean changing_time;
GtkWidget * loc_dlg;
+ GSettings * settings;
CcTimezoneCompletion * completion;
};
@@ -214,7 +215,7 @@ toggle_ntp (GtkWidget * radio, GParamSpec * pspec, IndicatorDatetimePanel * self
static void
sync_entry (IndicatorDatetimePanel * self, const gchar * location)
{
- gchar * name = get_current_zone_name (location);
+ gchar * name = get_current_zone_name (location, self->priv->settings);
gtk_entry_set_text (GTK_ENTRY (self->priv->tz_entry), name);
g_free (name);
@@ -599,11 +600,9 @@ timezone_selected (GtkEntryCompletion * widget, GtkTreeModel * model,
zone = cc_timezone_map_get_timezone_at_coords (self->priv->tzmap, lon, lat);
}
- GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
gchar * tz_name = g_strdup_printf ("%s %s", zone, name);
- g_settings_set_string (conf, SETTINGS_TIMEZONE_NAME_S, tz_name);
+ g_settings_set_string (self->priv->settings, SETTINGS_TIMEZONE_NAME_S, tz_name);
g_free (tz_name);
- g_object_unref (conf);
cc_timezone_map_set_timezone (self->priv->tzmap, zone);
@@ -623,7 +622,7 @@ entry_focus_out (GtkEntry * entry, GdkEventFocus * event, IndicatorDatetimePanel
gchar * zone;
g_object_get (location, "zone", &zone, NULL);
- gchar * name = get_current_zone_name (zone);
+ gchar * name = get_current_zone_name (zone, self->priv->settings);
gboolean correct = (g_strcmp0 (gtk_entry_get_text (entry), name) == 0);
g_free (name);
g_free (zone);
@@ -639,14 +638,18 @@ entry_focus_out (GtkEntry * entry, GdkEventFocus * event, IndicatorDatetimePanel
static void
indicator_datetime_panel_init (IndicatorDatetimePanel * self)
{
+ GError * error;
+ GSettings * conf;
+
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
INDICATOR_DATETIME_TYPE_PANEL,
IndicatorDatetimePanelPrivate);
- GError * error = NULL;
+ self->priv->settings = conf = g_settings_new (SETTINGS_INTERFACE);
self->priv->builder = gtk_builder_new ();
gtk_builder_set_translation_domain (self->priv->builder, GETTEXT_PACKAGE);
+ error = NULL;
gtk_builder_add_from_file (self->priv->builder, DATETIME_DIALOG_UI_FILE, &error);
if (error != NULL) {
/* We have to abort, we can't continue without the ui file */
@@ -655,8 +658,6 @@ indicator_datetime_panel_init (IndicatorDatetimePanel * self)
return;
}
- GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
-
/* Add policykit button */
GtkWidget * polkit_button = gtk_lock_button_new (NULL);
@@ -755,8 +756,6 @@ indicator_datetime_panel_init (IndicatorDatetimePanel * self)
#undef WIG
- g_object_unref (conf);
-
gtk_widget_show_all (panel);
gtk_container_add (GTK_CONTAINER (self), panel);
}
@@ -769,6 +768,7 @@ indicator_datetime_panel_dispose (GObject * object)
g_clear_object (&priv->builder);
g_clear_object (&priv->proxy);
+ g_clear_object (&priv->settings);
if (priv->loc_dlg) {
gtk_widget_destroy (priv->loc_dlg);
diff --git a/src/main.c b/src/main.c
index c75b2d7..022df00 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,7 +24,10 @@
#include <glib/gi18n.h>
#include <gio/gio.h>
+#include <libnotify/notify.h>
+#include "clock-live.h"
+#include "planner-eds.h"
#include "service.h"
/***
@@ -35,29 +38,48 @@ static void
on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
{
g_message ("exiting: service couldn't acquire or lost ownership of busname");
+
g_main_loop_quit ((GMainLoop*)loop);
}
int
main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
{
- GMainLoop * loop;
+ IndicatorDatetimeClock * clock;
+ IndicatorDatetimePlanner * planner;
IndicatorDatetimeService * service;
+ GMainLoop * loop;
+
+ /* Work around a deadlock in glib's type initialization. It can be
+ * removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is
+ * fixed.
+ */
+ g_type_ensure (G_TYPE_DBUS_CONNECTION);
/* boilerplate i18n */
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
textdomain (GETTEXT_PACKAGE);
+ /* init libnotify */
+ if (!notify_init ("indicator-datetime-service"))
+ g_critical ("libnotify initialization failed");
+
+ /* create the service */
+ clock = indicator_datetime_clock_live_new ();
+ planner = indicator_datetime_planner_eds_new ();
+ service = indicator_datetime_service_new (clock, planner);
+
/* run */
- service = indicator_datetime_service_new ();
loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST,
G_CALLBACK(on_name_lost), loop);
g_main_loop_run (loop);
+ g_main_loop_unref (loop);
/* cleanup */
- g_clear_object (&service);
- g_main_loop_unref (loop);
+ g_object_unref (service);
+ g_object_unref (planner);
+ g_object_unref (clock);
return 0;
}
diff --git a/src/planner-eds.c b/src/planner-eds.c
index 276058d..876fdfc 100644
--- a/src/planner-eds.c
+++ b/src/planner-eds.c
@@ -19,8 +19,6 @@
#include "config.h"
-#include <gio/gio.h> /* GFile, GFileMonitor */
-
#include <libical/ical.h>
#include <libical/icaltime.h>
#include <libecal/libecal.h>
@@ -30,6 +28,8 @@
struct _IndicatorDatetimePlannerEdsPriv
{
+ GSList * sources;
+ GCancellable * cancellable;
ESourceRegistry * source_registry;
};
@@ -39,44 +39,195 @@ G_DEFINE_TYPE (IndicatorDatetimePlannerEds,
indicator_datetime_planner_eds,
INDICATOR_TYPE_DATETIME_PLANNER)
+G_DEFINE_QUARK ("source-client", source_client)
+
/***
****
+**** my_get_appointments() helpers
+****
***/
-void
-indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt)
+/* whole-task data that all the subtasks can see */
+struct appointment_task_data
+{
+ /* a ref to the planner's cancellable */
+ GCancellable * cancellable;
+
+ /* how many subtasks are still running on */
+ int subtask_count;
+
+ /* the list of appointments to be returned */
+ GSList * appointments;
+};
+
+static struct appointment_task_data *
+appointment_task_data_new (GCancellable * cancellable)
+{
+ struct appointment_task_data * data;
+
+ data = g_slice_new0 (struct appointment_task_data);
+ data->cancellable = g_object_ref (cancellable);
+ return data;
+}
+
+static void
+appointment_task_data_free (gpointer gdata)
+{
+ struct appointment_task_data * data = gdata;
+
+ g_object_unref (data->cancellable);
+
+ g_slice_free (struct appointment_task_data, data);
+}
+
+static void
+appointment_task_done (GTask * task)
+{
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
+ g_task_return_pointer (task, data->appointments, NULL);
+ g_object_unref (task);
+}
+
+static void
+appointment_task_decrement_subtasks (GTask * task)
+{
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
+ if (g_atomic_int_dec_and_test (&data->subtask_count))
+ appointment_task_done (task);
+}
+
+static void
+appointment_task_increment_subtasks (GTask * task)
+{
+ struct appointment_task_data * data = g_task_get_task_data (task);
+
+ g_atomic_int_inc (&data->subtask_count);
+}
+
+/**
+*** get-the-appointment's-uri subtasks
+**/
+
+struct appointment_uri_subtask_data
+{
+ /* The parent task */
+ GTask * task;
+
+ /* The appointment whose uri we're looking for.
+ This pointer is owned by the Task and isn't reffed/unreffed by the subtask */
+ struct IndicatorDatetimeAppt * appt;
+};
+
+static void
+appointment_uri_subtask_done (struct appointment_uri_subtask_data * subdata)
{
- if (appt != NULL)
+ GTask * task = subdata->task;
+
+ /* free the subtask data */
+ g_slice_free (struct appointment_uri_subtask_data, subdata);
+
+ appointment_task_decrement_subtasks (task);
+}
+
+static struct appointment_uri_subtask_data *
+appointment_uri_subtask_data_new (GTask * task, struct IndicatorDatetimeAppt * appt)
+{
+ struct appointment_uri_subtask_data * subdata;
+
+ appointment_task_increment_subtasks (task);
+
+ subdata = g_slice_new0 (struct appointment_uri_subtask_data);
+ subdata->task = task;
+ subdata->appt = appt;
+ return subdata;
+}
+
+static void
+on_appointment_uris_ready (GObject * client,
+ GAsyncResult * res,
+ gpointer gsubdata)
+{
+ GSList * uris;
+ GError * error;
+ struct appointment_uri_subtask_data * subdata = gsubdata;
+
+ uris = NULL;
+ error = NULL;
+ e_cal_client_get_attachment_uris_finish (E_CAL_CLIENT(client), res, &uris, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Error getting appointment uris: %s", error->message);
+
+ g_error_free (error);
+ }
+ else if (uris != NULL)
{
- g_date_time_unref (appt->end);
- g_date_time_unref (appt->begin);
- g_free (appt->color);
- g_free (appt->summary);
- g_free (appt);
+ struct IndicatorDatetimeAppt * appt = subdata->appt;
+ appt->url = g_strdup (uris->data); /* copy the first URL */
+ g_debug ("found url '%s' for appointment '%s'", appt->url, appt->summary);
+ e_client_util_free_string_slist (uris);
}
+
+ appointment_uri_subtask_done (subdata);
}
-/***
-**** my_get_appointments() helpers
-***/
+/**
+*** enumerate-the-components subtasks
+**/
-struct my_get_appointments_data
+/* data struct for the enumerate-components subtask */
+struct appointment_component_subtask_data
{
- ESource * source;
- GSList * appointments;
+ /* The parent task */
+ GTask * task;
+
+ /* The client we're walking through. The subtask owns a ref to this */
+ ECalClient * client;
- /* ensure that recurring events don't get multiple IndicatorDatetimeAppts */
- GHashTable * added;
+ /* The appointment's color coding. The subtask owns this string */
+ gchar * color;
};
+static void
+on_appointment_component_subtask_done (gpointer gsubdata)
+{
+ struct appointment_component_subtask_data * subdata = gsubdata;
+ GTask * task = subdata->task;
+
+ /* free the subtask data */
+ g_free (subdata->color);
+ g_object_unref (subdata->client);
+ g_slice_free (struct appointment_component_subtask_data, subdata);
+
+ appointment_task_decrement_subtasks (task);
+}
+
+static struct appointment_component_subtask_data *
+appointment_component_subtask_data_new (GTask * task, ECalClient * client, const gchar * color)
+{
+ struct appointment_component_subtask_data * subdata;
+
+ appointment_task_increment_subtasks (task);
+
+ subdata = g_slice_new0 (struct appointment_component_subtask_data);
+ subdata->task = task;
+ subdata->client = g_object_ref (client);
+ subdata->color = g_strdup (color);
+ return subdata;
+}
+
static gboolean
my_get_appointments_foreach (ECalComponent * component,
time_t begin,
time_t end,
- gpointer gdata)
+ gpointer gsubdata)
{
const ECalComponentVType vtype = e_cal_component_get_vtype (component);
- struct my_get_appointments_data * data = gdata;
+ struct appointment_component_subtask_data * subdata = gsubdata;
+ struct appointment_task_data * data = g_task_get_task_data (subdata->task);
if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO))
{
@@ -87,7 +238,6 @@ my_get_appointments_foreach (ECalComponent * component,
e_cal_component_get_status (component, &status);
if ((uid != NULL) &&
- (!g_hash_table_contains (data->added, uid)) &&
(status != ICAL_STATUS_COMPLETED) &&
(status != ICAL_STATUS_CANCELLED))
{
@@ -96,8 +246,9 @@ my_get_appointments_foreach (ECalComponent * component,
GSList * recur_list;
ECalComponentText text;
struct IndicatorDatetimeAppt * appt;
+ struct appointment_uri_subtask_data * uri_subdata;
- appt = g_new0 (struct IndicatorDatetimeAppt, 1);
+ appt = g_slice_new0 (struct IndicatorDatetimeAppt);
/* Determine whether this is a recurring event.
NB: icalrecurrencetype supports complex recurrence patterns;
@@ -117,40 +268,50 @@ my_get_appointments_foreach (ECalComponent * component,
appt->begin = g_date_time_new_from_unix_local (begin);
appt->end = g_date_time_new_from_unix_local (end);
- appt->color = e_source_selectable_dup_color (e_source_get_extension (data->source, E_SOURCE_EXTENSION_CALENDAR));
+ appt->color = g_strdup (subdata->color);
appt->is_event = vtype == E_CAL_COMPONENT_EVENT;
appt->summary = g_strdup (text.value);
+ appt->uid = g_strdup (uid);
alarm_uids = e_cal_component_get_alarm_uids (component);
appt->has_alarms = alarm_uids != NULL;
cal_obj_uid_list_free (alarm_uids);
data->appointments = g_slist_prepend (data->appointments, appt);
- g_hash_table_add (data->added, g_strdup(uid));
+
+ /* start a new subtask to get the associated URIs */
+ uri_subdata = appointment_uri_subtask_data_new (subdata->task, appt);
+ e_cal_client_get_attachment_uris (subdata->client,
+ uid,
+ NULL,
+ data->cancellable,
+ on_appointment_uris_ready,
+ uri_subdata);
}
}
return G_SOURCE_CONTINUE;
}
-
/***
**** IndicatorDatetimePlanner virtual funcs
***/
-static GSList *
+static void
my_get_appointments (IndicatorDatetimePlanner * planner,
GDateTime * begin_datetime,
- GDateTime * end_datetime)
+ GDateTime * end_datetime,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GList * l;
- GList * sources;
+ GSList * l;
priv_t * p;
const char * str;
icaltimezone * default_timezone;
- struct my_get_appointments_data data;
const int64_t begin = g_date_time_to_unix (begin_datetime);
const int64_t end = g_date_time_to_unix (end_datetime);
+ GTask * task;
+ gboolean subtasks_added;
p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv;
@@ -172,60 +333,55 @@ my_get_appointments (IndicatorDatetimePlanner * planner,
*** walk through the sources to build the appointment list
**/
- data.source = NULL;
- data.appointments = NULL;
- data.added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ task = g_task_new (planner, p->cancellable, callback, user_data);
+ g_task_set_task_data (task,
+ appointment_task_data_new (p->cancellable),
+ appointment_task_data_free);
- sources = e_source_registry_list_sources (p->source_registry, E_SOURCE_EXTENSION_CALENDAR);
- for (l=sources; l!=NULL; l=l->next)
+ subtasks_added = FALSE;
+ for (l=p->sources; l!=NULL; l=l->next)
{
- GError * err;
ESource * source;
- ECalClient * ecc;
-
- source = E_SOURCE (l->data);
- if (e_source_get_enabled (source))
- {
- err = NULL;
- ecc = e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, &err);
- if (err != NULL)
- {
- g_warning ("Can't create ecal client: %s", err->message);
- g_error_free (err);
- }
- else
- {
- if (!e_client_open_sync (E_CLIENT (ecc), TRUE, NULL, &err))
- {
- g_debug ("Failed to open ecal client: %s", err->message);
- g_error_free (err);
- }
- else
- {
- if (default_timezone != NULL)
- e_cal_client_set_default_timezone (ecc, default_timezone);
-
- data.source = source;
- e_cal_client_generate_instances_sync (ecc, begin, end, my_get_appointments_foreach, &data);
- }
-
- g_object_unref (ecc);
- }
- }
+ ECalClient * client;
+ const char * color;
+ struct appointment_component_subtask_data * subdata;
+
+ source = l->data;
+ client = g_object_get_qdata (l->data, source_client_quark());
+ if (client == NULL)
+ continue;
+
+ if (default_timezone != NULL)
+ e_cal_client_set_default_timezone (client, default_timezone);
+
+ /* start a new subtask to enumerate all the components in this client. */
+ color = e_source_selectable_get_color (e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR));
+ subdata = appointment_component_subtask_data_new (task, client, color);
+ subtasks_added = TRUE;
+ e_cal_client_generate_instances (client,
+ begin,
+ end,
+ p->cancellable,
+ my_get_appointments_foreach,
+ subdata,
+ on_appointment_component_subtask_done);
}
- g_list_free_full (sources, g_object_unref);
+ if (!subtasks_added)
+ appointment_task_done (task);
+}
- g_debug ("%s EDS get_appointments returning %d appointments", G_STRLOC, g_slist_length (data.appointments));
- g_hash_table_destroy (data.added);
- return data.appointments;
+static GSList *
+my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
+ GAsyncResult * res,
+ GError ** error)
+{
+ return g_task_propagate_pointer (G_TASK(res), error);
}
-gboolean
+static gboolean
my_is_configured (IndicatorDatetimePlanner * planner)
{
- GList * sources;
- gboolean have_sources;
IndicatorDatetimePlannerEds * self;
/* confirm that it's installed... */
@@ -238,10 +394,7 @@ my_is_configured (IndicatorDatetimePlanner * planner)
/* see if there are any calendar sources */
self = INDICATOR_DATETIME_PLANNER_EDS (planner);
- sources = e_source_registry_list_sources (self->priv->source_registry, E_SOURCE_EXTENSION_CALENDAR);
- have_sources = sources != NULL;
- g_list_free_full (sources, g_object_unref);
- return have_sources;
+ return self->priv->sources != NULL;
}
static void
@@ -279,6 +432,148 @@ my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
}
/***
+**** Source / Client Wrangling
+***/
+
+static void
+on_client_connected (GObject * unused G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ EClient * client;
+
+ error = NULL;
+ client = e_cal_client_connect_finish (res, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("indicator-datetime cannot connect to EDS source: %s", error->message);
+
+ g_error_free (error);
+ }
+ else
+ {
+ /* we've got a new connected ECalClient, so store it & notify clients */
+
+ g_object_set_qdata_full (G_OBJECT(e_client_get_source(client)),
+ source_client_quark(),
+ client,
+ g_object_unref);
+
+ indicator_datetime_planner_emit_appointments_changed (gself);
+ }
+}
+
+static void
+on_source_enabled (ESourceRegistry * registry G_GNUC_UNUSED,
+ ESource * source,
+ gpointer gself)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ priv_t * p = self->priv;
+
+ e_cal_client_connect (source,
+ E_CAL_CLIENT_SOURCE_TYPE_EVENTS,
+ p->cancellable,
+ on_client_connected,
+ self);
+}
+
+static void
+on_source_added (ESourceRegistry * registry,
+ ESource * source,
+ gpointer gself)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ priv_t * p = self->priv;
+
+ p->sources = g_slist_prepend (p->sources, g_object_ref(source));
+
+ if (e_source_get_enabled (source))
+ on_source_enabled (registry, source, gself);
+}
+
+static void
+on_source_disabled (ESourceRegistry * registry G_GNUC_UNUSED,
+ ESource * source,
+ gpointer gself)
+{
+ ECalClient * client;
+
+ /* If this source has a connected ECalClient, remove it & notify clients */
+ if ((client = g_object_steal_qdata (G_OBJECT(source), source_client_quark())))
+ {
+ g_object_unref (client);
+ indicator_datetime_planner_emit_appointments_changed (gself);
+ }
+}
+
+static void
+on_source_removed (ESourceRegistry * registry,
+ ESource * source,
+ gpointer gself)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ priv_t * p = self->priv;
+
+ on_source_disabled (registry, source, gself);
+
+ p->sources = g_slist_remove (p->sources, source);
+ g_object_unref (source);
+}
+
+static void
+on_source_changed (ESourceRegistry * registry G_GNUC_UNUSED,
+ ESource * source G_GNUC_UNUSED,
+ gpointer gself)
+{
+ indicator_datetime_planner_emit_appointments_changed (gself);
+}
+
+static void
+on_source_registry_ready (GObject * source_object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ ESourceRegistry * r;
+
+ error = NULL;
+ r = e_source_registry_new_finish (res, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("indicator-datetime cannot show EDS appointments: %s", error->message);
+
+ g_error_free (error);
+ }
+ else
+ {
+ IndicatorDatetimePlannerEds * self;
+ priv_t * p;
+ GList * l;
+ GList * sources;
+
+ self = INDICATOR_DATETIME_PLANNER_EDS (gself);
+ p = self->priv;
+
+ g_signal_connect (r, "source-added", G_CALLBACK(on_source_added), self);
+ g_signal_connect (r, "source-removed", G_CALLBACK(on_source_removed), self);
+ g_signal_connect (r, "source-changed", G_CALLBACK(on_source_changed), self);
+ g_signal_connect (r, "source-disabled", G_CALLBACK(on_source_disabled), self);
+ g_signal_connect (r, "source-enabled", G_CALLBACK(on_source_enabled), self);
+
+ p->source_registry = r;
+
+ sources = e_source_registry_list_sources (r, E_SOURCE_EXTENSION_CALENDAR);
+ for (l=sources; l!=NULL; l=l->next)
+ on_source_added (r, l->data, self);
+ g_list_free_full (sources, g_object_unref);
+ }
+}
+
+/***
**** GObject virtual funcs
***/
@@ -288,6 +583,12 @@ my_dispose (GObject * o)
IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o);
priv_t * p = self->priv;
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel (p->cancellable);
+ g_clear_object (&p->cancellable);
+ }
+
if (p->source_registry != NULL)
{
g_signal_handlers_disconnect_by_func (p->source_registry,
@@ -301,7 +602,7 @@ my_dispose (GObject * o)
}
/***
-**** Insantiation
+**** Instantiation
***/
static void
@@ -318,6 +619,7 @@ indicator_datetime_planner_eds_class_init (IndicatorDatetimePlannerEdsClass * kl
planner_class->activate = my_activate;
planner_class->activate_time = my_activate_time;
planner_class->get_appointments = my_get_appointments;
+ planner_class->get_appointments_finish = my_get_appointments_finish;
g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv));
}
@@ -326,7 +628,6 @@ static void
indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self)
{
priv_t * p;
- GError * err;
p = G_TYPE_INSTANCE_GET_PRIVATE (self,
INDICATOR_TYPE_DATETIME_PLANNER_EDS,
@@ -334,22 +635,11 @@ indicator_datetime_planner_eds_init (IndicatorDatetimePlannerEds * self)
self->priv = p;
- err = 0;
- p->source_registry = e_source_registry_new_sync (NULL, &err);
- if (err != NULL)
- {
- g_warning ("indicator-datetime cannot show EDS appointments: %s", err->message);
- g_error_free (err);
- }
- else
- {
- gpointer o = p->source_registry;
- g_signal_connect_swapped (o, "source-added", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
- g_signal_connect_swapped (o, "source-removed", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
- g_signal_connect_swapped (o, "source-changed", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
- g_signal_connect_swapped (o, "source-disabled", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
- g_signal_connect_swapped (o, "source-enabled", G_CALLBACK(indicator_datetime_planner_emit_appointments_changed), self);
- }
+ p->cancellable = g_cancellable_new ();
+
+ e_source_registry_new (p->cancellable,
+ on_source_registry_ready,
+ self);
}
/***
diff --git a/src/planner-eds.h b/src/planner-eds.h
index a2c803a..dea9371 100644
--- a/src/planner-eds.h
+++ b/src/planner-eds.h
@@ -51,8 +51,6 @@ struct _IndicatorDatetimePlannerEdsClass
IndicatorDatetimePlannerClass parent_class;
};
-gboolean indicator_datetime_planner_eds_is_usable (void);
-
IndicatorDatetimePlanner * indicator_datetime_planner_eds_new (void);
G_END_DECLS
diff --git a/src/planner.c b/src/planner.c
index 1643651..9b9a77f 100644
--- a/src/planner.c
+++ b/src/planner.c
@@ -178,17 +178,46 @@ compare_appointments_by_start_time (gconstpointer ga, gconstpointer gb)
return g_date_time_compare (a->begin, b->begin);
}
+void
+indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self,
+ GDateTime * begin,
+ GDateTime * end,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IndicatorDatetimePlannerClass * klass;
+
+ g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self));
+ g_return_val_if_fail (begin != NULL, NULL);
+ g_return_val_if_fail (end != NULL, NULL);
+
+ klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self);
+ g_return_if_fail (klass->get_appointments != NULL);
+ klass->get_appointments (self, begin, end, callback, user_data);
+}
+
GSList *
-indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end)
+indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self,
+ GAsyncResult * res,
+ GError ** error)
{
+ IndicatorDatetimePlannerClass * klass;
GSList * appointments;
g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL);
- appointments = INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->get_appointments (self, begin, end);
+ klass = INDICATOR_DATETIME_PLANNER_GET_CLASS (self);
+ g_return_val_if_fail (klass->get_appointments_finish != NULL, NULL);
+ appointments = klass->get_appointments_finish (self, res, error);
return g_slist_sort (appointments, compare_appointments_by_start_time);
}
+void
+indicator_datetime_planner_free_appointments (GSList * l)
+{
+ g_slist_free_full (l, (GDestroyNotify)indicator_datetime_appt_free);
+}
+
gboolean
indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self)
{
@@ -230,3 +259,23 @@ indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self)
return self->priv->timezone;
}
+
+/***
+****
+***/
+
+void
+indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt)
+{
+ if (appt != NULL)
+ {
+ g_date_time_unref (appt->end);
+ g_date_time_unref (appt->begin);
+ g_free (appt->color);
+ g_free (appt->summary);
+ g_free (appt->url);
+ g_free (appt->uid);
+ g_slice_free (struct IndicatorDatetimeAppt, appt);
+ }
+}
+
diff --git a/src/planner.h b/src/planner.h
index f6148c6..ffe8937 100644
--- a/src/planner.h
+++ b/src/planner.h
@@ -22,6 +22,7 @@
#include <glib.h>
#include <glib-object.h> /* parent class */
+#include <gio/gio.h>
G_BEGIN_DECLS
@@ -39,8 +40,10 @@ GType indicator_datetime_planner_get_type (void);
struct IndicatorDatetimeAppt
{
- char * color;
- char * summary;
+ gchar * color;
+ gchar * summary;
+ gchar * url;
+ gchar * uid;
GDateTime * begin;
GDateTime * end;
gboolean is_event;
@@ -70,7 +73,16 @@ struct _IndicatorDatetimePlannerClass
/* virtual functions */
- GSList* (*get_appointments) (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end);
+ void (*get_appointments) (IndicatorDatetimePlanner * self,
+ GDateTime * begin,
+ GDateTime * end,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+ GSList* (*get_appointments_finish) (IndicatorDatetimePlanner * self,
+ GAsyncResult * res,
+ GError ** error);
+
gboolean (*is_configured) (IndicatorDatetimePlanner * self);
void (*activate) (IndicatorDatetimePlanner * self);
@@ -85,17 +97,33 @@ void indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt);
/**
* Get a list of appointments, sorted by start time.
+ */
+void indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self,
+ GDateTime * begin,
+ GDateTime * end,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+/**
+ * Finishes the async call begun with indicator_datetime_planner_get_appointments()
*
- * An easy way to free the list properly in one step is as follows:
- *
- * g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free);
- *
+ * To free the list properly, use indicator_datetime_planner_free_appointments()
*
* Return value: (element-type IndicatorDatetimeAppt)
* (transfer full):
* list of appointments
*/
-GSList * indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end);
+GSList * indicator_datetime_planner_get_appointments_finish (IndicatorDatetimePlanner * self,
+ GAsyncResult * res,
+ GError ** error);
+
+/**
+ * Convenience function for freeing a GSList of IndicatorDatetimeAppt.
+ *
+ * Equivalent to g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free);
+ */
+void indicator_datetime_planner_free_appointments (GSList *);
+
/**
* Returns false if the planner's backend is not configured.
diff --git a/src/service.c b/src/service.c
index fec2bb1..5fffc11 100644
--- a/src/service.c
+++ b/src/service.c
@@ -24,17 +24,18 @@
#include <glib/gi18n.h>
#include <gio/gio.h>
+#include <libnotify/notify.h>
+#include <json-glib/json-glib.h>
+#include <url-dispatcher.h>
#include "dbus-shared.h"
-#include "planner-eds.h"
-#include "timezone-file.h"
-#include "timezone-geoclue.h"
#include "service.h"
#include "settings-shared.h"
#include "utils.h"
#define SKEW_CHECK_INTERVAL_SEC 10
#define SKEW_DIFF_THRESHOLD_USEC ((SKEW_CHECK_INTERVAL_SEC+5) * G_USEC_PER_SEC)
+#define ALARM_CLOCK_ICON_NAME "alarm-clock"
G_DEFINE_TYPE (IndicatorDatetimeService,
indicator_datetime_service,
@@ -50,6 +51,16 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum
{
+ PROP_0,
+ PROP_CLOCK,
+ PROP_PLANNER,
+ PROP_LAST
+};
+
+static GParamSpec * properties[PROP_LAST] = { 0 };
+
+enum
+{
SECTION_HEADER = (1<<0),
SECTION_CALENDAR = (1<<1),
SECTION_APPOINTMENTS = (1<<2),
@@ -89,10 +100,18 @@ struct _IndicatorDatetimeServicePrivate
GSettings * settings;
- IndicatorDatetimeTimezone * tz_file;
- IndicatorDatetimeTimezone * tz_geoclue;
+ IndicatorDatetimeClock * clock;
IndicatorDatetimePlanner * planner;
+ /* the clock app's icon filename */
+ gchar * clock_app_icon_filename;
+
+ gchar * header_label_format_string;
+
+ /* Whether or not we've tried to load the clock app's icon.
+ This way we don't keep trying to reload it on the desktop */
+ gboolean clock_app_icon_initialized;
+
guint own_id;
guint actions_export_id;
GDBusConnection * conn;
@@ -106,6 +125,7 @@ struct _IndicatorDatetimeServicePrivate
guint header_timer;
guint timezone_timer;
+ guint alarm_timer;
/* Which year/month to show in the calendar,
and which day should get the cursor.
@@ -118,6 +138,22 @@ struct _IndicatorDatetimeServicePrivate
GSimpleAction * calendar_action;
GDBusProxy * login1_manager;
+
+ /* all the appointments in the selected calendar_date's month.
+ Used when populating the 'appointment-days' entry in
+ create_calendar_state() */
+ GSList * calendar_appointments;
+
+ /* appointments over the next few weeks.
+ Used when building SECTION_APPOINTMENTS */
+ GSList * upcoming_appointments;
+
+ /* variant cache */
+ GVariant * desktop_title_variant;
+ GVariant * phone_title_variant;
+ GVariant * visible_true_variant;
+ GVariant * visible_false_variant;
+ GVariant * alarm_icon_variant;
};
typedef IndicatorDatetimeServicePrivate priv_t;
@@ -136,6 +172,12 @@ indicator_clear_timer (guint * tag)
}
}
+static inline GDateTime *
+indicator_datetime_service_get_localtime (IndicatorDatetimeService * self)
+{
+ return indicator_datetime_clock_get_localtime (self->priv->clock);
+}
+
/***
****
***/
@@ -146,6 +188,8 @@ static void rebuild_soon (IndicatorDatetimeService * self, int section);
static inline void
rebuild_header_soon (IndicatorDatetimeService * self)
{
+ g_clear_pointer (&self->priv->header_label_format_string, g_free);
+
rebuild_soon (self, SECTION_HEADER);
}
@@ -218,7 +262,7 @@ calculate_seconds_until_next_fifteen_minutes (GDateTime * now)
g_date_time_get_day_of_month (next),
g_date_time_get_hour (next),
g_date_time_get_minute (next),
- 1);
+ 0.1);
str = g_date_time_format (start_of_next, "%F %T");
g_debug ("%s %s the next timestamp rebuild will be at %s", G_STRLOC, G_STRFUNC, str);
@@ -290,7 +334,7 @@ calculate_milliseconds_until_next_minute (GDateTime * now)
g_date_time_get_day_of_month (next),
g_date_time_get_hour (next),
g_date_time_get_minute (next),
- 0);
+ 0.1);
interval_usec = g_date_time_difference (start_of_next, now);
interval_msec = (interval_usec + 999) / 1000;
@@ -328,7 +372,7 @@ on_header_timer (gpointer gself)
return G_SOURCE_REMOVE;
}
-static char * get_header_label_format_string (IndicatorDatetimeService *);
+static const char * get_header_label_format_string (IndicatorDatetimeService *);
static void
start_header_timer (IndicatorDatetimeService * self)
@@ -342,11 +386,10 @@ start_header_timer (IndicatorDatetimeService * self)
if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S))
{
- char * fmt = get_header_label_format_string (self);
+ const char * fmt = get_header_label_format_string (self);
header_shows_seconds = fmt && (strstr(fmt,"%s") || strstr(fmt,"%S") ||
strstr(fmt,"%T") || strstr(fmt,"%X") ||
strstr(fmt,"%c"));
- g_free (fmt);
}
if (header_shows_seconds)
@@ -366,6 +409,197 @@ start_header_timer (IndicatorDatetimeService * self)
g_date_time_unref (now);
}
+/***
+**** ALARMS
+***/
+
+static void set_alarm_timer (IndicatorDatetimeService * self);
+
+static gboolean
+appointment_has_alarm_url (const struct IndicatorDatetimeAppt * appt)
+{
+ return (appt->has_alarms) &&
+ (appt->url != NULL) &&
+ (g_str_has_prefix (appt->url, "alarm:///"));
+}
+
+static gboolean
+datetimes_have_the_same_minute (GDateTime * a G_GNUC_UNUSED, GDateTime * b G_GNUC_UNUSED)
+{
+ int ay, am, ad;
+ int by, bm, bd;
+
+ g_date_time_get_ymd (a, &ay, &am, &ad);
+ g_date_time_get_ymd (b, &by, &bm, &bd);
+
+ return (ay == by) &&
+ (am == bm) &&
+ (ad == bd) &&
+ (g_date_time_get_hour (a) == g_date_time_get_hour (b)) &&
+ (g_date_time_get_minute (a) == g_date_time_get_minute (b));
+}
+
+static void
+dispatch_alarm_url (const struct IndicatorDatetimeAppt * appt)
+{
+ gchar * str;
+
+ g_return_if_fail (appt != NULL);
+ g_return_if_fail (appointment_has_alarm_url (appt));
+
+ str = g_date_time_format (appt->begin, "%F %T");
+ g_debug ("dispatching url \"%s\" for appointment \"%s\", which begins at %s",
+ appt->url, appt->summary, str);
+ g_free (str);
+
+ url_dispatch_send (appt->url, NULL, NULL);
+}
+
+static void
+on_snap_decided (NotifyNotification * notification G_GNUC_UNUSED,
+ char * action,
+ gpointer gurl)
+{
+ g_debug ("%s: %s", G_STRFUNC, action);
+
+ if (!g_strcmp0 (action, "show"))
+ {
+ const gchar * url = gurl;
+ g_debug ("dispatching url '%s'", url);
+ url_dispatch_send (url, NULL, NULL);
+ }
+}
+
+static void
+show_snap_decision_for_alarm (const struct IndicatorDatetimeAppt * appt)
+{
+ gchar * title;
+ const gchar * body;
+ const gchar * icon_name;
+ NotifyNotification * nn;
+ GError * error;
+
+ title = g_date_time_format (appt->begin,
+ get_terse_time_format_string (appt->begin));
+ body = appt->summary;
+ icon_name = ALARM_CLOCK_ICON_NAME;
+ g_debug ("creating a snap decision with title '%s', body '%s', icon '%s'",
+ title, body, icon_name);
+
+ nn = notify_notification_new (title, body, icon_name);
+ notify_notification_set_hint_string (nn,
+ "x-canonical-snap-decisions",
+ "true");
+ notify_notification_set_hint_string (nn,
+ "x-canonical-private-button-tint",
+ "true");
+ notify_notification_add_action (nn, "show", _("Show"),
+ on_snap_decided, g_strdup(appt->url), g_free);
+ notify_notification_add_action (nn, "dismiss", _("Dismiss"),
+ on_snap_decided, NULL, NULL);
+
+ error = NULL;
+ notify_notification_show (nn, &error);
+ if (error != NULL)
+ {
+ g_warning ("Unable to show alarm '%s' popup: %s", body, error->message);
+ g_error_free (error);
+ dispatch_alarm_url (appt);
+ }
+
+ g_free (title);
+}
+
+static void update_appointment_lists (IndicatorDatetimeService * self);
+
+static gboolean
+on_alarm_timer (gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ GDateTime * now;
+ GSList * l;
+
+ /* If there are any alarms at the current time, show a snap decision */
+ now = indicator_datetime_service_get_localtime (self);
+ for (l=self->priv->upcoming_appointments; l!=NULL; l=l->next)
+ {
+ const struct IndicatorDatetimeAppt * appt = l->data;
+
+ if (appointment_has_alarm_url (appt))
+ if (datetimes_have_the_same_minute (now, appt->begin))
+ show_snap_decision_for_alarm (appt);
+ }
+ g_date_time_unref (now);
+
+ /* rebuild the alarm list asynchronously.
+ set_upcoming_appointments() will update the alarm timer when this
+ async call is done, so no need to restart the timer here... */
+ update_appointment_lists (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* if there are upcoming alarms, set the alarm timer to the nearest one.
+ otherwise, unset the alarm timer. */
+static void
+set_alarm_timer (IndicatorDatetimeService * self)
+{
+ priv_t * p;
+ GDateTime * now;
+ GDateTime * alarm_time;
+ GSList * l;
+
+ p = self->priv;
+ indicator_clear_timer (&p->alarm_timer);
+
+ now = indicator_datetime_service_get_localtime (self);
+
+ /* find the time of the next alarm on our calendar */
+ alarm_time = NULL;
+ for (l=p->upcoming_appointments; l!=NULL; l=l->next)
+ {
+ const struct IndicatorDatetimeAppt * appt = l->data;
+
+ if (appointment_has_alarm_url (appt))
+ if (g_date_time_compare (appt->begin, now) > 0)
+ if (!alarm_time || g_date_time_compare (alarm_time, appt->begin) > 0)
+ alarm_time = appt->begin;
+ }
+
+ /* if there's an upcoming alarm, set a timer to wake up at that time */
+ if (alarm_time != NULL)
+ {
+ GTimeSpan interval_msec;
+ gchar * str;
+ GDateTime * then;
+
+ interval_msec = g_date_time_difference (alarm_time, now);
+ interval_msec += G_USEC_PER_SEC; /* fire a moment after alarm_time */
+ interval_msec /= 1000; /* convert from usec to msec */
+
+ str = g_date_time_format (alarm_time, "%F %T");
+ g_debug ("%s is the next alarm time", str);
+ g_free (str);
+ then = g_date_time_add_seconds (now, interval_msec/1000);
+ str = g_date_time_format (then, "%F %T");
+ g_debug ("%s is when we'll wake up for it", str);
+ g_free (str);
+ g_date_time_unref (then);
+
+ p->alarm_timer = g_timeout_add_full (G_PRIORITY_HIGH,
+ (guint) interval_msec,
+ on_alarm_timer,
+ self,
+ NULL);
+ }
+
+ g_date_time_unref (now);
+}
+
+/***
+****
+***/
+
/**
* General purpose handler for rebuilding sections and restarting their timers
* when time jumps for whatever reason:
@@ -415,68 +649,46 @@ skew_timer_func (gpointer gself)
****
***/
-typedef enum
-{
- TIME_FORMAT_MODE_LOCALE_DEFAULT,
- TIME_FORMAT_MODE_12_HOUR,
- TIME_FORMAT_MODE_24_HOUR,
- TIME_FORMAT_MODE_CUSTOM
-}
-TimeFormatMode;
-
-/* gets the user's time-format from GSettings */
-static TimeFormatMode
-get_time_format_mode (IndicatorDatetimeService * self)
-{
- char * str;
- TimeFormatMode mode;
-
- str = g_settings_get_string (self->priv->settings, SETTINGS_TIME_FORMAT_S);
-
- if (!g_strcmp0 ("12-hour", str))
- mode = TIME_FORMAT_MODE_12_HOUR;
- else if (!g_strcmp0 ("24-hour", str))
- mode = TIME_FORMAT_MODE_24_HOUR;
- else if (!g_strcmp0 ("custom", str))
- mode = TIME_FORMAT_MODE_CUSTOM;
- else
- mode = TIME_FORMAT_MODE_LOCALE_DEFAULT;
-
- g_free (str);
- return mode;
-}
-
-static gchar *
+static const gchar *
get_header_label_format_string (IndicatorDatetimeService * self)
{
- char * fmt;
- const TimeFormatMode mode = get_time_format_mode (self);
- GSettings * s = self->priv->settings;
+ priv_t * p = self->priv;
- if (mode == TIME_FORMAT_MODE_CUSTOM)
- {
- fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S);
- }
- else
+ if (p->header_label_format_string == NULL)
{
- gboolean show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S);
- gboolean show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S);
- fmt = generate_full_format_string (show_day, show_date);
+ char * fmt;
+ GSettings * s = p->settings;
+ const TimeFormatMode mode = g_settings_get_enum (s, SETTINGS_TIME_FORMAT_S);
+
+ if (mode == TIME_FORMAT_MODE_CUSTOM)
+ {
+ fmt = g_settings_get_string (s, SETTINGS_CUSTOM_TIME_FORMAT_S);
+ }
+ else
+ {
+ gboolean show_day = g_settings_get_boolean (s, SETTINGS_SHOW_DAY_S);
+ gboolean show_date = g_settings_get_boolean (s, SETTINGS_SHOW_DATE_S);
+ fmt = generate_full_format_string (show_day, show_date, s);
+ }
+
+ p->header_label_format_string = fmt;
}
- return fmt;
+ return p->header_label_format_string;
}
static GVariant *
create_desktop_header_state (IndicatorDatetimeService * self)
{
+ priv_t * p = self->priv;
GVariantBuilder b;
- gchar * fmt;
+ const gchar * fmt;
gchar * str;
gboolean visible;
GDateTime * now;
+ GVariant * label_variant;
- visible = g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S);
+ visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_CLOCK_S);
/* build the time string for the label & a11y */
fmt = get_header_label_format_string (self);
@@ -484,19 +696,19 @@ create_desktop_header_state (IndicatorDatetimeService * self)
str = g_date_time_format (now, fmt);
if (str == NULL)
{
- str = g_strdup (_("Unsupported date format"));
+ str = g_strdup_printf (_("Unsupported date format “%s”"), fmt);
g_warning ("%s", str);
}
- g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
- g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_string (str));
- g_variant_builder_add (&b, "{sv}", "label", g_variant_new_string (str));
- g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (visible));
+ label_variant = g_variant_new_take_string (str);
+ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", label_variant);
+ g_variant_builder_add (&b, "{sv}", "label", label_variant);
+ g_variant_builder_add_value (&b, p->desktop_title_variant);
+ g_variant_builder_add_value (&b, visible ? p->visible_true_variant : p->visible_false_variant);
/* cleanup */
g_date_time_unref (now);
- g_free (str);
- g_free (fmt);
return g_variant_builder_end (&b);
}
@@ -506,44 +718,37 @@ service_has_alarms (IndicatorDatetimeService * self);
static GVariant *
create_phone_header_state (IndicatorDatetimeService * self)
{
+ priv_t * p = self->priv;
+ const gboolean has_alarms = service_has_alarms (self);
GVariantBuilder b;
GDateTime * now;
const gchar * fmt;
- gchar * label;
- gboolean has_alarms;
- gchar * a11y;
- g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
-
- /* label */
- now = indicator_datetime_service_get_localtime (self);
- fmt = get_terse_time_format_string (now);
- label = g_date_time_format (now, fmt);
- g_variant_builder_add (&b, "{sv}", "label", g_variant_new_string (label));
+ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add_value (&b, p->phone_title_variant);
+ g_variant_builder_add_value (&b, p->visible_true_variant);
/* icon */
- if ((has_alarms = service_has_alarms (self)))
- {
- GIcon * icon;
- icon = g_themed_icon_new_with_default_fallbacks ("alarm-symbolic");
- g_variant_builder_add (&b, "{sv}", "icon", g_icon_serialize (icon));
- g_object_unref (icon);
- }
+ if (has_alarms)
+ g_variant_builder_add_value (&b, p->alarm_icon_variant);
- /* a11y */
+ /* label, a11y */
+ now = indicator_datetime_service_get_localtime (self);
+ fmt = get_terse_header_time_format_string ();
if (has_alarms)
- a11y = g_strdup_printf (_("%s (has alarms)"), label);
+ {
+ gchar * label = g_date_time_format (now, fmt);
+ gchar * a11y = g_strdup_printf (_("%s (has alarms)"), label);
+ g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (label));
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_take_string (a11y));
+ }
else
- a11y = g_strdup (label);
- g_variant_builder_add (&b, "{sv}", "accessible-desc",
- g_variant_new_string (a11y));
-
- /* visible */
- g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (TRUE));
+ {
+ GVariant * v = g_variant_new_take_string (g_date_time_format (now, fmt));
+ g_variant_builder_add (&b, "{sv}", "label", v);
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", v);
+ }
- /* cleanup */
- g_free (a11y);
- g_free (label);
g_date_time_unref (now);
return g_variant_builder_end (&b);
}
@@ -569,38 +774,6 @@ get_calendar_date (IndicatorDatetimeService * self)
return date;
}
-static GSList *
-get_all_appointments_this_month (IndicatorDatetimeService * self)
-{
- GSList * appointments = NULL;
- priv_t * p = self->priv;
-
- if (p->planner != NULL)
- {
- GDateTime * calendar_date;
- GDateTime * begin;
- GDateTime * end;
- int y, m, d;
-
- calendar_date = get_calendar_date (self);
- g_date_time_get_ymd (calendar_date, &y, &m, &d);
- begin = g_date_time_new_local (y, m, 1,
- 0, 0, 0);
- end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y),
- 23, 59, 0);
-
- appointments = indicator_datetime_planner_get_appointments (p->planner,
- begin,
- end);
-
- g_date_time_unref (end);
- g_date_time_unref (begin);
- g_date_time_unref (calendar_date);
- }
-
- return appointments;
-}
-
static GVariant *
create_calendar_state (IndicatorDatetimeService * self)
{
@@ -611,15 +784,13 @@ create_calendar_state (IndicatorDatetimeService * self)
GVariantBuilder day_builder;
GDateTime * date;
GSList * l;
- GSList * appts;
gboolean b;
priv_t * p = self->priv;
g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_DICTIONARY);
key = "appointment-days";
- appts = get_all_appointments_this_month (self);
- for (l=appts; l!=NULL; l=l->next)
+ for (l=p->calendar_appointments; l!=NULL; l=l->next)
{
const struct IndicatorDatetimeAppt * appt = l->data;
days[g_date_time_get_day_of_month (appt->begin)] = TRUE;
@@ -630,7 +801,6 @@ create_calendar_state (IndicatorDatetimeService * self)
g_variant_builder_add (&day_builder, "i", i);
g_variant_builder_add (&dict_builder, "{sv}", key,
g_variant_builder_end (&day_builder));
- g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
key = "calendar-day";
date = get_calendar_date (self);
@@ -678,10 +848,8 @@ add_localtime_menuitem (GMenu * menu,
static void
add_calendar_menuitem (GMenu * menu)
{
- char * label;
GMenuItem * menu_item;
- label = g_strdup ("[calendar]");
menu_item = g_menu_item_new ("[calendar]", NULL);
g_menu_item_set_action_and_target_value (menu_item, "indicator.calendar", g_variant_new_int64(0));
g_menu_item_set_attribute (menu_item, "x-canonical-type", "s", "com.canonical.indicator.calendar");
@@ -689,7 +857,6 @@ add_calendar_menuitem (GMenu * menu)
g_menu_append_item (menu, menu_item);
g_object_unref (menu_item);
- g_free (label);
}
static GMenuModel *
@@ -711,11 +878,8 @@ create_phone_calendar_section (IndicatorDatetimeService * self)
{
GMenu * menu = g_menu_new ();
- /* strftime(3) format string to show day of week */
- add_localtime_menuitem (menu, self, _("%A"), NULL);
-
/* strftime(3) format string to show date */
- add_localtime_menuitem (menu, self, _("%e %B %Y"), "calendar");
+ add_localtime_menuitem (menu, self, _("%A, %e %B %Y"), "calendar");
return G_MENU_MODEL (menu);
}
@@ -726,38 +890,6 @@ create_phone_calendar_section (IndicatorDatetimeService * self)
****
***/
-/* gets the next MAX_APPTS appointments */
-static GSList *
-get_upcoming_appointments (IndicatorDatetimeService * self)
-{
- const int MAX_APPTS = 5;
- GSList * l;
- GSList * appts = NULL;
- priv_t * p = self->priv;
-
- if (p->planner != NULL)
- {
- GDateTime * begin = get_calendar_date (self);
- GDateTime * end = g_date_time_add_months (begin, 1);
-
- appts = indicator_datetime_planner_get_appointments (p->planner,
- begin,
- end);
-
- g_date_time_unref (end);
- g_date_time_unref (begin);
- }
-
- /* truncate at MAX_APPTS */
- if ((l = g_slist_nth (appts, MAX_APPTS-1)))
- {
- g_slist_free_full (l->next, (GDestroyNotify)indicator_datetime_appt_free);
- l->next = NULL;
- }
-
- return appts;
-}
-
static gboolean
service_has_alarms (IndicatorDatetimeService * self)
{
@@ -765,7 +897,7 @@ service_has_alarms (IndicatorDatetimeService * self)
GSList * appts;
GSList * l;
- appts = get_upcoming_appointments (self);
+ appts = self->priv->upcoming_appointments;
for (l=appts; l!=NULL; l=l->next)
{
struct IndicatorDatetimeAppt * appt = l->data;
@@ -773,13 +905,13 @@ service_has_alarms (IndicatorDatetimeService * self)
break;
}
- g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
return has_alarms;
}
static char *
get_appointment_time_format (struct IndicatorDatetimeAppt * appt,
GDateTime * now,
+ GSettings * settings,
gboolean terse)
{
char * fmt;
@@ -788,7 +920,7 @@ get_appointment_time_format (struct IndicatorDatetimeAppt * appt,
if (appt->is_daily)
{
const char * time_fmt = terse ? get_terse_time_format_string (appt->begin)
- : get_full_time_format_string ();
+ : get_full_time_format_string (settings);
fmt = join_date_and_time_format_strings (_("Daily"), time_fmt);
}
else if (full_day)
@@ -800,31 +932,49 @@ get_appointment_time_format (struct IndicatorDatetimeAppt * appt,
else
{
fmt = terse ? generate_terse_format_string_at_time (now, appt->begin)
- : generate_full_format_string_at_time (now, appt->begin);
+ : generate_full_format_string_at_time (now, appt->begin, settings);
}
return fmt;
}
static void
-add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean terse)
+add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean phone)
{
- GDateTime * now = indicator_datetime_service_get_localtime (self);
+ const int MAX_APPTS = 5;
+ GDateTime * now;
+ GHashTable * added;
GSList * appts;
GSList * l;
+ int i;
+
+ now = indicator_datetime_service_get_localtime (self);
+
+ added = g_hash_table_new (g_str_hash, g_str_equal);
/* build appointment menuitems */
- appts = get_upcoming_appointments (self);
- for (l=appts; l!=NULL; l=l->next)
+ appts = self->priv->upcoming_appointments;
+ for (l=appts, i=0; l!=NULL && i<MAX_APPTS; l=l->next, i++)
{
struct IndicatorDatetimeAppt * appt = l->data;
- char * fmt = get_appointment_time_format (appt, now, terse);
- const gint64 unix_time = g_date_time_to_unix (appt->begin);
+ char * fmt;
+ gint64 unix_time;
GMenuItem * menu_item;
+ if (g_hash_table_contains (added, appt->uid))
+ continue;
+
+ g_hash_table_add (added, appt->uid);
+
+ fmt = get_appointment_time_format (appt, now, self->priv->settings, phone);
+ unix_time = g_date_time_to_unix (appt->begin);
+
menu_item = g_menu_item_new (appt->summary, NULL);
- if (!appt->has_alarms)
+ if (appt->has_alarms)
+ g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ICON,
+ "s", ALARM_CLOCK_ICON_NAME);
+ else if (appt->color != NULL)
g_menu_item_set_attribute (menu_item, "x-canonical-color",
"s", appt->color);
@@ -835,27 +985,79 @@ add_appointments (IndicatorDatetimeService * self, GMenu * menu, gboolean terse)
g_menu_item_set_attribute (menu_item, "x-canonical-type",
"s", appt->has_alarms ? "com.canonical.indicator.alarm"
: "com.canonical.indicator.appointment");
- g_menu_item_set_action_and_target_value (menu_item,
- "indicator.activate-planner",
- g_variant_new_int64 (unix_time));
+
+ if (phone)
+ g_menu_item_set_action_and_target_value (menu_item,
+ "indicator.activate-appointment",
+ g_variant_new_string (appt->uid));
+ else
+ g_menu_item_set_action_and_target_value (menu_item,
+ "indicator.activate-planner",
+ g_variant_new_int64 (unix_time));
g_menu_append_item (menu, menu_item);
g_object_unref (menu_item);
g_free (fmt);
}
/* cleanup */
+ g_hash_table_unref (added);
g_date_time_unref (now);
- g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
+}
+
+
+/* try to extract the clock app's filename from click. (/$pkgdir/$icon) */
+static gchar *
+get_clock_app_icon_filename (void)
+{
+ gchar * icon_filename = NULL;
+ gchar * pkgdir;
+
+ pkgdir = NULL;
+ g_spawn_command_line_sync ("click pkgdir com.ubuntu.clock", &pkgdir, NULL, NULL, NULL);
+ if (pkgdir != NULL)
+ {
+ gchar * manifest = NULL;
+ g_strstrip (pkgdir);
+ g_spawn_command_line_sync ("click info com.ubuntu.clock", &manifest, NULL, NULL, NULL);
+ if (manifest != NULL)
+ {
+ JsonParser * parser = json_parser_new ();
+ if (json_parser_load_from_data (parser, manifest, -1, NULL))
+ {
+ JsonNode * root = json_parser_get_root (parser); /* transfer-none */
+ if ((root != NULL) && (JSON_NODE_TYPE(root) == JSON_NODE_OBJECT))
+ {
+ JsonObject * o = json_node_get_object (root); /* transfer-none */
+ const gchar * icon_name = json_object_get_string_member (o, "icon");
+ if (icon_name != NULL)
+ icon_filename = g_build_filename (pkgdir, icon_name, NULL);
+ }
+ }
+ g_object_unref (parser);
+ g_free (manifest);
+ }
+ g_free (pkgdir);
+ }
+
+ return icon_filename;
}
static GMenuModel *
create_phone_appointments_section (IndicatorDatetimeService * self)
{
+ priv_t * p = self->priv;
GMenu * menu = g_menu_new ();
GMenuItem * menu_item;
- menu_item = g_menu_item_new (_("Clock"), NULL);
- g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ICON, "s", "clock");
+ if (G_UNLIKELY (!p->clock_app_icon_initialized))
+ {
+ p->clock_app_icon_initialized = TRUE;
+ p->clock_app_icon_filename = get_clock_app_icon_filename ();
+ }
+
+ menu_item = g_menu_item_new (_("Clock"), "indicator.activate-phone-clock-app");
+ if (p->clock_app_icon_filename != NULL)
+ g_menu_item_set_attribute (menu_item, G_MENU_ATTRIBUTE_ICON, "s", p->clock_app_icon_filename);
g_menu_append_item (menu, menu_item);
g_object_unref (menu_item);
@@ -893,61 +1095,6 @@ create_desktop_appointments_section (IndicatorDatetimeService * self)
****
***/
-static void
-on_current_timezone_changed (IndicatorDatetimeService * self)
-{
- on_local_time_jumped (self);
-}
-
-/* When the 'auto-detect timezone' boolean setting changes,
- start or stop watching geoclue and /etc/timezone */
-static void
-set_detect_location_enabled (IndicatorDatetimeService * self, gboolean enabled)
-{
- gboolean changed = FALSE;
- priv_t * p = self->priv;
-
- /* geoclue */
-
- if (!p->tz_geoclue && enabled)
- {
- p->tz_geoclue = indicator_datetime_timezone_geoclue_new ();
- g_signal_connect_swapped (p->tz_geoclue, "notify::timezone",
- G_CALLBACK(on_current_timezone_changed),
- self);
- changed = TRUE;
- }
- else if (p->tz_geoclue && !enabled)
- {
- g_signal_handlers_disconnect_by_func (p->tz_geoclue,
- on_current_timezone_changed,
- self);
- g_clear_object (&p->tz_geoclue);
- changed = TRUE;
- }
-
- /* timezone file */
-
- if (!p->tz_file && enabled)
- {
- p->tz_file = indicator_datetime_timezone_file_new (TIMEZONE_FILE);
- g_signal_connect_swapped (p->tz_file, "notify::timezone",
- G_CALLBACK(on_current_timezone_changed),
- self);
- changed = TRUE;
- }
- else if (p->tz_file && !enabled)
- {
- g_signal_handlers_disconnect_by_func (p->tz_file,
- on_current_timezone_changed,
- self);
- g_clear_object (&p->tz_file);
- changed = TRUE;
- }
-
- if (changed)
- on_current_timezone_changed (self);
-}
/* A temp struct used by create_locations_section()
for pruning duplicates and sorting. */
@@ -966,7 +1113,7 @@ time_location_free (struct TimeLocation * loc)
g_date_time_unref (loc->local_time);
g_free (loc->name);
g_free (loc->zone);
- g_free (loc);
+ g_slice_free (struct TimeLocation, loc);
}
static struct TimeLocation*
@@ -974,7 +1121,7 @@ time_location_new (const char * zone,
const char * name,
gboolean visible)
{
- struct TimeLocation * loc = g_new (struct TimeLocation, 1);
+ struct TimeLocation * loc = g_slice_new (struct TimeLocation);
GTimeZone * tz = g_time_zone_new (zone);
loc->zone = g_strdup (zone);
loc->name = g_strdup (name);
@@ -1033,44 +1180,30 @@ create_locations_section (IndicatorDatetimeService * self)
GSList * l;
GSList * locations = NULL;
gchar ** user_locations;
- gboolean visible;
- IndicatorDatetimeTimezone * detected_timezones[2];
+ const gchar ** detected_timezones;
priv_t * p = self->priv;
GDateTime * now = indicator_datetime_service_get_localtime (self);
- set_detect_location_enabled (self,
- g_settings_get_boolean (p->settings, SETTINGS_SHOW_DETECTED_S));
-
menu = g_menu_new ();
/***
- **** Build a list of locations to add: use geo_timezone,
- **** current_timezone, and SETTINGS_LOCATIONS_S, but omit duplicates.
+ **** Build a list of locations to add, omitting duplicates
***/
- /* maybe add the auto-detected timezones */
- detected_timezones[0] = p->tz_geoclue;
- detected_timezones[1] = p->tz_file;
- visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_DETECTED_S);
- for (i=0; i<G_N_ELEMENTS(detected_timezones); i++)
+ detected_timezones = indicator_datetime_clock_get_timezones (p->clock);
+ for (i=0; detected_timezones && detected_timezones[i]; i++)
{
- if (detected_timezones[i] != NULL)
- {
- const char * tz = indicator_datetime_timezone_get_timezone (detected_timezones[i]);
- if (tz && *tz)
- {
- gchar * name = get_current_zone_name (tz);
- locations = locations_add (locations, tz, name, visible);
- g_free (name);
- }
- }
+ const char * tz = detected_timezones[i];
+ gchar * name = get_current_zone_name (tz, p->settings);
+ locations = locations_add (locations, tz, name, TRUE);
+ g_free (name);
}
/* maybe add the user-specified locations */
user_locations = g_settings_get_strv (p->settings, SETTINGS_LOCATIONS_S);
if (user_locations != NULL)
{
- visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_LOCATIONS_S);
+ const gboolean visible = g_settings_get_boolean (p->settings, SETTINGS_SHOW_LOCATIONS_S);
for (i=0; user_locations[i] != NULL; i++)
{
@@ -1092,18 +1225,16 @@ create_locations_section (IndicatorDatetimeService * self)
struct TimeLocation * loc = l->data;
if (loc->visible)
{
- char * label;
char * detailed_action;
char * fmt;
GMenuItem * menu_item;
- label = g_strdup (loc->name);
detailed_action = g_strdup_printf ("indicator.set-location::%s %s",
loc->zone,
loc->name);
- fmt = generate_full_format_string_at_time (now, loc->local_time);
+ fmt = generate_full_format_string_at_time (now, loc->local_time, p->settings);
- menu_item = g_menu_item_new (label, detailed_action);
+ menu_item = g_menu_item_new (loc->name, detailed_action);
g_menu_item_set_attribute (menu_item, "x-canonical-type",
"s", "com.canonical.indicator.location");
g_menu_item_set_attribute (menu_item, "x-canonical-timezone",
@@ -1115,7 +1246,6 @@ create_locations_section (IndicatorDatetimeService * self)
g_object_unref (menu_item);
g_free (fmt);
g_free (detailed_action);
- g_free (label);
}
}
@@ -1140,7 +1270,7 @@ setlocation_data_free (struct setlocation_data * data)
{
g_free (data->timezone_id);
g_free (data->name);
- g_free (data);
+ g_slice_free (struct setlocation_data, data);
}
static void
@@ -1224,7 +1354,7 @@ indicator_datetime_service_set_location (IndicatorDatetimeService * self,
g_return_if_fail (name && *name);
g_return_if_fail (timezone_id && *timezone_id);
- data = g_new0 (struct setlocation_data, 1);
+ data = g_slice_new0 (struct setlocation_data);
data->timezone_id = g_strdup (timezone_id);
data->name = g_strdup (name);
data->service = self;
@@ -1376,7 +1506,44 @@ on_phone_settings_activated (GSimpleAction * a G_GNUC_UNUSED,
GVariant * param G_GNUC_UNUSED,
gpointer gself G_GNUC_UNUSED)
{
- execute_command ("system-settings time-date");
+ url_dispatch_send ("settings:///system/time-date", NULL, NULL);
+}
+
+static void
+on_activate_appointment (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param,
+ gpointer gself)
+{
+ priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv;
+ const gchar * uid = g_variant_get_string (param, NULL);
+
+ if (uid != NULL)
+ {
+ const struct IndicatorDatetimeAppt * appt;
+ GSList * l;
+
+ /* find the appointment that matches that uid */
+ for (l=p->upcoming_appointments, appt=NULL; l && !appt; l=l->next)
+ {
+ const struct IndicatorDatetimeAppt * tmp = l->data;
+ if (!g_strcmp0 (uid, tmp->uid))
+ appt = tmp;
+ }
+
+ /* if that appointment's an alarm, dispatch its url */
+ g_debug ("%s: uri '%s'; matching appt is %p", G_STRFUNC, uid, appt);
+ if (appt && appointment_has_alarm_url (appt))
+ dispatch_alarm_url (appt);
+ }
+}
+
+static void
+on_phone_clock_activated (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param G_GNUC_UNUSED,
+ gpointer gself G_GNUC_UNUSED)
+{
+ const char * url = "appid://com.ubuntu.clock/clock/current-user-version";
+ url_dispatch_send (url, NULL, NULL);
}
static void
@@ -1432,7 +1599,9 @@ init_gactions (IndicatorDatetimeService * self)
GActionEntry entries[] = {
{ "activate-desktop-settings", on_desktop_settings_activated },
{ "activate-phone-settings", on_phone_settings_activated },
+ { "activate-phone-clock-app", on_phone_clock_activated },
{ "activate-planner", on_activate_planner, "x", NULL },
+ { "activate-appointment", on_activate_appointment, "s", NULL },
{ "set-location", on_set_location, "s" }
};
@@ -1492,6 +1661,9 @@ rebuild_now (IndicatorDatetimeService * self, int sections)
struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP];
struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER];
+ if (p->actions == NULL)
+ return;
+
if (sections & SECTION_HEADER)
{
g_simple_action_set_state (p->desktop_header_action,
@@ -1604,6 +1776,137 @@ on_login1_manager_proxy_ready (GObject * object G_GNUC_UNUSED,
}
/***
+**** Appointments
+***/
+
+static void
+set_calendar_appointments (IndicatorDatetimeService * self,
+ GSList * appointments)
+{
+ priv_t * p = self->priv;
+
+ /* repopulate the list */
+ indicator_datetime_planner_free_appointments (p->calendar_appointments);
+ p->calendar_appointments = appointments;
+
+ /* sync the menus/actions */
+ update_calendar_action_state (self);
+ rebuild_calendar_section_soon (self);
+}
+
+static void
+on_calendar_appointments_ready (GObject * source,
+ GAsyncResult * res,
+ gpointer self)
+{
+ IndicatorDatetimePlanner * planner;
+ GError * error;
+ GSList * appointments;
+
+ planner = INDICATOR_DATETIME_PLANNER (source);
+ error = NULL;
+ appointments = indicator_datetime_planner_get_appointments_finish (planner,
+ res,
+ &error);
+
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("can't get this month's appointments: %s", error->message);
+
+ g_error_free (error);
+ }
+ else
+ {
+ set_calendar_appointments (INDICATOR_DATETIME_SERVICE (self),
+ appointments);
+ }
+}
+
+static void
+set_upcoming_appointments (IndicatorDatetimeService * self,
+ GSList * appointments)
+{
+ priv_t * p = self->priv;
+
+ /* repopulate the list */
+ indicator_datetime_planner_free_appointments (p->upcoming_appointments);
+ p->upcoming_appointments = appointments;
+
+ /* sync the menus/actions */
+ rebuild_appointments_section_soon (self);
+
+ /* alarm timer is keyed off of the next alarm time,
+ so it needs to be rebuilt when the appointment list changes */
+ set_alarm_timer (self);
+}
+
+static void
+on_upcoming_appointments_ready (GObject * source,
+ GAsyncResult * res,
+ gpointer self)
+{
+ IndicatorDatetimePlanner * planner;
+ GError * error;
+ GSList * appointments;
+
+ planner = INDICATOR_DATETIME_PLANNER (source);
+ error = NULL;
+ appointments = indicator_datetime_planner_get_appointments_finish (planner,
+ res,
+ &error);
+
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("can't get upcoming appointments: %s", error->message);
+
+ g_error_free (error);
+ }
+ else
+ {
+ set_upcoming_appointments (INDICATOR_DATETIME_SERVICE (self),
+ appointments);
+ }
+}
+
+static void
+update_appointment_lists (IndicatorDatetimeService * self)
+{
+ IndicatorDatetimePlanner * planner;
+ GDateTime * calendar_date;
+ GDateTime * begin;
+ GDateTime * end;
+ int y, m, d;
+
+ planner = self->priv->planner;
+ calendar_date = get_calendar_date (self);
+
+ /* get all the appointments in the calendar month */
+ g_date_time_get_ymd (calendar_date, &y, &m, &d);
+ begin = g_date_time_new_local (y, m, 1, 0, 0, 0.1);
+ end = g_date_time_new_local (y, m, g_date_get_days_in_month(m,y), 23, 59, 59.9);
+ if (begin && end)
+ indicator_datetime_planner_get_appointments (planner, begin, end,
+ on_calendar_appointments_ready,
+ self);
+ g_clear_pointer (&begin, g_date_time_unref);
+ g_clear_pointer (&end, g_date_time_unref);
+
+ /* get the upcoming appointments */
+ begin = g_date_time_ref (calendar_date);
+ end = g_date_time_add_months (begin, 1);
+ if (begin && end)
+ indicator_datetime_planner_get_appointments (planner, begin, end,
+ on_upcoming_appointments_ready,
+ self);
+ g_clear_pointer (&begin, g_date_time_unref);
+ g_clear_pointer (&end, g_date_time_unref);
+ g_clear_pointer (&calendar_date, g_date_time_unref);
+}
+
+
+/***
**** GDBus
***/
@@ -1642,9 +1945,6 @@ on_bus_acquired (GDBusConnection * connection,
char * path = g_strdup_printf ("%s/%s", BUS_PATH, menu_names[i]);
struct ProfileMenuInfo * menu = &p->menus[i];
- if (menu->menu == NULL)
- create_menu (self, i);
-
if ((id = g_dbus_connection_export_menu_model (connection,
path,
G_MENU_MODEL (menu->menu),
@@ -1708,6 +2008,53 @@ on_name_lost (GDBusConnection * connection G_GNUC_UNUSED,
***/
static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o);
+
+ switch (property_id)
+ {
+ case PROP_CLOCK:
+ g_value_set_object (value, self->priv->clock);
+ break;
+
+ case PROP_PLANNER:
+ g_value_set_object (value, self->priv->planner);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_set_property (GObject * o,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (o);
+
+ switch (property_id)
+ {
+ case PROP_CLOCK:
+ indicator_datetime_service_set_clock (self, g_value_get_object (value));
+ break;
+
+ case PROP_PLANNER:
+ indicator_datetime_service_set_planner (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+
+static void
my_dispose (GObject * o)
{
int i;
@@ -1728,13 +2075,8 @@ my_dispose (GObject * o)
g_clear_object (&p->cancellable);
}
- set_detect_location_enabled (self, FALSE);
-
- if (p->planner != NULL)
- {
- g_signal_handlers_disconnect_by_data (p->planner, self);
- g_clear_object (&p->planner);
- }
+ indicator_datetime_service_set_clock (self, NULL);
+ indicator_datetime_service_set_planner (self, NULL);
if (p->login1_manager != NULL)
{
@@ -1746,6 +2088,7 @@ my_dispose (GObject * o)
indicator_clear_timer (&p->rebuild_id);
indicator_clear_timer (&p->timezone_timer);
indicator_clear_timer (&p->header_timer);
+ indicator_clear_timer (&p->alarm_timer);
if (p->settings != NULL)
{
@@ -1758,12 +2101,17 @@ my_dispose (GObject * o)
for (i=0; i<N_PROFILES; ++i)
g_clear_object (&p->menus[i].menu);
- g_clear_object (&p->planner);
g_clear_object (&p->calendar_action);
g_clear_object (&p->desktop_header_action);
g_clear_object (&p->phone_header_action);
g_clear_object (&p->conn);
+ g_clear_pointer (&p->desktop_title_variant, g_variant_unref);
+ g_clear_pointer (&p->phone_title_variant, g_variant_unref);
+ g_clear_pointer (&p->visible_true_variant, g_variant_unref);
+ g_clear_pointer (&p->visible_false_variant, g_variant_unref);
+ g_clear_pointer (&p->alarm_icon_variant, g_variant_unref);
+
G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o);
}
@@ -1773,6 +2121,8 @@ my_finalize (GObject * o)
IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o);
priv_t * p = self->priv;
+ g_free (p->clock_app_icon_filename);
+ g_free (p->header_label_format_string);
g_clear_pointer (&p->skew_time, g_date_time_unref);
g_clear_pointer (&p->calendar_date, g_date_time_unref);
@@ -1786,9 +2136,45 @@ my_finalize (GObject * o)
static void
indicator_datetime_service_init (IndicatorDatetimeService * self)
{
- guint i, n;
+ GIcon * icon;
priv_t * p;
+
+ /* init the priv pointer */
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_SERVICE,
+ IndicatorDatetimeServicePrivate);
+ self->priv = p;
+
+ p->cancellable = g_cancellable_new ();
+
+ p->settings = g_settings_new (SETTINGS_INTERFACE);
+
+ p->desktop_title_variant = g_variant_new ("{sv}", "title", g_variant_new_string (_("Date and Time")));
+ g_variant_ref_sink (p->desktop_title_variant);
+
+ p->phone_title_variant = g_variant_new ("{sv}", "title", g_variant_new_string (_("Upcoming")));
+ g_variant_ref_sink (p->phone_title_variant);
+
+ p->visible_true_variant = g_variant_new ("{sv}", "visible", g_variant_new_boolean (TRUE));
+ g_variant_ref_sink (p->visible_true_variant);
+
+ p->visible_false_variant = g_variant_new ("{sv}", "visible", g_variant_new_boolean (FALSE));
+ g_variant_ref_sink (p->visible_false_variant);
+
+ icon = g_themed_icon_new_with_default_fallbacks (ALARM_CLOCK_ICON_NAME);
+ p->alarm_icon_variant = g_variant_new ("{sv}", "icon", g_icon_serialize (icon));
+ g_variant_ref_sink (p->alarm_icon_variant);
+ g_object_unref (icon);
+}
+
+static void
+my_constructed (GObject * gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ priv_t * p = self->priv;
GString * gstr = g_string_new (NULL);
+ guint i, n;
/* these are the settings that affect the
contents of the respective sections */
@@ -1825,30 +2211,10 @@ indicator_datetime_service_init (IndicatorDatetimeService * self)
};
- /* init the priv pointer */
-
- p = G_TYPE_INSTANCE_GET_PRIVATE (self,
- INDICATOR_TYPE_DATETIME_SERVICE,
- IndicatorDatetimeServicePrivate);
- self->priv = p;
-
- p->cancellable = g_cancellable_new ();
-
/***
- **** Create the planner and listen for changes
+ **** Listen for settings changes
***/
- p->planner = indicator_datetime_planner_eds_new ();
-
- g_signal_connect_swapped (p->planner, "appointments-changed",
- G_CALLBACK(rebuild_calendar_section_soon), self);
-
-
- /***
- **** Create the settings object and listen for changes
- ***/
-
- p->settings = g_settings_new (SETTINGS_INTERFACE);
for (i=0, n=G_N_ELEMENTS(header_settings); i<n; i++)
{
g_string_printf (gstr, "changed::%s", header_settings[i]);
@@ -1913,6 +2279,11 @@ indicator_datetime_service_init (IndicatorDatetimeService * self)
on_local_time_jumped (self);
+ set_alarm_timer (self);
+
+ for (i=0; i<N_PROFILES; ++i)
+ create_menu (self, i);
+
g_string_free (gstr, TRUE);
}
@@ -1920,9 +2291,13 @@ static void
indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
{
GObjectClass * object_class = G_OBJECT_CLASS (klass);
+ const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS;
object_class->dispose = my_dispose;
object_class->finalize = my_finalize;
+ object_class->constructed = my_constructed;
+ object_class->get_property = my_get_property;
+ object_class->set_property = my_set_property;
g_type_class_add_private (klass, sizeof (IndicatorDatetimeServicePrivate));
@@ -1934,6 +2309,24 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
+
+ /* install properties */
+
+ properties[PROP_0] = NULL;
+
+ properties[PROP_CLOCK] = g_param_spec_object ("clock",
+ "Clock",
+ "The clock",
+ INDICATOR_TYPE_DATETIME_CLOCK,
+ flags);
+
+ properties[PROP_PLANNER] = g_param_spec_object ("planner",
+ "Planner",
+ "The appointment provider",
+ INDICATOR_TYPE_DATETIME_PLANNER,
+ flags);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
}
/***
@@ -1941,21 +2334,17 @@ indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * klass)
***/
IndicatorDatetimeService *
-indicator_datetime_service_new (void)
+indicator_datetime_service_new (IndicatorDatetimeClock * clock,
+ IndicatorDatetimePlanner * planner)
{
- GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, NULL);
+ GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE,
+ "clock", clock,
+ "planner", planner,
+ NULL);
return INDICATOR_DATETIME_SERVICE (o);
}
-/* This currently just returns the system time,
- As we add test coverage, we'll need this to bypass the system time. */
-GDateTime *
-indicator_datetime_service_get_localtime (IndicatorDatetimeService * self G_GNUC_UNUSED)
-{
- return g_date_time_new_now_local ();
-}
-
void
indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
GDateTime * date)
@@ -1972,8 +2361,77 @@ indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
/* sync the menuitems and action states */
if (dirty)
+ update_appointment_lists (self);
+}
+
+static void
+on_clock_changed (IndicatorDatetimeService * self)
+{
+ on_local_time_jumped (self);
+}
+
+void
+indicator_datetime_service_set_clock (IndicatorDatetimeService * self,
+ IndicatorDatetimeClock * clock)
+{
+ priv_t * p;
+
+ g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
+ g_return_if_fail ((clock == NULL) || INDICATOR_IS_DATETIME_CLOCK (clock));
+
+ p = self->priv;
+
+ /* clear the old clock */
+
+ if (p->clock != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->clock, self);
+ g_clear_object (&p->clock);
+ }
+
+ /* set the new clock */
+
+ if (clock != NULL)
+ {
+ p->clock = g_object_ref (clock);
+
+ g_signal_connect_swapped (p->clock, "changed",
+ G_CALLBACK(on_clock_changed), self);
+ on_clock_changed (self);
+ }
+}
+
+void
+indicator_datetime_service_set_planner (IndicatorDatetimeService * self,
+ IndicatorDatetimePlanner * planner)
+{
+ priv_t * p;
+
+ g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
+ g_return_if_fail ((planner == NULL) || INDICATOR_IS_DATETIME_PLANNER (planner));
+
+ p = self->priv;
+
+ /* clear the old planner & appointments */
+
+ if (p->planner != NULL)
{
- update_calendar_action_state (self);
- rebuild_appointments_section_soon (self);
+ g_signal_handlers_disconnect_by_data (p->planner, self);
+ g_clear_object (&p->planner);
+ }
+
+ g_clear_pointer (&p->upcoming_appointments, indicator_datetime_planner_free_appointments);
+ g_clear_pointer (&p->calendar_appointments, indicator_datetime_planner_free_appointments);
+
+ /* set the new planner & begin fetching appointments from it */
+
+ if (planner != NULL)
+ {
+ p->planner = g_object_ref (planner);
+
+ g_signal_connect_swapped (p->planner, "appointments-changed",
+ G_CALLBACK(update_appointment_lists), self);
+
+ update_appointment_lists (self);
}
}
diff --git a/src/service.h b/src/service.h
index b142882..d38db72 100644
--- a/src/service.h
+++ b/src/service.h
@@ -23,6 +23,9 @@
#include <glib.h>
#include <glib-object.h>
+#include "clock.h"
+#include "planner.h"
+
G_BEGIN_DECLS
/* standard GObject macros */
@@ -62,13 +65,19 @@ struct _IndicatorDatetimeServiceClass
GType indicator_datetime_service_get_type (void);
-IndicatorDatetimeService * indicator_datetime_service_new (void);
-
-GDateTime * indicator_datetime_service_get_localtime (IndicatorDatetimeService * service);
+IndicatorDatetimeService * indicator_datetime_service_new (IndicatorDatetimeClock * clock,
+ IndicatorDatetimePlanner * planner);
void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
GDateTime * date);
+void indicator_datetime_service_set_planner (IndicatorDatetimeService * self,
+ IndicatorDatetimePlanner * planner);
+
+
+void indicator_datetime_service_set_clock (IndicatorDatetimeService * self,
+ IndicatorDatetimeClock * clock);
+
G_END_DECLS
diff --git a/src/settings-shared.h b/src/settings-shared.h
index 27ce34c..afcccb6 100644
--- a/src/settings-shared.h
+++ b/src/settings-shared.h
@@ -22,6 +22,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __DATETIME_SETTINGS_SHARED_H__
#define __DATETIME_SETTINGS_SHARED_H__
+typedef enum
+{
+ TIME_FORMAT_MODE_LOCALE_DEFAULT,
+ TIME_FORMAT_MODE_12_HOUR,
+ TIME_FORMAT_MODE_24_HOUR,
+ TIME_FORMAT_MODE_CUSTOM
+}
+TimeFormatMode;
+
#define SETTINGS_INTERFACE "com.canonical.indicator.datetime"
#define SETTINGS_SHOW_CLOCK_S "show-clock"
#define SETTINGS_TIME_FORMAT_S "time-format"
diff --git a/src/timezone-file.c b/src/timezone-file.c
index 2adf2ca..698ce3e 100644
--- a/src/timezone-file.c
+++ b/src/timezone-file.c
@@ -35,9 +35,7 @@ static GParamSpec * properties[PROP_LAST] = { 0 };
struct _IndicatorDatetimeTimezoneFilePriv
{
gchar * filename;
- GFile * file;
GFileMonitor * monitor;
- gchar * timezone;
};
typedef IndicatorDatetimeTimezoneFilePriv priv_t;
@@ -56,26 +54,18 @@ reload (IndicatorDatetimeTimezoneFile * self)
priv_t * p = self->priv;
GError * err = NULL;
- gchar * new_timezone = NULL;
+ gchar * timezone = NULL;
- if (!g_file_get_contents (p->filename, &new_timezone, NULL, &err))
+ if (!g_file_get_contents (p->filename, &timezone, NULL, &err))
{
g_warning ("%s Unable to read timezone file '%s': %s", G_STRLOC, p->filename, err->message);
g_error_free (err);
}
else
{
- g_strstrip (new_timezone);
-
- if (g_strcmp0 (p->timezone, new_timezone))
- {
- g_free (p->timezone);
- p->timezone = g_strdup (new_timezone);
- g_debug ("%s new timezone set: '%s'", G_STRLOC, p->timezone);
- indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self));
- }
-
- g_free (new_timezone);
+ g_strstrip (timezone);
+ indicator_datetime_timezone_set_timezone (INDICATOR_DATETIME_TIMEZONE(self), timezone);
+ g_free (timezone);
}
}
@@ -83,17 +73,17 @@ static void
set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename)
{
GError * err;
+ GFile * file;
priv_t * p = self->priv;
g_clear_object (&p->monitor);
- g_clear_object (&p->file);
g_free (p->filename);
p->filename = g_strdup (filename);
- p->file = g_file_new_for_path (p->filename);
-
err = NULL;
- p->monitor = g_file_monitor_file (p->file, G_FILE_MONITOR_NONE, NULL, &err);
+ file = g_file_new_for_path (p->filename);
+ p->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &err);
+ g_object_unref (file);
if (err != NULL)
{
g_warning ("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message);
@@ -109,16 +99,6 @@ set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename)
}
/***
-**** IndicatorDatetimeTimezoneClass funcs
-***/
-
-static const char *
-my_get_timezone (IndicatorDatetimeTimezone * self)
-{
- return INDICATOR_DATETIME_TIMEZONE_FILE(self)->priv->timezone;
-}
-
-/***
**** GObjectClass funcs
***/
@@ -167,7 +147,6 @@ my_dispose (GObject * o)
priv_t * p = self->priv;
g_clear_object (&p->monitor);
- g_clear_object (&p->file);
G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->dispose (o);
}
@@ -179,7 +158,6 @@ my_finalize (GObject * o)
priv_t * p = self->priv;
g_free (p->filename);
- g_free (p->timezone);
G_OBJECT_CLASS (indicator_datetime_timezone_file_parent_class)->finalize (o);
}
@@ -192,7 +170,6 @@ static void
indicator_datetime_timezone_file_class_init (IndicatorDatetimeTimezoneFileClass * klass)
{
GObjectClass * object_class;
- IndicatorDatetimeTimezoneClass * location_class;
const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS;
object_class = G_OBJECT_CLASS (klass);
@@ -201,9 +178,6 @@ indicator_datetime_timezone_file_class_init (IndicatorDatetimeTimezoneFileClass
object_class->set_property = my_set_property;
object_class->get_property = my_get_property;
- location_class = INDICATOR_DATETIME_TIMEZONE_CLASS (klass);
- location_class->get_timezone = my_get_timezone;
-
g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneFilePriv));
/* install properties */
diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c
index 239ac50..c89558d 100644
--- a/src/timezone-geoclue.c
+++ b/src/timezone-geoclue.c
@@ -29,7 +29,6 @@ struct _IndicatorDatetimeTimezoneGeocluePriv
GeoclueMaster * master;
GeoclueMasterClient * client;
GeoclueAddress * address;
- gchar * timezone;
};
typedef IndicatorDatetimeTimezoneGeocluePriv priv_t;
@@ -45,19 +44,6 @@ static void geo_restart (IndicatorDatetimeTimezoneGeoclue * self);
***/
static void
-set_timezone (IndicatorDatetimeTimezoneGeoclue * self, const gchar * timezone)
-{
- priv_t * p = self->priv;
-
- if (g_strcmp0 (p->timezone, timezone))
- {
- g_free (p->timezone);
- p->timezone = g_strdup (timezone);
- indicator_datetime_timezone_notify_timezone (INDICATOR_DATETIME_TIMEZONE(self));
- }
-}
-
-static void
on_address_changed (GeoclueAddress * address G_GNUC_UNUSED,
int timestamp G_GNUC_UNUSED,
GHashTable * addy_data,
@@ -73,10 +59,22 @@ on_address_changed (GeoclueAddress * address G_GNUC_UNUSED,
{
IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself);
const char * timezone = g_hash_table_lookup (addy_data, "timezone");
- set_timezone (self, timezone);
+ indicator_datetime_timezone_set_timezone (INDICATOR_DATETIME_TIMEZONE(self), timezone);
}
}
+/* The signal doesn't have the parameter for an error, so it ends up needing
+ a NULL inserted. */
+static void
+on_address_changed_sig (GeoclueAddress * address G_GNUC_UNUSED,
+ int timestamp G_GNUC_UNUSED,
+ GHashTable * addy_data,
+ GeoclueAccuracy * accuracy G_GNUC_UNUSED,
+ gpointer gself)
+{
+ return on_address_changed(address, timestamp, addy_data, accuracy, NULL, gself);
+}
+
static void
on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED,
GeoclueAddress * address,
@@ -95,7 +93,7 @@ on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED,
p->address = g_object_ref (address);
geoclue_address_get_address_async (address, on_address_changed, gself);
- g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed), gself);
+ g_signal_connect (address, "address-changed", G_CALLBACK(on_address_changed_sig), gself);
}
}
@@ -161,7 +159,7 @@ geo_stop (IndicatorDatetimeTimezoneGeoclue * self)
if (p->address != NULL)
{
- g_signal_handlers_disconnect_by_func (p->address, on_address_changed, self);
+ g_signal_handlers_disconnect_by_func (p->address, on_address_changed_sig, self);
g_clear_object (&p->address);
}
@@ -185,12 +183,6 @@ geo_restart (IndicatorDatetimeTimezoneGeoclue * self)
****
***/
-static const char *
-my_get_timezone (IndicatorDatetimeTimezone * self)
-{
- return INDICATOR_DATETIME_TIMEZONE_GEOCLUE(self)->priv->timezone;
-}
-
static void
my_dispose (GObject * o)
{
@@ -200,28 +192,12 @@ my_dispose (GObject * o)
}
static void
-my_finalize (GObject * o)
-{
- IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o);
- priv_t * p = self->priv;
-
- g_free (p->timezone);
-
- G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->finalize (o);
-}
-
-static void
indicator_datetime_timezone_geoclue_class_init (IndicatorDatetimeTimezoneGeoclueClass * klass)
{
GObjectClass * object_class;
- IndicatorDatetimeTimezoneClass * location_class;
object_class = G_OBJECT_CLASS (klass);
object_class->dispose = my_dispose;
- object_class->finalize = my_finalize;
-
- location_class = INDICATOR_DATETIME_TIMEZONE_CLASS (klass);
- location_class->get_timezone = my_get_timezone;
g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezoneGeocluePriv));
}
@@ -234,6 +210,7 @@ indicator_datetime_timezone_geoclue_init (IndicatorDatetimeTimezoneGeoclue * sel
p = G_TYPE_INSTANCE_GET_PRIVATE (self,
INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE,
IndicatorDatetimeTimezoneGeocluePriv);
+
self->priv = p;
geo_start (self);
diff --git a/src/timezone.c b/src/timezone.c
index a543155..ca207f2 100644
--- a/src/timezone.c
+++ b/src/timezone.c
@@ -32,6 +32,13 @@ enum
static GParamSpec * properties[PROP_LAST] = { 0, };
+struct _IndicatorDatetimeTimezonePriv
+{
+ GString * timezone;
+};
+
+typedef struct _IndicatorDatetimeTimezonePriv priv_t;
+
static void
my_get_property (GObject * o,
guint property_id,
@@ -52,9 +59,13 @@ my_get_property (GObject * o,
}
static void
-my_dispose (GObject * object)
+my_finalize (GObject * o)
{
- G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->dispose (object);
+ priv_t * p = INDICATOR_DATETIME_TIMEZONE(o)->priv;
+
+ g_string_free (p->timezone, TRUE);
+
+ G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->finalize (o);
}
static void
@@ -64,11 +75,11 @@ indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass)
GObjectClass * object_class;
const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS;
+ g_type_class_add_private (klass, sizeof (IndicatorDatetimeTimezonePriv));
+
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = my_get_property;
- object_class->dispose = my_dispose;
-
- klass->get_timezone = NULL;
+ object_class->finalize = my_finalize;
properties[PROP_TIMEZONE] = g_param_spec_string ("timezone",
"Timezone",
@@ -80,8 +91,17 @@ indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass)
}
static void
-indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self G_GNUC_UNUSED)
+indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self)
{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_TIMEZONE,
+ IndicatorDatetimeTimezonePriv);
+
+ p->timezone = g_string_new (NULL);
+
+ self->priv = p;
}
/***
@@ -93,13 +113,19 @@ indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone * self)
{
g_return_val_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self), NULL);
- return INDICATOR_DATETIME_TIMEZONE_GET_CLASS (self)->get_timezone (self);
+ return self->priv->timezone->str;
}
void
-indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone * self)
+indicator_datetime_timezone_set_timezone (IndicatorDatetimeTimezone * self,
+ const char * timezone)
{
- g_return_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self));
+ priv_t * p = self->priv;
- g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]);
+ if (g_strcmp0 (p->timezone->str, timezone))
+ {
+ g_string_assign (p->timezone, timezone);
+ g_debug ("%s new timezone set: '%s'", G_STRLOC, timezone);
+ g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]);
+ }
}
diff --git a/src/timezone.h b/src/timezone.h
index 076bedc..fa6593d 100644
--- a/src/timezone.h
+++ b/src/timezone.h
@@ -32,12 +32,11 @@ G_BEGIN_DECLS
#define INDICATOR_IS_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE))
typedef struct _IndicatorDatetimeTimezone IndicatorDatetimeTimezone;
+typedef struct _IndicatorDatetimeTimezonePriv IndicatorDatetimeTimezonePriv;
typedef struct _IndicatorDatetimeTimezoneClass IndicatorDatetimeTimezoneClass;
GType indicator_datetime_timezone_get_type (void);
-#define INDICATOR_DATETIME_TIMEZONE_PROPERTY_TIMEZONE "timezone"
-
/**
* Abstract Base Class for objects that provide a timezone.
*
@@ -51,14 +50,12 @@ struct _IndicatorDatetimeTimezone
{
/*< private >*/
GObject parent;
+ IndicatorDatetimeTimezonePriv * priv;
};
struct _IndicatorDatetimeTimezoneClass
{
GObjectClass parent_class;
-
- /* virtual functions */
- const char * (*get_timezone) (IndicatorDatetimeTimezone * self);
};
/***
@@ -67,7 +64,8 @@ struct _IndicatorDatetimeTimezoneClass
const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *);
-void indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone *);
+void indicator_datetime_timezone_set_timezone (IndicatorDatetimeTimezone *,
+ const char * new_timezone);
G_END_DECLS
diff --git a/src/utils.c b/src/utils.c
index b99de94..60fd393 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -53,48 +53,56 @@ is_locale_12h (void)
void
split_settings_location (const gchar * location, gchar ** zone, gchar ** name)
{
- gchar * location_dup = g_strdup (location);
- gchar * first = strchr (location_dup, ' ');
+ gchar * location_dup;
+ gchar * first;
- if (first) {
- first[0] = 0;
- }
+ location_dup = g_strdup (location);
+ g_strstrip (location_dup);
- if (zone) {
- *zone = location_dup;
- }
+ if ((first = strchr (location_dup, ' ')))
+ *first = '\0';
- if (name) {
- gchar * after = first ? g_strstrip (first + 1) : NULL;
- if (after == NULL || after[0] == 0) {
- /* Make up name from zone */
- gchar * chr = strrchr (location_dup, '/');
- after = g_strdup (chr ? chr + 1 : location_dup);
- while ((chr = strchr (after, '_')) != NULL) { /* and turn underscores to spaces */
- *chr = ' ';
- }
- *name = after;
+ if (zone != NULL)
+ {
+ *zone = location_dup;
}
- else {
- *name = g_strdup (after);
+
+ if (name != NULL)
+ {
+ gchar * after = first ? g_strstrip (first + 1) : NULL;
+
+ if (after && *after)
+ {
+ *name = g_strdup (after);
+ }
+ else /* make the name from zone */
+ {
+ gchar * chr = strrchr (location_dup, '/');
+ after = g_strdup (chr ? chr + 1 : location_dup);
+
+ /* replace underscores with spaces */
+ for (chr=after; chr && *chr; chr++)
+ if (*chr == '_')
+ *chr = ' ';
+
+ *name = after;
+ }
}
- }
}
gchar *
-get_current_zone_name (const gchar * location)
+get_current_zone_name (const gchar * location, GSettings * settings)
{
gchar * new_zone, * new_name;
+ gchar * tz_name;
gchar * old_zone, * old_name;
gchar * rv;
split_settings_location (location, &new_zone, &new_name);
- GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
- gchar * tz_name = g_settings_get_string (conf, SETTINGS_TIMEZONE_NAME_S);
+ tz_name = g_settings_get_string (settings, SETTINGS_TIMEZONE_NAME_S);
split_settings_location (tz_name, &old_zone, &old_name);
g_free (tz_name);
- g_object_unref (conf);
// new_name is always just a sanitized version of a timezone.
// old_name is potentially a saved "pretty" version of a timezone name from
@@ -134,9 +142,9 @@ T_(const char *msg)
LC_MESSAGES directory, so we won't find any translation there.
*/
char *message_locale = g_strdup(setlocale(LC_MESSAGES, NULL));
- char *time_locale = g_strdup(setlocale(LC_TIME, NULL));
+ const char *time_locale = setlocale (LC_TIME, NULL);
char *language = g_strdup(g_getenv("LANGUAGE"));
- char *rv;
+ const char *rv;
if (language)
g_unsetenv("LANGUAGE");
setlocale(LC_MESSAGES, time_locale);
@@ -149,7 +157,6 @@ T_(const char *msg)
if (language)
g_setenv("LANGUAGE", language, TRUE);
g_free(message_locale);
- g_free(time_locale);
g_free(language);
return rv;
}
@@ -287,6 +294,13 @@ get_terse_date_format_string (date_proximity_t proximity)
return fmt;
}
+const gchar*
+get_terse_header_time_format_string (void)
+{
+ /* a strftime(3) fmt string for a H:MM 12 hour time, eg "6:59 PM" */
+ return T_("%l:%M %p");
+}
+
const gchar *
get_terse_time_format_string (GDateTime * time)
{
@@ -294,8 +308,7 @@ get_terse_time_format_string (GDateTime * time)
if (g_date_time_get_minute (time) != 0)
{
- /* a strftime(3) fmt string for a HH:MM 12 hour time, eg "06:59 PM" */
- fmt = T_("%I:%M %p");
+ fmt = get_terse_header_time_format_string ();
}
else
{
@@ -348,33 +361,24 @@ get_full_date_format_string (gboolean show_day, gboolean show_date)
*
*/
-enum
-{
- SETTINGS_TIME_LOCALE = 0,
- SETTINGS_TIME_12_HOUR = 1,
- SETTINGS_TIME_24_HOUR = 2,
- SETTINGS_TIME_CUSTOM = 3
-};
-
const gchar *
-get_full_time_format_string (void)
+get_full_time_format_string (GSettings * settings)
{
- GSettings * settings;
gboolean twelvehour;
gboolean show_seconds;
const gchar * fmt;
- settings = g_settings_new (SETTINGS_INTERFACE);
+ g_return_val_if_fail (settings != NULL, NULL);
show_seconds = g_settings_get_boolean (settings, SETTINGS_SHOW_SECONDS_S);
switch (g_settings_get_enum (settings, SETTINGS_TIME_FORMAT_S))
{
- case SETTINGS_TIME_LOCALE:
+ case TIME_FORMAT_MODE_LOCALE_DEFAULT:
twelvehour = is_locale_12h();
break;
- case SETTINGS_TIME_24_HOUR:
+ case TIME_FORMAT_MODE_24_HOUR:
twelvehour = FALSE;
break;
@@ -383,8 +387,6 @@ get_full_time_format_string (void)
break;
}
- g_object_unref (settings);
-
if (twelvehour && show_seconds)
/* TRANSLATORS: a strftime(3) format for 12hr time w/seconds */
fmt = T_("%l:%M:%S %p");
@@ -402,19 +404,23 @@ get_full_time_format_string (void)
}
gchar *
-generate_full_format_string (gboolean show_day, gboolean show_date)
+generate_full_format_string (gboolean show_day, gboolean show_date, GSettings * settings)
{
const gchar * date_fmt = get_full_date_format_string (show_day, show_date);
- const gchar * time_fmt = get_full_time_format_string ();
+ const gchar * time_fmt = get_full_time_format_string (settings);
return join_date_and_time_format_strings (date_fmt, time_fmt);
}
gchar *
-generate_full_format_string_at_time (GDateTime * now, GDateTime * time)
+generate_full_format_string_at_time (GDateTime * now, GDateTime * time, GSettings * settings)
{
gboolean show_day;
gboolean show_date;
+ g_return_val_if_fail (now != NULL, NULL);
+ g_return_val_if_fail (time != NULL, NULL);
+ g_return_val_if_fail (settings != NULL, NULL);
+
switch (get_date_proximity (now, time))
{
case DATE_PROXIMITY_TODAY:
@@ -434,6 +440,6 @@ generate_full_format_string_at_time (GDateTime * now, GDateTime * time)
break;
}
- return generate_full_format_string (show_day, show_date);
+ return generate_full_format_string (show_day, show_date, settings);
}
diff --git a/src/utils.h b/src/utils.h
index 3b0d0a2..24eddb6 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -24,6 +24,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define __DATETIME_UTILS_H__
#include <glib.h>
+#include <gio/gio.h> /* GSettings */
G_BEGIN_DECLS
@@ -33,7 +34,8 @@ void split_settings_location (const char * location,
char ** zone,
char ** name);
-gchar * get_current_zone_name (const char * location);
+gchar * get_current_zone_name (const char * location,
+ GSettings * settings);
gchar* join_date_and_time_format_strings (const char * date_fmt,
const char * time_fmt);
@@ -43,16 +45,20 @@ gchar* join_date_and_time_format_strings (const char * date_fmt,
const gchar * get_terse_time_format_string (GDateTime * time);
-const gchar * get_full_time_format_string (void);
+const gchar * get_terse_header_time_format_string (void);
+
+const gchar * get_full_time_format_string (GSettings * settings);
gchar * generate_terse_format_string_at_time (GDateTime * now,
GDateTime * time);
gchar * generate_full_format_string (gboolean show_day,
- gboolean show_date);
+ gboolean show_date,
+ GSettings * settings);
gchar * generate_full_format_string_at_time (GDateTime * now,
- GDateTime * time);
+ GDateTime * time,
+ GSettings * settings);
G_END_DECLS
diff --git a/tests/Makefile.am b/tests/Makefile.am
index dce6076..e204378 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -50,3 +50,9 @@ test_indicator_CPPFLAGS = $(TEST_CPPFLAGS) -DSCHEMA_DIR="\"$(top_builddir)/tests
###
###
###
+
+TESTS += test-utils
+check_PROGRAMS += test-utils
+test_utils_SOURCES = test-utils.cc
+test_utils_LDADD = $(top_builddir)/src/libindicator-datetime-service.a $(TEST_LIBS)
+test_utils_CPPFLAGS = $(TEST_CPPFLAGS) -DSCHEMA_DIR="\"$(top_builddir)/tests/\""
diff --git a/tests/Makefile.am.strings b/tests/Makefile.am.strings
index 26a23a8..4a89e8f 100644
--- a/tests/Makefile.am.strings
+++ b/tests/Makefile.am.strings
@@ -29,7 +29,7 @@ test-space-ellipsis: $(top_srcdir)/po
test-ascii-quotes: $(top_srcdir)/po
@echo "#!/bin/bash" > $@
@echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@
- @echo "grep -c -e \"^msgid \\\".*'.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII apostrophy found in user visible strings\" >&2 && exit 1" >> $@
+ @echo "grep -c -e \"^msgid \\\".*'.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII apostrophe found in user visible strings\" >&2 && exit 1" >> $@
@echo "grep -c -e \"^msgid \\\".*\\\".*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII quote found in user visible strings\" >&2 && exit 1" >> $@
@echo "grep -c -e \"^msgid \\\".*\\\`.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII backtick found in user visible strings\" >&2 && exit 1" >> $@
@echo "exit 0" >> $@
diff --git a/tests/planner-mock.c b/tests/planner-mock.c
new file mode 100644
index 0000000..e67ad7e
--- /dev/null
+++ b/tests/planner-mock.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "planner-mock.h"
+
+struct _IndicatorDatetimePlannerMockPriv
+{
+ gboolean is_configured;
+};
+
+typedef IndicatorDatetimePlannerMockPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorDatetimePlannerMock,
+ indicator_datetime_planner_mock,
+ INDICATOR_TYPE_DATETIME_PLANNER)
+
+/***
+**** IndicatorDatetimePlanner virtual funcs
+***/
+
+static void
+my_get_appointments (IndicatorDatetimePlanner * planner,
+ GDateTime * begin_datetime,
+ GDateTime * end_datetime G_GNUC_UNUSED,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask * task;
+ GSList * appointments;
+ struct IndicatorDatetimeAppt * appt;
+ struct IndicatorDatetimeAppt * prev;
+
+ task = g_task_new (planner, NULL, callback, user_data);
+
+ /**
+ *** Build the appointments list
+ **/
+
+ appointments = NULL;
+
+ /* add a daily appointment that occurs at the beginning of the next minute */
+ appt = g_slice_new0 (struct IndicatorDatetimeAppt);
+ appt->is_daily = TRUE;
+ appt->begin = g_date_time_add_seconds (begin_datetime, 60-g_date_time_get_seconds(begin_datetime));
+ appt->end = g_date_time_add_minutes (appt->begin, 1);
+ appt->color = g_strdup ("#00FF00");
+ appt->is_event = TRUE;
+ appt->summary = g_strdup ("Daily alarm");
+ appt->uid = g_strdup ("this uid isn't very random.");
+ appt->has_alarms = TRUE;
+ appt->url = g_strdup ("alarm:///some-alarm-info-goes-here");
+ appointments = g_slist_prepend (appointments, appt);
+ prev = appt;
+
+ /* and add one for a minute later that has an alarm uri */
+ appt = g_slice_new0 (struct IndicatorDatetimeAppt);
+ appt->is_daily = TRUE;
+ appt->begin = g_date_time_add_minutes (prev->end, 1);
+ appt->end = g_date_time_add_minutes (appt->begin, 1);
+ appt->color = g_strdup ("#0000FF");
+ appt->is_event = TRUE;
+ appt->summary = g_strdup ("Second Daily alarm");
+ appt->uid = g_strdup ("this uid isn't very random either.");
+ appt->has_alarms = FALSE;
+ appointments = g_slist_prepend (appointments, appt);
+
+ /* done */
+ g_task_return_pointer (task, appointments, NULL);
+ g_object_unref (task);
+}
+
+static GSList *
+my_get_appointments_finish (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
+ GAsyncResult * res,
+ GError ** error)
+{
+ return g_task_propagate_pointer (G_TASK(res), error);
+}
+
+static gboolean
+my_is_configured (IndicatorDatetimePlanner * planner)
+{
+ IndicatorDatetimePlannerMock * self;
+ self = INDICATOR_DATETIME_PLANNER_MOCK (planner);
+ return self->priv->is_configured;
+}
+
+static void
+my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED)
+{
+ g_message ("%s %s", G_STRLOC, G_STRFUNC);
+}
+
+static void
+my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
+ GDateTime * activate_time)
+{
+ gchar * str = g_date_time_format (activate_time, "%F %T");
+ g_message ("%s %s: %s", G_STRLOC, G_STRFUNC, str);
+ g_free (str);
+}
+
+/***
+**** GObject virtual funcs
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ G_OBJECT_CLASS (indicator_datetime_planner_mock_parent_class)->dispose (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_datetime_planner_mock_class_init (IndicatorDatetimePlannerMockClass * klass)
+{
+ GObjectClass * object_class;
+ IndicatorDatetimePlannerClass * planner_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = my_dispose;
+
+ planner_class = INDICATOR_DATETIME_PLANNER_CLASS (klass);
+ planner_class->is_configured = my_is_configured;
+ planner_class->activate = my_activate;
+ planner_class->activate_time = my_activate_time;
+ planner_class->get_appointments = my_get_appointments;
+ planner_class->get_appointments_finish = my_get_appointments_finish;
+
+ g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerMockPriv));
+}
+
+static void
+indicator_datetime_planner_mock_init (IndicatorDatetimePlannerMock * self)
+{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_PLANNER_MOCK,
+ IndicatorDatetimePlannerMockPriv);
+
+ p->is_configured = TRUE;
+
+ self->priv = p;
+}
+
+/***
+**** Public
+***/
+
+IndicatorDatetimePlanner *
+indicator_datetime_planner_mock_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_MOCK, NULL);
+
+ return INDICATOR_DATETIME_PLANNER (o);
+}
diff --git a/tests/planner-mock.h b/tests/planner-mock.h
new file mode 100644
index 0000000..8d7d7c2
--- /dev/null
+++ b/tests/planner-mock.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INDICATOR_DATETIME_PLANNER_MOCK__H__
+#define __INDICATOR_DATETIME_PLANNER_MOCK__H__
+
+#include "planner.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_PLANNER_MOCK (indicator_datetime_planner_mock_get_type())
+#define INDICATOR_DATETIME_PLANNER_MOCK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK, IndicatorDatetimePlannerMock))
+#define INDICATOR_DATETIME_PLANNER_MOCK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK, IndicatorDatetimePlannerMockClass))
+#define INDICATOR_IS_DATETIME_PLANNER_MOCK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_MOCK))
+
+typedef struct _IndicatorDatetimePlannerMock IndicatorDatetimePlannerMock;
+typedef struct _IndicatorDatetimePlannerMockPriv IndicatorDatetimePlannerMockPriv;
+typedef struct _IndicatorDatetimePlannerMockClass IndicatorDatetimePlannerMockClass;
+
+GType indicator_datetime_planner_mock_get_type (void);
+
+/**
+ * An IndicatorDatetimePlanner which uses Evolution Data Server
+ * to get its list of appointments.
+ */
+struct _IndicatorDatetimePlannerMock
+{
+ /*< private >*/
+ IndicatorDatetimePlanner parent;
+ IndicatorDatetimePlannerMockPriv * priv;
+};
+
+struct _IndicatorDatetimePlannerMockClass
+{
+ IndicatorDatetimePlannerClass parent_class;
+};
+
+IndicatorDatetimePlanner * indicator_datetime_planner_mock_new (void);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_PLANNER_MOCK__H__ */
diff --git a/tests/test-utils.cc b/tests/test-utils.cc
new file mode 100644
index 0000000..d0f277b
--- /dev/null
+++ b/tests/test-utils.cc
@@ -0,0 +1,112 @@
+/*
+Copyright 2012 Canonical Ltd.
+
+Authors:
+ Charles Kerr <charles.kerr@canonical.com>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <gtest/gtest.h>
+
+#include <glib-object.h>
+
+#include "utils.h"
+
+/***
+****
+***/
+
+TEST (UtilsTest, SplitSettingsLocation)
+{
+ guint i;
+ guint n;
+
+ struct {
+ const char * location;
+ const char * expected_zone;
+ const char * expected_name;
+ } test_cases[] = {
+ { "America/Chicago Chicago", "America/Chicago", "Chicago" },
+ { "America/Chicago Oklahoma City", "America/Chicago", "Oklahoma City" },
+ { "America/Los_Angeles", "America/Los_Angeles", "Los Angeles" },
+ { "America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" },
+ { " America/Los_Angeles", "America/Los_Angeles", "Los Angeles" },
+ { " America/Los_Angeles ", "America/Los_Angeles", "Los Angeles" },
+ { "UTC UTC", "UTC", "UTC" }
+ };
+
+ for (i=0, n=G_N_ELEMENTS(test_cases); i<n; i++)
+ {
+ char * zone = NULL;
+ char * name = NULL;
+
+ split_settings_location (test_cases[i].location, &zone, &name);
+ ASSERT_STREQ (test_cases[i].expected_zone, zone);
+ ASSERT_STREQ (test_cases[i].expected_name, name);
+
+ g_free (zone);
+ g_free (name);
+ }
+}
+
+/***
+****
+***/
+
+#define EM_SPACE "\xE2\x80\x82"
+
+TEST (UtilsTest, GenerateTerseFormatString)
+{
+ guint i;
+ guint n;
+ GDateTime * arbitrary_day = g_date_time_new_local (2013, 6, 25, 12, 34, 56);
+ GDateTime * on_the_hour = g_date_time_new_local (2013, 6, 25, 12, 0, 0);
+
+ struct {
+ GDateTime * now;
+ GDateTime * time;
+ const char * expected_format_string;
+ } test_cases[] = {
+ { g_date_time_ref(arbitrary_day), g_date_time_ref(arbitrary_day), "%l:%M %p" }, /* identical time */
+ { g_date_time_ref(arbitrary_day), g_date_time_add_hours(arbitrary_day,1), "%l:%M %p" }, /* later today */
+ { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,1), "Tomorrow" EM_SPACE "%l:%M %p" }, /* tomorrow */
+ { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,2), "%a" EM_SPACE "%l:%M %p" },
+ { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,6), "%a" EM_SPACE "%l:%M %p" },
+ { g_date_time_ref(arbitrary_day), g_date_time_add_days(arbitrary_day,7), "%d %b" EM_SPACE "%l:%M %p" }, /* over one week away */
+
+ { g_date_time_ref(on_the_hour), g_date_time_ref(on_the_hour), "%l %p" }, /* identical time */
+ { g_date_time_ref(on_the_hour), g_date_time_add_hours(on_the_hour,1), "%l %p" }, /* later today */
+ { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,1), "Tomorrow" EM_SPACE "%l %p" }, /* tomorrow */
+ { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,2), "%a" EM_SPACE "%l %p" },
+ { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,6), "%a" EM_SPACE "%l %p" },
+ { g_date_time_ref(on_the_hour), g_date_time_add_days(on_the_hour,7), "%d %b" EM_SPACE "%l %p" }, /* over one week away */
+ };
+
+ for (i=0, n=G_N_ELEMENTS(test_cases); i<n; i++)
+ {
+ char * format_string;
+
+ format_string = generate_terse_format_string_at_time (test_cases[i].now,
+ test_cases[i].time);
+
+ ASSERT_STREQ (test_cases[i].expected_format_string, format_string);
+
+ g_free (format_string);
+ g_date_time_unref (test_cases[i].now);
+ g_date_time_unref (test_cases[i].time);
+ }
+
+ g_date_time_unref (arbitrary_day);
+ g_date_time_unref (on_the_hour);
+}