evolution-3.6.4/widgets/misc/e-charset-combo-box.c

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 }