1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/fontmap.cpp
3 // Purpose: wxFontMapper class
4 // Author: Vadim Zeitlin
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "fontmap.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
39 #include "wx/fontmap.h"
42 #include "wx/config.h"
43 #include "wx/memconf.h"
47 #include "wx/fontutil.h"
48 #include "wx/msgdlg.h"
49 #include "wx/fontdlg.h"
50 #include "wx/choicdlg.h"
53 #include "wx/encconv.h"
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 // the config paths we use
61 static const wxChar
* FONTMAPPER_ROOT_PATH
= wxT("/wxWindows/FontMapper");
62 static const wxChar
* FONTMAPPER_CHARSET_PATH
= wxT("Charsets");
63 static const wxChar
* FONTMAPPER_CHARSET_ALIAS_PATH
= wxT("Aliases");
65 // we only ask questions in GUI mode
67 static const wxChar
* FONTMAPPER_FONT_FROM_ENCODING_PATH
= wxT("Encodings");
68 static const wxChar
* FONTMAPPER_FONT_DONT_ASK
= wxT("none");
70 #endif // wxUSE_CONFIG
72 // encodings supported by GetEncodingDescription
73 static wxFontEncoding gs_encodings
[] =
75 wxFONTENCODING_ISO8859_1
,
76 wxFONTENCODING_ISO8859_2
,
77 wxFONTENCODING_ISO8859_3
,
78 wxFONTENCODING_ISO8859_4
,
79 wxFONTENCODING_ISO8859_5
,
80 wxFONTENCODING_ISO8859_6
,
81 wxFONTENCODING_ISO8859_7
,
82 wxFONTENCODING_ISO8859_8
,
83 wxFONTENCODING_ISO8859_9
,
84 wxFONTENCODING_ISO8859_10
,
85 wxFONTENCODING_ISO8859_11
,
86 wxFONTENCODING_ISO8859_12
,
87 wxFONTENCODING_ISO8859_13
,
88 wxFONTENCODING_ISO8859_14
,
89 wxFONTENCODING_ISO8859_15
,
95 wxFONTENCODING_CP1250
,
96 wxFONTENCODING_CP1251
,
97 wxFONTENCODING_CP1252
,
98 wxFONTENCODING_CP1253
,
99 wxFONTENCODING_CP1254
,
100 wxFONTENCODING_CP1255
,
101 wxFONTENCODING_CP1256
,
102 wxFONTENCODING_CP1257
,
103 wxFONTENCODING_CP437
,
108 // the descriptions for them
109 static const wxChar
* gs_encodingDescs
[] =
111 wxTRANSLATE( "Western European (ISO-8859-1)" ),
112 wxTRANSLATE( "Central European (ISO-8859-2)" ),
113 wxTRANSLATE( "Esperanto (ISO-8859-3)" ),
114 wxTRANSLATE( "Baltic (old) (ISO-8859-4)" ),
115 wxTRANSLATE( "Cyrillic (ISO-8859-5)" ),
116 wxTRANSLATE( "Arabic (ISO-8859-6)" ),
117 wxTRANSLATE( "Greek (ISO-8859-7)" ),
118 wxTRANSLATE( "Hebrew (ISO-8859-8)" ),
119 wxTRANSLATE( "Turkish (ISO-8859-9)" ),
120 wxTRANSLATE( "Nordic (ISO-8859-10)" ),
121 wxTRANSLATE( "Thai (ISO-8859-11)" ),
122 wxTRANSLATE( "Indian (ISO-8859-12)" ),
123 wxTRANSLATE( "Baltic (ISO-8859-13)" ),
124 wxTRANSLATE( "Celtic (ISO-8859-14)" ),
125 wxTRANSLATE( "Western European with Euro (ISO-8859-15)" ),
126 wxTRANSLATE( "KOI8-R" ),
127 wxTRANSLATE( "Windows Japanese (CP 932)" ),
128 wxTRANSLATE( "Windows Chinese Simplified (CP 936)" ),
129 wxTRANSLATE( "Windows Korean (CP 949)" ),
130 wxTRANSLATE( "Windows Chinese Traditional (CP 950)" ),
131 wxTRANSLATE( "Windows Central European (CP 1250)" ),
132 wxTRANSLATE( "Windows Cyrillic (CP 1251)" ),
133 wxTRANSLATE( "Windows Western European (CP 1252)" ),
134 wxTRANSLATE( "Windows Greek (CP 1253)" ),
135 wxTRANSLATE( "Windows Turkish (CP 1254)" ),
136 wxTRANSLATE( "Windows Hebrew (CP 1255)" ),
137 wxTRANSLATE( "Windows Arabic (CP 1256)" ),
138 wxTRANSLATE( "Windows Baltic (CP 1257)" ),
139 wxTRANSLATE( "Windows/DOS OEM (CP 437)" ),
140 wxTRANSLATE( "Unicode 7 bit (UTF-7)" ),
141 wxTRANSLATE( "Unicode 8 bit (UTF-8)" ),
144 // and the internal names (these are not translated on purpose!)
145 static const wxChar
* gs_encodingNames
[] =
156 wxT( "iso-8859-10" ),
157 wxT( "iso-8859-11" ),
158 wxT( "iso-8859-12" ),
159 wxT( "iso-8859-13" ),
160 wxT( "iso-8859-14" ),
161 wxT( "iso-8859-15" ),
163 wxT( "windows-932" ),
164 wxT( "windows-936" ),
165 wxT( "windows-949" ),
166 wxT( "windows-950" ),
167 wxT( "windows-1250" ),
168 wxT( "windows-1251" ),
169 wxT( "windows-1252" ),
170 wxT( "windows-1253" ),
171 wxT( "windows-1254" ),
172 wxT( "windows-1255" ),
173 wxT( "windows-1256" ),
174 wxT( "windows-1257" ),
175 wxT( "windows-437" ),
180 // ----------------------------------------------------------------------------
182 // ----------------------------------------------------------------------------
185 static wxFontMapper gs_fontMapper
;
187 // and public pointer
188 wxFontMapper
* wxTheFontMapper
= &gs_fontMapper
;
190 // ----------------------------------------------------------------------------
192 // ----------------------------------------------------------------------------
194 // change the config path during the lifetime of this object
195 class wxFontMapperPathChanger
198 wxFontMapperPathChanger(wxFontMapper
*fontMapper
, const wxString
& path
)
200 m_fontMapper
= fontMapper
;
201 m_ok
= m_fontMapper
->ChangePath(path
, &m_pathOld
);
204 bool IsOk() const { return m_ok
; }
206 ~wxFontMapperPathChanger()
209 m_fontMapper
->RestorePath(m_pathOld
);
213 wxFontMapper
*m_fontMapper
;
218 // ============================================================================
220 // ============================================================================
222 // ----------------------------------------------------------------------------
224 // ----------------------------------------------------------------------------
226 wxFontMapper::wxFontMapper()
230 m_configIsDummy
= FALSE
;
231 #endif // wxUSE_CONFIG
234 m_windowParent
= NULL
;
238 wxFontMapper::~wxFontMapper()
242 // ----------------------------------------------------------------------------
244 // ----------------------------------------------------------------------------
248 /* static */ const wxChar
*wxFontMapper::GetDefaultConfigPath()
250 return FONTMAPPER_ROOT_PATH
;
253 void wxFontMapper::SetConfigPath(const wxString
& prefix
)
255 wxCHECK_RET( !prefix
.IsEmpty() && prefix
[0] == wxCONFIG_PATH_SEPARATOR
,
256 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
258 m_configRootPath
= prefix
;
261 // ----------------------------------------------------------------------------
262 // get config object and path for it
263 // ----------------------------------------------------------------------------
265 wxConfigBase
*wxFontMapper::GetConfig()
270 m_config
= wxConfig::Get(FALSE
/*don't create on demand*/ );
274 // we still want to have a config object because otherwise we would
275 // keep asking the user the same questions in the interactive mode,
276 // so create a dummy config which won't write to any files/registry
277 // but will allow us to remember the results of the questions at
278 // least during this run
279 m_config
= new wxMemoryConfig
;
280 m_configIsDummy
= TRUE
;
281 // VS: we can't call wxConfig::Set(m_config) here because that would
282 // disable automatic wxConfig instance creation if this code was
283 // called before wxApp::OnInit (this happens in wxGTK -- it sets
284 // default wxFont encoding in wxApp::Initialize())
288 if ( m_configIsDummy
&& wxConfig::Get(FALSE
) != NULL
)
290 // VS: in case we created dummy m_config (see above), we want to switch back
291 // to the real one as soon as one becomes available.
292 m_config
= wxConfig::Get(FALSE
);
293 m_configIsDummy
= FALSE
;
294 // FIXME: ideally, we should add keys from dummy config to the real one now,
295 // but it is a low-priority task because typical wxWin application
296 // either doesn't use wxConfig at all or creates wxConfig object in
297 // wxApp::OnInit(), before any real interaction with the user takes
304 const wxString
& wxFontMapper::GetConfigPath()
306 if ( !m_configRootPath
)
309 m_configRootPath
= GetDefaultConfigPath();
312 return m_configRootPath
;
316 bool wxFontMapper::ChangePath(const wxString
& pathNew
, wxString
*pathOld
)
319 wxConfigBase
*config
= GetConfig();
323 *pathOld
= config
->GetPath();
325 wxString path
= GetConfigPath();
326 if ( path
.IsEmpty() || path
.Last() != wxCONFIG_PATH_SEPARATOR
)
328 path
+= wxCONFIG_PATH_SEPARATOR
;
331 wxASSERT_MSG( !pathNew
|| (pathNew
[0] != wxCONFIG_PATH_SEPARATOR
),
332 wxT("should be a relative path") );
336 config
->SetPath(path
);
344 void wxFontMapper::RestorePath(const wxString
& pathOld
)
347 GetConfig()->SetPath(pathOld
);
352 // ----------------------------------------------------------------------------
353 // charset/encoding correspondence
354 // ----------------------------------------------------------------------------
357 wxString
wxFontMapper::GetEncodingDescription(wxFontEncoding encoding
)
359 if ( encoding
== wxFONTENCODING_DEFAULT
)
361 return _("Default encoding");
364 size_t count
= WXSIZEOF(gs_encodingDescs
);
366 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
367 wxT("inconsitency detected - forgot to update one of the arrays?") );
369 for ( size_t i
= 0; i
< count
; i
++ )
371 if ( gs_encodings
[i
] == encoding
)
373 return wxGetTranslation(gs_encodingDescs
[i
]);
378 str
.Printf(_("Unknown encoding (%d)"), encoding
);
384 wxString
wxFontMapper::GetEncodingName(wxFontEncoding encoding
)
386 if ( encoding
== wxFONTENCODING_DEFAULT
)
391 size_t count
= WXSIZEOF(gs_encodingNames
);
393 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
394 wxT("inconsistency detected - forgot to update one of the arrays?") );
396 for ( size_t i
= 0; i
< count
; i
++ )
398 if ( gs_encodings
[i
] == encoding
)
400 return gs_encodingNames
[i
];
405 str
.Printf(_("unknown-%d"), encoding
);
410 wxFontEncoding
wxFontMapper::CharsetToEncoding(const wxString
& charset
,
413 // a special pseudo encoding which means "don't ask me about this charset
414 // any more" - we need it to avoid driving the user crazy with asking him
415 // time after time about the same charset which he [presumably] doesn't
416 // have the fonts fot
417 static const int wxFONTENCODING_UNKNOWN
= -2;
419 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
421 // we're going to modify it, make a copy
422 wxString cs
= charset
;
425 // first try the user-defined settings
427 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
429 wxConfigBase
*config
= GetConfig();
431 // do we have an encoding for this charset?
432 long value
= config
->Read(charset
, -1l);
435 if ( value
== wxFONTENCODING_UNKNOWN
)
437 // don't try to find it, in particular don't ask the user
438 return wxFONTENCODING_SYSTEM
;
441 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
443 encoding
= (wxFontEncoding
)value
;
447 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
448 value
, charset
.c_str());
452 if ( encoding
== wxFONTENCODING_SYSTEM
)
454 // may be we have an alias?
455 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
457 wxString alias
= config
->Read(charset
);
460 // yes, we do - use it instead
465 RestorePath(pathOld
);
467 #endif // wxUSE_CONFIG
469 // if didn't find it there, try to recognize it ourselves
470 if ( encoding
== wxFONTENCODING_SYSTEM
)
476 // discard the optional quotes
479 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
481 cs
= wxString(cs
.c_str(), cs
.length() - 1);
487 if ( !cs
|| cs
== wxT("US-ASCII") )
489 encoding
= wxFONTENCODING_DEFAULT
;
491 else if ( cs
== wxT("UTF-7") )
493 encoding
= wxFONTENCODING_UTF7
;
495 else if ( cs
== wxT("UTF-8") )
497 encoding
= wxFONTENCODING_UTF8
;
499 else if ( cs
== wxT("KOI8-R") ||
500 cs
== wxT("KOI8-U") ||
501 cs
== wxT("KOI8-RU") )
503 // although koi8-ru is not strictly speaking the same as koi8-r,
504 // they are similar enough to make mapping it to koi8 better than
505 // not reckognizing it at all
506 encoding
= wxFONTENCODING_KOI8
;
508 else if ( cs
.Left(3) == wxT("ISO") )
510 // the dash is optional (or, to be exact, it is not, but
511 // several brokenmails "forget" it)
512 const wxChar
*p
= cs
.c_str() + 3;
513 if ( *p
== wxT('-') )
517 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
519 // make it 0 based and check that it is strictly positive in
520 // the process (no such thing as iso8859-0 encoding)
521 if ( (value
-- > 0) &&
522 (value
< wxFONTENCODING_ISO8859_MAX
-
523 wxFONTENCODING_ISO8859_1
) )
525 // it's a valid ISO8859 encoding
526 value
+= wxFONTENCODING_ISO8859_1
;
527 encoding
= (wxFontEncoding
)value
;
531 else // check for Windows charsets
534 if ( cs
.Left(7) == wxT("WINDOWS") )
538 else if ( cs
.Left(2) == wxT("CP") )
542 else // not a Windows encoding
549 const wxChar
*p
= cs
.c_str() + len
;
550 if ( *p
== wxT('-') )
554 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
559 if ( value
< wxFONTENCODING_CP12_MAX
-
560 wxFONTENCODING_CP1250
)
562 // a valid Windows code page
563 value
+= wxFONTENCODING_CP1250
;
564 encoding
= (wxFontEncoding
)value
;
571 encoding
= wxFONTENCODING_CP932
;
575 encoding
= wxFONTENCODING_CP936
;
579 encoding
= wxFONTENCODING_CP949
;
583 encoding
= wxFONTENCODING_CP950
;
593 // if still no luck, ask the user - unless disabled
594 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
596 // prepare the dialog data
599 wxString
title(m_titleDialog
);
601 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
605 msg
.Printf(_("The charset '%s' is unknown. You may select\nanother charset to replace it with or choose\n[Cancel] if it cannot be replaced"), charset
.c_str());
607 // the list of choices
608 size_t count
= WXSIZEOF(gs_encodingDescs
);
610 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
611 wxT("inconsitency detected - forgot to update one of the arrays?") );
613 wxString
*encodingNamesTranslated
= new wxString
[count
];
615 for ( size_t i
= 0; i
< count
; i
++ )
617 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
621 wxWindow
*parent
= m_windowParent
;
623 parent
= wxTheApp
->GetTopWindow();
625 // do ask the user and get back the index in encodings table
626 int n
= wxGetSingleChoiceIndex(msg
, title
,
628 encodingNamesTranslated
,
631 delete [] encodingNamesTranslated
;
635 encoding
= gs_encodings
[n
];
639 // save the result in the config now
640 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
642 wxConfigBase
*config
= GetConfig();
644 // remember the alt encoding for this charset - or remember that
646 long value
= n
== -1 ? wxFONTENCODING_UNKNOWN
: (long)encoding
;
647 if ( !config
->Write(charset
, value
) )
649 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
652 RestorePath(pathOld
);
654 #endif // wxUSE_CONFIG
661 // ----------------------------------------------------------------------------
662 // support for unknown encodings: we maintain a map between the
663 // (platform-specific) strings identifying them and our wxFontEncodings they
664 // correspond to which is used by GetFontForEncoding() function
665 // ----------------------------------------------------------------------------
669 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
670 wxFontEncoding encReplacement
,
671 wxNativeEncodingInfo
*info
)
673 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
674 wxTestFontEncoding(*info
) )
677 // remember the mapping in the config
678 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
682 GetConfig()->Write(configEntry
, info
->ToString());
684 #endif // wxUSE_CONFIG
692 class ReentrancyBlocker
695 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
696 ~ReentrancyBlocker() { m_b
= FALSE
; }
703 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
704 wxNativeEncodingInfo
*info
,
705 const wxString
& facename
,
709 // we need a flag to prevent infinite recursion which happens, for
710 // example, when GetAltForEncoding() is called from an OnPaint() handler:
711 // in this case, wxYield() which is called from wxMessageBox() we use here
712 // will lead to another call of OnPaint() and hence to another call of
713 // GetAltForEncoding() - and it is impossible to catch this from the user
714 // code because we are called from wxFont ctor implicitly.
716 // assume we're always called from the main thread, so that it is safe to
718 static bool s_inGetAltForEncoding
= FALSE
;
720 if ( interactive
&& s_inGetAltForEncoding
)
723 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
726 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
728 info
->facename
= facename
;
730 if ( encoding
== wxFONTENCODING_DEFAULT
)
732 encoding
= wxFont::GetDefaultEncoding();
735 // if we failed to load the system default encoding, something is really
736 // wrong and we'd better stop now - otherwise we will go into endless
737 // recursion trying to create the font in the msg box with the error
739 if ( encoding
== wxFONTENCODING_SYSTEM
)
741 wxFatalError(_("can't load any font, aborting"));
743 // wxFatalError doesn't return
746 wxString configEntry
,
747 encName
= GetEncodingName(encoding
);
750 configEntry
= facename
+ _T("_");
752 configEntry
+= encName
;
755 // do we have a font spec for this encoding?
757 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
759 wxConfigBase
*config
= GetConfig();
761 wxString fontinfo
= config
->Read(configEntry
);
763 RestorePath(pathOld
);
765 // this special value means that we don't know of fonts for this
766 // encoding but, moreover, have already asked the user as well and he
767 // didn't specify any font neither
768 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
772 else // use the info entered the last time
774 if ( !!fontinfo
&& !!facename
)
776 // we tried to find a match with facename - now try without it
777 fontinfo
= config
->Read(encName
);
782 if ( info
->FromString(fontinfo
) )
784 if ( wxTestFontEncoding(*info
) )
789 //else: no such fonts, look for something else
790 // (should we erase the outdated value?)
794 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
798 //else: there is no information in config about this encoding
801 #endif // wxUSE_CONFIG
807 wxString
title(m_titleDialog
);
809 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
813 msg
.Printf(_("No font for displaying text in encoding '%s' found.\nWould you like to select a font to be used for this encoding\n(otherwise the text in this encoding will not be shown correctly)?"),
814 GetEncodingDescription(encoding
).c_str());
816 wxWindow
*parent
= m_windowParent
;
818 parent
= wxTheApp
->GetTopWindow();
820 if ( wxMessageBox(msg
, title
,
821 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
824 data
.SetEncoding(encoding
);
825 data
.EncodingInfo() = *info
;
826 wxFontDialog
dialog(parent
, &data
);
827 if ( dialog
.ShowModal() == wxID_OK
)
829 wxFontData retData
= dialog
.GetFontData();
830 wxFont font
= retData
.GetChosenFont();
832 *info
= retData
.EncodingInfo();
833 info
-> encoding
= retData
.GetEncoding();
836 // remember this in the config
837 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
839 GetConfig()->Write(configEntry
, info
->ToString());
841 RestorePath(pathOld
);
843 #endif // wxUSE_CONFIG
847 //else: the user canceled the font selection dialog
851 // the user doesn't want to select a font for this encoding,
852 // remember it to avoid asking the same question again later
854 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
856 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
858 RestorePath(pathOld
);
860 #endif // wxUSE_CONFIG
863 //else: we're in non-interactive mode
864 #endif // wxUSE_FONTDLG
866 // now try the default mappings:
867 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
868 size_t count
= equiv
.GetCount();
871 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
873 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
881 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
882 wxFontEncoding
*alt_encoding
,
883 const wxString
& facename
,
886 wxNativeEncodingInfo info
;
887 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
888 *alt_encoding
= info
.encoding
;
892 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
893 const wxString
& facename
)
895 wxNativeEncodingInfo info
;
897 if (wxGetNativeFontEncoding(encoding
, &info
))
899 info
.facename
= facename
;
900 return wxTestFontEncoding(info
);
908 #endif // wxUSE_FONTMAP