Merge lp:~ted/indicator-datetime/configurable-format into lp:indicator-datetime/0.3
- configurable-format
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cody Russell (community) | Approve | ||
Review via email: mp+30160@code.launchpad.net |
Commit message
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; |