OpenCPN Partial API docs
Loading...
Searching...
No Matches
FontMgr.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 ***************************************************************************
6 * Copyright (C) 2013 by David S. Register *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 **************************************************************************/
23
24#include <locale>
25
26#include <wx/gdicmn.h>
27#include <wx/tokenzr.h>
28
29#include "FontMgr.h"
30#include "OCPNPlatform.h"
31#include "ocpn_plugin.h"
32
34 wxFont *font;
35 int pointsize_req;
36 wxFontStyle style_req;
37 wxFontWeight weight_req;
38 bool underline_req;
39};
40
42public:
43 wxFont *FindOrCreateFont(int pointSize, wxFontFamily family,
44 wxFontStyle style, wxFontWeight weight,
45 bool underline = false,
46 const wxString &face = wxEmptyString,
47 wxFontEncoding encoding = wxFONTENCODING_DEFAULT);
48 void FreeAll(void);
49
50private:
51 bool isCached(font_cache_record& record, int pointSize, wxFontFamily family,
52 wxFontStyle style, wxFontWeight weight,
53 bool underline, const wxString &facename,
54 wxFontEncoding encoding);
55
56 std::vector<font_cache_record> m_fontVector;
57};
58
59extern wxString g_locale;
60
61wxString s_locale;
62int g_default_font_size;
63wxString g_default_font_facename;
64
65FontMgr *FontMgr::instance = NULL;
66
67FontMgr &FontMgr::Get() {
68 if (!instance) instance = new FontMgr;
69 return *instance;
70}
71
72void FontMgr::Shutdown() {
73 if (instance) {
74 delete instance;
75 instance = NULL;
76 }
77}
78
79FontMgr::FontMgr() : m_wxFontCache(NULL), m_fontlist(NULL), pDefFont(NULL) {
80 // Create the list of fonts
81 m_fontlist = new FontList;
82 m_fontlist->DeleteContents(true);
83
84 s_locale = g_locale;
85
86 // Get a nice generic font as default
87 pDefFont = FindOrCreateFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
88 wxFONTWEIGHT_BOLD, FALSE, wxString(_T ( "" )),
89 wxFONTENCODING_SYSTEM);
90}
91
92FontMgr::~FontMgr() {
93 m_fontlist->Clear();
94 delete m_fontlist;
95
96 delete m_wxFontCache;
97}
98
99void FontMgr::SetLocale(wxString &newLocale) { s_locale = newLocale; }
100
101wxColour FontMgr::GetFontColor(const wxString &TextElement) const {
102
103 // Look thru the font list for a match
104 MyFontDesc *pmfd;
105 auto node = m_fontlist->GetFirst();
106 while (node) {
107 pmfd = node->GetData();
108 if (pmfd->m_dialogstring == TextElement) {
109 if (pmfd->m_configstring.BeforeFirst('-') == s_locale)
110 return pmfd->m_color;
111 }
112 node = node->GetNext();
113 }
114
115 return wxColour(0, 0, 0);
116}
117
118bool FontMgr::SetFontColor(const wxString &TextElement,
119 const wxColour color) const {
120 // Look thru the font list for a match
121 MyFontDesc *pmfd;
122 auto node = m_fontlist->GetFirst();
123 while (node) {
124 pmfd = node->GetData();
125 if (pmfd->m_dialogstring == TextElement) {
126 if (pmfd->m_configstring.BeforeFirst('-') == s_locale) {
127 pmfd->m_color = color;
128 return true;
129 }
130 }
131 node = node->GetNext();
132 }
133
134 return false;
135}
136
137wxString FontMgr::GetFontConfigKey(const wxString &description) {
138 // Create the configstring by combining the locale with
139 // a hash of the font description. Hash is used because the i18n
140 // description can contain characters that mess up the config file.
141
142 wxString configkey;
143 configkey = s_locale;
144 configkey.Append(_T("-"));
145
146 using namespace std;
147 locale loc;
148 const collate<char> &coll = use_facet<collate<char> >(loc);
149 // char cFontDesc[101];
150 // wcstombs( cFontDesc, description.c_str(), 100 );
151 // cFontDesc[100] = 0;
152
153 wxCharBuffer abuf = description.ToUTF8();
154
155 int fdLen = strlen(abuf);
156
157 configkey.Append(wxString::Format(
158 _T("%08lx"), coll.hash(abuf.data(), abuf.data() + fdLen)));
159 return configkey;
160}
161
162wxFont *FontMgr::GetFont(const wxString &TextElement, int user_default_size) {
163 // Look thru the font list for a match
164 MyFontDesc *pmfd;
165 auto node = m_fontlist->GetFirst();
166 while (node) {
167 pmfd = node->GetData();
168 if (pmfd->m_dialogstring == TextElement) {
169 if (pmfd->m_configstring.BeforeFirst('-') == s_locale)
170 return pmfd->m_font;
171 }
172 node = node->GetNext();
173 }
174
175 // Found no font, so create a nice one and add to the list
176 wxString configkey = GetFontConfigKey(TextElement);
177
178 // Now create a benign, always present native font
179 // with optional user requested default size
180
181 // Get the system default font.
182 wxFont sys_font = *wxNORMAL_FONT;
183 int sys_font_size = sys_font.GetPointSize();
184 wxString FaceName = sys_font.GetFaceName();
185
186 int new_size;
187 if (0 == user_default_size) {
188 if (g_default_font_size)
189 new_size = g_default_font_size;
190 else
191 new_size = sys_font_size;
192 } else
193 new_size = user_default_size;
194
195 if (g_default_font_facename.Length()) FaceName = g_default_font_facename;
196
197 wxString nativefont = GetSimpleNativeFont(new_size, FaceName);
198 wxFont *nf = wxFont::New(nativefont);
199
200 wxColor color = GetDefaultFontColor( TextElement);
201
202 MyFontDesc *pnewfd = new MyFontDesc(TextElement, configkey, nf, color);
203 m_fontlist->Append(pnewfd);
204
205 return pnewfd->m_font;
206}
207
208wxColour FontMgr::GetDefaultFontColor( const wxString &TextElement ){
209 wxColor defaultColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
210
211 // Special cases here
212 if(TextElement.IsSameAs( "Console Legend") )
213 defaultColor = wxColour( 0, 255, 0);
214 if(TextElement.IsSameAs( "Console Value") )
215 defaultColor = wxColour( 0, 255, 0);
216
217#ifdef __WXMAC__
218 // Override, to adjust for light/dark mode
219 return wxColour(0,0,0);
220#endif
221
222 return defaultColor;
223}
224
225wxString FontMgr::GetSimpleNativeFont(int size, wxString face) {
226 // Now create a benign, always present native string
227 wxString nativefont;
228
229 // this should work for all platforms
230 nativefont = wxFont(size, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
231 wxFONTWEIGHT_NORMAL, false, face)
232 .GetNativeFontInfoDesc();
233
234 return nativefont;
235}
236
237bool FontMgr::SetFont(const wxString &TextElement, wxFont *pFont,
238 wxColour color) {
239 // Look thru the font list for a match
240 MyFontDesc *pmfd;
241 auto node = m_fontlist->GetFirst();
242 while (node) {
243 pmfd = node->GetData();
244 if (pmfd->m_dialogstring == TextElement) {
245 if (pmfd->m_configstring.BeforeFirst('-') == s_locale) {
246 // Todo Think about this
247 //
248
249 // Cannot delete the present font, since it may be in use elsewhere
250 // This WILL leak....but only on font changes
251
252 // delete pmfd->m_font; // purge
253 // any old value
254
255 pmfd->m_font = pFont;
256 pmfd->m_nativeInfo = pFont->GetNativeFontInfoDesc();
257 pmfd->m_color = color;
258
259 return true;
260 }
261 }
262 node = node->GetNext();
263 }
264
265 return false;
266}
267
268int FontMgr::GetNumFonts(void) const { return m_fontlist->GetCount(); }
269
270const wxString &FontMgr::GetConfigString(int i) const {
271 MyFontDesc *pfd = m_fontlist->Item(i)->GetData();
272 return pfd->m_configstring;
273}
274
275const wxString &FontMgr::GetDialogString(int i) const {
276 MyFontDesc *pfd = m_fontlist->Item(i)->GetData();
277 return pfd->m_dialogstring;
278}
279
280const wxString &FontMgr::GetNativeDesc(int i) const {
281 MyFontDesc *pfd = m_fontlist->Item(i)->GetData();
282 return pfd->m_nativeInfo;
283}
284
285wxString FontMgr::GetFullConfigDesc(int i) const {
286 MyFontDesc *pfd = m_fontlist->Item(i)->GetData();
287 wxString ret = pfd->m_dialogstring;
288 ret.Append(_T ( ":" ));
289 ret.Append(pfd->m_nativeInfo);
290 ret.Append(_T ( ":" ));
291
292 wxString cols(_T("rgb(0,0,0)"));
293 if (pfd->m_color.IsOk()) cols = pfd->m_color.GetAsString(wxC2S_CSS_SYNTAX);
294
295 ret.Append(cols);
296 return ret;
297}
298
299MyFontDesc *FontMgr::FindFontByConfigString(wxString pConfigString) {
300 // Search for a match in the list
301 MyFontDesc *pmfd;
302 auto node = m_fontlist->GetFirst();
303
304 while (node) {
305 pmfd = node->GetData();
306 if (pmfd->m_configstring == pConfigString) {
307 return pmfd;
308 }
309 node = node->GetNext();
310 }
311
312 return NULL;
313}
314
315void FontMgr::LoadFontNative(wxString *pConfigString, wxString *pNativeDesc) {
316 // Parse the descriptor string
317
318 wxStringTokenizer tk(*pNativeDesc, _T ( ":" ));
319 wxString dialogstring = tk.GetNextToken();
320 wxString nativefont = tk.GetNextToken();
321
322 wxString c = tk.GetNextToken();
323 wxColour color(c); // from string description
324
325 // Search for a match in the list
326 MyFontDesc *pmfd;
327 auto node = m_fontlist->GetFirst();
328
329 while (node) {
330 pmfd = node->GetData();
331 if (pmfd->m_configstring == *pConfigString) {
332 if (pmfd->m_configstring.BeforeFirst('-') == g_locale) {
333 pmfd->m_nativeInfo = nativefont;
334 wxFont *nf = pmfd->m_font->New(pmfd->m_nativeInfo);
335 pmfd->m_font = nf;
336 break;
337 }
338 }
339 node = node->GetNext();
340 }
341
342 // Create and add the font to the list
343 if (!node) {
344 wxFont *nf0 = new wxFont();
345
346#ifdef __OCPN__ANDROID__
347 wxFont *nf = new wxFont(nativefont);
348#else
349 wxFont *nf = nf0->New(nativefont);
350#endif
351
352 double font_size = nf->GetPointSize();
353 wxString s = nf->GetNativeFontInfoDesc();
354
355 // Scrub the native font string for bad unicode conversion
356#ifdef __WXMSW__
357 wxString face = nf->GetFaceName();
358 const wxChar *t = face.c_str();
359 if (*t > 255) {
360 delete nf;
361 wxString substitute_native = GetSimpleNativeFont(12, _T(""));
362 nf = nf0->New(substitute_native);
363 }
364#endif
365 delete nf0;
366
367 MyFontDesc *pnewfd =
368 new MyFontDesc(dialogstring, *pConfigString, nf, color);
369 m_fontlist->Append(pnewfd);
370 }
371}
372
373wxFont *FontMgr::FindOrCreateFont(int point_size, wxFontFamily family,
374 wxFontStyle style, wxFontWeight weight,
375 bool underline, const wxString &facename,
376 wxFontEncoding encoding) {
377 if (m_wxFontCache == 0) m_wxFontCache = new OCPNwxFontList;
378 return m_wxFontCache->FindOrCreateFont(point_size, family, style, weight,
379 underline, facename, encoding);
380}
381
382bool OCPNwxFontList::isCached(font_cache_record& record, int pointSize, wxFontFamily family,
383 wxFontStyle style, wxFontWeight weight,
384 bool underline, const wxString &facename,
385 wxFontEncoding encoding) {
386 if (record.pointsize_req == pointSize && record.style_req == style &&
387 record.weight_req == weight && record.underline_req == underline) {
388 bool same;
389
390 wxFont *font = record.font;
391 // empty facename matches anything at all: this is bad because
392 // depending on which fonts are already created, we might get back
393 // a different font if we create it with empty facename, but it is
394 // still better than never matching anything in the cache at all
395 // in this case
396 if (!facename.empty()) {
397 const wxString &fontFace = font->GetFaceName();
398
399 // empty facename matches everything
400 same = !fontFace || fontFace == facename;
401 } else {
402 same = font->GetFamily() == family;
403 }
404 if (same && (encoding != wxFONTENCODING_DEFAULT)) {
405 // have to match the encoding too
406 same = font->GetEncoding() == encoding;
407 }
408 return same;
409 }
410 return false;
411}
412
413wxFont *OCPNwxFontList::FindOrCreateFont(int pointSize, wxFontFamily family,
414 wxFontStyle style, wxFontWeight weight,
415 bool underline,
416 const wxString &facename,
417 wxFontEncoding encoding) {
418 // from wx source code
419 // In all ports but wxOSX, the effective family of a font created using
420 // wxFONTFAMILY_DEFAULT is wxFONTFAMILY_SWISS so this is what we need to
421 // use for comparison.
422 //
423 // In wxOSX the original wxFONTFAMILY_DEFAULT seems to be kept and it uses
424 // a different font than wxFONTFAMILY_SWISS anyhow so we just preserve it.
425#ifndef __WXOSX__
426 if (family == wxFONTFAMILY_DEFAULT) family = wxFONTFAMILY_SWISS;
427#endif // !__WXOSX__
428
429 wxFont *font;
430 for (size_t i=0; i < m_fontVector.size() ; i++){
431 font_cache_record record = m_fontVector[i];
432 if (isCached(record, pointSize, family, style, weight, underline, facename,
433 encoding))
434 return record.font;
435 }
436
437 // font not found, create the new one
438 // Support scaled HDPI displays automatically
439
440 font = NULL;
441 wxFont fontTmp(OCPN_GetDisplayContentScaleFactor() * pointSize,
442 family, style, weight, underline, facename, encoding);
443 if (fontTmp.IsOk()) {
444 font = new wxFont(fontTmp);
445 font_cache_record record;
446 record.font = font;
447 record.pointsize_req = pointSize;
448 record.style_req = style;
449 record.weight_req = weight;
450 record.underline_req = underline;
451 m_fontVector.push_back(record);
452 }
453
454 return font;
455}
456
457void OCPNwxFontList::FreeAll(void) {
458 wxFont *font;
459 for (size_t i=0; i < m_fontVector.size() ; i++){
460 font_cache_record record = m_fontVector[i];
461 font = record.font;
462 delete font;
463 }
464 m_fontVector.clear();
465}
466
467static wxString FontCandidates[] = {_T("AISTargetAlert"),
468 _T("AISTargetQuery"),
469 _T("StatusBar"),
470 _T("AIS Target Name" ),
471 _T("ObjectQuery"),
472 _T("RouteLegInfoRollover"),
473 _T("ExtendedTideIcon"),
474 _T("CurrentValue"),
475 _T("Console Legend"),
476 _T("Console Value"),
477 _T("AISRollover"),
478 _T("TideCurrentGraphRollover"),
479 _T("Marks"),
480 _T("ChartTexts"),
481 _T("ToolTips"),
482 _T("Dialog"),
483 _T("Menu"),
484 _T("GridText"),
485 _T("END_OF_LIST")};
486
487void FontMgr::ScrubList() {
488 wxString now_locale = g_locale;
489 wxArrayString string_array;
490
491 // Build the composite candidate array
492 wxArrayString candidateArray;
493 unsigned int i = 0;
494
495 // The fixed, static list
496 while (true) {
497 wxString candidate = FontCandidates[i];
498 if (candidate == _T("END_OF_LIST")) {
499 break;
500 }
501
502 candidateArray.Add(candidate);
503 i++;
504 }
505
506 // The Aux Key array
507 for (unsigned int i = 0; i < m_AuxKeyArray.GetCount(); i++) {
508 candidateArray.Add(m_AuxKeyArray[i]);
509 }
510
511 for (unsigned int i = 0; i < candidateArray.GetCount(); i++) {
512 wxString candidate = candidateArray[i];
513
514 // For each font identifier string in the FontCandidate array...
515
516 // In the current locale, walk the loaded list looking for a translation
517 // that is correct, according to the currently load .mo file.
518 // If found, add to a temporary array
519
520 wxString trans = wxGetTranslation(candidate);
521
522 MyFontDesc *pmfd;
523 auto node = m_fontlist->GetFirst();
524 while (node) {
525 pmfd = node->GetData();
526 wxString tlocale = pmfd->m_configstring.BeforeFirst('-');
527 if (tlocale == now_locale) {
528 if (trans == pmfd->m_dialogstring) {
529 string_array.Add(pmfd->m_dialogstring);
530 }
531 }
532
533 node = node->GetNext();
534 }
535 }
536
537 // now we have an array of correct translations
538 // Walk the loaded list again.
539 // If a list item's translation is not in the "good" array, mark it for
540 // removal
541
542 MyFontDesc *pmfd;
543 auto node = m_fontlist->GetFirst();
544 while (node) {
545 pmfd = node->GetData();
546 wxString tlocale = pmfd->m_configstring.BeforeFirst('-');
547 if (tlocale == now_locale) {
548 bool bfound = false;
549 for (unsigned int i = 0; i < string_array.GetCount(); i++) {
550 if (string_array[i] == pmfd->m_dialogstring) {
551 bfound = true;
552 break;
553 }
554 }
555 if (!bfound) { // mark for removal
556 pmfd->m_dialogstring = _T("");
557 pmfd->m_configstring = _T("");
558 }
559 }
560
561 node = node->GetNext();
562 }
563
564 // Remove the marked list items
565 node = m_fontlist->GetFirst();
566 while (node) {
567 pmfd = node->GetData();
568 if (pmfd->m_dialogstring == _T("")) {
569 bool bd = m_fontlist->DeleteObject(pmfd);
570 if (bd) node = m_fontlist->GetFirst();
571 } else
572 node = node->GetNext();
573 }
574
575 // And finally, for good measure, make sure that everything in the candidate
576 // array has a valid entry in the list
577 i = 0;
578 while (true) {
579 wxString candidate = FontCandidates[i];
580 if (candidate == _T("END_OF_LIST")) {
581 break;
582 }
583
584 GetFont(wxGetTranslation(candidate), g_default_font_size);
585
586 i++;
587 }
588}
589
590bool FontMgr::AddAuxKey(wxString key) {
591 for (unsigned int i = 0; i < m_AuxKeyArray.GetCount(); i++) {
592 if (m_AuxKeyArray[i] == key) return false;
593 }
594 m_AuxKeyArray.Add(key);
595 return true;
596}
Manages the font list.
Definition: FontMgr.h:41