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
,
91 wxFONTENCODING_CP1250
,
92 wxFONTENCODING_CP1251
,
93 wxFONTENCODING_CP1252
,
94 wxFONTENCODING_CP1253
,
95 wxFONTENCODING_CP1254
,
96 wxFONTENCODING_CP1255
,
97 wxFONTENCODING_CP1256
,
98 wxFONTENCODING_CP1257
,
104 // the descriptions for them
105 static const wxChar
* gs_encodingDescs
[] =
107 wxTRANSLATE( "Western European (ISO-8859-1)" ),
108 wxTRANSLATE( "Central European (ISO-8859-2)" ),
109 wxTRANSLATE( "Esperanto (ISO-8859-3)" ),
110 wxTRANSLATE( "Baltic (old) (ISO-8859-4)" ),
111 wxTRANSLATE( "Cyrillic (ISO-8859-5)" ),
112 wxTRANSLATE( "Arabic (ISO-8859-6)" ),
113 wxTRANSLATE( "Greek (ISO-8859-7)" ),
114 wxTRANSLATE( "Hebrew (ISO-8859-8)" ),
115 wxTRANSLATE( "Turkish (ISO-8859-9)" ),
116 wxTRANSLATE( "Nordic (ISO-8859-10)" ),
117 wxTRANSLATE( "Thai (ISO-8859-11)" ),
118 wxTRANSLATE( "Indian (ISO-8859-12)" ),
119 wxTRANSLATE( "Baltic (ISO-8859-13)" ),
120 wxTRANSLATE( "Celtic (ISO-8859-14)" ),
121 wxTRANSLATE( "Western European with Euro (ISO-8859-15)" ),
122 wxTRANSLATE( "KOI8-R" ),
123 wxTRANSLATE( "Windows Central European (CP 1250)" ),
124 wxTRANSLATE( "Windows Cyrillic (CP 1251)" ),
125 wxTRANSLATE( "Windows Western European (CP 1252)" ),
126 wxTRANSLATE( "Windows Greek (CP 1253)" ),
127 wxTRANSLATE( "Windows Turkish (CP 1254)" ),
128 wxTRANSLATE( "Windows Hebrew (CP 1255)" ),
129 wxTRANSLATE( "Windows Arabic (CP 1256)" ),
130 wxTRANSLATE( "Windows Baltic (CP 1257)" ),
131 wxTRANSLATE( "Windows/DOS OEM (CP 437)" ),
132 wxTRANSLATE( "Unicode 7 bit (UTF-7)" ),
133 wxTRANSLATE( "Unicode 8 bit (UTF-8)" ),
136 // and the internal names
137 static const wxChar
* gs_encodingNames
[] =
148 wxT( "iso-8859-10" ),
149 wxT( "iso-8859-11" ),
150 wxT( "iso-8859-12" ),
151 wxT( "iso-8859-13" ),
152 wxT( "iso-8859-14" ),
153 wxT( "iso-8859-15" ),
155 wxT( "windows-1250" ),
156 wxT( "windows-1251" ),
157 wxT( "windows-1252" ),
158 wxT( "windows-1253" ),
159 wxT( "windows-1254" ),
160 wxT( "windows-1255" ),
161 wxT( "windows-1256" ),
162 wxT( "windows-1257" ),
163 wxT( "windows-437" ),
168 // ----------------------------------------------------------------------------
170 // ----------------------------------------------------------------------------
173 static wxFontMapper gs_fontMapper
;
175 // and public pointer
176 wxFontMapper
* wxTheFontMapper
= &gs_fontMapper
;
178 // ----------------------------------------------------------------------------
180 // ----------------------------------------------------------------------------
182 // change the config path during the lifetime of this object
183 class wxFontMapperPathChanger
186 wxFontMapperPathChanger(wxFontMapper
*fontMapper
, const wxString
& path
)
188 m_fontMapper
= fontMapper
;
189 m_ok
= m_fontMapper
->ChangePath(path
, &m_pathOld
);
192 bool IsOk() const { return m_ok
; }
194 ~wxFontMapperPathChanger()
197 m_fontMapper
->RestorePath(m_pathOld
);
201 wxFontMapper
*m_fontMapper
;
206 // ============================================================================
208 // ============================================================================
210 // ----------------------------------------------------------------------------
212 // ----------------------------------------------------------------------------
214 wxFontMapper::wxFontMapper()
218 #endif // wxUSE_CONFIG
221 m_windowParent
= NULL
;
225 wxFontMapper::~wxFontMapper()
229 // ----------------------------------------------------------------------------
231 // ----------------------------------------------------------------------------
235 /* static */ const wxChar
*wxFontMapper::GetDefaultConfigPath()
237 return FONTMAPPER_ROOT_PATH
;
240 void wxFontMapper::SetConfigPath(const wxString
& prefix
)
242 wxCHECK_RET( !prefix
.IsEmpty() && prefix
[0] == wxCONFIG_PATH_SEPARATOR
,
243 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
245 m_configRootPath
= prefix
;
248 // ----------------------------------------------------------------------------
249 // get config object and path for it
250 // ----------------------------------------------------------------------------
252 wxConfigBase
*wxFontMapper::GetConfig()
257 m_config
= wxConfig::Get(FALSE
/*don't create on demand*/ );
261 // we still want to have a config object because otherwise we would
262 // keep asking the user the same questions in the interactive mode,
263 // so create a dummy config which won't write to any files/registry
264 // but will allow us to remember the results of the questions at
265 // least during this run
266 m_config
= new wxMemoryConfig
;
267 wxConfig::Set(m_config
);
274 const wxString
& wxFontMapper::GetConfigPath()
276 if ( !m_configRootPath
)
279 m_configRootPath
= GetDefaultConfigPath();
282 return m_configRootPath
;
286 bool wxFontMapper::ChangePath(const wxString
& pathNew
, wxString
*pathOld
)
289 wxConfigBase
*config
= GetConfig();
293 *pathOld
= config
->GetPath();
295 wxString path
= GetConfigPath();
296 if ( path
.IsEmpty() || path
.Last() != wxCONFIG_PATH_SEPARATOR
)
298 path
+= wxCONFIG_PATH_SEPARATOR
;
301 wxASSERT_MSG( !pathNew
|| (pathNew
[0] != wxCONFIG_PATH_SEPARATOR
),
302 wxT("should be a relative path") );
306 config
->SetPath(path
);
314 void wxFontMapper::RestorePath(const wxString
& pathOld
)
317 GetConfig()->SetPath(pathOld
);
322 // ----------------------------------------------------------------------------
323 // charset/encoding correspondence
324 // ----------------------------------------------------------------------------
327 wxString
wxFontMapper::GetEncodingDescription(wxFontEncoding encoding
)
329 if ( encoding
== wxFONTENCODING_DEFAULT
)
331 return _("Default encoding");
334 size_t count
= WXSIZEOF(gs_encodingDescs
);
336 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
337 wxT("inconsitency detected - forgot to update one of the arrays?") );
339 for ( size_t i
= 0; i
< count
; i
++ )
341 if ( gs_encodings
[i
] == encoding
)
343 return wxGetTranslation(gs_encodingDescs
[i
]);
348 str
.Printf(_("Unknown encoding (%d)"), encoding
);
354 wxString
wxFontMapper::GetEncodingName(wxFontEncoding encoding
)
356 if ( encoding
== wxFONTENCODING_DEFAULT
)
361 size_t count
= WXSIZEOF(gs_encodingNames
);
363 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
364 wxT("inconsistency detected - forgot to update one of the arrays?") );
366 for ( size_t i
= 0; i
< count
; i
++ )
368 if ( gs_encodings
[i
] == encoding
)
370 return wxGetTranslation(gs_encodingNames
[i
]);
375 str
.Printf(_("unknown-%d"), encoding
);
380 wxFontEncoding
wxFontMapper::CharsetToEncoding(const wxString
& charset
,
383 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
385 // we're going to modify it, make a copy
386 wxString cs
= charset
;
389 // first try the user-defined settings
391 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
393 wxConfigBase
*config
= GetConfig();
395 // do we have an encoding for this charset?
396 long value
= config
->Read(charset
, -1l);
399 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
401 encoding
= (wxFontEncoding
)value
;
405 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
406 value
, charset
.c_str());
410 if ( encoding
== wxFONTENCODING_SYSTEM
)
412 // may be we have an alias?
413 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
415 wxString alias
= config
->Read(charset
);
418 // yes, we do - use it instead
423 RestorePath(pathOld
);
427 // if didn't find it there, try to recognize it ourselves
428 if ( encoding
== wxFONTENCODING_SYSTEM
)
434 // discard the optional quotes
437 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
439 cs
= wxString(cs
.c_str(), cs
.length() - 1);
445 if ( !cs
|| cs
== wxT("US-ASCII") )
447 encoding
= wxFONTENCODING_DEFAULT
;
449 else if ( cs
== wxT("UTF-7") )
451 encoding
= wxFONTENCODING_UTF7
;
453 else if ( cs
== wxT("UTF-8") )
455 encoding
= wxFONTENCODING_UTF8
;
457 else if ( cs
== wxT("KOI8-R") ||
458 cs
== wxT("KOI8-U") ||
459 cs
== wxT("KOI8-RU") )
461 // although koi8-ru is not strictly speaking the same as koi8-r,
462 // they are similar enough to make mapping it to koi8 better than
463 // not reckognizing it at all
464 encoding
= wxFONTENCODING_KOI8
;
466 else if ( cs
.Left(3) == wxT("ISO") )
468 // the dash is optional (or, to be exact, it is not, but
469 // several brokenmails "forget" it)
470 const wxChar
*p
= cs
.c_str() + 3;
471 if ( *p
== wxT('-') )
475 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
477 // make it 0 based and check that it is strictly positive in
478 // the process (no such thing as iso8859-0 encoding)
479 if ( (value
-- > 0) &&
480 (value
< wxFONTENCODING_ISO8859_MAX
-
481 wxFONTENCODING_ISO8859_1
) )
483 // it's a valid ISO8859 encoding
484 value
+= wxFONTENCODING_ISO8859_1
;
485 encoding
= (wxFontEncoding
)value
;
489 else // check for Windows charsets
492 if ( cs
.Left(7) == wxT("WINDOWS") )
496 else if ( cs
.Left(2) == wxT("CP") )
500 else // not a Windows encoding
507 const wxChar
*p
= cs
.c_str() + len
;
508 if ( *p
== wxT('-') )
512 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
517 if ( value
< wxFONTENCODING_CP12_MAX
-
518 wxFONTENCODING_CP1250
)
520 // a valid Windows code page
521 value
+= wxFONTENCODING_CP1250
;
522 encoding
= (wxFontEncoding
)value
;
532 // if still no luck, ask the user - unless disabled
533 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
535 // prepare the dialog data
538 wxString
title(m_titleDialog
);
540 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
544 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());
546 // the list of choices
547 size_t count
= WXSIZEOF(gs_encodingDescs
);
549 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
550 wxT("inconsitency detected - forgot to update one of the arrays?") );
552 wxString
*encodingNamesTranslated
= new wxString
[count
];
554 for ( size_t i
= 0; i
< count
; i
++ )
556 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
560 wxWindow
*parent
= m_windowParent
;
562 parent
= wxTheApp
->GetTopWindow();
564 // do ask the user and get back the index in encodings table
565 int n
= wxGetSingleChoiceIndex(msg
, title
,
567 encodingNamesTranslated
,
570 delete [] encodingNamesTranslated
;
574 encoding
= gs_encodings
[n
];
577 // save the result in the config now
578 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
580 wxConfigBase
*config
= GetConfig();
582 // remember the alt encoding for this charset
583 if ( !config
->Write(charset
, (long)encoding
) )
585 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
588 RestorePath(pathOld
);
590 #endif // wxUSE_CONFIG
599 // ----------------------------------------------------------------------------
600 // support for unknown encodings: we maintain a map between the
601 // (platform-specific) strings identifying them and our wxFontEncodings they
602 // correspond to which is used by GetFontForEncoding() function
603 // ----------------------------------------------------------------------------
607 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
608 wxFontEncoding encReplacement
,
609 wxNativeEncodingInfo
*info
)
611 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
612 wxTestFontEncoding(*info
) )
615 // remember the mapping in the config
616 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
620 GetConfig()->Write(configEntry
, info
->ToString());
622 #endif // wxUSE_CONFIG
630 class ReentrancyBlocker
633 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
634 ~ReentrancyBlocker() { m_b
= FALSE
; }
641 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
642 wxNativeEncodingInfo
*info
,
643 const wxString
& facename
,
647 // we need a flag to prevent infinite recursion which happens, for
648 // example, when GetAltForEncoding() is called from an OnPaint() handler:
649 // in this case, wxYield() which is called from wxMessageBox() we use here
650 // will lead to another call of OnPaint() and hence to another call of
651 // GetAltForEncoding() - and it is impossible to catch this from the user
652 // code because we are called from wxFont ctor implicitly.
654 // assume we're always called from the main thread, so that it is safe to
656 static bool s_inGetAltForEncoding
= FALSE
;
658 if ( interactive
&& s_inGetAltForEncoding
)
661 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
664 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
666 info
->facename
= facename
;
668 if ( encoding
== wxFONTENCODING_DEFAULT
)
670 encoding
= wxFont::GetDefaultEncoding();
673 // if we failed to load the system default encoding, something is really
674 // wrong and we'd better stop now - otherwise we will go into endless
675 // recursion trying to create the font in the msg box with the error
677 if ( encoding
== wxFONTENCODING_SYSTEM
)
679 wxFatalError(_("can't load any font, aborting"));
681 // wxFatalError doesn't return
684 wxString configEntry
, encName
= GetEncodingName(encoding
);
687 configEntry
= facename
+ _T("_");
689 configEntry
+= encName
;
692 // do we have a font spec for this encoding?
694 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
696 wxConfigBase
*config
= GetConfig();
698 wxString fontinfo
= config
->Read(configEntry
);
700 RestorePath(pathOld
);
702 // this special value means that we don't know of fonts for this
703 // encoding but, moreover, have already asked the user as well and he
704 // didn't specify any font neither
705 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
709 else // use the info entered the last time
711 if ( !!fontinfo
&& !!facename
)
713 // we tried to find a match with facename - now try without it
714 fontinfo
= config
->Read(encName
);
719 if ( info
->FromString(fontinfo
) )
721 if ( wxTestFontEncoding(*info
) )
726 //else: no such fonts, look for something else
727 // (should we erase the outdated value?)
731 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
735 //else: there is no information in config about this encoding
738 #endif // wxUSE_CONFIG
744 wxString
title(m_titleDialog
);
746 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
750 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)?"),
751 GetEncodingDescription(encoding
).c_str());
753 wxWindow
*parent
= m_windowParent
;
755 parent
= wxTheApp
->GetTopWindow();
757 if ( wxMessageBox(msg
, title
,
758 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
761 data
.SetEncoding(encoding
);
762 data
.EncodingInfo() = *info
;
763 wxFontDialog
dialog(parent
, &data
);
764 if ( dialog
.ShowModal() == wxID_OK
)
766 wxFontData retData
= dialog
.GetFontData();
767 wxFont font
= retData
.GetChosenFont();
769 *info
= retData
.EncodingInfo();
770 info
-> encoding
= retData
.GetEncoding();
773 // remember this in the config
774 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
776 GetConfig()->Write(configEntry
, info
->ToString());
778 RestorePath(pathOld
);
780 #endif // wxUSE_CONFIG
784 //else: the user canceled the font selection dialog
788 // the user doesn't want to select a font for this encoding,
789 // remember it to avoid asking the same question again later
791 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
793 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
795 RestorePath(pathOld
);
797 #endif // wxUSE_CONFIG
800 //else: we're in non-interactive mode
801 #endif // wxUSE_FONTDLG
803 // now try the default mappings:
804 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
805 size_t count
= equiv
.GetCount();
808 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
810 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
818 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
819 wxFontEncoding
*alt_encoding
,
820 const wxString
& facename
,
823 wxNativeEncodingInfo info
;
824 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
825 *alt_encoding
= info
.encoding
;
829 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
830 const wxString
& facename
)
832 wxNativeEncodingInfo info
;
834 if (wxGetNativeFontEncoding(encoding
, &info
))
836 info
.facename
= facename
;
837 return wxTestFontEncoding(info
);
845 #endif // wxUSE_FONTMAP