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

Subscribers

People subscribed via source and target branches

to all changes: