1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/fontmap.cpp
3 // Purpose:     wxFontMapper class
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     04.11.99
7 // Copyright:   (c) 1999-2003 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 #ifdef __BORLANDC__
23     #pragma hdrstop
24 #endif
25 
26 #if wxUSE_FONTMAP
27 
28 #include "wx/fontmap.h"
29 
30 #ifndef WX_PRECOMP
31     #include "wx/app.h"
32     #include "wx/log.h"
33     #include "wx/intl.h"
34     #include "wx/msgdlg.h"
35     #include "wx/choicdlg.h"
36 #endif // PCH
37 
38 #if wxUSE_CONFIG
39     #include "wx/config.h"
40 #endif // wxUSE_CONFIG
41 
42 #if defined(__WXMSW__)
43   #include  "wx/msw/private.h"  // includes windows.h for LOGFONT
44   #include  "wx/msw/winundef.h"
45 #endif
46 
47 #include "wx/fmappriv.h"
48 #include "wx/fontutil.h"
49 #include "wx/fontdlg.h"
50 #include "wx/encinfo.h"
51 
52 #include "wx/encconv.h"
53 
54 // ----------------------------------------------------------------------------
55 // XTI
56 // ----------------------------------------------------------------------------
57 
58 wxBEGIN_ENUM( wxFontEncoding )
59 wxENUM_MEMBER( wxFONTENCODING_SYSTEM )
60 wxENUM_MEMBER( wxFONTENCODING_DEFAULT )
61 
62 wxENUM_MEMBER( wxFONTENCODING_ISO8859_1 )
63 wxENUM_MEMBER( wxFONTENCODING_ISO8859_2 )
64 wxENUM_MEMBER( wxFONTENCODING_ISO8859_3 )
65 wxENUM_MEMBER( wxFONTENCODING_ISO8859_4 )
66 wxENUM_MEMBER( wxFONTENCODING_ISO8859_5 )
67 wxENUM_MEMBER( wxFONTENCODING_ISO8859_6 )
68 wxENUM_MEMBER( wxFONTENCODING_ISO8859_7 )
69 wxENUM_MEMBER( wxFONTENCODING_ISO8859_8 )
70 wxENUM_MEMBER( wxFONTENCODING_ISO8859_9 )
71 wxENUM_MEMBER( wxFONTENCODING_ISO8859_10 )
72 wxENUM_MEMBER( wxFONTENCODING_ISO8859_11 )
73 wxENUM_MEMBER( wxFONTENCODING_ISO8859_12 )
74 wxENUM_MEMBER( wxFONTENCODING_ISO8859_13 )
75 wxENUM_MEMBER( wxFONTENCODING_ISO8859_14 )
76 wxENUM_MEMBER( wxFONTENCODING_ISO8859_15 )
77 wxENUM_MEMBER( wxFONTENCODING_ISO8859_MAX )
78 wxENUM_MEMBER( wxFONTENCODING_KOI8 )
79 wxENUM_MEMBER( wxFONTENCODING_KOI8_U )
80 wxENUM_MEMBER( wxFONTENCODING_ALTERNATIVE )
81 wxENUM_MEMBER( wxFONTENCODING_BULGARIAN )
82 wxENUM_MEMBER( wxFONTENCODING_CP437 )
83 wxENUM_MEMBER( wxFONTENCODING_CP850 )
84 wxENUM_MEMBER( wxFONTENCODING_CP852 )
85 wxENUM_MEMBER( wxFONTENCODING_CP855 )
86 wxENUM_MEMBER( wxFONTENCODING_CP866 )
87 
88 wxENUM_MEMBER( wxFONTENCODING_CP874 )
89 wxENUM_MEMBER( wxFONTENCODING_CP932 )
90 wxENUM_MEMBER( wxFONTENCODING_CP936 )
91 wxENUM_MEMBER( wxFONTENCODING_CP949 )
92 wxENUM_MEMBER( wxFONTENCODING_CP950 )
93 wxENUM_MEMBER( wxFONTENCODING_CP1250 )
94 wxENUM_MEMBER( wxFONTENCODING_CP1251 )
95 wxENUM_MEMBER( wxFONTENCODING_CP1252 )
96 wxENUM_MEMBER( wxFONTENCODING_CP1253 )
97 wxENUM_MEMBER( wxFONTENCODING_CP1254 )
98 wxENUM_MEMBER( wxFONTENCODING_CP1255 )
99 wxENUM_MEMBER( wxFONTENCODING_CP1256 )
100 wxENUM_MEMBER( wxFONTENCODING_CP1257 )
101 wxENUM_MEMBER( wxFONTENCODING_CP1258 )
102 wxENUM_MEMBER( wxFONTENCODING_CP1361 )
103 wxENUM_MEMBER( wxFONTENCODING_CP12_MAX )
104 wxENUM_MEMBER( wxFONTENCODING_UTF7 )
105 wxENUM_MEMBER( wxFONTENCODING_UTF8 )
106 wxENUM_MEMBER( wxFONTENCODING_GB2312 )
107 wxENUM_MEMBER( wxFONTENCODING_BIG5 )
108 wxENUM_MEMBER( wxFONTENCODING_SHIFT_JIS )
109 wxENUM_MEMBER( wxFONTENCODING_EUC_JP )
110 wxENUM_MEMBER( wxFONTENCODING_UNICODE )
111 wxEND_ENUM( wxFontEncoding )
112 
113 // ----------------------------------------------------------------------------
114 // constants
115 // ----------------------------------------------------------------------------
116 
117 // the config paths we use
118 #if wxUSE_CONFIG
119 
120 static const wxChar* FONTMAPPER_FONT_FROM_ENCODING_PATH = wxT("Encodings");
121 static const wxChar* FONTMAPPER_FONT_DONT_ASK = wxT("none");
122 
123 #endif // wxUSE_CONFIG
124 
125 // ----------------------------------------------------------------------------
126 // private classes
127 // ----------------------------------------------------------------------------
128 
129 // it may happen that while we're showing a dialog asking the user about
130 // something, another request for an encoding mapping arrives: in this case it
131 // is best to not do anything because otherwise we risk to enter an infinite
132 // loop so we create an object of this class on stack to test for this in all
133 // interactive functions
134 class ReentrancyBlocker
135 {
136 public:
ReentrancyBlocker(bool & flag)137     ReentrancyBlocker(bool& flag) : m_flagOld(flag), m_flag(flag)
138         { m_flag = true; }
~ReentrancyBlocker()139     ~ReentrancyBlocker() { m_flag = m_flagOld; }
140 
141 private:
142     bool m_flagOld;
143     bool& m_flag;
144 
145     wxDECLARE_NO_COPY_CLASS(ReentrancyBlocker);
146 };
147 
148 // ============================================================================
149 // implementation
150 // ============================================================================
151 
152 // ----------------------------------------------------------------------------
153 // ctor and dtor
154 // ----------------------------------------------------------------------------
155 
wxFontMapper()156 wxFontMapper::wxFontMapper()
157 {
158     m_windowParent = NULL;
159 }
160 
~wxFontMapper()161 wxFontMapper::~wxFontMapper()
162 {
163 }
164 
165 /* static */
Get()166 wxFontMapper *wxFontMapper::Get()
167 {
168     wxFontMapperBase *fontmapper = wxFontMapperBase::Get();
169     wxASSERT_MSG( !fontmapper->IsDummy(),
170                  wxT("GUI code requested a wxFontMapper but we only have a wxFontMapperBase.") );
171 
172     // Now return it anyway because there's a chance the GUI code might just
173     // only want to call wxFontMapperBase functions and it's better than
174     // crashing by returning NULL
175     return (wxFontMapper *)fontmapper;
176 }
177 
178 wxFontEncoding
CharsetToEncoding(const wxString & charset,bool interactive)179 wxFontMapper::CharsetToEncoding(const wxString& charset, bool interactive)
180 {
181     // try the ways not needing the users intervention first
182     int encoding = wxFontMapperBase::NonInteractiveCharsetToEncoding(charset);
183 
184     // if we failed to find the encoding, ask the user -- unless disabled
185     if ( encoding == wxFONTENCODING_UNKNOWN )
186     {
187         // this is the special value which disables asking the user (he had
188         // chosen to suppress this the last time)
189         encoding = wxFONTENCODING_SYSTEM;
190     }
191 #if wxUSE_CHOICEDLG
192     else if ( (encoding == wxFONTENCODING_SYSTEM) && interactive )
193     {
194         // prepare the dialog data
195 
196         // the dialog title
197         wxString title(m_titleDialog);
198         if ( !title )
199             title << wxTheApp->GetAppDisplayName() << _(": unknown charset");
200 
201         // the message
202         wxString msg;
203         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);
204 
205         // the list of choices
206         const size_t count = GetSupportedEncodingsCount();
207 
208         wxString *encodingNamesTranslated = new wxString[count];
209 
210         for ( size_t i = 0; i < count; i++ )
211         {
212             encodingNamesTranslated[i] = GetEncodingDescription(GetEncoding(i));
213         }
214 
215         // the parent window
216         wxWindow *parent = m_windowParent;
217         if ( !parent )
218             parent = wxTheApp->GetTopWindow();
219 
220         // do ask the user and get back the index in encodings table
221         int n = wxGetSingleChoiceIndex(msg, title,
222                                        count,
223                                        encodingNamesTranslated,
224                                        parent);
225 
226         delete [] encodingNamesTranslated;
227 
228         if ( n != -1 )
229         {
230             encoding = GetEncoding(n);
231         }
232 
233 #if wxUSE_CONFIG && wxUSE_FILECONFIG
234         // save the result in the config now
235         wxFontMapperPathChanger path(this, FONTMAPPER_CHARSET_PATH);
236         if ( path.IsOk() )
237         {
238             wxConfigBase *config = GetConfig();
239 
240             // remember the alt encoding for this charset -- or remember that
241             // we don't know it
242             long value = n == -1 ? (long)wxFONTENCODING_UNKNOWN : (long)encoding;
243             if ( !config->Write(charset, value) )
244             {
245                 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset);
246             }
247         }
248 #endif // wxUSE_CONFIG
249     }
250 #else
251     wxUnusedVar(interactive);
252 #endif // wxUSE_CHOICEDLG
253 
254     return (wxFontEncoding)encoding;
255 }
256 
257 // ----------------------------------------------------------------------------
258 // support for unknown encodings: we maintain a map between the
259 // (platform-specific) strings identifying them and our wxFontEncodings they
260 // correspond to which is used by GetFontForEncoding() function
261 // ----------------------------------------------------------------------------
262 
TestAltEncoding(const wxString & configEntry,wxFontEncoding encReplacement,wxNativeEncodingInfo * info)263 bool wxFontMapper::TestAltEncoding(const wxString& configEntry,
264                                    wxFontEncoding encReplacement,
265                                    wxNativeEncodingInfo *info)
266 {
267     if ( wxGetNativeFontEncoding(encReplacement, info) &&
268          wxTestFontEncoding(*info) )
269     {
270 #if wxUSE_CONFIG && wxUSE_FILECONFIG
271         // remember the mapping in the config
272         wxFontMapperPathChanger path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH);
273 
274         if ( path.IsOk() )
275         {
276             GetConfig()->Write(configEntry, info->ToString());
277         }
278 #else
279         wxUnusedVar(configEntry);
280 #endif // wxUSE_CONFIG
281         return true;
282     }
283 
284     return false;
285 }
286 
GetAltForEncoding(wxFontEncoding encoding,wxNativeEncodingInfo * info,const wxString & facename,bool interactive)287 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding,
288                                      wxNativeEncodingInfo *info,
289                                      const wxString& facename,
290                                      bool interactive)
291 {
292 #if wxUSE_GUI
293     // we need a flag to prevent infinite recursion which happens, for
294     // example, when GetAltForEncoding() is called from an OnPaint() handler:
295     // in this case, wxYield() which is called from wxMessageBox() we use here
296     // will lead to another call of OnPaint() and hence to another call of
297     // GetAltForEncoding() -- and it is impossible to catch this from the user
298     // code because we are called from wxFont ctor implicitly.
299 
300     // assume we're always called from the main thread, so that it is safe to
301     // use a static var
302     static bool s_inGetAltForEncoding = false;
303 
304     if ( interactive && s_inGetAltForEncoding )
305         return false;
306 
307     ReentrancyBlocker blocker(s_inGetAltForEncoding);
308 #endif // wxUSE_GUI
309 
310     wxCHECK_MSG( info, false, wxT("bad pointer in GetAltForEncoding") );
311 
312     info->facename = facename;
313 
314     if ( encoding == wxFONTENCODING_DEFAULT )
315     {
316         encoding = wxFont::GetDefaultEncoding();
317     }
318 
319     // if we failed to load the system default encoding, something is really
320     // wrong and we'd better stop now -- otherwise we will go into endless
321     // recursion trying to create the font in the msg box with the error
322     // message
323     if ( encoding == wxFONTENCODING_SYSTEM )
324     {
325         wxLogFatalError(_("can't load any font, aborting"));
326 
327         // wxLogFatalError doesn't return
328     }
329 
330     wxString configEntry,
331              encName = GetEncodingName(encoding);
332     if ( !facename.empty() )
333     {
334         configEntry = facename + wxT("_");
335     }
336     configEntry += encName;
337 
338 #if wxUSE_CONFIG && wxUSE_FILECONFIG
339     // do we have a font spec for this encoding?
340     wxString fontinfo;
341     wxFontMapperPathChanger path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH);
342     if ( path.IsOk() )
343     {
344         fontinfo = GetConfig()->Read(configEntry);
345     }
346 
347     // this special value means that we don't know of fonts for this
348     // encoding but, moreover, have already asked the user as well and he
349     // didn't specify any font neither
350     if ( fontinfo == FONTMAPPER_FONT_DONT_ASK )
351     {
352         interactive = false;
353     }
354     else // use the info entered the last time
355     {
356         if ( !fontinfo.empty() && !facename.empty() )
357         {
358             // we tried to find a match with facename -- now try without it
359             fontinfo = GetConfig()->Read(encName);
360         }
361 
362         if ( !fontinfo.empty() )
363         {
364             if ( info->FromString(fontinfo) )
365             {
366                 if ( wxTestFontEncoding(*info) )
367                 {
368                     // ok, got something
369                     return true;
370                 }
371                 //else: no such fonts, look for something else
372                 //      (should we erase the outdated value?)
373             }
374             else
375             {
376                 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"),
377                            fontinfo);
378             }
379         }
380         //else: there is no information in config about this encoding
381     }
382 #endif // wxUSE_CONFIG
383 
384     // now try to map this encoding to a compatible one which we have on this
385     // system
386     wxFontEncodingArray equiv = wxEncodingConverter::GetAllEquivalents(encoding);
387     size_t count = equiv.GetCount();
388     bool foundEquivEncoding = false;
389     wxFontEncoding equivEncoding = wxFONTENCODING_SYSTEM;
390     if ( count )
391     {
392         for ( size_t i = 0; i < count && !foundEquivEncoding; i++ )
393         {
394             // don't test for encoding itself, we already know we don't have it
395             if ( equiv[i] == encoding )
396                 continue;
397 
398             if ( TestAltEncoding(configEntry, equiv[i], info) )
399             {
400                 equivEncoding = equiv[i];
401 
402                 foundEquivEncoding = true;
403             }
404         }
405     }
406 
407     // ask the user
408 #if wxUSE_FONTDLG
409     if ( interactive )
410     {
411         wxString title(m_titleDialog);
412         if ( !title )
413             title << wxTheApp->GetAppDisplayName() << _(": unknown encoding");
414 
415         // built the message
416         wxString encDesc = GetEncodingDescription(encoding),
417                  msg;
418         if ( foundEquivEncoding )
419         {
420             // ask the user if he wants to override found alternative encoding
421             msg.Printf(_("No font for displaying text in encoding '%s' found,\nbut an alternative encoding '%s' is available.\nDo you want to use this encoding (otherwise you will have to choose another one)?"),
422                        encDesc, GetEncodingDescription(equivEncoding));
423         }
424         else
425         {
426             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)?"),
427                        encDesc);
428         }
429 
430         // the question is different in 2 cases so the answer has to be
431         // interpreted differently as well
432         int answer = foundEquivEncoding ? wxNO : wxYES;
433 
434         if ( wxMessageBox(msg, title,
435                           wxICON_QUESTION | wxYES_NO,
436                           m_windowParent) == answer )
437         {
438             wxFontData data;
439             data.SetEncoding(encoding);
440             data.EncodingInfo() = *info;
441             wxFontDialog dialog(m_windowParent, data);
442             if ( dialog.ShowModal() == wxID_OK )
443             {
444                 wxFontData retData = dialog.GetFontData();
445 
446                 *info = retData.EncodingInfo();
447                 info->encoding = retData.GetEncoding();
448 
449 #if wxUSE_CONFIG && wxUSE_FILECONFIG
450                 // remember this in the config
451                 wxFontMapperPathChanger path2(this,
452                                               FONTMAPPER_FONT_FROM_ENCODING_PATH);
453                 if ( path2.IsOk() )
454                 {
455                     GetConfig()->Write(configEntry, info->ToString());
456                 }
457 #endif // wxUSE_CONFIG
458 
459                 return true;
460             }
461             //else: the user canceled the font selection dialog
462         }
463         else
464         {
465             // the user doesn't want to select a font for this encoding
466             // or selected to use equivalent encoding
467             //
468             // remember it to avoid asking the same question again later
469 #if wxUSE_CONFIG && wxUSE_FILECONFIG
470             wxFontMapperPathChanger path2(this,
471                                           FONTMAPPER_FONT_FROM_ENCODING_PATH);
472             if ( path2.IsOk() )
473             {
474                 GetConfig()->Write
475                              (
476                                 configEntry,
477                                 foundEquivEncoding
478                                     ? (const wxChar*)info->ToString().c_str()
479                                     : FONTMAPPER_FONT_DONT_ASK
480                              );
481             }
482 #endif // wxUSE_CONFIG
483         }
484     }
485     //else: we're in non-interactive mode
486 #else
487     wxUnusedVar(equivEncoding);
488 #endif // wxUSE_FONTDLG
489 
490     return foundEquivEncoding;
491 }
492 
GetAltForEncoding(wxFontEncoding encoding,wxFontEncoding * encodingAlt,const wxString & facename,bool interactive)493 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding,
494                                      wxFontEncoding *encodingAlt,
495                                      const wxString& facename,
496                                      bool interactive)
497 {
498     wxCHECK_MSG( encodingAlt, false,
499                     wxT("wxFontEncoding::GetAltForEncoding(): NULL pointer") );
500 
501     wxNativeEncodingInfo info;
502     if ( !GetAltForEncoding(encoding, &info, facename, interactive) )
503         return false;
504 
505     *encodingAlt = info.encoding;
506 
507     return true;
508 }
509 
IsEncodingAvailable(wxFontEncoding encoding,const wxString & facename)510 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding,
511                                        const wxString& facename)
512 {
513     wxNativeEncodingInfo info;
514 
515     if ( !wxGetNativeFontEncoding(encoding, &info) )
516         return false;
517 
518     info.facename = facename;
519     return wxTestFontEncoding(info);
520 }
521 
522 #endif // wxUSE_FONTMAP
523