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