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 (these are not translated on purpose!)
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 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
,
716 encName
= GetEncodingName(encoding
);
719 configEntry
= facename
+ _T("_");
721 configEntry
+= encName
;
724 // do we have a font spec for this encoding?
726 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
728 wxConfigBase
*config
= GetConfig();
730 wxString fontinfo
= config
->Read(configEntry
);
732 RestorePath(pathOld
);
734 // this special value means that we don't know of fonts for this
735 // encoding but, moreover, have already asked the user as well and he
736 // didn't specify any font neither
737 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
741 else // use the info entered the last time
743 if ( !!fontinfo
&& !!facename
)
745 // we tried to find a match with facename - now try without it
746 fontinfo
= config
->Read(encName
);
751 if ( info
->FromString(fontinfo
) )
753 if ( wxTestFontEncoding(*info
) )
758 //else: no such fonts, look for something else
759 // (should we erase the outdated value?)
763 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
767 //else: there is no information in config about this encoding
770 #endif // wxUSE_CONFIG
776 wxString
title(m_titleDialog
);
778 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
782 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)?"),
783 GetEncodingDescription(encoding
).c_str());
785 wxWindow
*parent
= m_windowParent
;
787 parent
= wxTheApp
->GetTopWindow();
789 if ( wxMessageBox(msg
, title
,
790 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
793 data
.SetEncoding(encoding
);
794 data
.EncodingInfo() = *info
;
795 wxFontDialog
dialog(parent
, &data
);
796 if ( dialog
.ShowModal() == wxID_OK
)
798 wxFontData retData
= dialog
.GetFontData();
799 wxFont font
= retData
.GetChosenFont();
801 *info
= retData
.EncodingInfo();
802 info
-> encoding
= retData
.GetEncoding();
805 // remember this in the config
806 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
808 GetConfig()->Write(configEntry
, info
->ToString());
810 RestorePath(pathOld
);
812 #endif // wxUSE_CONFIG
816 //else: the user canceled the font selection dialog
820 // the user doesn't want to select a font for this encoding,
821 // remember it to avoid asking the same question again later
823 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
825 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
827 RestorePath(pathOld
);
829 #endif // wxUSE_CONFIG
832 //else: we're in non-interactive mode
833 #endif // wxUSE_FONTDLG
835 // now try the default mappings:
836 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
837 size_t count
= equiv
.GetCount();
840 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
842 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
850 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
851 wxFontEncoding
*alt_encoding
,
852 const wxString
& facename
,
855 wxNativeEncodingInfo info
;
856 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
857 *alt_encoding
= info
.encoding
;
861 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
862 const wxString
& facename
)
864 wxNativeEncodingInfo info
;
866 if (wxGetNativeFontEncoding(encoding
, &info
))
868 info
.facename
= facename
;
869 return wxTestFontEncoding(info
);
877 #endif // wxUSE_FONTMAP