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"
37 #include "wx/fontmap.h"
40 #include "wx/config.h"
41 #include "wx/memconf.h"
45 #include "wx/fontutil.h"
46 #include "wx/msgdlg.h"
47 #include "wx/fontdlg.h"
48 #include "wx/choicdlg.h"
51 #include "wx/encconv.h"
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 // the config paths we use
58 static const wxChar
* FONTMAPPER_ROOT_PATH
= wxT("/wxWindows/FontMapper");
59 static const wxChar
* FONTMAPPER_CHARSET_PATH
= wxT("Charsets");
60 static const wxChar
* FONTMAPPER_CHARSET_ALIAS_PATH
= wxT("Aliases");
62 // we only ask questions in GUI mode
64 static const wxChar
* FONTMAPPER_FONT_FROM_ENCODING_PATH
= wxT("Encodings");
65 static const wxChar
* FONTMAPPER_FONT_DONT_ASK
= wxT("none");
68 // encodings supported by GetEncodingDescription
69 static wxFontEncoding gs_encodings
[] =
71 wxFONTENCODING_ISO8859_1
,
72 wxFONTENCODING_ISO8859_2
,
73 wxFONTENCODING_ISO8859_3
,
74 wxFONTENCODING_ISO8859_4
,
75 wxFONTENCODING_ISO8859_5
,
76 wxFONTENCODING_ISO8859_6
,
77 wxFONTENCODING_ISO8859_7
,
78 wxFONTENCODING_ISO8859_8
,
79 wxFONTENCODING_ISO8859_9
,
80 wxFONTENCODING_ISO8859_10
,
81 wxFONTENCODING_ISO8859_11
,
82 wxFONTENCODING_ISO8859_12
,
83 wxFONTENCODING_ISO8859_13
,
84 wxFONTENCODING_ISO8859_14
,
85 wxFONTENCODING_ISO8859_15
,
87 wxFONTENCODING_CP1250
,
88 wxFONTENCODING_CP1251
,
89 wxFONTENCODING_CP1252
,
90 wxFONTENCODING_CP1253
,
91 wxFONTENCODING_CP1254
,
92 wxFONTENCODING_CP1255
,
93 wxFONTENCODING_CP1256
,
94 wxFONTENCODING_CP1257
,
100 // the descriptions for them
101 static const wxChar
* gs_encodingDescs
[] =
103 wxTRANSLATE( "Western European (ISO-8859-1)" ),
104 wxTRANSLATE( "Central European (ISO-8859-2)" ),
105 wxTRANSLATE( "Esperanto (ISO-8859-3)" ),
106 wxTRANSLATE( "Baltic (old) (ISO-8859-4)" ),
107 wxTRANSLATE( "Cyrillic (ISO-8859-5)" ),
108 wxTRANSLATE( "Arabic (ISO-8859-6)" ),
109 wxTRANSLATE( "Greek (ISO-8859-7)" ),
110 wxTRANSLATE( "Hebrew (ISO-8859-8)" ),
111 wxTRANSLATE( "Turkish (ISO-8859-9)" ),
112 wxTRANSLATE( "Nordic (ISO-8859-10)" ),
113 wxTRANSLATE( "Thai (ISO-8859-11)" ),
114 wxTRANSLATE( "Indian (ISO-8859-12)" ),
115 wxTRANSLATE( "Baltic (ISO-8859-13)" ),
116 wxTRANSLATE( "Celtic (ISO-8859-14)" ),
117 wxTRANSLATE( "Western European with Euro (ISO-8859-15)" ),
118 wxTRANSLATE( "KOI8-R" ),
119 wxTRANSLATE( "Windows Central European (CP 1250)" ),
120 wxTRANSLATE( "Windows Cyrillic (CP 1251)" ),
121 wxTRANSLATE( "Windows Western European (CP 1252)" ),
122 wxTRANSLATE( "Windows Greek (CP 1253)" ),
123 wxTRANSLATE( "Windows Turkish (CP 1254)" ),
124 wxTRANSLATE( "Windows Hebrew (CP 1255)" ),
125 wxTRANSLATE( "Windows Arabic (CP 1256)" ),
126 wxTRANSLATE( "Windows Baltic (CP 1257)" ),
127 wxTRANSLATE( "Windows/DOS OEM (CP 437)" ),
128 wxTRANSLATE( "Unicode 7 bit (UTF-7)" ),
129 wxTRANSLATE( "Unicode 8 bit (UTF-8)" ),
132 // and the internal names
133 static const wxChar
* gs_encodingNames
[] =
144 wxT( "iso-8859-10" ),
145 wxT( "iso-8859-11" ),
146 wxT( "iso-8859-12" ),
147 wxT( "iso-8859-13" ),
148 wxT( "iso-8859-14" ),
149 wxT( "iso-8859-15" ),
151 wxT( "windows-1250" ),
152 wxT( "windows-1251" ),
153 wxT( "windows-1252" ),
154 wxT( "windows-1253" ),
155 wxT( "windows-1254" ),
156 wxT( "windows-1255" ),
157 wxT( "windows-1256" ),
158 wxT( "windows-1257" ),
159 wxT( "windows-437" ),
164 // ----------------------------------------------------------------------------
166 // ----------------------------------------------------------------------------
169 static wxFontMapper gs_fontMapper
;
171 // and public pointer
172 wxFontMapper
* wxTheFontMapper
= &gs_fontMapper
;
174 // ----------------------------------------------------------------------------
176 // ----------------------------------------------------------------------------
178 // change the config path during the lifetime of this object
179 class wxFontMapperPathChanger
182 wxFontMapperPathChanger(wxFontMapper
*fontMapper
, const wxString
& path
)
184 m_fontMapper
= fontMapper
;
185 m_ok
= m_fontMapper
->ChangePath(path
, &m_pathOld
);
188 bool IsOk() const { return m_ok
; }
190 ~wxFontMapperPathChanger()
193 m_fontMapper
->RestorePath(m_pathOld
);
197 wxFontMapper
*m_fontMapper
;
202 // ============================================================================
204 // ============================================================================
206 // ----------------------------------------------------------------------------
208 // ----------------------------------------------------------------------------
210 wxFontMapper::wxFontMapper()
214 #endif // wxUSE_CONFIG
217 m_windowParent
= NULL
;
221 wxFontMapper::~wxFontMapper()
225 // ----------------------------------------------------------------------------
227 // ----------------------------------------------------------------------------
231 /* static */ const wxChar
*wxFontMapper::GetDefaultConfigPath()
233 return FONTMAPPER_ROOT_PATH
;
236 void wxFontMapper::SetConfigPath(const wxString
& prefix
)
238 wxCHECK_RET( !prefix
.IsEmpty() && prefix
[0] == wxCONFIG_PATH_SEPARATOR
,
239 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
241 m_configRootPath
= prefix
;
244 // ----------------------------------------------------------------------------
245 // get config object and path for it
246 // ----------------------------------------------------------------------------
248 wxConfigBase
*wxFontMapper::GetConfig()
253 m_config
= wxConfig::Get(FALSE
/*don't create on demand*/ );
257 // we still want to have a config object because otherwise we would
258 // keep asking the user the same questions in the interactive mode,
259 // so create a dummy config which won't write to any files/registry
260 // but will allow us to remember the results of the questions at
261 // least during this run
262 m_config
= new wxMemoryConfig
;
263 wxConfig::Set(m_config
);
270 const wxString
& wxFontMapper::GetConfigPath()
272 if ( !m_configRootPath
)
275 m_configRootPath
= GetDefaultConfigPath();
278 return m_configRootPath
;
282 bool wxFontMapper::ChangePath(const wxString
& pathNew
, wxString
*pathOld
)
285 wxConfigBase
*config
= GetConfig();
289 *pathOld
= config
->GetPath();
291 wxString path
= GetConfigPath();
292 if ( path
.IsEmpty() || path
.Last() != wxCONFIG_PATH_SEPARATOR
)
294 path
+= wxCONFIG_PATH_SEPARATOR
;
297 wxASSERT_MSG( !pathNew
|| (pathNew
[0] != wxCONFIG_PATH_SEPARATOR
),
298 wxT("should be a relative path") );
302 config
->SetPath(path
);
310 void wxFontMapper::RestorePath(const wxString
& pathOld
)
313 GetConfig()->SetPath(pathOld
);
318 // ----------------------------------------------------------------------------
319 // charset/encoding correspondence
320 // ----------------------------------------------------------------------------
323 wxString
wxFontMapper::GetEncodingDescription(wxFontEncoding encoding
)
325 if ( encoding
== wxFONTENCODING_DEFAULT
)
327 return _("Default encoding");
330 size_t count
= WXSIZEOF(gs_encodingDescs
);
332 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
333 wxT("inconsitency detected - forgot to update one of the arrays?") );
335 for ( size_t i
= 0; i
< count
; i
++ )
337 if ( gs_encodings
[i
] == encoding
)
339 return wxGetTranslation(gs_encodingDescs
[i
]);
344 str
.Printf(_("Unknown encoding (%d)"), encoding
);
350 wxString
wxFontMapper::GetEncodingName(wxFontEncoding encoding
)
352 if ( encoding
== wxFONTENCODING_DEFAULT
)
357 size_t count
= WXSIZEOF(gs_encodingNames
);
359 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
360 wxT("inconsistency detected - forgot to update one of the arrays?") );
362 for ( size_t i
= 0; i
< count
; i
++ )
364 if ( gs_encodings
[i
] == encoding
)
366 return wxGetTranslation(gs_encodingNames
[i
]);
371 str
.Printf(_("unknown-%d"), encoding
);
376 wxFontEncoding
wxFontMapper::CharsetToEncoding(const wxString
& charset
,
379 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
381 // we're going to modify it, make a copy
382 wxString cs
= charset
;
385 // first try the user-defined settings
387 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
389 wxConfigBase
*config
= GetConfig();
391 // do we have an encoding for this charset?
392 long value
= config
->Read(charset
, -1l);
395 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
397 encoding
= (wxFontEncoding
)value
;
401 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
402 value
, charset
.c_str());
406 if ( encoding
== wxFONTENCODING_SYSTEM
)
408 // may be we have an alias?
409 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
411 wxString alias
= config
->Read(charset
);
414 // yes, we do - use it instead
419 RestorePath(pathOld
);
423 // if didn't find it there, try to recognize it ourselves
424 if ( encoding
== wxFONTENCODING_SYSTEM
)
430 // discard the optional quotes
433 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
435 cs
= wxString(cs
.c_str(), cs
.length() - 1);
441 if ( !cs
|| cs
== wxT("US-ASCII") )
443 encoding
= wxFONTENCODING_DEFAULT
;
445 else if ( cs
== wxT("UTF-7") )
447 encoding
= wxFONTENCODING_UTF7
;
449 else if ( cs
== wxT("UTF-8") )
451 encoding
= wxFONTENCODING_UTF8
;
453 else if ( cs
== wxT("KOI8-R") ||
454 cs
== wxT("KOI8-U") ||
455 cs
== wxT("KOI8-RU") )
457 // although koi8-ru is not strictly speaking the same as koi8-r,
458 // they are similar enough to make mapping it to koi8 better than
459 // not reckognizing it at all
460 encoding
= wxFONTENCODING_KOI8
;
462 else if ( cs
.Left(3) == wxT("ISO") )
464 // the dash is optional (or, to be exact, it is not, but
465 // several brokenmails "forget" it)
466 const wxChar
*p
= cs
.c_str() + 3;
467 if ( *p
== wxT('-') )
471 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
473 // make it 0 based and check that it is strictly positive in
474 // the process (no such thing as iso8859-0 encoding)
475 if ( (value
-- > 0) &&
476 (value
< wxFONTENCODING_ISO8859_MAX
-
477 wxFONTENCODING_ISO8859_1
) )
479 // it's a valid ISO8859 encoding
480 value
+= wxFONTENCODING_ISO8859_1
;
481 encoding
= (wxFontEncoding
)value
;
485 else // check for Windows charsets
488 if ( cs
.Left(7) == wxT("WINDOWS") )
492 else if ( cs
.Left(2) == wxT("CP") )
496 else // not a Windows encoding
503 const wxChar
*p
= cs
.c_str() + len
;
504 if ( *p
== wxT('-') )
508 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
513 if ( value
< wxFONTENCODING_CP12_MAX
-
514 wxFONTENCODING_CP1250
)
516 // a valid Windows code page
517 value
+= wxFONTENCODING_CP1250
;
518 encoding
= (wxFontEncoding
)value
;
528 // if still no luck, ask the user - unless disabled
529 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
531 // prepare the dialog data
534 wxString
title(m_titleDialog
);
536 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
540 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());
542 // the list of choices
543 size_t count
= WXSIZEOF(gs_encodingDescs
);
545 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
546 wxT("inconsitency detected - forgot to update one of the arrays?") );
548 wxString
*encodingNamesTranslated
= new wxString
[count
];
550 for ( size_t i
= 0; i
< count
; i
++ )
552 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
556 wxWindow
*parent
= m_windowParent
;
558 parent
= wxTheApp
->GetTopWindow();
560 // do ask the user and get back the index in encodings table
561 int n
= wxGetSingleChoiceIndex(msg
, title
,
563 encodingNamesTranslated
,
566 delete [] encodingNamesTranslated
;
570 encoding
= gs_encodings
[n
];
573 // save the result in the config now
574 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
576 wxConfigBase
*config
= GetConfig();
578 // remember the alt encoding for this charset
579 if ( !config
->Write(charset
, (long)encoding
) )
581 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
584 RestorePath(pathOld
);
586 #endif // wxUSE_CONFIG
595 // ----------------------------------------------------------------------------
596 // support for unknown encodings: we maintain a map between the
597 // (platform-specific) strings identifying them and our wxFontEncodings they
598 // correspond to which is used by GetFontForEncoding() function
599 // ----------------------------------------------------------------------------
603 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
604 wxFontEncoding encReplacement
,
605 wxNativeEncodingInfo
*info
)
607 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
608 wxTestFontEncoding(*info
) )
611 // remember the mapping in the config
612 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
616 GetConfig()->Write(configEntry
, info
->ToString());
618 #endif // wxUSE_CONFIG
626 class ReentrancyBlocker
629 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
630 ~ReentrancyBlocker() { m_b
= FALSE
; }
637 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
638 wxNativeEncodingInfo
*info
,
639 const wxString
& facename
,
643 // we need a flag to prevent infinite recursion which happens, for
644 // example, when GetAltForEncoding() is called from an OnPaint() handler:
645 // in this case, wxYield() which is called from wxMessageBox() we use here
646 // will lead to another call of OnPaint() and hence to another call of
647 // GetAltForEncoding() - and it is impossible to catch this from the user
648 // code because we are called from wxFont ctor implicitly.
650 // assume we're always called from the main thread, so that it is safe to
652 static bool s_inGetAltForEncoding
= FALSE
;
654 if ( interactive
&& s_inGetAltForEncoding
)
657 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
660 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
662 info
->facename
= facename
;
664 if ( encoding
== wxFONTENCODING_DEFAULT
)
666 encoding
= wxFont::GetDefaultEncoding();
669 // if we failed to load the system default encoding, something is really
670 // wrong and we'd better stop now - otherwise we will go into endless
671 // recursion trying to create the font in the msg box with the error
673 if ( encoding
== wxFONTENCODING_SYSTEM
)
675 wxFatalError(_("can't load any font, aborting"));
677 // wxFatalError doesn't return
680 wxString configEntry
, encName
= GetEncodingName(encoding
);
683 configEntry
= facename
+ _T("_");
685 configEntry
+= encName
;
688 // do we have a font spec for this encoding?
690 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
692 wxConfigBase
*config
= GetConfig();
694 wxString fontinfo
= config
->Read(configEntry
);
696 RestorePath(pathOld
);
698 // this special value means that we don't know of fonts for this
699 // encoding but, moreover, have already asked the user as well and he
700 // didn't specify any font neither
701 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
705 else // use the info entered the last time
707 if ( !!fontinfo
&& !!facename
)
709 // we tried to find a match with facename - now try without it
710 fontinfo
= config
->Read(encName
);
715 if ( info
->FromString(fontinfo
) )
717 if ( wxTestFontEncoding(*info
) )
722 //else: no such fonts, look for something else
723 // (should we erase the outdated value?)
727 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
731 //else: there is no information in config about this encoding
734 #endif // wxUSE_CONFIG
739 wxString
title(m_titleDialog
);
741 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
745 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)?"),
746 GetEncodingDescription(encoding
).c_str());
748 wxWindow
*parent
= m_windowParent
;
750 parent
= wxTheApp
->GetTopWindow();
752 if ( wxMessageBox(msg
, title
,
753 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
756 data
.SetEncoding(encoding
);
757 data
.EncodingInfo() = *info
;
758 wxFontDialog
dialog(parent
, &data
);
759 if ( dialog
.ShowModal() == wxID_OK
)
761 wxFontData retData
= dialog
.GetFontData();
762 wxFont font
= retData
.GetChosenFont();
764 *info
= retData
.EncodingInfo();
765 info
-> encoding
= retData
.GetEncoding();
768 // remember this in the config
769 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
771 GetConfig()->Write(configEntry
, info
->ToString());
773 RestorePath(pathOld
);
775 #endif // wxUSE_CONFIG
779 //else: the user canceled the font selection dialog
783 // the user doesn't want to select a font for this encoding,
784 // remember it to avoid asking the same question again later
786 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
788 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
790 RestorePath(pathOld
);
792 #endif // wxUSE_CONFIG
795 //else: we're in non-interactive mode
797 // now try the default mappings:
798 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
799 size_t count
= equiv
.GetCount();
802 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
804 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
812 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
813 wxFontEncoding
*alt_encoding
,
814 const wxString
& facename
,
817 wxNativeEncodingInfo info
;
818 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
819 *alt_encoding
= info
.encoding
;
823 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
824 const wxString
& facename
)
826 wxNativeEncodingInfo info
;
828 if (wxGetNativeFontEncoding(encoding
, &info
))
830 info
.facename
= facename
;
831 return wxTestFontEncoding(info
);