Move privileges.c and unicode-utils.c from epan to wsutil (so things like
[obnox/wireshark/wip.git] / gtk / font_utils.c
1 /* font_utils.c
2  * Utilities to use for font manipulation
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31
32 #include <gtk/gtk.h>
33
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36
37 #ifdef _WIN32
38 #include <windows.h>
39 #include <tchar.h>
40 #include <wsutil/unicode-utils.h>
41 #endif
42
43 #include "../simple_dialog.h"
44
45 #include "gtk/main.h"
46 #include "gtk/recent.h"
47 #include "gtk/gtkglobals.h"
48 #include "gtk/font_utils.h"
49 #include "gtk/main_packet_list.h"
50 #include "gtk/main_proto_draw.h"
51 #include "gtk/follow_tcp.h"
52
53
54
55 PangoFontDescription *m_r_font, *m_b_font;
56
57
58 /* Get the regular user font.
59  *
60  * @return the regular user font
61  */
62 PangoFontDescription *user_font_get_regular(void)
63 {
64     return m_r_font;
65 }
66
67 /* Get the bold user font.
68  *
69  * @return the bold user font
70  */
71 PangoFontDescription *user_font_get_bold(void)
72 {
73     return m_b_font;
74 }
75
76 static void
77 set_fonts(PangoFontDescription *regular, PangoFontDescription *bold)
78 {
79         /* Yes, assert. The code that loads the font should check
80          * for NULL and provide its own error message. */
81         g_assert(m_r_font && m_b_font);
82         m_r_font = regular;
83         m_b_font = bold;
84 }
85
86 void
87 view_zoom_in_cb(GtkWidget *w _U_, gpointer d _U_)
88 {
89     gint save_gui_zoom_level;
90
91     save_gui_zoom_level = recent.gui_zoom_level;
92     recent.gui_zoom_level++;
93     switch (user_font_apply(FALSE)) {
94
95     case FA_SUCCESS:
96         break;
97
98     case FA_FONT_NOT_RESIZEABLE:
99         /* "font_apply()" popped up an alert box. */
100         recent.gui_zoom_level = save_gui_zoom_level;    /* undo zoom */
101         break;
102
103     case FA_FONT_NOT_AVAILABLE:
104         /* We assume this means that the specified size isn't available. */
105         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
106             "Your current font isn't available in the next larger size.\n");
107         recent.gui_zoom_level = save_gui_zoom_level;    /* undo zoom */
108         break;
109     }
110 }
111
112 void
113 view_zoom_out_cb(GtkWidget *w _U_, gpointer d _U_)
114 {
115     gint save_gui_zoom_level;
116
117     save_gui_zoom_level = recent.gui_zoom_level;
118     recent.gui_zoom_level--;
119     switch (user_font_apply(FALSE)) {
120
121     case FA_SUCCESS:
122         break;
123
124     case FA_FONT_NOT_RESIZEABLE:
125         /* "font_apply()" popped up an alert box. */
126         recent.gui_zoom_level = save_gui_zoom_level;    /* undo zoom */
127         break;
128
129     case FA_FONT_NOT_AVAILABLE:
130         /* We assume this means that the specified size isn't available. */
131         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
132             "Your current font isn't available in the next smaller size.\n");
133         recent.gui_zoom_level = save_gui_zoom_level;    /* undo zoom */
134         break;
135     }
136 }
137
138 void
139 view_zoom_100_cb(GtkWidget *w _U_, gpointer d _U_)
140 {
141     gint save_gui_zoom_level;
142
143     save_gui_zoom_level = recent.gui_zoom_level;
144     recent.gui_zoom_level = 0;
145     switch (user_font_apply(FALSE)) {
146
147     case FA_SUCCESS:
148         break;
149
150     case FA_FONT_NOT_RESIZEABLE:
151         /* "font_apply()" popped up an alert box. */
152         recent.gui_zoom_level = save_gui_zoom_level;    /* undo zoom */
153         break;
154
155     case FA_FONT_NOT_AVAILABLE:
156         /* We assume this means that the specified size isn't available.
157            XXX - this "shouldn't happen". */
158         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
159             "Your current font couldn't be reloaded at the size you selected.\n");
160         recent.gui_zoom_level = save_gui_zoom_level;    /* undo zoom */
161         break;
162     }
163 }
164
165
166
167 gboolean
168 user_font_test(gchar *font_name)
169 {
170         PangoFontDescription *new_r_font, *new_b_font;
171
172         new_r_font = pango_font_description_from_string(font_name);
173         if (new_r_font == NULL) {
174                 /* Oops, that font didn't work.
175                    Tell the user, but don't tear down the font selection
176                    dialog, so that they can try again. */
177                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
178                    "The font you selected can't be loaded.");
179
180                 return FALSE;
181         }
182
183         new_b_font = pango_font_description_copy(new_r_font);
184         pango_font_description_set_weight(new_b_font, PANGO_WEIGHT_BOLD);
185         if (new_b_font == NULL) {
186                 /* Oops, that font didn't work.
187                    Tell the user, but don't tear down the font selection
188                    dialog, so that they can try again. */
189                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
190                    "The font you selected doesn't have a boldface version.");
191
192                 pango_font_description_free(new_r_font);
193                 return FALSE;
194         }
195
196         return TRUE;
197 }
198
199
200
201 /* Given a font name, construct the name of a version of that font with
202    the current zoom factor applied. */
203 static char *
204 font_zoom(char *gui_font_name)
205 {
206     char *new_font_name;
207     char *font_name_dup;
208     char *font_name_p;
209     long font_point_size_l;
210
211     if (recent.gui_zoom_level == 0) {
212         /* There is no zoom factor - just return the name, so that if
213            this is GTK+ 1.2[.x] and the font name isn't an XLFD font
214            name, we don't fail. */
215         return g_strdup(gui_font_name);
216     }
217
218     font_name_dup = g_strdup(gui_font_name);
219     font_name_p = font_name_dup;
220
221     /* find the start of the font_size string */
222     font_name_p = strrchr(font_name_dup, ' ');
223     *font_name_p = '\0';
224     font_name_p++;
225
226     /* calculate the new font size */
227     font_point_size_l = strtol(font_name_p, NULL, 10);
228     font_point_size_l += recent.gui_zoom_level;
229
230     /* build a new font name */
231     new_font_name = g_strdup_printf("%s %ld", font_name_dup, font_point_size_l);
232
233     g_free(font_name_dup);
234
235     return new_font_name;
236 }
237
238 fa_ret_t
239 user_font_apply(gboolean saved_column_width) {
240     char *gui_font_name;
241     PangoFontDescription *new_r_font, *new_b_font;
242     PangoFontDescription *old_r_font = NULL, *old_b_font = NULL;
243
244     /* convert font name to reflect the zoom level */
245     gui_font_name = font_zoom(prefs.gui_font_name);
246     if (gui_font_name == NULL) {
247         /*
248          * This means the font name isn't an XLFD font name.
249          * We just report that for now as a font not available in
250          * multiple sizes.
251          */
252         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
253             "Your current font isn't available in any other sizes.\n");
254         return FA_FONT_NOT_RESIZEABLE;
255     }
256
257     /* load normal and bold font */
258     new_r_font = pango_font_description_from_string(gui_font_name);
259     new_b_font = pango_font_description_copy(new_r_font);
260     pango_font_description_set_weight(new_b_font, PANGO_WEIGHT_BOLD);
261
262     if (new_r_font == NULL || new_b_font == NULL) {
263         /* We're no longer using the new fonts; unreference them. */
264         if (new_r_font != NULL)
265             pango_font_description_free(new_r_font);
266         if (new_b_font != NULL)
267             pango_font_description_free(new_b_font);
268         g_free(gui_font_name);
269
270         /* We let our caller pop up a dialog box, as the error message
271            depends on the context (did they zoom in or out, or did they
272            do something else? */
273         return FA_FONT_NOT_AVAILABLE;
274     }
275
276     /* the font(s) seem to be ok */
277     packet_list_set_font(new_r_font, saved_column_width);
278     set_ptree_font_all(new_r_font);
279     old_r_font = m_r_font;
280     old_b_font = m_b_font;
281     set_fonts(new_r_font, new_b_font);
282
283     /* Redraw the hex dump windows. */
284     redraw_hex_dump_all();
285
286     /* Redraw the "Follow TCP Stream" windows. */
287     follow_tcp_redraw_all();
288
289     /* We're no longer using the old fonts; unreference them. */
290     if (old_r_font != NULL)
291         pango_font_description_free(old_r_font);
292     if (old_b_font != NULL)
293         pango_font_description_free(old_b_font);
294     g_free(gui_font_name);
295
296     return FA_SUCCESS;
297 }
298
299
300 #ifdef _WIN32
301
302 #define NAME_BUFFER_LEN 32
303
304 static char appfontname[128] = "tahoma 8";
305
306 static void
307 set_app_font_gtk2(const char *fontname)
308 {
309     GtkSettings *settings;
310
311     if (fontname != NULL && *fontname == 0) return;
312
313     settings = gtk_settings_get_default();
314
315     if (fontname == NULL) {
316         g_object_set(G_OBJECT(settings), "gtk-font-name", appfontname, NULL);
317     } else {
318         GtkWidget *w;
319         PangoFontDescription *pfd;
320         PangoContext *pc;
321         PangoFont *pfont;
322
323         w = gtk_label_new(NULL);
324         pfd = pango_font_description_from_string(fontname);
325         pc = gtk_widget_get_pango_context(w);
326         pfont = pango_context_load_font(pc, pfd);
327
328         if (pfont != NULL) {
329             g_strlcpy(appfontname, fontname, 128);
330             appfontname[127] = '\0';
331             g_object_set(G_OBJECT(settings), "gtk-font-name", appfontname, NULL);
332         }
333
334         gtk_widget_destroy(w);
335         pango_font_description_free(pfd);
336     }
337 }
338
339 static char *default_windows_menu_fontspec_gtk2(void)
340 {
341     gchar *fontspec = NULL;
342     NONCLIENTMETRICS ncm;
343
344     memset(&ncm, 0, sizeof ncm);
345     ncm.cbSize = sizeof ncm;
346
347     if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0)) {
348         HDC screen = GetDC(0);
349         double y_scale = 72.0 / GetDeviceCaps(screen, LOGPIXELSY);
350         int point_size = (int) (ncm.lfMenuFont.lfHeight * y_scale);
351
352         if (point_size < 0) point_size = -point_size;
353         fontspec = g_strdup_printf("%s %d", ncm.lfMenuFont.lfFaceName,
354                                    point_size);
355         ReleaseDC(0, screen);
356     }
357
358     return fontspec;
359 }
360
361 static void try_to_get_windows_font_gtk2(void)
362 {
363     gchar *fontspec;
364
365     fontspec = default_windows_menu_fontspec_gtk2();
366
367     if (fontspec != NULL) {
368         int match = 0;
369         PangoFontDescription *pfd;
370         PangoFont *pfont;
371         PangoContext *pc;
372         GtkWidget *w;
373
374         pfd = pango_font_description_from_string(fontspec);
375
376         w = gtk_label_new(NULL);
377         pc = gtk_widget_get_pango_context(w);
378         pfont = pango_context_load_font(pc, pfd);
379         match = (pfont != NULL);
380
381         pango_font_description_free(pfd);
382         g_object_unref(G_OBJECT(pc));
383         gtk_widget_destroy(w);
384
385         if (match) set_app_font_gtk2(fontspec);
386         g_free(fontspec);
387     }
388 }
389 #endif /* _WIN32 */
390
391
392 void font_init(void)
393 {
394 #ifdef _WIN32
395   /* try to load the application font for GTK2 */
396   try_to_get_windows_font_gtk2();
397 #endif
398
399   /* Try to load the regular and boldface fixed-width fonts */
400   m_r_font = pango_font_description_from_string(prefs.gui_font_name);
401   m_b_font = pango_font_description_copy(m_r_font);
402   pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
403   if (m_r_font == NULL || m_b_font == NULL) {
404     /* XXX - pop this up as a dialog box? no */
405     if (m_r_font == NULL) {
406         fprintf(stderr, "wireshark: Warning: font %s not found - defaulting to Monospace 9\n",
407                 prefs.gui_font_name);
408     } else {
409       pango_font_description_free(m_r_font);
410     }
411     if (m_b_font == NULL) {
412         fprintf(stderr, "wireshark: Warning: bold font %s not found - defaulting"
413                         " to Monospace 9\n", prefs.gui_font_name);
414     } else {
415       pango_font_description_free(m_b_font);
416     }
417     if ((m_r_font = pango_font_description_from_string("Monospace 9")) == NULL)
418     {
419       fprintf(stderr, "wireshark: Error: font Monospace 9 not found\n");
420       exit(1);
421     }
422     if ((m_b_font = pango_font_description_copy(m_r_font)) == NULL) {
423       fprintf(stderr, "wireshark: Error: font Monospace 9 bold not found\n");
424       exit(1);
425     }
426     g_free(prefs.gui_font_name);
427     pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
428     prefs.gui_font_name = g_strdup("Monospace 9");
429   }
430
431   /* Call this for the side-effects that set_fonts() produces */
432   set_fonts(m_r_font, m_b_font);
433 }