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
;
574 // if still no luck, ask the user - unless disabled
575 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
577 // prepare the dialog data
580 wxString
title(m_titleDialog
);
582 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
586 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());
588 // the list of choices
589 size_t count
= WXSIZEOF(gs_encodingDescs
);
591 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
592 wxT("inconsitency detected - forgot to update one of the arrays?") );
594 wxString
*encodingNamesTranslated
= new wxString
[count
];
596 for ( size_t i
= 0; i
< count
; i
++ )
598 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
602 wxWindow
*parent
= m_windowParent
;
604 parent
= wxTheApp
->GetTopWindow();
606 // do ask the user and get back the index in encodings table
607 int n
= wxGetSingleChoiceIndex(msg
, title
,
609 encodingNamesTranslated
,
612 delete [] encodingNamesTranslated
;
616 encoding
= gs_encodings
[n
];
620 // save the result in the config now
621 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
623 wxConfigBase
*config
= GetConfig();
625 // remember the alt encoding for this charset - or remember that
627 long value
= n
== -1 ? wxFONTENCODING_UNKNOWN
: (long)encoding
;
628 if ( !config
->Write(charset
, value
) )
630 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
633 RestorePath(pathOld
);
635 #endif // wxUSE_CONFIG
642 // ----------------------------------------------------------------------------
643 // support for unknown encodings: we maintain a map between the
644 // (platform-specific) strings identifying them and our wxFontEncodings they
645 // correspond to which is used by GetFontForEncoding() function
646 // ----------------------------------------------------------------------------
650 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
651 wxFontEncoding encReplacement
,
652 wxNativeEncodingInfo
*info
)
654 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
655 wxTestFontEncoding(*info
) )
658 // remember the mapping in the config
659 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
663 GetConfig()->Write(configEntry
, info
->ToString());
665 #endif // wxUSE_CONFIG
673 class ReentrancyBlocker
676 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
677 ~ReentrancyBlocker() { m_b
= FALSE
; }
684 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
685 wxNativeEncodingInfo
*info
,
686 const wxString
& facename
,
690 // we need a flag to prevent infinite recursion which happens, for
691 // example, when GetAltForEncoding() is called from an OnPaint() handler:
692 // in this case, wxYield() which is called from wxMessageBox() we use here
693 // will lead to another call of OnPaint() and hence to another call of
694 // GetAltForEncoding() - and it is impossible to catch this from the user
695 // code because we are called from wxFont ctor implicitly.
697 // assume we're always called from the main thread, so that it is safe to
699 static bool s_inGetAltForEncoding
= FALSE
;
701 if ( interactive
&& s_inGetAltForEncoding
)
704 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
707 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
709 info
->facename
= facename
;
711 if ( encoding
== wxFONTENCODING_DEFAULT
)
713 encoding
= wxFont::GetDefaultEncoding();
716 // if we failed to load the system default encoding, something is really
717 // wrong and we'd better stop now - otherwise we will go into endless
718 // recursion trying to create the font in the msg box with the error
720 if ( encoding
== wxFONTENCODING_SYSTEM
)
722 wxFatalError(_("can't load any font, aborting"));
724 // wxFatalError doesn't return
727 wxString configEntry
,
728 encName
= GetEncodingName(encoding
);
731 configEntry
= facename
+ _T("_");
733 configEntry
+= encName
;
736 // do we have a font spec for this encoding?
738 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
740 wxConfigBase
*config
= GetConfig();
742 wxString fontinfo
= config
->Read(configEntry
);
744 RestorePath(pathOld
);
746 // this special value means that we don't know of fonts for this
747 // encoding but, moreover, have already asked the user as well and he
748 // didn't specify any font neither
749 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
753 else // use the info entered the last time
755 if ( !!fontinfo
&& !!facename
)
757 // we tried to find a match with facename - now try without it
758 fontinfo
= config
->Read(encName
);
763 if ( info
->FromString(fontinfo
) )
765 if ( wxTestFontEncoding(*info
) )
770 //else: no such fonts, look for something else
771 // (should we erase the outdated value?)
775 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
779 //else: there is no information in config about this encoding
782 #endif // wxUSE_CONFIG
788 wxString
title(m_titleDialog
);
790 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
794 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)?"),
795 GetEncodingDescription(encoding
).c_str());
797 wxWindow
*parent
= m_windowParent
;
799 parent
= wxTheApp
->GetTopWindow();
801 if ( wxMessageBox(msg
, title
,
802 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
805 data
.SetEncoding(encoding
);
806 data
.EncodingInfo() = *info
;
807 wxFontDialog
dialog(parent
, &data
);
808 if ( dialog
.ShowModal() == wxID_OK
)
810 wxFontData retData
= dialog
.GetFontData();
811 wxFont font
= retData
.GetChosenFont();
813 *info
= retData
.EncodingInfo();
814 info
-> encoding
= retData
.GetEncoding();
817 // remember this in the config
818 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
820 GetConfig()->Write(configEntry
, info
->ToString());
822 RestorePath(pathOld
);
824 #endif // wxUSE_CONFIG
828 //else: the user canceled the font selection dialog
832 // the user doesn't want to select a font for this encoding,
833 // remember it to avoid asking the same question again later
835 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
837 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
839 RestorePath(pathOld
);
841 #endif // wxUSE_CONFIG
844 //else: we're in non-interactive mode
845 #endif // wxUSE_FONTDLG
847 // now try the default mappings:
848 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
849 size_t count
= equiv
.GetCount();
852 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
854 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
862 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
863 wxFontEncoding
*alt_encoding
,
864 const wxString
& facename
,
867 wxNativeEncodingInfo info
;
868 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
869 *alt_encoding
= info
.encoding
;
873 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
874 const wxString
& facename
)
876 wxNativeEncodingInfo info
;
878 if (wxGetNativeFontEncoding(encoding
, &info
))
880 info
.facename
= facename
;
881 return wxTestFontEncoding(info
);
889 #endif // wxUSE_FONTMAP