summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
m---------lib/the_Foundation0
-rw-r--r--po/en.po27
-rw-r--r--res/lang/cs.binbin32086 -> 32702 bytes
-rw-r--r--res/lang/de.binbin30896 -> 31529 bytes
-rw-r--r--res/lang/en.binbin26965 -> 27598 bytes
-rw-r--r--res/lang/eo.binbin26163 -> 26796 bytes
-rw-r--r--res/lang/es.binbin30808 -> 31417 bytes
-rw-r--r--res/lang/es_MX.binbin28035 -> 28668 bytes
-rw-r--r--res/lang/fi.binbin30606 -> 31239 bytes
-rw-r--r--res/lang/fr.binbin31693 -> 32326 bytes
-rw-r--r--res/lang/gl.binbin29952 -> 30585 bytes
-rw-r--r--res/lang/hu.binbin31718 -> 32351 bytes
-rw-r--r--res/lang/ia.binbin29748 -> 30381 bytes
-rw-r--r--res/lang/ie.binbin29655 -> 30288 bytes
-rw-r--r--res/lang/isv.binbin25686 -> 26319 bytes
-rw-r--r--res/lang/nl.binbin29075 -> 29708 bytes
-rw-r--r--res/lang/pl.binbin30321 -> 30954 bytes
-rw-r--r--res/lang/ru.binbin45668 -> 46274 bytes
-rw-r--r--res/lang/sk.binbin26022 -> 26655 bytes
-rw-r--r--res/lang/sr.binbin44964 -> 45580 bytes
-rw-r--r--res/lang/tok.binbin27801 -> 28434 bytes
-rw-r--r--res/lang/tr.binbin29912 -> 30535 bytes
-rw-r--r--res/lang/uk.binbin45045 -> 45661 bytes
-rw-r--r--res/lang/zh_Hans.binbin25930 -> 26563 bytes
-rw-r--r--res/lang/zh_Hant.binbin26328 -> 26961 bytes
-rw-r--r--src/app.c66
-rw-r--r--src/app.h2
-rw-r--r--src/fontpack.c101
-rw-r--r--src/fontpack.h2
-rw-r--r--src/ui/banner.c3
-rw-r--r--src/ui/text.c40
-rw-r--r--src/ui/text.h2
-rw-r--r--src/ui/util.c48
-rw-r--r--src/ui/util.h1
35 files changed, 271 insertions, 23 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 97d5d279..b185f646 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,7 @@
18cmake_minimum_required (VERSION 3.9) 18cmake_minimum_required (VERSION 3.9)
19 19
20project (Lagrange 20project (Lagrange
21 VERSION 1.10.1 21 VERSION 1.11.0
22 DESCRIPTION "A Beautiful Gemini Client" 22 DESCRIPTION "A Beautiful Gemini Client"
23 LANGUAGES C 23 LANGUAGES C
24) 24)
diff --git a/lib/the_Foundation b/lib/the_Foundation
Subproject 453f05f6efb46824ff0dca0174036c7624473e4 Subproject f2f7fef560fc8642e3e449239d7e00f14491e13
diff --git a/po/en.po b/po/en.po
index e0e086e3..cc5f2091 100644
--- a/po/en.po
+++ b/po/en.po
@@ -1181,6 +1181,33 @@ msgstr "Russian"
1181msgid "lang.es" 1181msgid "lang.es"
1182msgstr "Spanish" 1182msgstr "Spanish"
1183 1183
1184msgid "heading.glyphfinder"
1185msgstr "Missing Glyphs"
1186
1187msgid "dlg.glyphfinder.missing"
1188msgstr "The following characters could not be shown:"
1189
1190msgid "dlg.glyphfinder.help"
1191msgstr "You can try searching the skyjake.fi Font Library for fonts that provide glyphs for these characters, or manually install new TrueType fonts."
1192
1193msgid "dlg.glyphfinder.help.empty"
1194msgstr "Please reload the page to check again for missing glyphs."
1195
1196msgid "dlg.glyphfinder.disable"
1197msgstr "Disable Warnings"
1198
1199msgid "dlg.glyphfinder.search"
1200msgstr "Search Font Library"
1201
1202msgid "heading.glyphfinder.results"
1203msgstr "Search Results"
1204
1205msgid "glyphfinder.results"
1206msgstr "The following fontpacks provide one or more of the missing glyphs:"
1207
1208msgid "glyphfinder.results.empty"
1209msgstr "Sorry, no matching fontpacks were found."
1210
1184msgid "heading.newident" 1211msgid "heading.newident"
1185msgstr "New Identity" 1212msgstr "New Identity"
1186 1213
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index 6624fbd4..6f1afa85 100644
--- a/res/lang/cs.bin
+++ b/res/lang/cs.bin
Binary files differ
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 9c3d4541..ecb601aa 100644
--- a/res/lang/de.bin
+++ b/res/lang/de.bin
Binary files differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 5f649846..758adbba 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/res/lang/eo.bin b/res/lang/eo.bin
index ace592df..1de8a7db 100644
--- a/res/lang/eo.bin
+++ b/res/lang/eo.bin
Binary files differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 09b7151b..a11c6f78 100644
--- a/res/lang/es.bin
+++ b/res/lang/es.bin
Binary files differ
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin
index dfcd6306..40379ee0 100644
--- a/res/lang/es_MX.bin
+++ b/res/lang/es_MX.bin
Binary files differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 44d2741d..18a4c11d 100644
--- a/res/lang/fi.bin
+++ b/res/lang/fi.bin
Binary files differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index ad16b2e7..ba098242 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/gl.bin b/res/lang/gl.bin
index f1501b4a..7727f587 100644
--- a/res/lang/gl.bin
+++ b/res/lang/gl.bin
Binary files differ
diff --git a/res/lang/hu.bin b/res/lang/hu.bin
index c070dd49..94af8aa8 100644
--- a/res/lang/hu.bin
+++ b/res/lang/hu.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 34633dd1..1aac22fe 100644
--- a/res/lang/ia.bin
+++ b/res/lang/ia.bin
Binary files differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index 722ee20d..4d5ac8d9 100644
--- a/res/lang/ie.bin
+++ b/res/lang/ie.bin
Binary files differ
diff --git a/res/lang/isv.bin b/res/lang/isv.bin
index 3be01643..c3931a80 100644
--- a/res/lang/isv.bin
+++ b/res/lang/isv.bin
Binary files differ
diff --git a/res/lang/nl.bin b/res/lang/nl.bin
index dcc9fe97..579f0df8 100644
--- a/res/lang/nl.bin
+++ b/res/lang/nl.bin
Binary files differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index a32e9d10..448be098 100644
--- a/res/lang/pl.bin
+++ b/res/lang/pl.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 6a9b8e67..04114010 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/res/lang/sk.bin b/res/lang/sk.bin
index 833872c9..fc984d97 100644
--- a/res/lang/sk.bin
+++ b/res/lang/sk.bin
Binary files differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index a1e87e95..ef949942 100644
--- a/res/lang/sr.bin
+++ b/res/lang/sr.bin
Binary files differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index d17f075e..cfbee57e 100644
--- a/res/lang/tok.bin
+++ b/res/lang/tok.bin
Binary files differ
diff --git a/res/lang/tr.bin b/res/lang/tr.bin
index 981ced04..8552574c 100644
--- a/res/lang/tr.bin
+++ b/res/lang/tr.bin
Binary files differ
diff --git a/res/lang/uk.bin b/res/lang/uk.bin
index d5e78f6f..8f39c7fd 100644
--- a/res/lang/uk.bin
+++ b/res/lang/uk.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 246b7c42..2f0c03fa 100644
--- a/res/lang/zh_Hans.bin
+++ b/res/lang/zh_Hans.bin
Binary files differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index 0e94f66a..f978013c 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 30221582..2b1ca1b6 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1362,6 +1362,10 @@ void processEvents_App(enum iAppEventMode eventMode) {
1362 dispatchCommands_Periodic(&d->periodic); 1362 dispatchCommands_Periodic(&d->periodic);
1363 continue; 1363 continue;
1364 } 1364 }
1365 if (ev.type == SDL_USEREVENT && ev.user.code == releaseObject_UserEventCode) {
1366 iRelease(ev.user.data1);
1367 continue;
1368 }
1365#if defined (LAGRANGE_ENABLE_IDLE_SLEEP) 1369#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
1366 if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) { 1370 if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) {
1367 if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_ && 1371 if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_ &&
@@ -2245,6 +2249,13 @@ void resetFonts_App(void) {
2245 } 2249 }
2246} 2250}
2247 2251
2252void availableFontsChanged_App(void) {
2253 iApp *d = &app_;
2254 iConstForEach(PtrArray, win, listWindows_App_(d, collectNew_PtrArray())) {
2255 resetMissing_Text(text_Window(win.ptr));
2256 }
2257}
2258
2248static void invalidateCachedDocuments_App_(void) { 2259static void invalidateCachedDocuments_App_(void) {
2249 iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { 2260 iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) {
2250 invalidateCachedLayout_History(history_DocumentWidget(i.object)); 2261 invalidateCachedLayout_History(history_DocumentWidget(i.object));
@@ -2371,24 +2382,49 @@ iBool handleCommand_App(const char *cmd) {
2371 reload_Fonts(); /* also does font cache reset, window invalidation */ 2382 reload_Fonts(); /* also does font cache reset, window invalidation */
2372 return iTrue; 2383 return iTrue;
2373 } 2384 }
2374#if 0 2385 else if (equal_Command(cmd, "font.find")) {
2375 else if (equal_Command(cmd, "font.user")) { 2386 searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars"));
2376 const char *path = suffixPtr_Command(cmd, "path"); 2387 return iTrue;
2377 if (cmp_String(&d->prefs.symbolFontPath, path)) { 2388 }
2378 if (!isFrozen) { 2389 else if (equal_Command(cmd, "font.found")) {
2379 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); 2390 if (hasLabel_Command(cmd, "error")) {
2380 } 2391 makeSimpleMessage_Widget("${heading.glyphfinder}",
2381 setCStr_String(&d->prefs.symbolFontPath, path); 2392 format_CStr("%d %s",
2382 loadUserFonts_Text(); 2393 argLabel_Command(cmd, "error"),
2383 resetFonts_App(d); 2394 suffixPtr_Command(cmd, "msg")));
2384 if (!isFrozen) { 2395 return iTrue;
2385 postCommand_App("font.changed");
2386 postCommand_App("window.unfreeze");
2387 }
2388 } 2396 }
2397 iString *src = collectNew_String();
2398 setCStr_String(src, "# ${heading.glyphfinder.results}\n\n");
2399 iRangecc path = iNullRange;
2400 iBool isFirst = iTrue;
2401 while (nextSplit_Rangecc(range_Command(cmd, "packs"), ",", &path)) {
2402 if (isFirst) {
2403 appendCStr_String(src, "${glyphfinder.results}\n\n");
2404 }
2405 iRangecc fpath = path;
2406 iRangecc fsize = path;
2407 fpath.end = strchr(fpath.start, ';');
2408 fsize.start = fpath.end + 1;
2409 const uint32_t size = strtoul(fsize.start, NULL, 10);
2410 appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s (%.1f MB)\n",
2411 cstr_Rangecc(fpath),
2412 cstr_Rangecc(fpath),
2413 (double) size / 1.0e6);
2414 isFirst = iFalse;
2415 }
2416 if (isFirst) {
2417 appendFormat_String(src, "${glyphfinder.results.empty}\n");
2418 }
2419 appendCStr_String(src, "\n=> about:fonts ${menu.fonts}");
2420 iDocumentWidget *page = newTab_App(NULL, iTrue);
2421 translate_Lang(src);
2422 setUrlAndSource_DocumentWidget(page,
2423 collectNewCStr_String(""),
2424 collectNewCStr_String("text/gemini"),
2425 utf8_String(src));
2389 return iTrue; 2426 return iTrue;
2390 } 2427 }
2391#endif
2392 else if (equal_Command(cmd, "font.set")) { 2428 else if (equal_Command(cmd, "font.set")) {
2393 if (!isFrozen) { 2429 if (!isFrozen) {
2394 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); 2430 setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
diff --git a/src/app.h b/src/app.h
index 5968de0d..22fe5d46 100644
--- a/src/app.h
+++ b/src/app.h
@@ -67,6 +67,7 @@ enum iUserEventCode {
67 take, it could turn into a tap-and-hold for example. */ 67 take, it could turn into a tap-and-hold for example. */
68 widgetTapBegins_UserEventCode, 68 widgetTapBegins_UserEventCode,
69 widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */ 69 widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */
70 releaseObject_UserEventCode, /* object that needs releasing in the main thread */
70}; 71};
71 72
72const iString *execPath_App (void); 73const iString *execPath_App (void);
@@ -142,6 +143,7 @@ iDocumentWidget * document_Command (const char *cmd);
142void openInDefaultBrowser_App(const iString *url); 143void openInDefaultBrowser_App(const iString *url);
143void revealPath_App (const iString *path); 144void revealPath_App (const iString *path);
144void resetFonts_App (void); 145void resetFonts_App (void);
146void availableFontsChanged_App(void);
145 147
146iMainWindow * mainWindow_App (void); 148iMainWindow * mainWindow_App (void);
147void closePopups_App (void); 149void closePopups_App (void);
diff --git a/src/fontpack.c b/src/fontpack.c
index a440234e..924d4781 100644
--- a/src/fontpack.c
+++ b/src/fontpack.c
@@ -23,6 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
23#include "fontpack.h" 23#include "fontpack.h"
24#include "resources.h" 24#include "resources.h"
25#include "ui/window.h" 25#include "ui/window.h"
26#include "gmrequest.h"
26#include "app.h" 27#include "app.h"
27 28
28#include <the_Foundation/archive.h> 29#include <the_Foundation/archive.h>
@@ -1018,6 +1019,7 @@ void install_Fonts(const iString *packId, const iBlock *data) {
1018 iRelease(f); 1019 iRelease(f);
1019 /* Newly installed fontpacks may have a higher priority that overrides other fonts. */ 1020 /* Newly installed fontpacks may have a higher priority that overrides other fonts. */
1020 reload_Fonts(); 1021 reload_Fonts();
1022 availableFontsChanged_App();
1021} 1023}
1022 1024
1023void installFontFile_Fonts(const iString *fileName, const iBlock *data) { 1025void installFontFile_Fonts(const iString *fileName, const iBlock *data) {
@@ -1028,6 +1030,7 @@ void installFontFile_Fonts(const iString *fileName, const iBlock *data) {
1028 } 1030 }
1029 iRelease(f); 1031 iRelease(f);
1030 reload_Fonts(); 1032 reload_Fonts();
1033 availableFontsChanged_App();
1031} 1034}
1032 1035
1033void enablePack_Fonts(const iString *packId, iBool enable) { 1036void enablePack_Fonts(const iString *packId, iBool enable) {
@@ -1040,6 +1043,7 @@ void enablePack_Fonts(const iString *packId, iBool enable) {
1040 } 1043 }
1041 updateActive_Fonts(); 1044 updateActive_Fonts();
1042 resetFonts_App(); 1045 resetFonts_App();
1046 availableFontsChanged_App();
1043 invalidate_Window(get_MainWindow()); 1047 invalidate_Window(get_MainWindow());
1044} 1048}
1045 1049
@@ -1047,5 +1051,100 @@ void updateActive_Fonts(void) {
1047 sortSpecs_Fonts_(&fonts_); 1051 sortSpecs_Fonts_(&fonts_);
1048} 1052}
1049 1053
1050iDefineClass(FontFile) 1054static void findCharactersInCMap_(iGmRequest *d, iGmRequest *req) {
1055 /* Note: Called in background thread. */
1056 iUnused(req);
1057 const iString *missingChars = userData_Object(d);
1058 if (isSuccess_GmStatusCode(status_GmRequest(d))) {
1059 iStringList *matchingPacks = new_StringList();
1060 iChar needed[20];
1061 iChar minChar = UINT32_MAX, maxChar = 0;
1062 size_t numNeeded = 0;
1063 iConstForEach(String, ch, missingChars) {
1064 needed[numNeeded++] = ch.value;
1065 minChar = iMin(minChar, ch.value);
1066 maxChar = iMax(maxChar, ch.value);
1067 if (numNeeded == iElemCount(needed)) {
1068 /* Shouldn't be that many. */
1069 break;
1070 }
1071 }
1072 iBlock *data = decompressGzip_Block(body_GmRequest(d));
1073 iRangecc line = iNullRange;
1074 while (nextSplit_Rangecc(range_Block(data), "\n", &line)) {
1075 iRangecc fontpackPath = iNullRange;
1076 for (const char *pos = line.start; pos < line.end; pos++) {
1077 if (*pos == ':') {
1078 fontpackPath.start = line.start;
1079 fontpackPath.end = pos;
1080 line.start = pos + 1;
1081 trimStart_Rangecc(&line);
1082 break;
1083 }
1084 }
1085 if (fontpackPath.start) {
1086 /* Parse the character ranges and see if any match what we need. */
1087 const char *pos = line.start;
1088 while (pos < line.end) {
1089 char *endp;
1090 uint32_t first = strtoul(pos, &endp, 10);
1091 uint32_t last = first;
1092 if (*endp == '-') {
1093 last = strtoul(endp + 1, &endp, 10);
1094 }
1095 if (maxChar < first) {
1096 break; /* The rest are even higher. */
1097 }
1098 if (minChar <= last) {
1099 for (size_t i = 0; i < numNeeded; i++) {
1100 if (needed[i] >= first && needed[i] <= last) {
1101 /* Got it. */
1102 pushBackRange_StringList(matchingPacks, fontpackPath);
1103 break;
1104 }
1105 }
1106 }
1107 pos = endp + 1;
1108 }
1109 }
1110 }
1111 delete_Block(data);
1112 iString result;
1113 init_String(&result);
1114 format_String(&result, "font.found chars:%s packs:", cstr_String(missingChars));
1115 iConstForEach(StringList, s, matchingPacks) {
1116 if (s.pos != 0) {
1117 appendCStr_String(&result, ",");
1118 }
1119 append_String(&result, s.value);
1120 }
1121 postCommandString_Root(NULL, &result);
1122 deinit_String(&result);
1123 iRelease(matchingPacks);
1124 }
1125 else {
1126 /* Report error. */
1127 postCommandf_Root(NULL,
1128 "font.found chars:%s error:%d msg:\x1b[1m%s\x1b[0m\n%s",
1129 cstr_String(missingChars),
1130 status_GmRequest(d),
1131 cstr_String(meta_GmRequest(d)),
1132 cstr_String(url_GmRequest(d)));
1133 }
1134// fflush(stdout);
1135 delete_String(userData_Object(d));
1136 /* We can't delete ourselves; threads must be joined from another thread. */
1137 SDL_PushEvent((SDL_Event *) &(SDL_UserEvent){
1138 .type = SDL_USEREVENT, .code = releaseObject_UserEventCode, .data1 = d });
1139}
1051 1140
1141void searchOnlineLibraryForCharacters_Fonts(const iString *chars) {
1142 /* Fetch the character map from skyjake.fi. */
1143 iGmRequest *req = new_GmRequest(certs_App());
1144 setUrl_GmRequest(req, collectNewCStr_String("gemini://skyjake.fi/fonts/cmap.txt.gz"));
1145 setUserData_Object(req, copy_String(chars));
1146 iConnect(GmRequest, req, finished, req, findCharactersInCMap_);
1147 submit_GmRequest(req);
1148}
1149
1150iDefineClass(FontFile)
diff --git a/src/fontpack.h b/src/fontpack.h
index aa6f2b9f..27ecd087 100644
--- a/src/fontpack.h
+++ b/src/fontpack.h
@@ -186,3 +186,5 @@ void reload_Fonts (void);
186iLocalDef iBool isInstalled_Fonts(const char *packId) { 186iLocalDef iBool isInstalled_Fonts(const char *packId) {
187 return pack_Fonts(packId) != NULL; 187 return pack_Fonts(packId) != NULL;
188} 188}
189
190void searchOnlineLibraryForCharacters_Fonts (const iString *chars);
diff --git a/src/ui/banner.c b/src/ui/banner.c
index 11ae1574..79d70039 100644
--- a/src/ui/banner.c
+++ b/src/ui/banner.c
@@ -327,7 +327,8 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) {
327 else { 327 else {
328 switch (item->code) { 328 switch (item->code) {
329 case missingGlyphs_GmStatusCode: 329 case missingGlyphs_GmStatusCode:
330 postCommandf_App("open newtab:1 url:about:fonts"); 330 //postCommandf_App("open newtab:1 url:about:fonts");
331 makeGlyphFinder_Widget();
331 break; 332 break;
332 case ansiEscapes_GmStatusCode: 333 case ansiEscapes_GmStatusCode:
333 makeQuestion_Widget( 334 makeQuestion_Widget(
diff --git a/src/ui/text.c b/src/ui/text.c
index 7bb418eb..86ac709b 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -247,8 +247,6 @@ struct Impl_CacheRow {
247}; 247};
248 248
249struct Impl_Text { 249struct Impl_Text {
250// enum iTextFont contentFont;
251// enum iTextFont headingFont;
252 float contentFontSize; 250 float contentFontSize;
253 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ 251 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */
254 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ 252 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */
@@ -264,7 +262,8 @@ struct Impl_Text {
264 int ansiFlags; 262 int ansiFlags;
265 int baseFontId; /* base attributes (for restoring via escapes) */ 263 int baseFontId; /* base attributes (for restoring via escapes) */
266 int baseFgColorId; 264 int baseFgColorId;
267 iBool missingGlyphs; /* true if a glyph couldn't be found */ 265 iBool missingGlyphs; /* true if a glyph couldn't be found */
266 iChar missingChars[20]; /* rotating buffer of the latest missing characters */
268}; 267};
269 268
270iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) 269iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)
@@ -341,6 +340,8 @@ static void initFonts_Text_(iText *d) {
341 printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); 340 printf("[Text] %zu font variants ready\n", size_Array(&d->fonts));
342#endif 341#endif
343 gap_Text = iRound(gap_UI * d->contentFontSize); 342 gap_Text = iRound(gap_UI * d->contentFontSize);
343// d->missingGlyphs = iFalse;
344// iZap(d->missingChars);
344} 345}
345 346
346static void deinitFonts_Text_(iText *d) { 347static void deinitFonts_Text_(iText *d) {
@@ -403,6 +404,7 @@ void init_Text(iText *d, SDL_Renderer *render) {
403 d->baseFontId = -1; 404 d->baseFontId = -1;
404 d->baseFgColorId = -1; 405 d->baseFgColorId = -1;
405 d->missingGlyphs = iFalse; 406 d->missingGlyphs = iFalse;
407 iZap(d->missingChars);
406 d->render = render; 408 d->render = render;
407 /* A grayscale palette for rasterized glyphs. */ { 409 /* A grayscale palette for rasterized glyphs. */ {
408 SDL_Color colors[256]; 410 SDL_Color colors[256];
@@ -589,8 +591,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
589 } 591 }
590 } 592 }
591 if (!*glyphIndex) { 593 if (!*glyphIndex) {
592 activeText_->missingGlyphs = iTrue; 594 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int) ch); fflush(stderr);
593 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); 595 iText *tx = activeText_;
596 tx->missingGlyphs = iTrue;
597 /* Remember a few of the latest missing characters. */
598 iBool gotIt = iFalse;
599 for (size_t i = 0; i < iElemCount(tx->missingChars); i++) {
600 if (tx->missingChars[i] == ch) {
601 gotIt = iTrue;
602 break;
603 }
604 }
605 if (!gotIt) {
606 memmove(tx->missingChars + 1,
607 tx->missingChars,
608 sizeof(tx->missingChars) - sizeof(tx->missingChars[0]));
609 tx->missingChars[0] = ch;
610 }
594 } 611 }
595 return d; 612 return d;
596} 613}
@@ -2199,6 +2216,19 @@ iBool checkMissing_Text(void) {
2199 return missing; 2216 return missing;
2200} 2217}
2201 2218
2219iChar missing_Text(size_t index) {
2220 const iText *d = activeText_;
2221 if (index >= iElemCount(d->missingChars)) {
2222 return 0;
2223 }
2224 return d->missingChars[index];
2225}
2226
2227void resetMissing_Text(iText *d) {
2228 d->missingGlyphs = iFalse;
2229 iZap(d->missingChars);
2230}
2231
2202SDL_Texture *glyphCache_Text(void) { 2232SDL_Texture *glyphCache_Text(void) {
2203 return activeText_->cache; 2233 return activeText_->cache;
2204} 2234}
diff --git a/src/ui/text.h b/src/ui/text.h
index c8bb6f85..a34cc9bd 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -227,6 +227,8 @@ struct Impl_WrapText {
227iTextMetrics measure_WrapText (iWrapText *, int fontId); 227iTextMetrics measure_WrapText (iWrapText *, int fontId);
228iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); 228iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color);
229 229
230iChar missing_Text (size_t index);
231void resetMissing_Text (iText *);
230iBool checkMissing_Text (void); /* returns the flag, and clears it */ 232iBool checkMissing_Text (void); /* returns the flag, and clears it */
231SDL_Texture * glyphCache_Text (void); 233SDL_Texture * glyphCache_Text (void);
232 234
diff --git a/src/ui/util.c b/src/ui/util.c
index 31907721..8e71dcec 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -3451,6 +3451,54 @@ iWidget *makeTranslation_Widget(iWidget *parent) {
3451 return dlg; 3451 return dlg;
3452} 3452}
3453 3453
3454iWidget *makeGlyphFinder_Widget(void) {
3455 iString msg;
3456 iString command;
3457 init_String(&msg);
3458 initCStr_String(&command, "!font.find chars:");
3459 for (size_t i = 0; ; i++) {
3460 iChar ch = missing_Text(i);
3461 if (!ch) break;
3462 appendFormat_String(&msg, " U+%04X", ch);
3463 appendChar_String(&command, ch);
3464 }
3465 iArray items;
3466 init_Array(&items, sizeof(iMenuItem));
3467 if (!isEmpty_String(&msg)) {
3468 prependCStr_String(&msg, "${dlg.glyphfinder.missing} ");
3469 appendCStr_String(&msg, "\n\n${dlg.glyphfinder.help}");
3470 pushBackN_Array(
3471 &items,
3472 (iMenuItem[]){
3473 { "${menu.fonts}", 0, 0, "!open newtab:1 url:about:fonts" },
3474 { "${dlg.glyphfinder.disable}", 0, 0, "prefs.font.warnmissing.changed arg:0" },
3475 { "---" },
3476 { uiTextCaution_ColorEscape magnifyingGlass_Icon " ${dlg.glyphfinder.search}",
3477 0,
3478 0,
3479 cstr_String(&command) },
3480 { "${close}", 0, 0, "cancel" } },
3481 5);
3482 }
3483 else {
3484 setCStr_String(&msg, "${dlg.glyphfinder.help.empty}");
3485 pushBackN_Array(&items,
3486 (iMenuItem[]){ { "${menu.reload}", 0, 0, "navigate.reload" },
3487 { "${close}", 0, 0, "cancel" } },
3488 2);
3489 }
3490 iWidget *dlg = makeQuestion_Widget("${heading.glyphfinder}", cstr_String(&msg),
3491 constData_Array(&items),
3492 size_Array(&items));
3493 arrange_Widget(dlg);
3494 deinit_Array(&items);
3495 deinit_String(&command);
3496 deinit_String(&msg);
3497 return dlg;
3498}
3499
3500/*----------------------------------------------------------------------------------------------*/
3501
3454void init_PerfTimer(iPerfTimer *d) { 3502void init_PerfTimer(iPerfTimer *d) {
3455 d->ticks = SDL_GetPerformanceCounter(); 3503 d->ticks = SDL_GetPerformanceCounter();
3456} 3504}
diff --git a/src/ui/util.h b/src/ui/util.h
index 98ce784c..0289d579 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -342,6 +342,7 @@ iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *titl
342iWidget * makeIdentityCreation_Widget (void); 342iWidget * makeIdentityCreation_Widget (void);
343iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); 343iWidget * makeFeedSettings_Widget (uint32_t bookmarkId);
344iWidget * makeTranslation_Widget (iWidget *parent); 344iWidget * makeTranslation_Widget (iWidget *parent);
345iWidget * makeGlyphFinder_Widget (void);
345 346
346const char * languageId_String (const iString *menuItemLabel); 347const char * languageId_String (const iString *menuItemLabel);
347int languageIndex_CStr (const char *langId); 348int languageIndex_CStr (const char *langId);