No issues found
1 /*
2 * e-charset-combo-box.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "e-charset-combo-box.h"
27
28 #include <glib/gi18n.h>
29
30 #include "e-util/e-charset.h"
31 #include "e-util/e-util.h"
32
33 #define E_CHARSET_COMBO_BOX_GET_PRIVATE(obj) \
34 (G_TYPE_INSTANCE_GET_PRIVATE \
35 ((obj), E_TYPE_CHARSET_COMBO_BOX, ECharsetComboBoxPrivate))
36
37 #define DEFAULT_CHARSET "UTF-8"
38 #define OTHER_VALUE G_MAXINT
39
40 struct _ECharsetComboBoxPrivate {
41 GtkActionGroup *action_group;
42 GtkRadioAction *other_action;
43 GHashTable *charset_index;
44
45 /* Used when the user clicks Cancel in the character set
46 * dialog. Reverts to the previous combo box setting. */
47 gint previous_index;
48
49 /* When setting the character set programmatically, this
50 * prevents the custom character set dialog from running. */
51 guint block_dialog : 1;
52 };
53
54 enum {
55 PROP_0,
56 PROP_CHARSET
57 };
58
59 G_DEFINE_TYPE (
60 ECharsetComboBox,
61 e_charset_combo_box,
62 E_TYPE_ACTION_COMBO_BOX)
63
64 static void
65 charset_combo_box_entry_changed_cb (GtkEntry *entry,
66 GtkDialog *dialog)
67 {
68 const gchar *text;
69 gboolean sensitive;
70
71 text = gtk_entry_get_text (entry);
72 sensitive = (text != NULL && *text != '\0');
73 gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, sensitive);
74 }
75
76 static void
77 charset_combo_box_run_dialog (ECharsetComboBox *combo_box)
78 {
79 GtkDialog *dialog;
80 GtkEntry *entry;
81 GtkWidget *container;
82 GtkWidget *widget;
83 GObject *object;
84 gpointer parent;
85 const gchar *charset;
86
87 /* FIXME Using a dialog for this is lame. Selecting "Other..."
88 * should unlock an entry directly in the Preferences tab.
89 * Unfortunately space in Preferences is at a premium right
90 * now, but we should revisit this when the space issue is
91 * finally resolved. */
92
93 parent = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
94 parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
95
96 object = G_OBJECT (combo_box->priv->other_action);
97 charset = g_object_get_data (object, "charset");
98
99 widget = gtk_dialog_new_with_buttons (
100 _("Character Encoding"), parent,
101 GTK_DIALOG_DESTROY_WITH_PARENT,
102 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
103 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
104
105 /* Load the broken border width defaults so we can override them. */
106 gtk_widget_ensure_style (widget);
107
108 dialog = GTK_DIALOG (widget);
109
110 gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
111
112 gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
113
114 widget = gtk_dialog_get_action_area (dialog);
115 gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
116
117 widget = gtk_dialog_get_content_area (dialog);
118 gtk_box_set_spacing (GTK_BOX (widget), 12);
119 gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
120
121 container = widget;
122
123 widget = gtk_label_new (_("Enter the character set to use"));
124 gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
125 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
126 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
127 gtk_widget_show (widget);
128
129 widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
130 gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
131 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
132 gtk_widget_show (widget);
133
134 container = widget;
135
136 widget = gtk_entry_new ();
137 entry = GTK_ENTRY (widget);
138 gtk_entry_set_activates_default (entry, TRUE);
139 gtk_container_add (GTK_CONTAINER (container), widget);
140 gtk_widget_show (widget);
141
142 g_signal_connect (
143 entry, "changed",
144 G_CALLBACK (charset_combo_box_entry_changed_cb), dialog);
145
146 /* Set the default text -after- connecting the signal handler.
147 * This will initialize the "OK" button to the proper state. */
148 gtk_entry_set_text (entry, charset);
149
150 if (gtk_dialog_run (dialog) != GTK_RESPONSE_OK) {
151 gint active;
152
153 /* Revert to the previously selected character set. */
154 combo_box->priv->block_dialog = TRUE;
155 active = combo_box->priv->previous_index;
156 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), active);
157 combo_box->priv->block_dialog = FALSE;
158
159 goto exit;
160 }
161
162 charset = gtk_entry_get_text (entry);
163 g_return_if_fail (charset != NULL && charset != '\0');
164
165 g_object_set_data_full (
166 object, "charset", g_strdup (charset),
167 (GDestroyNotify) g_free);
168
169 exit:
170 gtk_widget_destroy (GTK_WIDGET (dialog));
171 }
172
173 static void
174 charset_combo_box_notify_charset_cb (ECharsetComboBox *combo_box)
175 {
176 GtkToggleAction *action;
177
178 action = GTK_TOGGLE_ACTION (combo_box->priv->other_action);
179 if (!gtk_toggle_action_get_active (action))
180 return;
181
182 if (combo_box->priv->block_dialog)
183 return;
184
185 /* "Other" action was selected by user. */
186 charset_combo_box_run_dialog (combo_box);
187 }
188
189 static void
190 charset_combo_box_set_property (GObject *object,
191 guint property_id,
192 const GValue *value,
193 GParamSpec *pspec)
194 {
195 switch (property_id) {
196 case PROP_CHARSET:
197 e_charset_combo_box_set_charset (
198 E_CHARSET_COMBO_BOX (object),
199 g_value_get_string (value));
200 return;
201 }
202
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
204 }
205
206 static void
207 charset_combo_box_get_property (GObject *object,
208 guint property_id,
209 GValue *value,
210 GParamSpec *pspec)
211 {
212 switch (property_id) {
213 case PROP_CHARSET:
214 g_value_set_string (
215 value, e_charset_combo_box_get_charset (
216 E_CHARSET_COMBO_BOX (object)));
217 return;
218 }
219
220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
221 }
222
223 static void
224 charset_combo_box_dispose (GObject *object)
225 {
226 ECharsetComboBoxPrivate *priv;
227
228 priv = E_CHARSET_COMBO_BOX_GET_PRIVATE (object);
229
230 if (priv->action_group != NULL) {
231 g_object_unref (priv->action_group);
232 priv->action_group = NULL;
233 }
234
235 if (priv->other_action != NULL) {
236 g_object_unref (priv->other_action);
237 priv->other_action = NULL;
238 }
239
240 g_hash_table_remove_all (priv->charset_index);
241
242 /* Chain up to parent's dispose() method. */
243 G_OBJECT_CLASS (e_charset_combo_box_parent_class)->dispose (object);
244 }
245
246 static void
247 charset_combo_box_finalize (GObject *object)
248 {
249 ECharsetComboBoxPrivate *priv;
250
251 priv = E_CHARSET_COMBO_BOX_GET_PRIVATE (object);
252
253 g_hash_table_destroy (priv->charset_index);
254
255 /* Chain up to parent's finalize() method. */
256 G_OBJECT_CLASS (e_charset_combo_box_parent_class)->finalize (object);
257 }
258
259 static void
260 charset_combo_box_changed (GtkComboBox *combo_box)
261 {
262 ECharsetComboBoxPrivate *priv;
263
264 priv = E_CHARSET_COMBO_BOX_GET_PRIVATE (combo_box);
265
266 /* Chain up to parent's changed() method. */
267 GTK_COMBO_BOX_CLASS (e_charset_combo_box_parent_class)->
268 changed (combo_box);
269
270 /* Notify -before- updating previous index. */
271 g_object_notify (G_OBJECT (combo_box), "charset");
272 priv->previous_index = gtk_combo_box_get_active (combo_box);
273 }
274
275 static void
276 e_charset_combo_box_class_init (ECharsetComboBoxClass *class)
277 {
278 GObjectClass *object_class;
279 GtkComboBoxClass *combo_box_class;
280
281 g_type_class_add_private (class, sizeof (ECharsetComboBoxPrivate));
282
283 object_class = G_OBJECT_CLASS (class);
284 object_class->set_property = charset_combo_box_set_property;
285 object_class->get_property = charset_combo_box_get_property;
286 object_class->dispose = charset_combo_box_dispose;
287 object_class->finalize = charset_combo_box_finalize;
288
289 combo_box_class = GTK_COMBO_BOX_CLASS (class);
290 combo_box_class->changed = charset_combo_box_changed;
291
292 g_object_class_install_property (
293 object_class,
294 PROP_CHARSET,
295 g_param_spec_string (
296 "charset",
297 "Charset",
298 "The selected character set",
299 "UTF-8",
300 G_PARAM_READWRITE |
301 G_PARAM_CONSTRUCT));
302 }
303
304 static void
305 e_charset_combo_box_init (ECharsetComboBox *combo_box)
306 {
307 GtkActionGroup *action_group;
308 GtkRadioAction *radio_action;
309 GHashTable *charset_index;
310 GSList *group, *iter;
311
312 action_group = gtk_action_group_new ("charset-combo-box-internal");
313
314 charset_index = g_hash_table_new_full (
315 g_str_hash, g_str_equal,
316 (GDestroyNotify) g_free,
317 (GDestroyNotify) g_object_unref);
318
319 combo_box->priv = E_CHARSET_COMBO_BOX_GET_PRIVATE (combo_box);
320 combo_box->priv->action_group = action_group;
321 combo_box->priv->charset_index = charset_index;
322
323 group = e_charset_add_radio_actions (
324 action_group, "charset-", NULL, NULL, NULL);
325
326 /* Populate the character set index. */
327 for (iter = group; iter != NULL; iter = iter->next) {
328 GObject *object = iter->data;
329 const gchar *charset;
330
331 charset = g_object_get_data (object, "charset");
332 g_return_if_fail (charset != NULL);
333
334 g_hash_table_insert (
335 charset_index, g_strdup (charset),
336 g_object_ref (object));
337 }
338
339 /* Note the "other" action is not included in the index. */
340
341 radio_action = gtk_radio_action_new (
342 "charset-other", _("Other..."), NULL, NULL, OTHER_VALUE);
343
344 g_object_set_data (G_OBJECT (radio_action), "charset", (gpointer) "");
345
346 gtk_radio_action_set_group (radio_action, group);
347 group = gtk_radio_action_get_group (radio_action);
348
349 e_action_combo_box_set_action (
350 E_ACTION_COMBO_BOX (combo_box), radio_action);
351
352 e_action_combo_box_add_separator_after (
353 E_ACTION_COMBO_BOX (combo_box), g_slist_length (group));
354
355 g_signal_connect (
356 combo_box, "notify::charset",
357 G_CALLBACK (charset_combo_box_notify_charset_cb), NULL);
358
359 combo_box->priv->other_action = radio_action;
360 }
361
362 GtkWidget *
363 e_charset_combo_box_new (void)
364 {
365 return g_object_new (E_TYPE_CHARSET_COMBO_BOX, NULL);
366 }
367
368 const gchar *
369 e_charset_combo_box_get_charset (ECharsetComboBox *combo_box)
370 {
371 GtkRadioAction *radio_action;
372
373 g_return_val_if_fail (E_IS_CHARSET_COMBO_BOX (combo_box), NULL);
374
375 radio_action = combo_box->priv->other_action;
376 radio_action = e_radio_action_get_current_action (radio_action);
377
378 return g_object_get_data (G_OBJECT (radio_action), "charset");
379 }
380
381 void
382 e_charset_combo_box_set_charset (ECharsetComboBox *combo_box,
383 const gchar *charset)
384 {
385 GHashTable *charset_index;
386 GtkRadioAction *radio_action;
387
388 g_return_if_fail (E_IS_CHARSET_COMBO_BOX (combo_box));
389
390 if (charset == NULL || *charset == '\0')
391 charset = "UTF-8";
392
393 charset_index = combo_box->priv->charset_index;
394 radio_action = g_hash_table_lookup (charset_index, charset);
395
396 if (radio_action == NULL) {
397 radio_action = combo_box->priv->other_action;
398 g_object_set_data_full (
399 G_OBJECT (radio_action),
400 "charset", g_strdup (charset),
401 (GDestroyNotify) g_free);
402 }
403
404 combo_box->priv->block_dialog = TRUE;
405 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (radio_action), TRUE);
406 combo_box->priv->block_dialog = FALSE;
407 }