← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~ted/indicator-datetime/configurable-format into lp:indicator-datetime

 

Ted Gould has proposed merging lp:~ted/indicator-datetime/configurable-format into lp:indicator-datetime.

Requested reviews:
  Indicator Applet Developers (indicator-applet-developers)


Making it so that the clock is very configurable via gsettings.  I think that we can now support any time format any crazy country can throw at us!  Whew!
-- 
https://code.launchpad.net/~ted/indicator-datetime/configurable-format/+merge/30160
Your team ayatana-commits is subscribed to branch lp:indicator-datetime.
=== modified file '.bzrignore'
--- .bzrignore	2010-07-07 22:01:53 +0000
+++ .bzrignore	2010-07-16 22:03:40 +0000
@@ -6,3 +6,4 @@
 po/indicator-datetime.pot
 indicator-datetime-[0-9].[0-9].[0-9].tar.gz
 data/indicator-datetime.service
+data/org.ayatana.indicator.datetime.gschema.valid

=== modified file 'configure.ac'
--- configure.ac	2010-07-15 15:30:50 +0000
+++ configure.ac	2010-07-16 22:03:40 +0000
@@ -35,15 +35,23 @@
 INDICATOR_REQUIRED_VERSION=0.3.0
 DBUSMENUGLIB_REQUIRED_VERSION=0.1.1
 DBUSMENUGTK_REQUIRED_VERSION=0.1.1
+GIO_REQUIRED_VERSION=2.25.0
 
 PKG_CHECK_MODULES(INDICATOR, indicator >= $INDICATOR_REQUIRED_VERSION
                              dbusmenu-glib >= $DBUSMENUGLIB_REQUIRED_VERSION
-                             dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION)
+                             dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION
+                             gio-2.0 >= $GIO_REQUIRED_VERSION)
 
 AC_SUBST(INDICATOR_CFLAGS)
 AC_SUBST(INDICATOR_LIBS)
 
 ###########################
+# Grab the GSettings Macros
+###########################
+
+GLIB_GSETTINGS
+
+###########################
 # Check to see if we're local
 ###########################
 

=== modified file 'data/Makefile.am'
--- data/Makefile.am	2010-05-19 02:47:35 +0000
+++ data/Makefile.am	2010-07-16 22:03:40 +0000
@@ -1,5 +1,9 @@
 #SUBDIRS = icons
 
+gsettings_SCHEMAS = \
+	org.ayatana.indicator.datetime.gschema.xml
+@GSETTINGS_RULES@
+
 dbus_servicesdir = $(DBUSSERVICEDIR)
 dbus_services_DATA = indicator-datetime.service
 

=== added file 'data/org.ayatana.indicator.datetime.gschema.xml'
--- data/org.ayatana.indicator.datetime.gschema.xml	1970-01-01 00:00:00 +0000
+++ data/org.ayatana.indicator.datetime.gschema.xml	2010-07-16 22:03:40 +0000
@@ -0,0 +1,61 @@
+<schemalist>
+	<enum id="time-enum">
+		<value nick="locale-default" value="0" />
+		<value nick="12-hour" value="1" />
+		<value nick="24-hour" value="2" />
+		<value nick="custom" value="3" />
+	</enum>
+	<schema id="org.ayatana.indicator.datetime" path="/apps/indicators/datetime/" gettext-domain="indicator-datetime">
+		<key name="time-format" enum="time-enum">
+			<default>'locale-default'</default>
+			<summary>What the time format should be</summary>
+			<description>
+			  Controls the time format that is displayed in the indicator.  For almost
+			  all users this should be the default for their locale.  If you think the
+			  setting is wrong for your locale please join or talk to the translation
+			  team for your langauge.  If you just want something different you can
+			  adjust this to be either 12 or 24 time.  Or, you can use a custom format
+			  string and set the custom-time-format setting.
+			</description>
+		</key>
+		<key name="show-seconds" type="b">
+			<default>false</default>
+			<summary>Show the number of seconds in the indicator</summary>
+			<description>
+			  Makes the datetime indicator show the number of seconds in the indicator.
+			  It's important to note that this will cause additional battery drain as
+			  the time will update 60 times as often, so it is not recommended.  Also,
+			  this setting will be ignored if the time-format value is set to custom.
+			</description>
+		</key>
+		<key name="show-day" type="b">
+			<default>false</default>
+			<summary>Show the day of the week in the indicator</summary>
+			<description>
+			  Puts the day of the week on the panel along with the time and/or date
+			  depending on settings.  This setting will be ignored if the time-format
+			  value is set to custom.
+			</description>
+		</key>
+		<key name="show-date" type="b">
+			<default>false</default>
+			<summary>Show the month and date in the indicator</summary>
+			<description>
+			  Puts the month and the date in the panel along with the time and/or day
+			  of the week depending on settings.  This setting will be ignored if the
+			  time-format value is set to custom.
+			</description>
+		</key>
+		<key name="custom-time-format" type="s">
+			<default>"%l:%M %p"</default>
+			<summary>The format string passed to strftime</summary>
+			<description>
+			  The format of the time and/or date that is visible on the panel when using
+			  the indicator.  For most users this will be a set of predefined values as
+			  determined by the configuration utility, but advanced users can change it
+			  to anything strftime can accept.  Look at the man page on strftime for
+			  more information.
+			</description>
+		</key>
+	</schema>
+</schemalist>

=== modified file 'src/indicator-datetime.c'
--- src/indicator-datetime.c	2010-07-12 20:51:03 +0000
+++ src/indicator-datetime.c	2010-07-16 22:03:40 +0000
@@ -27,6 +27,7 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <glib/gi18n-lib.h>
+#include <gio/gio.h>
 
 /* Indicator Stuff */
 #include <libindicator/indicator.h>
@@ -63,12 +64,57 @@
 	GtkLabel * label;
 	guint timer;
 
+	gchar * time_string;
+
+	gint time_mode;
+	gboolean show_seconds;
+	gboolean show_date;
+	gboolean show_day;
+	gchar * custom_string;
+
 	guint idle_measure;
 	gint  max_width;
 
 	IndicatorServiceManager * sm;
 	DbusmenuGtkMenu * menu;
-};
+
+	GSettings * settings;
+};
+
+/* Enum for the properties so that they can be quickly
+   found and looked up. */
+enum {
+	PROP_0,
+	PROP_TIME_FORMAT,
+	PROP_SHOW_SECONDS,
+	PROP_SHOW_DAY,
+	PROP_SHOW_DATE,
+	PROP_CUSTOM_TIME_FORMAT
+};
+
+#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 SETTINGS_INTERFACE              "org.ayatana.indicator.datetime"
+#define SETTINGS_TIME_FORMAT_S          "time-format"
+#define SETTINGS_SHOW_SECONDS_S         "show-seconds"
+#define SETTINGS_SHOW_DAY_S             "show-day"
+#define SETTINGS_SHOW_DATE_S            "show-date"
+#define SETTINGS_CUSTOM_TIME_FORMAT_S   "custom-time-format"
+
+enum {
+	SETTINGS_TIME_LOCALE = 0,
+	SETTINGS_TIME_12_HOUR = 1,
+	SETTINGS_TIME_24_HOUR = 2,
+	SETTINGS_TIME_CUSTOM = 3
+};
+
+#define DEFAULT_TIME_12_FORMAT   "%l:%M %p"
+#define DEFAULT_TIME_24_FORMAT   "%H:%M"
+#define DEFAULT_TIME_FORMAT      DEFAULT_TIME_12_FORMAT
 
 #define INDICATOR_DATETIME_GET_PRIVATE(o) \
 (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_DATETIME_TYPE, IndicatorDatetimePrivate))
@@ -77,10 +123,18 @@
 
 static void indicator_datetime_class_init (IndicatorDatetimeClass *klass);
 static void indicator_datetime_init       (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 GVariant * bind_enum_set           (const GValue * value, const GVariantType * type, gpointer user_data);
+static gboolean bind_enum_get             (GValue * value, GVariant * variant, gpointer user_data);
+static gchar * generate_format_string     (IndicatorDatetime * self);
+static struct tm * update_label           (IndicatorDatetime * io);
+static void guess_label_size              (IndicatorDatetime * self);
+static void setup_timer                   (IndicatorDatetime * self, struct tm * ltime);
 
 /* Indicator Module Config */
 INDICATOR_SET_VERSION
@@ -98,11 +152,52 @@
 	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;
 
+	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 strftime().",
+	                                                  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 strftime to make a string for displaying on the panel.",
+	                                                     DEFAULT_TIME_FORMAT,
+	                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
 	return;
 }
 
@@ -117,9 +212,51 @@
 	self->priv->idle_measure = 0;
 	self->priv->max_width = 0;
 
+	self->priv->time_string = g_strdup(DEFAULT_TIME_FORMAT);
+
+	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->sm = NULL;
 	self->priv->menu = NULL;
 
+	self->priv->settings = g_settings_new(SETTINGS_INTERFACE);
+	if (self->priv->settings != NULL) {
+		g_settings_bind_with_mapping(self->priv->settings,
+		                SETTINGS_TIME_FORMAT_S,
+		                self,
+		                PROP_TIME_FORMAT_S,
+		                G_SETTINGS_BIND_DEFAULT,
+		                bind_enum_get,
+		                bind_enum_set,
+		                NULL, NULL); /* Userdata and destroy func */
+		g_settings_bind(self->priv->settings,
+		                SETTINGS_SHOW_SECONDS_S,
+		                self,
+		                PROP_SHOW_SECONDS_S,
+		                G_SETTINGS_BIND_DEFAULT);
+		g_settings_bind(self->priv->settings,
+		                SETTINGS_SHOW_DAY_S,
+		                self,
+		                PROP_SHOW_DAY_S,
+		                G_SETTINGS_BIND_DEFAULT);
+		g_settings_bind(self->priv->settings,
+		                SETTINGS_SHOW_DATE_S,
+		                self,
+		                PROP_SHOW_DATE_S,
+		                G_SETTINGS_BIND_DEFAULT);
+		g_settings_bind(self->priv->settings,
+		                SETTINGS_CUSTOM_TIME_FORMAT_S,
+		                self,
+		                PROP_CUSTOM_TIME_FORMAT_S,
+		                G_SETTINGS_BIND_DEFAULT);
+	} else {
+		g_warning("Unable to get settings for '" SETTINGS_INTERFACE "'");
+	}
+
 	self->priv->sm = indicator_service_manager_new_version(SERVICE_NAME, SERVICE_VERSION);
 
 	return;
@@ -155,6 +292,11 @@
 		self->priv->sm = NULL;
 	}
 
+	if (self->priv->settings != NULL) {
+		g_object_unref(G_OBJECT(self->priv->settings));
+		self->priv->settings = NULL;
+	}
+
 	G_OBJECT_CLASS (indicator_datetime_parent_class)->dispose (object);
 	return;
 }
@@ -162,11 +304,181 @@
 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 the int value into a string GVariant */
+static GVariant *
+bind_enum_set (const GValue * value, const GVariantType * type, gpointer user_data)
+{
+	switch (g_value_get_int(value)) {
+	case SETTINGS_TIME_LOCALE:
+		return g_variant_new_string("locale-default");
+	case SETTINGS_TIME_12_HOUR:
+		return g_variant_new_string("12-hour");
+	case SETTINGS_TIME_24_HOUR:
+		return g_variant_new_string("24-hour");
+	case SETTINGS_TIME_CUSTOM:
+		return g_variant_new_string("custom");
+	default:
+		return NULL;
+	}
+}
+
+/* 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;
+}
+
+/* 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_TIME_FORMAT: {
+		gint newval = g_value_get_int(value);
+		if (newval != self->priv->time_mode) {
+			update = TRUE;
+			self->priv->time_mode = newval;
+		}
+		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);
+			if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
+				update = TRUE;
+				setup_timer(self, NULL);
+			}
+		}
+		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(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);
+	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_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;
+	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
@@ -190,12 +502,12 @@
 }
 
 /* Updates the label to be the current time. */
-static void
+static struct tm *
 update_label (IndicatorDatetime * io)
 {
 	IndicatorDatetime * self = INDICATOR_DATETIME(io);
 
-	if (self->priv->label == NULL) return;
+	if (self->priv->label == NULL) return NULL;
 
 	gchar longstr[128];
 	time_t t;
@@ -206,10 +518,10 @@
 	if (ltime == NULL) {
 		g_debug("Error getting local time");
 		gtk_label_set_label(self->priv->label, _("Error getting time"));
-		return;
+		return NULL;
 	}
 
-	strftime(longstr, 128, "%l:%M %p", ltime);
+	strftime(longstr, 128, self->priv->time_string, ltime);
 	
 	gchar * utf8 = g_locale_to_utf8(longstr, -1, NULL, NULL, NULL);
 	gtk_label_set_label(self->priv->label, utf8);
@@ -219,24 +531,43 @@
 		self->priv->idle_measure = g_idle_add(idle_measure, io);
 	}
 
-	return;
+	return ltime;
 }
 
 /* Runs every minute and updates the time */
 gboolean
-minute_timer_func (gpointer user_data)
+timer_func (gpointer user_data)
 {
 	IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
+	self->priv->timer = 0;
+	struct tm * ltime = update_label(self);
+	setup_timer(self, ltime);
+	return FALSE;
+}
 
-	if (self->priv->label != NULL) {
-		update_label(self);
-		return TRUE;
+/* Configure the timer to run the next time through */
+static void
+setup_timer (IndicatorDatetime * self, struct tm * ltime)
+{
+	if (self->priv->timer != 0) {
+		g_source_remove(self->priv->timer);
+		self->priv->timer = 0;
+	}
+	
+	if (self->priv->show_seconds) {
+		self->priv->timer = g_timeout_add_seconds(1, timer_func, self);
 	} else {
-		self->priv->timer = 0;
-		return FALSE;
+		if (ltime == NULL) {
+			time_t t;
+			t = time(NULL);
+			ltime = localtime(&t);
+		}
+
+		/* Plus 2 so we're just after the minute, don't want to be early. */
+		self->priv->timer = g_timeout_add_seconds(60 - ltime->tm_sec + 2, timer_func, self);
 	}
 
-	return FALSE;
+	return;
 }
 
 /* Does a quick meausre of how big the string is in
@@ -254,33 +585,203 @@
 	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;
+};
+
+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)
+};
+
+/* 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 (IndicatorDatetime * self)
+{
+	gint retval = 0;
+	glong strlength = g_utf8_strlen(self->priv->time_string, -1);
+	gint i;
+	g_debug("Evaluating bitmask for '%s'", self->priv->time_string);
+
+	for (i = 0; i < strlength; i++) {
+		if (self->priv->time_string[i] == '%' && i + 1 < strlength) {
+			gchar evalchar = self->priv->time_string[i + 1];
+
+			/* If we're using alternate formats we need to skip those characters */
+			if (evalchar == 'E' || evalchar == 'O') {
+				if (i + 2 < strlength) {
+					evalchar = self->priv->time_string[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));
-
-	/* TRANSLATORS: This string is used for measuring the size of
-	   the font used for showing the time and is not shown to the
-	   user anywhere. */
-	gchar * am_str = g_strdup_printf(_("%d%d:%d%d AM"), FAT_NUMBER, FAT_NUMBER, FAT_NUMBER, FAT_NUMBER);
-	gint am_width = measure_string(style, context, am_str);
-	g_free(am_str);
-
-	/* TRANSLATORS: This string is used for measuring the size of
-	   the font used for showing the time and is not shown to the
-	   user anywhere. */
-	gchar * pm_str = g_strdup_printf(_("%d%d:%d%d PM"), FAT_NUMBER, FAT_NUMBER, FAT_NUMBER, FAT_NUMBER);
-	gint pm_width = measure_string(style, context, pm_str);
-	g_free(pm_str);
-
-	self->priv->max_width = MAX(am_width, pm_width);
+	gint * max_width = &(self->priv->max_width);
+	gint posibilitymask = generate_strftime_bitmask(self);
+
+	/* 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 posible times", timevals->len);
+	gint check_time;
+	for (check_time = 0; check_time < timevals->len; check_time++) {
+		gchar longstr[128];
+		strftime(longstr, 128, self->priv->time_string, &(g_array_index(timevals, struct tm, check_time)));
+		
+		gchar * utf8 = g_locale_to_utf8(longstr, -1, NULL, NULL, NULL);
+		gint length = measure_string(style, context, utf8);
+		g_free(utf8);
+
+		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;
@@ -298,6 +799,89 @@
 	return;
 }
 
+/* Tries to figure out what our format string should be.  Lots
+   of translator comments in here. */
+static gchar *
+generate_format_string (IndicatorDatetime * self)
+{
+	if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
+		return g_strdup(self->priv->custom_string);
+	}
+
+	gboolean twelvehour = TRUE;
+
+	if (self->priv->time_mode == SETTINGS_TIME_LOCALE) {
+		/* TRANSLATORS: This string is used to determine the default
+		   clock style for your locale.  If it is the string '12' then
+		   the default will be a 12-hour clock using AM/PM string.  If
+		   it is '24' then it will be a 24-hour clock.  Users may over
+		   ride this setting so it's still important to translate the
+		   other strings no matter how this is set. */
+		const gchar * locale_default = _("12");
+
+		if (g_strcmp0(locale_default, "24") == 0) {
+			twelvehour = FALSE;
+		}
+	} else if (self->priv->time_mode == SETTINGS_TIME_24_HOUR) {
+		twelvehour = FALSE;
+	}
+
+	const gchar * time_string = NULL;
+	if (twelvehour) {
+		if (self->priv->show_seconds) {
+			/* TRANSLATORS: A format string for the strftime function for
+			   a clock showing 12-hour time with seconds. */
+			time_string = _("%l:%M:%S %p");
+		} else {
+			/* TRANSLATORS: A format string for the strftime function for
+			   a clock showing 12-hour time. */
+			time_string = _(DEFAULT_TIME_12_FORMAT);
+		}
+	} else {
+		if (self->priv->show_seconds) {
+			/* TRANSLATORS: A format string for the strftime function for
+			   a clock showing 24-hour time with seconds. */
+			time_string = _("%H:%M:%S");
+		} else {
+			/* TRANSLATORS: A format string for the strftime function for
+			   a clock showing 24-hour time. */
+			time_string = _(DEFAULT_TIME_24_FORMAT);
+		}
+	}
+	
+	/* Checkpoint, let's not fail */
+	g_return_val_if_fail(time_string != NULL, g_strdup(DEFAULT_TIME_FORMAT));
+
+	/* If there's no date or day let's just leave now and
+	   not worry about the rest of this code */
+	if (!self->priv->show_date && !self->priv->show_day) {
+		return g_strdup(time_string);
+	}
+
+	const gchar * date_string = NULL;
+	if (self->priv->show_date && self->priv->show_day) {
+		/* TRANSLATORS:  This is a format string passed to strftime to represent
+		   the day of the week, the month and the day of the month. */
+		date_string = _("%a %b %e");
+	} else if (self->priv->show_date) {
+		/* TRANSLATORS:  This is a format string passed to strftime to represent
+		   the month and the day of the month. */
+		date_string = _("%b %e");
+	} else if (self->priv->show_day) {
+		/* TRANSLATORS:  This is a format string passed to strftime to represent
+		   the day of the week. */
+		date_string = _("%a");
+	}
+
+	/* Check point, we should have a date string */
+	g_return_val_if_fail(date_string != NULL, g_strdup(time_string));
+
+	/* TRANSLATORS: This is a format string passed to strftime to combine the
+	   date and the time.  The value of "%s, %s" would result in a string like
+	   this in US English 12-hour time: 'Fri Jul 16, 11:50 AM' */
+	return g_strdup_printf(_("%s, %s"), date_string, time_string);
+}
+
 /* Grabs the label.  Creates it if it doesn't
    exist already */
 static GtkLabel *
@@ -316,7 +900,7 @@
 	}
 
 	if (self->priv->timer == 0) {
-		self->priv->timer = g_timeout_add_seconds(60, minute_timer_func, self);
+		setup_timer(self, NULL);
 	}
 
 	return self->priv->label;


Follow ups