summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po30
-rw-r--r--res/lang/de.binbin27781 -> 28319 bytes
-rw-r--r--res/lang/en.binbin25278 -> 25818 bytes
-rw-r--r--res/lang/eo.binbin24055 -> 24595 bytes
-rw-r--r--res/lang/es.binbin28165 -> 28703 bytes
-rw-r--r--res/lang/es_MX.binbin26106 -> 26644 bytes
-rw-r--r--res/lang/fi.binbin28079 -> 28617 bytes
-rw-r--r--res/lang/fr.binbin28987 -> 29525 bytes
-rw-r--r--res/lang/gl.binbin27443 -> 27981 bytes
-rw-r--r--res/lang/ia.binbin27106 -> 27644 bytes
-rw-r--r--res/lang/ie.binbin27311 -> 27849 bytes
-rw-r--r--res/lang/isv.binbin23982 -> 24522 bytes
-rw-r--r--res/lang/pl.binbin28382 -> 28920 bytes
-rw-r--r--res/lang/ru.binbin40754 -> 41292 bytes
-rw-r--r--res/lang/sk.binbin24315 -> 24855 bytes
-rw-r--r--res/lang/sr.binbin40626 -> 41164 bytes
-rw-r--r--res/lang/tok.binbin25556 -> 26094 bytes
-rw-r--r--res/lang/zh_Hans.binbin24058 -> 24596 bytes
-rw-r--r--res/lang/zh_Hant.binbin24199 -> 24737 bytes
-rw-r--r--src/gmdocument.c47
-rw-r--r--src/gmdocument.h6
-rw-r--r--src/gmutil.c8
-rw-r--r--src/gmutil.h2
-rw-r--r--src/ui/banner.c77
-rw-r--r--src/ui/documentwidget.c27
-rw-r--r--src/ui/labelwidget.c8
-rw-r--r--src/ui/text.c9
-rw-r--r--src/ui/text.h1
28 files changed, 182 insertions, 33 deletions
diff --git a/po/en.po b/po/en.po
index 4e10cc9b..37db7424 100644
--- a/po/en.po
+++ b/po/en.po
@@ -647,7 +647,7 @@ msgstr "DELETE IDENTITY"
647 647
648#, c-format 648#, c-format
649msgid "dlg.confirm.ident.delete" 649msgid "dlg.confirm.ident.delete"
650msgstr "Do you really want to delete the identity\n%s%s%s\nincluding its certificate and private key files?" 650msgstr "Do you really want to delete the identity %s\"%s\"%s including its certificate and private key files?"
651 651
652msgid "dlg.ident.delete" 652msgid "dlg.ident.delete"
653msgstr "Delete Identity and Files" 653msgstr "Delete Identity and Files"
@@ -665,7 +665,7 @@ msgstr "UNSUBSCRIBE"
665 665
666#, c-format 666#, c-format
667msgid "dlg.confirm.unsub" 667msgid "dlg.confirm.unsub"
668msgstr "Really unsubscribe from feed\n\"%s\"?" 668msgstr "Really unsubscribe from feed \"%s\"?"
669 669
670msgid "dlg.unsub" 670msgid "dlg.unsub"
671msgstr "Unsubscribe" 671msgstr "Unsubscribe"
@@ -1014,7 +1014,7 @@ msgid "heading.uploadport"
1014msgstr "TITAN UPLOAD PORT" 1014msgstr "TITAN UPLOAD PORT"
1015 1015
1016msgid "dlg.uploadport.msg" 1016msgid "dlg.uploadport.msg"
1017msgstr "Set the Titan server port to use for this URL.\nThe port is saved in the site-specific configuration." 1017msgstr "Set the Titan server port to use for this URL. The port is saved in the site-specific configuration."
1018 1018
1019msgid "dlg.uploadport.set" 1019msgid "dlg.uploadport.set"
1020msgstr "Set Port" 1020msgstr "Set Port"
@@ -1857,6 +1857,18 @@ msgstr "Invalid Certificate"
1857msgid "error.cert.invalid.msg" 1857msgid "error.cert.invalid.msg"
1858msgstr "The provided client certificate is expired or invalid." 1858msgstr "The provided client certificate is expired or invalid."
1859 1859
1860msgid "error.ansi"
1861msgstr "Terminal Emulation"
1862
1863msgid "error.ansi.msg"
1864msgstr "This page may not be displayed correctly. It uses ANSI escape sequences to style text, which is only partially supported. You could try using a terminal emulator to view it."
1865
1866msgid "error.glyphs"
1867msgstr "Missing Glyphs"
1868
1869msgid "error.glyphs.msg"
1870msgstr "This page could not displayed in full because some characters are missing. You can install additional fonts to fix this."
1871
1860msgid "gempub.cover.viewlocal" 1872msgid "gempub.cover.viewlocal"
1861msgstr "This Gempub book can be viewed after it has been saved locally." 1873msgstr "This Gempub book can be viewed after it has been saved locally."
1862 1874
@@ -1947,7 +1959,7 @@ msgstr "DELETE FONTPACK"
1947 1959
1948#, c-format 1960#, c-format
1949msgid "dlg.fontpack.delete.confirm" 1961msgid "dlg.fontpack.delete.confirm"
1950msgstr "Do you really want to permanently delete\nthe fontpack \"%s\"?" 1962msgstr "Do you really want to permanently delete the fontpack \"%s\"?"
1951 1963
1952msgid "dlg.fontpack.delete" 1964msgid "dlg.fontpack.delete"
1953msgstr "Delete Fontpack" 1965msgstr "Delete Fontpack"
@@ -1970,3 +1982,13 @@ msgstr "Lagrange attempts to load all individual TrueType files that are copied
1970msgid "truetype.help.installed" 1982msgid "truetype.help.installed"
1971msgstr "This font is installed in the user fonts directory." 1983msgstr "This font is installed in the user fonts directory."
1972 1984
1985msgid "heading.dismiss.warning"
1986msgstr "DISMISS WARNING"
1987
1988#, c-format
1989msgid "dlg.dismiss.ansi"
1990msgstr "Permanently dismiss warning about terminal emulation on %s?"
1991
1992msgid "dlg.dismiss.warning"
1993msgstr "Dismiss Warning"
1994
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 260f2e5a..83b483d2 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 fb3bf4dd..11bc8e33 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 d49f2e26..a60f01f1 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 ef1aed2e..69bcaab2 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 fe32a70f..fc77fdfd 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 f4eddb4f..83633ca2 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 8156fe47..45c11f2c 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 48ca1bd7..03132f81 100644
--- a/res/lang/gl.bin
+++ b/res/lang/gl.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 1f749638..ef71e46b 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 45d8c737..87d2169b 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 81bdb744..34dbccfb 100644
--- a/res/lang/isv.bin
+++ b/res/lang/isv.bin
Binary files differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index 2bb0c45e..8c0d29d1 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 df0fea58..46994a99 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 ac847262..df5db2d2 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 7be9ab33..a0c36b93 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 4a213691..24db65b4 100644
--- a/res/lang/tok.bin
+++ b/res/lang/tok.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 5abdc3ee..9c23c550 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 2295148e..bc94f856 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/gmdocument.c b/src/gmdocument.c
index bd3fa788..c7d5501f 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -107,6 +107,7 @@ struct Impl_GmDocument {
107 iChar siteIcon; 107 iChar siteIcon;
108 iMedia * media; 108 iMedia * media;
109 iStringSet *openURLs; /* currently open URLs for highlighting links */ 109 iStringSet *openURLs; /* currently open URLs for highlighting links */
110 int warnings;
110 iBool isPaletteValid; 111 iBool isPaletteValid;
111 iColor palette[tmMax_ColorId]; /* copy of the color palette */ 112 iColor palette[tmMax_ColorId]; /* copy of the color palette */
112}; 113};
@@ -596,6 +597,8 @@ static void doLayout_GmDocument_(iGmDocument *d) {
596 isPreformat = iTrue; 597 isPreformat = iTrue;
597 isFirstText = iFalse; 598 isFirstText = iFalse;
598 } 599 }
600 d->warnings &= ~missingGlyphs_GmDocumentWarning;
601 checkMissing_Text(); /* clear the flag */
599 setAnsiFlags_Text(d->theme.ansiEscapes); 602 setAnsiFlags_Text(d->theme.ansiEscapes);
600 while (nextSplit_Rangecc(content, "\n", &contentLine)) { 603 while (nextSplit_Rangecc(content, "\n", &contentLine)) {
601 iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */ 604 iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */
@@ -1045,6 +1048,9 @@ static void doLayout_GmDocument_(iGmDocument *d) {
1045 } 1048 }
1046#endif 1049#endif
1047 d->size.y = pos.y; 1050 d->size.y = pos.y;
1051 if (checkMissing_Text()) {
1052 d->warnings |= missingGlyphs_GmDocumentWarning;
1053 }
1048 /* Go over the preformatted blocks and mark them wide if at least one run is wide. */ { 1054 /* Go over the preformatted blocks and mark them wide if at least one run is wide. */ {
1049 /* TODO: Store the dimensions and ranges for later access. */ 1055 /* TODO: Store the dimensions and ranges for later access. */
1050 iForEach(Array, i, &d->layout) { 1056 iForEach(Array, i, &d->layout) {
@@ -1084,6 +1090,7 @@ void init_GmDocument(iGmDocument *d) {
1084 d->siteIcon = 0; 1090 d->siteIcon = 0;
1085 d->media = new_Media(); 1091 d->media = new_Media();
1086 d->openURLs = NULL; 1092 d->openURLs = NULL;
1093 d->warnings = 0;
1087 d->isPaletteValid = iFalse; 1094 d->isPaletteValid = iFalse;
1088 iZap(d->palette); 1095 iZap(d->palette);
1089} 1096}
@@ -1123,16 +1130,28 @@ static void setDerivedThemeColors_(enum iGmDocumentTheme theme) {
1123 mix_Color(get_Color(tmBannerTitle_ColorId), 1130 mix_Color(get_Color(tmBannerTitle_ColorId),
1124 get_Color(tmBackground_ColorId), 1131 get_Color(tmBackground_ColorId),
1125 theme == colorfulDark_GmDocumentTheme ? 0.55f : 0)); 1132 theme == colorfulDark_GmDocumentTheme ? 0.55f : 0));
1126 const int bannerItemFg = isDark_GmDocumentTheme(currentTheme_()) ? white_ColorId : black_ColorId; 1133 /* Banner colors. */
1127 set_Color(tmBannerItemBackground_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), 1134 if (theme == highContrast_GmDocumentTheme) {
1128 get_Color(tmBannerTitle_ColorId), 0.1f)); 1135 set_Color(tmBannerItemBackground_ColorId, get_Color(tmBannerBackground_ColorId));
1129 set_Color(tmBannerItemFrame_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), 1136 set_Color(tmBannerItemFrame_ColorId, get_Color(tmBannerIcon_ColorId));
1130 get_Color(tmBannerTitle_ColorId), 0.4f)); 1137 set_Color(tmBannerItemTitle_ColorId, get_Color(tmBannerTitle_ColorId));
1131 set_Color(tmBannerItemText_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), 1138 set_Color(tmBannerItemText_ColorId, get_Color(tmBannerTitle_ColorId));
1132 get_Color(bannerItemFg), 0.75f)); 1139 }
1133 set_Color(tmBannerItemTitle_ColorId, get_Color(bannerItemFg)); 1140 else {
1141 const int bannerItemFg = isDark_GmDocumentTheme(currentTheme_()) ? white_ColorId : black_ColorId;
1142 set_Color(tmBannerItemBackground_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId),
1143 get_Color(tmBannerTitle_ColorId), 0.1f));
1144 set_Color(tmBannerItemFrame_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId),
1145 get_Color(tmBannerTitle_ColorId), 0.4f));
1146 set_Color(tmBannerItemText_ColorId, mix_Color(get_Color(tmBannerTitle_ColorId),
1147 get_Color(bannerItemFg), 0.5f));
1148 set_Color(tmBannerItemTitle_ColorId, get_Color(bannerItemFg));
1149 }
1150 /* Modified backgrounds. */
1134 set_Color(tmBackgroundAltText_ColorId, 1151 set_Color(tmBackgroundAltText_ColorId,
1135 mix_Color(get_Color(tmQuoteIcon_ColorId), get_Color(tmBackground_ColorId), 0.85f)); 1152 mix_Color(get_Color(tmQuoteIcon_ColorId), get_Color(tmBackground_ColorId), 0.85f));
1153 set_Color(tmFrameAltText_ColorId,
1154 mix_Color(get_Color(tmQuoteIcon_ColorId), get_Color(tmBackground_ColorId), 0.4f));
1136 set_Color(tmBackgroundOpenLink_ColorId, 1155 set_Color(tmBackgroundOpenLink_ColorId,
1137 mix_Color(get_Color(tmLinkText_ColorId), get_Color(tmBackground_ColorId), 0.90f)); 1156 mix_Color(get_Color(tmLinkText_ColorId), get_Color(tmBackground_ColorId), 0.90f));
1138 set_Color(tmFrameOpenLink_ColorId, 1157 set_Color(tmFrameOpenLink_ColorId,
@@ -2008,6 +2027,14 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int
2008 /* Normalize and convert to Gemtext if needed. */ 2027 /* Normalize and convert to Gemtext if needed. */
2009 set_String(&d->unormSource, source); 2028 set_String(&d->unormSource, source);
2010 set_String(&d->source, source); 2029 set_String(&d->source, source);
2030 /* Detect use of ANSI escapes. */ {
2031 iRegExp *ansiEsc = new_RegExp("\x1b[[()]([0-9;AB]*?)m", 0);
2032 iRegExpMatch m;
2033 init_RegExpMatch(&m);
2034 const iBool found = matchString_RegExp(ansiEsc, &d->unormSource, &m);
2035 iChangeFlags(d->warnings, ansiEscapes_GmDocumentWarning, found);
2036 iRelease(ansiEsc);
2037 }
2011 if (d->format == gemini_SourceFormat) { 2038 if (d->format == gemini_SourceFormat) {
2012 d->theme.ansiEscapes = prefs_App()->gemtextAnsiEscapes; 2039 d->theme.ansiEscapes = prefs_App()->gemtextAnsiEscapes;
2013 } 2040 }
@@ -2157,6 +2184,10 @@ size_t memorySize_GmDocument(const iGmDocument *d) {
2157 memorySize_Media(d->media); 2184 memorySize_Media(d->media);
2158} 2185}
2159 2186
2187int warnings_GmDocument(const iGmDocument *d) {
2188 return d->warnings;
2189}
2190
2160iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { 2191iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) {
2161 const char * src = constBegin_String(&d->source); 2192 const char * src = constBegin_String(&d->source);
2162 const size_t startPos = (start ? start - src : 0); 2193 const size_t startPos = (start ? start - src : 0);
diff --git a/src/gmdocument.h b/src/gmdocument.h
index e010ad43..0c24302f 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -172,6 +172,11 @@ iRangecc findLoc_GmRun (const iGmRun *, iInt2 pos);
172iDeclareClass(GmDocument) 172iDeclareClass(GmDocument)
173iDeclareObjectConstruction(GmDocument) 173iDeclareObjectConstruction(GmDocument)
174 174
175enum iGmDocumentWarning {
176 ansiEscapes_GmDocumentWarning = iBit(1),
177 missingGlyphs_GmDocumentWarning = iBit(2),
178};
179
175/* 180/*
176enum iGmDocumentBanner { 181enum iGmDocumentBanner {
177 none_GmDocumentBanner, 182 none_GmDocumentBanner,
@@ -221,6 +226,7 @@ iInt2 size_GmDocument (const iGmDocument *);
221const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ 226const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */
222const iString * source_GmDocument (const iGmDocument *); 227const iString * source_GmDocument (const iGmDocument *);
223size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ 228size_t memorySize_GmDocument (const iGmDocument *); /* bytes */
229int warnings_GmDocument (const iGmDocument *);
224 230
225iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); 231iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start);
226iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); 232iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before);
diff --git a/src/gmutil.c b/src/gmutil.c
index 264e7ac8..22219221 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -755,6 +755,14 @@ static const struct {
755 { 0x1f645, /* no good */ 755 { 0x1f645, /* no good */
756 "${error.certverify}", 756 "${error.certverify}",
757 "${error.certverify.msg}" } }, 757 "${error.certverify.msg}" } },
758 { ansiEscapes_GmStatusCode,
759 { 0x1f5b3, /* old computer */
760 "${error.ansi}",
761 "${error.ansi.msg}" } },
762 { missingGlyphs_GmStatusCode,
763 { 0x1f520, /* ABCD */
764 "${error.glyphs}",
765 "${error.glyphs.msg}" } },
758 { temporaryFailure_GmStatusCode, 766 { temporaryFailure_GmStatusCode,
759 { 0x1f50c, /* electric plug */ 767 { 0x1f50c, /* electric plug */
760 "${error.temporary}", 768 "${error.temporary}",
diff --git a/src/gmutil.h b/src/gmutil.h
index c65a17e6..6b10b444 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -45,6 +45,8 @@ enum iGmStatusCode {
45 tlsFailure_GmStatusCode, 45 tlsFailure_GmStatusCode,
46 tlsServerCertificateExpired_GmStatusCode, 46 tlsServerCertificateExpired_GmStatusCode,
47 tlsServerCertificateNotVerified_GmStatusCode, 47 tlsServerCertificateNotVerified_GmStatusCode,
48 ansiEscapes_GmStatusCode,
49 missingGlyphs_GmStatusCode,
48 50
49 none_GmStatusCode = 0, 51 none_GmStatusCode = 0,
50 /* general status code categories */ 52 /* general status code categories */
diff --git a/src/ui/banner.c b/src/ui/banner.c
index d95c853b..e817f1bb 100644
--- a/src/ui/banner.c
+++ b/src/ui/banner.c
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
27#include "lang.h" 27#include "lang.h"
28#include "paint.h" 28#include "paint.h"
29#include "util.h" 29#include "util.h"
30#include "app.h"
30 31
31iDeclareType(BannerItem) 32iDeclareType(BannerItem)
32 33
@@ -53,27 +54,33 @@ struct Impl_Banner {
53 iRect rect; 54 iRect rect;
54 iString site; 55 iString site;
55 iString icon; 56 iString icon;
57 int siteHeight;
56 iArray items; 58 iArray items;
57 iBool isClick; 59 iBool isClick;
58}; 60};
59 61
60iDefineTypeConstruction(Banner) 62iDefineTypeConstruction(Banner)
61 63
64#define itemGap_Banner_ (3 * gap_UI)
65#define itemVPad_Banner_ (2 * gap_UI)
66#define itemHPad_Banner_ (3 * gap_UI)
67#define bottomPad_Banner_ (4 * gap_UI)
68
62static void updateHeight_Banner_(iBanner *d) { 69static void updateHeight_Banner_(iBanner *d) {
63 d->rect.size.y = 0; 70 d->rect.size.y = 0;
64 if (!isEmpty_String(&d->site)) { 71 if (!isEmpty_String(&d->site)) {
65 d->rect.size.y += lineHeight_Text(banner_FontId) * 2; 72 d->siteHeight = lineHeight_Text(banner_FontId) * 2;
73 d->rect.size.y += d->siteHeight;
66 } 74 }
67 const size_t numItems = size_Array(&d->items); 75 const size_t numItems = size_Array(&d->items);
68 if (numItems) { 76 if (numItems) {
69 const int outerPad = 2 * gap_UI;
70 const int innerPad = gap_UI; 77 const int innerPad = gap_UI;
71 iConstForEach(Array, i, &d->items) { 78 iConstForEach(Array, i, &d->items) {
72 const iBannerItem *item = i.value; 79 const iBannerItem *item = i.value;
73 d->rect.size.y += item->height; 80 d->rect.size.y += item->height;
74 } 81 }
75 d->rect.size.y += (numItems - 1) * innerPad; 82 d->rect.size.y += (numItems - 1) * itemGap_Banner_;
76 d->rect.size.y += outerPad; 83 d->rect.size.y += bottomPad_Banner_;
77 } 84 }
78} 85}
79 86
@@ -99,9 +106,9 @@ void setOwner_Banner(iBanner *d, iDocumentWidget *owner) {
99 106
100static void updateItemHeight_Banner_(const iBanner *d, iBannerItem *item) { 107static void updateItemHeight_Banner_(const iBanner *d, iBannerItem *item) {
101 item->height = measureWrapRange_Text(uiContent_FontId, 108 item->height = measureWrapRange_Text(uiContent_FontId,
102 width_Rect(d->rect) - 6 * gap_UI, 109 width_Rect(d->rect) - 2 * itemHPad_Banner_,
103 range_String(&item->text)) 110 range_String(&item->text))
104 .bounds.size.y + 4 * gap_UI; 111 .bounds.size.y + 2 * itemVPad_Banner_;
105} 112}
106 113
107void setWidth_Banner(iBanner *d, int width) { 114void setWidth_Banner(iBanner *d, int width) {
@@ -186,6 +193,7 @@ void draw_Banner(const iBanner *d) {
186 return; 193 return;
187 } 194 }
188 iRect bounds = d->rect; 195 iRect bounds = d->rect;
196 /* TODO: use d->siteHeight */
189 iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2); 197 iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2);
190 iPaint p; 198 iPaint p;
191 init_Paint(&p); 199 init_Paint(&p);
@@ -218,16 +226,28 @@ void draw_Banner(const iBanner *d) {
218 setBaseAttributes_Text(uiContent_FontId, tmBannerItemText_ColorId); 226 setBaseAttributes_Text(uiContent_FontId, tmBannerItemText_ColorId);
219 iWrapText wt = { 227 iWrapText wt = {
220 .text = range_String(&item->text), 228 .text = range_String(&item->text),
221 .maxWidth = width_Rect(itemRect) - 6 * gap_UI, 229 .maxWidth = width_Rect(itemRect) - 2 * itemHPad_Banner_,
222 .mode = word_WrapTextMode 230 .mode = word_WrapTextMode
223 }; 231 };
224 draw_WrapText(&wt, uiContent_FontId, add_I2(pos, init_I2(3 * gap_UI, 2 * gap_UI)), 232 draw_WrapText(&wt, uiContent_FontId, add_I2(pos, init_I2(itemHPad_Banner_, itemVPad_Banner_)),
225 tmBannerItemText_ColorId); 233 tmBannerItemText_ColorId);
226 pos.y += innerPad; 234 pos.y += item->height + itemGap_Banner_;
227 } 235 }
228 setBaseAttributes_Text(-1, -1); 236 setBaseAttributes_Text(-1, -1);
229} 237}
230 238
239static size_t itemAtCoord_Banner_(const iBanner *d, iInt2 coord) {
240 iInt2 pos = addY_I2(topLeft_Rect(d->rect), lineHeight_Text(banner_FontId) * 2);
241 iConstForEach(Array, i, &d->items) {
242 const iBannerItem *item = i.value;
243 if (contains_Rect((iRect){ pos, init_I2(d->rect.size.x, item->height)}, coord)) {
244 return index_ArrayConstIterator(&i);
245 }
246 pos.y += itemGap_Banner_ + item->height;
247 }
248 return iInvalidPos;
249}
250
231iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { 251iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) {
232 iWidget *w = as_Widget(d->doc); 252 iWidget *w = as_Widget(d->doc);
233 switch (ev->type) { 253 switch (ev->type) {
@@ -240,14 +260,49 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) {
240 case SDL_MOUSEBUTTONUP: 260 case SDL_MOUSEBUTTONUP:
241 /* Clicking on the top/side banner navigates to site root. */ 261 /* Clicking on the top/side banner navigates to site root. */
242 if (ev->button.button == SDL_BUTTON_LEFT) { 262 if (ev->button.button == SDL_BUTTON_LEFT) {
243 const iBool isInside = contains_Rect(d->rect, init_I2(ev->button.x, ev->button.y)); 263 const iInt2 coord = init_I2(ev->button.x, ev->button.y);
264 const iBool isInside = contains_Rect(d->rect, coord);
244 if (isInside && ev->button.state == SDL_PRESSED) { 265 if (isInside && ev->button.state == SDL_PRESSED) {
245 d->isClick = iTrue; 266 d->isClick = iTrue;
246 return iTrue; 267 return iTrue;
247 } 268 }
248 else if (ev->button.state == SDL_RELEASED) { 269 else if (ev->button.state == SDL_RELEASED) {
249 if (d->isClick && isInside) { 270 if (d->isClick && isInside) {
250 postCommand_Widget(d->doc, "navigate.root"); 271 const size_t index = itemAtCoord_Banner_(d, coord);
272 if (index == iInvalidPos) {
273 if (coord.y < top_Rect(d->rect) + d->siteHeight) {
274 postCommand_Widget(d->doc, "navigate.root");
275 }
276 }
277 else {
278 const iBannerItem *item = constAt_Array(&d->items, index);
279 if (item->type == error_BannerType) {
280 postCommand_Widget(d->doc, "document.info");
281 }
282 else {
283 switch (item->code) {
284 case missingGlyphs_GmStatusCode:
285 postCommandf_App("open newtab:1 url:about:fonts");
286 break;
287 case ansiEscapes_GmStatusCode:
288 makeQuestion_Widget(uiHeading_ColorEscape "${heading.dismiss.warning}",
289 format_Lang("${dlg.dismiss.ansi}",
290 format_CStr(uiTextStrong_ColorEscape "%s"
291 restore_ColorEscape, cstr_Rangecc(urlHost_String(url_DocumentWidget(d->doc))))),
292 (iMenuItem[]){ { "${cancel}" },
293 { uiTextAction_ColorEscape "${dlg.dismiss.warning}",
294 SDLK_RETURN, 0,
295 format_CStr("document.dismiss warning:%d",
296 ansiEscapes_GmDocumentWarning)
297 }
298 }, 2);
299 break;
300 default:
301 postCommand_Widget(d->doc, "document.info");
302 break;
303 }
304 }
305 }
251 } 306 }
252 d->isClick = iFalse; 307 d->isClick = iFalse;
253 } 308 }
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 13a8dae7..755cec6a 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -934,7 +934,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
934 updateSideOpacity_DocumentWidget_(d, iTrue); 934 updateSideOpacity_DocumentWidget_(d, iTrue);
935 animateMedia_DocumentWidget_(d); 935 animateMedia_DocumentWidget_(d);
936 setPos_Banner(d->banner, addY_I2(topLeft_Rect(documentBounds_DocumentWidget_(d)), 936 setPos_Banner(d->banner, addY_I2(topLeft_Rect(documentBounds_DocumentWidget_(d)),
937 -pos_SmoothScroll(&d->scrollY))); 937 -pos_SmoothScroll(&d->scrollY)));
938 /*init_I2(documentBounds_DocumentWidget_(d).pos.x, 938 /*init_I2(documentBounds_DocumentWidget_(d).pos.x,
939 viewPos_DocumentWidget_(d) - 939 viewPos_DocumentWidget_(d) -
940 documentTopPad_DocumentWidget_(d)));*/ 940 documentTopPad_DocumentWidget_(d)));*/
@@ -1291,7 +1291,6 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode
1291 iRelease(errorDoc); 1291 iRelease(errorDoc);
1292 clear_Banner(d->banner); 1292 clear_Banner(d->banner);
1293 add_Banner(d->banner, error_BannerType, code, meta); 1293 add_Banner(d->banner, error_BannerType, code, meta);
1294// translate_Lang(src);
1295 d->state = ready_RequestState; 1294 d->state = ready_RequestState;
1296 setSource_DocumentWidget(d, src); 1295 setSource_DocumentWidget(d, src);
1297 updateTheme_DocumentWidget_(d); 1296 updateTheme_DocumentWidget_(d);
@@ -1798,6 +1797,16 @@ static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) {
1798 } 1797 }
1799} 1798}
1800 1799
1800static void addBannerWarnings_DocumentWidget_(iDocumentWidget *d) {
1801 if (warnings_GmDocument(d->doc) & missingGlyphs_GmDocumentWarning) {
1802 add_Banner(d->banner, warning_BannerType, missingGlyphs_GmStatusCode, NULL);
1803 /* TODO: List one or more of the missing characters and/or their Unicode blocks? */
1804 }
1805 if (warnings_GmDocument(d->doc) & ansiEscapes_GmDocumentWarning) {
1806 add_Banner(d->banner, warning_BannerType, ansiEscapes_GmStatusCode, NULL);
1807 }
1808}
1809
1801static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, 1810static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY,
1802 const iGmResponse *resp, iGmDocument *cachedDoc) { 1811 const iGmResponse *resp, iGmDocument *cachedDoc) {
1803 setLinkNumberMode_DocumentWidget_(d, iFalse); 1812 setLinkNumberMode_DocumentWidget_(d, iFalse);
@@ -1824,6 +1833,7 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n
1824// (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); 1833// (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0);
1825 clear_Banner(d->banner); 1834 clear_Banner(d->banner);
1826 updateBanner_DocumentWidget_(d); 1835 updateBanner_DocumentWidget_(d);
1836 addBannerWarnings_DocumentWidget_(d);
1827 } 1837 }
1828 d->state = ready_RequestState; 1838 d->state = ready_RequestState;
1829 postProcessRequestContent_DocumentWidget_(d, iTrue); 1839 postProcessRequestContent_DocumentWidget_(d, iTrue);
@@ -2864,10 +2874,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2864 size_Array(items)); 2874 size_Array(items));
2865 delete_Array(items); 2875 delete_Array(items);
2866 /* Enforce a minimum size. */ 2876 /* Enforce a minimum size. */
2867 iWidget *sizer = new_Widget(); 2877// iWidget *sizer = new_Widget();
2868 setFixedSize_Widget(sizer, init_I2(gap_UI * 65, 1)); 2878// setFixedSize_Widget(sizer, init_I2(gap_UI * 65, 1));
2869 addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag); 2879// addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag);
2870 setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse); 2880// setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse);
2871 if (deviceType_App() != phone_AppDeviceType) { 2881 if (deviceType_App() != phone_AppDeviceType) {
2872 const iWidget *lockButton = findWidget_Root("navbar.lock"); 2882 const iWidget *lockButton = findWidget_Root("navbar.lock");
2873 setPos_Widget(dlg, windowToLocal_Widget(dlg, bottomLeft_Rect(bounds_Widget(lockButton)))); 2883 setPos_Widget(dlg, windowToLocal_Widget(dlg, bottomLeft_Rect(bounds_Widget(lockButton))));
@@ -2989,6 +2999,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2989 if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { 2999 if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) {
2990 init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); /* TODO: unless user already scrolled! */ 3000 init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); /* TODO: unless user already scrolled! */
2991 } 3001 }
3002 addBannerWarnings_DocumentWidget_(d);
2992 iChangeFlags(d->flags, 3003 iChangeFlags(d->flags,
2993 urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag, 3004 urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag,
2994 iFalse); 3005 iFalse);
@@ -4377,7 +4388,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4377 if (run->flags & altText_GmRunFlag) { 4388 if (run->flags & altText_GmRunFlag) {
4378 const iInt2 margin = preRunMargin_GmDocument(doc, preId_GmRun(run)); 4389 const iInt2 margin = preRunMargin_GmDocument(doc, preId_GmRun(run));
4379 fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); 4390 fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId);
4380 drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); 4391 drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmFrameAltText_ColorId);
4381 drawWrapRange_Text(run->font, 4392 drawWrapRange_Text(run->font,
4382 add_I2(visPos, margin), 4393 add_I2(visPos, margin),
4383 run->visBounds.size.x - 2 * margin.x, 4394 run->visBounds.size.x - 2 * margin.x,
@@ -5053,7 +5064,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
5053 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); 5064 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
5054 } 5065 }
5055 fillRect_Paint(&ctx.paint, altRect, tmBackgroundAltText_ColorId); 5066 fillRect_Paint(&ctx.paint, altRect, tmBackgroundAltText_ColorId);
5056 drawRect_Paint(&ctx.paint, altRect, tmQuoteIcon_ColorId); 5067 drawRect_Paint(&ctx.paint, altRect, tmFrameAltText_ColorId);
5057 setOpacity_Text(altTextOpacity); 5068 setOpacity_Text(altTextOpacity);
5058 drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap, 5069 drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap,
5059 tmQuote_ColorId, meta->altText); 5070 tmQuote_ColorId, meta->altText);
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index fbcd24b9..daca05d1 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -380,8 +380,12 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
380 } 380 }
381 if (d->flags.wrap) { 381 if (d->flags.wrap) {
382 const iRect cont = contentBounds_LabelWidget_(d); 382 const iRect cont = contentBounds_LabelWidget_(d);
383 drawWrapRange_Text( 383 iWrapText wt = {
384 d->font, topLeft_Rect(cont), width_Rect(cont), fg, range_String(&d->label)); 384 .text = range_String(&d->label),
385 .maxWidth = width_Rect(cont),
386 .mode = word_WrapTextMode,
387 };
388 draw_WrapText(&wt, d->font, topLeft_Rect(cont), fg);
385 } 389 }
386 else if (flags & alignLeft_WidgetFlag) { 390 else if (flags & alignLeft_WidgetFlag) {
387 draw_Text(d->font, add_I2(bounds.pos, addX_I2(padding_LabelWidget_(d, 0), iconPad)), 391 draw_Text(d->font, add_I2(bounds.pos, addX_I2(padding_LabelWidget_(d, 0), iconPad)),
diff --git a/src/ui/text.c b/src/ui/text.c
index 106c55e9..2ef54d39 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -264,6 +264,7 @@ struct Impl_Text {
264 int ansiFlags; 264 int ansiFlags;
265 int baseFontId; /* base attributes (for restoring via escapes) */ 265 int baseFontId; /* base attributes (for restoring via escapes) */
266 int baseColorId; 266 int baseColorId;
267 iBool missingGlyphs; /* true if a glyph couldn't be found */
267}; 268};
268 269
269iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) 270iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)
@@ -797,6 +798,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
797 } 798 }
798#endif // 0 799#endif // 0
799 if (!*glyphIndex) { 800 if (!*glyphIndex) {
801 activeText_->missingGlyphs = iTrue;
800 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); 802 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr);
801 } 803 }
802 return d; 804 return d;
@@ -2260,6 +2262,13 @@ iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) {
2260 return tm; 2262 return tm;
2261} 2263}
2262 2264
2265iBool checkMissing_Text(void) {
2266 iText *d = activeText_;
2267 const iBool missing = d->missingGlyphs;
2268 d->missingGlyphs = iFalse;
2269 return missing;
2270}
2271
2263SDL_Texture *glyphCache_Text(void) { 2272SDL_Texture *glyphCache_Text(void) {
2264 return activeText_->cache; 2273 return activeText_->cache;
2265} 2274}
diff --git a/src/ui/text.h b/src/ui/text.h
index 99ddb704..13a636d4 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -220,6 +220,7 @@ struct Impl_WrapText {
220iTextMetrics measure_WrapText (iWrapText *, int fontId); 220iTextMetrics measure_WrapText (iWrapText *, int fontId);
221iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); 221iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color);
222 222
223iBool checkMissing_Text (void); /* returns the flag, and clears it */
223SDL_Texture * glyphCache_Text (void); 224SDL_Texture * glyphCache_Text (void);
224 225
225enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode }; 226enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode };