summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/about/version.gmi2
-rw-r--r--src/gmcerts.c6
-rw-r--r--src/sitespec.c51
-rw-r--r--src/sitespec.h9
-rw-r--r--src/ui/uploadwidget.c45
-rw-r--r--src/ui/util.c50
-rw-r--r--src/ui/util.h1
7 files changed, 129 insertions, 35 deletions
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 @@
8 8
9## 1.8.3 9## 1.8.3
10* Fixed clicking on UI elements that are over the page top banner. The banner would always get clicked instead. 10* Fixed clicking on UI elements that are over the page top banner. The banner would always get clicked instead.
11* Titan upload identity is remembered as a site-specific setting. It is no longer affected by selections in the Identities sidebar.
12* macOS: Fixed updating items in native menus, e.g., upload identity selection.
11 13
12## 1.8.2 14## 1.8.2
13* Fixed encoding of `+` characters in URLs as per RFC 3986. 15* 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) {
295} 295}
296 296
297iGmIdentity *findIdentity_GmCerts(iGmCerts *d, const iBlock *fingerprint) { 297iGmIdentity *findIdentity_GmCerts(iGmCerts *d, const iBlock *fingerprint) {
298 if (isEmpty_Block(fingerprint)) {
299 return NULL;
300 }
298 iForEach(PtrArray, i, &d->idents) { 301 iForEach(PtrArray, i, &d->idents) {
299 iGmIdentity *ident = i.ptr; 302 iGmIdentity *ident = i.ptr;
300 if (cmp_Block(fingerprint, &ident->fingerprint) == 0) { /* TODO: could use a hash */ 303 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) {
549} 552}
550 553
551const iGmIdentity *identityForUrl_GmCerts(const iGmCerts *d, const iString *url) { 554const iGmIdentity *identityForUrl_GmCerts(const iGmCerts *d, const iString *url) {
555 if (isEmpty_String(url)) {
556 return NULL;
557 }
552 lock_Mutex(d->mtx); 558 lock_Mutex(d->mtx);
553 const iGmIdentity *found = NULL; 559 const iGmIdentity *found = NULL;
554 iConstForEach(PtrArray, i, &d->idents) { 560 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)
33struct Impl_SiteParams { 33struct Impl_SiteParams {
34 iObject object; 34 iObject object;
35 uint16_t titanPort; 35 uint16_t titanPort;
36 iString titanIdentity; /* fingerprint */
36 int dismissWarnings; 37 int dismissWarnings;
37 /* TODO: theme seed, style settings */ 38 /* TODO: theme seed, style settings */
38}; 39};
39 40
40void init_SiteParams(iSiteParams *d) { 41void init_SiteParams(iSiteParams *d) {
41 d->titanPort = 0; /* undefined */ 42 d->titanPort = 0; /* undefined */
43 init_String(&d->titanIdentity);
42 d->dismissWarnings = 0; 44 d->dismissWarnings = 0;
43} 45}
44 46
45void deinit_SiteParams(iSiteParams *d) { 47void deinit_SiteParams(iSiteParams *d) {
46 iUnused(d); 48 deinit_String(&d->titanIdentity);
47} 49}
48 50
49iDefineClass(SiteParams) 51iDefineClass(SiteParams)
@@ -122,6 +124,9 @@ static void handleIniKeyValue_SiteSpec_(void *context, const iString *table, con
122 if (!cmp_String(key, "titanPort")) { 124 if (!cmp_String(key, "titanPort")) {
123 d->loadParams->titanPort = number_TomlValue(value); 125 d->loadParams->titanPort = number_TomlValue(value);
124 } 126 }
127 else if (!cmp_String(key, "titanIdentity") && value->type == string_TomlType) {
128 set_String(&d->loadParams->titanIdentity, value->value.string);
129 }
125 else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) { 130 else if (!cmp_String(key, "dismissWarnings") && value->type == int64_TomlType) {
126 d->loadParams->dismissWarnings = value->value.int64; 131 d->loadParams->dismissWarnings = value->value.int64;
127 } 132 }
@@ -152,6 +157,10 @@ static void save_SiteSpec_(iSiteSpec *d) {
152 if (params->titanPort) { 157 if (params->titanPort) {
153 appendFormat_String(buf, "titanPort = %u\n", params->titanPort); 158 appendFormat_String(buf, "titanPort = %u\n", params->titanPort);
154 } 159 }
160 if (!isEmpty_String(&params->titanIdentity)) {
161 appendFormat_String(
162 buf, "titanIdentity = \"%s\"\n", cstr_String(&params->titanIdentity));
163 }
155 if (params->dismissWarnings) { 164 if (params->dismissWarnings) {
156 appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings); 165 appendFormat_String(buf, "dismissWarnings = 0x%x\n", params->dismissWarnings);
157 } 166 }
@@ -205,6 +214,30 @@ void setValue_SiteSpec(const iString *site, enum iSiteSpecKey key, int value) {
205 } 214 }
206} 215}
207 216
217void setValueString_SiteSpec(const iString *site, enum iSiteSpecKey key, const iString *value) {
218 iSiteSpec *d = &siteSpec_;
219 const iString *hashKey = collect_String(lower_String(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;
226 switch (key) {
227 case titanIdentity_SiteSpecKey:
228 if (!equal_String(&params->titanIdentity, value)) {
229 needSave = iTrue;
230 set_String(&params->titanIdentity, value);
231 }
232 break;
233 default:
234 break;
235 }
236 if (needSave) {
237 save_SiteSpec_(d);
238 }
239}
240
208int value_SiteSpec(const iString *site, enum iSiteSpecKey key) { 241int value_SiteSpec(const iString *site, enum iSiteSpecKey key) {
209 iSiteSpec *d = &siteSpec_; 242 iSiteSpec *d = &siteSpec_;
210 const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site))); 243 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) {
220 return 0; 253 return 0;
221 } 254 }
222} 255}
256
257const iString *valueString_SiteSpec(const iString *site, enum iSiteSpecKey key) {
258 iSiteSpec *d = &siteSpec_;
259 const iSiteParams *params = constValue_StringHash(&d->sites, collect_String(lower_String(site)));
260 if (!params) {
261 return 0;
262 }
263 switch (key) {
264 case titanIdentity_SiteSpecKey:
265 return &params->titanIdentity;
266 default:
267 return collectNew_String();
268 }
269}
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)
28 28
29enum iSiteSpecKey { 29enum iSiteSpecKey {
30 titanPort_SiteSpecKey, 30 titanPort_SiteSpecKey,
31 titanIdentity_SiteSpecKey,
31 dismissWarnings_SiteSpecKey, 32 dismissWarnings_SiteSpecKey,
32}; 33};
33 34
34void init_SiteSpec (const char *saveDir); 35void init_SiteSpec (const char *saveDir);
35void deinit_SiteSpec (void); 36void deinit_SiteSpec (void);
36 37
37void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value); /* changes saved immediately */ 38/* changes saved immediately */
38int value_SiteSpec (const iString *site, enum iSiteSpecKey key); 39void setValue_SiteSpec (const iString *site, enum iSiteSpecKey key, int value);
40void setValueString_SiteSpec (const iString *site, enum iSiteSpecKey key, const iString *value);
41
42int value_SiteSpec (const iString *site, enum iSiteSpecKey key);
43const 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)
45 45
46enum iUploadIdentity { 46enum iUploadIdentity {
47 none_UploadIdentity, 47 none_UploadIdentity,
48 defaultForUrl_UploadIdentity, 48 defaultForSite_UploadIdentity,
49 dropdown_UploadIdentity, 49 dropdown_UploadIdentity,
50}; 50};
51 51
@@ -104,9 +104,16 @@ static void updateInputMaxHeight_UploadWidget_(iUploadWidget *d) {
104 (avail - inputPos.y) / lineHeight_Text(font_InputWidget(d->input)))); 104 (avail - inputPos.y) / lineHeight_Text(font_InputWidget(d->input))));
105} 105}
106 106
107static const iGmIdentity *titanIdentityForUrl_(const iString *url) {
108 return findIdentity_GmCerts(
109 certs_App(),
110 collect_Block(hexDecode_Rangecc(range_String(valueString_SiteSpec(
111 collectNewRange_String(urlRoot_String(url)), titanIdentity_SiteSpecKey)))));
112}
113
107static const iArray *makeIdentityItems_UploadWidget_(const iUploadWidget *d) { 114static const iArray *makeIdentityItems_UploadWidget_(const iUploadWidget *d) {
108 iArray *items = collectNew_Array(sizeof(iMenuItem)); 115 iArray *items = collectNew_Array(sizeof(iMenuItem));
109 const iGmIdentity *urlId = identityForUrl_GmCerts(certs_App(), &d->url); 116 const iGmIdentity *urlId = titanIdentityForUrl_(&d->url);
110 pushBack_Array(items, 117 pushBack_Array(items,
111 &(iMenuItem){ format_CStr("${dlg.upload.id.default} (%s)", 118 &(iMenuItem){ format_CStr("${dlg.upload.id.default} (%s)",
112 urlId ? cstr_String(name_GmIdentity(urlId)) 119 urlId ? cstr_String(name_GmIdentity(urlId))
@@ -147,7 +154,7 @@ void init_UploadWidget(iUploadWidget *d) {
147 d->request = NULL; 154 d->request = NULL;
148 init_String(&d->filePath); 155 init_String(&d->filePath);
149 d->fileSize = 0; 156 d->fileSize = 0;
150 d->idMode = defaultForUrl_UploadIdentity; 157 d->idMode = defaultForSite_UploadIdentity;
151 init_Block(&d->idFingerprint, 0); 158 init_Block(&d->idFingerprint, 0);
152 const iMenuItem actions[] = { 159 const iMenuItem actions[] = {
153 { "${upload.port}", 0, 0, "upload.setport" }, 160 { "${upload.port}", 0, 0, "upload.setport" },
@@ -289,16 +296,22 @@ void deinit_UploadWidget(iUploadWidget *d) {
289 296
290static void remakeIdentityItems_UploadWidget_(iUploadWidget *d) { 297static void remakeIdentityItems_UploadWidget_(iUploadWidget *d) {
291 iWidget *dropMenu = findChild_Widget(findChild_Widget(as_Widget(d), "upload.id"), "menu"); 298 iWidget *dropMenu = findChild_Widget(findChild_Widget(as_Widget(d), "upload.id"), "menu");
292 releaseChildren_Widget(dropMenu);
293 const iArray *items = makeIdentityItems_UploadWidget_(d); 299 const iArray *items = makeIdentityItems_UploadWidget_(d);
294 makeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items)); 300 /* TODO: Make the following a utility method. */
301 if (flags_Widget(dropMenu) & nativeMenu_WidgetFlag) {
302 setNativeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items));
303 }
304 else {
305 releaseChildren_Widget(dropMenu);
306 makeMenuItems_Widget(dropMenu, constData_Array(items), size_Array(items));
307 }
295} 308}
296 309
297static void updateIdentityDropdown_UploadWidget_(iUploadWidget *d) { 310static void updateIdentityDropdown_UploadWidget_(iUploadWidget *d) {
298 updateDropdownSelection_LabelWidget( 311 updateDropdownSelection_LabelWidget(
299 findChild_Widget(as_Widget(d), "upload.id"), 312 findChild_Widget(as_Widget(d), "upload.id"),
300 d->idMode == none_UploadIdentity ? " arg:0" 313 d->idMode == none_UploadIdentity ? " arg:0"
301 : d->idMode == defaultForUrl_UploadIdentity 314 : d->idMode == defaultForSite_UploadIdentity
302 ? " arg:1" 315 ? " arg:1"
303 : format_CStr(" fp:%s", cstrCollect_String(hexEncode_Block(&d->idFingerprint)))); 316 : format_CStr(" fp:%s", cstrCollect_String(hexEncode_Block(&d->idFingerprint))));
304} 317}
@@ -422,7 +435,7 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
422 } 435 }
423 else if (arg_Command(cmd)) { 436 else if (arg_Command(cmd)) {
424 clear_Block(&d->idFingerprint); 437 clear_Block(&d->idFingerprint);
425 d->idMode = defaultForUrl_UploadIdentity; 438 d->idMode = defaultForSite_UploadIdentity;
426 } 439 }
427 else { 440 else {
428 clear_Block(&d->idFingerprint); 441 clear_Block(&d->idFingerprint);
@@ -452,19 +465,27 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
452 setSendProgressFunc_GmRequest(d->request, updateProgress_UploadWidget_); 465 setSendProgressFunc_GmRequest(d->request, updateProgress_UploadWidget_);
453 setUserData_Object(d->request, d); 466 setUserData_Object(d->request, d);
454 setUrl_GmRequest(d->request, &d->url); 467 setUrl_GmRequest(d->request, &d->url);
468 const iString *site = collectNewRange_String(urlRoot_String(&d->url));
455 switch (d->idMode) { 469 switch (d->idMode) {
456 case defaultForUrl_UploadIdentity:
457 break; /* GmRequest handles it */
458 case none_UploadIdentity: 470 case none_UploadIdentity:
459 setIdentity_GmRequest(d->request, NULL); 471 /* Ensure no identity will be used for this specific URL. */
460 signOut_GmCerts(certs_App(), url_GmRequest(d->request)); 472 signOut_GmCerts(certs_App(), url_GmRequest(d->request));
473 setValueString_SiteSpec(site, titanIdentity_SiteSpecKey, collectNew_String());
461 break; 474 break;
462 case dropdown_UploadIdentity: { 475 case dropdown_UploadIdentity: {
463 iGmIdentity *ident = findIdentity_GmCerts(certs_App(), &d->idFingerprint); 476 iGmIdentity *ident = findIdentity_GmCerts(certs_App(), &d->idFingerprint);
464 setIdentity_GmRequest(d->request, ident); 477 if (ident) {
465 signIn_GmCerts(certs_App(), ident, url_GmRequest(d->request)); 478 setValueString_SiteSpec(site,
479 titanIdentity_SiteSpecKey,
480 collect_String(hexEncode_Block(&ident->fingerprint)));
481 }
466 break; 482 break;
467 } 483 }
484 default:
485 break;
486 }
487 if (d->idMode != none_UploadIdentity) {
488 setIdentity_GmRequest(d->request, titanIdentityForUrl_(&d->url));
468 } 489 }
469 if (isText) { 490 if (isText) {
470 /* Uploading text. */ 491 /* 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) {
809 delete_Array(items); 809 delete_Array(items);
810} 810}
811 811
812iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { 812void releaseNativeMenu_Widget(iWidget *d) {
813 iWidget *menu = new_Widget();
814#if defined (iHaveNativeContextMenus) 813#if defined (iHaveNativeContextMenus)
815 setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue); 814 iArray *items = userData_Object(d);
815 if (items) {
816 iAssert(flags_Widget(d) & nativeMenu_WidgetFlag);
817 iAssert(items);
818 deleteMenuItems_(items);
819 setUserData_Object(d, NULL);
820 }
821#else
822 iUnused(d);
823#endif
824}
825
826void setNativeMenuItems_Widget(iWidget *menu, const iMenuItem *items, size_t n) {
827#if defined (iHaveNativeContextMenus)
828 iAssert(flags_Widget(menu) & nativeMenu_WidgetFlag);
829 releaseNativeMenu_Widget(menu);
816 setUserData_Object(menu, deepCopyMenuItems_(menu, items, n)); 830 setUserData_Object(menu, deepCopyMenuItems_(menu, items, n));
817 addChild_Widget(parent, menu);
818 iRelease(menu); /* owned by parent now */
819 /* Keyboard shortcuts still need to triggerable via the menu, although the items don't exist. */ { 831 /* Keyboard shortcuts still need to triggerable via the menu, although the items don't exist. */ {
832 releaseChildren_Widget(menu);
820 for (size_t i = 0; i < n; i++) { 833 for (size_t i = 0; i < n; i++) {
821 const iMenuItem *item = &items[i]; 834 const iMenuItem *item = &items[i];
822 if (item->key) { 835 if (item->key) {
@@ -824,6 +837,17 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
824 } 837 }
825 } 838 }
826 } 839 }
840#endif
841}
842
843iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
844 iWidget *menu = new_Widget();
845#if defined (iHaveNativeContextMenus)
846 setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue);
847 addChild_Widget(parent, menu);
848 iRelease(menu); /* owned by parent now */
849 setUserData_Object(menu, NULL);
850 setNativeMenuItems_Widget(menu, items, n);
827#else 851#else
828 /* Non-native custom popup menu. This may still be displayed inside a separate window. */ 852 /* Non-native custom popup menu. This may still be displayed inside a separate window. */
829 setDrawBufferEnabled_Widget(menu, iTrue); 853 setDrawBufferEnabled_Widget(menu, iTrue);
@@ -990,18 +1014,6 @@ iLocalDef iBool isUsingMenuPopupWindows_(void) {
990#endif 1014#endif
991} 1015}
992 1016
993void releaseNativeMenu_Widget(iWidget *d) {
994#if defined (iHaveNativeContextMenus)
995 iArray *items = userData_Object(d);
996 iAssert(flags_Widget(d) & nativeMenu_WidgetFlag);
997 iAssert(items);
998 deleteMenuItems_(items);
999 setUserData_Object(d, NULL);
1000#else
1001 iUnused(d);
1002#endif
1003}
1004
1005void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) { 1017void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, int menuOpenFlags) {
1006 const iBool postCommands = (menuOpenFlags & postCommands_MenuOpenFlags) != 0; 1018 const iBool postCommands = (menuOpenFlags & postCommands_MenuOpenFlags) != 0;
1007#if defined (iHaveNativeContextMenus) 1019#if defined (iHaveNativeContextMenus)
@@ -1263,8 +1275,8 @@ void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *s
1263 iMenuItem *item = findNativeMenuItem_Widget(menu, selectedCommand); 1275 iMenuItem *item = findNativeMenuItem_Widget(menu, selectedCommand);
1264 if (item) { 1276 if (item) {
1265 setSelected_NativeMenuItem(item, iTrue); 1277 setSelected_NativeMenuItem(item, iTrue);
1266 updateText_LabelWidget(dropButton, 1278 updateText_LabelWidget(
1267 removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label))); 1279 dropButton, removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label)));
1268 } 1280 }
1269 return; 1281 return;
1270 } 1282 }
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
249void setMenuItemDisabledByIndex_Widget(iWidget *menu, size_t index, iBool disable); 249void setMenuItemDisabledByIndex_Widget(iWidget *menu, size_t index, iBool disable);
250void setMenuItemLabel_Widget (iWidget *menu, const char *command, const char *newLabel); 250void setMenuItemLabel_Widget (iWidget *menu, const char *command, const char *newLabel);
251void setMenuItemLabelByIndex_Widget (iWidget *menu, size_t index, const char *newLabel); 251void setMenuItemLabelByIndex_Widget (iWidget *menu, size_t index, const char *newLabel);
252void setNativeMenuItems_Widget (iWidget *, const iMenuItem *items, size_t n);
252 253
253int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */ 254int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */
254 255