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 wxFontEncoding encoding
= wxFONTENCODING_SYSTEM
;
403 // we're going to modify it, make a copy
404 wxString cs
= charset
;
407 // first try the user-defined settings
409 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
411 wxConfigBase
*config
= GetConfig();
413 // do we have an encoding for this charset?
414 long value
= config
->Read(charset
, -1l);
417 if ( value
>= 0 && value
<= wxFONTENCODING_MAX
)
419 encoding
= (wxFontEncoding
)value
;
423 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
424 value
, charset
.c_str());
428 if ( encoding
== wxFONTENCODING_SYSTEM
)
430 // may be we have an alias?
431 config
->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH
);
433 wxString alias
= config
->Read(charset
);
436 // yes, we do - use it instead
441 RestorePath(pathOld
);
445 // if didn't find it there, try to recognize it ourselves
446 if ( encoding
== wxFONTENCODING_SYSTEM
)
452 // discard the optional quotes
455 if ( cs
[0u] == _T('"') && cs
.Last() == _T('"') )
457 cs
= wxString(cs
.c_str(), cs
.length() - 1);
463 if ( !cs
|| cs
== wxT("US-ASCII") )
465 encoding
= wxFONTENCODING_DEFAULT
;
467 else if ( cs
== wxT("UTF-7") )
469 encoding
= wxFONTENCODING_UTF7
;
471 else if ( cs
== wxT("UTF-8") )
473 encoding
= wxFONTENCODING_UTF8
;
475 else if ( cs
== wxT("KOI8-R") ||
476 cs
== wxT("KOI8-U") ||
477 cs
== wxT("KOI8-RU") )
479 // although koi8-ru is not strictly speaking the same as koi8-r,
480 // they are similar enough to make mapping it to koi8 better than
481 // not reckognizing it at all
482 encoding
= wxFONTENCODING_KOI8
;
484 else if ( cs
.Left(3) == wxT("ISO") )
486 // the dash is optional (or, to be exact, it is not, but
487 // several brokenmails "forget" it)
488 const wxChar
*p
= cs
.c_str() + 3;
489 if ( *p
== wxT('-') )
493 if ( wxSscanf(p
, wxT("8859-%u"), &value
) == 1 )
495 // make it 0 based and check that it is strictly positive in
496 // the process (no such thing as iso8859-0 encoding)
497 if ( (value
-- > 0) &&
498 (value
< wxFONTENCODING_ISO8859_MAX
-
499 wxFONTENCODING_ISO8859_1
) )
501 // it's a valid ISO8859 encoding
502 value
+= wxFONTENCODING_ISO8859_1
;
503 encoding
= (wxFontEncoding
)value
;
507 else // check for Windows charsets
510 if ( cs
.Left(7) == wxT("WINDOWS") )
514 else if ( cs
.Left(2) == wxT("CP") )
518 else // not a Windows encoding
525 const wxChar
*p
= cs
.c_str() + len
;
526 if ( *p
== wxT('-') )
530 if ( wxSscanf(p
, wxT("%u"), &value
) == 1 )
535 if ( value
< wxFONTENCODING_CP12_MAX
-
536 wxFONTENCODING_CP1250
)
538 // a valid Windows code page
539 value
+= wxFONTENCODING_CP1250
;
540 encoding
= (wxFontEncoding
)value
;
550 // if still no luck, ask the user - unless disabled
551 if ( (encoding
== wxFONTENCODING_SYSTEM
) && interactive
)
553 // prepare the dialog data
556 wxString
title(m_titleDialog
);
558 title
<< wxTheApp
->GetAppName() << _(": unknown charset");
562 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());
564 // the list of choices
565 size_t count
= WXSIZEOF(gs_encodingDescs
);
567 wxASSERT_MSG( count
== WXSIZEOF(gs_encodings
),
568 wxT("inconsitency detected - forgot to update one of the arrays?") );
570 wxString
*encodingNamesTranslated
= new wxString
[count
];
572 for ( size_t i
= 0; i
< count
; i
++ )
574 encodingNamesTranslated
[i
] = wxGetTranslation(gs_encodingDescs
[i
]);
578 wxWindow
*parent
= m_windowParent
;
580 parent
= wxTheApp
->GetTopWindow();
582 // do ask the user and get back the index in encodings table
583 int n
= wxGetSingleChoiceIndex(msg
, title
,
585 encodingNamesTranslated
,
588 delete [] encodingNamesTranslated
;
592 encoding
= gs_encodings
[n
];
595 // save the result in the config now
596 if ( ChangePath(FONTMAPPER_CHARSET_PATH
, &pathOld
) )
598 wxConfigBase
*config
= GetConfig();
600 // remember the alt encoding for this charset
601 if ( !config
->Write(charset
, (long)encoding
) )
603 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset
.c_str());
606 RestorePath(pathOld
);
608 #endif // wxUSE_CONFIG
617 // ----------------------------------------------------------------------------
618 // support for unknown encodings: we maintain a map between the
619 // (platform-specific) strings identifying them and our wxFontEncodings they
620 // correspond to which is used by GetFontForEncoding() function
621 // ----------------------------------------------------------------------------
625 bool wxFontMapper::TestAltEncoding(const wxString
& configEntry
,
626 wxFontEncoding encReplacement
,
627 wxNativeEncodingInfo
*info
)
629 if ( wxGetNativeFontEncoding(encReplacement
, info
) &&
630 wxTestFontEncoding(*info
) )
633 // remember the mapping in the config
634 wxFontMapperPathChanger
path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH
);
638 GetConfig()->Write(configEntry
, info
->ToString());
640 #endif // wxUSE_CONFIG
648 class ReentrancyBlocker
651 ReentrancyBlocker(bool& b
) : m_b(b
) { m_b
= TRUE
; }
652 ~ReentrancyBlocker() { m_b
= FALSE
; }
659 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
660 wxNativeEncodingInfo
*info
,
661 const wxString
& facename
,
665 // we need a flag to prevent infinite recursion which happens, for
666 // example, when GetAltForEncoding() is called from an OnPaint() handler:
667 // in this case, wxYield() which is called from wxMessageBox() we use here
668 // will lead to another call of OnPaint() and hence to another call of
669 // GetAltForEncoding() - and it is impossible to catch this from the user
670 // code because we are called from wxFont ctor implicitly.
672 // assume we're always called from the main thread, so that it is safe to
674 static bool s_inGetAltForEncoding
= FALSE
;
676 if ( interactive
&& s_inGetAltForEncoding
)
679 ReentrancyBlocker
blocker(s_inGetAltForEncoding
);
682 wxCHECK_MSG( info
, FALSE
, wxT("bad pointer in GetAltForEncoding") );
684 info
->facename
= facename
;
686 if ( encoding
== wxFONTENCODING_DEFAULT
)
688 encoding
= wxFont::GetDefaultEncoding();
691 // if we failed to load the system default encoding, something is really
692 // wrong and we'd better stop now - otherwise we will go into endless
693 // recursion trying to create the font in the msg box with the error
695 if ( encoding
== wxFONTENCODING_SYSTEM
)
697 wxFatalError(_("can't load any font, aborting"));
699 // wxFatalError doesn't return
702 wxString configEntry
, encName
= GetEncodingName(encoding
);
705 configEntry
= facename
+ _T("_");
707 configEntry
+= encName
;
710 // do we have a font spec for this encoding?
712 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
714 wxConfigBase
*config
= GetConfig();
716 wxString fontinfo
= config
->Read(configEntry
);
718 RestorePath(pathOld
);
720 // this special value means that we don't know of fonts for this
721 // encoding but, moreover, have already asked the user as well and he
722 // didn't specify any font neither
723 if ( fontinfo
== FONTMAPPER_FONT_DONT_ASK
)
727 else // use the info entered the last time
729 if ( !!fontinfo
&& !!facename
)
731 // we tried to find a match with facename - now try without it
732 fontinfo
= config
->Read(encName
);
737 if ( info
->FromString(fontinfo
) )
739 if ( wxTestFontEncoding(*info
) )
744 //else: no such fonts, look for something else
745 // (should we erase the outdated value?)
749 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
753 //else: there is no information in config about this encoding
756 #endif // wxUSE_CONFIG
762 wxString
title(m_titleDialog
);
764 title
<< wxTheApp
->GetAppName() << _(": unknown encoding");
768 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)?"),
769 GetEncodingDescription(encoding
).c_str());
771 wxWindow
*parent
= m_windowParent
;
773 parent
= wxTheApp
->GetTopWindow();
775 if ( wxMessageBox(msg
, title
,
776 wxICON_QUESTION
| wxYES_NO
, parent
) == wxYES
)
779 data
.SetEncoding(encoding
);
780 data
.EncodingInfo() = *info
;
781 wxFontDialog
dialog(parent
, &data
);
782 if ( dialog
.ShowModal() == wxID_OK
)
784 wxFontData retData
= dialog
.GetFontData();
785 wxFont font
= retData
.GetChosenFont();
787 *info
= retData
.EncodingInfo();
788 info
-> encoding
= retData
.GetEncoding();
791 // remember this in the config
792 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
794 GetConfig()->Write(configEntry
, info
->ToString());
796 RestorePath(pathOld
);
798 #endif // wxUSE_CONFIG
802 //else: the user canceled the font selection dialog
806 // the user doesn't want to select a font for this encoding,
807 // remember it to avoid asking the same question again later
809 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH
, &pathOld
) )
811 GetConfig()->Write(configEntry
, FONTMAPPER_FONT_DONT_ASK
);
813 RestorePath(pathOld
);
815 #endif // wxUSE_CONFIG
818 //else: we're in non-interactive mode
819 #endif // wxUSE_FONTDLG
821 // now try the default mappings:
822 wxFontEncodingArray equiv
= wxEncodingConverter::GetAllEquivalents(encoding
);
823 size_t count
= equiv
.GetCount();
826 for ( size_t i
= (equiv
[0] == encoding
) ? 1 : 0; i
< count
; i
++ )
828 if ( TestAltEncoding(configEntry
, equiv
[i
], info
) )
836 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding
,
837 wxFontEncoding
*alt_encoding
,
838 const wxString
& facename
,
841 wxNativeEncodingInfo info
;
842 bool r
= GetAltForEncoding(encoding
, &info
, facename
, interactive
);
843 *alt_encoding
= info
.encoding
;
847 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding
,
848 const wxString
& facename
)
850 wxNativeEncodingInfo info
;
852 if (wxGetNativeFontEncoding(encoding
, &info
))
854 info
.facename
= facename
;
855 return wxTestFontEncoding(info
);
863 #endif // wxUSE_FONTMAP