1 /*-
2  * Copyright (C) 2012 Nick Schermer <nick@xfce.org>
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 
22 #ifdef HAVE_MEMORY_H
23 #include <memory.h>
24 #endif
25 #ifdef HAVE_STRING_H
26 #include <string.h>
27 #endif
28 
29 #include <libxfce4util/libxfce4util.h>
30 
31 #include <terminal/terminal-encoding-action.h>
32 #include <terminal/terminal-private.h>
33 
34 
35 
36 /* Signal identifiers */
37 enum
38 {
39   ENCODING_CHANGED,
40   LAST_SIGNAL,
41 };
42 
43 
44 
45 static void       terminal_encoding_action_finalize         (GObject                *object);
46 static GtkWidget *terminal_encoding_action_create_menu_item (GtkAction              *action);
47 static void       terminal_encoding_action_menu_shown       (GtkWidget              *menu,
48                                                              TerminalEncodingAction *action);
49 
50 
51 
52 struct _TerminalEncodingActionClass
53 {
54   GtkActionClass parent_class;
55 };
56 
57 struct _TerminalEncodingAction
58 {
59   GtkAction  parent_instance;
60   gchar     *current;
61 };
62 
63 
64 
65 /* group names for the charsets below, order matters! */
66 static const gchar *terminal_encodings_names[] =
67 {
68   N_("Western European"),
69   N_("Central European"),
70   N_("Baltic"),
71   N_("South-Eastern Europe"),
72   N_("Turkish"),
73   N_("Cyrillic"),
74   N_("Chinese Traditional"),
75   N_("Chinese Simplified"),
76   N_("Korean"),
77   N_("Japanese"),
78   N_("Greek"),
79   N_("Arabic"),
80   N_("Hebrew"),
81   N_("Thai"),
82   N_("Vietnamese"),
83   N_("Nordic"),
84   N_("Celtic"),
85   N_("Romanian"),
86   N_("Armenian"),
87   N_("Georgian"),
88   N_("Unicode"),
89   N_("Other"),
90 };
91 
92 /* charsets for the groups above, order matters! */
93 static const gchar *terminal_encodings_charsets[][8] =
94 {
95   /* Western European */
96   { "ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "IBM850", NULL },
97   /* Central European */
98   { "ISO-8859-2", "ISO-8859-3", "WINDOWS-1250", "IBM852", NULL },
99   /* Baltic */
100   { "ISO-8859-4", "ISO-8859-13", "WINDOWS-1257", NULL },
101   /* South-Eastern Europe */
102   { "ISO-8859-16", NULL },
103   /* Turkish */
104   { "ISO-8859-9", "WINDOWS-1254", "IBM857", NULL },
105   /* Cyrillic */
106   { "CP866", "KOI8-R", "ISO-8859-5", "WINDOWS-1251", "KOI8-U", "IBM855", "ISO-IR-111", NULL },
107   /* Chinese Traditional */
108   { "BIG5", "BIG5-HKSCS", "EUC-TW", NULL },
109   /* Chinese Simplified */
110   { "GB18030", "GBK", "GB2312", NULL },
111   /* Korean */
112   { "EUC-KR", "ISO-2022-KR", "UHC", NULL },
113   /* Japanese */
114   { "SHIFT_JIS", "JIS7", "EUC-JP", "ISO-2022-JP", NULL },
115   /* Greek */
116   { "ISO-8859-7", "WINDOWS-1253", NULL },
117   /* Arabic */
118   { "ISO-8859-6", "IBM864", "WINDOWS-1256", NULL },
119   /* Hebrew */
120   { "ISO-8859-8", "ISO-8859-8-I", "IBM862", "WINDOWS-1255", NULL },
121   /* Thai */
122   { "TIS-620", "ISO-8859-11", NULL },
123   /* Vietnamese */
124   { "TCVN", "VISCII", "WINDOWS-1258", NULL },
125   /* Nordic */
126   { "ISO-8859-10", NULL },
127   /* Celtic */
128   { "ISO-8859-14", NULL },
129   /* Romanian */
130   { "ISO-8859-16", NULL },
131   /* Armenian */
132   { "ARMSCII-8", NULL },
133   /* Georgian */
134   { "GEORGIAN-PS", NULL },
135   /* Unicode */
136   { "UTF-8", NULL },
137   /* Other */
138   { "IBM874", "TSCII", NULL },
139 };
140 
141 
142 
143 static guint  encoding_action_signals[LAST_SIGNAL];
144 static GQuark encoding_action_quark = 0;
145 
146 
147 
148 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_DEFINE_TYPE(TerminalEncodingAction,terminal_encoding_action,GTK_TYPE_ACTION)149 G_DEFINE_TYPE (TerminalEncodingAction, terminal_encoding_action, GTK_TYPE_ACTION)
150 G_GNUC_END_IGNORE_DEPRECATIONS
151 
152 
153 
154 static void
155 terminal_encoding_action_class_init (TerminalEncodingActionClass *klass)
156 {
157   GtkActionClass *gtkaction_class;
158   GObjectClass   *gobject_class;
159 
160   gobject_class = G_OBJECT_CLASS (klass);
161   gobject_class->finalize = terminal_encoding_action_finalize;
162 
163 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
164   gtkaction_class = GTK_ACTION_CLASS (klass);
165 G_GNUC_END_IGNORE_DEPRECATIONS
166   gtkaction_class->create_menu_item = terminal_encoding_action_create_menu_item;
167 
168   encoding_action_quark = g_quark_from_static_string ("encoding-action-quark");
169 
170   encoding_action_signals[ENCODING_CHANGED] =
171     g_signal_new (I_("encoding-changed"),
172                   G_TYPE_FROM_CLASS (klass),
173                   G_SIGNAL_RUN_LAST,
174                   0, NULL, NULL,
175                   g_cclosure_marshal_VOID__STRING,
176                   G_TYPE_NONE, 1, G_TYPE_STRING);
177 }
178 
179 
180 
181 static void
terminal_encoding_action_init(TerminalEncodingAction * action)182 terminal_encoding_action_init (TerminalEncodingAction *action)
183 {
184 
185 }
186 
187 
188 
189 static void
terminal_encoding_action_finalize(GObject * object)190 terminal_encoding_action_finalize (GObject *object)
191 {
192   TerminalEncodingAction *action = TERMINAL_ENCODING_ACTION (object);
193 
194   g_free (action->current);
195 
196   (*G_OBJECT_CLASS (terminal_encoding_action_parent_class)->finalize) (object);
197 }
198 
199 
200 
201 static GtkWidget *
terminal_encoding_action_create_menu_item(GtkAction * action)202 terminal_encoding_action_create_menu_item (GtkAction *action)
203 {
204   GtkWidget *item;
205   GtkWidget *menu;
206 
207   terminal_return_val_if_fail (TERMINAL_IS_ENCODING_ACTION (action), NULL);
208 
209   /* let GtkAction allocate the menu item */
210 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
211   item = (*GTK_ACTION_CLASS (terminal_encoding_action_parent_class)->create_menu_item) (action);
212 G_GNUC_END_IGNORE_DEPRECATIONS
213 
214   /* associate an empty submenu with the item (will be filled when shown) */
215   menu = gtk_menu_new ();
216   g_signal_connect (G_OBJECT (menu), "show", G_CALLBACK (terminal_encoding_action_menu_shown), action);
217   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
218 
219   return item;
220 }
221 
222 
223 
224 static void
terminal_encoding_action_activated(GtkWidget * item,TerminalEncodingAction * encoding_action)225 terminal_encoding_action_activated (GtkWidget              *item,
226                                     TerminalEncodingAction *encoding_action)
227 {
228   const gchar *charset;
229 
230   terminal_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item));
231 
232   if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)))
233     return;
234 
235   /* menu charset or null to reset */
236   charset = g_object_get_qdata (G_OBJECT (item), encoding_action_quark);
237   g_signal_emit (G_OBJECT (encoding_action),
238                  encoding_action_signals[ENCODING_CHANGED], 0, charset);
239 }
240 
241 
242 
243 static void
terminal_encoding_action_menu_shown(GtkWidget * menu,TerminalEncodingAction * action)244 terminal_encoding_action_menu_shown (GtkWidget              *menu,
245                                      TerminalEncodingAction *action)
246 {
247   GList         *children;
248   guint          n, k;
249   GtkWidget     *item;
250   GtkWidget     *item2;
251   GtkWidget     *submenu;
252   const gchar   *charset;
253   GSList        *groups = NULL;
254   gboolean       found;
255   GtkWidget     *label;
256   GtkWidget     *bold_item = NULL;
257   PangoAttrList *attrs;
258   const gchar   *default_charset;
259   gchar         *default_label;
260 
261   terminal_return_if_fail (TERMINAL_IS_ENCODING_ACTION (action));
262   terminal_return_if_fail (GTK_IS_MENU_SHELL (menu));
263 
264   /* drop all existing children of the menu first */
265   children = gtk_container_get_children (GTK_CONTAINER (menu));
266   g_list_free_full (children, (GDestroyNotify) gtk_widget_destroy);
267 
268   g_get_charset (&default_charset);
269   found = (action->current == NULL
270            || g_strcmp0 (default_charset, action->current) == 0);
271 
272   /* action to reset to the default */
273   default_label = g_strdup_printf (_("Default (%s)"), default_charset);
274   item = gtk_radio_menu_item_new_with_label (groups, default_label);
275   groups = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
276   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
277   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), found);
278   g_signal_connect (G_OBJECT (item), "activate",
279       G_CALLBACK (terminal_encoding_action_activated), action);
280   g_free (default_label);
281 
282   /*add the groups */
283   for (n = 0; n < G_N_ELEMENTS (terminal_encodings_names); n++)
284     {
285       /* category item */
286       item = gtk_menu_item_new_with_label (_(terminal_encodings_names[n]));
287       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
288 
289       submenu = gtk_menu_new ();
290       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
291 
292       /* submenu with charset */
293       for (k = 0; k < G_N_ELEMENTS (*terminal_encodings_charsets); k++)
294         {
295           charset = terminal_encodings_charsets[n][k];
296           if (charset == NULL)
297             break;
298 
299           item2 = gtk_radio_menu_item_new_with_label (groups, charset);
300           groups = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item2));
301           gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item2);
302           g_object_set_qdata (G_OBJECT (item2), encoding_action_quark, (gchar *) charset);
303 
304           if (!found
305               && strcmp (action->current, charset) == 0)
306             {
307               gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item2), TRUE);
308 
309               found = TRUE;
310               bold_item = item;
311             }
312 
313           g_signal_connect (G_OBJECT (item2), "activate",
314               G_CALLBACK (terminal_encoding_action_activated), action);
315         }
316     }
317 
318   if (!found)
319     {
320       /* add an action with the unknown charset */
321       item2 = gtk_radio_menu_item_new_with_label (groups, action->current);
322       groups = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item2));
323       g_object_set_qdata_full (G_OBJECT (item2), encoding_action_quark,
324                                g_strdup (action->current), g_free);
325       gtk_menu_shell_append (GTK_MENU_SHELL (submenu), item2);
326       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item2), TRUE);
327       g_signal_connect (G_OBJECT (item2), "activate",
328         G_CALLBACK (terminal_encoding_action_activated), action);
329 
330       /* other group */
331       bold_item = item;
332     }
333 
334   if (bold_item != NULL)
335     {
336       attrs = pango_attr_list_new ();
337       pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
338       label = gtk_bin_get_child (GTK_BIN (bold_item));
339       gtk_label_set_attributes (GTK_LABEL (label), attrs);
340       pango_attr_list_unref (attrs);
341     }
342 
343   gtk_widget_show_all (menu);
344 }
345 
346 
347 
348 GtkAction*
terminal_encoding_action_new(const gchar * name,const gchar * label)349 terminal_encoding_action_new (const gchar *name,
350                               const gchar *label)
351 {
352   terminal_return_val_if_fail (name != NULL, NULL);
353   terminal_return_val_if_fail (label != NULL, NULL);
354 
355   return g_object_new (TERMINAL_TYPE_ENCODING_ACTION,
356                        "hide-if-empty", FALSE,
357                        "label", label,
358                        "name", name,
359                        NULL);
360 }
361 
362 
363 
364 void
terminal_encoding_action_set_charset(GtkAction * gtkaction,const gchar * charset)365 terminal_encoding_action_set_charset (GtkAction   *gtkaction,
366                                       const gchar *charset)
367 {
368   TerminalEncodingAction *action = TERMINAL_ENCODING_ACTION (gtkaction);
369 
370   terminal_return_if_fail (TERMINAL_IS_ENCODING_ACTION (action));
371 
372   g_free (action->current);
373   action->current = g_strdup (charset);
374 }
375 
376 
377 
378 
379 
380 
381 
382 GtkTreeModel *
terminal_encoding_model_new(const gchar * current,GtkTreeIter * current_iter)383 terminal_encoding_model_new (const gchar *current,
384                              GtkTreeIter *current_iter)
385 {
386   GtkTreeStore *store;
387   guint         n;
388   guint         k;
389   GtkTreeIter   parent;
390   const gchar  *charset;
391   gboolean      found;
392   GtkTreeIter   iter;
393   gchar        *default_label;
394 
395   store = gtk_tree_store_new (N_ENCODING_COLUMNS,
396                               G_TYPE_STRING,
397                               G_TYPE_BOOLEAN,
398                               G_TYPE_STRING);
399 
400   /* default */
401   g_get_charset (&charset);
402   default_label = g_strdup_printf (_("Default (%s)"), charset);
403   gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
404                                      ENCODING_COLUMN_TITLE, default_label,
405                                      ENCODING_COLUMN_VALUE, NULL,
406                                      ENCODING_COLUMN_IS_CHARSET, TRUE,
407                                      -1);
408   g_free (default_label);
409   found = (current == NULL || g_strcmp0 (current, charset) == 0);
410   if (found)
411     *current_iter = iter;
412 
413   /*add the groups */
414   for (n = 0; n < G_N_ELEMENTS (terminal_encodings_names); n++)
415     {
416       /* category item */
417       gtk_tree_store_insert_with_values (store, &parent, NULL, n + 1,
418                                          ENCODING_COLUMN_TITLE, _(terminal_encodings_names[n]),
419                                          -1);
420 
421       /* submenu with charset */
422       for (k = 0; k < G_N_ELEMENTS (*terminal_encodings_charsets); k++)
423         {
424           charset = terminal_encodings_charsets[n][k];
425           if (charset == NULL)
426             break;
427 
428           gtk_tree_store_insert_with_values (store, &iter, &parent, k,
429                                              ENCODING_COLUMN_TITLE, charset,
430                                              ENCODING_COLUMN_VALUE, charset,
431                                              -1);
432 
433           if (!found && g_strcmp0 (charset, current) == 0)
434             {
435               *current_iter = iter;
436               found = TRUE;
437             }
438         }
439     }
440 
441   if (!found)
442     {
443       /* add custom in other menu */
444       gtk_tree_store_insert_with_values (store, current_iter, &parent, k,
445                                          ENCODING_COLUMN_TITLE, current,
446                                          ENCODING_COLUMN_IS_CHARSET, TRUE,
447                                          ENCODING_COLUMN_VALUE, current,
448                                          -1);
449     }
450 
451   return GTK_TREE_MODEL (store);
452 }
453 
454