fixed off by 1 error in CharsetToEncoding() for CP encodings
[wxWidgets.git] / src / common / fontmap.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/fontmap.cpp
3 // Purpose: wxFontMapper class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 04.11.99
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "fontmap.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/app.h"
33 #include "wx/log.h"
34 #include "wx/intl.h"
35 #endif // PCH
36
37 #include "wx/fontmap.h"
38
39 #if wxUSE_CONFIG
40 #include "wx/config.h"
41 #include "wx/memconf.h"
42 #endif
43
44 #if wxUSE_GUI
45 #include "wx/msgdlg.h"
46 #include "wx/fontdlg.h"
47 #include "wx/choicdlg.h"
48 #endif // wxUSE_GUI
49
50 #include "wx/encconv.h"
51
52 // ----------------------------------------------------------------------------
53 // constants
54 // ----------------------------------------------------------------------------
55
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");
60 #if wxUSE_GUI
61 static const wxChar* FONTMAPPER_FONT_FROM_ENCODING_PATH = wxT("Encodings");
62 #endif // wxUSE_GUI
63
64 // encodings supported by GetEncodingDescription
65 static wxFontEncoding gs_encodings[] =
66 {
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,
82 wxFONTENCODING_KOI8,
83 wxFONTENCODING_CP1250,
84 wxFONTENCODING_CP1251,
85 wxFONTENCODING_CP1252,
86 wxFONTENCODING_CP1253,
87 wxFONTENCODING_CP1254,
88 wxFONTENCODING_CP1255,
89 wxFONTENCODING_CP1256,
90 wxFONTENCODING_CP1257,
91 wxFONTENCODING_CP437,
92 };
93
94 // the descriptions for them
95 static const wxChar* gs_encodingDescs[] =
96 {
97 wxTRANSLATE( "Western European (ISO-8859-1/Latin 1)" ),
98 wxTRANSLATE( "Central European (ISO-8859-2/Latin 2)" ),
99 wxTRANSLATE( "Esperanto (ISO-8859-3)" ),
100 wxTRANSLATE( "Baltic (old) (ISO-8859-4)" ),
101 wxTRANSLATE( "Cyrillic (Latin 5)" ),
102 wxTRANSLATE( "Arabic (ISO-8859-6)" ),
103 wxTRANSLATE( "Greek (ISO-8859-7)" ),
104 wxTRANSLATE( "Hebrew (ISO-8859-8)" ),
105 wxTRANSLATE( "Turkish (ISO-8859-9)" ),
106 wxTRANSLATE( "Nordic (ISO-8859-10)" ),
107 wxTRANSLATE( "Thai (ISO-8859-11)" ),
108 wxTRANSLATE( "Indian (ISO-8859-12)" ),
109 wxTRANSLATE( "Baltic (ISO-8859-13)" ),
110 wxTRANSLATE( "Celtic (ISO-8859-14)" ),
111 wxTRANSLATE( "Western European with Euro (ISO-8859-15/Latin 0)" ),
112 wxTRANSLATE( "KOI8-R" ),
113 wxTRANSLATE( "Windows Central European (CP 1250)" ),
114 wxTRANSLATE( "Windows Cyrillic (CP 1251)" ),
115 wxTRANSLATE( "Windows Western European (CP 1252)" ),
116 wxTRANSLATE( "Windows Greek (CP 1253)" ),
117 wxTRANSLATE( "Windows Turkish (CP 1254)" ),
118 wxTRANSLATE( "Windows Hebrew (CP 1255)" ),
119 wxTRANSLATE( "Windows Arabic (CP 1256)" ),
120 wxTRANSLATE( "Windows Baltic (CP 1257)" ),
121 wxTRANSLATE( "Windows/DOS OEM (CP 437)" ),
122 };
123
124 // and the internal names
125 static const wxChar* gs_encodingNames[] =
126 {
127 wxT( "iso8859-1" ),
128 wxT( "iso8859-2" ),
129 wxT( "iso8859-3" ),
130 wxT( "iso8859-4" ),
131 wxT( "iso8859-5" ),
132 wxT( "iso8859-6" ),
133 wxT( "iso8859-7" ),
134 wxT( "iso8859-8" ),
135 wxT( "iso8859-9" ),
136 wxT( "iso8859-10" ),
137 wxT( "iso8859-11" ),
138 wxT( "iso8859-12" ),
139 wxT( "iso8859-13" ),
140 wxT( "iso8859-14" ),
141 wxT( "iso8859-15" ),
142 wxT( "koi8-r" ),
143 wxT( "windows1250" ),
144 wxT( "windows1251" ),
145 wxT( "windows1252" ),
146 wxT( "windows1253" ),
147 wxT( "windows1254" ),
148 wxT( "windows1255" ),
149 wxT( "windows1256" ),
150 wxT( "windows1257" ),
151 wxT( "windows437" ),
152 };
153
154 // ----------------------------------------------------------------------------
155 // global data
156 // ----------------------------------------------------------------------------
157
158 // private object
159 static wxFontMapper gs_fontMapper;
160
161 // and public pointer
162 wxFontMapper * wxTheFontMapper = &gs_fontMapper;
163
164 // ----------------------------------------------------------------------------
165 // private classes
166 // ----------------------------------------------------------------------------
167
168 // change the config path during the lifetime of this object
169 class wxFontMapperPathChanger
170 {
171 public:
172 wxFontMapperPathChanger(wxFontMapper *fontMapper, const wxString& path)
173 {
174 m_fontMapper = fontMapper;
175 m_ok = m_fontMapper->ChangePath(path, &m_pathOld);
176 }
177
178 bool IsOk() const { return m_ok; }
179
180 ~wxFontMapperPathChanger()
181 {
182 if ( IsOk() )
183 m_fontMapper->RestorePath(m_pathOld);
184 }
185
186 private:
187 wxFontMapper *m_fontMapper;
188 bool m_ok;
189 wxString m_pathOld;
190 };
191
192 // ============================================================================
193 // implementation
194 // ============================================================================
195
196 // ----------------------------------------------------------------------------
197 // ctor and dtor
198 // ----------------------------------------------------------------------------
199
200 wxFontMapper::wxFontMapper()
201 {
202 #if wxUSE_CONFIG
203 m_config = NULL;
204 #endif // wxUSE_CONFIG
205
206 #if wxUSE_GUI
207 m_windowParent = NULL;
208 #endif // wxUSE_GUI
209 }
210
211 wxFontMapper::~wxFontMapper()
212 {
213 }
214
215 // ----------------------------------------------------------------------------
216 // customisation
217 // ----------------------------------------------------------------------------
218
219 #if wxUSE_CONFIG
220
221 /* static */ const wxChar *wxFontMapper::GetDefaultConfigPath()
222 {
223 return FONTMAPPER_ROOT_PATH;
224 }
225
226 void wxFontMapper::SetConfigPath(const wxString& prefix)
227 {
228 wxCHECK_RET( !prefix.IsEmpty() && prefix[0] == wxCONFIG_PATH_SEPARATOR,
229 wxT("an absolute path should be given to wxFontMapper::SetConfigPath()") );
230
231 m_configRootPath = prefix;
232 }
233
234 // ----------------------------------------------------------------------------
235 // get config object and path for it
236 // ----------------------------------------------------------------------------
237
238 wxConfigBase *wxFontMapper::GetConfig()
239 {
240 if ( !m_config )
241 {
242 // try the default
243 m_config = wxConfig::Get(FALSE /*don't create on demand*/ );
244
245 if ( !m_config )
246 {
247 // we still want to have a config object because otherwise we would
248 // keep asking the user the same questions in the interactive mode,
249 // so create a dummy config which won't write to any files/registry
250 // but will allow us to remember the results of the questions at
251 // least during this run
252 m_config = new wxMemoryConfig;
253 wxConfig::Set(m_config);
254 }
255 }
256
257 return m_config;
258 }
259
260 const wxString& wxFontMapper::GetConfigPath()
261 {
262 if ( !m_configRootPath )
263 {
264 // use the default
265 m_configRootPath = GetDefaultConfigPath();
266 }
267
268 return m_configRootPath;
269 }
270 #endif
271
272 bool wxFontMapper::ChangePath(const wxString& pathNew, wxString *pathOld)
273 {
274 #if wxUSE_CONFIG
275 wxConfigBase *config = GetConfig();
276 if ( !config )
277 return FALSE;
278
279 *pathOld = config->GetPath();
280
281 wxString path = GetConfigPath();
282 if ( path.IsEmpty() || path.Last() != wxCONFIG_PATH_SEPARATOR )
283 {
284 path += wxCONFIG_PATH_SEPARATOR;
285 }
286
287 wxASSERT_MSG( !pathNew || (pathNew[0] != wxCONFIG_PATH_SEPARATOR),
288 wxT("should be a relative path") );
289
290 path += pathNew;
291
292 config->SetPath(path);
293
294 return TRUE;
295 #else
296 return FALSE;
297 #endif
298 }
299
300 void wxFontMapper::RestorePath(const wxString& pathOld)
301 {
302 #if wxUSE_CONFIG
303 GetConfig()->SetPath(pathOld);
304 #else
305 #endif
306 }
307
308 // ----------------------------------------------------------------------------
309 // charset/encoding correspondence
310 // ----------------------------------------------------------------------------
311
312 /* static */
313 wxString wxFontMapper::GetEncodingDescription(wxFontEncoding encoding)
314 {
315 size_t count = WXSIZEOF(gs_encodingDescs);
316
317 wxASSERT_MSG( count == WXSIZEOF(gs_encodings),
318 wxT("inconsitency detected - forgot to update one of the arrays?") );
319
320 for ( size_t i = 0; i < count; i++ )
321 {
322 if ( gs_encodings[i] == encoding )
323 {
324 return wxGetTranslation(gs_encodingDescs[i]);
325 }
326 }
327
328 wxString str;
329 str.Printf(_("Unknown encoding (%d)"), encoding);
330
331 return str;
332 }
333
334 /* static */
335 wxString wxFontMapper::GetEncodingName(wxFontEncoding encoding)
336 {
337 size_t count = WXSIZEOF(gs_encodingNames);
338
339 wxASSERT_MSG( count == WXSIZEOF(gs_encodings),
340 wxT("inconsistency detected - forgot to update one of the arrays?") );
341
342 for ( size_t i = 0; i < count; i++ )
343 {
344 if ( gs_encodings[i] == encoding )
345 {
346 return wxGetTranslation(gs_encodingNames[i]);
347 }
348 }
349
350 wxString str;
351 str.Printf(_("unknown-%d"), encoding);
352
353 return str;
354 }
355
356 wxFontEncoding wxFontMapper::CharsetToEncoding(const wxString& charset,
357 bool interactive)
358 {
359 wxFontEncoding encoding = wxFONTENCODING_SYSTEM;
360
361 // we're going to modify it, make a copy
362 wxString cs = charset;
363
364 #if wxUSE_CONFIG
365 // first try the user-defined settings
366 wxString pathOld;
367 if ( ChangePath(FONTMAPPER_CHARSET_PATH, &pathOld) )
368 {
369 wxConfigBase *config = GetConfig();
370
371 // do we have an encoding for this charset?
372 long value = config->Read(charset, -1l);
373 if ( value != -1 )
374 {
375 if ( value >= 0 && value <= wxFONTENCODING_MAX )
376 {
377 encoding = (wxFontEncoding)value;
378 }
379 else
380 {
381 wxLogDebug(wxT("corrupted config data: invalid encoding %ld for charset '%s' ignored"),
382 value, charset.c_str());
383 }
384 }
385
386 if ( encoding == wxFONTENCODING_SYSTEM )
387 {
388 // may be we have an alias?
389 config->SetPath(FONTMAPPER_CHARSET_ALIAS_PATH);
390
391 wxString alias = config->Read(charset);
392 if ( !!alias )
393 {
394 // yes, we do - use it instead
395 cs = alias;
396 }
397 }
398
399 RestorePath(pathOld);
400 }
401 #endif
402
403 // if didn't find it there, try to reckognise it ourselves
404 if ( encoding == wxFONTENCODING_SYSTEM )
405 {
406 // discard the optional quotes
407 if ( !!cs )
408 {
409 if ( cs[0u] == _T('"') && cs.Last() == _T('"') )
410 {
411 cs = wxString(cs.c_str(), cs.length() - 1);
412 }
413 }
414
415 cs.MakeUpper();
416
417 if ( !cs || cs == wxT("US-ASCII") )
418 encoding = wxFONTENCODING_DEFAULT;
419 else if ( cs == wxT("KOI8-R") || cs == wxT("KOI8-U") )
420 encoding = wxFONTENCODING_KOI8;
421 else if ( cs.Left(3) == wxT("ISO") )
422 {
423 // the dash is optional (or, to be exact, it is not, but
424 // several brokenmails "forget" it)
425 const wxChar *p = cs.c_str() + 3;
426 if ( *p == wxT('-') )
427 p++;
428
429 unsigned int value;
430 if ( wxSscanf(p, wxT("8859-%u"), &value) == 1 )
431 {
432 if ( value < wxFONTENCODING_ISO8859_MAX -
433 wxFONTENCODING_ISO8859_1 )
434 {
435 // it's a valid ISO8859 encoding
436 value += wxFONTENCODING_ISO8859_1 - 1;
437 encoding = (wxFontEncoding)value;
438 }
439 }
440 }
441 else if ( cs.Left(8) == wxT("WINDOWS-") )
442 {
443 int value;
444 if ( wxSscanf(cs.c_str() + 8, wxT("%u"), &value) == 1 )
445 {
446 if ( value >= 1250 )
447 {
448 value -= 1250;
449 if ( value < wxFONTENCODING_CP12_MAX -
450 wxFONTENCODING_CP1250 )
451 {
452 // a valid Windows code page
453 value += wxFONTENCODING_CP1250;
454 encoding = (wxFontEncoding)value;
455 }
456 }
457 }
458 }
459 //else: unknown
460 }
461
462 #if wxUSE_GUI
463 // if still no luck, ask the user - unless disabled
464 if ( (encoding == wxFONTENCODING_SYSTEM) && interactive )
465 {
466 // prepare the dialog data
467
468 // the dialog title
469 wxString title(m_titleDialog);
470 if ( !title )
471 title << wxTheApp->GetAppName() << _(": unknown charset");
472
473 // the message
474 wxString msg;
475 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());
476
477 // the list of choices
478 size_t count = WXSIZEOF(gs_encodingDescs);
479
480 wxASSERT_MSG( count == WXSIZEOF(gs_encodings),
481 wxT("inconsitency detected - forgot to update one of the arrays?") );
482
483 wxString *encodingNamesTranslated = new wxString[count];
484
485 for ( size_t i = 0; i < count; i++ )
486 {
487 encodingNamesTranslated[i] = wxGetTranslation(gs_encodingDescs[i]);
488 }
489
490 // the parent window
491 wxWindow *parent = m_windowParent;
492 if ( !parent )
493 parent = wxTheApp->GetTopWindow();
494
495 // do ask the user and get back the index in encodings table
496 int n = wxGetSingleChoiceIndex(msg, title,
497 count,
498 encodingNamesTranslated,
499 parent);
500
501 delete [] encodingNamesTranslated;
502
503 if ( n != -1 )
504 {
505 encoding = gs_encodings[n];
506
507 #if wxUSE_CONFIG
508 // save the result in the config now
509 if ( ChangePath(FONTMAPPER_CHARSET_PATH, &pathOld) )
510 {
511 wxConfigBase *config = GetConfig();
512
513 // remember the alt encoding for this charset
514 if ( !config->Write(charset, (long)encoding) )
515 {
516 wxLogError(_("Failed to remember the encoding for the charset '%s'."), charset.c_str());
517 }
518
519 RestorePath(pathOld);
520 }
521 #endif // wxUSE_CONFIG
522 }
523 //else: cancelled
524 }
525 #endif // wxUSE_GUI
526
527 return encoding;
528 }
529
530 // ----------------------------------------------------------------------------
531 // support for unknown encodings: we maintain a map between the
532 // (platform-specific) strings identifying them and our wxFontEncodings they
533 // correspond to which is used by GetFontForEncoding() function
534 // ----------------------------------------------------------------------------
535
536 #if wxUSE_GUI
537
538 bool wxFontMapper::TestAltEncoding(const wxString& configEntry,
539 wxFontEncoding encReplacement,
540 wxNativeEncodingInfo *info)
541 {
542 if ( wxGetNativeFontEncoding(encReplacement, info) &&
543 wxTestFontEncoding(*info) )
544 {
545 #if wxUSE_CONFIG
546 // remember the mapping in the config
547 wxFontMapperPathChanger path(this, FONTMAPPER_FONT_FROM_ENCODING_PATH);
548
549 if ( path.IsOk() )
550 {
551 GetConfig()->Write(configEntry, info->ToString());
552 }
553 #endif // wxUSE_CONFIG
554 return TRUE;
555 }
556
557 return FALSE;
558 }
559
560 #if wxUSE_GUI
561 class ReentrancyBlocker
562 {
563 public:
564 ReentrancyBlocker(bool& b) : m_b(b) { m_b = TRUE; }
565 ~ReentrancyBlocker() { m_b = FALSE; }
566
567 private:
568 bool& m_b;
569 };
570 #endif
571
572 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding,
573 wxNativeEncodingInfo *info,
574 const wxString& facename,
575 bool interactive)
576 {
577 #if wxUSE_GUI
578 // we need a flag to prevent infinite recursion which happens, for
579 // example, when GetAltForEncoding() is called from an OnPaint() handler:
580 // in this case, wxYield() which is called from wxMessageBox() we use here
581 // will lead to another call of OnPaint() and hence to another call of
582 // GetAltForEncoding() - and it is impossible to catch this from the user
583 // code because we are called from wxFont ctor implicitly.
584
585 // assume we're always called from the main thread, so that it is safe to
586 // use a static var
587 static bool s_inGetAltForEncoding = FALSE;
588
589 if ( interactive && s_inGetAltForEncoding )
590 return FALSE;
591
592 ReentrancyBlocker blocker(s_inGetAltForEncoding);
593 #endif // wxUSE_GUI
594
595 wxCHECK_MSG( info, FALSE, wxT("bad pointer in GetAltForEncoding") );
596
597 info->facename = facename;
598
599 if ( encoding == wxFONTENCODING_DEFAULT )
600 {
601 encoding = wxFont::GetDefaultEncoding();
602 }
603
604 // if we failed to load the system default encoding, something is really
605 // wrong and we'd better stop now - otherwise we will go into endless
606 // recursion trying to create the font in the msg box with the error
607 // message
608 if ( encoding == wxFONTENCODING_SYSTEM )
609 {
610 wxFatalError(_("can't load any font, aborting"));
611
612 // wxFatalError doesn't return
613 }
614
615 wxString configEntry, encName = GetEncodingName(encoding);
616 if ( !!facename )
617 {
618 configEntry = facename + _T("_");
619 }
620 configEntry += encName;
621
622 #if wxUSE_CONFIG
623 // do we have a font spec for this encoding?
624 wxString pathOld;
625 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH, &pathOld) )
626 {
627 wxConfigBase *config = GetConfig();
628
629 wxString fontinfo = config->Read(configEntry);
630
631 RestorePath(pathOld);
632
633 if ( !!fontinfo && !!facename )
634 {
635 // we tried to find a match with facename - now try without it
636 fontinfo = config->Read(encName);
637 }
638
639 if ( !!fontinfo )
640 {
641 if ( info->FromString(fontinfo) )
642 {
643 if ( wxTestFontEncoding(*info) )
644 {
645 // ok, got something
646 return TRUE;
647 }
648 //else: no such fonts, look for something else
649 }
650 else
651 {
652 wxLogDebug(wxT("corrupted config data: string '%s' is not a valid font encoding info"), fontinfo.c_str());
653 }
654 }
655 //else: there is no information in config about this encoding
656 }
657 #endif // wxUSE_CONFIG
658
659 // ask the user
660 if ( interactive )
661 {
662 wxString title(m_titleDialog);
663 if ( !title )
664 title << wxTheApp->GetAppName() << _(": unknown encoding");
665
666 // the message
667 wxString msg;
668 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)?"),
669 GetEncodingDescription(encoding).c_str());
670
671 wxWindow *parent = m_windowParent;
672 if ( !parent )
673 parent = wxTheApp->GetTopWindow();
674
675 if ( wxMessageBox(msg, title,
676 wxICON_QUESTION | wxYES_NO, parent) == wxYES )
677 {
678 wxFontData data;
679 data.SetEncoding(encoding);
680 data.EncodingInfo() = *info;
681 wxFontDialog dialog(parent, &data);
682 if ( dialog.ShowModal() == wxID_OK )
683 {
684 wxFontData retData = dialog.GetFontData();
685 wxFont font = retData.GetChosenFont();
686
687 *info = retData.EncodingInfo();
688 info -> encoding = retData.GetEncoding();
689
690 #if wxUSE_CONFIG
691 // remember this in the config
692 if ( ChangePath(FONTMAPPER_FONT_FROM_ENCODING_PATH, &pathOld) )
693 {
694 GetConfig()->Write(configEntry, info->ToString());
695
696 RestorePath(pathOld);
697 }
698 #endif
699
700 return TRUE;
701 }
702 //else: the user canceled the font selection dialog
703 }
704 //else: the user doesn't want to select a font
705 }
706 //else: we're in non-interactive mode
707
708 // now try the default mappings:
709 wxFontEncodingArray equiv = wxEncodingConverter::GetAllEquivalents(encoding);
710 size_t count = equiv.GetCount();
711 if ( count )
712 {
713 for ( size_t i = (equiv[0] == encoding) ? 1 : 0; i < count; i++ )
714 {
715 if ( TestAltEncoding(configEntry, equiv[i], info) )
716 return TRUE;
717 }
718 }
719
720 return FALSE;
721 }
722
723 bool wxFontMapper::GetAltForEncoding(wxFontEncoding encoding,
724 wxFontEncoding *alt_encoding,
725 const wxString& facename,
726 bool interactive)
727 {
728 wxNativeEncodingInfo info;
729 bool r = GetAltForEncoding(encoding, &info, facename, interactive);
730 *alt_encoding = info.encoding;
731 return r;
732 }
733
734 bool wxFontMapper::IsEncodingAvailable(wxFontEncoding encoding,
735 const wxString& facename)
736 {
737 wxNativeEncodingInfo info;
738
739 if (wxGetNativeFontEncoding(encoding, &info))
740 {
741 info.facename = facename;
742 return wxTestFontEncoding(info);
743 }
744
745 return FALSE;
746 }
747
748 #endif // wxUSE_GUI