summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po9
-rw-r--r--res/about/version.gmi5
-rw-r--r--res/lang/cs.binbin30899 -> 30962 bytes
-rw-r--r--res/lang/de.binbin29906 -> 29969 bytes
-rw-r--r--res/lang/en.binbin26003 -> 26066 bytes
-rw-r--r--res/lang/eo.binbin24957 -> 25020 bytes
-rw-r--r--res/lang/es.binbin29730 -> 29793 bytes
-rw-r--r--res/lang/es_MX.binbin27062 -> 27125 bytes
-rw-r--r--res/lang/fi.binbin29563 -> 29626 bytes
-rw-r--r--res/lang/fr.binbin30710 -> 30773 bytes
-rw-r--r--res/lang/gl.binbin28915 -> 28978 bytes
-rw-r--r--res/lang/hu.binbin30735 -> 30798 bytes
-rw-r--r--res/lang/ia.binbin28062 -> 28125 bytes
-rw-r--r--res/lang/ie.binbin28650 -> 28713 bytes
-rw-r--r--res/lang/isv.binbin24723 -> 24786 bytes
-rw-r--r--res/lang/pl.binbin29338 -> 29401 bytes
-rw-r--r--res/lang/ru.binbin44098 -> 44161 bytes
-rw-r--r--res/lang/sk.binbin25059 -> 25122 bytes
-rw-r--r--res/lang/sr.binbin43524 -> 43587 bytes
-rw-r--r--res/lang/tok.binbin26772 -> 26835 bytes
-rw-r--r--res/lang/tr.binbin28956 -> 29019 bytes
-rw-r--r--res/lang/uk.binbin43443 -> 43506 bytes
-rw-r--r--res/lang/zh_Hans.binbin24957 -> 25020 bytes
-rw-r--r--res/lang/zh_Hant.binbin25155 -> 25218 bytes
-rw-r--r--src/app.c19
-rw-r--r--src/gmcerts.c9
-rw-r--r--src/gmcerts.h5
-rw-r--r--src/sitespec.c81
-rw-r--r--src/sitespec.h20
-rw-r--r--src/ui/root.c89
30 files changed, 214 insertions, 23 deletions
diff --git a/po/en.po b/po/en.po
index 066c4eca..9a154c5c 100644
--- a/po/en.po
+++ b/po/en.po
@@ -321,6 +321,10 @@ msgstr "Show History"
321msgid "menu.show.identities" 321msgid "menu.show.identities"
322msgstr "Show Identities" 322msgstr "Show Identities"
323 323
324# Active identity toolbar menu.
325msgid "menu.hide.identities"
326msgstr "Hide Identities"
327""
324# Used in the View menu on macOS. Shows sidebar and switches sidebar tab. 328# Used in the View menu on macOS. Shows sidebar and switches sidebar tab.
325msgid "menu.show.outline" 329msgid "menu.show.outline"
326msgstr "Show Page Outline" 330msgstr "Show Page Outline"
@@ -629,6 +633,11 @@ msgstr "Stop Using Everywhere"
629msgid "ident.export" 633msgid "ident.export"
630msgstr "Export" 634msgstr "Export"
631 635
636# The %s represents the name of an identity.
637#, c-format
638msgid "ident.switch"
639msgstr "Switch to %s"
640
632msgid "heading.ident.use" 641msgid "heading.ident.use"
633msgstr "IDENTITY USAGE" 642msgstr "IDENTITY USAGE"
634 643
diff --git a/res/about/version.gmi b/res/about/version.gmi
index a9043b2c..7a14e79b 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -6,6 +6,10 @@
6``` 6```
7# Release notes 7# Release notes
8 8
9## 1.10
10New features:
11* Identity toolbar menu can be used to switch between alternate identities that have been used on the site. If you have multiple accounts on a site, this makes it more convenient to switch between them.
12
9## 1.9.2 13## 1.9.2
10* Windows: Use the correct version number for update checks. 14* Windows: Use the correct version number for update checks.
11* Shorter label for "Mark All as Read" in Feeds sidebar actions. 15* Shorter label for "Mark All as Read" in Feeds sidebar actions.
@@ -22,7 +26,6 @@
22* Fixed the New Tab button not staying at the right edge of the window, depending on how many tabs are open. 26* Fixed the New Tab button not staying at the right edge of the window, depending on how many tabs are open.
23 27
24## 1.9 28## 1.9
25
26New features: 29New features:
27* Added a toolbar button for toggling the left sidebar. 30* Added a toolbar button for toggling the left sidebar.
28* Added an unsplit button in the toolbar when in split view mode. 31* Added an unsplit button in the toolbar when in split view mode.
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index 647e1f34..d3e06c73 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 b2bb35a0..b9d155a7 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 fbf4c73c..5bb8299a 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 7156b7c8..ff1ddb01 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 1acb50d1..ba9b2343 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 078de89d..f4ddcc72 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 ccd3e133..f73891d6 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 96c1148e..0851f535 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 4269b5ed..80215589 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 7b7edb50..7d7ed94e 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 4750b545..572015c2 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 e4c90ef9..bb485f4e 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 80754fc5..f90a1e7d 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 c0affedf..753e595b 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 966deaea..edcebb14 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 deda3b69..b843a383 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 8d8591e6..1ef302d9 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 1b0c0733..6e3c7af7 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 60ef518d..71b9382f 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 2ed16909..f7040f2f 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 3a83dd40..3ccab576 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 7da4b273..39393417 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 9f4d60e3..540c46d8 100644
--- a/src/app.c
+++ b/src/app.c
@@ -3149,6 +3149,25 @@ iBool handleCommand_App(const char *cmd) {
3149 postCommand_App("idents.changed"); 3149 postCommand_App("idents.changed");
3150 return iTrue; 3150 return iTrue;
3151 } 3151 }
3152 else if (equal_Command(cmd, "ident.switch")) {
3153 /* This is different than "ident.signin" in that the currently used identity's activation
3154 URL is used instead of the current one. */
3155 const iString *docUrl = url_DocumentWidget(document_App());
3156 const iGmIdentity *cur = identityForUrl_GmCerts(d->certs, docUrl);
3157 iGmIdentity *dst = findIdentity_GmCerts(
3158 d->certs, collect_Block(hexDecode_Rangecc(range_Command(cmd, "fp"))));
3159 if (cur && dst && cur != dst) {
3160 iString *useUrl = copy_String(findUse_GmIdentity(cur, docUrl));
3161 if (isEmpty_String(useUrl)) {
3162 useUrl = copy_String(docUrl);
3163 }
3164 signIn_GmCerts(d->certs, dst, useUrl);
3165 postCommand_App("idents.changed");
3166 postCommand_App("navigate.reload");
3167 delete_String(useUrl);
3168 }
3169 return iTrue;
3170 }
3152 else if (equal_Command(cmd, "idents.changed")) { 3171 else if (equal_Command(cmd, "idents.changed")) {
3153 saveIdentities_GmCerts(d->certs); 3172 saveIdentities_GmCerts(d->certs);
3154 return iFalse; 3173 return iFalse;
diff --git a/src/gmcerts.c b/src/gmcerts.c
index f95fea7d..345c36e0 100644
--- a/src/gmcerts.c
+++ b/src/gmcerts.c
@@ -201,6 +201,15 @@ void clearUse_GmIdentity(iGmIdentity *d) {
201 clear_StringSet(d->useUrls); 201 clear_StringSet(d->useUrls);
202} 202}
203 203
204const iString *findUse_GmIdentity(const iGmIdentity *d, const iString *url) {
205 iConstForEach(StringSet, using, d->useUrls) {
206 if (startsWith_String(url, cstr_String(using.value))) {
207 return using.value;
208 }
209 }
210 return NULL;
211}
212
204const iString *name_GmIdentity(const iGmIdentity *d) { 213const iString *name_GmIdentity(const iGmIdentity *d) {
205 iString *name = collect_String(subject_TlsCertificate(d->cert)); 214 iString *name = collect_String(subject_TlsCertificate(d->cert));
206 if (startsWith_String(name, "CN = ")) { 215 if (startsWith_String(name, "CN = ")) {
diff --git a/src/gmcerts.h b/src/gmcerts.h
index 02a41c14..6ece1954 100644
--- a/src/gmcerts.h
+++ b/src/gmcerts.h
@@ -48,8 +48,9 @@ iBool isUsed_GmIdentity (const iGmIdentity *);
48iBool isUsedOn_GmIdentity (const iGmIdentity *, const iString *url); 48iBool isUsedOn_GmIdentity (const iGmIdentity *, const iString *url);
49iBool isUsedOnDomain_GmIdentity (const iGmIdentity *, const iRangecc domain); 49iBool isUsedOnDomain_GmIdentity (const iGmIdentity *, const iRangecc domain);
50 50
51void setUse_GmIdentity (iGmIdentity *, const iString *url, iBool use); 51void setUse_GmIdentity (iGmIdentity *, const iString *url, iBool use);
52void clearUse_GmIdentity (iGmIdentity *); 52void clearUse_GmIdentity (iGmIdentity *);
53const iString *findUse_GmIdentity (const iGmIdentity *, const iString *url);
53 54
54const iString *name_GmIdentity(const iGmIdentity *); 55const iString *name_GmIdentity(const iGmIdentity *);
55 56
diff --git a/src/sitespec.c b/src/sitespec.c
index 6f4546f0..f8b77c86 100644
--- a/src/sitespec.c
+++ b/src/sitespec.c
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include <the_Foundation/file.h> 25#include <the_Foundation/file.h>
26#include <the_Foundation/path.h> 26#include <the_Foundation/path.h>
27#include <the_Foundation/stringhash.h> 27#include <the_Foundation/stringhash.h>
28#include <the_Foundation/stringarray.h>
28#include <the_Foundation/toml.h> 29#include <the_Foundation/toml.h>
29 30
30iDeclareClass(SiteParams) 31iDeclareClass(SiteParams)
@@ -35,6 +36,7 @@ struct Impl_SiteParams {
35 uint16_t titanPort; 36 uint16_t titanPort;
36 iString titanIdentity; /* fingerprint */ 37 iString titanIdentity; /* fingerprint */
37 int dismissWarnings; 38 int dismissWarnings;
39 iStringArray usedIdentities; /* fingerprints; latest ones at the end */
38 /* TODO: theme seed, style settings */ 40 /* TODO: theme seed, style settings */
39}; 41};
40 42
@@ -42,12 +44,23 @@ void init_SiteParams(iSiteParams *d) {
42 d->titanPort = 0; /* undefined */ 44 d->titanPort = 0; /* undefined */
43 init_String(&d->titanIdentity); 45 init_String(&d->titanIdentity);
44 d->dismissWarnings = 0; 46 d->dismissWarnings = 0;
47 init_StringArray(&d->usedIdentities);
45} 48}
46 49
47void deinit_SiteParams(iSiteParams *d) { 50void deinit_SiteParams(iSiteParams *d) {
51 deinit_StringArray(&d->usedIdentities);
48 deinit_String(&d->titanIdentity); 52 deinit_String(&d->titanIdentity);
49} 53}
50 54
55static size_t findUsedIdentity_SiteParams_(const iSiteParams *d, const iString *fingerprint) {
56 iConstForEach(StringArray, i, &d->usedIdentities) {
57 if (equal_String(i.value, fingerprint)) {
58 return index_StringArrayConstIterator(&i);
59 }
60 }
61 return iInvalidPos;
62}
63
51iDefineClass(SiteParams) 64iDefineClass(SiteParams)
52iDefineObjectConstruction(SiteParams) 65iDefineObjectConstruction(SiteParams)
53 66
@@ -130,6 +143,12 @@ static void handleIniKeyValue_SiteSpec_(void *context, const iString *table, con
130 else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) { 143 else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) {
131 d->loadParams->dismissWarnings = value->value.int64; 144 d->loadParams->dismissWarnings = value->value.int64;
132 } 145 }
146 else if (!cmp_String(key, "usedIdentities") && value->type == string_TomlType) {
147 iRangecc seg = iNullRange;
148 while (nextSplit_Rangecc(range_String(value->value.string), " ", &seg)) {
149 pushBack_StringArray(&d->loadParams->usedIdentities, collectNewRange_String(seg));
150 }
151 }
133} 152}
134 153
135static iBool load_SiteSpec_(iSiteSpec *d) { 154static iBool load_SiteSpec_(iSiteSpec *d) {
@@ -151,6 +170,7 @@ static void save_SiteSpec_(iSiteSpec *d) {
151 if (open_File(f, writeOnly_FileMode | text_FileMode)) { 170 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
152 iString *buf = new_String(); 171 iString *buf = new_String();
153 iConstForEach(StringHash, i, &d->sites) { 172 iConstForEach(StringHash, i, &d->sites) {
173 iBeginCollect();
154 const iBlock * key = &i.value->keyBlock; 174 const iBlock * key = &i.value->keyBlock;
155 const iSiteParams *params = i.value->object; 175 const iSiteParams *params = i.value->object;
156 format_String(buf, "[%s]\n", cstr_Block(key)); 176 format_String(buf, "[%s]\n", cstr_Block(key));
@@ -164,8 +184,15 @@ static void save_SiteSpec_(iSiteSpec *d) {
164 if (params->dismissWarnings) { 184 if (params->dismissWarnings) {
165 appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings); 185 appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings);
166 } 186 }
187 if (!isEmpty_StringArray(&params->usedIdentities)) {
188 appendFormat_String(
189 buf,
190 "usedIdentities = \"%s\"\n",
191 cstrCollect_String(joinCStr_StringArray(&params->usedIdentities, " ")));
192 }
167 appendCStr_String(buf, "\n"); 193 appendCStr_String(buf, "\n");
168 write_File(f, utf8_String(buf)); 194 write_File(f, utf8_String(buf));
195 iEndCollect();
169 } 196 }
170 delete_String(buf); 197 delete_String(buf);
171 } 198 }
@@ -188,14 +215,19 @@ void deinit_SiteSpec(void) {
188 deinit_String(&d->saveDir); 215 deinit_String(&d->saveDir);
189} 216}
190 217
191void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) { 218static iSiteParams *findParams_SiteSpec_(iSiteSpec *d, const iString *site) {
192 iSiteSpec *d = &siteSpec_;
193 const iString *hashKey = collect_String(lower_String(site)); 219 const iString *hashKey = collect_String(lower_String(site));
194 iSiteParams *params = value_StringHash(&d->sites, hashKey); 220 iSiteParams *params = value_StringHash(&d->sites, hashKey);
195 if (!params) { 221 if (!params) {
196 params = new_SiteParams(); 222 params = new_SiteParams();
197 insert_StringHash(&d->sites, hashKey, params); 223 insert_StringHash(&d->sites, hashKey, params);
198 } 224 }
225 return params;
226}
227
228void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) {
229 iSiteSpec *d = &siteSpec_;
230 iSiteParams *params = findParams_SiteSpec_(d, site);
199 iBool needSave = iFalse; 231 iBool needSave = iFalse;
200 switch (key) { 232 switch (key) {
201 case titanPort_SiteSpecKey: 233 case titanPort_SiteSpecKey:
@@ -216,12 +248,7 @@ void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) {
216 248
217void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) { 249void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) {
218 iSiteSpec *d = &siteSpec_; 250 iSiteSpec *d = &siteSpec_;
219 const iString *hashKey = collect_String(lower_String(site)); 251 iSiteParams *params = findParams_SiteSpec_(d, site);
220 iSiteParams *params = value_StringHash(&d->sites, hashKey);
221 if (!params) {
222 params = new_SiteParams();
223 insert_StringHash(&d->sites, hashKey, params);
224 }
225 iBool needSave = iFalse; 252 iBool needSave = iFalse;
226 switch (key) { 253 switch (key) {
227 case titanIdentity_SiteSpecKey: 254 case titanIdentity_SiteSpecKey:
@@ -238,6 +265,44 @@ void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const i
238 } 265 }
239} 266}
240 267
268static void insertOrRemoveString_SiteSpec_(iSiteSpec *d, const iString *site, enum iSiteSpecKey key,
269 const iString *value, iBool doInsert) {
270 iSiteParams *params = findParams_SiteSpec_(d, site);
271 iBool needSave = iFalse;
272 switch (key) {
273 case usedIdentities_SiteSpecKey: {
274 const size_t index = findUsedIdentity_SiteParams_(params, value);
275 if (doInsert && index == iInvalidPos) {
276 pushBack_StringArray(&params->usedIdentities, value);
277 needSave = iTrue;
278 }
279 else if (!doInsert && index != iInvalidPos) {
280 remove_StringArray(&params->usedIdentities, index);
281 needSave = iTrue;
282 }
283 break;
284 }
285 default:
286 break;
287 }
288 if (needSave) {
289 save_SiteSpec_(d);
290 }
291}
292
293void insertString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) {
294 insertOrRemoveString_SiteSpec_(&siteSpec_, site, key, value, iTrue);
295}
296
297void removeString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) {
298 insertOrRemoveString_SiteSpec_(&siteSpec_, site, key, value, iFalse);
299}
300
301const iStringArray *strings_SiteSpec(const iString *site, enum iSiteSpecKey key) {
302 const iSiteParams *params = findParams_SiteSpec_(&siteSpec_, site);
303 return &params->usedIdentities;
304}
305
241int value_SiteSpec(const iString *site, enum iSiteSpecKey key) { 306int value_SiteSpec(const iString *site, enum iSiteSpecKey key) {
242 iSiteSpec *d = &siteSpec_; 307 iSiteSpec *d = &siteSpec_;
243 const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site))); 308 const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site)));
diff --git a/src/sitespec.h b/src/sitespec.h
index 5adaeb8c..11c40e3c 100644
--- a/src/sitespec.h
+++ b/src/sitespec.h
@@ -22,22 +22,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#pragma once 23#pragma once
24 24
25#include <the_Foundation/string.h> 25#include <the_Foundation/stringarray.h>
26 26
27iDeclareType(SiteSpec) 27iDeclareType(SiteSpec)
28 28
29enum iSiteSpecKey { 29enum iSiteSpecKey {
30 titanPort_SiteSpecKey, 30 titanPort_SiteSpecKey, /* int */
31 titanIdentity_SiteSpecKey, 31 titanIdentity_SiteSpecKey, /* String */
32 dismissWarnings_SiteSpecKey, 32 dismissWarnings_SiteSpecKey, /* int */
33 usedIdentities_SiteSpecKey, /* StringArray */
33}; 34};
34 35
35void init_SiteSpec (const char *saveDir); 36void init_SiteSpec (const char *saveDir);
36void deinit_SiteSpec (void); 37void deinit_SiteSpec (void);
37 38
38/* changes saved immediately */ 39/* changes saved immediately */
39void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value); 40void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value);
40void setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value); 41void setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value);
42void insertString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value);
43void removeString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value);
41 44
42int value_SiteSpec (const iString *site, enum iSiteSpecKey key); 45int value_SiteSpec (const iString *site, enum iSiteSpecKey key);
43const iString * valueString_SiteSpec (const iString *site, enum iSiteSpecKey key); 46const iString * valueString_SiteSpec (const iString *site, enum iSiteSpecKey key);
47const iStringArray *strings_SiteSpec (const iString *site, enum iSiteSpecKey key);
diff --git a/src/ui/root.c b/src/ui/root.c
index c2161d80..f06ae842 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -38,6 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
38#include "../history.h" 38#include "../history.h"
39#include "../gmcerts.h" 39#include "../gmcerts.h"
40#include "../gmutil.h" 40#include "../gmutil.h"
41#include "../sitespec.h"
41#include "../visited.h" 42#include "../visited.h"
42 43
43#if defined (iPlatformMsys) 44#if defined (iPlatformMsys)
@@ -330,6 +331,66 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
330 openMenuFlags_Widget(menu, zero_I2(), postCommands_MenuOpenFlags | center_MenuOpenFlags); 331 openMenuFlags_Widget(menu, zero_I2(), postCommands_MenuOpenFlags | center_MenuOpenFlags);
331 return iTrue; 332 return iTrue;
332 } 333 }
334 else if (equal_Command(cmd, "identmenu.open")) {
335 iWidget *button = findWidget_Root("navbar.ident");
336 iArray items;
337 init_Array(&items, sizeof(iMenuItem));
338 /* Current identity. */
339 const iString *docUrl = url_DocumentWidget(document_App());
340 const iGmIdentity *ident = identityForUrl_GmCerts(certs_App(), docUrl);
341 const iString *fp = collect_String(hexEncode_Block(&ident->fingerprint));
342 pushBackN_Array(&items,
343 (iMenuItem[]){ { format_CStr("///" uiHeading_ColorEscape "%s",
344 ident ? cstr_String(name_GmIdentity(ident))
345 : "${menu.identity.notactive}") },
346 { "---" } },
347 2);
348 /* Alternate identities. */ {
349 const iString *site = collectNewRange_String(urlRoot_String(docUrl));
350 iBool haveAlts = iFalse;
351 iConstForEach(StringArray, i, strings_SiteSpec(site, usedIdentities_SiteSpecKey)) {
352 if (!equal_String(i.value, fp)) {
353 const iBlock *otherFp = collect_Block(hexDecode_Rangecc(range_String(i.value)));
354 const iGmIdentity *other = findIdentity_GmCerts(certs_App(), otherFp);
355 if (other) {
356 pushBack_Array(
357 &items,
358 &(iMenuItem){
359 format_CStr(translateCStr_Lang("\U0001f816 ${ident.switch}"),
360 cstr_String(name_GmIdentity(other))),
361 0,
362 0,
363 format_CStr("ident.switch fp:%s", cstr_String(i.value)) });
364 haveAlts = iTrue;
365 }
366 }
367 }
368 if (haveAlts) {
369 pushBack_Array(&items, &(iMenuItem){ "---" });
370 }
371 }
372 iSidebarWidget *sidebar = findWidget_App("sidebar");
373 pushBackN_Array(
374 &items,
375 (iMenuItem[]){
376 { add_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" },
377 { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" },
378 { "---" },
379 { isVisible_Widget(sidebar) && mode_SidebarWidget(sidebar) == identities_SidebarMode
380 ? leftHalf_Icon " ${menu.hide.identities}"
381 : leftHalf_Icon " ${menu.show.identities}",
382 0,
383 0,
384 deviceType_App() == phone_AppDeviceType ? "toolbar.showident"
385 : "sidebar.mode arg:3 toggle:1" },
386 },
387 4);
388 iWidget *menu =
389 makeMenu_Widget(button, constData_Array(&items), size_Array(&items));
390 openMenu_Widget(menu, topLeft_Rect(bounds_Widget(button)));
391 deinit_Array(&items);
392 return iTrue;
393 }
333 else if (equal_Command(cmd, "contextclick")) { 394 else if (equal_Command(cmd, "contextclick")) {
334 iBool showBarMenu = iFalse; 395 iBool showBarMenu = iFalse;
335 if (equal_Rangecc(range_Command(cmd, "id"), "buttons")) { 396 if (equal_Rangecc(range_Command(cmd, "id"), "buttons")) {
@@ -780,6 +841,26 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
780 dismissPortraitPhoneSidebars_Root(get_Root()); 841 dismissPortraitPhoneSidebars_Root(get_Root());
781 updateNavBarIdentity_(navBar); 842 updateNavBarIdentity_(navBar);
782 updateNavDirButtons_(navBar); 843 updateNavDirButtons_(navBar);
844 /* Update site-specific used identities. */ {
845 const iGmIdentity *ident =
846 identityForUrl_GmCerts(certs_App(), url_DocumentWidget(document_App()));
847 if (ident) {
848 const iString *site =
849 collectNewRange_String(urlRoot_String(canonicalUrl_String(urlStr)));
850 const iStringArray *usedIdents =
851 strings_SiteSpec(site, usedIdentities_SiteSpecKey);
852 const iString *fingerprint = collect_String(hexEncode_Block(&ident->fingerprint));
853 /* Keep this identity at the end of the list. */
854 removeString_SiteSpec(site, usedIdentities_SiteSpecKey, fingerprint);
855 insertString_SiteSpec(site, usedIdentities_SiteSpecKey, fingerprint);
856 /* Keep the list short. */
857 while (size_StringArray(usedIdents) > 5) {
858 removeString_SiteSpec(site,
859 usedIdentities_SiteSpecKey,
860 constAt_StringArray(usedIdents, 0));
861 }
862 }
863 }
783 /* Icon updates should be limited to automatically chosen icons if the user 864 /* Icon updates should be limited to automatically chosen icons if the user
784 is allowed to pick their own in the future. */ 865 is allowed to pick their own in the future. */
785 if (updateBookmarkIcon_Bookmarks(bookmarks_App(), urlStr, 866 if (updateBookmarkIcon_Bookmarks(bookmarks_App(), urlStr,
@@ -1268,10 +1349,10 @@ void createUserInterface_Root(iRoot *d) {
1268 setId_Widget(addChild_Widget(rightEmbed, iClob(makePadding_Widget(0))), "url.embedpad"); 1349 setId_Widget(addChild_Widget(rightEmbed, iClob(makePadding_Widget(0))), "url.embedpad");
1269 } 1350 }
1270 /* The active identity menu. */ { 1351 /* The active identity menu. */ {
1271 iLabelWidget *idMenu = makeMenuButton_LabelWidget( 1352 iLabelWidget *idButton = new_LabelWidget(person_Icon, "identmenu.open");
1272 "\U0001f464", identityButtonMenuItems_, iElemCount(identityButtonMenuItems_)); 1353// "\U0001f464", identityButtonMenuItems_, iElemCount(identityButtonMenuItems_));
1273 setAlignVisually_LabelWidget(idMenu, iTrue); 1354 setAlignVisually_LabelWidget(idButton, iTrue);
1274 setId_Widget(addChildFlags_Widget(navBar, iClob(idMenu), collapse_WidgetFlag), "navbar.ident"); 1355 setId_Widget(addChildFlags_Widget(navBar, iClob(idButton), collapse_WidgetFlag), "navbar.ident");
1275 } 1356 }
1276 addChildFlags_Widget(navBar, iClob(new_Widget()), expand_WidgetFlag); 1357 addChildFlags_Widget(navBar, iClob(new_Widget()), expand_WidgetFlag);
1277 setId_Widget(addChildFlags_Widget(navBar, 1358 setId_Widget(addChildFlags_Widget(navBar,