2 * Copyright © 2002 Ricardo Fernández Pascual
3 * Copyright © 2003 Marco Pesenti Gritti
4 * Copyright © 2003 Christian Persch
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * $Id: ephy-encoding-menu.c 6952 2007-03-11 19:42:02Z chpe $
25 #include "ephy-encoding-menu.h"
26 #include "ephy-encoding-dialog.h"
27 #include "ephy-encodings.h"
28 #include "ephy-embed.h"
29 #include "ephy-embed-shell.h"
30 #include "ephy-shell.h"
31 #include "ephy-debug.h"
33 #include <gtk/gtkaction.h>
34 #include <gtk/gtktoggleaction.h>
35 #include <gtk/gtkradioaction.h>
36 #include <gtk/gtkuimanager.h>
37 #include <glib/gi18n.h>
40 #define EPHY_ENCODING_MENU_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_ENCODING_MENU, EphyEncodingMenuPrivate))
42 struct _EphyEncodingMenuPrivate
44 EphyEncodings
*encodings
;
46 GtkUIManager
*manager
;
47 GtkActionGroup
*action_group
;
50 GSList
*encodings_radio_group
;
51 EphyEncodingDialog
*dialog
;
54 #define ENCODING_PLACEHOLDER_PATH "/menubar/ViewMenu/ViewEncodingMenu/ViewEncodingPlaceholder"
56 static void ephy_encoding_menu_class_init (EphyEncodingMenuClass
*klass
);
57 static void ephy_encoding_menu_init (EphyEncodingMenu
*menu
);
65 static GObjectClass
*parent_class
= NULL
;
68 ephy_encoding_menu_get_type (void)
70 static GType type
= 0;
72 if (G_UNLIKELY (type
== 0))
74 const GTypeInfo our_info
=
76 sizeof (EphyEncodingMenuClass
),
79 (GClassInitFunc
) ephy_encoding_menu_class_init
,
82 sizeof (EphyEncodingMenu
),
84 (GInstanceInitFunc
) ephy_encoding_menu_init
87 type
= g_type_register_static (G_TYPE_OBJECT
,
96 ephy_encoding_menu_init (EphyEncodingMenu
*menu
)
98 menu
->priv
= EPHY_ENCODING_MENU_GET_PRIVATE (menu
);
100 menu
->priv
->encodings
=
101 EPHY_ENCODINGS (ephy_embed_shell_get_encodings
102 (EPHY_EMBED_SHELL (ephy_shell
)));
106 sort_encodings (gconstpointer a
, gconstpointer b
)
108 EphyNode
*node1
= (EphyNode
*) a
;
109 EphyNode
*node2
= (EphyNode
*) b
;
110 const char *key1
, *key2
;
112 key1
= ephy_node_get_property_string
113 (node1
, EPHY_NODE_ENCODING_PROP_COLLATION_KEY
);
114 key2
= ephy_node_get_property_string
115 (node2
, EPHY_NODE_ENCODING_PROP_COLLATION_KEY
);
117 return strcmp (key1
, key2
);
121 add_menu_item (EphyNode
*node
, EphyEncodingMenu
*menu
)
124 char action
[128], name
[128];
126 code
= ephy_node_get_property_string
127 (node
, EPHY_NODE_ENCODING_PROP_ENCODING
);
129 g_snprintf (action
, sizeof (action
), "Encoding%s", code
);
130 g_snprintf (name
, sizeof (name
), "%sItem", action
);
132 gtk_ui_manager_add_ui (menu
->priv
->manager
, menu
->priv
->merge_id
,
133 ENCODING_PLACEHOLDER_PATH
,
135 GTK_UI_MANAGER_MENUITEM
, FALSE
);
139 update_encoding_menu_cb (GtkAction
*dummy
, EphyEncodingMenu
*menu
)
141 EphyEncodingMenuPrivate
*p
= menu
->priv
;
147 GList
*recent
, *related
= NULL
, *l
;
148 EphyLanguageGroup groups
;
149 gboolean is_automatic
;
151 START_PROFILER ("Rebuilding encoding menu")
153 /* FIXME: block the "activate" signal on the actions instead; needs to
154 * wait until g_signal_handlers_block_matched supports blocking
155 * by signal id alone.
157 menu
->priv
->update_tag
= TRUE
;
159 /* get most recently used encodings */
160 recent
= ephy_encodings_get_recent (p
->encodings
);
162 embed
= ephy_window_get_active_embed (p
->window
);
163 encoding
= ephy_embed_get_encoding (embed
);
164 if (encoding
== NULL
) goto build_menu
;
166 enc_node
= ephy_encodings_get_node (p
->encodings
, encoding
, TRUE
);
167 g_assert (EPHY_IS_NODE (enc_node
));
169 /* check if encoding was overridden */
170 is_automatic
= ephy_embed_has_automatic_encoding (embed
);
172 action
= gtk_action_group_get_action (p
->action_group
,
173 "ViewEncodingAutomatic");
174 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), is_automatic
);
175 gtk_action_set_sensitive (action
, !is_automatic
);
177 /* set the encodings group's active member */
178 g_snprintf (name
, sizeof (name
), "Encoding%s", encoding
);
179 action
= gtk_action_group_get_action (p
->action_group
, name
);
180 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), TRUE
);
182 /* get encodings related to the current encoding */
183 groups
= ephy_node_get_property_int
184 (enc_node
, EPHY_NODE_ENCODING_PROP_LANGUAGE_GROUPS
);
186 related
= ephy_encodings_get_encodings (p
->encodings
, groups
);
187 related
= g_list_sort (related
, (GCompareFunc
) sort_encodings
);
189 /* add the current encoding to the list of
190 * things to display, making sure we don't add it more than once
192 if (g_list_find (related
, enc_node
) == NULL
193 && g_list_find (recent
, enc_node
) == NULL
)
195 related
= g_list_prepend (related
, enc_node
);
198 /* make sure related and recent are disjoint so we don't display twice */
199 for (l
= related
; l
!= NULL
; l
= l
->next
)
201 recent
= g_list_remove (recent
, l
->data
);
204 recent
= g_list_sort (recent
, (GCompareFunc
) sort_encodings
);
210 gtk_ui_manager_remove_ui (p
->manager
, p
->merge_id
);
211 gtk_ui_manager_ensure_update (p
->manager
);
214 /* build the new menu */
215 p
->merge_id
= gtk_ui_manager_new_merge_id (p
->manager
);
217 gtk_ui_manager_add_ui (p
->manager
, p
->merge_id
,
218 ENCODING_PLACEHOLDER_PATH
,
219 "ViewEncodingAutomaticItem",
220 "ViewEncodingAutomatic",
221 GTK_UI_MANAGER_MENUITEM
, FALSE
);
223 gtk_ui_manager_add_ui (p
->manager
, p
->merge_id
,
224 ENCODING_PLACEHOLDER_PATH
,
226 GTK_UI_MANAGER_SEPARATOR
, FALSE
);
228 g_list_foreach (recent
, (GFunc
) add_menu_item
, menu
);
230 gtk_ui_manager_add_ui (p
->manager
, p
->merge_id
,
231 ENCODING_PLACEHOLDER_PATH
,
233 GTK_UI_MANAGER_SEPARATOR
, FALSE
);
235 g_list_foreach (related
, (GFunc
) add_menu_item
, menu
);
237 gtk_ui_manager_add_ui (p
->manager
, p
->merge_id
,
238 ENCODING_PLACEHOLDER_PATH
,
240 GTK_UI_MANAGER_SEPARATOR
, FALSE
);
242 gtk_ui_manager_add_ui (p
->manager
, p
->merge_id
,
243 ENCODING_PLACEHOLDER_PATH
,
244 "ViewEncodingOtherItem",
246 GTK_UI_MANAGER_MENUITEM
, FALSE
);
249 g_list_free (related
);
250 g_list_free (recent
);
254 menu
->priv
->update_tag
= FALSE
;
256 STOP_PROFILER ("Rebuilding encoding menu")
260 encoding_activate_cb (GtkAction
*action
, EphyEncodingMenu
*menu
)
263 const char *name
, *encoding
;
265 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)) == FALSE
266 || menu
->priv
->update_tag
)
271 name
= gtk_action_get_name (GTK_ACTION (action
));
272 encoding
= name
+ strlen("Encoding");
274 embed
= ephy_window_get_active_embed (menu
->priv
->window
);
276 ephy_embed_set_encoding (embed
, encoding
);
278 ephy_encodings_add_recent (menu
->priv
->encodings
, encoding
);
282 add_action (EphyNode
*encodings
, EphyNode
*node
, EphyEncodingMenu
*menu
)
286 const char *encoding
, *title
;
288 encoding
= ephy_node_get_property_string
289 (node
, EPHY_NODE_ENCODING_PROP_ENCODING
);
290 title
= ephy_node_get_property_string
291 (node
, EPHY_NODE_ENCODING_PROP_TITLE
);
293 LOG ("add_action for encoding '%s'", encoding
);
295 g_snprintf (name
, sizeof (name
), "Encoding%s", encoding
);
297 action
= g_object_new (GTK_TYPE_RADIO_ACTION
,
302 gtk_radio_action_set_group (GTK_RADIO_ACTION (action
),
303 menu
->priv
->encodings_radio_group
);
304 menu
->priv
->encodings_radio_group
= gtk_radio_action_get_group
305 (GTK_RADIO_ACTION (action
));
307 g_signal_connect (action
, "activate",
308 G_CALLBACK (encoding_activate_cb
),
311 gtk_action_group_add_action_with_accel
312 (menu
->priv
->action_group
, action
, NULL
);
313 g_object_unref (action
);
317 ephy_encoding_menu_view_dialog_cb (GtkAction
*action
, EphyEncodingMenu
*menu
)
319 if (menu
->priv
->dialog
== NULL
)
321 EphyEncodingDialog
**dialog
= &menu
->priv
->dialog
;
322 menu
->priv
->dialog
= ephy_encoding_dialog_new
323 (menu
->priv
->window
);
325 g_object_add_weak_pointer(G_OBJECT (menu
->priv
->dialog
),
329 ephy_dialog_show (EPHY_DIALOG (menu
->priv
->dialog
));
333 ephy_encoding_menu_automatic_cb (GtkAction
*action
, EphyEncodingMenu
*menu
)
337 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action
)) == FALSE
338 || menu
->priv
->update_tag
)
343 embed
= ephy_window_get_active_embed (menu
->priv
->window
);
345 /* setting "" will clear the forced encoding */
346 ephy_embed_set_encoding (embed
, "");
349 static const GtkActionEntry menu_entries
[] =
351 { "ViewEncodingOther", NULL
, N_("_Other…"), NULL
,
352 N_("Other encodings"),
353 G_CALLBACK (ephy_encoding_menu_view_dialog_cb
) }
356 static const GtkToggleActionEntry toggle_menu_entries
[] =
358 { "ViewEncodingAutomatic", NULL
, N_("_Automatic"), NULL
,
359 N_("Use the encoding specified by the document"),
360 G_CALLBACK (ephy_encoding_menu_automatic_cb
), FALSE
}
364 ephy_encoding_menu_set_window (EphyEncodingMenu
*menu
, EphyWindow
*window
)
366 GtkActionGroup
*action_group
;
372 g_return_if_fail (EPHY_IS_WINDOW (window
));
374 menu
->priv
->window
= window
;
375 menu
->priv
->manager
= GTK_UI_MANAGER (ephy_window_get_ui_manager (window
));
377 action_group
= gtk_action_group_new ("EncodingActions");
378 gtk_action_group_set_translation_domain (action_group
, NULL
);
379 menu
->priv
->action_group
= action_group
;
381 gtk_action_group_add_actions (action_group
, menu_entries
,
382 G_N_ELEMENTS (menu_entries
), menu
);
383 gtk_action_group_add_toggle_actions (action_group
, toggle_menu_entries
,
384 G_N_ELEMENTS (toggle_menu_entries
), menu
);
386 /* add actions for the existing encodings */
387 encodings
= ephy_encodings_get_all (menu
->priv
->encodings
);
388 children
= ephy_node_get_children (encodings
);
389 for (i
= 0; i
< children
->len
; i
++)
393 encoding
= (EphyNode
*) g_ptr_array_index (children
, i
);
394 add_action (encodings
, encoding
, menu
);
397 /* when we encounter an unknown encoding, it is added to the database,
398 * so we need to listen to child_added on the encodings node to
399 * add an action for it
401 ephy_node_signal_connect_object (encodings
,
402 EPHY_NODE_CHILD_ADDED
,
403 (EphyNodeCallback
) add_action
,
406 gtk_ui_manager_insert_action_group (menu
->priv
->manager
,
408 g_object_unref (action_group
);
410 action
= gtk_ui_manager_get_action (menu
->priv
->manager
,
411 "/menubar/ViewMenu");
412 g_signal_connect_object (action
, "activate",
413 G_CALLBACK (update_encoding_menu_cb
),
418 ephy_encoding_menu_set_property (GObject
*object
,
423 EphyEncodingMenu
*menu
= EPHY_ENCODING_MENU (object
);
428 ephy_encoding_menu_set_window (menu
, g_value_get_object (value
));
434 ephy_encoding_menu_get_property (GObject
*object
,
439 EphyEncodingMenu
*menu
= EPHY_ENCODING_MENU (object
);
444 g_value_set_object (value
, menu
->priv
->window
);
450 ephy_encoding_menu_finalize (GObject
*object
)
452 EphyEncodingMenu
*menu
= EPHY_ENCODING_MENU (object
);
454 if (menu
->priv
->dialog
)
456 g_object_unref (menu
->priv
->dialog
);
459 G_OBJECT_CLASS (parent_class
)->finalize (object
);
463 ephy_encoding_menu_class_init (EphyEncodingMenuClass
*klass
)
465 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
467 parent_class
= g_type_class_peek_parent (klass
);
469 object_class
->finalize
= ephy_encoding_menu_finalize
;
470 object_class
->set_property
= ephy_encoding_menu_set_property
;
471 object_class
->get_property
= ephy_encoding_menu_get_property
;
473 g_object_class_install_property (object_class
,
475 g_param_spec_object ("window",
479 G_PARAM_READWRITE
| G_PARAM_STATIC_NAME
| G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
|
480 G_PARAM_CONSTRUCT_ONLY
));
482 g_type_class_add_private (object_class
, sizeof(EphyEncodingMenuPrivate
));
486 ephy_encoding_menu_new (EphyWindow
*window
)
488 return g_object_new (EPHY_TYPE_ENCODING_MENU
,