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

Proposed by Ted Gould
Status: Merged
Merged at revision: 16
Proposed branch: lp:~ted/indicator-datetime/configurable-format
Merge into: lp:indicator-datetime/0.3
Diff against target: 906 lines (+690/-32)
5 files modified
.bzrignore (+1/-0)
configure.ac (+9/-1)
data/Makefile.am (+4/-0)
data/org.ayatana.indicator.datetime.gschema.xml (+61/-0)
src/indicator-datetime.c (+615/-31)
To merge this branch: bzr merge lp:~ted/indicator-datetime/configurable-format
Reviewer Review Type Date Requested Status
Cody Russell (community) Approve
Review via email: mp+30160@code.launchpad.net

Description of the change

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!

To post a comment you must log in.
Revision history for this message
Cody Russell (bratsche) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2010-07-07 22:01:53 +0000
3+++ .bzrignore 2010-07-16 22:03:40 +0000
4@@ -6,3 +6,4 @@
5 po/indicator-datetime.pot
6 indicator-datetime-[0-9].[0-9].[0-9].tar.gz
7 data/indicator-datetime.service
8+data/org.ayatana.indicator.datetime.gschema.valid
9
10=== modified file 'configure.ac'
11--- configure.ac 2010-07-15 15:30:50 +0000
12+++ configure.ac 2010-07-16 22:03:40 +0000
13@@ -35,15 +35,23 @@
14 INDICATOR_REQUIRED_VERSION=0.3.0
15 DBUSMENUGLIB_REQUIRED_VERSION=0.1.1
16 DBUSMENUGTK_REQUIRED_VERSION=0.1.1
17+GIO_REQUIRED_VERSION=2.25.0
18
19 PKG_CHECK_MODULES(INDICATOR, indicator >= $INDICATOR_REQUIRED_VERSION
20 dbusmenu-glib >= $DBUSMENUGLIB_REQUIRED_VERSION
21- dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION)
22+ dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION
23+ gio-2.0 >= $GIO_REQUIRED_VERSION)
24
25 AC_SUBST(INDICATOR_CFLAGS)
26 AC_SUBST(INDICATOR_LIBS)
27
28 ###########################
29+# Grab the GSettings Macros
30+###########################
31+
32+GLIB_GSETTINGS
33+
34+###########################
35 # Check to see if we're local
36 ###########################
37
38
39=== modified file 'data/Makefile.am'
40--- data/Makefile.am 2010-05-19 02:47:35 +0000
41+++ data/Makefile.am 2010-07-16 22:03:40 +0000
42@@ -1,5 +1,9 @@
43 #SUBDIRS = icons
44
45+gsettings_SCHEMAS = \
46+ org.ayatana.indicator.datetime.gschema.xml
47+@GSETTINGS_RULES@
48+
49 dbus_servicesdir = $(DBUSSERVICEDIR)
50 dbus_services_DATA = indicator-datetime.service
51
52
53=== added file 'data/org.ayatana.indicator.datetime.gschema.xml'
54--- data/org.ayatana.indicator.datetime.gschema.xml 1970-01-01 00:00:00 +0000
55+++ data/org.ayatana.indicator.datetime.gschema.xml 2010-07-16 22:03:40 +0000
56@@ -0,0 +1,61 @@
57+<schemalist>
58+ <enum id="time-enum">
59+ <value nick="locale-default" value="0" />
60+ <value nick="12-hour" value="1" />
61+ <value nick="24-hour" value="2" />
62+ <value nick="custom" value="3" />
63+ </enum>
64+ <schema id="org.ayatana.indicator.datetime" path="/apps/indicators/datetime/" gettext-domain="indicator-datetime">
65+ <key name="time-format" enum="time-enum">
66+ <default>'locale-default'</default>
67+ <summary>What the time format should be</summary>
68+ <description>
69+ Controls the time format that is displayed in the indicator. For almost
70+ all users this should be the default for their locale. If you think the
71+ setting is wrong for your locale please join or talk to the translation
72+ team for your langauge. If you just want something different you can
73+ adjust this to be either 12 or 24 time. Or, you can use a custom format
74+ string and set the custom-time-format setting.
75+ </description>
76+ </key>
77+ <key name="show-seconds" type="b">
78+ <default>false</default>
79+ <summary>Show the number of seconds in the indicator</summary>
80+ <description>
81+ Makes the datetime indicator show the number of seconds in the indicator.
82+ It's important to note that this will cause additional battery drain as
83+ the time will update 60 times as often, so it is not recommended. Also,
84+ this setting will be ignored if the time-format value is set to custom.
85+ </description>
86+ </key>
87+ <key name="show-day" type="b">
88+ <default>false</default>
89+ <summary>Show the day of the week in the indicator</summary>
90+ <description>
91+ Puts the day of the week on the panel along with the time and/or date
92+ depending on settings. This setting will be ignored if the time-format
93+ value is set to custom.
94+ </description>
95+ </key>
96+ <key name="show-date" type="b">
97+ <default>false</default>
98+ <summary>Show the month and date in the indicator</summary>
99+ <description>
100+ Puts the month and the date in the panel along with the time and/or day
101+ of the week depending on settings. This setting will be ignored if the
102+ time-format value is set to custom.
103+ </description>
104+ </key>
105+ <key name="custom-time-format" type="s">
106+ <default>"%l:%M %p"</default>
107+ <summary>The format string passed to strftime</summary>
108+ <description>
109+ The format of the time and/or date that is visible on the panel when using
110+ the indicator. For most users this will be a set of predefined values as
111+ determined by the configuration utility, but advanced users can change it
112+ to anything strftime can accept. Look at the man page on strftime for
113+ more information.
114+ </description>
115+ </key>
116+ </schema>
117+</schemalist>
118
119=== modified file 'src/indicator-datetime.c'
120--- src/indicator-datetime.c 2010-07-12 20:51:03 +0000
121+++ src/indicator-datetime.c 2010-07-16 22:03:40 +0000
122@@ -27,6 +27,7 @@
123 #include <glib.h>
124 #include <glib-object.h>
125 #include <glib/gi18n-lib.h>
126+#include <gio/gio.h>
127
128 /* Indicator Stuff */
129 #include <libindicator/indicator.h>
130@@ -63,12 +64,57 @@
131 GtkLabel * label;
132 guint timer;
133
134+ gchar * time_string;
135+
136+ gint time_mode;
137+ gboolean show_seconds;
138+ gboolean show_date;
139+ gboolean show_day;
140+ gchar * custom_string;
141+
142 guint idle_measure;
143 gint max_width;
144
145 IndicatorServiceManager * sm;
146 DbusmenuGtkMenu * menu;
147-};
148+
149+ GSettings * settings;
150+};
151+
152+/* Enum for the properties so that they can be quickly
153+ found and looked up. */
154+enum {
155+ PROP_0,
156+ PROP_TIME_FORMAT,
157+ PROP_SHOW_SECONDS,
158+ PROP_SHOW_DAY,
159+ PROP_SHOW_DATE,
160+ PROP_CUSTOM_TIME_FORMAT
161+};
162+
163+#define PROP_TIME_FORMAT_S "time-format"
164+#define PROP_SHOW_SECONDS_S "show-seconds"
165+#define PROP_SHOW_DAY_S "show-day"
166+#define PROP_SHOW_DATE_S "show-date"
167+#define PROP_CUSTOM_TIME_FORMAT_S "custom-time-format"
168+
169+#define SETTINGS_INTERFACE "org.ayatana.indicator.datetime"
170+#define SETTINGS_TIME_FORMAT_S "time-format"
171+#define SETTINGS_SHOW_SECONDS_S "show-seconds"
172+#define SETTINGS_SHOW_DAY_S "show-day"
173+#define SETTINGS_SHOW_DATE_S "show-date"
174+#define SETTINGS_CUSTOM_TIME_FORMAT_S "custom-time-format"
175+
176+enum {
177+ SETTINGS_TIME_LOCALE = 0,
178+ SETTINGS_TIME_12_HOUR = 1,
179+ SETTINGS_TIME_24_HOUR = 2,
180+ SETTINGS_TIME_CUSTOM = 3
181+};
182+
183+#define DEFAULT_TIME_12_FORMAT "%l:%M %p"
184+#define DEFAULT_TIME_24_FORMAT "%H:%M"
185+#define DEFAULT_TIME_FORMAT DEFAULT_TIME_12_FORMAT
186
187 #define INDICATOR_DATETIME_GET_PRIVATE(o) \
188 (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_DATETIME_TYPE, IndicatorDatetimePrivate))
189@@ -77,10 +123,18 @@
190
191 static void indicator_datetime_class_init (IndicatorDatetimeClass *klass);
192 static void indicator_datetime_init (IndicatorDatetime *self);
193+static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
194+static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
195 static void indicator_datetime_dispose (GObject *object);
196 static void indicator_datetime_finalize (GObject *object);
197 static GtkLabel * get_label (IndicatorObject * io);
198 static GtkMenu * get_menu (IndicatorObject * io);
199+static GVariant * bind_enum_set (const GValue * value, const GVariantType * type, gpointer user_data);
200+static gboolean bind_enum_get (GValue * value, GVariant * variant, gpointer user_data);
201+static gchar * generate_format_string (IndicatorDatetime * self);
202+static struct tm * update_label (IndicatorDatetime * io);
203+static void guess_label_size (IndicatorDatetime * self);
204+static void setup_timer (IndicatorDatetime * self, struct tm * ltime);
205
206 /* Indicator Module Config */
207 INDICATOR_SET_VERSION
208@@ -98,11 +152,52 @@
209 object_class->dispose = indicator_datetime_dispose;
210 object_class->finalize = indicator_datetime_finalize;
211
212+ object_class->set_property = set_property;
213+ object_class->get_property = get_property;
214+
215 IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
216
217 io_class->get_label = get_label;
218 io_class->get_menu = get_menu;
219
220+ g_object_class_install_property (object_class,
221+ PROP_TIME_FORMAT,
222+ g_param_spec_int(PROP_TIME_FORMAT_S,
223+ "A choice of which format should be used on the panel",
224+ "Chooses between letting the locale choose the time, 12-hour time, 24-time or using the custom string passed to strftime().",
225+ SETTINGS_TIME_LOCALE, /* min */
226+ SETTINGS_TIME_CUSTOM, /* max */
227+ SETTINGS_TIME_LOCALE, /* default */
228+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
229+ g_object_class_install_property (object_class,
230+ PROP_SHOW_SECONDS,
231+ g_param_spec_boolean(PROP_SHOW_SECONDS_S,
232+ "Whether to show seconds in the indicator.",
233+ "Shows seconds along with the time in the indicator. Also effects refresh interval.",
234+ FALSE, /* default */
235+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236+ g_object_class_install_property (object_class,
237+ PROP_SHOW_DAY,
238+ g_param_spec_boolean(PROP_SHOW_DAY_S,
239+ "Whether to show the day of the week in the indicator.",
240+ "Shows the day of the week along with the time in the indicator.",
241+ FALSE, /* default */
242+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
243+ g_object_class_install_property (object_class,
244+ PROP_SHOW_DATE,
245+ g_param_spec_boolean(PROP_SHOW_DATE_S,
246+ "Whether to show the day and month in the indicator.",
247+ "Shows the day and month along with the time in the indicator.",
248+ FALSE, /* default */
249+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
250+ g_object_class_install_property (object_class,
251+ PROP_CUSTOM_TIME_FORMAT,
252+ g_param_spec_string(PROP_CUSTOM_TIME_FORMAT_S,
253+ "The format that is used to show the time on the panel.",
254+ "A format string in the form used to pass to strftime to make a string for displaying on the panel.",
255+ DEFAULT_TIME_FORMAT,
256+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
257+
258 return;
259 }
260
261@@ -117,9 +212,51 @@
262 self->priv->idle_measure = 0;
263 self->priv->max_width = 0;
264
265+ self->priv->time_string = g_strdup(DEFAULT_TIME_FORMAT);
266+
267+ self->priv->time_mode = SETTINGS_TIME_LOCALE;
268+ self->priv->show_seconds = FALSE;
269+ self->priv->show_date = FALSE;
270+ self->priv->show_day = FALSE;
271+ self->priv->custom_string = g_strdup(DEFAULT_TIME_FORMAT);
272+
273 self->priv->sm = NULL;
274 self->priv->menu = NULL;
275
276+ self->priv->settings = g_settings_new(SETTINGS_INTERFACE);
277+ if (self->priv->settings != NULL) {
278+ g_settings_bind_with_mapping(self->priv->settings,
279+ SETTINGS_TIME_FORMAT_S,
280+ self,
281+ PROP_TIME_FORMAT_S,
282+ G_SETTINGS_BIND_DEFAULT,
283+ bind_enum_get,
284+ bind_enum_set,
285+ NULL, NULL); /* Userdata and destroy func */
286+ g_settings_bind(self->priv->settings,
287+ SETTINGS_SHOW_SECONDS_S,
288+ self,
289+ PROP_SHOW_SECONDS_S,
290+ G_SETTINGS_BIND_DEFAULT);
291+ g_settings_bind(self->priv->settings,
292+ SETTINGS_SHOW_DAY_S,
293+ self,
294+ PROP_SHOW_DAY_S,
295+ G_SETTINGS_BIND_DEFAULT);
296+ g_settings_bind(self->priv->settings,
297+ SETTINGS_SHOW_DATE_S,
298+ self,
299+ PROP_SHOW_DATE_S,
300+ G_SETTINGS_BIND_DEFAULT);
301+ g_settings_bind(self->priv->settings,
302+ SETTINGS_CUSTOM_TIME_FORMAT_S,
303+ self,
304+ PROP_CUSTOM_TIME_FORMAT_S,
305+ G_SETTINGS_BIND_DEFAULT);
306+ } else {
307+ g_warning("Unable to get settings for '" SETTINGS_INTERFACE "'");
308+ }
309+
310 self->priv->sm = indicator_service_manager_new_version(SERVICE_NAME, SERVICE_VERSION);
311
312 return;
313@@ -155,6 +292,11 @@
314 self->priv->sm = NULL;
315 }
316
317+ if (self->priv->settings != NULL) {
318+ g_object_unref(G_OBJECT(self->priv->settings));
319+ self->priv->settings = NULL;
320+ }
321+
322 G_OBJECT_CLASS (indicator_datetime_parent_class)->dispose (object);
323 return;
324 }
325@@ -162,11 +304,181 @@
326 static void
327 indicator_datetime_finalize (GObject *object)
328 {
329+ IndicatorDatetime * self = INDICATOR_DATETIME(object);
330+
331+ if (self->priv->time_string != NULL) {
332+ g_free(self->priv->time_string);
333+ self->priv->time_string = NULL;
334+ }
335+
336+ if (self->priv->custom_string != NULL) {
337+ g_free(self->priv->custom_string);
338+ self->priv->custom_string = NULL;
339+ }
340
341 G_OBJECT_CLASS (indicator_datetime_parent_class)->finalize (object);
342 return;
343 }
344
345+/* Turns the int value into a string GVariant */
346+static GVariant *
347+bind_enum_set (const GValue * value, const GVariantType * type, gpointer user_data)
348+{
349+ switch (g_value_get_int(value)) {
350+ case SETTINGS_TIME_LOCALE:
351+ return g_variant_new_string("locale-default");
352+ case SETTINGS_TIME_12_HOUR:
353+ return g_variant_new_string("12-hour");
354+ case SETTINGS_TIME_24_HOUR:
355+ return g_variant_new_string("24-hour");
356+ case SETTINGS_TIME_CUSTOM:
357+ return g_variant_new_string("custom");
358+ default:
359+ return NULL;
360+ }
361+}
362+
363+/* Turns a string GVariant into an int value */
364+static gboolean
365+bind_enum_get (GValue * value, GVariant * variant, gpointer user_data)
366+{
367+ const gchar * str = g_variant_get_string(variant, NULL);
368+ gint output = 0;
369+
370+ if (g_strcmp0(str, "locale-default") == 0) {
371+ output = SETTINGS_TIME_LOCALE;
372+ } else if (g_strcmp0(str, "12-hour") == 0) {
373+ output = SETTINGS_TIME_12_HOUR;
374+ } else if (g_strcmp0(str, "24-hour") == 0) {
375+ output = SETTINGS_TIME_24_HOUR;
376+ } else if (g_strcmp0(str, "custom") == 0) {
377+ output = SETTINGS_TIME_CUSTOM;
378+ } else {
379+ return FALSE;
380+ }
381+
382+ g_value_set_int(value, output);
383+ return TRUE;
384+}
385+
386+/* Sets a property on the object */
387+static void
388+set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
389+{
390+ IndicatorDatetime * self = INDICATOR_DATETIME(object);
391+ gboolean update = FALSE;
392+
393+ switch(prop_id) {
394+ case PROP_TIME_FORMAT: {
395+ gint newval = g_value_get_int(value);
396+ if (newval != self->priv->time_mode) {
397+ update = TRUE;
398+ self->priv->time_mode = newval;
399+ }
400+ break;
401+ }
402+ case PROP_SHOW_SECONDS:
403+ if (g_value_get_boolean(value) != self->priv->show_seconds) {
404+ self->priv->show_seconds = !self->priv->show_seconds;
405+ if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
406+ update = TRUE;
407+ setup_timer(self, NULL);
408+ }
409+ }
410+ break;
411+ case PROP_SHOW_DAY:
412+ if (g_value_get_boolean(value) != self->priv->show_day) {
413+ self->priv->show_day = !self->priv->show_day;
414+ if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
415+ update = TRUE;
416+ }
417+ }
418+ break;
419+ case PROP_SHOW_DATE:
420+ if (g_value_get_boolean(value) != self->priv->show_date) {
421+ self->priv->show_date = !self->priv->show_date;
422+ if (self->priv->time_mode != SETTINGS_TIME_CUSTOM) {
423+ update = TRUE;
424+ }
425+ }
426+ break;
427+ case PROP_CUSTOM_TIME_FORMAT: {
428+ const gchar * newstr = g_value_get_string(value);
429+ if (g_strcmp0(newstr, self->priv->custom_string) != 0) {
430+ if (self->priv->custom_string != NULL) {
431+ g_free(self->priv->custom_string);
432+ self->priv->custom_string = NULL;
433+ }
434+ self->priv->custom_string = g_strdup(newstr);
435+ if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
436+ update = TRUE;
437+ setup_timer(self, NULL);
438+ }
439+ }
440+ break;
441+ }
442+ default:
443+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
444+ return;
445+ }
446+
447+ if (!update) {
448+ return;
449+ }
450+
451+ /* Get the new format string */
452+ gchar * newformat = generate_format_string(self);
453+
454+ /* check to ensure the format really changed */
455+ if (g_strcmp0(self->priv->time_string, newformat) == 0) {
456+ g_free(newformat);
457+ return;
458+ }
459+
460+ /* Okay now process the change */
461+ if (self->priv->time_string != NULL) {
462+ g_free(self->priv->time_string);
463+ self->priv->time_string = NULL;
464+ }
465+ self->priv->time_string = newformat;
466+
467+ /* And update everything */
468+ update_label(self);
469+ guess_label_size(self);
470+
471+ return;
472+}
473+
474+/* Gets a property from the object */
475+static void
476+get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
477+{
478+ IndicatorDatetime * self = INDICATOR_DATETIME(object);
479+
480+ switch(prop_id) {
481+ case PROP_TIME_FORMAT:
482+ g_value_set_int(value, self->priv->time_mode);
483+ break;
484+ case PROP_SHOW_SECONDS:
485+ g_value_set_boolean(value, self->priv->show_seconds);
486+ break;
487+ case PROP_SHOW_DAY:
488+ g_value_set_boolean(value, self->priv->show_day);
489+ break;
490+ case PROP_SHOW_DATE:
491+ g_value_set_boolean(value, self->priv->show_date);
492+ break;
493+ case PROP_CUSTOM_TIME_FORMAT:
494+ g_value_set_string(value, self->priv->custom_string);
495+ break;
496+ default:
497+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
498+ return;
499+ }
500+
501+ return;
502+}
503+
504 /* Looks at the size of the label, if it grew beyond what we
505 thought was the max, make sure it doesn't shrink again. */
506 static gboolean
507@@ -190,12 +502,12 @@
508 }
509
510 /* Updates the label to be the current time. */
511-static void
512+static struct tm *
513 update_label (IndicatorDatetime * io)
514 {
515 IndicatorDatetime * self = INDICATOR_DATETIME(io);
516
517- if (self->priv->label == NULL) return;
518+ if (self->priv->label == NULL) return NULL;
519
520 gchar longstr[128];
521 time_t t;
522@@ -206,10 +518,10 @@
523 if (ltime == NULL) {
524 g_debug("Error getting local time");
525 gtk_label_set_label(self->priv->label, _("Error getting time"));
526- return;
527+ return NULL;
528 }
529
530- strftime(longstr, 128, "%l:%M %p", ltime);
531+ strftime(longstr, 128, self->priv->time_string, ltime);
532
533 gchar * utf8 = g_locale_to_utf8(longstr, -1, NULL, NULL, NULL);
534 gtk_label_set_label(self->priv->label, utf8);
535@@ -219,24 +531,43 @@
536 self->priv->idle_measure = g_idle_add(idle_measure, io);
537 }
538
539- return;
540+ return ltime;
541 }
542
543 /* Runs every minute and updates the time */
544 gboolean
545-minute_timer_func (gpointer user_data)
546+timer_func (gpointer user_data)
547 {
548 IndicatorDatetime * self = INDICATOR_DATETIME(user_data);
549+ self->priv->timer = 0;
550+ struct tm * ltime = update_label(self);
551+ setup_timer(self, ltime);
552+ return FALSE;
553+}
554
555- if (self->priv->label != NULL) {
556- update_label(self);
557- return TRUE;
558+/* Configure the timer to run the next time through */
559+static void
560+setup_timer (IndicatorDatetime * self, struct tm * ltime)
561+{
562+ if (self->priv->timer != 0) {
563+ g_source_remove(self->priv->timer);
564+ self->priv->timer = 0;
565+ }
566+
567+ if (self->priv->show_seconds) {
568+ self->priv->timer = g_timeout_add_seconds(1, timer_func, self);
569 } else {
570- self->priv->timer = 0;
571- return FALSE;
572+ if (ltime == NULL) {
573+ time_t t;
574+ t = time(NULL);
575+ ltime = localtime(&t);
576+ }
577+
578+ /* Plus 2 so we're just after the minute, don't want to be early. */
579+ self->priv->timer = g_timeout_add_seconds(60 - ltime->tm_sec + 2, timer_func, self);
580 }
581
582- return FALSE;
583+ return;
584 }
585
586 /* Does a quick meausre of how big the string is in
587@@ -254,33 +585,203 @@
588 return width;
589 }
590
591+/* Format for the table of strftime() modifiers to what
592+ we need to check when determining the length */
593+typedef struct _strftime_type_t strftime_type_t;
594+struct _strftime_type_t {
595+ char character;
596+ gint mask;
597+};
598+
599+enum {
600+ STRFTIME_MASK_NONE = 0, /* Hours or minutes as we always test those */
601+ STRFTIME_MASK_SECONDS = 1 << 0, /* Seconds count */
602+ STRFTIME_MASK_AMPM = 1 << 1, /* AM/PM counts */
603+ STRFTIME_MASK_WEEK = 1 << 2, /* Day of the week maters (Sat, Sun, etc.) */
604+ STRFTIME_MASK_DAY = 1 << 3, /* Day of the month counts (Feb 1st) */
605+ STRFTIME_MASK_MONTH = 1 << 4, /* Which month matters */
606+ STRFTIME_MASK_YEAR = 1 << 5, /* Which year matters */
607+ /* Last entry, combines all previous */
608+ STRFTIME_MASK_ALL = (STRFTIME_MASK_SECONDS | STRFTIME_MASK_AMPM | STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR)
609+};
610+
611+/* A table taken from the man page of strftime to what the different
612+ characters can effect. These are worst case in that we need to
613+ test the length based on all these things to ensure that we have
614+ a reasonable string lenght measurement. */
615+const static strftime_type_t strftime_type[] = {
616+ {'a', STRFTIME_MASK_WEEK},
617+ {'A', STRFTIME_MASK_WEEK},
618+ {'b', STRFTIME_MASK_MONTH},
619+ {'B', STRFTIME_MASK_MONTH},
620+ {'c', STRFTIME_MASK_ALL}, /* We don't know, so we have to assume all */
621+ {'C', STRFTIME_MASK_YEAR},
622+ {'d', STRFTIME_MASK_MONTH},
623+ {'D', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY},
624+ {'e', STRFTIME_MASK_DAY},
625+ {'F', STRFTIME_MASK_MONTH | STRFTIME_MASK_YEAR | STRFTIME_MASK_DAY},
626+ {'G', STRFTIME_MASK_YEAR},
627+ {'g', STRFTIME_MASK_YEAR},
628+ {'h', STRFTIME_MASK_MONTH},
629+ {'j', STRFTIME_MASK_DAY},
630+ {'m', STRFTIME_MASK_MONTH},
631+ {'p', STRFTIME_MASK_AMPM},
632+ {'P', STRFTIME_MASK_AMPM},
633+ {'r', STRFTIME_MASK_AMPM},
634+ {'s', STRFTIME_MASK_SECONDS},
635+ {'S', STRFTIME_MASK_SECONDS},
636+ {'T', STRFTIME_MASK_SECONDS},
637+ {'u', STRFTIME_MASK_WEEK},
638+ {'U', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
639+ {'V', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
640+ {'w', STRFTIME_MASK_DAY},
641+ {'W', STRFTIME_MASK_DAY | STRFTIME_MASK_MONTH},
642+ {'x', STRFTIME_MASK_YEAR | STRFTIME_MASK_MONTH | STRFTIME_MASK_DAY | STRFTIME_MASK_WEEK},
643+ {'X', STRFTIME_MASK_SECONDS},
644+ {'y', STRFTIME_MASK_YEAR},
645+ {'Y', STRFTIME_MASK_YEAR},
646+ /* Last one */
647+ {0, 0}
648+};
649+
650 #define FAT_NUMBER 8
651
652+/* Looks through the characters in the format string to
653+ ensure that we can figure out which of the things we
654+ need to check in determining the length. */
655+static gint
656+generate_strftime_bitmask (IndicatorDatetime * self)
657+{
658+ gint retval = 0;
659+ glong strlength = g_utf8_strlen(self->priv->time_string, -1);
660+ gint i;
661+ g_debug("Evaluating bitmask for '%s'", self->priv->time_string);
662+
663+ for (i = 0; i < strlength; i++) {
664+ if (self->priv->time_string[i] == '%' && i + 1 < strlength) {
665+ gchar evalchar = self->priv->time_string[i + 1];
666+
667+ /* If we're using alternate formats we need to skip those characters */
668+ if (evalchar == 'E' || evalchar == 'O') {
669+ if (i + 2 < strlength) {
670+ evalchar = self->priv->time_string[i + 2];
671+ } else {
672+ continue;
673+ }
674+ }
675+
676+ /* Let's look at that character in the table */
677+ int j;
678+ for (j = 0; strftime_type[j].character != 0; j++) {
679+ if (strftime_type[j].character == evalchar) {
680+ retval |= strftime_type[j].mask;
681+ break;
682+ }
683+ }
684+ }
685+ }
686+
687+ return retval;
688+}
689+
690+/* Build an array up of all the time values that we want to check
691+ for length to ensure we're in a good place */
692+static void
693+build_timeval_array (GArray * timevals, gint mask)
694+{
695+ struct tm mytm = {0};
696+
697+ /* Sun 12/28/8888 00:00 */
698+ mytm.tm_hour = 0;
699+ mytm.tm_mday = 28;
700+ mytm.tm_mon = 11;
701+ mytm.tm_year = 8888 - 1900;
702+ mytm.tm_wday = 0;
703+ mytm.tm_yday = 363;
704+ g_array_append_val(timevals, mytm);
705+
706+ if (mask & STRFTIME_MASK_AMPM) {
707+ /* Sun 12/28/8888 12:00 */
708+ mytm.tm_hour = 12;
709+ g_array_append_val(timevals, mytm);
710+ }
711+
712+ /* NOTE: Ignoring year 8888 should handle it */
713+
714+ if (mask & STRFTIME_MASK_MONTH) {
715+ gint oldlen = timevals->len;
716+ gint i, j;
717+ for (i = 0; i < oldlen; i++) {
718+ for (j = 0; j < 11; j++) {
719+ struct tm localval = g_array_index(timevals, struct tm, i);
720+ localval.tm_mon = j;
721+ /* Not sure if I need to adjust yday & wday, hope not */
722+ g_array_append_val(timevals, localval);
723+ }
724+ }
725+ }
726+
727+ /* Doing these together as it seems like just slightly more
728+ coverage on the numerical days, but worth it. */
729+ if (mask & (STRFTIME_MASK_WEEK | STRFTIME_MASK_DAY)) {
730+ gint oldlen = timevals->len;
731+ gint i, j;
732+ for (i = 0; i < oldlen; i++) {
733+ for (j = 22; j < 28; j++) {
734+ struct tm localval = g_array_index(timevals, struct tm, i);
735+
736+ gint diff = 28 - j;
737+
738+ localval.tm_mday = j;
739+ localval.tm_wday = localval.tm_wday - diff;
740+ if (localval.tm_wday < 0) {
741+ localval.tm_wday += 7;
742+ }
743+ localval.tm_yday = localval.tm_yday - diff;
744+
745+ g_array_append_val(timevals, localval);
746+ }
747+ }
748+ }
749+
750+ return;
751+}
752+
753 /* Try to get a good guess at what a maximum width of the entire
754 string would be. */
755 static void
756 guess_label_size (IndicatorDatetime * self)
757 {
758+ /* This is during startup. */
759+ if (self->priv->label == NULL) return;
760+
761 GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(self->priv->label));
762 PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(self->priv->label));
763-
764- /* TRANSLATORS: This string is used for measuring the size of
765- the font used for showing the time and is not shown to the
766- user anywhere. */
767- gchar * am_str = g_strdup_printf(_("%d%d:%d%d AM"), FAT_NUMBER, FAT_NUMBER, FAT_NUMBER, FAT_NUMBER);
768- gint am_width = measure_string(style, context, am_str);
769- g_free(am_str);
770-
771- /* TRANSLATORS: This string is used for measuring the size of
772- the font used for showing the time and is not shown to the
773- user anywhere. */
774- gchar * pm_str = g_strdup_printf(_("%d%d:%d%d PM"), FAT_NUMBER, FAT_NUMBER, FAT_NUMBER, FAT_NUMBER);
775- gint pm_width = measure_string(style, context, pm_str);
776- g_free(pm_str);
777-
778- self->priv->max_width = MAX(am_width, pm_width);
779+ gint * max_width = &(self->priv->max_width);
780+ gint posibilitymask = generate_strftime_bitmask(self);
781+
782+ /* Build the array of possibilities that we want to test */
783+ GArray * timevals = g_array_new(FALSE, TRUE, sizeof(struct tm));
784+ build_timeval_array(timevals, posibilitymask);
785+
786+ g_debug("Checking against %d posible times", timevals->len);
787+ gint check_time;
788+ for (check_time = 0; check_time < timevals->len; check_time++) {
789+ gchar longstr[128];
790+ strftime(longstr, 128, self->priv->time_string, &(g_array_index(timevals, struct tm, check_time)));
791+
792+ gchar * utf8 = g_locale_to_utf8(longstr, -1, NULL, NULL, NULL);
793+ gint length = measure_string(style, context, utf8);
794+ g_free(utf8);
795+
796+ if (length > *max_width) {
797+ *max_width = length;
798+ }
799+ }
800+
801+ g_array_free(timevals, TRUE);
802+
803 gtk_widget_set_size_request(GTK_WIDGET(self->priv->label), self->priv->max_width, -1);
804-
805 g_debug("Guessing max time width: %d", self->priv->max_width);
806
807 return;
808@@ -298,6 +799,89 @@
809 return;
810 }
811
812+/* Tries to figure out what our format string should be. Lots
813+ of translator comments in here. */
814+static gchar *
815+generate_format_string (IndicatorDatetime * self)
816+{
817+ if (self->priv->time_mode == SETTINGS_TIME_CUSTOM) {
818+ return g_strdup(self->priv->custom_string);
819+ }
820+
821+ gboolean twelvehour = TRUE;
822+
823+ if (self->priv->time_mode == SETTINGS_TIME_LOCALE) {
824+ /* TRANSLATORS: This string is used to determine the default
825+ clock style for your locale. If it is the string '12' then
826+ the default will be a 12-hour clock using AM/PM string. If
827+ it is '24' then it will be a 24-hour clock. Users may over
828+ ride this setting so it's still important to translate the
829+ other strings no matter how this is set. */
830+ const gchar * locale_default = _("12");
831+
832+ if (g_strcmp0(locale_default, "24") == 0) {
833+ twelvehour = FALSE;
834+ }
835+ } else if (self->priv->time_mode == SETTINGS_TIME_24_HOUR) {
836+ twelvehour = FALSE;
837+ }
838+
839+ const gchar * time_string = NULL;
840+ if (twelvehour) {
841+ if (self->priv->show_seconds) {
842+ /* TRANSLATORS: A format string for the strftime function for
843+ a clock showing 12-hour time with seconds. */
844+ time_string = _("%l:%M:%S %p");
845+ } else {
846+ /* TRANSLATORS: A format string for the strftime function for
847+ a clock showing 12-hour time. */
848+ time_string = _(DEFAULT_TIME_12_FORMAT);
849+ }
850+ } else {
851+ if (self->priv->show_seconds) {
852+ /* TRANSLATORS: A format string for the strftime function for
853+ a clock showing 24-hour time with seconds. */
854+ time_string = _("%H:%M:%S");
855+ } else {
856+ /* TRANSLATORS: A format string for the strftime function for
857+ a clock showing 24-hour time. */
858+ time_string = _(DEFAULT_TIME_24_FORMAT);
859+ }
860+ }
861+
862+ /* Checkpoint, let's not fail */
863+ g_return_val_if_fail(time_string != NULL, g_strdup(DEFAULT_TIME_FORMAT));
864+
865+ /* If there's no date or day let's just leave now and
866+ not worry about the rest of this code */
867+ if (!self->priv->show_date && !self->priv->show_day) {
868+ return g_strdup(time_string);
869+ }
870+
871+ const gchar * date_string = NULL;
872+ if (self->priv->show_date && self->priv->show_day) {
873+ /* TRANSLATORS: This is a format string passed to strftime to represent
874+ the day of the week, the month and the day of the month. */
875+ date_string = _("%a %b %e");
876+ } else if (self->priv->show_date) {
877+ /* TRANSLATORS: This is a format string passed to strftime to represent
878+ the month and the day of the month. */
879+ date_string = _("%b %e");
880+ } else if (self->priv->show_day) {
881+ /* TRANSLATORS: This is a format string passed to strftime to represent
882+ the day of the week. */
883+ date_string = _("%a");
884+ }
885+
886+ /* Check point, we should have a date string */
887+ g_return_val_if_fail(date_string != NULL, g_strdup(time_string));
888+
889+ /* TRANSLATORS: This is a format string passed to strftime to combine the
890+ date and the time. The value of "%s, %s" would result in a string like
891+ this in US English 12-hour time: 'Fri Jul 16, 11:50 AM' */
892+ return g_strdup_printf(_("%s, %s"), date_string, time_string);
893+}
894+
895 /* Grabs the label. Creates it if it doesn't
896 exist already */
897 static GtkLabel *
898@@ -316,7 +900,7 @@
899 }
900
901 if (self->priv->timer == 0) {
902- self->priv->timer = g_timeout_add_seconds(60, minute_timer_func, self);
903+ setup_timer(self, NULL);
904 }
905
906 return self->priv->label;

Subscribers

People subscribed via source and target branches

to all changes: