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