aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2013-06-21 15:01:54 +0000
committerTarmac <Unknown>2013-06-21 15:01:54 +0000
commit854fb1a47bed41e41e6fed9c5e498dc81146302a (patch)
treec0db59a66b90162a7f8ae2519430249ce5a5ffb6 /src
parent05ba6fa097260473c774d0d077d4e3add3db134b (diff)
parent51bd7465ac148a34ec37cb8760daf3b282b5ed6a (diff)
downloadayatana-indicator-datetime-854fb1a47bed41e41e6fed9c5e498dc81146302a.tar.gz
ayatana-indicator-datetime-854fb1a47bed41e41e6fed9c5e498dc81146302a.tar.bz2
ayatana-indicator-datetime-854fb1a47bed41e41e6fed9c5e498dc81146302a.zip
This push extracts geoclue to its own self-contained module, fixes an errant g_critical, and merges ryan's timedate1 changes from the 13.04 branch.
Approved by PS Jenkins bot, Lars Uebernickel.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am62
-rw-r--r--src/datetime-interface.c200
-rw-r--r--src/datetime-interface.h57
-rw-r--r--src/datetime-prefs-locations.c9
-rw-r--r--src/datetime-prefs.c64
-rw-r--r--src/datetime-service.c1508
-rw-r--r--src/datetime-service.xml11
-rw-r--r--src/dbus-shared.h22
-rw-r--r--src/indicator-datetime.c1535
-rw-r--r--src/main.c63
-rw-r--r--src/planner-eds.c330
-rw-r--r--src/planner-eds.h60
-rw-r--r--src/planner.c232
-rw-r--r--src/planner.h137
-rw-r--r--src/service.c1804
-rw-r--r--src/service.h75
-rw-r--r--src/timezone-file.c240
-rw-r--r--src/timezone-file.h58
-rw-r--r--src/timezone-geoclue.c252
-rw-r--r--src/timezone-geoclue.h57
-rw-r--r--src/timezone.c105
-rw-r--r--src/timezone.h74
-rw-r--r--src/utils.c30
-rw-r--r--src/utils.h3
24 files changed, 3546 insertions, 3442 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index a31cb4b..7143c6b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,16 +7,27 @@ endif
libexec_PROGRAMS = indicator-datetime-service
indicator_datetime_service_SOURCES = \
- datetime-interface.c \
- datetime-interface.h \
- gen-datetime-service.xml.c \
- datetime-service.c \
+ 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
+
indicator_datetime_service_CFLAGS = \
-Wall \
+ -Wextra -Wno-missing-field-initializers \
-Werror \
$(SERVICE_CFLAGS) \
$(COVERAGE_CFLAGS) \
@@ -27,28 +38,6 @@ indicator_datetime_service_LDADD = \
indicator_datetime_service_LDFLAGS = \
$(COVERAGE_LDFLAGS)
-datetimelibdir = $(INDICATORDIR)
-datetimelib_LTLIBRARIES = libdatetime.la
-libdatetime_la_SOURCES = \
- gen-datetime-service.xml.h \
- dbus-shared.h \
- settings-shared.h \
- utils.c \
- utils.h \
- indicator-datetime.c
-libdatetime_la_CFLAGS = \
- $(INDICATOR_CFLAGS) \
- $(COVERAGE_CFLAGS) \
- -Wall -Werror \
- -DTIMEZONE_FILE="\"/etc/timezone\"" \
- -DG_LOG_DOMAIN=\"Indicator-Datetime\"
-libdatetime_la_LIBADD = \
- $(INDICATOR_LIBS)
-libdatetime_la_LDFLAGS = \
- $(COVERAGE_LDFLAGS) \
- -module \
- -avoid-version
-
if BUILD_CCPANEL
libindicator_datetime_la_SOURCES =\
datetime-prefs.c \
@@ -70,24 +59,3 @@ libindicator_datetime_la_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
-module -avoid-version
endif
-
-gen-%.xml.c: %.xml
- @echo "Building $@ from $<"
- @echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@
- @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@
- @echo ";" >> $@
-
-gen-%.xml.h: %.xml
- @echo "Building $@ from $<"
- @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@
-
-BUILT_SOURCES = \
- gen-datetime-service.xml.c \
- gen-datetime-service.xml.h
-
-
-CLEANFILES = \
- $(BUILT_SOURCES)
-
-EXTRA_DIST = \
- datetime-service.xml
diff --git a/src/datetime-interface.c b/src/datetime-interface.c
deleted file mode 100644
index e67be85..0000000
--- a/src/datetime-interface.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
-An indicator to time and date related information in the menubar.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gio/gio.h>
-
-#include "datetime-interface.h"
-#include "gen-datetime-service.xml.h"
-#include "dbus-shared.h"
-
-/**
- DatetimeInterfacePrivate:
- @dbus_registration: The handle for this object being registered
- on dbus.
-
- Structure to define the memory for the private area
- of the datetime interface instance.
-*/
-struct _DatetimeInterfacePrivate {
- GDBusConnection * bus;
- GCancellable * bus_cancel;
- guint dbus_registration;
-};
-
-#define DATETIME_INTERFACE_GET_PRIVATE(o) (DATETIME_INTERFACE(o)->priv)
-
-/* GDBus Stuff */
-static GDBusNodeInfo * node_info = NULL;
-static GDBusInterfaceInfo * interface_info = NULL;
-
-static void datetime_interface_class_init (DatetimeInterfaceClass *klass);
-static void datetime_interface_init (DatetimeInterface *self);
-static void datetime_interface_dispose (GObject *object);
-static void datetime_interface_finalize (GObject *object);
-static void bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data);
-
-G_DEFINE_TYPE (DatetimeInterface, datetime_interface, G_TYPE_OBJECT);
-
-static void
-datetime_interface_class_init (DatetimeInterfaceClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (DatetimeInterfacePrivate));
-
- object_class->dispose = datetime_interface_dispose;
- object_class->finalize = datetime_interface_finalize;
-
- /* Setting up the DBus interfaces */
- if (node_info == NULL) {
- GError * error = NULL;
-
- node_info = g_dbus_node_info_new_for_xml(_datetime_service, &error);
- if (error != NULL) {
- g_error("Unable to parse Datetime Service Interface description: %s", error->message);
- g_error_free(error);
- }
- }
-
- if (interface_info == NULL) {
- interface_info = g_dbus_node_info_lookup_interface(node_info, SERVICE_IFACE);
-
- if (interface_info == NULL) {
- g_error("Unable to find interface '" SERVICE_IFACE "'");
- }
- }
-
- return;
-}
-
-static void
-datetime_interface_init (DatetimeInterface *self)
-{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, DATETIME_INTERFACE_TYPE, DatetimeInterfacePrivate);
-
- self->priv->bus = NULL;
- self->priv->bus_cancel = NULL;
- self->priv->dbus_registration = 0;
-
- self->priv->bus_cancel = g_cancellable_new();
- g_bus_get(G_BUS_TYPE_SESSION,
- self->priv->bus_cancel,
- bus_get_cb,
- self);
-
- return;
-}
-
-static void
-bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data)
-{
- GError * error = NULL;
- GDBusConnection * connection = g_bus_get_finish(res, &error);
-
- if (error != NULL) {
- g_error("OMG! Unable to get a connection to DBus: %s", error->message);
- g_error_free(error);
- return;
- }
-
- DatetimeInterfacePrivate * priv = DATETIME_INTERFACE_GET_PRIVATE(user_data);
-
- g_warn_if_fail(priv->bus == NULL);
- priv->bus = connection;
-
- g_clear_object (&priv->bus_cancel);
-
- /* Now register our object on our new connection */
- priv->dbus_registration = g_dbus_connection_register_object(priv->bus,
- SERVICE_OBJ,
- interface_info,
- NULL,
- user_data,
- NULL,
- &error);
-
- if (error != NULL) {
- g_error("Unable to register the object to DBus: %s", error->message);
- g_error_free(error);
- return;
- }
-
- return;
-}
-
-static void
-datetime_interface_dispose (GObject *object)
-{
- DatetimeInterfacePrivate * priv = DATETIME_INTERFACE_GET_PRIVATE(object);
-
- if (priv->dbus_registration != 0) {
- g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
- /* Don't care if it fails, there's nothing we can do */
- priv->dbus_registration = 0;
- }
-
- g_clear_object (&priv->bus);
-
- if (priv->bus_cancel != NULL) {
- g_cancellable_cancel(priv->bus_cancel);
- g_clear_object (&priv->bus_cancel);
- }
-
- G_OBJECT_CLASS (datetime_interface_parent_class)->dispose (object);
- return;
-}
-
-static void
-datetime_interface_finalize (GObject *object)
-{
-
- G_OBJECT_CLASS (datetime_interface_parent_class)->finalize (object);
- return;
-}
-
-void
-datetime_interface_update (DatetimeInterface *self)
-{
- g_return_if_fail(IS_DATETIME_INTERFACE(self));
-
- DatetimeInterfacePrivate * priv = DATETIME_INTERFACE_GET_PRIVATE(self);
- GError * error = NULL;
-
- g_dbus_connection_emit_signal (priv->bus,
- NULL,
- SERVICE_OBJ,
- SERVICE_IFACE,
- "UpdateTime",
- NULL,
- &error);
-
- if (error != NULL) {
- g_error("Unable to send UpdateTime signal: %s", error->message);
- g_error_free(error);
- return;
- }
-
- return;
-}
diff --git a/src/datetime-interface.h b/src/datetime-interface.h
deleted file mode 100644
index ae85605..0000000
--- a/src/datetime-interface.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-An indicator to time and date related information in the menubar.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@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 __DATETIME_INTERFACE_H__
-#define __DATETIME_INTERFACE_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define DATETIME_INTERFACE_TYPE (datetime_interface_get_type ())
-#define DATETIME_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DATETIME_INTERFACE_TYPE, DatetimeInterface))
-#define DATETIME_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DATETIME_INTERFACE_TYPE, DatetimeInterfaceClass))
-#define IS_DATETIME_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DATETIME_INTERFACE_TYPE))
-#define IS_DATETIME_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DATETIME_INTERFACE_TYPE))
-#define DATETIME_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DATETIME_INTERFACE_TYPE, DatetimeInterfaceClass))
-
-typedef struct _DatetimeInterface DatetimeInterface;
-typedef struct _DatetimeInterfacePrivate DatetimeInterfacePrivate;
-typedef struct _DatetimeInterfaceClass DatetimeInterfaceClass;
-
-struct _DatetimeInterfaceClass {
- GObjectClass parent_class;
-
- void (*update_time) (void);
-};
-
-struct _DatetimeInterface {
- GObject parent;
- DatetimeInterfacePrivate * priv;
-};
-
-GType datetime_interface_get_type (void);
-void datetime_interface_update (DatetimeInterface *self);
-
-G_END_DECLS
-
-#endif
diff --git a/src/datetime-prefs-locations.c b/src/datetime-prefs-locations.c
index 33fd660..4bbf053 100644
--- a/src/datetime-prefs-locations.c
+++ b/src/datetime-prefs-locations.c
@@ -25,6 +25,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#include <stdlib.h>
+#include <time.h> /* time_t */
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <timezonemap/timezone-completion.h>
@@ -416,10 +417,9 @@ update_times (GtkWidget * dlg)
g_signal_handlers_block_by_func (store, save_when_idle, dlg);
- GDateTime * now = g_date_time_new_now_local ();
-
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
+ GDateTime * now = g_date_time_new_now_local ();
do {
gchar * strzone;
@@ -428,7 +428,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_format_string_at_time (now_tz);
+ gchar * format = generate_format_string_at_time (now, now_tz);
gchar * time_str = g_date_time_format (now_tz, format);
gchar * old_time_str;
@@ -444,10 +444,9 @@ update_times (GtkWidget * dlg)
}
g_free (strzone);
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+ g_date_time_unref (now);
}
- g_date_time_unref (now);
-
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 d7f0def..1eaaf1e 100644
--- a/src/datetime-prefs.c
+++ b/src/datetime-prefs.c
@@ -58,6 +58,7 @@ struct _IndicatorDatetimePanel
struct _IndicatorDatetimePanelPrivate
{
+ guint name_watch_id;
GtkBuilder * builder;
GDBusProxy * proxy;
GtkWidget * auto_radio;
@@ -175,7 +176,8 @@ polkit_perm_ready (GObject *source_object, GAsyncResult *res, gpointer user_data
GPermission * permission = polkit_permission_new_finish (res, &error);
if (error != NULL) {
- g_warning ("Could not get permission object: %s", error->message);
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not get permission object: %s", error->message);
g_error_free (error);
return;
}
@@ -191,7 +193,8 @@ dbus_set_answered (GObject *object, GAsyncResult *res, gpointer command)
GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
if (error != NULL) {
- g_warning("Could not set '%s' using timedated: %s", (gchar *)command, error->message);
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning("Could not set '%s' using timedated: %s", (gchar *)command, error->message);
g_error_free(error);
return;
}
@@ -246,7 +249,8 @@ proxy_ready (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self)
self->priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
if (error != NULL) {
- g_critical("Could not grab DBus proxy for timedated: %s", error->message);
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_critical("Could not grab DBus proxy for timedated: %s", error->message);
g_error_free(error);
return;
}
@@ -286,33 +290,30 @@ proxy_ready (GObject *object, GAsyncResult *res, IndicatorDatetimePanel * self)
}
}
+#define WIG(name) GTK_WIDGET (gtk_builder_get_object(self->priv->builder, name))
+
static void
-service_name_owner_changed (GDBusProxy * proxy, GParamSpec *pspec, gpointer user_data)
+set_show_clock_check_sensitive (IndicatorDatetimePanel * self,
+ gboolean sensitive)
{
- GtkWidget * widget = GTK_WIDGET (user_data);
- gchar * owner = g_dbus_proxy_get_name_owner (proxy);
-
- gtk_widget_set_sensitive (widget, (owner != NULL));
-
- g_free (owner);
+ gtk_widget_set_sensitive (WIG("showClockCheck"), sensitive);
}
static void
-service_proxy_ready (GObject *object, GAsyncResult *res, gpointer user_data)
+on_bus_name_appeared (GDBusConnection * connection G_GNUC_UNUSED,
+ const char * name G_GNUC_UNUSED,
+ const char * name_owner,
+ gpointer self)
{
- GError * error = NULL;
-
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
- if (error != NULL) {
- g_critical("Could not grab DBus proxy for indicator-datetime-service: %s", error->message);
- g_error_free(error);
- return;
- }
+ set_show_clock_check_sensitive (self, name_owner && *name_owner);
+}
- /* And now, do initial proxy configuration */
- g_signal_connect (proxy, "notify::g-name-owner", G_CALLBACK (service_name_owner_changed), user_data);
- service_name_owner_changed (proxy, NULL, user_data);
+static void
+on_bus_name_vanished (GDBusConnection * connection G_GNUC_UNUSED,
+ const char * name G_GNUC_UNUSED,
+ gpointer self)
+{
+ set_show_clock_check_sensitive (self, FALSE);
}
static gboolean
@@ -656,7 +657,6 @@ indicator_datetime_panel_init (IndicatorDatetimePanel * self)
GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
-#define WIG(name) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, name))
/* Add policykit button */
GtkWidget * polkit_button = gtk_lock_button_new (NULL);
@@ -745,10 +745,13 @@ indicator_datetime_panel_init (IndicatorDatetimePanel * self)
but that doesn't yet claim a name on the bus. Presumably the service
would have been started by any such indicator, so this will at least tell
us if there *was* a datetime module run this session. */
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
- SERVICE_NAME, SERVICE_OBJ, SERVICE_IFACE,
- NULL, (GAsyncReadyCallback)service_proxy_ready,
- WIG ("showClockCheck"));
+ self->priv->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+ BUS_NAME,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_bus_name_appeared,
+ on_bus_name_vanished,
+ self,
+ NULL);
#undef WIG
@@ -772,6 +775,11 @@ indicator_datetime_panel_dispose (GObject * object)
priv->loc_dlg = NULL;
}
+ if (priv->name_watch_id != 0) {
+ g_bus_unwatch_name (priv->name_watch_id);
+ priv->name_watch_id = 0;
+ }
+
if (priv->save_time_id) {
g_source_remove (priv->save_time_id);
priv->save_time_id = 0;
diff --git a/src/datetime-service.c b/src/datetime-service.c
deleted file mode 100644
index cd8f7a6..0000000
--- a/src/datetime-service.c
+++ /dev/null
@@ -1,1508 +0,0 @@
-/*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
-An indicator to time and date related information in the menubar.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@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 <libindicator/indicator-service.h>
-#include <locale.h>
-
-#include <gtk/gtk.h>
-#include <gdk/gdk.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#include <math.h>
-
-#include <libdbusmenu-gtk/menuitem.h>
-#include <libdbusmenu-glib/server.h>
-#include <libdbusmenu-glib/client.h>
-#include <libdbusmenu-glib/menuitem.h>
-
-#include <geoclue/geoclue-master.h>
-#include <geoclue/geoclue-master-client.h>
-
-#include <time.h>
-#include <libecal/libecal.h>
-#include <libical/ical.h>
-#include <libedataserver/libedataserver.h>
-// Other users of ecal seem to also include these, not sure why they should be included by the above
-#include <libical/icaltime.h>
-#include <cairo/cairo.h>
-
-#include "datetime-interface.h"
-#include "dbus-shared.h"
-#include "settings-shared.h"
-#include "utils.h"
-
-/* how often to check for clock skew */
-#define SKEW_CHECK_INTERVAL_SEC 10
-
-#define MAX_APPOINTMENT_MENUITEMS 5
-
-#define SKEW_DIFF_THRESHOLD_SEC (SKEW_CHECK_INTERVAL_SEC + 5)
-
-#ifdef HAVE_CCPANEL
- #define SETTINGS_APP_INVOCATION "gnome-control-center indicator-datetime"
-#else
- #define SETTINGS_APP_INVOCATION "gnome-control-center datetime"
-#endif
-
-static gboolean update_appointment_menu_items (gpointer user_data);
-static void update_location_menu_items (void);
-static void day_timer_reset (void);
-static gboolean get_greeter_mode (void);
-
-static void quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data);
-
-static IndicatorService * service = NULL;
-static GMainLoop * mainloop = NULL;
-static DbusmenuServer * server = NULL;
-static DbusmenuMenuitem * root = NULL;
-static DatetimeInterface * dbus = NULL;
-
-/* Global Items */
-static DbusmenuMenuitem * date = NULL;
-static DbusmenuMenuitem * calendar = NULL;
-static DbusmenuMenuitem * settings = NULL;
-static DbusmenuMenuitem * events_separator = NULL;
-static DbusmenuMenuitem * locations_separator = NULL;
-static DbusmenuMenuitem * add_appointment = NULL;
-static DbusmenuMenuitem * appointments[MAX_APPOINTMENT_MENUITEMS];
-static GSList * location_menu_items = NULL;
-static GList * comp_instances = NULL;
-static gboolean updating_appointments = FALSE;
-static time_t start_time_appointments = (time_t) 0;
-static GSettings * conf = NULL;
-static ESourceRegistry * source_registry = NULL;
-static GList * appointment_sources = NULL;
-
-
-/* Our 2 important timezones */
-static gchar * current_timezone = NULL;
-static gchar * geo_timezone = NULL;
-
-struct comp_instance {
- ECalComponent *comp;
- time_t start;
- time_t end;
- ESource *source;
-};
-
-/**
- * A temp struct used by update_location_menu_items() for pruning duplicates and sorting.
- */
-struct TimeLocation
-{
- gint32 offset;
- gchar * zone;
- gchar * name;
- gboolean visible;
-};
-static void
-time_location_free (struct TimeLocation * loc)
-{
- g_free (loc->name);
- g_free (loc->zone);
- g_free (loc);
-}
-static struct TimeLocation*
-time_location_new (const char * zone, const char * name, gboolean visible, time_t now)
-{
- struct TimeLocation * loc = g_new (struct TimeLocation, 1);
- GTimeZone * tz = g_time_zone_new (zone);
- gint interval = g_time_zone_find_interval (tz, G_TIME_TYPE_UNIVERSAL, now);
- loc->offset = g_time_zone_get_offset (tz, interval);
- loc->zone = g_strdup (zone);
- loc->name = g_strdup (name);
- loc->visible = visible;
- g_time_zone_unref (tz);
- g_debug ("%s zone '%s' name '%s' offset is %d", G_STRLOC, zone, name, (int)loc->offset);
- return loc;
-}
-static int
-time_location_compare (const struct TimeLocation * a, const struct TimeLocation * b)
-{
- int ret = a->offset - b->offset; /* primary key */
- if (!ret)
- ret = g_strcmp0 (a->name, b->name); /* secondary key */
- if (!ret)
- ret = a->visible - b->visible; /* tertiary key */
- g_debug ("%s comparing '%s' (%d) to '%s' (%d), returning %d", G_STRLOC, a->name, (int)a->offset, b->name, (int)b->offset, ret);
- return ret;
-}
-static GSList*
-locations_add (GSList * locations, const char * zone, const char * name, gboolean visible, time_t now)
-{
- struct TimeLocation * loc = time_location_new (zone, name, visible, now);
-
- if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare) == NULL) {
- g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name);
- locations = g_slist_append (locations, loc);
- } else {
- g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name);
- time_location_free (loc);
- }
- return locations;
-}
-
-/* Update the timezone entries */
-static void
-update_location_menu_items (void)
-{
- /* if we're in greeter mode, don't bother */
- if (locations_separator == NULL)
- return;
-
- /* remove the previous locations */
- while (location_menu_items != NULL) {
- DbusmenuMenuitem * item = DBUSMENU_MENUITEM(location_menu_items->data);
- location_menu_items = g_slist_remove(location_menu_items, item);
- dbusmenu_menuitem_child_delete(root, DBUSMENU_MENUITEM(item));
- g_object_unref(G_OBJECT(item));
- }
-
- /***
- **** Build a list of locations to add: use geo_timezone,
- **** current_timezone, and SETTINGS_LOCATIONS_S, but omit duplicates.
- ***/
-
- GSList * locations = NULL;
- const time_t now = time(NULL);
-
- /* maybe add geo_timezone */
- if (geo_timezone != NULL) {
- const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S);
- gchar * name = get_current_zone_name (geo_timezone);
- locations = locations_add (locations, geo_timezone, name, visible, now);
- g_free (name);
- }
-
- /* maybe add current_timezone */
- if (current_timezone != NULL) {
- const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S);
- gchar * name = get_current_zone_name (current_timezone);
- locations = locations_add (locations, current_timezone, name, visible, now);
- g_free (name);
- }
-
- /* maybe add the user-specified custom locations */
- gchar ** user_locations = g_settings_get_strv (conf, SETTINGS_LOCATIONS_S);
- if (user_locations != NULL) {
- gint i;
- const guint location_count = g_strv_length (user_locations);
- const gboolean visible = g_settings_get_boolean (conf, SETTINGS_SHOW_LOCATIONS_S);
- g_debug ("%s Found %u user-specified locations", G_STRLOC, location_count);
- for (i=0; i<location_count; i++) {
- gchar * zone;
- gchar * name;
- split_settings_location (user_locations[i], &zone, &name);
- locations = locations_add (locations, zone, name, visible, now);
- g_free (name);
- g_free (zone);
- }
- g_strfreev (user_locations);
- user_locations = NULL;
- }
-
- /* finally create menuitems for each location */
- gint offset = dbusmenu_menuitem_get_position (locations_separator, root)+1;
- GSList * l;
- gboolean have_visible_location = FALSE;
- for (l=locations; l!=NULL; l=l->next) {
- struct TimeLocation * loc = l->data;
- g_debug("%s Adding location: zone '%s', name '%s'", G_STRLOC, loc->zone, loc->name);
- DbusmenuMenuitem * item = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, TIMEZONE_MENUITEM_TYPE);
- dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_NAME, loc->name);
- dbusmenu_menuitem_property_set (item, TIMEZONE_MENUITEM_PROP_ZONE, loc->zone);
- dbusmenu_menuitem_property_set_bool (item, TIMEZONE_MENUITEM_PROP_RADIO, FALSE);
- dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, loc->visible);
- dbusmenu_menuitem_child_add_position (root, item, offset++);
- g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(quick_set_tz), NULL);
- location_menu_items = g_slist_append (location_menu_items, item);
- if (loc->visible)
- have_visible_location = TRUE;
- time_location_free (loc);
- }
- g_slist_free (locations);
- locations = NULL;
-
- /* if there's at least one item being shown, show the separator too */
- dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, have_visible_location);
-}
-
-/* Update the current timezone */
-static void
-update_current_timezone (void) {
- /* Clear old data */
- if (current_timezone != NULL) {
- g_free(current_timezone);
- current_timezone = NULL;
- }
-
- current_timezone = read_timezone ();
- if (current_timezone == NULL) {
- return;
- }
-
- g_debug("System timezone is: %s", current_timezone);
-
- update_location_menu_items();
-
- return;
-}
-
-static void
-quick_set_tz_cb (GObject *object, GAsyncResult *res, gpointer data)
-{
- GError * error = NULL;
- GVariant * answers = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
-
- if (error != NULL) {
- g_warning("Could not set timezone using timedated: %s", error->message);
- g_clear_error (&error);
- return;
- }
-
- g_variant_unref (answers);
-}
-
-static void
-quick_set_tz_proxy_cb (GObject *object, GAsyncResult *res, gpointer zone)
-{
- GError * error = NULL;
-
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
- if (error != NULL) {
- g_warning("Could not grab DBus proxy for timedated: %s", error->message);
- g_clear_error (&error);
- g_free (zone);
- return;
- }
-
- g_dbus_proxy_call (proxy, "SetTimezone", g_variant_new ("(sb)", zone, TRUE),
- G_DBUS_CALL_FLAGS_NONE, -1, NULL, quick_set_tz_cb, NULL);
- g_free (zone);
- g_object_unref (proxy);
-}
-
-static void
-quick_set_tz (DbusmenuMenuitem * menuitem, guint timestamp, gpointer user_data)
-{
- const gchar * tz = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_ZONE);
- g_debug("Quick setting timezone to: %s", tz);
-
- g_return_if_fail(tz != NULL);
-
- const gchar * name = dbusmenu_menuitem_property_get(menuitem, TIMEZONE_MENUITEM_PROP_NAME);
-
- /* Set it in gsettings so we don't lose user's preferred name */
- GSettings * conf = g_settings_new (SETTINGS_INTERFACE);
- gchar * tz_full = g_strdup_printf ("%s %s", tz, name);
- g_settings_set_string (conf, SETTINGS_TIMEZONE_NAME_S, tz_full);
- g_free (tz_full);
- g_object_unref (conf);
-
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
- "org.freedesktop.timedate1",
- "/org/freedesktop/timedate1",
- "org.freedesktop.timedate1",
- NULL, quick_set_tz_proxy_cb, g_strdup (tz));
-
- return;
-}
-
-/* Updates the label in the date menuitem */
-static gboolean
-update_datetime (gpointer user_data)
-{
- GDateTime *datetime;
- gchar *utf8;
-
- g_debug("Updating Date/Time");
-
- datetime = g_date_time_new_now_local ();
- if (datetime == NULL) {
- g_warning("Error getting local time");
- dbusmenu_menuitem_property_set(date, DBUSMENU_MENUITEM_PROP_LABEL, _("Error getting time"));
- g_date_time_unref (datetime);
- return FALSE;
- }
-
- /* eranslators: strftime(3) style date format on top of the menu when you click on the clock */
- utf8 = g_date_time_format (datetime, _("%A, %e %B %Y"));
-
- dbusmenu_menuitem_property_set(date, DBUSMENU_MENUITEM_PROP_LABEL, utf8);
-
- g_date_time_unref (datetime);
- g_free(utf8);
-
- return G_SOURCE_REMOVE;
-}
-
-/* Run a particular program based on an activation */
-static void
-execute_command (const gchar * command)
-{
- GError * error = NULL;
-
- g_debug("Issuing command '%s'", command);
- if (!g_spawn_command_line_async(command, &error)) {
- g_warning("Unable to start %s: %s", (char *)command, error->message);
- g_clear_error (&error);
- }
-}
-
-/* Run a particular program based on an activation */
-static void
-activate_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED,
- guint timestamp G_GNUC_UNUSED,
- const gchar * command)
-{
- execute_command (command);
-}
-
-static gboolean
-update_appointment_menu_items_idle (gpointer user_data)
-{
- update_appointment_menu_items(user_data);
- return FALSE;
-}
-
-static void
-hide_all_appointments (void)
-{
- int i;
-
- for (i=0; i<MAX_APPOINTMENT_MENUITEMS; i++) {
- if (appointments[i]) {
- dbusmenu_menuitem_property_set_bool(appointments[i], DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- dbusmenu_menuitem_property_set_bool(appointments[i], DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- }
- }
-}
-
-static gboolean
-month_changed_cb (DbusmenuMenuitem * menuitem, gchar *name, GVariant *variant, guint timestamp)
-{
- start_time_appointments = (time_t)g_variant_get_uint32(variant);
-
- g_debug("Received month changed with timestamp: %d -> %s",(int)start_time_appointments, ctime(&start_time_appointments));
- /* By default one of the first things we do is
- clear the marks as we don't know the correct
- ones yet and we don't want to confuse the
- user. */
- dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS);
-
- g_idle_add(update_appointment_menu_items_idle, NULL);
- return TRUE;
-}
-
-static gboolean
-day_selected_cb (DbusmenuMenuitem * menuitem, gchar *name, GVariant *variant, guint timestamp)
-{
- time_t new_time = (time_t)g_variant_get_uint32(variant);
- g_warn_if_fail(new_time != 0);
-
- if (start_time_appointments == 0 || new_time == 0) {
- /* If we've got nothing, assume everyhting is going to
- get repopulated, let's start with a clean slate */
- dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS);
- } else {
- /* No check to see if we changed months. If we did we'll
- want to clear the marks. Otherwise we're cool keeping
- them around. */
- struct tm start_tm;
- struct tm new_tm;
-
- localtime_r(&start_time_appointments, &start_tm);
- localtime_r(&new_time, &new_tm);
-
- if (start_tm.tm_mon != new_tm.tm_mon) {
- dbusmenu_menuitem_property_remove(menuitem, CALENDAR_MENUITEM_PROP_MARKS);
- }
- }
-
- start_time_appointments = new_time;
-
- g_debug("Received day-selected with timestamp: %d -> %s",(int)start_time_appointments, ctime(&start_time_appointments));
- g_idle_add(update_appointment_menu_items_idle, NULL);
-
- return TRUE;
-}
-
-static gboolean
-day_selected_double_click_cb (DbusmenuMenuitem * menuitem G_GNUC_UNUSED,
- gchar * name G_GNUC_UNUSED,
- GVariant * variant,
- guint timestamp G_GNUC_UNUSED)
-{
- const time_t evotime = (time_t)g_variant_get_uint32(variant);
-
- g_debug("Received day-selected-double-click with timestamp: %d -> %s",(int)evotime, ctime(&evotime));
-
- gchar *ad = isodate_from_time_t(evotime);
- gchar *cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL);
-
- execute_command (cmd);
-
- g_free (cmd);
- g_free (ad);
-
- return TRUE;
-}
-
-static guint ecaltimer = 0;
-
-static void
-start_ecal_timer(void)
-{
- if (ecaltimer != 0) {
- g_source_remove(ecaltimer);
- ecaltimer = 0;
- }
- if (update_appointment_menu_items(NULL))
- ecaltimer = g_timeout_add_seconds(60*5, update_appointment_menu_items, NULL);
-}
-
-static void
-stop_ecal_timer(void)
-{
- if (ecaltimer != 0) {
- g_source_remove(ecaltimer);
- ecaltimer = 0;
- }
-}
-static gboolean
-idle_start_ecal_timer (gpointer data)
-{
- start_ecal_timer();
- return FALSE;
-}
-
-static void
-show_events_changed (void)
-{
- if (g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) {
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- start_ecal_timer();
- } else {
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- hide_all_appointments ();
- stop_ecal_timer();
- }
-}
-
-static gboolean
-calendar_app_is_usable (void)
-{
- /* confirm that it's installed... */
- gchar *evo = g_find_program_in_path("evolution");
- if (evo == NULL)
- return FALSE;
- g_debug ("found calendar app: '%s'", evo);
- g_free (evo);
-
- /* see if there are any calendar sources */
- return appointment_sources > 0;
-}
-
-/* Looks for the calendar application and enables the item if
- we have one, starts ecal timer if events are turned on */
-static gboolean
-check_for_calendar (gpointer user_data)
-{
- g_return_val_if_fail (calendar != NULL, FALSE);
-
- dbusmenu_menuitem_property_set_bool(date, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
-
- if (!get_greeter_mode () && calendar_app_is_usable()) {
-
- int i;
- int pos = 2;
-
- g_signal_connect (G_OBJECT(date), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK (activate_cb), "evolution -c calendar");
-
- events_separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(events_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- dbusmenu_menuitem_child_add_position(root, events_separator, pos++);
-
- for (i=0; i<MAX_APPOINTMENT_MENUITEMS; i++)
- {
- DbusmenuMenuitem * item = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TYPE, APPOINTMENT_MENUITEM_TYPE);
- dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- appointments[i] = item;
- dbusmenu_menuitem_child_add_position(root, item, pos++);
- }
-
- add_appointment = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (add_appointment, DBUSMENU_MENUITEM_PROP_LABEL, _("Add Event…"));
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- g_signal_connect(G_OBJECT(add_appointment), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), "evolution -c calendar");
- dbusmenu_menuitem_child_add_position (root, add_appointment, pos++);
-
- if (g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) {
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- g_idle_add((GSourceFunc)idle_start_ecal_timer, NULL);
- } else {
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- stop_ecal_timer();
- }
-
- // Connect to calendar events
- g_signal_connect(calendar, "event::month-changed", G_CALLBACK(month_changed_cb), NULL);
- g_signal_connect(calendar, "event::day-selected", G_CALLBACK(day_selected_cb), NULL);
- g_signal_connect(calendar, "event::day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), NULL);
- } else {
- g_debug("Unable to find calendar app.");
- if (add_appointment != NULL)
- dbusmenu_menuitem_property_set_bool(add_appointment, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- if (events_separator != NULL)
- dbusmenu_menuitem_property_set_bool(events_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- }
-
- if (g_settings_get_boolean(conf, SETTINGS_SHOW_CALENDAR_S)) {
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- } else {
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- }
-
- return FALSE;
-}
-
-static gint
-compare_comp_instances (gconstpointer ga, gconstpointer gb)
-{
- const struct comp_instance * a = ga;
- const struct comp_instance * b = gb;
-
- /* sort by start time */
- if (a->start < b->start) return -1;
- if (a->start > b->start) return 1;
- return 0;
-}
-
-static struct comp_instance*
-comp_instance_new (ECalComponent * comp, time_t start, time_t end, ESource * source)
-{
- g_debug("Using times start %s, end %s", ctime(&start), ctime(&end));
-
- struct comp_instance *ci = g_new (struct comp_instance, 1);
- ci->comp = g_object_ref (comp);
- ci->source = source;
- ci->start = start;
- ci->end = end;
- return ci;
-}
-static void
-comp_instance_free (struct comp_instance* ci)
-{
- if (ci != NULL) {
- g_clear_object (&ci->comp);
- g_free (ci);
- }
-}
-
-static gboolean
-populate_appointment_instances (ECalComponent * comp,
- time_t start,
- time_t end,
- gpointer data)
-{
- const ECalComponentVType vtype = e_cal_component_get_vtype (comp);
-
- if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO))
- {
- icalproperty_status status;
- e_cal_component_get_status (comp, &status);
-
- if ((status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED))
- {
- gchar * str = e_cal_component_get_as_string (comp);
- g_debug("Appending item %s", str);
- struct comp_instance *ci = comp_instance_new (comp, start, end, E_SOURCE(data));
- comp_instances = g_list_append (comp_instances, ci);
- g_free (str);
- }
- }
-
- return TRUE; /* tell eds to keep iterating */
-}
-
-/* Populate the menu with todays, next 5 appointments.
- * we should hook into the ABOUT TO SHOW signal and use that to update the appointments.
- * Experience has shown that caldav's and webcals can be slow to load from eds
- * this is a problem mainly on the EDS side of things, not ours.
- */
-static gboolean
-update_appointment_menu_items (gpointer user_data __attribute__ ((unused)))
-{
- // FFR: we should take into account short term timers, for instance
- // tea timers, pomodoro timers etc... that people may add, this is hinted to in the spec.
- g_debug("Update appointments called");
- if (calendar == NULL) return FALSE;
- if (!g_settings_get_boolean(conf, SETTINGS_SHOW_EVENTS_S)) return FALSE;
- if (updating_appointments) return TRUE;
- updating_appointments = TRUE;
-
- time_t curtime = 0, t1 = 0, t2 = 0;
- GList *l, *s;
- GError *gerror = NULL;
- gint i;
- gint width = 0, height = 0;
- GList * sources = NULL;
-
- // Get today & work out query times
- time(&curtime);
- struct tm *today = localtime(&curtime);
- const int mday = today->tm_mday;
- const int mon = today->tm_mon;
- const int year = today->tm_year;
-
- int start_month_saved = mon;
-
- struct tm *start_tm = NULL;
- int this_year = today->tm_year + 1900;
- int days[12]={31,28,31,30,31,30,31,31,30,31,30,31};
- if ((this_year % 400 == 0) || (this_year % 100 > 0 && this_year % 4 == 0)) days[1] = 29;
-
- int highlightdays = days[mon] - mday + 1;
- t1 = curtime; // By default the current time is the appointment start time.
-
- if (start_time_appointments > 0) {
- start_tm = localtime(&start_time_appointments);
- int start_month = start_tm->tm_mon;
- start_month_saved = start_month;
- int start_year = start_tm->tm_year + 1900;
- if ((start_month != mon) || (start_year != this_year)) {
- // Set t1 to the start of that month.
- struct tm month_start = {0};
- month_start.tm_year = start_tm->tm_year;
- month_start.tm_mon = start_tm->tm_mon;
- month_start.tm_mday = 1;
- t1 = mktime(&month_start);
- highlightdays = days[start_month];
- }
- }
-
- g_debug("Will highlight %d days from %s", highlightdays, ctime(&t1));
-
- highlightdays = highlightdays + 7; // Minimum of 7 days ahead
- t2 = t1 + (time_t) (highlightdays * 24 * 60 * 60);
-
- // clear any previous comp_instances
- g_list_free_full (comp_instances, (GDestroyNotify)comp_instance_free);
- comp_instances = NULL;
-
- // Generate instances for all sources
- for (s=appointment_sources; s!=NULL; s=s->next) {
-
- ESource *source = E_SOURCE (s->data);
- ECalClient *ecal = e_cal_client_new(source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, &gerror);
-
- if (!ecal) {
- g_debug ("Cannot create ecal client: %s", gerror->message);
- g_clear_error (&gerror);
- continue;
- }
-
- icaltimezone* current_zone = icaltimezone_get_builtin_timezone(current_timezone);
- if (!current_zone) {
- // current_timezone may be a TZID?
- current_zone = icaltimezone_get_builtin_timezone_from_tzid(current_timezone);
- }
-
- e_cal_client_set_default_timezone (ecal, current_zone);
-
- g_debug("Checking if source %s is enabled", e_source_get_uid(source));
- if (e_source_get_enabled (source)) {
- g_debug("source is enabled, generating instances");
-
- if (!e_client_open_sync (E_CLIENT (ecal), TRUE, NULL, &gerror)) {
- g_debug("Failed to open source: %s", gerror->message);
- g_clear_error (&gerror);
- g_object_unref(ecal);
- continue;
- }
-
- e_cal_client_generate_instances_sync (ecal,
- t1,
- t2,
- populate_appointment_instances,
- source);
- }
- g_object_unref(ecal);
- }
-
- g_debug("Number of ECalComponents returned: %d", g_list_length(comp_instances));
- GList *sorted_comp_instances = g_list_sort(comp_instances, compare_comp_instances);
- comp_instances = NULL;
- g_debug("Components sorted");
-
- hide_all_appointments ();
-
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- if (width <= 0) width = 12;
- if (height <= 0) height = 12;
- if (width > 30) width = 12;
- if (height > 30) height = 12;
-
- gchar *time_format_str = g_settings_get_string(conf, SETTINGS_TIME_FORMAT_S);
- gint apt_output;
- if (g_strcmp0(time_format_str, "12-hour") == 0) {
- apt_output = SETTINGS_TIME_12_HOUR;
- } else if (g_strcmp0(time_format_str, "24-hour") == 0) {
- apt_output = SETTINGS_TIME_24_HOUR;
- } else if (is_locale_12h()) {
- apt_output = SETTINGS_TIME_12_HOUR;
- } else {
- apt_output = SETTINGS_TIME_24_HOUR;
- }
- g_free (time_format_str);
-
- GVariantBuilder markeddays;
- g_variant_builder_init (&markeddays, G_VARIANT_TYPE ("ai"));
-
- i = 0;
- for (l = sorted_comp_instances; l; l = l->next) {
- struct comp_instance *ci = l->data;
- ECalComponent *ecalcomp = ci->comp;
- char right[20];
- //const gchar *uri;
- DbusmenuMenuitem * item;
-
- ECalComponentVType vtype = e_cal_component_get_vtype (ecalcomp);
- struct tm due_data = {0};
- struct tm *due = NULL;
- if (vtype == E_CAL_COMPONENT_EVENT) due = localtime_r(&ci->start, &due_data);
- else if (vtype == E_CAL_COMPONENT_TODO) due = localtime_r(&ci->end, &due_data);
- else continue;
-
- const int dmday = due->tm_mday;
- const int dmon = due->tm_mon;
- const int dyear = due->tm_year;
-
- if (start_month_saved == dmon) {
- // Mark day if our query hasn't hit the next month.
- g_debug("Adding marked date %s, %d", ctime(&ci->start), dmday);
- g_variant_builder_add (&markeddays, "i", dmday);
- }
-
- // If the appointment time is less than the selected date,
- // don't create an appointment item for it.
- if (vtype == E_CAL_COMPONENT_EVENT) {
- if (ci->start < start_time_appointments) continue;
- } else if (vtype == E_CAL_COMPONENT_TODO) {
- if (ci->end < start_time_appointments) continue;
- }
-
- if (i >= MAX_APPOINTMENT_MENUITEMS)
- continue;
-
- item = appointments[i];
- i++;
-
- /* Remove the icon as we might not replace it on error */
- dbusmenu_menuitem_property_remove(item, APPOINTMENT_MENUITEM_PROP_ICON);
-
- /* Remove the activate handler */
- g_signal_handlers_disconnect_matched(G_OBJECT(item), G_SIGNAL_MATCH_FUNC, 0, 0, NULL, G_CALLBACK(activate_cb), NULL);
-
- dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
-
-
- // Label text
- ECalComponentText valuetext;
- e_cal_component_get_summary (ecalcomp, &valuetext);
- const gchar * summary = valuetext.value;
- g_debug("Summary: %s", summary);
- dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_LABEL, summary);
-
- gboolean full_day = FALSE;
- if (vtype == E_CAL_COMPONENT_EVENT) {
- time_t start = ci->start;
- if (time_add_day(start, 1) == ci->end) {
- full_day = TRUE;
- }
- }
-
- // Due text
- if (full_day) {
- struct tm fulldaytime = {0};
- localtime_r(&ci->start, &fulldaytime);
-
- /* TRANSLATORS: This is a strftime string for the day for full day events
- in the menu. It should most likely be either '%A' for a full text day
- (Wednesday) or '%a' for a shortened one (Wed). You should only need to
- change for '%a' in the case of langauges with very long day names. */
- strftime(right, 20, _("%A"), &fulldaytime);
- } else {
- if (apt_output == SETTINGS_TIME_12_HOUR) {
- if ((mday == dmday) && (mon == dmon) && (year == dyear))
- strftime(right, 20, _(DEFAULT_TIME_12_FORMAT), due);
- else
- strftime(right, 20, _(DEFAULT_TIME_12_FORMAT_WITH_DAY), due);
- } else if (apt_output == SETTINGS_TIME_24_HOUR) {
- if ((mday == dmday) && (mon == dmon) && (year == dyear))
- strftime(right, 20, _(DEFAULT_TIME_24_FORMAT), due);
- else
- strftime(right, 20, _(DEFAULT_TIME_24_FORMAT_WITH_DAY), due);
- }
- }
- g_debug("Appointment time: %s, for date %s", right, asctime(due));
- dbusmenu_menuitem_property_set (item, APPOINTMENT_MENUITEM_PROP_RIGHT, right);
-
- // Now we pull out the URI for the calendar event and try to create a URI that'll work when we execute evolution
- // FIXME Because the URI stuff is really broken, we're going to open the calendar at todays date instead
- //e_cal_component_get_uid(ecalcomp, &uri);
- gchar * ad = isodate_from_time_t(mktime(due));
- gchar * cmd = g_strconcat("evolution calendar:///?startdate=", ad, NULL);
- g_debug("Command to Execute: %s", cmd);
- g_signal_connect_data (G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK(activate_cb), cmd, (GClosureNotify)g_free, 0);
- g_free (ad);
-
- const gchar *color_spec = e_source_selectable_get_color (e_source_get_extension (ci->source, E_SOURCE_EXTENSION_CALENDAR));
- g_debug("Colour to use: %s", color_spec);
-
- // Draw the correct icon for the appointment type and then tint it using mask fill.
- // For now we'll create a circle
- if (color_spec != NULL) {
- g_debug("Creating a cairo surface: size, %d by %d", width, height);
- cairo_surface_t *surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, width, height );
- cairo_t *cr = cairo_create(surface);
- GdkRGBA rgba;
- if (gdk_rgba_parse (&rgba, color_spec))
- gdk_cairo_set_source_rgba (cr, &rgba);
- cairo_paint(cr);
- cairo_set_source_rgba(cr, 0,0,0,0.5);
- cairo_set_line_width(cr, 1);
- cairo_rectangle (cr, 0.5, 0.5, width-1, height-1);
- cairo_stroke(cr);
- // Convert to pixbuf, in gtk3 this is done with gdk_pixbuf_get_from_surface
- cairo_content_t content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
- GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
- !!(content & CAIRO_CONTENT_ALPHA),
- 8, width, height);
- if (pixbuf != NULL) {
- gint sstride = cairo_image_surface_get_stride( surface );
- gint dstride = gdk_pixbuf_get_rowstride (pixbuf);
- guchar *spixels = cairo_image_surface_get_data( surface );
- guchar *dpixels = gdk_pixbuf_get_pixels (pixbuf);
-
- int x, y;
- for (y = 0; y < height; y++) {
- guint32 *src = (guint32 *) spixels;
-
- for (x = 0; x < width; x++) {
- guint alpha = src[x] >> 24;
-
- if (alpha == 0) {
- dpixels[x * 4 + 0] = 0;
- dpixels[x * 4 + 1] = 0;
- dpixels[x * 4 + 2] = 0;
- } else {
- dpixels[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
- dpixels[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
- dpixels[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
- }
- dpixels[x * 4 + 3] = alpha;
- }
- spixels += sstride;
- dpixels += dstride;
- }
-
- dbusmenu_menuitem_property_set_image (item, APPOINTMENT_MENUITEM_PROP_ICON, pixbuf);
- g_clear_object (&pixbuf);
- } else {
- g_debug("Creating pixbuf from surface failed");
- }
- cairo_surface_destroy (surface);
- cairo_destroy(cr);
- }
- g_debug("Adding appointment: %p", item);
- }
-
- g_clear_error (&gerror);
-
- g_list_free_full (sorted_comp_instances, (GDestroyNotify)comp_instance_free);
- sorted_comp_instances = NULL;
-
- GVariant * marks = g_variant_builder_end (&markeddays);
- dbusmenu_menuitem_property_set_variant (calendar, CALENDAR_MENUITEM_PROP_MARKS, marks);
-
- g_clear_object (&sources);
-
- updating_appointments = FALSE;
- g_debug("End of objects");
- return TRUE;
-}
-
-/* Looks for the time and date admin application and enables the
- item we have one */
-static gboolean
-check_for_timeadmin (gpointer user_data)
-{
- g_return_val_if_fail (settings != NULL, FALSE);
-
- gchar * timeadmin = g_find_program_in_path("gnome-control-center");
- if (timeadmin != NULL) {
- g_debug("Found the gnome-control-center application: %s", timeadmin);
- dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- g_free(timeadmin);
- } else {
- g_debug("Unable to find gnome-control-center app.");
- dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- }
-
- return FALSE;
-}
-
-static void
-show_locations_changed (void)
-{
- /* Re-calculate */
- update_location_menu_items();
-}
-
-static void
-time_format_changed (void)
-{
- update_appointment_menu_items(NULL);
-}
-
-/* Does the work to build the default menu, really calls out
- to other functions but this is the core to clean up the
- main function. */
-static void
-build_menus (DbusmenuMenuitem * root)
-{
- g_debug("Building Menus.");
- if (date == NULL) {
- date = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (date, DBUSMENU_MENUITEM_PROP_LABEL, _("No date yet…"));
- dbusmenu_menuitem_property_set_bool(date, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- dbusmenu_menuitem_child_append(root, date);
-
- g_idle_add(update_datetime, NULL);
- }
-
- if (calendar == NULL) {
- calendar = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (calendar, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CALENDAR_MENUITEM_TYPE);
- /* insensitive until we check for available apps */
- dbusmenu_menuitem_property_set_bool(calendar, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- g_signal_connect (G_OBJECT(calendar), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK (activate_cb), "evolution -c calendar");
- dbusmenu_menuitem_child_append(root, calendar);
-
- g_idle_add(check_for_calendar, NULL);
- }
-
- if (!get_greeter_mode ()) {
- locations_separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(locations_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- dbusmenu_menuitem_property_set_bool (locations_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_append(root, locations_separator);
-
- update_location_menu_items();
-
- g_signal_connect (conf, "changed::" SETTINGS_SHOW_LOCATIONS_S, G_CALLBACK (show_locations_changed), NULL);
- g_signal_connect (conf, "changed::" SETTINGS_SHOW_DETECTED_S, G_CALLBACK (show_locations_changed), NULL);
- g_signal_connect (conf, "changed::" SETTINGS_LOCATIONS_S, G_CALLBACK (show_locations_changed), NULL);
- g_signal_connect (conf, "changed::" SETTINGS_SHOW_EVENTS_S, G_CALLBACK (show_events_changed), NULL);
- g_signal_connect (conf, "changed::" SETTINGS_TIME_FORMAT_S, G_CALLBACK (time_format_changed), NULL);
-
- DbusmenuMenuitem * separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- dbusmenu_menuitem_child_append(root, separator);
-
- settings = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set (settings, DBUSMENU_MENUITEM_PROP_LABEL, _("Time & Date Settings…"));
- /* insensitive until we check for available apps */
- dbusmenu_menuitem_property_set_bool(settings, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- g_signal_connect(G_OBJECT(settings), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), SETTINGS_APP_INVOCATION);
- dbusmenu_menuitem_child_append(root, settings);
- g_idle_add(check_for_timeadmin, NULL);
- }
-
- return;
-}
-
-static void
-on_clock_skew (void)
-{
- /* tell the indicators to refresh */
- if (IS_DATETIME_INTERFACE (dbus))
- datetime_interface_update (DATETIME_INTERFACE(dbus));
-
- /* update our day label */
- update_datetime (NULL);
- day_timer_reset();
-
- return;
-}
-
-/* Run when the timezone file changes */
-static void
-timezone_changed (GFileMonitor * monitor, GFile * file, GFile * otherfile, GFileMonitorEvent event, gpointer user_data)
-{
- update_current_timezone();
- on_clock_skew();
- return;
-}
-
-/* Set up monitoring the timezone file */
-static void
-build_timezone (DatetimeInterface * dbus)
-{
- GFile * timezonefile = g_file_new_for_path(TIMEZONE_FILE);
- GFileMonitor * monitor = g_file_monitor_file(timezonefile, G_FILE_MONITOR_NONE, NULL, NULL);
- if (monitor != NULL) {
- g_signal_connect(G_OBJECT(monitor), "changed", G_CALLBACK(timezone_changed), dbus);
- g_debug("Monitoring timezone file: '" TIMEZONE_FILE "'");
- } else {
- g_warning("Unable to monitor timezone file: '" TIMEZONE_FILE "'");
- }
- g_object_unref(timezonefile);
- return;
-}
-
-/* Source ID for the timer */
-static guint day_timer = 0;
-
-/* Execute at a given time, update and setup a new
- timer to go again. */
-static gboolean
-day_timer_func (gpointer user_data)
-{
- day_timer = 0;
- /* Reset up each time to reduce error */
- day_timer_reset();
- update_datetime(NULL);
- return G_SOURCE_REMOVE;
-}
-
-/* Sets up the time to launch the timer to update the
- date in the datetime entry */
-static void
-day_timer_reset (void)
-{
- if (day_timer != 0) {
- g_source_remove(day_timer);
- day_timer = 0;
- }
-
- time_t t;
- t = time(NULL);
- struct tm * ltime = localtime(&t);
-
- day_timer = g_timeout_add_seconds(((23 - ltime->tm_hour) * 60 * 60) +
- ((59 - ltime->tm_min) * 60) +
- ((60 - ltime->tm_sec)) + 60 /* one minute past */,
- day_timer_func, NULL);
-
- return;
-}
-
-static gboolean
-skew_check_timer_func (gpointer unused G_GNUC_UNUSED)
-{
- static time_t prev_time = 0;
- const time_t cur_time = time (NULL);
- const double diff_sec = fabs (difftime (cur_time, prev_time));
-
- if (prev_time && (diff_sec > SKEW_DIFF_THRESHOLD_SEC)) {
- g_debug (G_STRLOC" clock skew detected (%.0f seconds)", diff_sec);
- on_clock_skew ();
- }
-
- prev_time = cur_time;
- return G_SOURCE_CONTINUE;
-}
-
-static void
-session_active_change_cb (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name,
- GVariant * parameters, gpointer user_data)
-{
- // Suspending / returning from suspend (true / false)
- if (g_strcmp0(signal_name, "PrepareForSleep") == 0) {
- gboolean sleeping = FALSE;
- g_variant_get (parameters, "(b)", &sleeping);
- if (!sleeping) {
- g_debug ("System has been resumed; adjusting clock");
- on_clock_skew ();
- }
- }
- return;
-}
-
-/* for hooking into console kit signal on wake from suspend */
-static void
-system_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data)
-{
- GError * error = NULL;
-
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
-
- if (error != NULL) {
- g_warning("Could not grab DBus proxy for logind: %s", error->message);
- g_clear_error (&error);
- return;
- }
-
- g_signal_connect(proxy, "g-signal", G_CALLBACK(session_active_change_cb), user_data);
-}
-
-
-/****
-***** GEOCLUE
-****/
-
-static void geo_start (void);
-static void geo_stop (void);
-static void geo_create_client (GeoclueMaster * master, GeoclueMasterClient * client, gchar * path, GError * error, gpointer user_data);
-static void geo_client_invalid (GeoclueMasterClient * client, gpointer user_data);
-
-static GeoclueMaster * geo_master = NULL;
-static GeoclueMasterClient * geo_client = NULL;
-static GeoclueAddress * geo_address = NULL;
-
-static void
-geo_set_timezone (const gchar * timezone)
-{
- if (geo_timezone != timezone) {
- g_clear_pointer (&geo_timezone, g_free);
- geo_timezone = g_strdup (timezone);
- g_debug("Geoclue timezone is: %s", timezone ? timezone : "(Null)");
- update_location_menu_items();
- }
-}
-
-/* Callback from getting the address */
-static void
-geo_address_cb (GeoclueAddress * address, int timestamp, GHashTable * addy_data, GeoclueAccuracy * accuracy, GError * error, gpointer user_data)
-{
- if (error == NULL) {
- geo_set_timezone (g_hash_table_lookup (addy_data, "timezone"));
- } else {
- g_warning("Unable to get Geoclue address: %s", error->message);
- g_clear_error (&error);
- }
-}
-
-/* Clean up the reference we kept to the address and make sure to
- drop the signals incase someone else has one. */
-static void
-geo_address_clean (void)
-{
- if (geo_address != NULL) {
- g_signal_handlers_disconnect_by_func (geo_address, geo_address_cb, NULL);
- g_clear_object (&geo_address);
- }
-}
-
-/* Clean up and remove all signal handlers from the client as we
- unreference it as well. */
-static void
-geo_client_clean (void)
-{
- if (geo_client != NULL) {
- g_signal_handlers_disconnect_by_func (geo_client, geo_client_invalid, NULL);
- g_clear_object (&geo_client);
- }
-}
-
-/* Callback from creating the address */
-static void
-geo_create_address (GeoclueMasterClient * master, GeoclueAddress * address, GError * error, gpointer user_data)
-{
- if (error != NULL) {
- g_warning("Unable to create GeoClue address: %s", error->message);
- g_clear_error (&error);
- return;
- }
-
- /* We shouldn't have created a new address if we already had one
- so this is a warning. But, it really is only a mem-leak so we
- don't need to error out. */
- g_warn_if_fail(geo_address == NULL);
- geo_address_clean();
-
- g_debug("Created Geoclue Address");
- geo_address = g_object_ref (address);
-
- geoclue_address_get_address_async (geo_address, geo_address_cb, NULL);
-
- g_signal_connect (address, "address-changed", G_CALLBACK(geo_address_cb), NULL);
-}
-
-/* Callback from setting requirements */
-static void
-geo_req_set (GeoclueMasterClient * master, GError * error, gpointer user_data)
-{
- if (error != NULL) {
- g_warning("Unable to set Geoclue requirements: %s", error->message);
- g_clear_error (&error);
- }
-}
-
-/* Client is killing itself rather oddly */
-static void
-geo_client_invalid (GeoclueMasterClient * client, gpointer user_data)
-{
- g_warning("Master client invalid, rebuilding.");
- geo_stop ();
- geo_start ();
-}
-
-static void
-geo_stop (void)
-{
- geo_set_timezone (NULL);
-
- geo_address_clean ();
- geo_client_clean ();
- g_clear_object (&geo_master);
-}
-
-static void
-geo_start (void)
-{
- g_warn_if_fail (geo_master == NULL);
-
- g_clear_object (&geo_master);
- geo_master = geoclue_master_get_default();
- geoclue_master_create_client_async (geo_master, geo_create_client, NULL);
-}
-
-/* Callback from creating the client */
-static void
-geo_create_client (GeoclueMaster * master, GeoclueMasterClient * client, gchar * path, GError * error, gpointer user_data)
-{
- g_debug("Created Geoclue client at: %s", path);
-
- geo_client = client;
-
- if (error != NULL) {
- g_warning("Unable to get a GeoClue client! '%s' Geolocation based timezone support will not be available.", error->message);
- g_clear_error (&error);
- return;
- }
-
- if (client == NULL) {
- g_warning(_("Unable to get a GeoClue client! Geolocation based timezone support will not be available."));
- return;
- }
-
- g_object_ref (geo_client);
-
- /* New client, make sure we don't have an address hanging on */
- geo_address_clean();
-
- geoclue_master_client_set_requirements_async(geo_client,
- GEOCLUE_ACCURACY_LEVEL_REGION,
- 0,
- FALSE,
- GEOCLUE_RESOURCE_ALL,
- geo_req_set,
- NULL);
-
- geoclue_master_client_create_address_async(geo_client, geo_create_address, NULL);
-
- g_signal_connect(client, "invalidated", G_CALLBACK(geo_client_invalid), NULL);
-}
-
-static void
-on_use_geoclue_changed_cb (GSettings * settings, gchar * key, gpointer unused G_GNUC_UNUSED)
-{
- geo_stop ();
-
- if (g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S))
- geo_start ();
-}
-
-/****
-*****
-****/
-
-static gboolean
-get_greeter_mode (void)
-{
- const gchar *var;
- var = g_getenv("INDICATOR_GREETER_MODE");
- return (g_strcmp0(var, "1") == 0);
-}
-
-/* Repsonds to the service object saying it's time to shutdown.
- It stops the mainloop. */
-static void
-service_shutdown (IndicatorService * service, gpointer user_data)
-{
- g_warning("Shutting down service!");
- g_main_loop_quit(mainloop);
- return;
-}
-
-static void
-free_appointment_sources (void)
-{
- g_list_free_full (appointment_sources, g_object_unref);
- appointment_sources = NULL;
-}
-
-static void
-source_changed_cb (ESource *source __attribute__ ((unused)),
- gpointer user_data)
-{
- update_appointment_menu_items (user_data);
-}
-
-static void
-init_appointment_sources (ESourceRegistry *registry)
-{
- GList * l;
-
- appointment_sources = e_source_registry_list_sources (registry, E_SOURCE_EXTENSION_CALENDAR);
-
- for (l=appointment_sources; l!=NULL; l=l->next)
- g_signal_connect (G_OBJECT(l->data), "changed", G_CALLBACK (source_changed_cb), NULL);
-}
-
-/* rebuilds both the appointment sources and menu */
-static void
-update_appointments (ESourceRegistry *registry,
- ESource *source __attribute__ ((unused)),
- gpointer user_data __attribute__ ((unused)))
-{
- free_appointment_sources ();
- init_appointment_sources (registry);
-
- update_appointment_menu_items (NULL);
-}
-
-static void
-source_registry_changed_cb (ESourceRegistry *registry __attribute__ ((unused)),
- ESource *source __attribute__ ((unused)),
- gpointer user_data)
-{
- update_appointment_menu_items (user_data);
-}
-
-/* Function to build everything up. Entry point from asm. */
-int
-main (int argc, char ** argv)
-{
- /* Acknowledging the service init and setting up the interface */
- service = indicator_service_new_version(SERVICE_NAME, SERVICE_VERSION);
- g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL);
-
- /* Setting up i18n and gettext. Apparently, we need
- all of these. */
- setlocale (LC_ALL, "");
- bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
- textdomain (GETTEXT_PACKAGE);
-
- /* Set up GSettings */
- conf = g_settings_new(SETTINGS_INTERFACE);
- g_signal_connect (conf, "changed::show-auto-detected-location",
- G_CALLBACK(on_use_geoclue_changed_cb), NULL);
- // TODO Add a signal handler to catch other gsettings changes and respond to them
-
- /* Build our list of appointment calendar sources.
- When a source changes, update our menu items.
- When sources are added or removed, update our list and menu items. */
- source_registry = e_source_registry_new_sync (NULL, NULL);
- g_object_connect (source_registry,
- "signal::source-added", G_CALLBACK (update_appointments), NULL,
- "signal::source-removed", G_CALLBACK (update_appointments), NULL,
- "signal::source-changed", G_CALLBACK (source_registry_changed_cb), NULL,
- "signal::source-disabled", G_CALLBACK (source_registry_changed_cb), NULL,
- "signal::source-enabled", G_CALLBACK (source_registry_changed_cb), NULL,
- NULL);
- init_appointment_sources (source_registry);
-
- /* Building the base menu */
- server = dbusmenu_server_new(MENU_OBJ);
- root = dbusmenu_menuitem_new();
- dbusmenu_server_set_root(server, root);
-
- build_menus(root);
-
- /* Cache the timezone */
- update_current_timezone();
-
- /* Setup geoclue */
- if (g_settings_get_boolean (conf, SETTINGS_SHOW_DETECTED_S))
- geo_start ();
-
- /* Setup dbus interface */
- dbus = g_object_new(DATETIME_INTERFACE_TYPE, NULL);
-
- /* Setup timezone watch */
- build_timezone(dbus);
-
- /* Set up the day timer */
- day_timer_reset();
-
- /* Set up the skew-check timer */
- g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC,
- skew_check_timer_func,
- NULL);
-
- /* And watch for system resumes */
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- NULL, system_proxy_cb, dbus);
-
- mainloop = g_main_loop_new(NULL, FALSE);
- g_main_loop_run(mainloop);
-
- free_appointment_sources();
-
- g_object_unref(G_OBJECT(conf));
- g_object_unref(G_OBJECT(dbus));
- g_object_unref(G_OBJECT(service));
- g_object_unref(G_OBJECT(server));
- g_object_unref(G_OBJECT(root));
- g_object_unref(G_OBJECT(source_registry));
-
- icaltimezone_free_builtin_timezones();
-
- geo_stop ();
-
- return 0;
-}
diff --git a/src/datetime-service.xml b/src/datetime-service.xml
deleted file mode 100644
index eda064f..0000000
--- a/src/datetime-service.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<node name="/">
- <interface name="com.canonical.indicator.datetime.service">
-
-<!-- Methods -->
-
-<!-- Signals -->
- <signal name="UpdateTime" />
-
- </interface>
-</node>
diff --git a/src/dbus-shared.h b/src/dbus-shared.h
index 9e3a781..24319e3 100644
--- a/src/dbus-shared.h
+++ b/src/dbus-shared.h
@@ -19,24 +19,6 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define BUS_NAME "com.canonical.indicator.datetime"
+#define BUS_PATH "/com/canonical/indicator/datetime"
-#define SERVICE_NAME "com.canonical.indicator.datetime"
-#define SERVICE_IFACE "com.canonical.indicator.datetime.service"
-#define SERVICE_OBJ "/com/canonical/indicator/datetime/service"
-#define SERVICE_VERSION 1
-
-#define MENU_OBJ "/com/canonical/indicator/datetime/menu"
-
-#define DBUSMENU_CALENDAR_MENUITEM_TYPE "x-canonical-calendar-item"
-
-#define CALENDAR_MENUITEM_PROP_MARKS "calendar-marks"
-
-#define APPOINTMENT_MENUITEM_TYPE "appointment-item"
-#define APPOINTMENT_MENUITEM_PROP_LABEL "appointment-label"
-#define APPOINTMENT_MENUITEM_PROP_ICON "appointment-icon"
-#define APPOINTMENT_MENUITEM_PROP_RIGHT "appointment-time"
-
-#define TIMEZONE_MENUITEM_TYPE "timezone-item"
-#define TIMEZONE_MENUITEM_PROP_ZONE "timezone-zone"
-#define TIMEZONE_MENUITEM_PROP_NAME "timezone-name"
-#define TIMEZONE_MENUITEM_PROP_RADIO "timezone-radio"
diff --git a/src/indicator-datetime.c b/src/indicator-datetime.c
deleted file mode 100644
index f7d1a78..0000000
--- a/src/indicator-datetime.c
+++ /dev/null
@@ -1,1535 +0,0 @@
-/*
-An indicator to time and date related information in the menubar.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <locale.h>
-#include <langinfo.h>
-#include <string.h>
-#include <time.h>
-
-/* GStuff */
-#include <glib.h>
-#include <glib/gprintf.h>
-#include <glib-object.h>
-#include <glib/gi18n-lib.h>
-#include <gio/gio.h>
-
-/* Indicator Stuff */
-#include <libindicator/indicator.h>
-#include <libindicator/indicator-object.h>
-#include <libindicator/indicator-service-manager.h>
-
-/* DBusMenu */
-#include <libdbusmenu-gtk/menu.h>
-#include <libido/libido.h>
-#include <libdbusmenu-gtk/menuitem.h>
-
-#include "utils.h"
-#include "dbus-shared.h"
-#include "settings-shared.h"
-
-
-#define INDICATOR_DATETIME_TYPE (indicator_datetime_get_type ())
-#define INDICATOR_DATETIME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_DATETIME_TYPE, IndicatorDatetime))
-#define INDICATOR_DATETIME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_DATETIME_TYPE, IndicatorDatetimeClass))
-#define IS_INDICATOR_DATETIME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_DATETIME_TYPE))
-#define IS_INDICATOR_DATETIME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_DATETIME_TYPE))
-#define INDICATOR_DATETIME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_DATETIME_TYPE, IndicatorDatetimeClass))
-
-typedef struct _IndicatorDatetime IndicatorDatetime;
-typedef struct _IndicatorDatetimeClass IndicatorDatetimeClass;
-typedef struct _IndicatorDatetimePrivate IndicatorDatetimePrivate;
-
-struct _IndicatorDatetimeClass {
- IndicatorObjectClass parent_class;
-};
-
-struct _IndicatorDatetime {
- IndicatorObject parent;
- IndicatorDatetimePrivate * priv;
-};
-
-struct _IndicatorDatetimePrivate {
- GtkLabel * label;
- guint timer;
-
- gchar * time_string;
-
- gboolean show_clock;
- gint time_mode;
- gboolean show_seconds;
- gboolean show_date;
- gboolean show_day;
- gchar * custom_string;
- gboolean custom_show_seconds;
-
- gboolean show_week_numbers;
- gboolean show_calendar;
- gint week_start;
-
- guint idle_measure;
- gint max_width;
-
- IndicatorServiceManager * sm;
- DbusmenuGtkMenu * menu;
-
- GCancellable * service_proxy_cancel;
- GDBusProxy * service_proxy;
- IdoCalendarMenuItem *ido_calendar;
-
- GList * timezone_items;
-
- GSettings * settings;
-
- GtkSizeGroup * indicator_right_group;
-};
-
-/* Enum for the properties so that they can be quickly
- found and looked up. */
-enum {
- PROP_0,
- PROP_SHOW_CLOCK,
- PROP_TIME_FORMAT,
- PROP_SHOW_SECONDS,
- PROP_SHOW_DAY,
- PROP_SHOW_DATE,
- PROP_CUSTOM_TIME_FORMAT,
- PROP_SHOW_WEEK_NUMBERS,
- PROP_SHOW_CALENDAR
-};
-
-typedef struct _indicator_item_t indicator_item_t;
-struct _indicator_item_t {
- IndicatorDatetime * self;
- DbusmenuMenuitem * mi;
- GtkWidget * gmi;
- GtkWidget * icon;
- GtkWidget * label;
- GtkWidget * right;
-};
-
-#define PROP_SHOW_CLOCK_S "show-clock"
-#define PROP_TIME_FORMAT_S "time-format"
-#define PROP_SHOW_SECONDS_S "show-seconds"
-#define PROP_SHOW_DAY_S "show-day"
-#define PROP_SHOW_DATE_S "show-date"
-#define PROP_CUSTOM_TIME_FORMAT_S "custom-time-format"
-#define PROP_SHOW_WEEK_NUMBERS_S "show-week-numbers"
-#define PROP_SHOW_CALENDAR_S "show-calendar"
-
-enum {
- STRFTIME_MASK_NONE = 0, /* Hours or minutes as we always test those */
- STRFTIME_MASK_SECONDS = 1 << 0, /* Seconds count */
- STRFTIME_MASK_AMPM = 1 << 1, /* AM/PM counts */
- STRFTIME_MASK_WEEK = 1 << 2, /* Day of the week maters (Sat, Sun, etc.) */
- STRFTIME_MASK_DAY = 1 << 3, /* Day of the month counts (Feb 1st) */
- STRFTIME_MASK_MONTH = 1 << 4, /* Which month matters */
- STRFTIME_MASK_YEAR = 1 << 5, /* Which year matters */
- /* Last entry, combines all previous */
- STRFTIME_MASK_ALL = (STRFTIME_MASK_SECONDS | STRFTIME_MASK_AMPM | STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR)
-};
-
-GType indicator_datetime_get_type (void) G_GNUC_CONST;
-
-static void indicator_datetime_class_init (IndicatorDatetimeClass *klass);
-static void indicator_datetime_init (IndicatorDatetime *self);
-static void timezone_update_all_labels (IndicatorDatetime *self);
-static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
-static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
-static void indicator_datetime_dispose (GObject *object);
-static void indicator_datetime_finalize (GObject *object);
-static GtkLabel * get_label (IndicatorObject * io);
-static GtkMenu * get_menu (IndicatorObject * io);
-static const gchar * get_accessible_desc (IndicatorObject * io);
-static const gchar * get_name_hint (IndicatorObject * io);
-static gboolean bind_enum_get (GValue * value, GVariant * variant, gpointer user_data);
-static gchar * generate_format_string_now (IndicatorDatetime * self);
-static void update_label (IndicatorDatetime * io, GDateTime ** datetime);
-static void guess_label_size (IndicatorDatetime * self);
-static void setup_timer (IndicatorDatetime * self, GDateTime * datetime);
-static void update_time (IndicatorDatetime * self);
-static void receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data);
-static void service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data);
-static gint generate_strftime_bitmask (const char *time_str);
-static void timezone_update_labels (indicator_item_t * mi_data);
-static gboolean new_calendar_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
-static gboolean new_appointment_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
-static gboolean new_timezone_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data);
-
-/* Indicator Module Config */
-INDICATOR_SET_VERSION
-INDICATOR_SET_TYPE(INDICATOR_DATETIME_TYPE)
-
-G_DEFINE_TYPE (IndicatorDatetime, indicator_datetime, INDICATOR_OBJECT_TYPE);
-
-static void
-indicator_datetime_class_init (IndicatorDatetimeClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (IndicatorDatetimePrivate));
-
- object_class->dispose = indicator_datetime_dispose;
- object_class->finalize = indicator_datetime_finalize;
-
- object_class->set_property = set_property;
- object_class->get_property = get_property;
-
- IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
-
- io_class->get_label = get_label;
- io_class->get_menu = get_menu;
- io_class->get_accessible_desc = get_accessible_desc;
- io_class->get_name_hint = get_name_hint;
-
- g_object_class_install_property (object_class,
- PROP_SHOW_CLOCK,
- g_param_spec_boolean(PROP_SHOW_CLOCK_S,
- "Whether to show the clock in the menu bar.",
- "Shows indicator-datetime in the shell's menu bar.",
- TRUE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_TIME_FORMAT,
- g_param_spec_int(PROP_TIME_FORMAT_S,
- "A choice of which format should be used on the panel",
- "Chooses between letting the locale choose the time, 12-hour time, 24-time or using the custom string passed to g_date_time_format().",
- SETTINGS_TIME_LOCALE, /* min */
- SETTINGS_TIME_CUSTOM, /* max */
- SETTINGS_TIME_LOCALE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_SHOW_SECONDS,
- g_param_spec_boolean(PROP_SHOW_SECONDS_S,
- "Whether to show seconds in the indicator.",
- "Shows seconds along with the time in the indicator. Also effects refresh interval.",
- FALSE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_SHOW_DAY,
- g_param_spec_boolean(PROP_SHOW_DAY_S,
- "Whether to show the day of the week in the indicator.",
- "Shows the day of the week along with the time in the indicator.",
- FALSE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_SHOW_DATE,
- g_param_spec_boolean(PROP_SHOW_DATE_S,
- "Whether to show the day and month in the indicator.",
- "Shows the day and month along with the time in the indicator.",
- FALSE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_CUSTOM_TIME_FORMAT,
- g_param_spec_string(PROP_CUSTOM_TIME_FORMAT_S,
- "The format that is used to show the time on the panel.",
- "A format string in the form used to pass to g_date_time_format() to make a string for displaying on the panel.",
- DEFAULT_TIME_FORMAT,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class,
- PROP_SHOW_WEEK_NUMBERS,
- g_param_spec_boolean(PROP_SHOW_WEEK_NUMBERS_S,
- "Whether to show the week numbers in the calendar.",
- "Shows the week numbers in the monthly calendar in indicator-datetime's menu.",
- FALSE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_SHOW_CALENDAR,
- g_param_spec_boolean(PROP_SHOW_CALENDAR_S,
- "Whether to show the calendar.",
- "Shows the monthly calendar in indicator-datetime's menu.",
- TRUE, /* default */
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- return;
-}
-
-static void
-menu_visible_notify_cb(GtkWidget * menu, G_GNUC_UNUSED GParamSpec *pspec, gpointer user_data)
-{
- IndicatorDatetime * self;
- g_debug ("notify visible signal received");
-
- self = INDICATOR_DATETIME (user_data);
- g_assert (self != NULL);
-
- /* if the calendar widget's been created, set it to today's datë */
- if (self->priv->ido_calendar != NULL) {
- GtkWidget * w;
- GtkCalendar * calendar;
- gint cur_y, cur_m, cur_d;
- guint cal_y, cal_m, cal_d;
- GDateTime * datetime = g_date_time_new_now_local ();
-
- g_date_time_get_ymd (datetime, &cur_y, &cur_m, &cur_d);
- w = ido_calendar_menu_item_get_calendar (self->priv->ido_calendar);
- calendar = GTK_CALENDAR(w);
- g_return_if_fail (calendar != NULL);
-
- gtk_calendar_get_date (calendar, &cal_y, &cal_m, &cal_d);
- if ((cur_y != cal_y) || (cur_m-1 != cal_m))
- gtk_calendar_select_month (calendar, cur_m-1, cur_y); /* (cur_m is 1-based) */
- if (cur_d != cal_d)
- gtk_calendar_select_day (calendar, cur_d);
-
- g_date_time_unref (datetime);
- }
-
- /* Update in case date was changed outside of indicator-datetime */
- update_label(self, NULL);
- timezone_update_all_labels(self);
-
- // Make sure the day-selected signal is sent so the menu updates - may duplicate
- /*GVariant *variant = g_variant_new_uint32((guint)curtime);
- guint timestamp = (guint)time(NULL);
- dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(self->priv->ido_calendar), "day-selected", variant, timestamp);*/
-}
-
-static void
-indicator_datetime_init (IndicatorDatetime *self)
-{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- INDICATOR_DATETIME_TYPE,
- IndicatorDatetimePrivate);
-
- self->priv->label = NULL;
- self->priv->timer = 0;
-
- self->priv->idle_measure = 0;
- self->priv->max_width = 0;
-
- self->priv->show_clock = TRUE;
- self->priv->time_mode = SETTINGS_TIME_LOCALE;
- self->priv->show_seconds = FALSE;
- self->priv->show_date = FALSE;
- self->priv->show_day = FALSE;
- self->priv->custom_string = g_strdup(DEFAULT_TIME_FORMAT);
- self->priv->custom_show_seconds = FALSE;
-
- self->priv->time_string = generate_format_string_now(self);
-
- self->priv->service_proxy = NULL;
-
- self->priv->sm = NULL;
- self->priv->menu = NULL;
-
- self->priv->settings = g_settings_new(SETTINGS_INTERFACE);
- if (self->priv->settings != NULL) {
- g_settings_bind(self->priv->settings,
- SETTINGS_SHOW_CLOCK_S,
- self,
- PROP_SHOW_CLOCK_S,
- G_SETTINGS_BIND_GET);
- g_settings_bind_with_mapping(self->priv->settings,
- SETTINGS_TIME_FORMAT_S,
- self,
- PROP_TIME_FORMAT_S,
- G_SETTINGS_BIND_GET,
- bind_enum_get,
- NULL, NULL, NULL); /* set mapping, userdata and destroy func */
- g_settings_bind(self->priv->settings,
- SETTINGS_SHOW_SECONDS_S,
- self,
- PROP_SHOW_SECONDS_S,
- G_SETTINGS_BIND_GET);
- g_settings_bind(self->priv->settings,
- SETTINGS_SHOW_DAY_S,
- self,
- PROP_SHOW_DAY_S,
- G_SETTINGS_BIND_GET);
- g_settings_bind(self->priv->settings,
- SETTINGS_SHOW_DATE_S,
- self,
- PROP_SHOW_DATE_S,
- G_SETTINGS_BIND_GET);
- g_settings_bind(self->priv->settings,
- SETTINGS_CUSTOM_TIME_FORMAT_S,
- self,
- PROP_CUSTOM_TIME_FORMAT_S,
- G_SETTINGS_BIND_GET);
- g_settings_bind(self->priv->settings,
- SETTINGS_SHOW_WEEK_NUMBERS_S,
- self,
- PROP_SHOW_WEEK_NUMBERS_S,
- G_SETTINGS_BIND_GET);
- g_settings_bind(self->priv->settings,
- SETTINGS_SHOW_CALENDAR_S,
- self,
- PROP_SHOW_CALENDAR_S,
- G_SETTINGS_BIND_GET);
- } else {
- g_warning("Unable to get settings for '" SETTINGS_INTERFACE "'");
- }
-
- self->priv->sm = indicator_service_manager_new_version(SERVICE_NAME, SERVICE_VERSION);
- self->priv->indicator_right_group = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL));
-
- self->priv->menu = dbusmenu_gtkmenu_new(SERVICE_NAME, MENU_OBJ);
-
- g_signal_connect(self->priv->menu, "notify::visible", G_CALLBACK(menu_visible_notify_cb), self);
-
- DbusmenuGtkClient *client = dbusmenu_gtkmenu_get_client(self->priv->menu);
- dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), DBUSMENU_CALENDAR_MENUITEM_TYPE, new_calendar_item, self, NULL);
- dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), APPOINTMENT_MENUITEM_TYPE, new_appointment_item, self, NULL);
- dbusmenu_client_add_type_handler_full(DBUSMENU_CLIENT(client), TIMEZONE_MENUITEM_TYPE, new_timezone_item, self, NULL);
-
- self->priv->service_proxy_cancel = g_cancellable_new();
-
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- SERVICE_NAME,
- SERVICE_OBJ,
- SERVICE_IFACE,
- self->priv->service_proxy_cancel,
- service_proxy_cb,
- self);
-
- return;
-}
-
-/* Callback from trying to create the proxy for the serivce, this
- could include starting the service. Sometime it'll fail and
- we'll try to start that dang service again! */
-static void
-service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data)
-{
- GError * error = NULL;
-
- IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
- g_return_if_fail(self != NULL);
- IndicatorDatetimePrivate * priv = self->priv;
-
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
-
- g_clear_object (&priv->service_proxy_cancel);
-
- if (error != NULL) {
- g_warning("Could not grab DBus proxy for %s: %s", SERVICE_NAME, error->message);
- g_error_free(error);
- return;
- }
-
- /* Okay, we're good to grab the proxy at this point, we're
- sure that it's ours. */
- priv->service_proxy = proxy;
-
- g_signal_connect(proxy, "g-signal", G_CALLBACK(receive_signal), self);
-
- return;
-}
-
-static void
-indicator_datetime_dispose (GObject *object)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(object);
- IndicatorDatetimePrivate * priv = self->priv;
-
- if (priv->timer != 0) {
- g_source_remove(priv->timer);
- priv->timer = 0;
- }
-
- if (priv->idle_measure != 0) {
- g_source_remove(priv->idle_measure);
- priv->idle_measure = 0;
- }
-
- g_clear_object (&priv->label);
- g_clear_object (&priv->menu);
- g_clear_object (&priv->sm);
- g_clear_object (&priv->settings);
- g_clear_object (&priv->service_proxy);
- g_clear_object (&priv->indicator_right_group);
- g_clear_object (&priv->ido_calendar);
- g_clear_object (&priv->service_proxy_cancel);
-
- G_OBJECT_CLASS (indicator_datetime_parent_class)->dispose (object);
- return;
-}
-
-static void
-indicator_datetime_finalize (GObject *object)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(object);
-
- if (self->priv->time_string != NULL) {
- g_free(self->priv->time_string);
- self->priv->time_string = NULL;
- }
-
- if (self->priv->custom_string != NULL) {
- g_free(self->priv->custom_string);
- self->priv->custom_string = NULL;
- }
-
- G_OBJECT_CLASS (indicator_datetime_parent_class)->finalize (object);
- return;
-}
-
-/* Turns a string GVariant into an int value */
-static gboolean
-bind_enum_get (GValue * value, GVariant * variant, gpointer user_data)
-{
- const gchar * str = g_variant_get_string(variant, NULL);
- gint output = 0;
-
- if (g_strcmp0(str, "locale-default") == 0) {
- output = SETTINGS_TIME_LOCALE;
- } else if (g_strcmp0(str, "12-hour") == 0) {
- output = SETTINGS_TIME_12_HOUR;
- } else if (g_strcmp0(str, "24-hour") == 0) {
- output = SETTINGS_TIME_24_HOUR;
- } else if (g_strcmp0(str, "custom") == 0) {
- output = SETTINGS_TIME_CUSTOM;
- } else {
- return FALSE;
- }
-
- g_value_set_int(value, output);
- return TRUE;
-}
-
-static void
-timezone_update_all_labels (IndicatorDatetime * self)
-{
- IndicatorDatetimePrivate *priv = self->priv;
-
- g_list_foreach(priv->timezone_items, (GFunc)timezone_update_labels, NULL);
-}
-
-/* Sets a property on the object */
-static void
-set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(object);
- gboolean update = FALSE;
-
- switch(prop_id) {
- case PROP_SHOW_CLOCK: {
- if (g_value_get_boolean(value) != self->priv->show_clock) {
- self->priv->show_clock = g_value_get_boolean(value);
- if (self->priv->label != NULL) {
- gtk_widget_set_visible (GTK_WIDGET (self->priv->label), self->priv->show_clock);
- }
- }
- break;
- }
- case PROP_TIME_FORMAT: {
- gint newval = g_value_get_int(value);
- if (newval != self->priv->time_mode) {
- update = TRUE;
- self->priv->time_mode = newval;
- setup_timer(self, NULL);
- }
- break;
- }
- case PROP_SHOW_SECONDS: {
- if (g_value_get_boolean(value) != self->priv->show_seconds) {
- self->priv->show_seconds = !self->priv->show_seconds;
- if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
- update = TRUE;
- setup_timer(self, NULL);
- }
- }
- break;
- }
- case PROP_SHOW_DAY: {
- if (g_value_get_boolean(value) != self->priv->show_day) {
- self->priv->show_day = !self->priv->show_day;
- if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
- update = TRUE;
- }
- }
- break;
- }
- case PROP_SHOW_DATE: {
- if (g_value_get_boolean(value) != self->priv->show_date) {
- self->priv->show_date = !self->priv->show_date;
- if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
- update = TRUE;
- }
- }
- break;
- }
- case PROP_CUSTOM_TIME_FORMAT: {
- const gchar * newstr = g_value_get_string(value);
- if (g_strcmp0(newstr, self->priv->custom_string) != 0) {
- if (self->priv->custom_string != NULL) {
- g_free(self->priv->custom_string);
- self->priv->custom_string = NULL;
- }
- self->priv->custom_string = g_strdup(newstr);
- gint time_mask = generate_strftime_bitmask(newstr);
- self->priv->custom_show_seconds = (time_mask & STRFTIME_MASK_SECONDS);
- if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
- update = TRUE;
- setup_timer(self, NULL);
- }
- }
- break;
- }
- case PROP_SHOW_WEEK_NUMBERS: {
- if (g_value_get_boolean(value) != self->priv->show_week_numbers) {
- GtkCalendarDisplayOptions flags = ido_calendar_menu_item_get_display_options (self->priv->ido_calendar);
- if (g_value_get_boolean(value) == TRUE)
- flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
- else
- flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS;
- ido_calendar_menu_item_set_display_options (self->priv->ido_calendar, flags);
- self->priv->show_week_numbers = g_value_get_boolean(value);
- }
- break;
- }
- case PROP_SHOW_CALENDAR: {
- if (g_value_get_boolean(value) != self->priv->show_calendar) {
- self->priv->show_calendar = g_value_get_boolean(value);
- if (self->priv->ido_calendar != NULL) {
- gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar);
- }
- }
- break;
- }
- default: {
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
- return;
- }
- }
-
- if (!update) {
- return;
- }
-
- /* Get the new format string */
- gchar * newformat = generate_format_string_now(self);
-
- /* check to ensure the format really changed */
- if (g_strcmp0(self->priv->time_string, newformat) == 0) {
- g_free(newformat);
- return;
- }
-
- /* Okay now process the change */
- if (self->priv->time_string != NULL) {
- g_free(self->priv->time_string);
- self->priv->time_string = NULL;
- }
- self->priv->time_string = newformat;
-
- /* And update everything */
- update_label(self, NULL);
- timezone_update_all_labels(self);
- guess_label_size(self);
-
- return;
-}
-
-/* Gets a property from the object */
-static void
-get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(object);
-
- switch(prop_id) {
- case PROP_SHOW_CLOCK:
- g_value_set_boolean(value, self->priv->show_clock);
- break;
- case PROP_TIME_FORMAT:
- g_value_set_int(value, self->priv->time_mode);
- break;
- case PROP_SHOW_SECONDS:
- g_value_set_boolean(value, self->priv->show_seconds);
- break;
- case PROP_SHOW_DAY:
- g_value_set_boolean(value, self->priv->show_day);
- break;
- case PROP_SHOW_DATE:
- g_value_set_boolean(value, self->priv->show_date);
- break;
- case PROP_CUSTOM_TIME_FORMAT:
- g_value_set_string(value, self->priv->custom_string);
- break;
- case PROP_SHOW_WEEK_NUMBERS:
- g_value_set_boolean(value, self->priv->show_week_numbers);
- break;
- case PROP_SHOW_CALENDAR:
- g_value_set_boolean(value, self->priv->show_calendar);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
- return;
- }
-
- return;
-}
-
-/* Looks at the size of the label, if it grew beyond what we
- thought was the max, make sure it doesn't shrink again. */
-static gboolean
-idle_measure (gpointer data)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(data);
- self->priv->idle_measure = 0;
-
- GtkAllocation allocation;
- gtk_widget_get_allocation(GTK_WIDGET(self->priv->label), &allocation);
-
- if (allocation.width > self->priv->max_width) {
- if (self->priv->max_width != 0) {
- g_warning("Guessed wrong. We thought the max would be %d but we're now at %d", self->priv->max_width, allocation.width);
- }
- self->priv->max_width = allocation.width;
- gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1);
- }
-
- return FALSE;
-}
-
-/* Updates the accessible description */
-static void
-update_accessible_description (IndicatorDatetime * io)
-{
- GList * entries = indicator_object_get_entries(INDICATOR_OBJECT(io));
- IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
-
- entry->accessible_desc = get_accessible_desc(INDICATOR_OBJECT(io));
-
- g_signal_emit(G_OBJECT(io),
- INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID,
- 0,
- entry,
- TRUE);
-
- g_list_free(entries);
-
- return;
-}
-
-/* Updates the label to be the current time. */
-static void
-set_label_to_time_in_zone (IndicatorDatetime * self, GtkLabel * label,
- GTimeZone * tz, const gchar * format,
- GDateTime ** datetime)
-{
- gboolean unref_tz = FALSE;
- if (tz == NULL) {
- gchar * zone = read_timezone ();
- if (zone == NULL)
- return;
- tz = g_time_zone_new(zone);
- unref_tz = TRUE;
- g_free (zone);
- }
-
- GDateTime * datetime_now;
- datetime_now = g_date_time_new_now(tz);
-
- gchar * timestr;
- if (format == NULL) {
- gchar * format_for_time = generate_format_string_at_time(datetime_now);
- timestr = g_date_time_format(datetime_now, format_for_time);
- g_free(format_for_time);
- }
- else {
- timestr = g_date_time_format(datetime_now, format);
- if (timestr == NULL) {
- g_warning ("The custom date format is not valid, check the\n"
- "g_date_time_format() documentation for the supported\n"
- "format specifiers ");
- timestr = g_strdup ("Date format not supported");
- }
- }
-
- gboolean use_markup = FALSE;
- if (pango_parse_markup(timestr, -1, 0, NULL, NULL, NULL, NULL))
- use_markup = TRUE;
-
- if (use_markup)
- gtk_label_set_markup(label, timestr);
- else
- gtk_label_set_text(label, timestr);
-
- g_free(timestr);
-
- if (datetime)
- *datetime = datetime_now;
- else
- g_date_time_unref(datetime_now);
-
- if (unref_tz)
- g_time_zone_unref(tz);
-
- return;
-}
-
-/* Updates the label to be the current time. */
-static void
-update_label (IndicatorDatetime * io, GDateTime ** datetime)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(io);
-
- if (self->priv->label == NULL) return;
-
- set_label_to_time_in_zone(self, self->priv->label, NULL, self->priv->time_string, datetime);
-
- if (self->priv->idle_measure == 0) {
- self->priv->idle_measure = g_idle_add(idle_measure, io);
- }
-
- update_accessible_description(io);
-
- return;
-}
-
-/* Update the time right now. Usually the result of a timezone switch. */
-static void
-update_time (IndicatorDatetime * self)
-{
- GDateTime * dt = NULL;
- update_label(self, &dt);
- timezone_update_all_labels(self);
- if (dt != NULL) {
- setup_timer(self, dt);
- g_date_time_unref(dt);
- }
- return;
-}
-
-/* Receives all signals from the service, routed to the appropriate functions */
-static void
-receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name,
- GVariant * parameters, gpointer user_data)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
-
- if (g_strcmp0(signal_name, "UpdateTime") == 0) {
- update_time(self);
- }
-
- return;
-}
-
-/* Runs every minute and updates the time */
-gboolean
-timer_func (gpointer user_data)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
- self->priv->timer = 0;
- GDateTime * dt = NULL;
- update_label(self, &dt);
- timezone_update_all_labels(self);
- if (dt != NULL) {
- setup_timer(self, dt);
- g_date_time_unref(dt);
- }
- return FALSE;
-}
-
-/* Configure the timer to run the next time through */
-static void
-setup_timer (IndicatorDatetime * self, GDateTime * datetime)
-{
- gboolean unref = FALSE;
-
- if (self->priv->timer != 0) {
- g_source_remove(self->priv->timer);
- self->priv->timer = 0;
- }
-
- if (self->priv->show_seconds ||
- (self->priv->time_mode == SETTINGS_TIME_CUSTOM && self->priv->custom_show_seconds)) {
- self->priv->timer = g_timeout_add_full(G_PRIORITY_HIGH, 999, timer_func, self, NULL);
- } else {
- if (datetime == NULL) {
- datetime = g_date_time_new_now_local();
- unref = TRUE;
- }
-
- /* Plus 2 so we're just after the minute, don't want to be early. */
- gint seconds = (gint)g_date_time_get_seconds(datetime);
- self->priv->timer = g_timeout_add_seconds(60 - seconds + 2, timer_func, self);
-
- if (unref) {
- g_date_time_unref(datetime);
- }
- }
-
- return;
-}
-
-/* Does a quick meausre of how big the string is in
- pixels with a Pango layout */
-static gint
-measure_string (GtkStyle * style, PangoContext * context, const gchar * string)
-{
- PangoLayout * layout = pango_layout_new(context);
-
- if (pango_parse_markup(string, -1, 0, NULL, NULL, NULL, NULL))
- pango_layout_set_markup(layout, string, -1);
- else
- pango_layout_set_text(layout, string, -1);
-
- pango_layout_set_font_description(layout, style->font_desc);
-
- gint width;
- pango_layout_get_pixel_size(layout, &width, NULL);
- g_object_unref(layout);
- return width;
-}
-
-/* Format for the table of strftime() modifiers to what
- we need to check when determining the length */
-typedef struct _strftime_type_t strftime_type_t;
-struct _strftime_type_t {
- char character;
- gint mask;
-};
-
-/* A table taken from the man page of strftime to what the different
- characters can effect. These are worst case in that we need to
- test the length based on all these things to ensure that we have
- a reasonable string lenght measurement. */
-const static strftime_type_t strftime_type[] = {
- {'a', STRFTIME_MASK_WEEK},
- {'A', STRFTIME_MASK_WEEK},
- {'b', STRFTIME_MASK_MONTH},
- {'B', STRFTIME_MASK_MONTH},
- {'c', STRFTIME_MASK_ALL}, /* We don't know, so we have to assume all */
- {'C', STRFTIME_MASK_YEAR},
- {'d', STRFTIME_MASK_MONTH},
- {'D', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY},
- {'e', STRFTIME_MASK_DAY},
- {'F', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY},
- {'G', STRFTIME_MASK_YEAR},
- {'g', STRFTIME_MASK_YEAR},
- {'h', STRFTIME_MASK_MONTH},
- {'j', STRFTIME_MASK_DAY},
- {'m', STRFTIME_MASK_MONTH},
- {'p', STRFTIME_MASK_AMPM},
- {'P', STRFTIME_MASK_AMPM},
- {'r', STRFTIME_MASK_AMPM},
- {'s', STRFTIME_MASK_SECONDS},
- {'S', STRFTIME_MASK_SECONDS},
- {'T', STRFTIME_MASK_SECONDS},
- {'u', STRFTIME_MASK_WEEK},
- {'U', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
- {'V', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
- {'w', STRFTIME_MASK_DAY},
- {'W', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
- {'x', STRFTIME_MASK_YEAR | STRFTIME_MASK_MONTH | STRFTIME_MASK_DAY | STRFTIME_MASK_WEEK},
- {'X', STRFTIME_MASK_SECONDS},
- {'y', STRFTIME_MASK_YEAR},
- {'Y', STRFTIME_MASK_YEAR},
- /* Last one */
- {0, 0}
-};
-
-#define FAT_NUMBER 8
-
-/* Looks through the characters in the format string to
- ensure that we can figure out which of the things we
- need to check in determining the length. */
-static gint
-generate_strftime_bitmask (const char *time_str)
-{
- gint retval = 0;
- glong strlength = g_utf8_strlen(time_str, -1);
- gint i;
- g_debug("Evaluating bitmask for '%s'", time_str);
-
- for (i = 0; i < strlength; i++) {
- if (time_str[i] == '%' && i + 1 < strlength) {
- gchar evalchar = time_str[i + 1];
-
- /* If we're using alternate formats we need to skip those characters */
- if (evalchar == 'E' || evalchar == 'O') {
- if (i + 2 < strlength) {
- evalchar = time_str[i + 2];
- } else {
- continue;
- }
- }
-
- /* Let's look at that character in the table */
- int j;
- for (j = 0; strftime_type[j].character != 0; j++) {
- if (strftime_type[j].character == evalchar) {
- retval |= strftime_type[j].mask;
- break;
- }
- }
- }
- }
-
- return retval;
-}
-
-/* Build an array up of all the time values that we want to check
- for length to ensure we're in a good place */
-static void
-build_timeval_array (GArray * timevals, gint mask)
-{
- struct tm mytm = {0};
-
- /* Sun 12/28/8888 00:00 */
- mytm.tm_hour = 0;
- mytm.tm_mday = 28;
- mytm.tm_mon = 11;
- mytm.tm_year = 8888 - 1900;
- mytm.tm_wday = 0;
- mytm.tm_yday = 363;
- g_array_append_val(timevals, mytm);
-
- if (mask & STRFTIME_MASK_AMPM) {
- /* Sun 12/28/8888 12:00 */
- mytm.tm_hour = 12;
- g_array_append_val(timevals, mytm);
- }
-
- /* NOTE: Ignoring year 8888 should handle it */
-
- if (mask & STRFTIME_MASK_MONTH) {
- gint oldlen = timevals->len;
- gint i, j;
- for (i = 0; i < oldlen; i++) {
- for (j = 0; j < 11; j++) {
- struct tm localval = g_array_index(timevals, struct tm, i);
- localval.tm_mon = j;
- /* Not sure if I need to adjust yday & wday, hope not */
- g_array_append_val(timevals, localval);
- }
- }
- }
-
- /* Doing these together as it seems like just slightly more
- coverage on the numerical days, but worth it. */
- if (mask & (STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY)) {
- gint oldlen = timevals->len;
- gint i, j;
- for (i = 0; i < oldlen; i++) {
- for (j = 22; j < 28; j++) {
- struct tm localval = g_array_index(timevals, struct tm, i);
-
- gint diff = 28 - j;
-
- localval.tm_mday = j;
- localval.tm_wday = localval.tm_wday - diff;
- if (localval.tm_wday < 0) {
- localval.tm_wday += 7;
- }
- localval.tm_yday = localval.tm_yday - diff;
-
- g_array_append_val(timevals, localval);
- }
- }
- }
-
- return;
-}
-
-/* Try to get a good guess at what a maximum width of the entire
- string would be. */
-static void
-guess_label_size (IndicatorDatetime * self)
-{
- /* This is during startup. */
- if (self->priv->label == NULL) return;
-
- GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(self->priv->label));
- PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(self->priv->label));
- gint * max_width = &(self->priv->max_width);
- gint posibilitymask = generate_strftime_bitmask(self->priv->time_string);
-
- /* Reset max width */
- *max_width = 0;
-
- /* Build the array of possibilities that we want to test */
- GArray * timevals = g_array_new(FALSE, TRUE, sizeof(struct tm));
- build_timeval_array(timevals, posibilitymask);
-
- g_debug("Checking against %d possible times", timevals->len);
- gint check_time;
- for (check_time = 0; check_time < timevals->len; check_time++) {
- struct tm * timeval = &g_array_index(timevals, struct tm, check_time);
- GDateTime * dt = g_date_time_new_local(timeval->tm_year, timeval->tm_mon, timeval->tm_mday, timeval->tm_hour, timeval->tm_min, timeval->tm_sec);
- gchar * timestr = g_date_time_format(dt, self->priv->time_string);
-
- gint length = measure_string(style, context, timestr);
-
- g_free(timestr);
- g_date_time_unref(dt);
-
- if (length > *max_width) {
- *max_width = length;
- }
- }
-
- g_array_free(timevals, TRUE);
-
- gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1);
- g_debug("Guessing max time width: %d", self->priv->max_width);
-
- return;
-}
-
-/* React to the style changing, which could mean an font
- update. */
-static void
-style_changed (GtkWidget * widget, GtkStyle * oldstyle, gpointer data)
-{
- g_debug("New style for time label");
- IndicatorDatetime * self = INDICATOR_DATETIME(data);
- guess_label_size(self);
- update_label(self, NULL);
- timezone_update_all_labels(self);
- return;
-}
-
-/* Respond to changes in the screen to update the text gravity */
-static void
-update_text_gravity (GtkWidget *widget, GdkScreen *previous_screen, gpointer data)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(data);
- if (self->priv->label == NULL) return;
-
- PangoLayout *layout;
- PangoContext *context;
-
- layout = gtk_label_get_layout (GTK_LABEL(self->priv->label));
- context = pango_layout_get_context(layout);
- pango_context_set_base_gravity(context, PANGO_GRAVITY_AUTO);
-}
-
-static gchar *
-generate_format_string_now (IndicatorDatetime * self)
-{
- if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
- return g_strdup(self->priv->custom_string);
- }
- else {
- return generate_format_string_full(self->priv->show_day,
- self->priv->show_date);
- }
-}
-
-static void
-timezone_update_labels (indicator_item_t * mi_data)
-{
- const gchar * zone = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_ZONE);
- const gchar * name = dbusmenu_menuitem_property_get(mi_data->mi, TIMEZONE_MENUITEM_PROP_NAME);
-
- gtk_label_set_text(GTK_LABEL(mi_data->label), name);
-
- /* Show current time in that zone on the right */
- GTimeZone * tz = g_time_zone_new(zone);
- set_label_to_time_in_zone(mi_data->self, GTK_LABEL(mi_data->right), tz, NULL, NULL);
- g_time_zone_unref(tz);
-}
-
-/* Whenever we have a property change on a DbusmenuMenuitem
- we need to be responsive to that. */
-static void
-indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value, indicator_item_t * mi_data)
-{
- if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_LABEL)) {
- /* Set the main label */
- gtk_label_set_text(GTK_LABEL(mi_data->label), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_RIGHT)) {
- /* Set the right label */
- gtk_label_set_text(GTK_LABEL(mi_data->right), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, APPOINTMENT_MENUITEM_PROP_ICON)) {
- /* We don't use the value here, which is probably less efficient,
- but it's easier to use the easy function. And since th value
- is already cached, shouldn't be a big deal really. */
- GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(mi, APPOINTMENT_MENUITEM_PROP_ICON);
- if (pixbuf != NULL) {
- /* If we've got a pixbuf we need to make sure it's of a reasonable
- size to fit in the menu. If not, rescale it. */
- GdkPixbuf * resized_pixbuf;
- gint width, height;
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- if (gdk_pixbuf_get_width(pixbuf) > width ||
- gdk_pixbuf_get_height(pixbuf) > height) {
- g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
- resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
- width,
- height,
- GDK_INTERP_BILINEAR);
- } else {
- g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
- resized_pixbuf = pixbuf;
- }
- gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
- /* The other pixbuf should be free'd by the dbusmenu. */
- if (resized_pixbuf != pixbuf) {
- g_object_unref(resized_pixbuf);
- }
- }
- } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_ZONE)) {
- timezone_update_labels(mi_data);
- } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_NAME)) {
- timezone_update_labels(mi_data);
- } else if (!g_strcmp0(prop, TIMEZONE_MENUITEM_PROP_RADIO)) {
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi), g_variant_get_boolean(value));
- }
- return;
-}
-// Properties for marking and unmarking the calendar
-static void
-calendar_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant *value, IdoCalendarMenuItem * mi_data)
-{
- g_debug("Changing calendar property: %s", prop);
- if (!g_strcmp0(prop, CALENDAR_MENUITEM_PROP_MARKS)) {
- ido_calendar_menu_item_clear_marks (IDO_CALENDAR_MENU_ITEM (mi_data));
-
- if (value != NULL) {
- GVariantIter *iter;
- gint day;
-
- g_debug("\tMarks: %s", g_variant_print(value, FALSE));
-
- g_variant_get (value, "ai", &iter);
- while (g_variant_iter_loop (iter, "i", &day)) {
- ido_calendar_menu_item_mark_day (IDO_CALENDAR_MENU_ITEM (mi_data), day);
- }
- g_variant_iter_free (iter);
- } else {
- g_debug("\tMarks: <cleared>");
- }
- }
- return;
-}
-
-/* We have a small little menuitem type that handles all
- of the fun stuff for indicators. Mostly this is the
- shifting over and putting the icon in with some right
- side text that'll be determined by the service.
- Copied verbatim from an old revision (including comments) of indicator-messages
-*/
-static gboolean
-new_appointment_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
-{
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
- g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE);
- /* Note: not checking parent, it's reasonable for it to be NULL */
- IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
-
- indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
-
- mi_data->gmi = gtk_menu_item_new();
-
- GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
-
- /* Icon, probably someone's face or avatar on an IM */
- mi_data->icon = gtk_image_new();
- GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(newitem, APPOINTMENT_MENUITEM_PROP_ICON);
-
- if (pixbuf != NULL) {
- /* If we've got a pixbuf we need to make sure it's of a reasonable
- size to fit in the menu. If not, rescale it. */
- GdkPixbuf * resized_pixbuf;
- gint width, height;
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- if (gdk_pixbuf_get_width(pixbuf) > width ||
- gdk_pixbuf_get_height(pixbuf) > height) {
- g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
- resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
- width,
- height,
- GDK_INTERP_BILINEAR);
- } else {
- g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
- resized_pixbuf = pixbuf;
- }
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
-
- /* The other pixbuf should be free'd by the dbusmenu. */
- if (resized_pixbuf != pixbuf) {
- g_object_unref(resized_pixbuf);
- }
- }
- gtk_misc_set_alignment(GTK_MISC(mi_data->icon), 0.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, 0);
- gtk_widget_show(mi_data->icon);
-
- /* Label, probably a username, chat room or mailbox name */
- mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPOINTMENT_MENUITEM_PROP_LABEL));
- gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
-
- GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(mi_data->label));
- PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(mi_data->label));
- gint length = measure_string(style, context, "MMMMMMMMMMMMMMM"); // 15 char wide string max
- gtk_widget_set_size_request(GTK_WIDGET(mi_data->label), length, -1); // Set the min size in pixels
-
- gtk_label_set_ellipsize(GTK_LABEL(mi_data->label), PANGO_ELLIPSIZE_END);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
- gtk_widget_show(mi_data->label);
-
- /* Usually either the time or the count on the individual
- item. */
- mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPOINTMENT_MENUITEM_PROP_RIGHT));
- gtk_size_group_add_widget(self->priv->indicator_right_group, mi_data->right);
- gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0);
- gtk_widget_show(mi_data->right);
-
- gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox);
- gtk_widget_show(hbox);
-
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(mi_data->gmi), parent);
-
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data);
- return TRUE;
-}
-
-static void
-month_changed_cb (IdoCalendarMenuItem *ido,
- gpointer user_data)
-{
- guint d,m,y;
- DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data);
- ido_calendar_menu_item_get_date(ido, &y, &m, &d);
- struct tm date = {0};
- date.tm_mday = d;
- date.tm_mon = m;
- date.tm_year = y - 1900;
- guint selecteddate = (guint)mktime(&date);
- g_debug("Got month changed signal: %s", asctime(&date));
- GVariant *variant = g_variant_new_uint32(selecteddate);
- guint timestamp = (guint)time(NULL);
- dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "month-changed", variant, timestamp);
-}
-
-static void
-day_selected_cb (IdoCalendarMenuItem *ido,
- gpointer user_data)
-{
- guint d,m,y;
- DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data);
- ido_calendar_menu_item_get_date(ido, &y, &m, &d);
- struct tm date = {0};
- date.tm_mday = d;
- date.tm_mon = m;
- date.tm_year = y - 1900;
- guint selecteddate = (guint)mktime(&date);
- g_debug("Got day selected signal: %s", asctime(&date));
- GVariant *variant = g_variant_new_uint32(selecteddate);
- guint timestamp = (guint)time(NULL);
- dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "day-selected", variant, timestamp);
-}
-
-static void
-day_selected_double_click_cb (IdoCalendarMenuItem *ido,
- gpointer user_data)
-{
- guint d,m,y;
- DbusmenuMenuitem * item = DBUSMENU_MENUITEM (user_data);
- ido_calendar_menu_item_get_date(ido, &y, &m, &d);
- struct tm date = {0};
- date.tm_mday = d;
- date.tm_mon = m;
- date.tm_year = y - 1900;
- guint selecteddate = (guint)mktime(&date);
- g_debug("Got day selected double click signal: %s", asctime(&date));
- GVariant *variant = g_variant_new_uint32(selecteddate);
- guint timestamp = (guint)time(NULL);
- dbusmenu_menuitem_handle_event(DBUSMENU_MENUITEM(item), "day-selected-double-click", variant, timestamp);
-}
-
-static gboolean
-new_calendar_item (DbusmenuMenuitem * newitem,
- DbusmenuMenuitem * parent,
- DbusmenuClient * client,
- gpointer user_data)
-{
- g_debug("New calendar item");
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
- g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE);
- /* Note: not checking parent, it's reasonable for it to be NULL */
-
- IndicatorDatetime *self = INDICATOR_DATETIME(user_data);
-
- IdoCalendarMenuItem *ido = IDO_CALENDAR_MENU_ITEM (ido_calendar_menu_item_new ());
- self->priv->ido_calendar = ido;
-
- GtkCalendarDisplayOptions flags = ido_calendar_menu_item_get_display_options (self->priv->ido_calendar);
- if (self->priv->show_week_numbers == TRUE)
- flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
- else
- flags &= ~GTK_CALENDAR_SHOW_WEEK_NUMBERS;
- ido_calendar_menu_item_set_display_options (self->priv->ido_calendar, flags);
-
- gtk_widget_set_visible (GTK_WIDGET (self->priv->ido_calendar), self->priv->show_calendar);
-
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(ido), parent);
-
- g_signal_connect_after(ido, "month-changed", G_CALLBACK(month_changed_cb), (gpointer)newitem);
- g_signal_connect_after(ido, "day-selected", G_CALLBACK(day_selected_cb), (gpointer)newitem);
- g_signal_connect_after(ido, "day-selected-double-click", G_CALLBACK(day_selected_double_click_cb), (gpointer)newitem);
-
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(calendar_prop_change_cb), ido);
-
- /* Run the current values through prop changed */
- GVariant * propval = NULL;
-
- propval = dbusmenu_menuitem_property_get_variant(newitem, CALENDAR_MENUITEM_PROP_MARKS);
- if (propval != NULL) {
- calendar_prop_change_cb(newitem, CALENDAR_MENUITEM_PROP_MARKS, propval, ido);
- }
-
- return TRUE;
-}
-
-static void
-timezone_toggled_cb (GtkCheckMenuItem *checkmenuitem, DbusmenuMenuitem * dbusitem)
-{
- /* Make sure that the displayed radio-active setting is always
- consistent with the dbus menuitem */
- gtk_check_menu_item_set_active(checkmenuitem,
- dbusmenu_menuitem_property_get_bool(dbusitem, TIMEZONE_MENUITEM_PROP_RADIO));
-}
-
-static void
-timezone_destroyed_cb (indicator_item_t * mi_data, DbusmenuMenuitem * dbusitem)
-{
- IndicatorDatetime *self = INDICATOR_DATETIME (mi_data->self);
- IndicatorDatetimePrivate *priv = self->priv;
-
- priv->timezone_items = g_list_remove(priv->timezone_items, mi_data);
- g_signal_handlers_disconnect_by_func(G_OBJECT(mi_data->gmi), G_CALLBACK(timezone_toggled_cb), dbusitem);
- g_free(mi_data);
-}
-
-static gboolean
-new_timezone_item(DbusmenuMenuitem * newitem,
- DbusmenuMenuitem * parent,
- DbusmenuClient * client,
- gpointer user_data)
-{
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
- g_return_val_if_fail(IS_INDICATOR_DATETIME(user_data), FALSE);
- /* Note: not checking parent, it's reasonable for it to be NULL */
-
- IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
- IndicatorDatetimePrivate *priv = self->priv;
-
- // Menu item with a radio button and a right aligned time
- indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
-
- priv->timezone_items = g_list_prepend(priv->timezone_items, mi_data);
-
- mi_data->self = self;
- mi_data->mi = newitem;
- mi_data->gmi = gtk_check_menu_item_new();
-
- gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(mi_data->gmi), TRUE);
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi_data->gmi),
- dbusmenu_menuitem_property_get_bool(newitem, TIMEZONE_MENUITEM_PROP_RADIO));
-
- GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
-
- /* Label, probably a username, chat room or mailbox name */
- mi_data->label = gtk_label_new("");
- gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
- gtk_widget_show(mi_data->label);
-
- /* Usually either the time or the count on the individual
- item. */
- mi_data->right = gtk_label_new("");
- gtk_size_group_add_widget(self->priv->indicator_right_group, mi_data->right);
- gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, 0);
- gtk_widget_show(mi_data->right);
-
- timezone_update_labels(mi_data);
-
- gtk_container_add(GTK_CONTAINER(mi_data->gmi), hbox);
- gtk_widget_show(hbox);
-
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, GTK_MENU_ITEM(mi_data->gmi), parent);
-
- g_signal_connect(G_OBJECT(mi_data->gmi), "toggled", G_CALLBACK(timezone_toggled_cb), newitem);
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data);
- g_object_weak_ref(G_OBJECT(newitem), (GWeakNotify)timezone_destroyed_cb, mi_data);
-
- return TRUE;
-}
-
-/* Grabs the label. Creates it if it doesn't
- exist already */
-static GtkLabel *
-get_label (IndicatorObject * io)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(io);
-
- /* If there's not a label, we'll build ourselves one */
- if (self->priv->label == NULL) {
- self->priv->label = GTK_LABEL(gtk_label_new("Time"));
- gtk_label_set_justify (GTK_LABEL(self->priv->label), GTK_JUSTIFY_CENTER);
- g_object_ref(G_OBJECT(self->priv->label));
- g_signal_connect(G_OBJECT(self->priv->label), "style-set", G_CALLBACK(style_changed), self);
- g_signal_connect(G_OBJECT(self->priv->label), "screen-changed", G_CALLBACK(update_text_gravity), self);
- guess_label_size(self);
- update_label(self, NULL);
- gtk_widget_set_visible(GTK_WIDGET (self->priv->label), self->priv->show_clock);
- }
-
- if (self->priv->timer == 0) {
- setup_timer(self, NULL);
- }
-
- return self->priv->label;
-}
-
-static GtkMenu *
-get_menu (IndicatorObject * io)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(io);
-
- return GTK_MENU(self->priv->menu);
-}
-
-static const gchar *
-get_accessible_desc (IndicatorObject * io)
-{
- IndicatorDatetime * self = INDICATOR_DATETIME(io);
- const gchar * name;
-
- if (self->priv->label != NULL) {
- name = gtk_label_get_text(self->priv->label);
- return name;
- }
- return NULL;
-}
-
-static const gchar *
-get_name_hint (IndicatorObject * io)
-{
- return PACKAGE_NAME;
-}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..c75b2d7
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,63 @@
+/*
+ * 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 <locale.h>
+#include <stdlib.h> /* exit() */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "service.h"
+
+/***
+****
+***/
+
+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;
+ IndicatorDatetimeService * service;
+
+ /* boilerplate i18n */
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+
+ /* 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);
+
+ /* cleanup */
+ g_clear_object (&service);
+ g_main_loop_unref (loop);
+ return 0;
+}
diff --git a/src/planner-eds.c b/src/planner-eds.c
new file mode 100644
index 0000000..8fdc50b
--- /dev/null
+++ b/src/planner-eds.c
@@ -0,0 +1,330 @@
+/*
+ * 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 <gio/gio.h> /* GFile, GFileMonitor */
+
+#include <libical/ical.h>
+#include <libical/icaltime.h>
+#include <libecal/libecal.h>
+#include <libedataserver/libedataserver.h>
+
+#include "planner-eds.h"
+
+struct _IndicatorDatetimePlannerEdsPriv
+{
+ ESourceRegistry * source_registry;
+};
+
+typedef IndicatorDatetimePlannerEdsPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorDatetimePlannerEds,
+ indicator_datetime_planner_eds,
+ INDICATOR_TYPE_DATETIME_PLANNER)
+
+/***
+****
+***/
+
+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);
+ }
+}
+
+/***
+**** my_get_appointments() helpers
+***/
+
+struct my_get_appointments_data
+{
+ ESource * source;
+ GSList * appointments;
+};
+
+static gboolean
+my_get_appointments_foreach (ECalComponent * component,
+ time_t begin,
+ time_t end,
+ gpointer gdata)
+{
+ const ECalComponentVType vtype = e_cal_component_get_vtype (component);
+ struct my_get_appointments_data * data = gdata;
+
+ if ((vtype == E_CAL_COMPONENT_EVENT) || (vtype == E_CAL_COMPONENT_TODO))
+ {
+ icalproperty_status status;
+ e_cal_component_get_status (component, &status);
+ if ((status != ICAL_STATUS_COMPLETED) && (status != ICAL_STATUS_CANCELLED))
+ {
+ ECalComponentText text;
+ struct IndicatorDatetimeAppt * appt = g_new0 (struct IndicatorDatetimeAppt, 1);
+
+ text.value = "";
+ e_cal_component_get_summary (component, &text);
+
+ 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->is_event = vtype == E_CAL_COMPONENT_EVENT;
+ appt->summary = g_strdup (text.value);
+
+ data->appointments = g_slist_prepend (data->appointments, appt);
+ }
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+
+/***
+**** IndicatorDatetimePlanner virtual funcs
+***/
+
+static GSList *
+my_get_appointments (IndicatorDatetimePlanner * planner,
+ GDateTime * begin_datetime,
+ GDateTime * end_datetime)
+{
+ GList * l;
+ GList * sources;
+ 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);
+
+ p = INDICATOR_DATETIME_PLANNER_EDS (planner)->priv;
+
+ /**
+ *** init the default timezone
+ **/
+
+ default_timezone = NULL;
+
+ if ((str = indicator_datetime_planner_get_timezone (planner)))
+ {
+ default_timezone = icaltimezone_get_builtin_timezone (str);
+
+ if (default_timezone == NULL) /* maybe str is a tzid? */
+ default_timezone = icaltimezone_get_builtin_timezone_from_tzid (str);
+ }
+
+ /**
+ *** walk through the sources to build the appointment list
+ **/
+
+ data.source = NULL;
+ data.appointments = NULL;
+
+ sources = e_source_registry_list_sources (p->source_registry, E_SOURCE_EXTENSION_CALENDAR);
+ for (l=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);
+ }
+ }
+ }
+
+ g_list_free_full (sources, g_object_unref);
+
+ g_debug ("%s EDS get_appointments returning %d appointments", G_STRLOC, g_slist_length (data.appointments));
+ return data.appointments;
+}
+
+gboolean
+my_is_configured (IndicatorDatetimePlanner * planner)
+{
+ GList * sources;
+ gboolean have_sources;
+ IndicatorDatetimePlannerEds * self;
+
+ /* confirm that it's installed... */
+ gchar *evo = g_find_program_in_path ("evolution");
+ if (evo == NULL)
+ return FALSE;
+
+ g_debug ("found calendar app: '%s'", evo);
+ g_free (evo);
+
+ /* 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;
+}
+
+static void
+my_activate (IndicatorDatetimePlanner * self G_GNUC_UNUSED)
+{
+ GError * error = NULL;
+ const char * const command = "evolution -c calendar";
+
+ if (!g_spawn_command_line_async (command, &error))
+ {
+ g_warning ("Unable to start %s: %s", command, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+my_activate_time (IndicatorDatetimePlanner * self G_GNUC_UNUSED,
+ GDateTime * activate_time)
+{
+ gchar * isodate;
+ gchar * command;
+ GError * err;
+
+ isodate = g_date_time_format (activate_time, "%Y%m%d");
+ command = g_strdup_printf ("evolution \"calendar:///?startdate=%s\"", isodate);
+ err = 0;
+ if (!g_spawn_command_line_async (command, &err))
+ {
+ g_warning ("Unable to start %s: %s", command, err->message);
+ g_error_free (err);
+ }
+
+ g_free (command);
+ g_free (isodate);
+}
+
+/***
+**** GObject virtual funcs
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorDatetimePlannerEds * self = INDICATOR_DATETIME_PLANNER_EDS (o);
+ priv_t * p = self->priv;
+
+ if (p->source_registry != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (p->source_registry,
+ indicator_datetime_planner_emit_appointments_changed,
+ self);
+
+ g_clear_object (&self->priv->source_registry);
+ }
+
+ G_OBJECT_CLASS (indicator_datetime_planner_eds_parent_class)->dispose (o);
+}
+
+/***
+**** Insantiation
+***/
+
+static void
+indicator_datetime_planner_eds_class_init (IndicatorDatetimePlannerEdsClass * 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;
+
+ g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerEdsPriv));
+}
+
+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,
+ IndicatorDatetimePlannerEdsPriv);
+
+ 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);
+ }
+}
+
+/***
+**** Public
+***/
+
+IndicatorDatetimePlanner *
+indicator_datetime_planner_eds_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_PLANNER_EDS, NULL);
+
+ return INDICATOR_DATETIME_PLANNER (o);
+}
diff --git a/src/planner-eds.h b/src/planner-eds.h
new file mode 100644
index 0000000..a2c803a
--- /dev/null
+++ b/src/planner-eds.h
@@ -0,0 +1,60 @@
+/*
+ * 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_EDS__H__
+#define __INDICATOR_DATETIME_PLANNER_EDS__H__
+
+#include "planner.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_PLANNER_EDS (indicator_datetime_planner_eds_get_type())
+#define INDICATOR_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEds))
+#define INDICATOR_DATETIME_PLANNER_EDS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS, IndicatorDatetimePlannerEdsClass))
+#define INDICATOR_IS_DATETIME_PLANNER_EDS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER_EDS))
+
+typedef struct _IndicatorDatetimePlannerEds IndicatorDatetimePlannerEds;
+typedef struct _IndicatorDatetimePlannerEdsPriv IndicatorDatetimePlannerEdsPriv;
+typedef struct _IndicatorDatetimePlannerEdsClass IndicatorDatetimePlannerEdsClass;
+
+GType indicator_datetime_planner_eds_get_type (void);
+
+/**
+ * An IndicatorDatetimePlanner which uses Evolution Data Server
+ * to get its list of appointments.
+ */
+struct _IndicatorDatetimePlannerEds
+{
+ /*< private >*/
+ IndicatorDatetimePlanner parent;
+ IndicatorDatetimePlannerEdsPriv * priv;
+};
+
+struct _IndicatorDatetimePlannerEdsClass
+{
+ IndicatorDatetimePlannerClass parent_class;
+};
+
+gboolean indicator_datetime_planner_eds_is_usable (void);
+
+IndicatorDatetimePlanner * indicator_datetime_planner_eds_new (void);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_PLANNER_EDS__H__ */
diff --git a/src/planner.c b/src/planner.c
new file mode 100644
index 0000000..1643651
--- /dev/null
+++ b/src/planner.c
@@ -0,0 +1,232 @@
+/*
+ * 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 "planner.h"
+
+/**
+*** Signals Boilerplate
+**/
+
+enum
+{
+ SIGNAL_APPTS_CHANGED,
+ SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+/**
+*** Properties Boilerplate
+**/
+
+enum
+{
+ PROP_0,
+ PROP_TIMEZONE,
+ PROP_LAST
+};
+
+static GParamSpec * properties[PROP_LAST] = { 0 };
+
+/**
+*** GObject Boilerplate
+**/
+
+G_DEFINE_TYPE (IndicatorDatetimePlanner,
+ indicator_datetime_planner,
+ G_TYPE_OBJECT)
+
+struct _IndicatorDatetimePlannerPriv
+{
+ char * timezone;
+};
+
+/***
+**** GObjectClass virtual funcs
+***/
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o);
+
+ switch (property_id)
+ {
+ case PROP_TIMEZONE:
+ g_value_set_string (value, self->priv->timezone);
+ 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)
+{
+ IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER (o);
+
+ switch (property_id)
+ {
+ case PROP_TIMEZONE:
+ indicator_datetime_planner_set_timezone (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorDatetimePlanner * self = INDICATOR_DATETIME_PLANNER(o);
+
+ g_free (self->priv->timezone);
+
+ G_OBJECT_CLASS (indicator_datetime_planner_parent_class)->finalize (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_datetime_planner_class_init (IndicatorDatetimePlannerClass * klass)
+{
+ GObjectClass * object_class;
+ const GParamFlags flags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS;
+
+ g_type_class_add_private (klass, sizeof (IndicatorDatetimePlannerPriv));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = my_finalize;
+ object_class->get_property = my_get_property;
+ object_class->set_property = my_set_property;
+
+ klass->get_appointments = NULL;
+
+ signals[SIGNAL_APPTS_CHANGED] = g_signal_new ("appointments-changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IndicatorDatetimePlannerClass, appointments_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* install properties */
+
+ properties[PROP_0] = NULL;
+
+ properties[PROP_TIMEZONE] = g_param_spec_string ("timezone",
+ "Timezone",
+ "Default timezone for the EDS appointments",
+ "",
+ flags);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+}
+
+static void
+indicator_datetime_planner_init (IndicatorDatetimePlanner * self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_PLANNER,
+ IndicatorDatetimePlannerPriv);
+}
+
+/***
+**** Public API
+***/
+
+void
+indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self));
+
+ g_signal_emit (self, signals[SIGNAL_APPTS_CHANGED], 0, NULL);
+}
+
+static gint
+compare_appointments_by_start_time (gconstpointer ga, gconstpointer gb)
+{
+ const struct IndicatorDatetimeAppt * a = ga;
+ const struct IndicatorDatetimeAppt * b = gb;
+
+ return g_date_time_compare (a->begin, b->begin);
+}
+
+GSList *
+indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end)
+{
+ 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);
+ return g_slist_sort (appointments, compare_appointments_by_start_time);
+}
+
+gboolean
+indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self)
+{
+ g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), FALSE);
+
+ return INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->is_configured (self);
+}
+
+void
+indicator_datetime_planner_activate (IndicatorDatetimePlanner * self)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self));
+
+ INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate (self);
+}
+
+void
+indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self));
+
+ INDICATOR_DATETIME_PLANNER_GET_CLASS (self)->activate_time (self, time);
+}
+
+void
+indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_PLANNER (self));
+
+ g_free (self->priv->timezone);
+ self->priv->timezone = g_strdup (timezone);
+ g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]);
+}
+
+const char *
+indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self)
+{
+ g_return_val_if_fail (INDICATOR_IS_DATETIME_PLANNER (self), NULL);
+
+ return self->priv->timezone;
+}
diff --git a/src/planner.h b/src/planner.h
new file mode 100644
index 0000000..7499b75
--- /dev/null
+++ b/src/planner.h
@@ -0,0 +1,137 @@
+/*
+ * 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__H__
+#define __INDICATOR_DATETIME_PLANNER__H__
+
+#include <glib.h>
+#include <glib-object.h> /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_PLANNER (indicator_datetime_planner_get_type())
+#define INDICATOR_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlanner))
+#define INDICATOR_DATETIME_PLANNER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass))
+#define INDICATOR_DATETIME_PLANNER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_PLANNER, IndicatorDatetimePlannerClass))
+#define INDICATOR_IS_DATETIME_PLANNER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_PLANNER))
+
+typedef struct _IndicatorDatetimePlanner IndicatorDatetimePlanner;
+typedef struct _IndicatorDatetimePlannerPriv IndicatorDatetimePlannerPriv;
+typedef struct _IndicatorDatetimePlannerClass IndicatorDatetimePlannerClass;
+
+GType indicator_datetime_planner_get_type (void);
+
+struct IndicatorDatetimeAppt
+{
+ char * color;
+ char * summary;
+ GDateTime * begin;
+ GDateTime * end;
+ gboolean is_event;
+};
+
+/**
+ * Abstract Base Class for objects that provides appointments and events.
+ *
+ * These will be listed in the appointments section of indicator-datetime's menu.
+ */
+struct _IndicatorDatetimePlanner
+{
+ /*< private >*/
+ GObject parent;
+ IndicatorDatetimePlannerPriv * priv;
+};
+
+struct _IndicatorDatetimePlannerClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+
+ void (*appointments_changed) (IndicatorDatetimePlanner * self);
+
+ /* virtual functions */
+
+ GSList* (*get_appointments) (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end);
+
+ gboolean (*is_configured) (IndicatorDatetimePlanner * self);
+ void (*activate) (IndicatorDatetimePlanner * self);
+ void (*activate_time) (IndicatorDatetimePlanner * self, GDateTime *);
+};
+
+/***
+****
+***/
+
+void indicator_datetime_appt_free (struct IndicatorDatetimeAppt * appt);
+
+/**
+ * Get a list of appointments, sorted by start time.
+ *
+ * An easy way to free the list properly in one step is as follows:
+ *
+ * g_slist_free_full (list, (GDestroyNotify)indicator_datetime_appt_free);
+ *
+ *
+ * Return value: (element-type IndicatorDatetimeAppt)
+ * (transfer full):
+ * list of appointments
+ */
+GSList * indicator_datetime_planner_get_appointments (IndicatorDatetimePlanner * self, GDateTime * begin, GDateTime * end);
+
+/**
+ * Returns false if the planner's backend is not configured.
+ *
+ * This can be used on startup to determine whether or not to use this planner.
+ */
+gboolean indicator_datetime_planner_is_configured (IndicatorDatetimePlanner * self);
+
+/**
+ * Activate this planner.
+ *
+ * This is used to activate the planner's backend's event editor.
+ */
+void indicator_datetime_planner_activate (IndicatorDatetimePlanner * self);
+
+/**
+ * Activate this planner.
+ *
+ * This is used to activate the planner's backend's event editor,
+ * with an added hint of the specific time that the user would like to edit.
+ */
+void indicator_datetime_planner_activate_time (IndicatorDatetimePlanner * self, GDateTime * time);
+
+/**
+ * Set the timezone.
+ *
+ * This is used as a default timezone if the backend's events don't provide their own.
+ */
+void indicator_datetime_planner_set_timezone (IndicatorDatetimePlanner * self, const char * timezone);
+
+const char * indicator_datetime_planner_get_timezone (IndicatorDatetimePlanner * self);
+
+
+/**
+ * Emits the "appointments-changed" signal. This should only be called by subclasses.
+ */
+void indicator_datetime_planner_emit_appointments_changed (IndicatorDatetimePlanner * self);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_PLANNER__H__ */
diff --git a/src/service.c b/src/service.c
new file mode 100644
index 0000000..558b03a
--- /dev/null
+++ b/src/service.c
@@ -0,0 +1,1804 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ * Ted Gould <ted@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 <string.h> /* strstr() */
+
+#include <glib/gi18n.h>
+#include <gio/gio.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)
+
+G_DEFINE_TYPE (IndicatorDatetimeService,
+ indicator_datetime_service,
+ G_TYPE_OBJECT)
+
+enum
+{
+ SIGNAL_NAME_LOST,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum
+{
+ SECTION_HEADER = (1<<0),
+ SECTION_CALENDAR = (1<<1),
+ SECTION_APPOINTMENTS = (1<<2),
+ SECTION_LOCATIONS = (1<<3),
+ SECTION_SETTINGS = (1<<4),
+};
+
+enum
+{
+ PROFILE_DESKTOP,
+ PROFILE_GREETER,
+ N_PROFILES
+};
+
+static const char * const menu_names[N_PROFILES] =
+{
+ "desktop",
+ "greeter"
+};
+
+struct ProfileMenuInfo
+{
+ /* the root level -- the header is the only child of this */
+ GMenu * menu;
+
+ /* parent of the sections. This is the header's submenu */
+ GMenu * submenu;
+
+ guint export_id;
+};
+
+struct _IndicatorDatetimeServicePrivate
+{
+ GCancellable * cancellable;
+
+ GSettings * settings;
+
+ IndicatorDatetimeTimezone * tz_file;
+ IndicatorDatetimeTimezone * tz_geoclue;
+ IndicatorDatetimePlanner * planner;
+
+ guint own_id;
+ guint actions_export_id;
+ GDBusConnection * conn;
+
+ guint rebuild_id;
+ int rebuild_flags;
+ struct ProfileMenuInfo menus[N_PROFILES];
+
+ GDateTime * skew_time;
+ guint skew_timer;
+
+ guint header_timer;
+ guint timezone_timer;
+
+ /* Which year/month to show in the calendar,
+ and which day should get the cursor.
+ This value is reflected in the calendar action's state */
+ GDateTime * calendar_date;
+
+ GSimpleActionGroup * actions;
+ GSimpleAction * header_action;
+ GSimpleAction * calendar_action;
+
+ GDBusProxy * login1_manager;
+};
+
+typedef IndicatorDatetimeServicePrivate priv_t;
+
+/***
+****
+***/
+
+static void
+indicator_clear_timer (guint * tag)
+{
+ if (*tag)
+ {
+ g_source_remove (*tag);
+ *tag = 0;
+ }
+}
+
+/***
+****
+***/
+
+static void rebuild_now (IndicatorDatetimeService * self, int section);
+static void rebuild_soon (IndicatorDatetimeService * self, int section);
+
+static inline void
+rebuild_header_soon (IndicatorDatetimeService * self)
+{
+ rebuild_soon (self, SECTION_HEADER);
+}
+
+static inline void
+rebuild_calendar_section_soon (IndicatorDatetimeService * self)
+{
+ rebuild_soon (self, SECTION_CALENDAR);
+}
+
+static inline void
+rebuild_appointments_section_soon (IndicatorDatetimeService * self)
+{
+ rebuild_soon (self, SECTION_APPOINTMENTS);
+}
+
+static inline void
+rebuild_locations_section_soon (IndicatorDatetimeService * self)
+{
+ rebuild_soon (self, SECTION_LOCATIONS);
+}
+
+static inline void
+rebuild_settings_section_soon (IndicatorDatetimeService * self)
+{
+ rebuild_soon (self, SECTION_SETTINGS);
+}
+
+/***
+**** TIMEZONE TIMER
+***/
+
+/*
+ * Periodically rebuild the sections that have time format strings
+ * that are dependent on the current time:
+ *
+ * 1. appointment menuitems' time format strings depend on the
+ * current time; for example, they don't show the day of week
+ * if the appointment is today.
+ *
+ * 2. location menuitems' time format strings depend on the
+ * current time; for example, they don't show the day of the week
+ * if the local date and location date are the same.
+ *
+ * 3. the "local date" menuitem in the calendar section is,
+ * obviously, dependent on the local time.
+ *
+ * In short, we want to update whenever the number of days between two zone
+ * might have changed. We do that by updating when either zone's day changes.
+ *
+ * Since not all UTC offsets are evenly divisible by hours
+ * (examples: Newfoundland UTC-03:30, Nepal UTC+05:45), refreshing on the hour
+ * is not enough. We need to refresh at HH:00, HH:15, HH:30, and HH:45.
+ */
+
+static guint
+calculate_seconds_until_next_fifteen_minutes (GDateTime * now)
+{
+ char * str;
+ gint minute;
+ guint seconds;
+ GTimeSpan diff;
+ GDateTime * next;
+ GDateTime * start_of_next;
+
+ minute = g_date_time_get_minute (now);
+ minute = 15 - (minute % 15);
+ next = g_date_time_add_minutes (now, minute);
+ start_of_next = g_date_time_new_local (g_date_time_get_year (next),
+ g_date_time_get_month (next),
+ g_date_time_get_day_of_month (next),
+ g_date_time_get_hour (next),
+ g_date_time_get_minute (next),
+ 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);
+ g_free (str);
+
+ diff = g_date_time_difference (start_of_next, now);
+ seconds = (diff + (G_TIME_SPAN_SECOND-1)) / G_TIME_SPAN_SECOND;
+
+ g_date_time_unref (start_of_next);
+ g_date_time_unref (next);
+
+ return seconds;
+}
+
+static void start_timezone_timer (IndicatorDatetimeService * self);
+
+static gboolean
+on_timezone_timer (gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+
+ rebuild_soon (self, SECTION_CALENDAR |
+ SECTION_APPOINTMENTS |
+ SECTION_LOCATIONS);
+
+ /* Restarting the timer to recalculate the interval. This helps us to hit
+ our marks despite clock skew, suspend+resume, leap seconds, etc */
+ start_timezone_timer (self);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+start_timezone_timer (IndicatorDatetimeService * self)
+{
+ GDateTime * now;
+ guint seconds;
+ priv_t * p = self->priv;
+
+ indicator_clear_timer (&p->timezone_timer);
+
+ now = indicator_datetime_service_get_localtime (self);
+ seconds = calculate_seconds_until_next_fifteen_minutes (now);
+ p->timezone_timer = g_timeout_add_seconds (seconds, on_timezone_timer, self);
+ g_date_time_unref (now);
+}
+
+/***
+**** HEADER TIMER
+***/
+
+/*
+ * This is to periodically rebuild the header's action's state.
+ *
+ * If the label shows seconds, update when we reach the next second.
+ * Otherwise, update when we reach the next minute.
+ */
+
+static guint
+calculate_milliseconds_until_next_minute (GDateTime * now)
+{
+ GDateTime * next;
+ GDateTime * start_of_next;
+ GTimeSpan interval_usec;
+ guint interval_msec;
+
+ next = g_date_time_add_minutes (now, 1);
+ start_of_next = g_date_time_new_local (g_date_time_get_year (next),
+ g_date_time_get_month (next),
+ g_date_time_get_day_of_month (next),
+ g_date_time_get_hour (next),
+ g_date_time_get_minute (next),
+ 0);
+
+ interval_usec = g_date_time_difference (start_of_next, now);
+ interval_msec = (interval_usec + 999) / 1000;
+
+ g_date_time_unref (start_of_next);
+ g_date_time_unref (next);
+
+ return interval_msec;
+}
+
+static gint
+calculate_milliseconds_until_next_second (GDateTime * now)
+{
+ gint interval_usec;
+ guint interval_msec;
+
+ interval_usec = G_USEC_PER_SEC - g_date_time_get_microsecond (now);
+ interval_msec = (interval_usec + 999) / 1000;
+
+ return interval_msec;
+}
+
+static void start_header_timer (IndicatorDatetimeService * self);
+
+static gboolean
+on_header_timer (gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+
+ rebuild_now (self, SECTION_HEADER);
+
+ /* Restarting the timer to recalculate the interval. This helps us to hit
+ our marks despite clock skew, suspend+resume, leap seconds, etc */
+ start_header_timer (self);
+ return G_SOURCE_REMOVE;
+}
+
+static char * get_header_label_format_string (IndicatorDatetimeService *);
+
+static void
+start_header_timer (IndicatorDatetimeService * self)
+{
+ guint interval_msec;
+ gboolean header_shows_seconds = FALSE;
+ priv_t * p = self->priv;
+ GDateTime * now = indicator_datetime_service_get_localtime (self);
+
+ indicator_clear_timer (&p->header_timer);
+
+ if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CLOCK_S))
+ {
+ 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)
+ interval_msec = calculate_milliseconds_until_next_second (now);
+ else
+ interval_msec = calculate_milliseconds_until_next_minute (now);
+
+ interval_msec += 50; /* add a small margin to ensure the callback
+ fires /after/ next is reached */
+
+ p->header_timer = g_timeout_add_full (G_PRIORITY_HIGH,
+ interval_msec,
+ on_header_timer,
+ self,
+ NULL);
+
+ g_date_time_unref (now);
+}
+
+/**
+ * General purpose handler for rebuilding sections and restarting their timers
+ * when time jumps for whatever reason:
+ *
+ * - clock skew
+ * - laptop suspend + resume
+ * - geoclue detects that we've changed timezones
+ * - Unity is running inside a TARDIS
+ */
+static void
+on_local_time_jumped (IndicatorDatetimeService * self)
+{
+ g_debug ("%s %s", G_STRLOC, G_STRFUNC);
+
+ /* these calls accomplish two things:
+ 1. rebuild the necessary states / menuitems when time jumps
+ 2. restart the timers so their new wait interval is correct */
+
+ on_header_timer (self);
+ on_timezone_timer (self);
+}
+
+static gboolean
+skew_timer_func (gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ GDateTime * now = indicator_datetime_service_get_localtime (self);
+ priv_t * p = self->priv;
+
+ /* check for clock skew: has too much time passed since the last check? */
+ if (p->skew_time != NULL)
+ {
+ const GTimeSpan diff = g_date_time_difference (now, p->skew_time);
+
+ if (diff > SKEW_DIFF_THRESHOLD_USEC)
+ on_local_time_jumped (self);
+ }
+
+ g_clear_pointer (&p->skew_time, g_date_time_unref);
+ p->skew_time = now;
+ return G_SOURCE_CONTINUE;
+}
+
+/***
+****
+**** HEADER SECTION
+****
+***/
+
+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 *
+get_header_label_format_string (IndicatorDatetimeService * self)
+{
+ char * fmt;
+ const TimeFormatMode mode = get_time_format_mode (self);
+ GSettings * s = self->priv->settings;
+
+ 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_format_string_full (show_day, show_date);
+ }
+
+ return fmt;
+}
+
+static GVariant *
+create_header_state (IndicatorDatetimeService * self)
+{
+ GVariantBuilder b;
+ gchar * fmt;
+ gchar * str;
+ gboolean visible;
+ GDateTime * now;
+ priv_t * p = self->priv;
+
+ 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);
+ now = indicator_datetime_service_get_localtime (self);
+ str = g_date_time_format (now, fmt);
+ if (str == NULL)
+ {
+ str = g_strdup (_("Unsupported date format"));
+ 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));
+
+ /* cleanup */
+ g_date_time_unref (now);
+ g_free (str);
+ g_free (fmt);
+ return g_variant_builder_end (&b);
+}
+
+
+/***
+****
+**** CALENDAR SECTION
+****
+***/
+
+static GDateTime *
+get_calendar_date (IndicatorDatetimeService * self)
+{
+ GDateTime * date;
+ priv_t * p = self->priv;
+
+ if (p->calendar_date)
+ date = g_date_time_ref (p->calendar_date);
+ else
+ date = indicator_datetime_service_get_localtime (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, 0,
+ 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)
+{
+ guint i;
+ const char * key;
+ gboolean days[32] = { 0 };
+ GVariantBuilder dict_builder;
+ 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)
+ {
+ const struct IndicatorDatetimeAppt * appt = l->data;
+ days[g_date_time_get_day_of_month (appt->begin)] = TRUE;
+ }
+ g_variant_builder_init (&day_builder, G_VARIANT_TYPE("ai"));
+ for (i=0; i<G_N_ELEMENTS(days); i++)
+ if (days[i])
+ 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);
+ g_variant_builder_add (&dict_builder, "{sv}", key,
+ g_variant_new_int64 (g_date_time_to_unix (date)));
+ g_date_time_unref (date);
+
+ key = "show-week-numbers";
+ b = g_settings_get_boolean (p->settings, SETTINGS_SHOW_WEEK_NUMBERS_S);
+ g_variant_builder_add (&dict_builder, "{sv}", key, g_variant_new_boolean (b));
+
+ return g_variant_builder_end (&dict_builder);
+}
+
+static void
+update_calendar_action_state (IndicatorDatetimeService * self)
+{
+ g_simple_action_set_state (self->priv->calendar_action,
+ create_calendar_state (self));
+}
+
+static GMenuModel *
+create_calendar_section (IndicatorDatetimeService * self)
+{
+ char * label;
+ GMenuItem * menu_item;
+ GDateTime * now;
+ GMenu * menu = g_menu_new ();
+
+ /* create the local date menuitem */
+ now = indicator_datetime_service_get_localtime (self);
+ label = g_date_time_format (now, _("%A, %e %B %Y"));
+ menu_item = g_menu_item_new (label, NULL);
+ g_menu_item_set_action_and_target_value (menu_item, "indicator.activate-planner",
+ g_variant_new_int64(0));
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+ g_free (label);
+ g_date_time_unref (now);
+
+ /* create the calendar menuitem */
+ if (g_settings_get_boolean (self->priv->settings, SETTINGS_SHOW_CALENDAR_S))
+ {
+ 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");
+ g_menu_item_set_attribute (menu_item, "activation-action",
+ "s", "indicator.activate-planner");
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+ g_free (label);
+ }
+
+ return G_MENU_MODEL (menu);
+}
+
+/***
+****
+**** APPOINTMENTS SECTION
+****
+***/
+
+/* 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 char *
+get_appointment_time_format (struct IndicatorDatetimeAppt * appt, GDateTime * now)
+{
+ char * fmt;
+ gboolean full_day = g_date_time_difference (appt->end, appt->begin) == G_TIME_SPAN_DAY;
+
+ if (full_day)
+ {
+ /* TRANSLATORS: This is a strftime string for the day for full day events
+ in the menu. It should most likely be either '%A' for a full text day
+ (Wednesday) or '%a' for a shortened one (Wed). You should only need to
+ change for '%a' in the case of langauges with very long day names. */
+ fmt = g_strdup (_("%A"));
+ }
+ else
+ {
+ fmt = generate_format_string_at_time (now, appt->begin);
+ }
+
+ return fmt;
+}
+
+static GMenuModel *
+create_appointments_section (IndicatorDatetimeService * self)
+{
+ priv_t * p = self->priv;
+ GMenu * menu = g_menu_new ();
+
+ if (g_settings_get_boolean (p->settings, SETTINGS_SHOW_EVENTS_S))
+ {
+ GSList * l;
+ GSList * appts;
+ GMenuItem * menu_item;
+ GDateTime * now = indicator_datetime_service_get_localtime (self);
+
+ /* build appointment menuitems */
+ appts = get_upcoming_appointments (self);
+ for (l=appts; l!=NULL; l=l->next)
+ {
+ struct IndicatorDatetimeAppt * appt = l->data;
+ char * fmt = get_appointment_time_format (appt, now);
+ const gint64 unix_time = g_date_time_to_unix (appt->begin);
+
+ menu_item = g_menu_item_new (appt->summary, NULL);
+ g_menu_item_set_attribute (menu_item, "x-canonical-color",
+ "s", appt->color);
+ g_menu_item_set_attribute (menu_item, "x-canonical-time",
+ "x", unix_time);
+ g_menu_item_set_attribute (menu_item, "x-canonical-time-format",
+ "s", fmt);
+ g_menu_item_set_attribute (menu_item, "x-canonical-type",
+ "s", "com.canonical.indicator.appointment");
+ 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);
+ }
+
+ /* build 'add event' menuitem */
+ menu_item = g_menu_item_new (_("Add Event…"), NULL);
+ g_menu_item_set_action_and_target_value (menu_item,
+ "indicator.activate-planner",
+ g_variant_new_int64 (0));
+ g_menu_append_item (menu, menu_item);
+ g_object_unref (menu_item);
+
+ /* cleanup */
+ g_date_time_unref (now);
+ g_slist_free_full (appts, (GDestroyNotify)indicator_datetime_appt_free);
+ }
+
+ return G_MENU_MODEL (menu);
+}
+
+
+/***
+****
+**** LOCATIONS SECTION
+****
+***/
+
+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. */
+struct TimeLocation
+{
+ GTimeSpan offset;
+ gchar * zone;
+ gchar * name;
+ gboolean visible;
+ GDateTime * local_time;
+};
+
+static void
+time_location_free (struct TimeLocation * loc)
+{
+ g_date_time_unref (loc->local_time);
+ g_free (loc->name);
+ g_free (loc->zone);
+ g_free (loc);
+}
+
+static struct TimeLocation*
+time_location_new (const char * zone,
+ const char * name,
+ gboolean visible)
+{
+ struct TimeLocation * loc = g_new (struct TimeLocation, 1);
+ GTimeZone * tz = g_time_zone_new (zone);
+ loc->zone = g_strdup (zone);
+ loc->name = g_strdup (name);
+ loc->visible = visible;
+ loc->local_time = g_date_time_new_now (tz);
+ loc->offset = g_date_time_get_utc_offset (loc->local_time);
+ g_time_zone_unref (tz);
+ return loc;
+}
+
+static int
+time_location_compare (const struct TimeLocation * a,
+ const struct TimeLocation * b)
+{
+ int ret = 0;
+
+ if (!ret && (a->offset != b->offset)) /* primary key */
+ ret = (a->offset < b->offset) ? -1 : 1;
+
+ if (!ret)
+ ret = g_strcmp0 (a->name, b->name); /* secondary key */
+
+ if (!ret)
+ ret = a->visible - b->visible; /* tertiary key */
+
+ return ret;
+}
+
+static GSList*
+locations_add (GSList * locations,
+ const char * zone,
+ const char * name,
+ gboolean visible)
+{
+ struct TimeLocation * loc = time_location_new (zone, name, visible);
+
+ if (g_slist_find_custom (locations, loc, (GCompareFunc)time_location_compare))
+ {
+ g_debug("%s Skipping duplicate zone '%s' name '%s'", G_STRLOC, zone, name);
+ time_location_free (loc);
+ }
+ else
+ {
+ g_debug ("%s Adding zone '%s', name '%s'", G_STRLOC, zone, name);
+ locations = g_slist_append (locations, loc);
+ }
+
+ return locations;
+}
+
+static GMenuModel *
+create_locations_section (IndicatorDatetimeService * self)
+{
+ guint i;
+ GMenu * menu;
+ GSList * l;
+ GSList * locations = NULL;
+ gchar ** user_locations;
+ gboolean visible;
+ IndicatorDatetimeTimezone * detected_timezones[2];
+ 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.
+ ***/
+
+ /* 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++)
+ {
+ 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);
+ }
+ }
+ }
+
+ /* 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);
+
+ for (i=0; user_locations[i] != NULL; i++)
+ {
+ gchar * zone;
+ gchar * name;
+ split_settings_location (user_locations[i], &zone, &name);
+ locations = locations_add (locations, zone, name, visible);
+ g_free (name);
+ g_free (zone);
+ }
+
+ g_strfreev (user_locations);
+ user_locations = NULL;
+ }
+
+ /* now build menuitems for all the locations */
+ for (l=locations; l!=NULL; l=l->next)
+ {
+ 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_format_string_at_time (now, loc->local_time);
+
+ menu_item = g_menu_item_new (label, 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",
+ "s", loc->zone);
+ g_menu_item_set_attribute (menu_item, "x-canonical-time-format",
+ "s", fmt);
+ g_menu_append_item (menu, menu_item);
+
+ g_object_unref (menu_item);
+ g_free (fmt);
+ g_free (detailed_action);
+ g_free (label);
+ }
+ }
+
+ g_date_time_unref (now);
+ g_slist_free_full (locations, (GDestroyNotify)time_location_free);
+ return G_MENU_MODEL (menu);
+}
+
+/***
+**** SET LOCATION
+***/
+
+struct setlocation_data
+{
+ IndicatorDatetimeService * service;
+ char * timezone_id;
+ char * name;
+};
+
+static void
+setlocation_data_free (struct setlocation_data * data)
+{
+ g_free (data->timezone_id);
+ g_free (data->name);
+ g_free (data);
+}
+
+static void
+on_datetime1_set_timezone_response (GObject * object,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ GError * err;
+ GVariant * answers;
+ struct setlocation_data * data = gdata;
+
+ err = NULL;
+ answers = g_dbus_proxy_call_finish (G_DBUS_PROXY(object), res, &err);
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not set new timezone: %s", err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ char * timezone_name = g_strdup_printf ("%s %s",
+ data->timezone_id,
+ data->name);
+
+ g_settings_set_string (data->service->priv->settings,
+ SETTINGS_TIMEZONE_NAME_S,
+ timezone_name);
+
+ g_free (timezone_name);
+ g_variant_unref (answers);
+ }
+
+ setlocation_data_free (data);
+}
+
+static void
+on_datetime1_proxy_ready (GObject * object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gdata)
+{
+ GError * err;
+ GDBusProxy * proxy;
+ struct setlocation_data * data = gdata;
+
+ err = NULL;
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &err);
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not grab DBus proxy for timedated: %s", err->message);
+
+ g_error_free (err);
+ setlocation_data_free (data);
+ }
+ else
+ {
+ g_dbus_proxy_call (proxy,
+ "SetTimezone",
+ g_variant_new ("(sb)", data->timezone_id, TRUE),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ data->service->priv->cancellable,
+ on_datetime1_set_timezone_response,
+ data);
+
+ g_object_unref (proxy);
+ }
+}
+
+static void
+indicator_datetime_service_set_location (IndicatorDatetimeService * self,
+ const char * timezone_id,
+ const char * name)
+{
+ priv_t * p = self->priv;
+ struct setlocation_data * data;
+
+ g_return_if_fail (INDICATOR_IS_DATETIME_SERVICE (self));
+ g_return_if_fail (name && *name);
+ g_return_if_fail (timezone_id && *timezone_id);
+
+ data = g_new0 (struct setlocation_data, 1);
+ data->timezone_id = g_strdup (timezone_id);
+ data->name = g_strdup (name);
+ data->service = self;
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.timedate1",
+ "/org/freedesktop/timedate1",
+ "org.freedesktop.timedate1",
+ p->cancellable,
+ on_datetime1_proxy_ready,
+ data);
+}
+
+static void
+on_set_location (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param,
+ gpointer gself)
+{
+ char * zone;
+ char * name;
+ IndicatorDatetimeService * self;
+
+ self = INDICATOR_DATETIME_SERVICE (gself);
+ split_settings_location (g_variant_get_string (param, NULL), &zone, &name);
+ indicator_datetime_service_set_location (self, zone, name);
+
+ g_free (name);
+ g_free (zone);
+}
+
+/***
+****
+***/
+
+static GMenuModel *
+create_settings_section (IndicatorDatetimeService * self G_GNUC_UNUSED)
+{
+ GMenu * menu = g_menu_new ();
+ g_menu_append (menu, _("Date and Time Settings\342\200\246"), "indicator.activate-settings");
+ return G_MENU_MODEL (menu);
+}
+
+static void
+create_menu (IndicatorDatetimeService * self, int profile)
+{
+ GMenu * menu;
+ GMenu * submenu;
+ GMenuItem * header;
+ GMenuModel * sections[16];
+ int i;
+ int n = 0;
+
+ g_assert (0<=profile && profile<N_PROFILES);
+ g_assert (self->priv->menus[profile].menu == NULL);
+
+ if (profile == PROFILE_DESKTOP)
+ {
+ sections[n++] = create_calendar_section (self);
+ sections[n++] = create_appointments_section (self);
+ sections[n++] = create_locations_section (self);
+ sections[n++] = create_settings_section (self);
+ }
+ else if (profile == PROFILE_GREETER)
+ {
+ sections[n++] = create_calendar_section (self);
+ }
+
+ /* add sections to the submenu */
+
+ submenu = g_menu_new ();
+
+ for (i=0; i<n; ++i)
+ {
+ g_menu_append_section (submenu, NULL, sections[i]);
+ g_object_unref (sections[i]);
+ }
+
+ /* add submenu to the header */
+ header = g_menu_item_new (NULL, "indicator._header");
+ g_menu_item_set_attribute (header, "x-canonical-type",
+ "s", "com.canonical.indicator.root");
+ g_menu_item_set_submenu (header, G_MENU_MODEL (submenu));
+ g_object_unref (submenu);
+
+ /* add header to the menu */
+ menu = g_menu_new ();
+ g_menu_append_item (menu, header);
+ g_object_unref (header);
+
+ self->priv->menus[profile].menu = menu;
+ self->priv->menus[profile].submenu = submenu;
+}
+
+/***
+**** GActions
+***/
+
+/* Run a particular program based on an activation */
+static void
+execute_command (const gchar * cmd)
+{
+ GError * err = NULL;
+
+ g_debug ("Issuing command '%s'", cmd);
+
+ if (!g_spawn_command_line_async (cmd, &err))
+ {
+ g_warning ("Unable to start %s: %s", cmd, err->message);
+ g_error_free (err);
+ }
+}
+
+static void
+on_settings_activated (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param G_GNUC_UNUSED,
+ gpointer gself G_GNUC_UNUSED)
+{
+#ifdef HAVE_CCPANEL
+ execute_command ("gnome-control-center indicator-datetime");
+#else
+ execute_command ("gnome-control-center datetime");
+#endif
+}
+
+static void
+on_activate_planner (GSimpleAction * a G_GNUC_UNUSED,
+ GVariant * param,
+ gpointer gself)
+{
+ priv_t * p = INDICATOR_DATETIME_SERVICE(gself)->priv;
+
+ if (p->planner != NULL)
+ {
+ const gint64 t = g_variant_get_int64 (param);
+ if (t)
+ {
+ GDateTime * date_time = g_date_time_new_from_unix_local (t);
+ indicator_datetime_planner_activate_time (p->planner, date_time);
+ g_date_time_unref (date_time);
+ }
+ else /* no time specified... */
+ {
+ indicator_datetime_planner_activate (p->planner);
+ }
+ }
+}
+
+static void
+on_calendar_action_activated (GSimpleAction * action G_GNUC_UNUSED,
+ GVariant * state,
+ gpointer gself)
+{
+ gint64 unix_time;
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+
+ if ((unix_time = g_variant_get_int64 (state)))
+ {
+ GDateTime * date = g_date_time_new_from_unix_local (unix_time);
+ indicator_datetime_service_set_calendar_date (self, date);
+ g_date_time_unref (date);
+ }
+ else /* unset */
+ {
+ indicator_datetime_service_set_calendar_date (self, NULL);
+ }
+}
+
+
+static void
+init_gactions (IndicatorDatetimeService * self)
+{
+ GSimpleAction * a;
+ priv_t * p = self->priv;
+
+ GActionEntry entries[] = {
+ { "activate-settings", on_settings_activated },
+ { "activate-planner", on_activate_planner, "x", NULL },
+ { "set-location", on_set_location, "s" }
+ };
+
+ p->actions = g_simple_action_group_new ();
+
+ g_action_map_add_action_entries (G_ACTION_MAP(p->actions),
+ entries,
+ G_N_ELEMENTS(entries),
+ self);
+
+ /* add the header action */
+ a = g_simple_action_new_stateful ("_header", NULL, create_header_state (self));
+ g_simple_action_group_insert (p->actions, G_ACTION(a));
+ p->header_action = a;
+
+ /* add the calendar action */
+ a = g_simple_action_new_stateful ("calendar",
+ G_VARIANT_TYPE_INT64,
+ create_calendar_state (self));
+ g_simple_action_group_insert (p->actions, G_ACTION(a));
+ g_signal_connect (a, "activate",
+ G_CALLBACK(on_calendar_action_activated), self);
+ p->calendar_action = a;
+
+ rebuild_now (self, SECTION_HEADER);
+}
+
+/***
+****
+***/
+
+/**
+ * A small helper function for rebuild_now().
+ * - removes the previous section
+ * - adds and unrefs the new section
+ */
+static void
+rebuild_section (GMenu * parent, int pos, GMenuModel * new_section)
+{
+ g_menu_remove (parent, pos);
+ g_menu_insert_section (parent, pos, NULL, new_section);
+ g_object_unref (new_section);
+}
+
+static void
+rebuild_now (IndicatorDatetimeService * self, int sections)
+{
+ priv_t * p = self->priv;
+ struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP];
+ struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER];
+
+ if (sections & SECTION_HEADER)
+ {
+ g_simple_action_set_state (p->header_action, create_header_state (self));
+ }
+
+ if (sections & SECTION_CALENDAR)
+ {
+ rebuild_section (desktop->submenu, 0, create_calendar_section (self));
+ rebuild_section (greeter->submenu, 0, create_calendar_section (self));
+ }
+
+ if (sections & SECTION_APPOINTMENTS)
+ {
+ rebuild_section (desktop->submenu, 1, create_appointments_section (self));
+ }
+
+ if (sections & SECTION_LOCATIONS)
+ {
+ rebuild_section (desktop->submenu, 2, create_locations_section (self));
+ }
+
+ if (sections & SECTION_SETTINGS)
+ {
+ rebuild_section (desktop->submenu, 3, create_settings_section (self));
+ }
+}
+
+static int
+rebuild_timeout_func (IndicatorDatetimeService * self)
+{
+ priv_t * p = self->priv;
+ rebuild_now (self, p->rebuild_flags);
+ p->rebuild_flags = 0;
+ p->rebuild_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static void
+rebuild_soon (IndicatorDatetimeService * self, int section)
+{
+ priv_t * p = self->priv;
+
+ p->rebuild_flags |= section;
+
+ if (p->rebuild_id == 0)
+ {
+ /* Change events seem to come over the bus in small bursts. This msec
+ value is an arbitrary number that tries to be large enough to fold
+ multiple events into a single rebuild, but small enough that the
+ user won't notice any lag. */
+ static const int REBUILD_INTERVAL_MSEC = 500;
+
+ p->rebuild_id = g_timeout_add (REBUILD_INTERVAL_MSEC,
+ (GSourceFunc)rebuild_timeout_func,
+ self);
+ }
+}
+
+/***
+**** org.freedesktop.login1.Manager
+***/
+
+static void
+on_login1_manager_signal (GDBusProxy * proxy G_GNUC_UNUSED,
+ gchar * sender_name G_GNUC_UNUSED,
+ gchar * signal_name,
+ GVariant * parameters,
+ gpointer gself)
+{
+ if (!g_strcmp0 (signal_name, "PrepareForSleep"))
+ {
+ gboolean sleeping = FALSE;
+ g_variant_get (parameters, "(b)", &sleeping);
+ if (!sleeping)
+ on_local_time_jumped (INDICATOR_DATETIME_SERVICE (gself));
+ }
+}
+
+static void
+on_login1_manager_proxy_ready (GObject * object G_GNUC_UNUSED,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * err;
+ GDBusProxy * proxy;
+
+ err = NULL;
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &err);
+
+ if (err != NULL)
+ {
+ if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Could not grab DBus proxy for logind: %s", err->message);
+
+ g_error_free (err);
+ }
+ else
+ {
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+ self->priv->login1_manager = proxy;
+ g_signal_connect (proxy, "g-signal",
+ G_CALLBACK(on_login1_manager_signal), self);
+ }
+}
+
+/***
+**** GDBus
+***/
+
+static void
+on_bus_acquired (GDBusConnection * connection,
+ const gchar * name,
+ gpointer gself)
+{
+ int i;
+ guint id;
+ GError * err = NULL;
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(gself);
+ priv_t * p = self->priv;
+
+ g_debug ("bus acquired: %s", name);
+
+ p->conn = g_object_ref (G_OBJECT (connection));
+
+ /* export the actions */
+ if ((id = g_dbus_connection_export_action_group (connection,
+ BUS_PATH,
+ G_ACTION_GROUP (p->actions),
+ &err)))
+ {
+ p->actions_export_id = id;
+ }
+ else
+ {
+ g_warning ("cannot export action group: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ /* export the menus */
+ for (i=0; i<N_PROFILES; ++i)
+ {
+ 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),
+ &err)))
+ {
+ menu->export_id = id;
+ }
+ else
+ {
+ g_warning ("cannot export %s menu: %s", menu_names[i], err->message);
+ g_clear_error (&err);
+ }
+
+ g_free (path);
+ }
+}
+
+static void
+unexport (IndicatorDatetimeService * self)
+{
+ int i;
+ priv_t * p = self->priv;
+
+ /* unexport the menus */
+ for (i=0; i<N_PROFILES; ++i)
+ {
+ guint * id = &self->priv->menus[i].export_id;
+
+ if (*id)
+ {
+ g_dbus_connection_unexport_menu_model (p->conn, *id);
+ *id = 0;
+ }
+ }
+
+ /* unexport the actions */
+ if (p->actions_export_id)
+ {
+ g_dbus_connection_unexport_action_group (p->conn, p->actions_export_id);
+ p->actions_export_id = 0;
+ }
+}
+
+static void
+on_name_lost (GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * name,
+ gpointer gself)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE (gself);
+
+ g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, name);
+
+ unexport (self);
+
+ g_signal_emit (self, signals[SIGNAL_NAME_LOST], 0, NULL);
+}
+
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ int i;
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o);
+ priv_t * p = self->priv;
+
+ if (p->own_id)
+ {
+ g_bus_unown_name (p->own_id);
+ p->own_id = 0;
+ }
+
+ unexport (self);
+
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel (p->cancellable);
+ 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);
+ }
+
+ if (p->login1_manager != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->login1_manager, self);
+ g_clear_object (&p->login1_manager);
+ }
+
+ indicator_clear_timer (&p->skew_timer);
+ indicator_clear_timer (&p->rebuild_id);
+ indicator_clear_timer (&p->timezone_timer);
+ indicator_clear_timer (&p->header_timer);
+
+ if (p->settings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->settings, self);
+ g_clear_object (&p->settings);
+ }
+
+ g_clear_object (&p->actions);
+
+ 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->header_action);
+ g_clear_object (&p->conn);
+
+ G_OBJECT_CLASS (indicator_datetime_service_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorDatetimeService * self = INDICATOR_DATETIME_SERVICE(o);
+ priv_t * p = self->priv;
+
+ g_clear_pointer (&p->skew_time, g_date_time_unref);
+ g_clear_pointer (&p->calendar_date, g_date_time_unref);
+
+ G_OBJECT_CLASS (indicator_datetime_service_parent_class)->finalize (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_datetime_service_init (IndicatorDatetimeService * self)
+{
+ guint i, n;
+ priv_t * p;
+ GString * gstr = g_string_new (NULL);
+
+ /* these are the settings that affect the
+ contents of the respective sections */
+ const char * const header_settings[] = {
+ SETTINGS_SHOW_CLOCK_S,
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S,
+ SETTINGS_SHOW_DAY_S,
+ SETTINGS_SHOW_DATE_S,
+ SETTINGS_CUSTOM_TIME_FORMAT_S
+ };
+ const char * const calendar_settings[] = {
+ SETTINGS_SHOW_CALENDAR_S,
+ SETTINGS_SHOW_WEEK_NUMBERS_S
+ };
+ const char * const appointment_settings[] = {
+ SETTINGS_SHOW_EVENTS_S,
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S
+ };
+ const char * const location_settings[] = {
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S,
+ SETTINGS_CUSTOM_TIME_FORMAT_S,
+ SETTINGS_SHOW_LOCATIONS_S,
+ SETTINGS_LOCATIONS_S,
+ SETTINGS_SHOW_DETECTED_S,
+ SETTINGS_TIMEZONE_NAME_S
+ };
+ const char * const time_format_string_settings[] = {
+ SETTINGS_TIME_FORMAT_S,
+ SETTINGS_SHOW_SECONDS_S,
+ SETTINGS_CUSTOM_TIME_FORMAT_S
+ };
+
+
+ /* 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
+ ***/
+
+ 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]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_header_soon), self);
+ }
+
+ for (i=0, n=G_N_ELEMENTS(calendar_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", calendar_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_calendar_section_soon), self);
+ }
+
+ for (i=0, n=G_N_ELEMENTS(appointment_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", appointment_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_appointments_section_soon), self);
+ }
+
+ for (i=0, n=G_N_ELEMENTS(location_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", location_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(rebuild_locations_section_soon), self);
+ }
+
+ /* The keys in time_format_string_settings affect the time format strings we build.
+ When these change, we need to rebuild everything that has a time format string. */
+ for (i=0, n=G_N_ELEMENTS(time_format_string_settings); i<n; i++)
+ {
+ g_string_printf (gstr, "changed::%s", time_format_string_settings[i]);
+ g_signal_connect_swapped (p->settings, gstr->str,
+ G_CALLBACK(on_local_time_jumped), self);
+ }
+
+ init_gactions (self);
+
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ p->cancellable,
+ on_login1_manager_proxy_ready,
+ self);
+
+ p->skew_timer = g_timeout_add_seconds (SKEW_CHECK_INTERVAL_SEC,
+ skew_timer_func,
+ self);
+
+ p->own_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+ on_bus_acquired,
+ NULL,
+ on_name_lost,
+ self,
+ NULL);
+
+ on_local_time_jumped (self);
+
+ g_string_free (gstr, TRUE);
+}
+
+static void
+indicator_datetime_service_class_init (IndicatorDatetimeServiceClass * 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 (IndicatorDatetimeServicePrivate));
+
+ signals[SIGNAL_NAME_LOST] = g_signal_new (
+ INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IndicatorDatetimeServiceClass, name_lost),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorDatetimeService *
+indicator_datetime_service_new (void)
+{
+ GObject * o = g_object_new (INDICATOR_TYPE_DATETIME_SERVICE, 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)
+{
+ gboolean dirty;
+ priv_t * p = self->priv;
+
+ dirty = !date || !p->calendar_date || g_date_time_compare (date, p->calendar_date);
+
+ /* update calendar_date */
+ g_clear_pointer (&p->calendar_date, g_date_time_unref);
+ if (date != NULL)
+ p->calendar_date = g_date_time_ref (date);
+
+ /* sync the menuitems and action states */
+ if (dirty)
+ {
+ update_calendar_action_state (self);
+ rebuild_appointments_section_soon (self);
+ }
+}
diff --git a/src/service.h b/src/service.h
new file mode 100644
index 0000000..b142882
--- /dev/null
+++ b/src/service.h
@@ -0,0 +1,75 @@
+/*
+ * 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_SERVICE_H__
+#define __INDICATOR_DATETIME_SERVICE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* standard GObject macros */
+#define INDICATOR_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_SERVICE, IndicatorDatetimeService))
+#define INDICATOR_TYPE_DATETIME_SERVICE (indicator_datetime_service_get_type())
+#define INDICATOR_IS_DATETIME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_SERVICE))
+
+typedef struct _IndicatorDatetimeService IndicatorDatetimeService;
+typedef struct _IndicatorDatetimeServiceClass IndicatorDatetimeServiceClass;
+typedef struct _IndicatorDatetimeServicePrivate IndicatorDatetimeServicePrivate;
+
+/* signal keys */
+#define INDICATOR_DATETIME_SERVICE_SIGNAL_NAME_LOST "name-lost"
+
+/**
+ * The Indicator Datetime Service.
+ */
+struct _IndicatorDatetimeService
+{
+ /*< private >*/
+ GObject parent;
+ IndicatorDatetimeServicePrivate * priv;
+};
+
+struct _IndicatorDatetimeServiceClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+
+ void (* name_lost)(IndicatorDatetimeService * self);
+};
+
+/***
+****
+***/
+
+GType indicator_datetime_service_get_type (void);
+
+IndicatorDatetimeService * indicator_datetime_service_new (void);
+
+GDateTime * indicator_datetime_service_get_localtime (IndicatorDatetimeService * service);
+
+void indicator_datetime_service_set_calendar_date (IndicatorDatetimeService * self,
+ GDateTime * date);
+
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_SERVICE_H__ */
diff --git a/src/timezone-file.c b/src/timezone-file.c
new file mode 100644
index 0000000..2adf2ca
--- /dev/null
+++ b/src/timezone-file.c
@@ -0,0 +1,240 @@
+/*
+ * 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 <gio/gio.h> /* GFile, GFileMonitor */
+
+#include "timezone-file.h"
+
+enum
+{
+ PROP_0,
+ PROP_FILENAME,
+ PROP_LAST
+};
+
+static GParamSpec * properties[PROP_LAST] = { 0 };
+
+struct _IndicatorDatetimeTimezoneFilePriv
+{
+ gchar * filename;
+ GFile * file;
+ GFileMonitor * monitor;
+ gchar * timezone;
+};
+
+typedef IndicatorDatetimeTimezoneFilePriv priv_t;
+
+G_DEFINE_TYPE (IndicatorDatetimeTimezoneFile,
+ indicator_datetime_timezone_file,
+ INDICATOR_TYPE_DATETIME_TIMEZONE)
+
+/***
+****
+***/
+
+static void
+reload (IndicatorDatetimeTimezoneFile * self)
+{
+ priv_t * p = self->priv;
+
+ GError * err = NULL;
+ gchar * new_timezone = NULL;
+
+ if (!g_file_get_contents (p->filename, &new_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);
+ }
+}
+
+static void
+set_filename (IndicatorDatetimeTimezoneFile * self, const char * filename)
+{
+ GError * err;
+ 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);
+ if (err != NULL)
+ {
+ g_warning ("%s Unable to monitor timezone file '%s': %s", G_STRLOC, TIMEZONE_FILE, err->message);
+ g_error_free (err);
+ }
+ else
+ {
+ g_signal_connect_swapped (p->monitor, "changed", G_CALLBACK(reload), self);
+ g_debug ("%s Monitoring timezone file '%s'", G_STRLOC, p->filename);
+ }
+
+ reload (self);
+}
+
+/***
+**** IndicatorDatetimeTimezoneClass funcs
+***/
+
+static const char *
+my_get_timezone (IndicatorDatetimeTimezone * self)
+{
+ return INDICATOR_DATETIME_TIMEZONE_FILE(self)->priv->timezone;
+}
+
+/***
+**** GObjectClass funcs
+***/
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o);
+
+ switch (property_id)
+ {
+ case PROP_FILENAME:
+ g_value_set_string (value, self->priv->filename);
+ 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)
+{
+ IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (o);
+
+ switch (property_id)
+ {
+ case PROP_FILENAME:
+ set_filename (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (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);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ IndicatorDatetimeTimezoneFile * self = INDICATOR_DATETIME_TIMEZONE_FILE (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);
+}
+
+/***
+****
+***/
+
+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);
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+ 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 */
+
+ properties[PROP_0] = NULL;
+
+ properties[PROP_FILENAME] = g_param_spec_string ("filename",
+ "Filename",
+ "Filename to monitor for TZ changes",
+ "",
+ flags);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+}
+
+static void
+indicator_datetime_timezone_file_init (IndicatorDatetimeTimezoneFile * self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_TIMEZONE_FILE,
+ IndicatorDatetimeTimezoneFilePriv);
+}
+
+/***
+**** Public
+***/
+
+IndicatorDatetimeTimezone *
+indicator_datetime_timezone_file_new (const char * filename)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, "filename", filename, NULL);
+
+ return INDICATOR_DATETIME_TIMEZONE (o);
+}
diff --git a/src/timezone-file.h b/src/timezone-file.h
new file mode 100644
index 0000000..b02abe1
--- /dev/null
+++ b/src/timezone-file.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_TIMEZONE_FILE__H__
+#define __INDICATOR_DATETIME_TIMEZONE_FILE__H__
+
+#include "timezone.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_TIMEZONE_FILE (indicator_datetime_timezone_file_get_type())
+#define INDICATOR_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFile))
+#define INDICATOR_DATETIME_TIMEZONE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE, IndicatorDatetimeTimezoneFileClass))
+#define INDICATOR_IS_DATETIME_TIMEZONE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_FILE))
+
+typedef struct _IndicatorDatetimeTimezoneFile IndicatorDatetimeTimezoneFile;
+typedef struct _IndicatorDatetimeTimezoneFilePriv IndicatorDatetimeTimezoneFilePriv;
+typedef struct _IndicatorDatetimeTimezoneFileClass IndicatorDatetimeTimezoneFileClass;
+
+GType indicator_datetime_timezone_file_get_type (void);
+
+/**
+ * An IndicatorDatetimeTimezone which uses a local file,
+ * such as /etc/timezone, to determine the timezone.
+ */
+struct _IndicatorDatetimeTimezoneFile
+{
+ /*< private >*/
+ IndicatorDatetimeTimezone parent;
+ IndicatorDatetimeTimezoneFilePriv * priv;
+};
+
+struct _IndicatorDatetimeTimezoneFileClass
+{
+ IndicatorDatetimeTimezoneClass parent_class;
+};
+
+IndicatorDatetimeTimezone * indicator_datetime_timezone_file_new (const char * filename);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_TIMEZONE_FILE__H__ */
diff --git a/src/timezone-geoclue.c b/src/timezone-geoclue.c
new file mode 100644
index 0000000..239ac50
--- /dev/null
+++ b/src/timezone-geoclue.c
@@ -0,0 +1,252 @@
+/*
+ * 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 <geoclue/geoclue-master.h>
+#include <geoclue/geoclue-master-client.h>
+
+#include "timezone-geoclue.h"
+
+struct _IndicatorDatetimeTimezoneGeocluePriv
+{
+ GeoclueMaster * master;
+ GeoclueMasterClient * client;
+ GeoclueAddress * address;
+ gchar * timezone;
+};
+
+typedef IndicatorDatetimeTimezoneGeocluePriv priv_t;
+
+G_DEFINE_TYPE (IndicatorDatetimeTimezoneGeoclue,
+ indicator_datetime_timezone_geoclue,
+ INDICATOR_TYPE_DATETIME_TIMEZONE)
+
+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,
+ GeoclueAccuracy * accuracy G_GNUC_UNUSED,
+ GError * error,
+ gpointer gself)
+{
+ if (error != NULL)
+ {
+ g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message);
+ }
+ else
+ {
+ IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself);
+ const char * timezone = g_hash_table_lookup (addy_data, "timezone");
+ set_timezone (self, timezone);
+ }
+}
+
+static void
+on_address_created (GeoclueMasterClient * master G_GNUC_UNUSED,
+ GeoclueAddress * address,
+ GError * error,
+ gpointer gself)
+{
+ if (error != NULL)
+ {
+ g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message);
+ }
+ else
+ {
+ priv_t * p = INDICATOR_DATETIME_TIMEZONE_GEOCLUE(gself)->priv;
+
+ g_assert (p->address == NULL);
+ 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);
+ }
+}
+
+static void
+on_requirements_set (GeoclueMasterClient * master G_GNUC_UNUSED,
+ GError * error,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ if (error != NULL)
+ {
+ g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message);
+ }
+}
+
+static void
+on_client_created (GeoclueMaster * master G_GNUC_UNUSED,
+ GeoclueMasterClient * client,
+ gchar * path,
+ GError * error,
+ gpointer gself)
+{
+ g_debug ("Created Geoclue client at: %s", path);
+
+ if (error != NULL)
+ {
+ g_warning ("%s Unable to get timezone from GeoClue: %s", G_STRFUNC, error->message);
+ }
+ else
+ {
+ IndicatorDatetimeTimezoneGeoclue * self = INDICATOR_DATETIME_TIMEZONE_GEOCLUE (gself);
+ priv_t * p = self->priv;
+
+ g_clear_object (&p->client);
+ p->client = g_object_ref (client);
+ g_signal_connect_swapped (p->client, "invalidated", G_CALLBACK(geo_restart), gself);
+
+ geoclue_master_client_set_requirements_async (p->client,
+ GEOCLUE_ACCURACY_LEVEL_REGION,
+ 0,
+ FALSE,
+ GEOCLUE_RESOURCE_ALL,
+ on_requirements_set,
+ NULL);
+
+ geoclue_master_client_create_address_async (p->client, on_address_created, gself);
+ }
+}
+
+static void
+geo_start (IndicatorDatetimeTimezoneGeoclue * self)
+{
+ priv_t * p = self->priv;
+
+ g_assert (p->master == NULL);
+ p->master = geoclue_master_get_default ();
+ geoclue_master_create_client_async (p->master, on_client_created, self);
+}
+
+static void
+geo_stop (IndicatorDatetimeTimezoneGeoclue * self)
+{
+ priv_t * p = self->priv;
+
+ if (p->address != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (p->address, on_address_changed, self);
+ g_clear_object (&p->address);
+ }
+
+ if (p->client != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (p->client, geo_restart, self);
+ g_clear_object (&p->client);
+ }
+
+ g_clear_object (&p->master);
+}
+
+static void
+geo_restart (IndicatorDatetimeTimezoneGeoclue * self)
+{
+ geo_stop (self);
+ geo_start (self);
+}
+
+/***
+****
+***/
+
+static const char *
+my_get_timezone (IndicatorDatetimeTimezone * self)
+{
+ return INDICATOR_DATETIME_TIMEZONE_GEOCLUE(self)->priv->timezone;
+}
+
+static void
+my_dispose (GObject * o)
+{
+ geo_stop (INDICATOR_DATETIME_TIMEZONE_GEOCLUE (o));
+
+ G_OBJECT_CLASS (indicator_datetime_timezone_geoclue_parent_class)->dispose (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));
+}
+
+static void
+indicator_datetime_timezone_geoclue_init (IndicatorDatetimeTimezoneGeoclue * self)
+{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE,
+ IndicatorDatetimeTimezoneGeocluePriv);
+ self->priv = p;
+
+ geo_start (self);
+}
+
+/***
+**** Public
+***/
+
+IndicatorDatetimeTimezone *
+indicator_datetime_timezone_geoclue_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, NULL);
+
+ return INDICATOR_DATETIME_TIMEZONE (o);
+}
diff --git a/src/timezone-geoclue.h b/src/timezone-geoclue.h
new file mode 100644
index 0000000..059bd81
--- /dev/null
+++ b/src/timezone-geoclue.h
@@ -0,0 +1,57 @@
+/*
+ * 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_TIMEZONE_GEOCLUE__H__
+#define __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__
+
+#include "timezone.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE (indicator_datetime_timezone_geoclue_get_type())
+#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclue))
+#define INDICATOR_DATETIME_TIMEZONE_GEOCLUE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE, IndicatorDatetimeTimezoneGeoclueClass))
+#define INDICATOR_IS_DATETIME_TIMEZONE_GEOCLUE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE_GEOCLUE))
+
+typedef struct _IndicatorDatetimeTimezoneGeoclue IndicatorDatetimeTimezoneGeoclue;
+typedef struct _IndicatorDatetimeTimezoneGeocluePriv IndicatorDatetimeTimezoneGeocluePriv;
+typedef struct _IndicatorDatetimeTimezoneGeoclueClass IndicatorDatetimeTimezoneGeoclueClass;
+
+GType indicator_datetime_timezone_geoclue_get_type (void);
+
+/**
+ * An IndicatorDatetimeTimezone which uses GeoClue to determine the timezone.
+ */
+struct _IndicatorDatetimeTimezoneGeoclue
+{
+ /*< private >*/
+ IndicatorDatetimeTimezone parent;
+ IndicatorDatetimeTimezoneGeocluePriv * priv;
+};
+
+struct _IndicatorDatetimeTimezoneGeoclueClass
+{
+ IndicatorDatetimeTimezoneClass parent_class;
+};
+
+IndicatorDatetimeTimezone * indicator_datetime_timezone_geoclue_new (void);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_TIMEZONE_GEOCLUE__H__ */
diff --git a/src/timezone.c b/src/timezone.c
new file mode 100644
index 0000000..a543155
--- /dev/null
+++ b/src/timezone.c
@@ -0,0 +1,105 @@
+/*
+ * 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 "timezone.h"
+
+G_DEFINE_TYPE (IndicatorDatetimeTimezone,
+ indicator_datetime_timezone,
+ G_TYPE_OBJECT)
+
+enum
+{
+ PROP_0,
+ PROP_TIMEZONE,
+ PROP_LAST
+};
+
+static GParamSpec * properties[PROP_LAST] = { 0, };
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorDatetimeTimezone * self = INDICATOR_DATETIME_TIMEZONE (o);
+
+ switch (property_id)
+ {
+ case PROP_TIMEZONE:
+ g_value_set_string (value, indicator_datetime_timezone_get_timezone (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_dispose (GObject * object)
+{
+ G_OBJECT_CLASS (indicator_datetime_timezone_parent_class)->dispose (object);
+}
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_datetime_timezone_class_init (IndicatorDatetimeTimezoneClass * klass)
+{
+ GObjectClass * object_class;
+ const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->get_property = my_get_property;
+ object_class->dispose = my_dispose;
+
+ klass->get_timezone = NULL;
+
+ properties[PROP_TIMEZONE] = g_param_spec_string ("timezone",
+ "Timezone",
+ "Timezone",
+ "",
+ flags);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+}
+
+static void
+indicator_datetime_timezone_init (IndicatorDatetimeTimezone * self G_GNUC_UNUSED)
+{
+}
+
+/***
+****
+***/
+
+const char *
+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);
+}
+
+void
+indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone * self)
+{
+ g_return_if_fail (INDICATOR_IS_DATETIME_TIMEZONE (self));
+
+ g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_TIMEZONE]);
+}
diff --git a/src/timezone.h b/src/timezone.h
new file mode 100644
index 0000000..076bedc
--- /dev/null
+++ b/src/timezone.h
@@ -0,0 +1,74 @@
+/*
+ * 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_TIMEZONE__H__
+#define __INDICATOR_DATETIME_TIMEZONE__H__
+
+#include <glib.h>
+#include <glib-object.h> /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DATETIME_TIMEZONE (indicator_datetime_timezone_get_type())
+#define INDICATOR_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezone))
+#define INDICATOR_DATETIME_TIMEZONE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass))
+#define INDICATOR_DATETIME_TIMEZONE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_DATETIME_TIMEZONE, IndicatorDatetimeTimezoneClass))
+#define INDICATOR_IS_DATETIME_TIMEZONE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_DATETIME_TIMEZONE))
+
+typedef struct _IndicatorDatetimeTimezone IndicatorDatetimeTimezone;
+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.
+ *
+ * We use this in datetime to determine the user's current timezone
+ * for display in the 'locations' section of the datetime indicator.
+ *
+ * This class has a 'timezone' property that clients can watch
+ * for change notifications.
+ */
+struct _IndicatorDatetimeTimezone
+{
+ /*< private >*/
+ GObject parent;
+};
+
+struct _IndicatorDatetimeTimezoneClass
+{
+ GObjectClass parent_class;
+
+ /* virtual functions */
+ const char * (*get_timezone) (IndicatorDatetimeTimezone * self);
+};
+
+/***
+****
+***/
+
+const char * indicator_datetime_timezone_get_timezone (IndicatorDatetimeTimezone *);
+
+void indicator_datetime_timezone_notify_timezone (IndicatorDatetimeTimezone *);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_DATETIME_TIMEZONE__H__ */
diff --git a/src/utils.c b/src/utils.c
index f5aecd6..fa54008 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -117,30 +117,6 @@ get_current_zone_name (const gchar * location)
return rv;
}
-gchar *
-read_timezone ()
-{
- GError * error = NULL;
- gchar * tempzone = NULL;
- if (!g_file_get_contents(TIMEZONE_FILE, &tempzone, NULL, &error)) {
- g_warning("Unable to read timezone file '" TIMEZONE_FILE "': %s", error->message);
- g_error_free(error);
- return NULL;
- }
-
- /* This shouldn't happen, so let's make it a big boom! */
- g_return_val_if_fail(tempzone != NULL, NULL);
-
- /* Note: this really makes sense as strstrip works in place
- so we end up with something a little odd without the dup
- so we have the dup to make sure everything is as expected
- for everyone else. */
- gchar * rv = g_strdup(g_strstrip(tempzone));
- g_free(tempzone);
-
- return rv;
-}
-
/* Translate msg according to the locale specified by LC_TIME */
static char *
T_(const char *msg)
@@ -250,7 +226,7 @@ generate_format_string_full (gboolean show_day, gboolean show_date)
}
gchar *
-generate_format_string_at_time (GDateTime * time)
+generate_format_string_at_time (GDateTime * now, GDateTime * time)
{
/* This is a bit less free-form than for the main "now" time label. */
/* If it is today, just the time should be shown (e.g. “3:55 PM”)
@@ -260,8 +236,6 @@ generate_format_string_at_time (GDateTime * time)
gboolean show_day = FALSE;
gboolean show_date = FALSE;
- GDateTime * now = g_date_time_new_now_local();
-
/* First, are we same day? */
gint time_year, time_month, time_day;
gint now_year, now_month, now_day;
@@ -296,8 +270,6 @@ generate_format_string_at_time (GDateTime * time)
g_date_time_unref(future_bound);
}
- g_date_time_unref (now);
-
return generate_format_string_full(show_day, show_date);
}
diff --git a/src/utils.h b/src/utils.h
index 788d516..8dc2964 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -30,9 +30,8 @@ G_BEGIN_DECLS
gboolean is_locale_12h (void);
void split_settings_location (const gchar * location, gchar ** zone, gchar ** name);
gchar * get_current_zone_name (const gchar * location);
-gchar * read_timezone ();
gchar * generate_format_string_full (gboolean show_day, gboolean show_date);
-gchar * generate_format_string_at_time (GDateTime * time);
+gchar * generate_format_string_at_time (GDateTime * now, GDateTime * time);
G_END_DECLS