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