diff options
Diffstat (limited to 'src/ui/banner.c')
-rw-r--r-- | src/ui/banner.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/ui/banner.c b/src/ui/banner.c new file mode 100644 index 00000000..5ec3c9f0 --- /dev/null +++ b/src/ui/banner.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "banner.h" | ||
24 | |||
25 | #include "command.h" | ||
26 | #include "documentwidget.h" | ||
27 | #include "paint.h" | ||
28 | #include "util.h" | ||
29 | |||
30 | iDeclareType(BannerItem) | ||
31 | |||
32 | struct Impl_BannerItem { | ||
33 | enum iBannerType type; | ||
34 | enum iGmStatusCode code; | ||
35 | iString message; | ||
36 | int height; | ||
37 | }; | ||
38 | |||
39 | static void init_BannerItem(iBannerItem *d) { | ||
40 | init_String(&d->message); | ||
41 | d->height = 0; | ||
42 | } | ||
43 | |||
44 | static void deinit_BannerItem(iBannerItem *d) { | ||
45 | deinit_String(&d->message); | ||
46 | } | ||
47 | |||
48 | /*----------------------------------------------------------------------------------------------*/ | ||
49 | |||
50 | struct Impl_Banner { | ||
51 | iDocumentWidget *doc; | ||
52 | iRect rect; | ||
53 | iString site; | ||
54 | iString icon; | ||
55 | iArray items; | ||
56 | }; | ||
57 | |||
58 | iDefineTypeConstruction(Banner) | ||
59 | |||
60 | static void updateHeight_Banner_(iBanner *d) { | ||
61 | d->rect.size.y = 0; | ||
62 | if (!isEmpty_String(&d->site)) { //} || !isEmpty_String(&d->icon)) { | ||
63 | d->rect.size.y += lineHeight_Text(banner_FontId) * 2; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | void init_Banner(iBanner *d) { | ||
68 | d->doc = NULL; | ||
69 | d->rect = zero_Rect(); | ||
70 | init_String(&d->site); | ||
71 | init_String(&d->icon); | ||
72 | init_Array(&d->items, sizeof(iBannerItem)); | ||
73 | } | ||
74 | |||
75 | void deinit_Banner(iBanner *d) { | ||
76 | clear_Banner(d); | ||
77 | deinit_Array(&d->items); | ||
78 | deinit_String(&d->icon); | ||
79 | deinit_String(&d->site); | ||
80 | } | ||
81 | |||
82 | void setOwner_Banner(iBanner *d, iDocumentWidget *owner) { | ||
83 | d->doc = owner; | ||
84 | } | ||
85 | |||
86 | void setWidth_Banner(iBanner *d, int width) { | ||
87 | d->rect.size.x = width; | ||
88 | updateHeight_Banner_(d); | ||
89 | } | ||
90 | |||
91 | void setPos_Banner(iBanner *d, iInt2 pos) { | ||
92 | d->rect.pos = pos; | ||
93 | } | ||
94 | |||
95 | int height_Banner(const iBanner *d) { | ||
96 | return d->rect.size.y; | ||
97 | } | ||
98 | |||
99 | void clear_Banner(iBanner *d) { | ||
100 | iForEach(Array, i, &d->items) { | ||
101 | deinit_BannerItem(i.value); | ||
102 | } | ||
103 | clear_Array(&d->items); | ||
104 | clear_String(&d->site); | ||
105 | clear_String(&d->icon); | ||
106 | d->rect.size.y = 0; | ||
107 | } | ||
108 | |||
109 | void setSite_Banner(iBanner *d, iRangecc site, iChar icon) { | ||
110 | clear_String(&d->site); | ||
111 | clear_String(&d->icon); | ||
112 | if (icon) { | ||
113 | setRange_String(&d->site, site); | ||
114 | appendChar_String(&d->icon, icon); | ||
115 | } | ||
116 | updateHeight_Banner_(d); | ||
117 | } | ||
118 | |||
119 | void add_Banner(iBanner *d, enum iBannerType type, enum iGmStatusCode code, const iString *message) { | ||
120 | iBannerItem item; | ||
121 | init_BannerItem(&item); | ||
122 | item.type = type; | ||
123 | item.code = code; | ||
124 | set_String(&item.message, message); | ||
125 | pushBack_Array(&d->items, &item); | ||
126 | updateHeight_Banner_(d); | ||
127 | } | ||
128 | |||
129 | void remove_Banner(iBanner *d, enum iGmStatusCode code) { | ||
130 | iForEach(Array, i, &d->items) { | ||
131 | iBannerItem *item = i.value; | ||
132 | if (item->code == code) { | ||
133 | deinit_BannerItem(item); | ||
134 | remove_ArrayIterator(&i); | ||
135 | } | ||
136 | } | ||
137 | updateHeight_Banner_(d); | ||
138 | } | ||
139 | |||
140 | iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | ||
141 | iWidget *w = as_Widget(d->doc); | ||
142 | /* on motion: */ | ||
143 | // setCursor_Window(window_Widget(w), SDL_SYSTEM_CURSOR_HAND); | ||
144 | return iFalse; | ||
145 | } | ||
146 | |||
147 | #if 0 | ||
148 | static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 visPos) { | ||
149 | const iGmDocument *doc = d->widget->doc; | ||
150 | const iChar icon = siteIcon_GmDocument(doc); | ||
151 | iString str; | ||
152 | init_String(&str); | ||
153 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | ||
154 | if (icon) { | ||
155 | appendChar_String(&str, icon); | ||
156 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); | ||
157 | drawRange_Text( | ||
158 | run->font, | ||
159 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | ||
160 | tmBannerIcon_ColorId, | ||
161 | range_String(&str)); | ||
162 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
163 | } | ||
164 | drawRange_Text(run->font, | ||
165 | bpos, | ||
166 | tmBannerTitle_ColorId, | ||
167 | bannerText_DocumentWidget_(d->widget)); | ||
168 | if (bannerType_GmDocument(doc) == certificateWarning_GmDocumentBanner) { | ||
169 | const int domainHeight = lineHeight_Text(banner_FontId) * 2; | ||
170 | iRect rect = { add_I2(visPos, init_I2(0, domainHeight)), | ||
171 | addY_I2(run->visBounds.size, -domainHeight - lineHeight_Text(uiContent_FontId)) }; | ||
172 | format_String(&str, "${heading.certwarn}"); | ||
173 | const int certFlags = d->widget->certFlags; | ||
174 | if (certFlags & timeVerified_GmCertFlag && certFlags & domainVerified_GmCertFlag) { | ||
175 | iUrl parts; | ||
176 | init_Url(&parts, d->widget->mod.url); | ||
177 | const iTime oldUntil = | ||
178 | domainValidUntil_GmCerts(certs_App(), parts.host, port_Url(&parts)); | ||
179 | iDate exp; | ||
180 | init_Date(&exp, &oldUntil); | ||
181 | iTime now; | ||
182 | initCurrent_Time(&now); | ||
183 | const int days = secondsSince_Time(&oldUntil, &now) / 3600 / 24; | ||
184 | appendCStr_String(&str, "\n"); | ||
185 | if (days <= 30) { | ||
186 | appendCStr_String(&str, | ||
187 | format_CStr(cstrCount_Lang("dlg.certwarn.mayberenewed.n", days), | ||
188 | cstrCollect_String(format_Date(&exp, "%Y-%m-%d")), | ||
189 | days)); | ||
190 | } | ||
191 | else { | ||
192 | appendCStr_String(&str, cstr_Lang("dlg.certwarn.different")); | ||
193 | } | ||
194 | } | ||
195 | else if (certFlags & domainVerified_GmCertFlag) { | ||
196 | appendCStr_String(&str, "\n"); | ||
197 | appendFormat_String(&str, cstr_Lang("dlg.certwarn.expired"), | ||
198 | cstrCollect_String(format_Date(&d->widget->certExpiry, "%Y-%m-%d"))); | ||
199 | } | ||
200 | else if (certFlags & timeVerified_GmCertFlag) { | ||
201 | appendCStr_String(&str, "\n"); | ||
202 | appendFormat_String(&str, cstr_Lang("dlg.certwarn.domain"), | ||
203 | cstr_String(d->widget->certSubject)); | ||
204 | } | ||
205 | else { | ||
206 | appendCStr_String(&str, "\n"); | ||
207 | appendCStr_String(&str, cstr_Lang("dlg.certwarn.domain.expired")); | ||
208 | } | ||
209 | const iInt2 dims = measureWrapRange_Text( | ||
210 | uiContent_FontId, width_Rect(rect) - 16 * gap_UI, range_String(&str)).bounds.size; | ||
211 | const int warnHeight = run->visBounds.size.y - domainHeight; | ||
212 | const int yOff = (lineHeight_Text(uiLabelLarge_FontId) - | ||
213 | lineHeight_Text(uiContent_FontId)) / 2; | ||
214 | const iRect bgRect = | ||
215 | init_Rect(0, visPos.y + domainHeight, d->widgetBounds.size.x, warnHeight); | ||
216 | fillRect_Paint(&d->paint, bgRect, orange_ColorId); | ||
217 | if (!isDark_ColorTheme(colorTheme_App())) { | ||
218 | drawHLine_Paint(&d->paint, | ||
219 | topLeft_Rect(bgRect), width_Rect(bgRect), tmBannerTitle_ColorId); | ||
220 | drawHLine_Paint(&d->paint, | ||
221 | bottomLeft_Rect(bgRect), width_Rect(bgRect), tmBannerTitle_ColorId); | ||
222 | } | ||
223 | const int fg = black_ColorId; | ||
224 | adjustEdges_Rect(&rect, warnHeight / 2 - dims.y / 2 - yOff, 0, 0, 0); | ||
225 | bpos = topLeft_Rect(rect); | ||
226 | draw_Text(uiLabelLarge_FontId, bpos, fg, "\u26a0"); | ||
227 | adjustEdges_Rect(&rect, 0, -8 * gap_UI, 0, 8 * gap_UI); | ||
228 | translate_Lang(&str); | ||
229 | drawWrapRange_Text(uiContent_FontId, | ||
230 | addY_I2(topLeft_Rect(rect), yOff), | ||
231 | width_Rect(rect), | ||
232 | fg, | ||
233 | range_String(&str)); | ||
234 | } | ||
235 | deinit_String(&str); | ||
236 | } | ||
237 | #endif | ||
238 | |||
239 | void draw_Banner(const iBanner *d) { | ||
240 | if (isEmpty_Banner(d)) { | ||
241 | return; | ||
242 | } | ||
243 | iRect bounds = d->rect; | ||
244 | iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2); | ||
245 | iPaint p; | ||
246 | init_Paint(&p); | ||
247 | // drawRect_Paint(&p, bounds, red_ColorId); | ||
248 | /* Draw the icon. */ | ||
249 | if (!isEmpty_String(&d->icon)) { | ||
250 | const int font = banner_FontId; | ||
251 | const iRect iconRect = visualBounds_Text(font, range_String(&d->icon)); | ||
252 | drawRange_Text(font, | ||
253 | addY_I2(pos, -mid_Rect(iconRect).y + lineHeight_Text(font) / 2), | ||
254 | tmBannerIcon_ColorId, | ||
255 | range_String(&d->icon)); | ||
256 | pos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
257 | } | ||
258 | /* Draw the site name. */ | ||
259 | if (!isEmpty_String(&d->site)) { | ||
260 | drawRange_Text(banner_FontId, pos, tmBannerTitle_ColorId, range_String(&d->site)); | ||
261 | } | ||
262 | } | ||