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