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/msgdlg.h"
46 #include "wx/fontdlg.h"
47 #include "wx/choicdlg.h"
50 #include "wx/encconv.h"
52 // ----------------------------------------------------------------------------
54 // ----------------------------------------------------------------------------
56 // the config paths we use
57 static const wxChar
* FONTMAPPER_ROOT_PATH
= wxT("/wxWindows/FontMapper");
58 static const wxChar
* FONTMAPPER_CHARSET_PATH
= wxT("Charsets");
59 static const wxChar
* FONTMAPPER_CHARSET_ALIAS_PATH
= wxT("Aliases");
61 static const wxChar
* FONTMAPPER_FONT_FROM_ENCODING_PATH
= wxT("Encodings");
64 // encodings supported by GetEncodingDescription
65 static wxFontEncoding gs_encodings
[] =
67 wxFONTENCODING_ISO8859_1
,
68 wxFONTENCODING_ISO8859_2
,
69 wxFONTENCODING_ISO8859_3
,
70 wxFONTENCODING_ISO8859_4
,
71 wxFONTENCODING_ISO8859_5
,
72 wxFONTENCODING_ISO8859_6
,
73 wxFONTENCODING_ISO8859_7
,
74 wxFONTENCODING_ISO8859_8
,
75 wxFONTENCODING_ISO8859_9
,
76 wxFONTENCODING_ISO8859_10
,
77 wxFONTENCODING_ISO8859_11
,
78 wxFONTENCODING_ISO8859_12
,
79 wxFONTENCODING_ISO8859_13
,
80 wxFONTENCODING_ISO8859_14
,
81 wxFONTENCODING_ISO8859_15
,
83 wxFONTENCODING_CP1250
,
84 wxFONTENCODING_CP1251
,
85 wxFONTENCODING_CP1252
,
86 wxFONTENCODING_CP1253
,
87 wxFONTENCODING_CP1254
,
88 wxFONTENCODING_CP1255
,
89 wxFONTENCODING_CP1256
,
90 wxFONTENCODING_CP1257
,
96 // the descriptions for them
97 static const wxChar
* gs_encodingDescs
[] =
99 wxTRANSLATE( "Western European (ISO-8859-1)" ),
100 wxTRANSLATE( "Central European (ISO-8859-2)" ),
101 wxTRANSLATE( "Esperanto (ISO-8859-3)" ),
102 wxTRANSLATE( "Baltic (old) (ISO-8859-4)" ),
103 wxTRANSLATE( "Cyrillic (ISO-8859-5)" ),
104 wxTRANSLATE( "Arabic (ISO-8859-6)" ),
105 wxTRANSLATE( "Greek (ISO-8859-7)" ),
106 wxTRANSLATE( "Hebrew (ISO-8859-8)" ),
107 wxTRANSLATE( "Turkish (ISO-8859-9)" ),
108 wxTRANSLATE( "Nordic (ISO-8859-10)" ),
109 wxTRANSLATE( "Thai (ISO-8859-11)" ),
110 wxTRANSLATE( "Indian (ISO-8859-12)" ),
111 wxTRANSLATE( "Baltic (ISO-8859-13)" ),
112 wxTRANSLATE( "Celtic (ISO-8859-14)" ),
113 wxTRANSLATE( "Western European with Euro (ISO-8859-15)" ),
114 wxTRANSLATE( "KOI8-R" ),
115 wxTRANSLATE( "Windows Central European (CP 1250)" ),
116 wxTRANSLATE( "Windows Cyrillic (CP 1251)" ),
117 wxTRANSLATE( "Windows Western European (CP 1252)" ),
118 wxTRANSLATE( "Windows Greek (CP 1253)" ),
119 wxTRANSLATE( "Windows Turkish (CP 1254)" ),
120 wxTRANSLATE( "Windows Hebrew (CP 1255)" ),
121 wxTRANSLATE( "Windows Arabic (CP 1256)" ),
122 wxTRANSLATE( "Windows Baltic (CP 1257)" ),
123 wxTRANSLATE( "Windows/DOS OEM (CP 437)" ),
124 wxTRANSLATE( "Unicode 7 bit (UTF-7)" ),
125 wxTRANSLATE( "Unicode 8 bit (UTF-8)" ),
128 // and the internal names
129 static const wxChar
* gs_encodingNames
[] =
147 wxT( "windows-1250" ),
148 wxT( "windows-1251" ),
149 wxT( "windows-1252" ),
150 wxT( "windows-1253" ),
151 wxT( "windows-1254" ),
152 wxT( "windows-1255" ),
153 wxT( "windows-1256" ),
154 wxT( "windows-1257" ),
155 wxT( "windows-437" ),
160 // ----------------------------------------------------------------------------
162 // ----------------------------------------------------------------------------
165 static wxFontMapper gs_fontMapper
;
167 // and public pointer
168 wxFontMapper
* wxTheFontMapper
= &gs_fontMapper
;
170 // ----------------------------------------------------------------------------
172 // ----------------------------------------------------------------------------
174 // change the config path during the lifetime of this object
175 class wxFontMapperPathChanger
178 wxFontMapperPathChanger(wxFontMapper
*fontMapper
, const wxString
& path
)
180 m_fontMapper
= fontMapper
;
181 m_ok
= m_fontMapper
->ChangePath(path
, &m_pathOld
);
184 bool IsOk() const { return m_ok
; }
186 ~wxFontMapperPathChanger()
189 m_fontMapper
->RestorePath(m_pathOld
);
193 wxFontMapper
*m_fontMapper
;
198 // ============================================================================
200 // ============================================================================
202 // ----------------------------------------------------------------------------
204 // ----------------------------------------------------------------------------
206 wxFontMapper::wxFontMapper()
210 #endif // wxUSE_CONFIG
213 m_windowParent
= NULL
;
217 wxFontMapper::~wxFontMapper()
221 // ----------------------------------------------------------------------------
223 // ----------------------------------------------------------------------------
227 /* static */ const wxChar
*wxFontMapper::GetDefaultConfigPath()
229 return FONTMAPPER_ROOT_PATH
;
232 void wxFontMapper::SetConfigPath(const wxString
& prefix
)
234 wxCHECK_RET( !prefix
.IsEmpty() && prefix
[0] == wxCONFIG_PATH_SEPARATOR
,
235 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
237 m_configRootPath
= prefix
;
240 // ----------------------------------------------------------------------------
241 // get config object and path for it
242 // ----------------------------------------------------------------------------
244 wxConfigBase
*wxFontMapper::GetConfig()
249 m_config
= wxConfig::Get(FALSE
/*don't create on demand*/ );
253 // we still want to have a config object because otherwise we would
254 // keep asking the user the same questions in the interactive mode,
255 // so create a dummy config which won't write to any files/registry
256 // but will allow us to remember the results of the questions at
257 // least during this run
258 m_config
= new wxMemoryConfig
;
259 wxConfig::Set(m_config
);
266 const wxString
& wxFontMapper::GetConfigPath()
268 if ( !m_configRootPath
)
271 m_configRootPath
= GetDefaultConfigPath();
274 return m_configRootPath
;
278 bool wxFontMapper::ChangePath(const wxString
& pathNew
, wxString
*pathOld
)
281 wxConfigBase
*config
= GetConfig();
285 *pathOld
= config
->GetPath();
287 wxString path
= GetConfigPath();
288 if ( path
.IsEmpty() || path
.Last() != wxCONFIG_PATH_SEPARATOR
)
290 path
+= wxCONFIG_PATH_SEPARATOR
;
293 wxASSERT_MSG( !pathNew
|| (pathNew
[0] != wxCONFIG_PATH_SEPARATOR
),
294 wxT("should be a relative path") );
298 config
->SetPath(path
);
306 void wxFontMapper::RestorePath(const wxString
& pathOld
)
309 GetConfig()->SetPath(pathOld
);
314 // ----------------------------------------------------------------------------
315 // charset/encoding correspondence
316 // ----------------------------------------------------------------------------
319 wxString
wxFontMapper::GetEncodingDescription(wxFontEncoding encoding
)
321 if ( encoding
== wxFONTENCODING_DEFAULT
)
323 return _("Default encoding");
326 size_t count
= WXSIZEOF(gs_encodingDescs
);
328 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
329 wxT("inconsitency detected - forgot to update one of the arrays?") );
331 for ( size_t i
= 0; i
< count
; i
++ )
333 if ( gs_encodings
[i
] == encoding
)
335 return wxGetTranslation(gs_encodingDescs
[i
]);
340 str
.Printf(_("Unknown encoding (%d)"), encoding
);
346 wxString
wxFontMapper::GetEncodingName(wxFontEncoding encoding
)
348 if ( encoding
== wxFONTENCODING_DEFAULT
)
353 size_t count
= WXSIZEOF(gs_encodingNames
);
355 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
356 wxT("inconsistency detected - forgot to update one of the arrays?") );
358 for ( size_t i
= 0; i
< count
; i
++ )
360 if ( gs_encodings
[i
] == encoding
)
362 return wxGetTranslation(gs_encodingNames
[i
]);
367 str
.Printf(_("unknown-%d"), encoding
);
372 wxFontEncoding
wxFontMapper::CharsetToEncoding(const wxString
& charset
,
375 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
377 // we're going to modify it, make a copy
378 wxString cs
= charset
;
381 // first try the user-defined settings
383 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
385 wxConfigBase
*config
= GetConfig();
387 // do we have an encoding for this charset?
388 long value
= config
->Read(charset
, -1l);
391 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
393 encoding
= (wxFontEncoding
)value
;
397 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
398 value
, charset
.c_str());
402 if ( encoding
== wxFONTENCODING_SYSTEM
)
404 // may be we have an alias?
405 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
407 wxString alias
= config
->Read(charset
);
410 // yes, we do - use it instead
415 RestorePath(pathOld
);
419 // if didn't find it there, try to recognize it ourselves
420 if ( encoding
== wxFONTENCODING_SYSTEM
)
426 // discard the optional quotes
429 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
431 cs
= wxString(cs
.c_str(), cs
.length() - 1);
437 if ( !cs
|| cs
== wxT("US-ASCII") )
439 encoding
= wxFONTENCODING_DEFAULT
;
441 else if ( cs
== wxT("UTF-7") )
443 encoding
= wxFONTENCODING_UTF7
;
445 else if ( cs
== wxT("UTF-8") )
447 encoding
= wxFONTENCODING_UTF8
;
449 else if ( cs
== wxT("KOI8-R") ||
450 cs
== wxT("KOI8-U") ||
451 cs
== wxT("KOI8-RU") )
453 // although koi8-ru is not strictly speaking the same as koi8-r,
454 // they are similar enough to make mapping it to koi8 better than
455 // not reckognizing it at all
456 encoding
= wxFONTENCODING_KOI8
;
458 else if ( cs
.Left(3) == wxT("ISO") )
460 // the dash is optional (or, to be exact, it is not, but
461 // several brokenmails "forget" it)
462 const wxChar
*p
= cs
.c_str() + 3;
463 if ( *p
== wxT('-') )
467 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
469 if ( value
< wxFONTENCODING_ISO8859_MAX
-
470 wxFONTENCODING_ISO8859_1
)
472 // it's a valid ISO8859 encoding
473 value
+= wxFONTENCODING_ISO8859_1
- 1;
474 encoding
= (wxFontEncoding
)value
;
478 else // check for Windows charsets
481 if ( cs
.Left(7) == wxT("WINDOWS") )
485 else if ( cs
.Left(2) == wxT("CP") )
489 else // not a Windows encoding
496 const wxChar
*p
= cs
.c_str() + len
;
497 if ( *p
== wxT('-') )
501 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
506 if ( value
< wxFONTENCODING_CP12_MAX
-
507 wxFONTENCODING_CP1250
)
509 // a valid Windows code page
510 value
+= wxFONTENCODING_CP1250
;
511 encoding
= (wxFontEncoding
)value
;
521 // if still no luck, ask the user - unless disabled
522 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
524 // prepare the dialog data
527 wxString
title(m_titleDialog
);
529 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
533 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());
535 // the list of choices
536 size_t count
= WXSIZEOF(gs_encodingDescs
);
538 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
539 wxT("inconsitency detected - forgot to update one of the arrays?") );
541 wxString
*encodingNamesTranslated
= new wxString
[count
];
543 for ( size_t i
= 0; i
< count
; i
++ )
545 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
549 wxWindow
*parent
= m_windowParent
;
551 parent
= wxTheApp
->GetTopWindow();
553 // do ask the user and get back the index in encodings table
554 int n
= wxGetSingleChoiceIndex(msg
, title
,
556 encodingNamesTranslated
,
559 delete [] encodingNamesTranslated
;
563 encoding
= gs_encodings
[n
];
566 // save the result in the config now
567 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
569 wxConfigBase
*config
= GetConfig();
571 // remember the alt encoding for this charset
572 if ( !config
->Write(charset
, (long)encoding
) )
574 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
577 RestorePath(pathOld
);
579 #endif // wxUSE_CONFIG
588 // ----------------------------------------------------------------------------
589 // support for unknown encodings: we maintain a map between the
590 // (platform-specific) strings identifying them and our wxFontEncodings they
591 // correspond to which is used by GetFontForEncoding() function
592 // ----------------------------------------------------------------------------
596 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
597 wxFontEncoding encReplacement
,
598 wxNativeEncodingInfo
*info
)
600 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
601 wxTestFontEncoding(*info
) )
604 // remember the mapping in the config
605 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
609 GetConfig()->Write(configEntry
, info
->ToString());
611 #endif // wxUSE_CONFIG
619 class ReentrancyBlocker
622 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
623 ~ReentrancyBlocker() { m_b
= FALSE
; }
630 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
631 wxNativeEncodingInfo
*info
,
632 const wxString
& facename
,
636 // we need a flag to prevent infinite recursion which happens, for
637 // example, when GetAltForEncoding() is called from an OnPaint() handler:
638 // in this case, wxYield() which is called from wxMessageBox() we use here
639 // will lead to another call of OnPaint() and hence to another call of
640 // GetAltForEncoding() - and it is impossible to catch this from the user
641 // code because we are called from wxFont ctor implicitly.
643 // assume we're always called from the main thread, so that it is safe to
645 static bool s_inGetAltForEncoding
= FALSE
;
647 if ( interactive
&& s_inGetAltForEncoding
)
650 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
653 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
655 info
->facename
= facename
;
657 if ( encoding
== wxFONTENCODING_DEFAULT
)
659 encoding
= wxFont::GetDefaultEncoding();
662 // if we failed to load the system default encoding, something is really
663 // wrong and we'd better stop now - otherwise we will go into endless
664 // recursion trying to create the font in the msg box with the error
666 if ( encoding
== wxFONTENCODING_SYSTEM
)
668 wxFatalError(_("can't load any font, aborting"));
670 // wxFatalError doesn't return
673 wxString configEntry
, encName
= GetEncodingName(encoding
);
676 configEntry
= facename
+ _T("_");
678 configEntry
+= encName
;
681 // do we have a font spec for this encoding?
683 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
685 wxConfigBase
*config
= GetConfig();
687 wxString fontinfo
= config
->Read(configEntry
);
689 RestorePath(pathOld
);
691 if ( !!fontinfo
&& !!facename
)
693 // we tried to find a match with facename - now try without it
694 fontinfo
= config
->Read(encName
);
699 if ( info
->FromString(fontinfo
) )
701 if ( wxTestFontEncoding(*info
) )
706 //else: no such fonts, look for something else
710 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"), fontinfo
.c_str());
713 //else: there is no information in config about this encoding
715 #endif // wxUSE_CONFIG
720 wxString
title(m_titleDialog
);
722 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
726 msg
.Printf(_("The encoding '%s' is unknown.\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)?"),
727 GetEncodingDescription(encoding
).c_str());
729 wxWindow
*parent
= m_windowParent
;
731 parent
= wxTheApp
->GetTopWindow();
733 if ( wxMessageBox(msg
, title
,
734 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
737 data
.SetEncoding(encoding
);
738 data
.EncodingInfo() = *info
;
739 wxFontDialog
dialog(parent
, &data
);
740 if ( dialog
.ShowModal() == wxID_OK
)
742 wxFontData retData
= dialog
.GetFontData();
743 wxFont font
= retData
.GetChosenFont();
745 *info
= retData
.EncodingInfo();
746 info
-> encoding
= retData
.GetEncoding();
749 // remember this in the config
750 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
752 GetConfig()->Write(configEntry
, info
->ToString());
754 RestorePath(pathOld
);
756 #endif // wxUSE_CONFIG
760 //else: the user canceled the font selection dialog
762 //else: the user doesn't want to select a font
764 //else: we're in non-interactive mode
766 // now try the default mappings:
767 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
768 size_t count
= equiv
.GetCount();
771 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
773 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
781 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
782 wxFontEncoding
*alt_encoding
,
783 const wxString
& facename
,
786 wxNativeEncodingInfo info
;
787 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
788 *alt_encoding
= info
.encoding
;
792 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
793 const wxString
& facename
)
795 wxNativeEncodingInfo info
;
797 if (wxGetNativeFontEncoding(encoding
, &info
))
799 info
.facename
= facename
;
800 return wxTestFontEncoding(info
);