Also add wxNativeFontInfo::SetStrikethrough() stub for wxGTK2.
[wxWidgets.git] / src / unix / fontutil.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/fontutil.cpp
3 // Purpose: Font helper functions for wxX11, wxGTK, wxMotif
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 05.11.99
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/fontutil.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/font.h" // wxFont enums
32 #include "wx/encinfo.h"
33 #include "wx/hash.h"
34 #include "wx/utils.h" // for wxGetDisplay()
35 #include "wx/module.h"
36 #endif // PCH
37
38 #include "wx/fontmap.h"
39 #include "wx/tokenzr.h"
40 #include "wx/fontenum.h"
41
42 #if wxUSE_PANGO
43
44 #include "pango/pango.h"
45
46 #ifdef __WXGTK20__
47 #include "wx/gtk/private.h"
48 extern GtkWidget *wxGetRootWindow();
49
50 #define wxPANGO_CONV wxGTK_CONV_SYS
51 #define wxPANGO_CONV_BACK wxGTK_CONV_BACK_SYS
52 #else
53 #include "wx/x11/private.h"
54 #include "wx/gtk/private/string.h"
55
56 #define wxPANGO_CONV(s) s.utf8_str()
57 #define wxPANGO_CONV_BACK(s) wxString::FromUTF8Unchecked(s)
58 #endif
59
60 // ----------------------------------------------------------------------------
61 // wxNativeFontInfo
62 // ----------------------------------------------------------------------------
63
64 void wxNativeFontInfo::Init()
65 {
66 description = NULL;
67 }
68
69 void wxNativeFontInfo::Init(const wxNativeFontInfo& info)
70 {
71 if (info.description)
72 description = pango_font_description_copy(info.description);
73 else
74 description = NULL;
75 }
76
77 void wxNativeFontInfo::Free()
78 {
79 if (description)
80 pango_font_description_free(description);
81 }
82
83 int wxNativeFontInfo::GetPointSize() const
84 {
85 return pango_font_description_get_size( description ) / PANGO_SCALE;
86 }
87
88 wxFontStyle wxNativeFontInfo::GetStyle() const
89 {
90 wxFontStyle m_style = wxFONTSTYLE_NORMAL;
91
92 switch (pango_font_description_get_style( description ))
93 {
94 case PANGO_STYLE_NORMAL:
95 m_style = wxFONTSTYLE_NORMAL;
96 break;
97 case PANGO_STYLE_ITALIC:
98 m_style = wxFONTSTYLE_ITALIC;
99 break;
100 case PANGO_STYLE_OBLIQUE:
101 m_style = wxFONTSTYLE_SLANT;
102 break;
103 }
104
105 return m_style;
106 }
107
108 wxFontWeight wxNativeFontInfo::GetWeight() const
109 {
110 // We seem to currently initialize only by string.
111 // In that case PANGO_FONT_MASK_WEIGHT is always set.
112 // if (!(pango_font_description_get_set_fields(description) & PANGO_FONT_MASK_WEIGHT))
113 // return wxFONTWEIGHT_NORMAL;
114
115 PangoWeight pango_weight = pango_font_description_get_weight( description );
116
117 // Until the API can be changed the following ranges of weight values are used:
118 // wxFONTWEIGHT_LIGHT: 100 .. 349 - range of 250
119 // wxFONTWEIGHT_NORMAL: 350 .. 599 - range of 250
120 // wxFONTWEIGHT_BOLD: 600 .. 900 - range of 301 (600 is "semibold" already)
121
122 if (pango_weight >= 600)
123 return wxFONTWEIGHT_BOLD;
124
125 if (pango_weight < 350)
126 return wxFONTWEIGHT_LIGHT;
127
128 return wxFONTWEIGHT_NORMAL;
129 }
130
131 bool wxNativeFontInfo::GetUnderlined() const
132 {
133 return false;
134 }
135
136 bool wxNativeFontInfo::GetStrikethrough() const
137 {
138 return false;
139 }
140
141 wxString wxNativeFontInfo::GetFaceName() const
142 {
143 // the Pango "family" is the wx "face name"
144 return wxPANGO_CONV_BACK(pango_font_description_get_family(description));
145 }
146
147 wxFontFamily wxNativeFontInfo::GetFamily() const
148 {
149 wxFontFamily ret = wxFONTFAMILY_UNKNOWN;
150
151 const char *family_name = pango_font_description_get_family( description );
152
153 // note: not passing -1 as the 2nd parameter to g_ascii_strdown to work
154 // around a bug in the 64-bit glib shipped with solaris 10, -1 causes it
155 // to try to allocate 2^32 bytes.
156 if ( !family_name )
157 return ret;
158 wxGtkString family_text(g_ascii_strdown(family_name, strlen(family_name)));
159
160 // Check for some common fonts, to salvage what we can from the current
161 // win32 centric wxFont API:
162 if (strncasecmp( family_text, "monospace", 9 ) == 0)
163 ret = wxFONTFAMILY_TELETYPE; // begins with "Monospace"
164 else if (strncasecmp( family_text, "courier", 7 ) == 0)
165 ret = wxFONTFAMILY_TELETYPE; // begins with "Courier"
166 #if defined(__WXGTK20__) || defined(HAVE_PANGO_FONT_FAMILY_IS_MONOSPACE)
167 else
168 #ifdef __WXGTK20__
169 if (!gtk_check_version(2,4,0))
170 #endif
171 {
172 PangoFontFamily **families;
173 PangoFontFamily *family = NULL;
174 int n_families;
175 pango_context_list_families(
176 #ifdef __WXGTK20__
177 gtk_widget_get_pango_context( wxGetRootWindow() ),
178 #else
179 wxTheApp->GetPangoContext(),
180 #endif
181 &families, &n_families);
182
183 for (int i = 0; i < n_families; ++i)
184 {
185 if (g_ascii_strcasecmp(pango_font_family_get_name( families[i] ),
186 pango_font_description_get_family( description )) == 0 )
187 {
188 family = families[i];
189 break;
190 }
191 }
192
193 g_free(families);
194
195 // Some gtk+ systems might query for a non-existing font from
196 // wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) on initialization,
197 // don't assert until wxSystemSettings::GetFont is checked for this - MR
198 // wxASSERT_MSG( family, "No appropriate PangoFontFamily found for ::description" );
199
200 if (family != NULL && pango_font_family_is_monospace( family ))
201 ret = wxFONTFAMILY_TELETYPE; // is deemed a monospace font by pango
202 }
203 #endif // GTK+ 2 || HAVE_PANGO_FONT_FAMILY_IS_MONOSPACE
204
205 if (ret == wxFONTFAMILY_UNKNOWN)
206 {
207 if (strstr( family_text, "sans" ) != NULL || strstr( family_text, "Sans" ) != NULL)
208 // checked before serif, so that "* Sans Serif" fonts are detected correctly
209 ret = wxFONTFAMILY_SWISS; // contains "Sans"
210 else if (strstr( family_text, "serif" ) != NULL || strstr( family_text, "Serif" ) != NULL)
211 ret = wxFONTFAMILY_ROMAN; // contains "Serif"
212 else if (strncasecmp( family_text, "times", 5 ) == 0)
213 ret = wxFONTFAMILY_ROMAN; // begins with "Times"
214 else if (strncasecmp( family_text, "old", 3 ) == 0)
215 ret = wxFONTFAMILY_DECORATIVE; // begins with "Old" - "Old English", "Old Town"
216 }
217
218 return ret;
219 }
220
221 wxFontEncoding wxNativeFontInfo::GetEncoding() const
222 {
223 return wxFONTENCODING_SYSTEM;
224 }
225
226 void wxNativeFontInfo::SetPointSize(int pointsize)
227 {
228 pango_font_description_set_size( description, pointsize * PANGO_SCALE );
229 }
230
231 void wxNativeFontInfo::SetStyle(wxFontStyle style)
232 {
233 switch (style)
234 {
235 case wxFONTSTYLE_ITALIC:
236 pango_font_description_set_style( description, PANGO_STYLE_ITALIC );
237 break;
238 case wxFONTSTYLE_SLANT:
239 pango_font_description_set_style( description, PANGO_STYLE_OBLIQUE );
240 break;
241 default:
242 wxFAIL_MSG( "unknown font style" );
243 // fall through
244 case wxFONTSTYLE_NORMAL:
245 pango_font_description_set_style( description, PANGO_STYLE_NORMAL );
246 break;
247 }
248 }
249
250 void wxNativeFontInfo::SetWeight(wxFontWeight weight)
251 {
252 switch (weight)
253 {
254 case wxFONTWEIGHT_BOLD:
255 pango_font_description_set_weight(description, PANGO_WEIGHT_BOLD);
256 break;
257 case wxFONTWEIGHT_LIGHT:
258 pango_font_description_set_weight(description, PANGO_WEIGHT_LIGHT);
259 break;
260 default:
261 wxFAIL_MSG( "unknown font weight" );
262 // fall through
263 case wxFONTWEIGHT_NORMAL:
264 pango_font_description_set_weight(description, PANGO_WEIGHT_NORMAL);
265 }
266 }
267
268 void wxNativeFontInfo::SetUnderlined(bool WXUNUSED(underlined))
269 {
270 // wxWindowDCImpl::DoDrawText will take care of rendering font with
271 // the underline attribute!
272 wxFAIL_MSG( "not implemented" );
273 }
274
275 void wxNativeFontInfo::SetStrikethrough(bool WXUNUSED(strikethrough))
276 {
277 wxFAIL_MSG( "not implemented" );
278 }
279
280 bool wxNativeFontInfo::SetFaceName(const wxString& facename)
281 {
282 pango_font_description_set_family(description, wxPANGO_CONV(facename));
283
284 // we return true because Pango doesn't tell us if the call failed or not;
285 // instead on wxGTK wxFont::SetFaceName() will call wxFontBase::SetFaceName()
286 // which does the check
287 return true;
288 }
289
290 void wxNativeFontInfo::SetFamily(wxFontFamily family)
291 {
292 wxArrayString facename;
293
294 // the list of fonts associated with a family was partially
295 // taken from http://www.codestyle.org/css/font-family
296
297 switch ( family )
298 {
299 case wxFONTFAMILY_SCRIPT:
300 // corresponds to the cursive font family in the page linked above
301 facename.Add(wxS("URW Chancery L"));
302 facename.Add(wxS("Comic Sans MS"));
303 break;
304
305 case wxFONTFAMILY_DECORATIVE:
306 // corresponds to the fantasy font family in the page linked above
307 facename.Add(wxS("Impact"));
308 break;
309
310 case wxFONTFAMILY_ROMAN:
311 // corresponds to the serif font family in the page linked above
312 facename.Add(wxS("Century Schoolbook L"));
313 facename.Add(wxS("URW Bookman L"));
314 facename.Add(wxS("URW Palladio L"));
315 facename.Add(wxS("DejaVu Serif"));
316 facename.Add(wxS("FreeSerif"));
317 facename.Add(wxS("Times New Roman"));
318 facename.Add(wxS("Times"));
319 break;
320
321 case wxFONTFAMILY_TELETYPE:
322 case wxFONTFAMILY_MODERN:
323 // corresponds to the monospace font family in the page linked above
324 facename.Add(wxS("DejaVu Sans Mono"));
325 facename.Add(wxS("Nimbus Mono L"));
326 facename.Add(wxS("Bitstream Vera Sans Mono"));
327 facename.Add(wxS("Andale Mono"));
328 facename.Add(wxS("Lucida Sans Typewriter"));
329 facename.Add(wxS("FreeMono"));
330 facename.Add(wxS("Courier New"));
331 facename.Add(wxS("Courier"));
332 break;
333
334 case wxFONTFAMILY_SWISS:
335 case wxFONTFAMILY_DEFAULT:
336 default:
337 // corresponds to the sans-serif font family in the page linked above
338 facename.Add(wxS("DejaVu Sans"));
339 facename.Add(wxS("URW Gothic L"));
340 facename.Add(wxS("Nimbus Sans L"));
341 facename.Add(wxS("Bitstream Vera Sans"));
342 facename.Add(wxS("Lucida Sans"));
343 facename.Add(wxS("Arial"));
344 facename.Add(wxS("FreeSans"));
345 break;
346 }
347
348 SetFaceName(facename);
349 }
350
351 void wxNativeFontInfo::SetEncoding(wxFontEncoding WXUNUSED(encoding))
352 {
353 wxFAIL_MSG( "not implemented: Pango encoding is always UTF8" );
354 }
355
356 bool wxNativeFontInfo::FromString(const wxString& s)
357 {
358 if (description)
359 pango_font_description_free( description );
360
361 // there is a bug in at least pango <= 1.13 which makes it (or its backends)
362 // segfault for very big point sizes and for negative point sizes.
363 // To workaround that bug for pango <= 1.13
364 // (see http://bugzilla.gnome.org/show_bug.cgi?id=340229)
365 // we do the check on the size here using same (arbitrary) limits used by
366 // pango > 1.13. Note that the segfault could happen also for pointsize
367 // smaller than this limit !!
368 wxString str(s);
369 const size_t pos = str.find_last_of(wxS(" "));
370 double size;
371 if ( pos != wxString::npos && wxString(str, pos + 1).ToDouble(&size) )
372 {
373 wxString sizeStr;
374 if ( size < 1 )
375 sizeStr = wxS("1");
376 else if ( size >= 1E6 )
377 sizeStr = wxS("1E6");
378
379 if ( !sizeStr.empty() )
380 {
381 // replace the old size with the adjusted one
382 str = wxString(s, 0, pos) + sizeStr;
383 }
384 }
385
386 description = pango_font_description_from_string(wxPANGO_CONV(str));
387
388 #if wxUSE_FONTENUM
389 // ensure a valid facename is selected
390 if (!wxFontEnumerator::IsValidFacename(GetFaceName()))
391 SetFaceName(wxNORMAL_FONT->GetFaceName());
392 #endif // wxUSE_FONTENUM
393
394 return true;
395 }
396
397 wxString wxNativeFontInfo::ToString() const
398 {
399 wxGtkString str(pango_font_description_to_string( description ));
400
401 return wxPANGO_CONV_BACK(str);
402 }
403
404 bool wxNativeFontInfo::FromUserString(const wxString& s)
405 {
406 return FromString( s );
407 }
408
409 wxString wxNativeFontInfo::ToUserString() const
410 {
411 return ToString();
412 }
413
414 #else // GTK+ 1.x
415
416 #ifdef __X__
417 #ifdef __VMS__
418 #pragma message disable nosimpint
419 #endif
420
421 #include <X11/Xlib.h>
422
423 #ifdef __VMS__
424 #pragma message enable nosimpint
425 #endif
426
427 #elif defined(__WXGTK__)
428 // we have to declare struct tm to avoid problems with first forward
429 // declaring it in C code (glib.h included from gdk.h does it) and then
430 // defining it when time.h is included from the headers below - this is
431 // known not to work at least with Sun CC 6.01
432 #include <time.h>
433
434 #include <gdk/gdk.h>
435 #endif
436
437
438 // ----------------------------------------------------------------------------
439 // private data
440 // ----------------------------------------------------------------------------
441
442 static wxHashTable *g_fontHash = NULL;
443
444 // ----------------------------------------------------------------------------
445 // private functions
446 // ----------------------------------------------------------------------------
447
448 // define the functions to create and destroy native fonts for this toolkit
449 #ifdef __X__
450 wxNativeFont wxLoadFont(const wxString& fontSpec)
451 {
452 return XLoadQueryFont((Display *)wxGetDisplay(), fontSpec);
453 }
454
455 inline void wxFreeFont(wxNativeFont font)
456 {
457 XFreeFont((Display *)wxGetDisplay(), (XFontStruct *)font);
458 }
459 #elif defined(__WXGTK__)
460 wxNativeFont wxLoadFont(const wxString& fontSpec)
461 {
462 // VZ: we should use gdk_fontset_load() instead of gdk_font_load()
463 // here to be able to display Japanese fonts correctly (at least
464 // this is what people report) but unfortunately doing it results
465 // in tons of warnings when using GTK with "normal" European
466 // languages and so we can't always do it and I don't know enough
467 // to determine when should this be done... (FIXME)
468 return gdk_font_load( wxConvertWX2MB(fontSpec) );
469 }
470
471 inline void wxFreeFont(wxNativeFont font)
472 {
473 gdk_font_unref(font);
474 }
475 #else
476 #error "Unknown GUI toolkit"
477 #endif
478
479 static bool wxTestFontSpec(const wxString& fontspec);
480
481 static wxNativeFont wxLoadQueryFont(int pointSize,
482 int family,
483 int style,
484 int weight,
485 bool underlined,
486 const wxString& facename,
487 const wxString& xregistry,
488 const wxString& xencoding,
489 wxString* xFontName);
490
491 // ============================================================================
492 // implementation
493 // ============================================================================
494
495 // ----------------------------------------------------------------------------
496 // wxNativeEncodingInfo
497 // ----------------------------------------------------------------------------
498
499 // convert to/from the string representation: format is
500 // encodingid;registry;encoding[;facename]
501 bool wxNativeEncodingInfo::FromString(const wxString& s)
502 {
503 // use ";", not "-" because it may be part of encoding name
504 wxStringTokenizer tokenizer(s, wxT(";"));
505
506 wxString encid = tokenizer.GetNextToken();
507 long enc;
508 if ( !encid.ToLong(&enc) )
509 return false;
510 encoding = (wxFontEncoding)enc;
511
512 xregistry = tokenizer.GetNextToken();
513 if ( !xregistry )
514 return false;
515
516 xencoding = tokenizer.GetNextToken();
517 if ( !xencoding )
518 return false;
519
520 // ok even if empty
521 facename = tokenizer.GetNextToken();
522
523 return true;
524 }
525
526 wxString wxNativeEncodingInfo::ToString() const
527 {
528 wxString s;
529 s << (long)encoding << wxT(';') << xregistry << wxT(';') << xencoding;
530 if ( !facename.empty() )
531 {
532 s << wxT(';') << facename;
533 }
534
535 return s;
536 }
537
538 // ----------------------------------------------------------------------------
539 // wxNativeFontInfo
540 // ----------------------------------------------------------------------------
541
542 void wxNativeFontInfo::Init()
543 {
544 m_isDefault = true;
545 }
546
547 bool wxNativeFontInfo::FromString(const wxString& s)
548 {
549 wxStringTokenizer tokenizer(s, wxT(";"));
550
551 // check the version
552 wxString token = tokenizer.GetNextToken();
553 if ( token != wxT('0') )
554 return false;
555
556 xFontName = tokenizer.GetNextToken();
557
558 // this should be the end
559 if ( tokenizer.HasMoreTokens() )
560 return false;
561
562 return FromXFontName(xFontName);
563 }
564
565 wxString wxNativeFontInfo::ToString() const
566 {
567 // 0 is the version
568 return wxString::Format(wxT("%d;%s"), 0, GetXFontName().c_str());
569 }
570
571 bool wxNativeFontInfo::FromUserString(const wxString& s)
572 {
573 return FromXFontName(s);
574 }
575
576 wxString wxNativeFontInfo::ToUserString() const
577 {
578 return GetXFontName();
579 }
580
581 bool wxNativeFontInfo::HasElements() const
582 {
583 // we suppose that the foundry is never empty, so if it is it means that we
584 // had never parsed the XLFD
585 return !fontElements[0].empty();
586 }
587
588 wxString wxNativeFontInfo::GetXFontComponent(wxXLFDField field) const
589 {
590 wxCHECK_MSG( field < wxXLFD_MAX, wxEmptyString, wxT("invalid XLFD field") );
591
592 if ( !HasElements() )
593 {
594 if ( !const_cast<wxNativeFontInfo *>(this)->FromXFontName(xFontName) )
595 return wxEmptyString;
596 }
597
598 return fontElements[field];
599 }
600
601 bool wxNativeFontInfo::FromXFontName(const wxString& fontname)
602 {
603 // TODO: we should be able to handle the font aliases here, but how?
604 wxStringTokenizer tokenizer(fontname, wxT("-"));
605
606 // skip the leading, usually empty field (font name registry)
607 if ( !tokenizer.HasMoreTokens() )
608 return false;
609
610 (void)tokenizer.GetNextToken();
611
612 for ( size_t n = 0; n < WXSIZEOF(fontElements); n++ )
613 {
614 if ( !tokenizer.HasMoreTokens() )
615 {
616 // not enough elements in the XLFD - or maybe an alias
617 return false;
618 }
619
620 wxString field = tokenizer.GetNextToken();
621 if ( !field.empty() && field != wxT('*') )
622 {
623 // we're really initialized now
624 m_isDefault = false;
625 }
626
627 fontElements[n] = field;
628 }
629
630 // this should be all
631 if ( tokenizer.HasMoreTokens() )
632 return false;
633
634 return true;
635 }
636
637 wxString wxNativeFontInfo::GetXFontName() const
638 {
639 if ( xFontName.empty() )
640 {
641 for ( size_t n = 0; n < WXSIZEOF(fontElements); n++ )
642 {
643 // replace the non specified elements with '*' except for the
644 // additional style which is usually just omitted
645 wxString elt = fontElements[n];
646 if ( elt.empty() && n != wxXLFD_ADDSTYLE )
647 {
648 elt = wxT('*');
649 }
650
651 const_cast<wxNativeFontInfo *>(this)->xFontName << wxT('-') << elt;
652 }
653 }
654
655 return xFontName;
656 }
657
658 void
659 wxNativeFontInfo::SetXFontComponent(wxXLFDField field, const wxString& value)
660 {
661 wxCHECK_RET( field < wxXLFD_MAX, wxT("invalid XLFD field") );
662
663 // this class should be initialized with a valid font spec first and only
664 // then the fields may be modified!
665 wxASSERT_MSG( !IsDefault(), wxT("can't modify an uninitialized XLFD") );
666
667 if ( !HasElements() )
668 {
669 if ( !const_cast<wxNativeFontInfo *>(this)->FromXFontName(xFontName) )
670 {
671 wxFAIL_MSG( wxT("can't set font element for invalid XLFD") );
672
673 return;
674 }
675 }
676
677 fontElements[field] = value;
678
679 // invalidate the XFLD, it doesn't correspond to the font elements any more
680 xFontName.clear();
681 }
682
683 void wxNativeFontInfo::SetXFontName(const wxString& xFontName_)
684 {
685 // invalidate the font elements, GetXFontComponent() will reparse the XLFD
686 fontElements[0].clear();
687
688 xFontName = xFontName_;
689
690 m_isDefault = false;
691 }
692
693 int wxNativeFontInfo::GetPointSize() const
694 {
695 const wxString s = GetXFontComponent(wxXLFD_POINTSIZE);
696
697 // return -1 to indicate that the size is unknown
698 long l;
699 return s.ToLong(&l) ? l : -1;
700 }
701
702 wxFontStyle wxNativeFontInfo::GetStyle() const
703 {
704 const wxString s = GetXFontComponent(wxXLFD_SLANT);
705
706 if ( s.length() != 1 )
707 {
708 // it is really unknown but we don't have any way to return it from
709 // here
710 return wxFONTSTYLE_NORMAL;
711 }
712
713 switch ( s[0].GetValue() )
714 {
715 default:
716 // again, unknown but consider normal by default
717
718 case wxT('r'):
719 return wxFONTSTYLE_NORMAL;
720
721 case wxT('i'):
722 return wxFONTSTYLE_ITALIC;
723
724 case wxT('o'):
725 return wxFONTSTYLE_SLANT;
726 }
727 }
728
729 wxFontWeight wxNativeFontInfo::GetWeight() const
730 {
731 const wxString s = GetXFontComponent(wxXLFD_WEIGHT).MakeLower();
732 if ( s.find(wxT("bold")) != wxString::npos || s == wxT("black") )
733 return wxFONTWEIGHT_BOLD;
734 else if ( s == wxT("light") )
735 return wxFONTWEIGHT_LIGHT;
736
737 return wxFONTWEIGHT_NORMAL;
738 }
739
740 bool wxNativeFontInfo::GetUnderlined() const
741 {
742 // X fonts are never underlined
743 return false;
744 }
745
746 wxString wxNativeFontInfo::GetFaceName() const
747 {
748 // wxWidgets facename probably more accurately corresponds to X family
749 return GetXFontComponent(wxXLFD_FAMILY);
750 }
751
752 wxFontFamily wxNativeFontInfo::GetFamily() const
753 {
754 // and wxWidgets family -- to X foundry, but we have to translate it to
755 // wxFontFamily somehow...
756 wxFAIL_MSG(wxT("not implemented")); // GetXFontComponent(wxXLFD_FOUNDRY);
757
758 return wxFONTFAMILY_DEFAULT;
759 }
760
761 wxFontEncoding wxNativeFontInfo::GetEncoding() const
762 {
763 // we already have the code for this but need to refactor it first
764 wxFAIL_MSG( wxT("not implemented") );
765
766 return wxFONTENCODING_MAX;
767 }
768
769 void wxNativeFontInfo::SetPointSize(int pointsize)
770 {
771 SetXFontComponent(wxXLFD_POINTSIZE, wxString::Format(wxT("%d"), pointsize));
772 }
773
774 void wxNativeFontInfo::SetStyle(wxFontStyle style)
775 {
776 wxString s;
777 switch ( style )
778 {
779 case wxFONTSTYLE_ITALIC:
780 s = wxT('i');
781 break;
782
783 case wxFONTSTYLE_SLANT:
784 s = wxT('o');
785 break;
786
787 case wxFONTSTYLE_NORMAL:
788 s = wxT('r');
789
790 default:
791 wxFAIL_MSG( wxT("unknown wxFontStyle in wxNativeFontInfo::SetStyle") );
792 return;
793 }
794
795 SetXFontComponent(wxXLFD_SLANT, s);
796 }
797
798 void wxNativeFontInfo::SetWeight(wxFontWeight weight)
799 {
800 wxString s;
801 switch ( weight )
802 {
803 case wxFONTWEIGHT_BOLD:
804 s = wxT("bold");
805 break;
806
807 case wxFONTWEIGHT_LIGHT:
808 s = wxT("light");
809 break;
810
811 case wxFONTWEIGHT_NORMAL:
812 s = wxT("medium");
813 break;
814
815 default:
816 wxFAIL_MSG( wxT("unknown wxFontWeight in wxNativeFontInfo::SetWeight") );
817 return;
818 }
819
820 SetXFontComponent(wxXLFD_WEIGHT, s);
821 }
822
823 void wxNativeFontInfo::SetUnderlined(bool WXUNUSED(underlined))
824 {
825 // can't do this under X
826 }
827
828 void wxNativeFontInfo::SetStrikethrough(bool WXUNUSED(strikethrough))
829 {
830 // this is not supported by Pango fonts neither
831 }
832
833 bool wxNativeFontInfo::SetFaceName(const wxString& facename)
834 {
835 SetXFontComponent(wxXLFD_FAMILY, facename);
836 return true;
837 }
838
839 void wxNativeFontInfo::SetFamily(wxFontFamily WXUNUSED(family))
840 {
841 // wxFontFamily -> X foundry, anyone?
842 wxFAIL_MSG( wxT("not implemented") );
843
844 // SetXFontComponent(wxXLFD_FOUNDRY, ...);
845 }
846
847 void wxNativeFontInfo::SetEncoding(wxFontEncoding encoding)
848 {
849 wxNativeEncodingInfo info;
850 if ( wxGetNativeFontEncoding(encoding, &info) )
851 {
852 SetXFontComponent(wxXLFD_ENCODING, info.xencoding);
853 SetXFontComponent(wxXLFD_REGISTRY, info.xregistry);
854 }
855 }
856
857 // ----------------------------------------------------------------------------
858 // common functions
859 // ----------------------------------------------------------------------------
860
861 bool wxGetNativeFontEncoding(wxFontEncoding encoding,
862 wxNativeEncodingInfo *info)
863 {
864 wxCHECK_MSG( info, false, wxT("bad pointer in wxGetNativeFontEncoding") );
865
866 if ( encoding == wxFONTENCODING_DEFAULT )
867 {
868 encoding = wxFont::GetDefaultEncoding();
869 }
870
871 switch ( encoding )
872 {
873 case wxFONTENCODING_ISO8859_1:
874 case wxFONTENCODING_ISO8859_2:
875 case wxFONTENCODING_ISO8859_3:
876 case wxFONTENCODING_ISO8859_4:
877 case wxFONTENCODING_ISO8859_5:
878 case wxFONTENCODING_ISO8859_6:
879 case wxFONTENCODING_ISO8859_7:
880 case wxFONTENCODING_ISO8859_8:
881 case wxFONTENCODING_ISO8859_9:
882 case wxFONTENCODING_ISO8859_10:
883 case wxFONTENCODING_ISO8859_11:
884 case wxFONTENCODING_ISO8859_12:
885 case wxFONTENCODING_ISO8859_13:
886 case wxFONTENCODING_ISO8859_14:
887 case wxFONTENCODING_ISO8859_15:
888 {
889 int cp = encoding - wxFONTENCODING_ISO8859_1 + 1;
890 info->xregistry = wxT("iso8859");
891 info->xencoding.Printf(wxT("%d"), cp);
892 }
893 break;
894
895 case wxFONTENCODING_UTF8:
896 info->xregistry = wxT("iso10646");
897 info->xencoding = wxT("*");
898 break;
899
900 case wxFONTENCODING_GB2312:
901 info->xregistry = wxT("GB2312"); // or the otherway round?
902 info->xencoding = wxT("*");
903 break;
904
905 case wxFONTENCODING_KOI8:
906 case wxFONTENCODING_KOI8_U:
907 info->xregistry = wxT("koi8");
908
909 // we don't make distinction between koi8-r, koi8-u and koi8-ru (so far)
910 info->xencoding = wxT("*");
911 break;
912
913 case wxFONTENCODING_CP1250:
914 case wxFONTENCODING_CP1251:
915 case wxFONTENCODING_CP1252:
916 case wxFONTENCODING_CP1253:
917 case wxFONTENCODING_CP1254:
918 case wxFONTENCODING_CP1255:
919 case wxFONTENCODING_CP1256:
920 case wxFONTENCODING_CP1257:
921 {
922 int cp = encoding - wxFONTENCODING_CP1250 + 1250;
923 info->xregistry = wxT("microsoft");
924 info->xencoding.Printf(wxT("cp%d"), cp);
925 }
926 break;
927
928 case wxFONTENCODING_EUC_JP:
929 case wxFONTENCODING_SHIFT_JIS:
930 info->xregistry = "jis*";
931 info->xencoding = "*";
932 break;
933
934 case wxFONTENCODING_SYSTEM:
935 info->xregistry =
936 info->xencoding = wxT("*");
937 break;
938
939 default:
940 // don't know how to translate this encoding into X fontspec
941 return false;
942 }
943
944 info->encoding = encoding;
945
946 return true;
947 }
948
949 bool wxTestFontEncoding(const wxNativeEncodingInfo& info)
950 {
951 wxString fontspec;
952 fontspec.Printf(wxT("-*-%s-*-*-*-*-*-*-*-*-*-*-%s-%s"),
953 !info.facename ? wxT("*") : info.facename.c_str(),
954 info.xregistry.c_str(),
955 info.xencoding.c_str());
956
957 return wxTestFontSpec(fontspec);
958 }
959
960 // ----------------------------------------------------------------------------
961 // X-specific functions
962 // ----------------------------------------------------------------------------
963
964 wxNativeFont wxLoadQueryNearestFont(int pointSize,
965 int family,
966 int style,
967 int weight,
968 bool underlined,
969 const wxString &facename,
970 wxFontEncoding encoding,
971 wxString* xFontName)
972 {
973 if ( encoding == wxFONTENCODING_DEFAULT )
974 {
975 encoding = wxFont::GetDefaultEncoding();
976 }
977
978 // first determine the encoding - if the font doesn't exist at all in this
979 // encoding, it's useless to do all other approximations (i.e. size,
980 // family &c don't matter much)
981 wxNativeEncodingInfo info;
982 if ( encoding == wxFONTENCODING_SYSTEM )
983 {
984 // This will always work so we don't test to save time
985 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
986 }
987 else
988 {
989 if ( !wxGetNativeFontEncoding(encoding, &info) ||
990 !wxTestFontEncoding(info) )
991 {
992 #if wxUSE_FONTMAP
993 if ( !wxFontMapper::Get()->GetAltForEncoding(encoding, &info) )
994 #endif // wxUSE_FONTMAP
995 {
996 // unspported encoding - replace it with the default
997 //
998 // NB: we can't just return 0 from here because wxGTK code doesn't
999 // check for it (i.e. it supposes that we'll always succeed),
1000 // so it would provoke a crash
1001 wxGetNativeFontEncoding(wxFONTENCODING_SYSTEM, &info);
1002 }
1003 }
1004 }
1005
1006 // OK, we have the correct xregistry/xencoding in info structure
1007 wxNativeFont font = 0;
1008
1009 // if we already have the X font name, try to use it
1010 if( xFontName && !xFontName->empty() )
1011 {
1012 //
1013 // Make sure point size is correct for scale factor.
1014 //
1015 wxStringTokenizer tokenizer(*xFontName, wxT("-"), wxTOKEN_RET_DELIMS);
1016 wxString newFontName;
1017
1018 for(int i = 0; i < 8; i++)
1019 newFontName += tokenizer.NextToken();
1020
1021 (void) tokenizer.NextToken();
1022
1023 newFontName += wxString::Format(wxT("%d-"), pointSize);
1024
1025 while(tokenizer.HasMoreTokens())
1026 newFontName += tokenizer.GetNextToken();
1027
1028 font = wxLoadFont(newFontName);
1029
1030 if(font)
1031 *xFontName = newFontName;
1032 }
1033
1034 if ( !font )
1035 {
1036 // search up and down by stepsize 10
1037 int max_size = pointSize + 20 * (1 + (pointSize/180));
1038 int min_size = pointSize - 20 * (1 + (pointSize/180));
1039
1040 int i, round; // counters
1041
1042 // first round: search for equal, then for smaller and for larger size
1043 // with the given weight and style
1044 int testweight = weight;
1045 int teststyle = style;
1046
1047 for ( round = 0; round < 3; round++ )
1048 {
1049 // second round: use normal weight
1050 if ( round == 1 )
1051 {
1052 if ( testweight != wxNORMAL )
1053 {
1054 testweight = wxNORMAL;
1055 }
1056 else
1057 {
1058 ++round; // fall through to third round
1059 }
1060 }
1061
1062 // third round: ... and use normal style
1063 if ( round == 2 )
1064 {
1065 if ( teststyle != wxNORMAL )
1066 {
1067 teststyle = wxNORMAL;
1068 }
1069 else
1070 {
1071 break;
1072 }
1073 }
1074 // Search for equal or smaller size (approx.)
1075 for ( i = pointSize; !font && i >= 10 && i >= min_size; i -= 10 )
1076 {
1077 font = wxLoadQueryFont(i, family, teststyle, testweight, underlined,
1078 facename, info.xregistry, info.xencoding,
1079 xFontName);
1080 }
1081
1082 // Search for larger size (approx.)
1083 for ( i = pointSize + 10; !font && i <= max_size; i += 10 )
1084 {
1085 font = wxLoadQueryFont(i, family, teststyle, testweight, underlined,
1086 facename, info.xregistry, info.xencoding,
1087 xFontName);
1088 }
1089 }
1090
1091 // Try default family
1092 if ( !font && family != wxDEFAULT )
1093 {
1094 font = wxLoadQueryFont(pointSize, wxDEFAULT, style, weight,
1095 underlined, facename,
1096 info.xregistry, info.xencoding,
1097 xFontName );
1098 }
1099
1100 // ignore size, family, style and weight but try to find font with the
1101 // given facename and encoding
1102 if ( !font )
1103 {
1104 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
1105 underlined, facename,
1106 info.xregistry, info.xencoding,
1107 xFontName);
1108
1109 // ignore family as well
1110 if ( !font )
1111 {
1112 font = wxLoadQueryFont(120, wxDEFAULT, wxNORMAL, wxNORMAL,
1113 underlined, wxEmptyString,
1114 info.xregistry, info.xencoding,
1115 xFontName);
1116
1117 // if it still failed, try to get the font of any size but
1118 // with the requested encoding: this can happen if the
1119 // encoding is only available in one size which happens to be
1120 // different from 120
1121 if ( !font )
1122 {
1123 font = wxLoadQueryFont(-1, wxDEFAULT, wxNORMAL, wxNORMAL,
1124 false, wxEmptyString,
1125 info.xregistry, info.xencoding,
1126 xFontName);
1127
1128 // this should never happen as we had tested for it in the
1129 // very beginning, but if it does, do return something non
1130 // NULL or we'd crash in wxFont code
1131 if ( !font )
1132 {
1133 wxFAIL_MSG( wxT("this encoding should be available!") );
1134
1135 font = wxLoadQueryFont(-1,
1136 wxDEFAULT, wxNORMAL, wxNORMAL,
1137 false, wxEmptyString,
1138 wxT("*"), wxT("*"),
1139 xFontName);
1140 }
1141 }
1142 }
1143 }
1144 }
1145
1146 return font;
1147 }
1148
1149 // ----------------------------------------------------------------------------
1150 // private functions
1151 // ----------------------------------------------------------------------------
1152
1153 // returns true if there are any fonts matching this font spec
1154 static bool wxTestFontSpec(const wxString& fontspec)
1155 {
1156 // some X servers will fail to load this font because there are too many
1157 // matches so we must test explicitly for this
1158 if ( fontspec == wxT("-*-*-*-*-*-*-*-*-*-*-*-*-*-*") )
1159 {
1160 return true;
1161 }
1162
1163 wxNativeFont test = (wxNativeFont) g_fontHash->Get( fontspec );
1164 if (test)
1165 {
1166 return true;
1167 }
1168
1169 test = wxLoadFont(fontspec);
1170 g_fontHash->Put( fontspec, (wxObject*) test );
1171
1172 if ( test )
1173 {
1174 wxFreeFont(test);
1175
1176 return true;
1177 }
1178 else
1179 {
1180 return false;
1181 }
1182 }
1183
1184 static wxNativeFont wxLoadQueryFont(int pointSize,
1185 int family,
1186 int style,
1187 int weight,
1188 bool WXUNUSED(underlined),
1189 const wxString& facename,
1190 const wxString& xregistry,
1191 const wxString& xencoding,
1192 wxString* xFontName)
1193 {
1194 wxString xfamily;
1195 switch (family)
1196 {
1197 case wxDECORATIVE: xfamily = wxT("lucida"); break;
1198 case wxROMAN: xfamily = wxT("times"); break;
1199 case wxMODERN: xfamily = wxT("courier"); break;
1200 case wxSWISS: xfamily = wxT("helvetica"); break;
1201 case wxTELETYPE: xfamily = wxT("lucidatypewriter"); break;
1202 case wxSCRIPT: xfamily = wxT("utopia"); break;
1203 default: xfamily = wxT("*");
1204 }
1205 #if wxUSE_NANOX
1206 int xweight;
1207 switch (weight)
1208 {
1209 case wxBOLD:
1210 {
1211 xweight = MWLF_WEIGHT_BOLD;
1212 break;
1213 }
1214 case wxLIGHT:
1215 {
1216 xweight = MWLF_WEIGHT_LIGHT;
1217 break;
1218 }
1219 case wxNORMAL:
1220 {
1221 xweight = MWLF_WEIGHT_NORMAL;
1222 break;
1223 }
1224
1225 default:
1226 {
1227 xweight = MWLF_WEIGHT_DEFAULT;
1228 break;
1229 }
1230 }
1231 GR_SCREEN_INFO screenInfo;
1232 GrGetScreenInfo(& screenInfo);
1233
1234 int yPixelsPerCM = screenInfo.ydpcm;
1235
1236 // A point is 1/72 of an inch.
1237 // An inch is 2.541 cm.
1238 // So pixelHeight = (pointSize / 72) (inches) * 2.541 (for cm) * yPixelsPerCM (for pixels)
1239 // In fact pointSize is 10 * the normal point size so
1240 // divide by 10.
1241
1242 int pixelHeight = (int) ( (((float)pointSize) / 720.0) * 2.541 * (float) yPixelsPerCM) ;
1243
1244 // An alternative: assume that the screen is 72 dpi.
1245 //int pixelHeight = (int) (((float)pointSize / 720.0) * 72.0) ;
1246 //int pixelHeight = (int) ((float)pointSize / 10.0) ;
1247
1248 GR_LOGFONT logFont;
1249 logFont.lfHeight = pixelHeight;
1250 logFont.lfWidth = 0;
1251 logFont.lfEscapement = 0;
1252 logFont.lfOrientation = 0;
1253 logFont.lfWeight = xweight;
1254 logFont.lfItalic = (style == wxNORMAL ? 0 : 1) ;
1255 logFont.lfUnderline = 0;
1256 logFont.lfStrikeOut = 0;
1257 logFont.lfCharSet = MWLF_CHARSET_DEFAULT; // TODO: select appropriate one
1258 logFont.lfOutPrecision = MWLF_TYPE_DEFAULT;
1259 logFont.lfClipPrecision = 0; // Not used
1260 logFont.lfRoman = (family == wxROMAN ? 1 : 0) ;
1261 logFont.lfSerif = (family == wxSWISS ? 0 : 1) ;
1262 logFont.lfSansSerif = !logFont.lfSerif ;
1263 logFont.lfModern = (family == wxMODERN ? 1 : 0) ;
1264 logFont.lfProportional = (family == wxTELETYPE ? 0 : 1) ;
1265 logFont.lfOblique = 0;
1266 logFont.lfSmallCaps = 0;
1267 logFont.lfPitch = 0; // 0 = default
1268 strcpy(logFont.lfFaceName, facename.c_str());
1269
1270 XFontStruct* fontInfo = (XFontStruct*) malloc(sizeof(XFontStruct));
1271 fontInfo->fid = GrCreateFont((GR_CHAR*) facename.c_str(), pixelHeight, & logFont);
1272 GrGetFontInfo(fontInfo->fid, & fontInfo->info);
1273 return (wxNativeFont) fontInfo;
1274
1275 #else
1276 wxString fontSpec;
1277 if (!facename.empty())
1278 {
1279 fontSpec.Printf(wxT("-*-%s-*-*-normal-*-*-*-*-*-*-*-*-*"),
1280 facename.c_str());
1281
1282 if ( wxTestFontSpec(fontSpec) )
1283 {
1284 xfamily = facename;
1285 }
1286 //else: no such family, use default one instead
1287 }
1288
1289 wxString xstyle;
1290 switch (style)
1291 {
1292 case wxSLANT:
1293 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
1294 xfamily.c_str());
1295 if ( wxTestFontSpec(fontSpec) )
1296 {
1297 xstyle = wxT("o");
1298 break;
1299 }
1300 // fall through - try wxITALIC now
1301
1302 case wxITALIC:
1303 fontSpec.Printf(wxT("-*-%s-*-i-*-*-*-*-*-*-*-*-*-*"),
1304 xfamily.c_str());
1305 if ( wxTestFontSpec(fontSpec) )
1306 {
1307 xstyle = wxT("i");
1308 }
1309 else if ( style == wxITALIC ) // and not wxSLANT
1310 {
1311 // try wxSLANT
1312 fontSpec.Printf(wxT("-*-%s-*-o-*-*-*-*-*-*-*-*-*-*"),
1313 xfamily.c_str());
1314 if ( wxTestFontSpec(fontSpec) )
1315 {
1316 xstyle = wxT("o");
1317 }
1318 else
1319 {
1320 // no italic, no slant - leave default
1321 xstyle = wxT("*");
1322 }
1323 }
1324 break;
1325
1326 default:
1327 wxFAIL_MSG(wxT("unknown font style"));
1328 // fall back to normal
1329
1330 case wxNORMAL:
1331 xstyle = wxT("r");
1332 break;
1333 }
1334
1335 wxString xweight;
1336 switch (weight)
1337 {
1338 case wxBOLD:
1339 {
1340 fontSpec.Printf(wxT("-*-%s-bold-*-*-*-*-*-*-*-*-*-*-*"),
1341 xfamily.c_str());
1342 if ( wxTestFontSpec(fontSpec) )
1343 {
1344 xweight = wxT("bold");
1345 break;
1346 }
1347 fontSpec.Printf(wxT("-*-%s-heavy-*-*-*-*-*-*-*-*-*-*-*"),
1348 xfamily.c_str());
1349 if ( wxTestFontSpec(fontSpec) )
1350 {
1351 xweight = wxT("heavy");
1352 break;
1353 }
1354 fontSpec.Printf(wxT("-*-%s-extrabold-*-*-*-*-*-*-*-*-*-*-*"),
1355 xfamily.c_str());
1356 if ( wxTestFontSpec(fontSpec) )
1357 {
1358 xweight = wxT("extrabold");
1359 break;
1360 }
1361 fontSpec.Printf(wxT("-*-%s-demibold-*-*-*-*-*-*-*-*-*-*-*"),
1362 xfamily.c_str());
1363 if ( wxTestFontSpec(fontSpec) )
1364 {
1365 xweight = wxT("demibold");
1366 break;
1367 }
1368 fontSpec.Printf(wxT("-*-%s-black-*-*-*-*-*-*-*-*-*-*-*"),
1369 xfamily.c_str());
1370 if ( wxTestFontSpec(fontSpec) )
1371 {
1372 xweight = wxT("black");
1373 break;
1374 }
1375 fontSpec.Printf(wxT("-*-%s-ultrablack-*-*-*-*-*-*-*-*-*-*-*"),
1376 xfamily.c_str());
1377 if ( wxTestFontSpec(fontSpec) )
1378 {
1379 xweight = wxT("ultrablack");
1380 break;
1381 }
1382 }
1383 break;
1384 case wxLIGHT:
1385 {
1386 fontSpec.Printf(wxT("-*-%s-light-*-*-*-*-*-*-*-*-*-*-*"),
1387 xfamily.c_str());
1388 if ( wxTestFontSpec(fontSpec) )
1389 {
1390 xweight = wxT("light");
1391 break;
1392 }
1393 fontSpec.Printf(wxT("-*-%s-thin-*-*-*-*-*-*-*-*-*-*-*"),
1394 xfamily.c_str());
1395 if ( wxTestFontSpec(fontSpec) )
1396 {
1397 xweight = wxT("thin");
1398 break;
1399 }
1400 }
1401 break;
1402 case wxNORMAL:
1403 {
1404 fontSpec.Printf(wxT("-*-%s-medium-*-*-*-*-*-*-*-*-*-*-*"),
1405 xfamily.c_str());
1406 if ( wxTestFontSpec(fontSpec) )
1407 {
1408 xweight = wxT("medium");
1409 break;
1410 }
1411 fontSpec.Printf(wxT("-*-%s-normal-*-*-*-*-*-*-*-*-*-*-*"),
1412 xfamily.c_str());
1413 if ( wxTestFontSpec(fontSpec) )
1414 {
1415 xweight = wxT("normal");
1416 break;
1417 }
1418 fontSpec.Printf(wxT("-*-%s-regular-*-*-*-*-*-*-*-*-*-*-*"),
1419 xfamily.c_str());
1420 if ( wxTestFontSpec(fontSpec) )
1421 {
1422 xweight = wxT("regular");
1423 break;
1424 }
1425 xweight = wxT("*");
1426 }
1427 break;
1428 default: xweight = wxT("*"); break;
1429 }
1430
1431 // if pointSize is -1, don't specify any
1432 wxString sizeSpec;
1433 if ( pointSize == -1 )
1434 {
1435 sizeSpec = wxT('*');
1436 }
1437 else
1438 {
1439 sizeSpec.Printf(wxT("%d"), pointSize);
1440 }
1441
1442 // construct the X font spec from our data
1443 fontSpec.Printf(wxT("-*-%s-%s-%s-normal-*-*-%s-*-*-*-*-%s-%s"),
1444 xfamily.c_str(), xweight.c_str(), xstyle.c_str(),
1445 sizeSpec.c_str(), xregistry.c_str(), xencoding.c_str());
1446
1447 if( xFontName )
1448 *xFontName = fontSpec;
1449
1450 return wxLoadFont(fontSpec);
1451 #endif
1452 // wxUSE_NANOX
1453 }
1454
1455 // ----------------------------------------------------------------------------
1456 // wxFontModule
1457 // ----------------------------------------------------------------------------
1458
1459 class wxFontModule : public wxModule
1460 {
1461 public:
1462 bool OnInit();
1463 void OnExit();
1464
1465 private:
1466 DECLARE_DYNAMIC_CLASS(wxFontModule)
1467 };
1468
1469 IMPLEMENT_DYNAMIC_CLASS(wxFontModule, wxModule)
1470
1471 bool wxFontModule::OnInit()
1472 {
1473 g_fontHash = new wxHashTable( wxKEY_STRING );
1474
1475 return true;
1476 }
1477
1478 void wxFontModule::OnExit()
1479 {
1480 wxDELETE(g_fontHash);
1481 }
1482
1483 #endif // GTK 2.0/1.x