From d368b2b41bf45b3a3a8a2ab5f8b61fb3dac5b7ea Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 14 Nov 2021 22:20:26 +0200 Subject: Localized language names in Preferences Each dropdown item now uses its own language. This avoids the need to translate all language names to every language, and also helps the user to find the language(s) they understand. The name strings are incorporated as C11 u8"" string literals, which hopefully won't cause compatibility issues as the_Foundation already requires C11. Note that the Translation dialog retains the localized language labels because the user may not speak the source language, so they need to see it in their own language. IssueID #391 --- src/ui/util.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'src/ui') diff --git a/src/ui/util.c b/src/ui/util.c index 88348ff8..2624bf2b 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -2199,27 +2199,27 @@ iWidget *makeDialog_Widget(const char *id, iWidget *makePreferences_Widget(void) { /* Common items. */ - const iMenuItem langItems[] = { { "${lang.cs} - cs", 0, 0, "uilang id:cs" }, - { "${lang.de} - de", 0, 0, "uilang id:de" }, - { "${lang.en} - en", 0, 0, "uilang id:en" }, - { "${lang.es} - es", 0, 0, "uilang id:es" }, - { "${lang.es.mx} - es", 0, 0, "uilang id:es_MX" }, - { "${lang.eo} - eo", 0, 0, "uilang id:eo" }, - { "${lang.fi} - fi", 0, 0, "uilang id:fi" }, - { "${lang.fr} - fr", 0, 0, "uilang id:fr" }, - { "${lang.gl} - gl", 0, 0, "uilang id:gl" }, - { "${lang.hu} - hu", 0, 0, "uilang id:hu" }, - { "${lang.ia} - ia", 0, 0, "uilang id:ia" }, - { "${lang.ie} - ie", 0, 0, "uilang id:ie" }, - { "${lang.isv} - isv", 0, 0, "uilang id:isv" }, - { "${lang.pl} - pl", 0, 0, "uilang id:pl" }, - { "${lang.ru} - ru", 0, 0, "uilang id:ru" }, - { "${lang.sk} - sk", 0, 0, "uilang id:sk" }, - { "${lang.sr} - sr", 0, 0, "uilang id:sr" }, - { "${lang.tok} - tok", 0, 0, "uilang id:tok" }, - { "${lang.uk} - uk", 0, 0, "uilang id:uk" }, - { "${lang.zh.hans} - zh", 0, 0, "uilang id:zh_Hans" }, - { "${lang.zh.hant} - zh", 0, 0, "uilang id:zh_Hant" }, + const iMenuItem langItems[] = { { u8"Čeština - cs", 0, 0, "uilang id:cs" }, + { u8"Deutsch - de", 0, 0, "uilang id:de" }, + { u8"English - en", 0, 0, "uilang id:en" }, + { u8"Español - es", 0, 0, "uilang id:es" }, + { u8"Español (México) - es", 0, 0, "uilang id:es_MX" }, + { u8"Esperanto - eo", 0, 0, "uilang id:eo" }, + { u8"Suomi - fi", 0, 0, "uilang id:fi" }, + { u8"Français - fr", 0, 0, "uilang id:fr" }, + { u8"Galego - gl", 0, 0, "uilang id:gl" }, + { u8"Magyar - hu", 0, 0, "uilang id:hu" }, + { u8"Interlingua - ia", 0, 0, "uilang id:ia" }, + { u8"Interlingue - ie", 0, 0, "uilang id:ie" }, + { u8"Interslavic - isv", 0, 0, "uilang id:isv" }, + { u8"Polski - pl", 0, 0, "uilang id:pl" }, + { u8"Русский - ru", 0, 0, "uilang id:ru" }, + { u8"Slovak - sk", 0, 0, "uilang id:sk" }, + { u8"Српски - sr", 0, 0, "uilang id:sr" }, + { u8"Toki pona - tok", 0, 0, "uilang id:tok" }, + { u8"Українська - uk", 0, 0, "uilang id:uk" }, + { u8"简体中文 - zh", 0, 0, "uilang id:zh_Hans" }, + { u8"繁體/正體中文 - zh", 0, 0, "uilang id:zh_Hant" }, { NULL } }; const iMenuItem returnKeyBehaviors[] = { { "${prefs.returnkey.linebreak} " uiTextAction_ColorEscape shift_Icon return_Icon -- cgit v1.2.3 From 984b005bd01a57e53212bc1176b57b617414e75d Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 19 Nov 2021 14:32:55 +0200 Subject: Fixed clicking on tabs when banner is underneath Banner was too eager to eat all mouse events. --- res/about/version.gmi | 3 +++ src/ui/banner.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'src/ui') diff --git a/res/about/version.gmi b/res/about/version.gmi index ffd91f7f..020373e4 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi @@ -6,6 +6,9 @@ ``` # Release notes +## 1.8.3 +* Fixed clicking on UI elements that are over the page top banner. The banner would always get clicked instead. + ## 1.8.2 * Fixed encoding of `+` characters in URLs as per RFC 3986. * Fixed crash when fontpack.ini specifies a file that cannot be found. diff --git a/src/ui/banner.c b/src/ui/banner.c index 0ffb1d9f..7168f4b2 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c @@ -257,8 +257,26 @@ static size_t itemAtCoord_Banner_(const iBanner *d, iInt2 coord) { return iInvalidPos; } +static iBool isInside_Banner(const iBanner *d, const SDL_Event *ev) { + if (ev->type == SDL_MOUSEMOTION || ev->type == SDL_MOUSEBUTTONDOWN || + ev->type == SDL_MOUSEBUTTONDOWN) { + iInt2 coord; + if (ev->type == SDL_MOUSEMOTION) { + coord = init_I2(ev->motion.x, ev->motion.y); + } + else { + coord = init_I2(ev->button.x, ev->button.y); + } + return contains_Rect(bounds_Widget(constAs_Widget(d->doc)), coord); + } + return iTrue; +} + iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { iWidget *w = as_Widget(d->doc); + if (!isInside_Banner(d, ev)) { + return iFalse; + } switch (ev->type) { case SDL_MOUSEMOTION: { const iInt2 coord = init_I2(ev->motion.x, ev->motion.y); -- cgit v1.2.3 From 4f18b568d40be7f6d80e2dd73dd6ae2fdf515a2f Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 19 Nov 2021 14:34:22 +0200 Subject: Cleanup --- src/ui/banner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ui') diff --git a/src/ui/banner.c b/src/ui/banner.c index 7168f4b2..2d166fd2 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c @@ -257,7 +257,7 @@ static size_t itemAtCoord_Banner_(const iBanner *d, iInt2 coord) { return iInvalidPos; } -static iBool isInside_Banner(const iBanner *d, const SDL_Event *ev) { +static iBool isInside_Banner_(const iBanner *d, const SDL_Event *ev) { if (ev->type == SDL_MOUSEMOTION || ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONDOWN) { iInt2 coord; -- cgit v1.2.3 From 42fb4f7d2190b32871d7d0a6336618b8e3500203 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 19 Nov 2021 14:34:34 +0200 Subject: Cleanup --- src/ui/banner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ui') diff --git a/src/ui/banner.c b/src/ui/banner.c index 2d166fd2..7ec189a4 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c @@ -274,7 +274,7 @@ static iBool isInside_Banner_(const iBanner *d, const SDL_Event *ev) { iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { iWidget *w = as_Widget(d->doc); - if (!isInside_Banner(d, ev)) { + if (!isInside_Banner_(d, ev)) { return iFalse; } switch (ev->type) { -- cgit v1.2.3 From c58985ed3105ff7fc29fc0d53ba81ba55882e05e Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 21 Nov 2021 14:35:32 +0200 Subject: Site-specific Titan upload identity The identity to be used for uploads is now in sitespec.ini, so it applies to an entire site root. This should match actual use cases better than having URL-specific identities. Also fixed an issue with native menus. Replacing the items with new ones was not implemented. IssueID #379 --- res/about/version.gmi | 2 ++ src/gmcerts.c | 6 ++++++ src/sitespec.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/sitespec.h | 9 +++++++-- src/ui/uploadwidget.c | 45 +++++++++++++++++++++++++++++++++------------ src/ui/util.c | 50 +++++++++++++++++++++++++++++++------------------- src/ui/util.h | 1 + 7 files changed, 129 insertions(+), 35 deletions(-) (limited to 'src/ui') diff --git a/res/about/version.gmi b/res/about/version.gmi index 020373e4..b5750c8c 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi @@ -8,6 +8,8 @@ ## 1.8.3 * Fixed clicking on UI elements that are over the page top banner. The banner would always get clicked instead. +* Titan upload identity is remembered as a site-specific setting. It is no longer affected by selections in the Identities sidebar. +* macOS: Fixed updating items in native menus, e.g., upload identity selection. ## 1.8.2 * Fixed encoding of `+` characters in URLs as per RFC 3986. diff --git a/src/gmcerts.c b/src/gmcerts.c index 36fd7d55..f95fea7d 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c @@ -295,6 +295,9 @@ static void loadIdentities_GmCerts_(iGmCerts *d) { } iGmIdentity *findIdentity_GmCerts(iGmCerts *d, const iBlock *fingerprint) { + if (isEmpty_Block(fingerprint)) { + return NULL; + } iForEach(PtrArray, i, &d->idents) { iGmIdentity *ident = i.ptr; if (cmp_Block(fingerprint, &ident->fingerprint) == 0) { /* TODO: could use a hash */ @@ -549,6 +552,9 @@ const iGmIdentity *constIdentity_GmCerts(const iGmCerts *d, unsigned int id) { } const iGmIdentity *identityForUrl_GmCerts(const iGmCerts *d, const iString *url) { + if (isEmpty_String(url)) { + return NULL; + } lock_Mutex(d->mtx); const iGmIdentity *found = NULL; iConstForEach(PtrArray, i, &d->idents) { diff --git a/src/sitespec.c b/src/sitespec.c index 0332af2d..6f4546f0 100644 --- a/src/sitespec.c +++ b/src/sitespec.c @@ -33,17 +33,19 @@ iDeclareObjectConstruction(SiteParams) struct Impl_SiteParams { iObject object; uint16_t titanPort; + iString titanIdentity; /* fingerprint */ int dismissWarnings; /* TODO: theme seed, style settings */ }; void init_SiteParams(iSiteParams *d) { - d->titanPort = 0; /* undefined */ + d->titanPort = 0; /* undefined */ + init_String(&d->titanIdentity); d->dismissWarnings = 0; } void deinit_SiteParams(iSiteParams *d) { - iUnused(d); + deinit_String(&d->titanIdentity); } iDefineClass(SiteParams) @@ -122,6 +124,9 @@ static void handleIniKeyValue_SiteSpec_(void *context, const iString *table, con if (!cmp_String(key, "titanPort")) { d->loadParams->titanPort = number_TomlValue(value); } + else if (!cmp_String(key, "titanIdentity") && value->type == string_TomlType) { + set_String(&d->loadParams->titanIdentity, value->value.string); + } else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) { d->loadParams->dismissWarnings = value->value.int64; } @@ -152,6 +157,10 @@ static void save_SiteSpec_(iSiteSpec *d) { if (params->titanPort) { appendFormat_String(buf, "titanPort = %u\n", params->titanPort); } + if (!isEmpty_String(¶ms->titanIdentity)) { + appendFormat_String( + buf, "titanIdentity = \"%s\"\n", cstr_String(¶ms->titanIdentity)); + } if (params->dismissWarnings) { appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings); } @@ -205,6 +214,30 @@ void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) { } } +void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) { + iSiteSpec *d = &siteSpec_; + const iString *hashKey = collect_String(lower_String(site)); + iSiteParams *params = value_StringHash(&d->sites, hashKey); + if (!params) { + params = new_SiteParams(); + insert_StringHash(&d->sites, hashKey, params); + } + iBool needSave = iFalse; + switch (key) { + case titanIdentity_SiteSpecKey: + if (!equal_String(¶ms->titanIdentity, value)) { + needSave = iTrue; + set_String(¶ms->titanIdentity, value); + } + break; + default: + break; + } + if (needSave) { + save_SiteSpec_(d); + } +} + int value_SiteSpec(const iString *site, enum iSiteSpecKey key) { iSiteSpec *d = &siteSpec_; const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site))); @@ -220,3 +253,17 @@ int value_SiteSpec(const iString *site, enum iSiteSpecKey key) { return 0; } } + +const iString *valueString_SiteSpec(const iString *site, enum iSiteSpecKey key) { + iSiteSpec *d = &siteSpec_; + const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site))); + if (!params) { + return 0; + } + switch (key) { + case titanIdentity_SiteSpecKey: + return ¶ms->titanIdentity; + default: + return collectNew_String(); + } +} diff --git a/src/sitespec.h b/src/sitespec.h index 6b64f073..5adaeb8c 100644 --- a/src/sitespec.h +++ b/src/sitespec.h @@ -28,11 +28,16 @@ iDeclareType(SiteSpec) enum iSiteSpecKey { titanPort_SiteSpecKey, + titanIdentity_SiteSpecKey, dismissWarnings_SiteSpecKey, }; void init_SiteSpec (const char *saveDir); void deinit_SiteSpec (void); -void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value); /* changes saved immediately */ -int value_SiteSpec (const iString *site, enum iSiteSpecKey key); +/* changes saved immediately */ +void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value); +void setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value); + +int value_SiteSpec (const iString *site, enum iSiteSpecKey key); +const iString * valueString_SiteSpec (const iString *site, enum iSiteSpecKey key); diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c index 90df1958..bad00071 100644 --- a/src/ui/uploadwidget.c +++ b/src/ui/uploadwidget.c @@ -45,7 +45,7 @@ iDefineObjectConstruction(UploadWidget) enum iUploadIdentity { none_UploadIdentity, - defaultForUrl_UploadIdentity, + defaultForSite_UploadIdentity, dropdown_UploadIdentity, }; @@ -104,9 +104,16 @@ static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) { (avail - inputPos.y) / lineHeight_Text(font_InputWidget(d->input)))); } +static const iGmIdentity *titanIdentityForUrl_(const iString *url) { + return findIdentity_GmCerts( + certs_App(), + collect_Block(hexDecode_Rangecc(range_String(valueString_SiteSpec( + collectNewRange_String(urlRoot_String(url)), titanIdentity_SiteSpecKey))))); +} + static const iArray *makeIdentityItems_UploadWidget_(const iUploadWidget *d) { iArray *items = collectNew_Array(sizeof(iMenuItem)); - const iGmIdentity *urlId = identityForUrl_GmCerts(certs_App(), &d->url); + const iGmIdentity *urlId = titanIdentityForUrl_(&d->url); pushBack_Array(items, &(iMenuItem){ format_CStr("${dlg.upload.id.default} (%s)", urlId ? cstr_String(name_GmIdentity(urlId)) @@ -147,7 +154,7 @@ void init_UploadWidget(iUploadWidget *d) { d->request = NULL; init_String(&d->filePath); d->fileSize = 0; - d->idMode = defaultForUrl_UploadIdentity; + d->idMode = defaultForSite_UploadIdentity; init_Block(&d->idFingerprint, 0); const iMenuItem actions[] = { { "${upload.port}", 0, 0, "upload.setport" }, @@ -289,16 +296,22 @@ void deinit_UploadWidget(iUploadWidget *d) { static void remakeIdentityItems_UploadWidget_(iUploadWidget *d) { iWidget *dropMenu = findChild_Widget(findChild_Widget(as_Widget(d), "upload.id"), "menu"); - releaseChildren_Widget(dropMenu); const iArray *items = makeIdentityItems_UploadWidget_(d); - makeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items)); + /* TODO: Make the following a utility method. */ + if (flags_Widget(dropMenu) & nativeMenu_WidgetFlag) { + setNativeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items)); + } + else { + releaseChildren_Widget(dropMenu); + makeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items)); + } } static void updateIdentityDropdown_UploadWidget_(iUploadWidget *d) { updateDropdownSelection_LabelWidget( findChild_Widget(as_Widget(d), "upload.id"), d->idMode == none_UploadIdentity ? " arg:0" - : d->idMode == defaultForUrl_UploadIdentity + : d->idMode == defaultForSite_UploadIdentity ? " arg:1" : format_CStr(" fp:%s", cstrCollect_String(hexEncode_Block(&d->idFingerprint)))); } @@ -422,7 +435,7 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) { } else if (arg_Command(cmd)) { clear_Block(&d->idFingerprint); - d->idMode = defaultForUrl_UploadIdentity; + d->idMode = defaultForSite_UploadIdentity; } else { clear_Block(&d->idFingerprint); @@ -452,19 +465,27 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) { setSendProgressFunc_GmRequest(d->request, updateProgress_UploadWidget_); setUserData_Object(d->request, d); setUrl_GmRequest(d->request, &d->url); + const iString *site = collectNewRange_String(urlRoot_String(&d->url)); switch (d->idMode) { - case defaultForUrl_UploadIdentity: - break; /* GmRequest handles it */ case none_UploadIdentity: - setIdentity_GmRequest(d->request, NULL); + /* Ensure no identity will be used for this specific URL. */ signOut_GmCerts(certs_App(), url_GmRequest(d->request)); + setValueString_SiteSpec(site, titanIdentity_SiteSpecKey, collectNew_String()); break; case dropdown_UploadIdentity: { iGmIdentity *ident = findIdentity_GmCerts(certs_App(), &d->idFingerprint); - setIdentity_GmRequest(d->request, ident); - signIn_GmCerts(certs_App(), ident, url_GmRequest(d->request)); + if (ident) { + setValueString_SiteSpec(site, + titanIdentity_SiteSpecKey, + collect_String(hexEncode_Block(&ident->fingerprint))); + } break; } + default: + break; + } + if (d->idMode != none_UploadIdentity) { + setIdentity_GmRequest(d->request, titanIdentityForUrl_(&d->url)); } if (isText) { /* Uploading text. */ diff --git a/src/ui/util.c b/src/ui/util.c index 2624bf2b..0a9dde0c 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -809,14 +809,27 @@ static void deleteMenuItems_(iArray *items) { delete_Array(items); } -iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { - iWidget *menu = new_Widget(); +void releaseNativeMenu_Widget(iWidget *d) { #if defined (iHaveNativeContextMenus) - setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue); + iArray *items = userData_Object(d); + if (items) { + iAssert(flags_Widget(d) & nativeMenu_WidgetFlag); + iAssert(items); + deleteMenuItems_(items); + setUserData_Object(d, NULL); + } +#else + iUnused(d); +#endif +} + +void setNativeMenuItems_Widget(iWidget *menu, const iMenuItem *items, size_t n) { +#if defined (iHaveNativeContextMenus) + iAssert(flags_Widget(menu) & nativeMenu_WidgetFlag); + releaseNativeMenu_Widget(menu); setUserData_Object(menu, deepCopyMenuItems_(menu, items, n)); - addChild_Widget(parent, menu); - iRelease(menu); /* owned by parent now */ /* Keyboard shortcuts still need to triggerable via the menu, although the items don't exist. */ { + releaseChildren_Widget(menu); for (size_t i = 0; i < n; i++) { const iMenuItem *item = &items[i]; if (item->key) { @@ -824,6 +837,17 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { } } } +#endif +} + +iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { + iWidget *menu = new_Widget(); +#if defined (iHaveNativeContextMenus) + setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue); + addChild_Widget(parent, menu); + iRelease(menu); /* owned by parent now */ + setUserData_Object(menu, NULL); + setNativeMenuItems_Widget(menu, items, n); #else /* Non-native custom popup menu. This may still be displayed inside a separate window. */ setDrawBufferEnabled_Widget(menu, iTrue); @@ -990,18 +1014,6 @@ iLocalDef iBool isUsingMenuPopupWindows_(void) { #endif } -void releaseNativeMenu_Widget(iWidget *d) { -#if defined (iHaveNativeContextMenus) - iArray *items = userData_Object(d); - iAssert(flags_Widget(d) & nativeMenu_WidgetFlag); - iAssert(items); - deleteMenuItems_(items); - setUserData_Object(d, NULL); -#else - iUnused(d); -#endif -} - void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) { const iBool postCommands = (menuOpenFlags & postCommands_MenuOpenFlags) != 0; #if defined (iHaveNativeContextMenus) @@ -1263,8 +1275,8 @@ void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *s iMenuItem *item = findNativeMenuItem_Widget(menu, selectedCommand); if (item) { setSelected_NativeMenuItem(item, iTrue); - updateText_LabelWidget(dropButton, - removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label))); + updateText_LabelWidget( + dropButton, removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label))); } return; } diff --git a/src/ui/util.h b/src/ui/util.h index cf96dfe4..52b3a692 100644 --- a/src/ui/util.h +++ b/src/ui/util.h @@ -249,6 +249,7 @@ void setMenuItemDisabled_Widget (iWidget *menu, const char *comm void setMenuItemDisabledByIndex_Widget(iWidget *menu, size_t index, iBool disable); void setMenuItemLabel_Widget (iWidget *menu, const char *command, const char *newLabel); void setMenuItemLabelByIndex_Widget (iWidget *menu, size_t index, const char *newLabel); +void setNativeMenuItems_Widget (iWidget *, const iMenuItem *items, size_t n); int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */ -- cgit v1.2.3