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 m_configIsDummy
= FALSE
;
219 #endif // wxUSE_CONFIG
222 m_windowParent
= NULL
;
226 wxFontMapper::~wxFontMapper()
230 // ----------------------------------------------------------------------------
232 // ----------------------------------------------------------------------------
236 /* static */ const wxChar
*wxFontMapper::GetDefaultConfigPath()
238 return FONTMAPPER_ROOT_PATH
;
241 void wxFontMapper::SetConfigPath(const wxString
& prefix
)
243 wxCHECK_RET( !prefix
.IsEmpty() && prefix
[0] == wxCONFIG_PATH_SEPARATOR
,
244 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
246 m_configRootPath
= prefix
;
249 // ----------------------------------------------------------------------------
250 // get config object and path for it
251 // ----------------------------------------------------------------------------
253 wxConfigBase
*wxFontMapper::GetConfig()
258 m_config
= wxConfig::Get(FALSE
/*don't create on demand*/ );
262 // we still want to have a config object because otherwise we would
263 // keep asking the user the same questions in the interactive mode,
264 // so create a dummy config which won't write to any files/registry
265 // but will allow us to remember the results of the questions at
266 // least during this run
267 m_config
= new wxMemoryConfig
;
268 m_configIsDummy
= TRUE
;
269 // VS: we can't call wxConfig::Set(m_config) here because that would
270 // disable automatic wxConfig instance creation if this code was
271 // called before wxApp::OnInit (this happens in wxGTK -- it sets
272 // default wxFont encoding in wxApp::Initialize())
276 if ( m_configIsDummy
&& wxConfig::Get(FALSE
) != NULL
)
278 // VS: in case we created dummy m_config (see above), we want to switch back
279 // to the real one as soon as one becomes available.
280 m_config
= wxConfig::Get(FALSE
);
281 m_configIsDummy
= FALSE
;
282 // FIXME: ideally, we should add keys from dummy config to the real one now,
283 // but it is a low-priority task because typical wxWin application
284 // either doesn't use wxConfig at all or creates wxConfig object in
285 // wxApp::OnInit(), before any real interaction with the user takes
292 const wxString
& wxFontMapper::GetConfigPath()
294 if ( !m_configRootPath
)
297 m_configRootPath
= GetDefaultConfigPath();
300 return m_configRootPath
;
304 bool wxFontMapper::ChangePath(const wxString
& pathNew
, wxString
*pathOld
)
307 wxConfigBase
*config
= GetConfig();
311 *pathOld
= config
->GetPath();
313 wxString path
= GetConfigPath();
314 if ( path
.IsEmpty() || path
.Last() != wxCONFIG_PATH_SEPARATOR
)
316 path
+= wxCONFIG_PATH_SEPARATOR
;
319 wxASSERT_MSG( !pathNew
|| (pathNew
[0] != wxCONFIG_PATH_SEPARATOR
),
320 wxT("should be a relative path") );
324 config
->SetPath(path
);
332 void wxFontMapper::RestorePath(const wxString
& pathOld
)
335 GetConfig()->SetPath(pathOld
);
340 // ----------------------------------------------------------------------------
341 // charset/encoding correspondence
342 // ----------------------------------------------------------------------------
345 wxString
wxFontMapper::GetEncodingDescription(wxFontEncoding encoding
)
347 if ( encoding
== wxFONTENCODING_DEFAULT
)
349 return _("Default encoding");
352 size_t count
= WXSIZEOF(gs_encodingDescs
);
354 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
355 wxT("inconsitency detected - forgot to update one of the arrays?") );
357 for ( size_t i
= 0; i
< count
; i
++ )
359 if ( gs_encodings
[i
] == encoding
)
361 return wxGetTranslation(gs_encodingDescs
[i
]);
366 str
.Printf(_("Unknown encoding (%d)"), encoding
);
372 wxString
wxFontMapper::GetEncodingName(wxFontEncoding encoding
)
374 if ( encoding
== wxFONTENCODING_DEFAULT
)
379 size_t count
= WXSIZEOF(gs_encodingNames
);
381 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
382 wxT("inconsistency detected - forgot to update one of the arrays?") );
384 for ( size_t i
= 0; i
< count
; i
++ )
386 if ( gs_encodings
[i
] == encoding
)
388 return wxGetTranslation(gs_encodingNames
[i
]);
393 str
.Printf(_("unknown-%d"), encoding
);
398 wxFontEncoding
wxFontMapper::CharsetToEncoding(const wxString
& charset
,
401 // a special pseudo encoding which means "don't ask me about this charset
402 // any more" - we need it to avoid driving the user crazy with asking him
403 // time after time about the same charset which he [presumably] doesn't
404 // have the fonts fot
405 static const int wxFONTENCODING_UNKNOWN
= -2;
407 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
409 // we're going to modify it, make a copy
410 wxString cs
= charset
;
413 // first try the user-defined settings
415 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
417 wxConfigBase
*config
= GetConfig();
419 // do we have an encoding for this charset?
420 long value
= config
->Read(charset
, -1l);
423 if ( value
== wxFONTENCODING_UNKNOWN
)
425 // don't try to find it, in particular don't ask the user
426 return wxFONTENCODING_SYSTEM
;
429 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
431 encoding
= (wxFontEncoding
)value
;
435 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
436 value
, charset
.c_str());
440 if ( encoding
== wxFONTENCODING_SYSTEM
)
442 // may be we have an alias?
443 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
445 wxString alias
= config
->Read(charset
);
448 // yes, we do - use it instead
453 RestorePath(pathOld
);
455 #endif // wxUSE_CONFIG
457 // if didn't find it there, try to recognize it ourselves
458 if ( encoding
== wxFONTENCODING_SYSTEM
)
464 // discard the optional quotes
467 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
469 cs
= wxString(cs
.c_str(), cs
.length() - 1);
475 if ( !cs
|| cs
== wxT("US-ASCII") )
477 encoding
= wxFONTENCODING_DEFAULT
;
479 else if ( cs
== wxT("UTF-7") )
481 encoding
= wxFONTENCODING_UTF7
;
483 else if ( cs
== wxT("UTF-8") )
485 encoding
= wxFONTENCODING_UTF8
;
487 else if ( cs
== wxT("KOI8-R") ||
488 cs
== wxT("KOI8-U") ||
489 cs
== wxT("KOI8-RU") )
491 // although koi8-ru is not strictly speaking the same as koi8-r,
492 // they are similar enough to make mapping it to koi8 better than
493 // not reckognizing it at all
494 encoding
= wxFONTENCODING_KOI8
;
496 else if ( cs
.Left(3) == wxT("ISO") )
498 // the dash is optional (or, to be exact, it is not, but
499 // several brokenmails "forget" it)
500 const wxChar
*p
= cs
.c_str() + 3;
501 if ( *p
== wxT('-') )
505 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
507 // make it 0 based and check that it is strictly positive in
508 // the process (no such thing as iso8859-0 encoding)
509 if ( (value
-- > 0) &&
510 (value
< wxFONTENCODING_ISO8859_MAX
-
511 wxFONTENCODING_ISO8859_1
) )
513 // it's a valid ISO8859 encoding
514 value
+= wxFONTENCODING_ISO8859_1
;
515 encoding
= (wxFontEncoding
)value
;
519 else // check for Windows charsets
522 if ( cs
.Left(7) == wxT("WINDOWS") )
526 else if ( cs
.Left(2) == wxT("CP") )
530 else // not a Windows encoding
537 const wxChar
*p
= cs
.c_str() + len
;
538 if ( *p
== wxT('-') )
542 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
547 if ( value
< wxFONTENCODING_CP12_MAX
-
548 wxFONTENCODING_CP1250
)
550 // a valid Windows code page
551 value
+= wxFONTENCODING_CP1250
;
552 encoding
= (wxFontEncoding
)value
;
562 // if still no luck, ask the user - unless disabled
563 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
565 // prepare the dialog data
568 wxString
title(m_titleDialog
);
570 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
574 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());
576 // the list of choices
577 size_t count
= WXSIZEOF(gs_encodingDescs
);
579 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
580 wxT("inconsitency detected - forgot to update one of the arrays?") );
582 wxString
*encodingNamesTranslated
= new wxString
[count
];
584 for ( size_t i
= 0; i
< count
; i
++ )
586 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
590 wxWindow
*parent
= m_windowParent
;
592 parent
= wxTheApp
->GetTopWindow();
594 // do ask the user and get back the index in encodings table
595 int n
= wxGetSingleChoiceIndex(msg
, title
,
597 encodingNamesTranslated
,
600 delete [] encodingNamesTranslated
;
604 encoding
= gs_encodings
[n
];
608 // save the result in the config now
609 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
611 wxConfigBase
*config
= GetConfig();
613 // remember the alt encoding for this charset - or remember that
615 long value
= n
== -1 ? wxFONTENCODING_UNKNOWN
: (long)encoding
;
616 if ( !config
->Write(charset
, value
) )
618 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
621 RestorePath(pathOld
);
623 #endif // wxUSE_CONFIG
630 // ----------------------------------------------------------------------------
631 // support for unknown encodings: we maintain a map between the
632 // (platform-specific) strings identifying them and our wxFontEncodings they
633 // correspond to which is used by GetFontForEncoding() function
634 // ----------------------------------------------------------------------------
638 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
639 wxFontEncoding encReplacement
,
640 wxNativeEncodingInfo
*info
)
642 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
643 wxTestFontEncoding(*info
) )
646 // remember the mapping in the config
647 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
651 GetConfig()->Write(configEntry
, info
->ToString());
653 #endif // wxUSE_CONFIG
661 class ReentrancyBlocker
664 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
665 ~ReentrancyBlocker() { m_b
= FALSE
; }
672 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
673 wxNativeEncodingInfo
*info
,
674 const wxString
& facename
,
678 // we need a flag to prevent infinite recursion which happens, for
679 // example, when GetAltForEncoding() is called from an OnPaint() handler:
680 // in this case, wxYield() which is called from wxMessageBox() we use here
681 // will lead to another call of OnPaint() and hence to another call of
682 // GetAltForEncoding() - and it is impossible to catch this from the user
683 // code because we are called from wxFont ctor implicitly.
685 // assume we're always called from the main thread, so that it is safe to
687 static bool s_inGetAltForEncoding
= FALSE
;
689 if ( interactive
&& s_inGetAltForEncoding
)
692 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
695 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
697 info
->facename
= facename
;
699 if ( encoding
== wxFONTENCODING_DEFAULT
)
701 encoding
= wxFont::GetDefaultEncoding();
704 // if we failed to load the system default encoding, something is really
705 // wrong and we'd better stop now - otherwise we will go into endless
706 // recursion trying to create the font in the msg box with the error
708 if ( encoding
== wxFONTENCODING_SYSTEM
)
710 wxFatalError(_("can't load any font, aborting"));
712 // wxFatalError doesn't return
715 wxString configEntry
, encName
= GetEncodingName(encoding
);
718 configEntry
= facename
+ _T("_");
720 configEntry
+= encName
;
723 // do we have a font spec for this encoding?
725 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
727 wxConfigBase
*config
= GetConfig();
729 wxString fontinfo
= config
->Read(configEntry
);
731 RestorePath(pathOld
);
733 // this special value means that we don't know of fonts for this
734 // encoding but, moreover, have already asked the user as well and he
735 // didn't specify any font neither
736 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
740 else // use the info entered the last time
742 if ( !!fontinfo
&& !!facename
)
744 // we tried to find a match with facename - now try without it
745 fontinfo
= config
->Read(encName
);
750 if ( info
->FromString(fontinfo
) )
752 if ( wxTestFontEncoding(*info
) )
757 //else: no such fonts, look for something else
758 // (should we erase the outdated value?)
762 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
766 //else: there is no information in config about this encoding
769 #endif // wxUSE_CONFIG
775 wxString
title(m_titleDialog
);
777 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
781 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)?"),
782 GetEncodingDescription(encoding
).c_str());
784 wxWindow
*parent
= m_windowParent
;
786 parent
= wxTheApp
->GetTopWindow();
788 if ( wxMessageBox(msg
, title
,
789 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
792 data
.SetEncoding(encoding
);
793 data
.EncodingInfo() = *info
;
794 wxFontDialog
dialog(parent
, &data
);
795 if ( dialog
.ShowModal() == wxID_OK
)
797 wxFontData retData
= dialog
.GetFontData();
798 wxFont font
= retData
.GetChosenFont();
800 *info
= retData
.EncodingInfo();
801 info
-> encoding
= retData
.GetEncoding();
804 // remember this in the config
805 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
807 GetConfig()->Write(configEntry
, info
->ToString());
809 RestorePath(pathOld
);
811 #endif // wxUSE_CONFIG
815 //else: the user canceled the font selection dialog
819 // the user doesn't want to select a font for this encoding,
820 // remember it to avoid asking the same question again later
822 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
824 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
826 RestorePath(pathOld
);
828 #endif // wxUSE_CONFIG
831 //else: we're in non-interactive mode
832 #endif // wxUSE_FONTDLG
834 // now try the default mappings:
835 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
836 size_t count
= equiv
.GetCount();
839 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
841 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
849 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
850 wxFontEncoding
*alt_encoding
,
851 const wxString
& facename
,
854 wxNativeEncodingInfo info
;
855 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
856 *alt_encoding
= info
.encoding
;
860 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
861 const wxString
& facename
)
863 wxNativeEncodingInfo info
;
865 if (wxGetNativeFontEncoding(encoding
, &info
))
867 info
.facename
= facename
;
868 return wxTestFontEncoding(info
);
876 #endif // wxUSE_FONTMAP