diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
m--------- | lib/the_Foundation | 0 | ||||
-rw-r--r-- | po/en.po | 27 | ||||
-rw-r--r-- | res/lang/cs.bin | bin | 32086 -> 32702 bytes | |||
-rw-r--r-- | res/lang/de.bin | bin | 30896 -> 31529 bytes | |||
-rw-r--r-- | res/lang/en.bin | bin | 26965 -> 27598 bytes | |||
-rw-r--r-- | res/lang/eo.bin | bin | 26163 -> 26796 bytes | |||
-rw-r--r-- | res/lang/es.bin | bin | 30808 -> 31417 bytes | |||
-rw-r--r-- | res/lang/es_MX.bin | bin | 28035 -> 28668 bytes | |||
-rw-r--r-- | res/lang/fi.bin | bin | 30606 -> 31239 bytes | |||
-rw-r--r-- | res/lang/fr.bin | bin | 31693 -> 32326 bytes | |||
-rw-r--r-- | res/lang/gl.bin | bin | 29952 -> 30585 bytes | |||
-rw-r--r-- | res/lang/hu.bin | bin | 31718 -> 32351 bytes | |||
-rw-r--r-- | res/lang/ia.bin | bin | 29748 -> 30381 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 29655 -> 30288 bytes | |||
-rw-r--r-- | res/lang/isv.bin | bin | 25686 -> 26319 bytes | |||
-rw-r--r-- | res/lang/nl.bin | bin | 29075 -> 29708 bytes | |||
-rw-r--r-- | res/lang/pl.bin | bin | 30321 -> 30954 bytes | |||
-rw-r--r-- | res/lang/ru.bin | bin | 45668 -> 46274 bytes | |||
-rw-r--r-- | res/lang/sk.bin | bin | 26022 -> 26655 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 44964 -> 45580 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 27801 -> 28434 bytes | |||
-rw-r--r-- | res/lang/tr.bin | bin | 29912 -> 30535 bytes | |||
-rw-r--r-- | res/lang/uk.bin | bin | 45045 -> 45661 bytes | |||
-rw-r--r-- | res/lang/zh_Hans.bin | bin | 25930 -> 26563 bytes | |||
-rw-r--r-- | res/lang/zh_Hant.bin | bin | 26328 -> 26961 bytes | |||
-rw-r--r-- | src/app.c | 66 | ||||
-rw-r--r-- | src/app.h | 2 | ||||
-rw-r--r-- | src/fontpack.c | 101 | ||||
-rw-r--r-- | src/fontpack.h | 2 | ||||
-rw-r--r-- | src/ui/banner.c | 3 | ||||
-rw-r--r-- | src/ui/text.c | 40 | ||||
-rw-r--r-- | src/ui/text.h | 2 | ||||
-rw-r--r-- | src/ui/util.c | 48 | ||||
-rw-r--r-- | src/ui/util.h | 1 |
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 @@ | |||
18 | cmake_minimum_required (VERSION 3.9) | 18 | cmake_minimum_required (VERSION 3.9) |
19 | 19 | ||
20 | project (Lagrange | 20 | project (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 | ||
@@ -1181,6 +1181,33 @@ msgstr "Russian" | |||
1181 | msgid "lang.es" | 1181 | msgid "lang.es" |
1182 | msgstr "Spanish" | 1182 | msgstr "Spanish" |
1183 | 1183 | ||
1184 | msgid "heading.glyphfinder" | ||
1185 | msgstr "Missing Glyphs" | ||
1186 | |||
1187 | msgid "dlg.glyphfinder.missing" | ||
1188 | msgstr "The following characters could not be shown:" | ||
1189 | |||
1190 | msgid "dlg.glyphfinder.help" | ||
1191 | msgstr "You can try searching the skyjake.fi Font Library for fonts that provide glyphs for these characters, or manually install new TrueType fonts." | ||
1192 | |||
1193 | msgid "dlg.glyphfinder.help.empty" | ||
1194 | msgstr "Please reload the page to check again for missing glyphs." | ||
1195 | |||
1196 | msgid "dlg.glyphfinder.disable" | ||
1197 | msgstr "Disable Warnings" | ||
1198 | |||
1199 | msgid "dlg.glyphfinder.search" | ||
1200 | msgstr "Search Font Library" | ||
1201 | |||
1202 | msgid "heading.glyphfinder.results" | ||
1203 | msgstr "Search Results" | ||
1204 | |||
1205 | msgid "glyphfinder.results" | ||
1206 | msgstr "The following fontpacks provide one or more of the missing glyphs:" | ||
1207 | |||
1208 | msgid "glyphfinder.results.empty" | ||
1209 | msgstr "Sorry, no matching fontpacks were found." | ||
1210 | |||
1184 | msgid "heading.newident" | 1211 | msgid "heading.newident" |
1185 | msgstr "New Identity" | 1212 | msgstr "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 | |||
@@ -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 | ||
2252 | void 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 | |||
2248 | static void invalidateCachedDocuments_App_(void) { | 2259 | static 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); |
@@ -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 | ||
72 | const iString *execPath_App (void); | 73 | const iString *execPath_App (void); |
@@ -142,6 +143,7 @@ iDocumentWidget * document_Command (const char *cmd); | |||
142 | void openInDefaultBrowser_App(const iString *url); | 143 | void openInDefaultBrowser_App(const iString *url); |
143 | void revealPath_App (const iString *path); | 144 | void revealPath_App (const iString *path); |
144 | void resetFonts_App (void); | 145 | void resetFonts_App (void); |
146 | void availableFontsChanged_App(void); | ||
145 | 147 | ||
146 | iMainWindow * mainWindow_App (void); | 148 | iMainWindow * mainWindow_App (void); |
147 | void closePopups_App (void); | 149 | void 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 | ||
1023 | void installFontFile_Fonts(const iString *fileName, const iBlock *data) { | 1025 | void 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 | ||
1033 | void enablePack_Fonts(const iString *packId, iBool enable) { | 1036 | void 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 | ||
1050 | iDefineClass(FontFile) | 1054 | static 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 | ||
1141 | void 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 | |||
1150 | iDefineClass(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); | |||
186 | iLocalDef iBool isInstalled_Fonts(const char *packId) { | 186 | iLocalDef iBool isInstalled_Fonts(const char *packId) { |
187 | return pack_Fonts(packId) != NULL; | 187 | return pack_Fonts(packId) != NULL; |
188 | } | 188 | } |
189 | |||
190 | void 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 | ||
249 | struct Impl_Text { | 249 | struct 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 | ||
270 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 269 | iDefineTypeConstructionArgs(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 | ||
346 | static void deinitFonts_Text_(iText *d) { | 347 | static 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 | ||
2219 | iChar 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 | |||
2227 | void resetMissing_Text(iText *d) { | ||
2228 | d->missingGlyphs = iFalse; | ||
2229 | iZap(d->missingChars); | ||
2230 | } | ||
2231 | |||
2202 | SDL_Texture *glyphCache_Text(void) { | 2232 | SDL_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 { | |||
227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); | 227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); |
228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); | 228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); |
229 | 229 | ||
230 | iChar missing_Text (size_t index); | ||
231 | void resetMissing_Text (iText *); | ||
230 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ | 232 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ |
231 | SDL_Texture * glyphCache_Text (void); | 233 | SDL_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 | ||
3454 | iWidget *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 | |||
3454 | void init_PerfTimer(iPerfTimer *d) { | 3502 | void 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 | |||
342 | iWidget * makeIdentityCreation_Widget (void); | 342 | iWidget * makeIdentityCreation_Widget (void); |
343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); | 343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); |
344 | iWidget * makeTranslation_Widget (iWidget *parent); | 344 | iWidget * makeTranslation_Widget (iWidget *parent); |
345 | iWidget * makeGlyphFinder_Widget (void); | ||
345 | 346 | ||
346 | const char * languageId_String (const iString *menuItemLabel); | 347 | const char * languageId_String (const iString *menuItemLabel); |
347 | int languageIndex_CStr (const char *langId); | 348 | int languageIndex_CStr (const char *langId); |