No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Damon Chaplin <damon@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 /*
24 * ETimezoneEntry - a field for setting a timezone. It shows the timezone in
25 * a GtkEntry with a '...' button beside it which shows a dialog for changing
26 * the timezone. The dialog contains a map of the world with a point for each
27 * timezone, and an option menu as an alternative way of selecting the
28 * timezone.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <widgets/e-timezone-dialog/e-timezone-dialog.h>
36 #include <glib/gi18n.h>
37 #include "e-timezone-entry.h"
38
39 #define E_TIMEZONE_ENTRY_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_TIMEZONE_ENTRY, ETimezoneEntryPrivate))
42
43 struct _ETimezoneEntryPrivate {
44 /* The current timezone, set in e_timezone_entry_set_timezone()
45 * or from the timezone dialog. Note that we don't copy it or
46 * use a ref count - we assume it is never destroyed for the
47 * lifetime of this widget. */
48 icaltimezone *timezone;
49
50 /* This can be set to the default timezone. If the current timezone
51 * setting in the ETimezoneEntry matches this, then the entry field
52 * is hidden. This makes the user interface simpler. */
53 icaltimezone *default_zone;
54
55 GtkWidget *entry;
56 GtkWidget *button;
57 };
58
59 enum {
60 PROP_0,
61 PROP_TIMEZONE
62 };
63
64 enum {
65 CHANGED,
66 LAST_SIGNAL
67 };
68
69 static guint signals[LAST_SIGNAL];
70
71 G_DEFINE_TYPE (ETimezoneEntry, e_timezone_entry, GTK_TYPE_HBOX)
72
73 static void
74 timezone_entry_emit_changed (ETimezoneEntry *timezone_entry)
75 {
76 g_signal_emit (timezone_entry, signals[CHANGED], 0);
77 }
78
79 static void
80 timezone_entry_update_entry (ETimezoneEntry *timezone_entry)
81 {
82 const gchar *display_name;
83 gchar *name_buffer;
84 icaltimezone *timezone;
85
86 timezone = e_timezone_entry_get_timezone (timezone_entry);
87
88 if (timezone != NULL) {
89 display_name = icaltimezone_get_display_name (timezone);
90
91 /* We check if it is one of our builtin timezone
92 * names, in which case we call gettext to translate
93 * it. If it isn't a builtin timezone name, we don't. */
94 if (icaltimezone_get_builtin_timezone (display_name))
95 display_name = _(display_name);
96 } else
97 display_name = "";
98
99 name_buffer = g_strdup (display_name);
100
101 gtk_entry_set_text (GTK_ENTRY (timezone_entry->priv->entry), name_buffer);
102
103 /* XXX Do we need to hide the timezone entry at all? I know
104 * this overrules the previous case of hiding the timezone
105 * entry field when we select the default timezone. */
106 gtk_widget_show (timezone_entry->priv->entry);
107
108 g_free (name_buffer);
109 }
110 static void
111 timezone_entry_add_relation (ETimezoneEntry *timezone_entry)
112 {
113 AtkObject *a11y_timezone_entry;
114 AtkObject *a11y_widget;
115 AtkRelationSet *set;
116 AtkRelation *relation;
117 GtkWidget *widget;
118 GPtrArray *target;
119 gpointer target_object;
120
121 /* add a labelled_by relation for widget for accessibility */
122
123 widget = GTK_WIDGET (timezone_entry);
124 a11y_timezone_entry = gtk_widget_get_accessible (widget);
125
126 widget = timezone_entry->priv->entry;
127 a11y_widget = gtk_widget_get_accessible (widget);
128
129 set = atk_object_ref_relation_set (a11y_widget);
130 if (set != NULL) {
131 relation = atk_relation_set_get_relation_by_type (
132 set, ATK_RELATION_LABELLED_BY);
133 /* check whether has a labelled_by relation already */
134 if (relation != NULL)
135 return;
136 }
137
138 set = atk_object_ref_relation_set (a11y_timezone_entry);
139 if (!set)
140 return;
141
142 relation = atk_relation_set_get_relation_by_type (
143 set, ATK_RELATION_LABELLED_BY);
144 if (relation != NULL) {
145 target = atk_relation_get_target (relation);
146 target_object = g_ptr_array_index (target, 0);
147 if (ATK_IS_OBJECT (target_object)) {
148 atk_object_add_relationship (
149 a11y_widget,
150 ATK_RELATION_LABELLED_BY,
151 ATK_OBJECT (target_object));
152 }
153 }
154 }
155
156 /* The arrow button beside the date field has been clicked, so we show the
157 * popup with the ECalendar in. */
158 static void
159 timezone_entry_button_clicked_cb (ETimezoneEntry *timezone_entry)
160 {
161 ETimezoneDialog *timezone_dialog;
162 GtkWidget *dialog;
163 icaltimezone *timezone;
164
165 timezone_dialog = e_timezone_dialog_new ();
166
167 timezone = e_timezone_entry_get_timezone (timezone_entry);
168 e_timezone_dialog_set_timezone (timezone_dialog, timezone);
169
170 dialog = e_timezone_dialog_get_toplevel (timezone_dialog);
171
172 if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
173 goto exit;
174
175 timezone = e_timezone_dialog_get_timezone (timezone_dialog);
176 e_timezone_entry_set_timezone (timezone_entry, timezone);
177 timezone_entry_update_entry (timezone_entry);
178
179 exit:
180 g_object_unref (timezone_dialog);
181 }
182
183 static void
184 timezone_entry_set_property (GObject *object,
185 guint property_id,
186 const GValue *value,
187 GParamSpec *pspec)
188 {
189 switch (property_id) {
190 case PROP_TIMEZONE:
191 e_timezone_entry_set_timezone (
192 E_TIMEZONE_ENTRY (object),
193 g_value_get_pointer (value));
194 return;
195 }
196
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
198 }
199
200 static void
201 timezone_entry_get_property (GObject *object,
202 guint property_id,
203 GValue *value,
204 GParamSpec *pspec)
205 {
206 switch (property_id) {
207 case PROP_TIMEZONE:
208 g_value_set_pointer (
209 value, e_timezone_entry_get_timezone (
210 E_TIMEZONE_ENTRY (object)));
211 return;
212 }
213
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
215 }
216
217 static gboolean
218 timezone_entry_mnemonic_activate (GtkWidget *widget,
219 gboolean group_cycling)
220 {
221 ETimezoneEntryPrivate *priv;
222
223 priv = E_TIMEZONE_ENTRY_GET_PRIVATE (widget);
224
225 if (gtk_widget_get_can_focus (widget)) {
226 if (priv->button != NULL)
227 gtk_widget_grab_focus (priv->button);
228 }
229
230 return TRUE;
231 }
232
233 static gboolean
234 timezone_entry_focus (GtkWidget *widget,
235 GtkDirectionType direction)
236 {
237 ETimezoneEntryPrivate *priv;
238
239 priv = E_TIMEZONE_ENTRY_GET_PRIVATE (widget);
240
241 if (direction == GTK_DIR_TAB_FORWARD) {
242 if (gtk_widget_has_focus (priv->entry))
243 gtk_widget_grab_focus (priv->button);
244 else if (gtk_widget_has_focus (priv->button))
245 return FALSE;
246 else if (gtk_widget_get_visible (priv->entry))
247 gtk_widget_grab_focus (priv->entry);
248 else
249 gtk_widget_grab_focus (priv->button);
250
251 } else if (direction == GTK_DIR_TAB_BACKWARD) {
252 if (gtk_widget_has_focus (priv->entry))
253 return FALSE;
254 else if (gtk_widget_has_focus (priv->button)) {
255 if (gtk_widget_get_visible (priv->entry))
256 gtk_widget_grab_focus (priv->entry);
257 else
258 return FALSE;
259 } else
260 gtk_widget_grab_focus (priv->button);
261 } else
262 return FALSE;
263
264 return TRUE;
265 }
266
267 static void
268 e_timezone_entry_class_init (ETimezoneEntryClass *class)
269 {
270 GObjectClass *object_class;
271 GtkWidgetClass *widget_class;
272
273 g_type_class_add_private (class, sizeof (ETimezoneEntryPrivate));
274
275 object_class = G_OBJECT_CLASS (class);
276 object_class->set_property = timezone_entry_set_property;
277 object_class->get_property = timezone_entry_get_property;
278
279 widget_class = GTK_WIDGET_CLASS (class);
280 widget_class->mnemonic_activate = timezone_entry_mnemonic_activate;
281 widget_class->focus = timezone_entry_focus;
282
283 g_object_class_install_property (
284 object_class,
285 PROP_TIMEZONE,
286 g_param_spec_pointer (
287 "timezone",
288 "Timezone",
289 NULL,
290 G_PARAM_READWRITE));
291
292 signals[CHANGED] = g_signal_new (
293 "changed",
294 G_TYPE_FROM_CLASS (object_class),
295 G_SIGNAL_RUN_LAST,
296 G_STRUCT_OFFSET (ETimezoneEntryClass, changed),
297 NULL, NULL,
298 g_cclosure_marshal_VOID__VOID,
299 G_TYPE_NONE, 0);
300 }
301
302 static void
303 e_timezone_entry_init (ETimezoneEntry *timezone_entry)
304 {
305 AtkObject *a11y;
306 GtkWidget *widget;
307
308 timezone_entry->priv = E_TIMEZONE_ENTRY_GET_PRIVATE (timezone_entry);
309
310 gtk_widget_set_can_focus (GTK_WIDGET (timezone_entry), TRUE);
311
312 widget = gtk_entry_new ();
313 gtk_editable_set_editable (GTK_EDITABLE (widget), FALSE);
314 gtk_box_pack_start (GTK_BOX (timezone_entry), widget, TRUE, TRUE, 0);
315 timezone_entry->priv->entry = widget;
316 gtk_widget_show (widget);
317
318 g_signal_connect_swapped (
319 widget, "changed",
320 G_CALLBACK (timezone_entry_emit_changed), timezone_entry);
321
322 widget = gtk_button_new_with_label (_("Select..."));
323 gtk_box_pack_start (GTK_BOX (timezone_entry), widget, FALSE, FALSE, 6);
324 timezone_entry->priv->button = widget;
325 gtk_widget_show (widget);
326
327 g_signal_connect_swapped (
328 widget, "clicked",
329 G_CALLBACK (timezone_entry_button_clicked_cb), timezone_entry);
330
331 a11y = gtk_widget_get_accessible (timezone_entry->priv->button);
332 if (a11y != NULL)
333 atk_object_set_name (a11y, _("Select Timezone"));
334 }
335
336 GtkWidget *
337 e_timezone_entry_new (void)
338 {
339 return g_object_new (E_TYPE_TIMEZONE_ENTRY, NULL);
340 }
341
342 icaltimezone *
343 e_timezone_entry_get_timezone (ETimezoneEntry *timezone_entry)
344 {
345 g_return_val_if_fail (E_IS_TIMEZONE_ENTRY (timezone_entry), NULL);
346
347 return timezone_entry->priv->timezone;
348 }
349
350 void
351 e_timezone_entry_set_timezone (ETimezoneEntry *timezone_entry,
352 icaltimezone *timezone)
353 {
354 g_return_if_fail (E_IS_TIMEZONE_ENTRY (timezone_entry));
355
356 if (timezone_entry->priv->timezone == timezone)
357 return;
358
359 timezone_entry->priv->timezone = timezone;
360
361 timezone_entry_update_entry (timezone_entry);
362 timezone_entry_add_relation (timezone_entry);
363
364 g_object_notify (G_OBJECT (timezone_entry), "timezone");
365 }
366
367 /* Sets the default timezone. If the current timezone matches this,
368 * then the entry field is hidden. This is useful since most people
369 * do not use timezones so it makes the user interface simpler. */
370 void
371 e_timezone_entry_set_default_timezone (ETimezoneEntry *timezone_entry,
372 icaltimezone *timezone)
373 {
374 g_return_if_fail (E_IS_TIMEZONE_ENTRY (timezone_entry));
375
376 timezone_entry->priv->default_zone = timezone;
377
378 timezone_entry_update_entry (timezone_entry);
379 }