jcs
/wikipedia
/amendments
/49
*: Make Wikipedia hostname configurable in a new settings window
Most of the settings dialog logic taken from Wallops
jcs made amendment 49 about 1 year ago
--- main.c Tue Aug 29 09:52:21 2023
+++ main.c Mon Oct 16 17:26:29 2023
@@ -22,10 +22,33 @@
#include "focusable.h"
#include "util.h"
+enum {
+ CONFIG_TYPE_STRING,
+ CONFIG_TYPE_SHORT,
+ CONFIG_TYPE_PASSWORD,
+ CONFIG_TYPE_BOOL
+};
+
+struct config_field {
+ char name[32];
+ short type;
+ unsigned short min;
+ unsigned short max;
+ short ditl_id;
+ short res_id;
+ char sdefault[64];
+ char password_storage[256];
+} config_fields[] = {
+ { "Hostname", CONFIG_TYPE_STRING, 1, 255,
+ SETTINGS_HOSTNAME_ID, STR_HOSTNAME_ID, DEFAULT_HOSTNAME },
+};
+
MenuHandle apple_menu, file_menu, edit_menu, view_menu;
bool quitting = false;
void handle_menu(long menu_id);
+void settings_apply_defaults(void);
+void settings_show(void);
int
main(void)
@@ -61,6 +84,8 @@ main(void)
menu_defaults();
DrawMenuBar();
+ settings_apply_defaults();
+
if (_TCPInit() != 0)
panic("Failed initializing MacTCP");
@@ -192,6 +217,9 @@ handle_menu(long menu_id)
break;
case FILE_MENU_ID:
switch (item) {
+ case FILE_MENU_SETTINGS_ID:
+ settings_show();
+ break;
case FILE_MENU_QUIT_ID:
if (focusables_quit())
quitting = true;
@@ -211,4 +239,212 @@ menu_defaults(void)
DisableItem(edit_menu, EDIT_MENU_COPY_ID);
DisableItem(edit_menu, EDIT_MENU_PASTE_ID);
DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
+}
+
+void
+settings_apply_defaults(void)
+{
+ Str255 txt;
+ StringHandle h;
+ DialogTHndl dlgh;
+ DialogPtr dlg;
+ Handle ihandle;
+ Rect irect;
+ size_t size, n, m;
+ short hit, itype, ret;
+
+ for (n = 0; n < nitems(config_fields); n++) {
+ struct config_field *cf = &config_fields[n];
+ long lval;
+ short sval;
+
+ if (cf->sdefault[0] == '\0')
+ continue;
+
+ h = GetString(cf->res_id);
+ if (h != NULL) {
+ HLock(h);
+ if ((*h)[0] > 0)
+ continue;
+
+ RmveResource(h);
+ ReleaseResource(h);
+ }
+
+ size = strlen(cf->sdefault);
+ h = (StringHandle)xNewHandle(size + 1);
+ HLock(h);
+ strlcpy(((char *)*h) + 1, cf->sdefault, size + 1);
+ (*h)[0] = size;
+ strlcpy((char *)txt, cf->name, sizeof(txt));
+ CtoPstr(txt);
+ AddResource(h, 'STR ', cf->res_id, txt);
+ ReleaseResource(h);
+ }
+}
+
+void
+settings_show(void)
+{
+ Str255 txt, hostname;
+ GrafPtr old_port;
+ StringHandle h;
+ DialogTHndl dlgh;
+ DialogPtr dlg;
+ Handle ihandle;
+ Rect irect;
+ size_t size, n, m;
+ short hit, itype, ret;
+
+ /* center dialog in screen */
+ dlgh = (DialogTHndl)xGetResource('DLOG', SETTINGS_DLOG_ID);
+ HLock(dlgh);
+ center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left,
+ (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top,
+ true, &(**dlgh).boundsRect);
+ HUnlock(dlgh);
+
+ if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL)
+ panic("Can't find connection DLOG %d", SETTINGS_DLOG_ID);
+
+ for (n = 0; n < nitems(config_fields); n++) {
+ struct config_field *cf = &config_fields[n];
+ short sval;
+
+ GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
+
+ if (cf->type == CONFIG_TYPE_PASSWORD) {
+ PasswordDialogFieldFilterSetup(cf->ditl_id,
+ cf->password_storage, sizeof(cf->password_storage));
+ }
+
+ if (cf->res_id && (h = GetString(cf->res_id))) {
+ HLock(h);
+ if (cf->type == CONFIG_TYPE_PASSWORD) {
+ size = (*h)[0];
+ if (size >= sizeof(cf->password_storage) - 1)
+ size = 0;
+ for (m = 0; m < size; m++)
+ cf->password_storage[m] = '•';
+ cf->password_storage[m] = '\0';
+ CtoPstr(cf->password_storage);
+ SetIText(ihandle, cf->password_storage);
+ memcpy(cf->password_storage, *h, size);
+ cf->password_storage[size] = '\0';
+ PtoCstr(cf->password_storage);
+ } else if (cf->type == CONFIG_TYPE_BOOL) {
+ SetCtlValue(ihandle, ((*h)[1] == '1'));
+ } else {
+ SetIText(ihandle, *h);
+ }
+ HUnlock(h);
+ ReleaseResource(h);
+ } else if (cf->type == CONFIG_TYPE_BOOL) {
+ SetCtlValue(ihandle, (cf->sdefault[0] == '1'));
+ } else if (cf->sdefault[0] != '\0') {
+ strlcpy((char *)&txt, cf->sdefault, sizeof(txt));
+ CtoPstr(txt);
+ SetIText(ihandle, txt);
+ }
+ }
+
+ ShowWindow(dlg);
+
+get_input:
+ /* outline ok button */
+ GetPort(&old_port);
+ SetPort(dlg);
+ GetDItem(dlg, ok, &itype, &ihandle, &irect);
+ PenSize(3, 3);
+ InsetRect(&irect, -4, -4);
+ FrameRoundRect(&irect, 16, 16);
+ PenNormal();
+
+ ModalDialog(PasswordDialogFieldFilter, &hit);
+ SetPort(old_port);
+
+ switch (hit) {
+ case -1:
+ case cancel:
+ DisposeDialog(dlg);
+ ReleaseResource(dlgh);
+ return;
+ case OK:
+ goto verify;
+ default:
+ GetDItem(dlg, hit, &itype, &ihandle, &irect);
+ if (itype == (ctrlItem + chkCtrl))
+ /* flip checkboxes */
+ SetCtlValue(ihandle, 1 - GetCtlValue(ihandle));
+ goto get_input;
+ }
+
+verify:
+ for (n = 0; n < nitems(config_fields); n++) {
+ struct config_field *cf = &config_fields[n];
+ long lval;
+ short sval;
+
+ GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
+
+ if (cf->type == CONFIG_TYPE_PASSWORD) {
+ memcpy((char *)&txt, cf->password_storage, sizeof(txt));
+ } else if (cf->type == CONFIG_TYPE_BOOL) {
+ snprintf((char *)&txt, sizeof(txt), "%d",
+ GetCtlValue(ihandle));
+ } else {
+ GetIText(ihandle, txt);
+ PtoCstr(txt);
+ }
+
+ switch (cf->type) {
+ case CONFIG_TYPE_STRING:
+ case CONFIG_TYPE_PASSWORD:
+ if (cf->min && strlen((char *)txt) < cf->min) {
+ warn("%s is too short (minimum %d)", cf->name, cf->min);
+ goto get_input;
+ }
+ if (cf->max && strlen((char *)txt) > cf->max) {
+ warn("%s is too long (maximum %d)", cf->name, cf->max);
+ goto get_input;
+ }
+ break;
+ case CONFIG_TYPE_SHORT:
+ lval = atol((char *)txt);
+ if (lval < cf->min) {
+ warn("%s must be at least %d", cf->name, cf->min);
+ goto get_input;
+ }
+ if (lval > cf->max) {
+ warn("%s must be less than %d", cf->name, cf->max);
+ goto get_input;
+ }
+ break;
+ case CONFIG_TYPE_BOOL:
+ break;
+ }
+
+ switch (cf->ditl_id) {
+ case SETTINGS_HOSTNAME_ID:
+ strlcpy((char *)&hostname, (char *)txt, sizeof(hostname));
+ break;
+ }
+
+ if ((h = GetString(cf->res_id)) != NULL) {
+ RmveResource(h);
+ ReleaseResource(h);
+ }
+ size = strlen((char *)txt) + 1;
+ h = (StringHandle)xNewHandle(size);
+ strlcpy((char *)*h, (char *)txt, size);
+ CtoPstr(*h);
+ strlcpy((char *)txt, cf->name, sizeof(txt));
+ CtoPstr(txt);
+ AddResource(h, 'STR ', cf->res_id, txt);
+ ReleaseResource(h);
+ }
+
+ PasswordDialogFieldFinish();
+ DisposeDialog(dlg);
+ ReleaseResource(dlgh);
}
--- wikipedia.π.r Wed Sep 7 17:20:29 2022
+++ wikipedia.π.r Mon Oct 16 17:35:12 2023
@@ -5,8 +5,10 @@ data 'MENU' (128) {
};
data 'MENU' (129) {
- $"0081 0000 0000 0000 0000 FFFF FFFF 0446" /* .Å.............F */
- $"696C 6504 5175 6974 0051 0000 00" /* ile.Quit.Q... */
+ $"0081 0000 0000 0000 0000 FFFF FFFB 0446" /* .Å.............F */
+ $"696C 650B 5365 7474 696E 6773 2E2E 2E00" /* ile.Settings.... */
+ $"0000 0001 2D00 0000 0004 5175 6974 0051" /* ....-.....Quit.Q */
+ $"0000 00" /* ... */
};
data 'MENU' (130) {
@@ -38,6 +40,15 @@ data 'DITL' (130, "ASK") {
$"A002 0001" /* †... */
};
+data 'DITL' (129, "SETTINGS_DITL") {
+ $"0003 0000 0000 003C 00E6 0050 0130 0402" /* .......<...P.0.. */
+ $"4F4B 0000 0000 003C 0064 0050 00AE 0406" /* OK.....<.d.P.Æ.. */
+ $"4361 6E63 656C 0000 0000 0014 008C 0024" /* Cancel.......å.$ */
+ $"0186 1000 0000 0000 000A 000A 002C 0083" /* .Ü...........,.É */
+ $"8813 5769 6B69 7065 6469 6120 486F 7374" /* à.Wikipedia Host */
+ $"6E61 6D65 3A6C" /* name:l */
+};
+
data 'vers' (1) {
$"0010 6000 0000 0330 2E31 2630 2E31 20A9" /* ..`....0.1&0.1 © */
$"2032 3032 322C 206A 6F73 6875 6120 7374" /* 2022, joshua st */
@@ -75,5 +86,11 @@ data 'WPDA' (0, "Owner resource") {
data 'FREF' (128) {
$"4150 504C 0000 7F" /* APPL... */
+};
+
+data 'DLOG' (129, "SETTINGS_DLOG") {
+ $"0052 003C 00AA 01D1 0004 0000 0100 0000" /* .R.<.™.—........ */
+ $"0000 0081 1143 6F6E 6E65 6374 2074 6F20" /* ...Å.Connect to */
+ $"5365 7276 6572" /* Server */
};
--- wikipedia.c Wed Aug 30 17:51:12 2023
+++ wikipedia.c Mon Oct 16 17:30:54 2023
@@ -23,16 +23,13 @@
#include "utf8.h"
#include "util.h"
-/* en.wikipedia.org doesn't support non-TLS :( */
-#define WIKIPEDIA_HOST "wikipedia.jcs.org"
-
struct wikipedia_request *
wikipedia_fetch_article(struct browser *browser, char *name)
{
static char url[256];
struct wikipedia_request *wpr;
short state;
- char *nencoded;
+ char *nencoded, *hostname;
unsigned char *uname;
progress("Fetching \"%s\"...", name);
@@ -58,11 +55,20 @@ wikipedia_fetch_article(struct browser *browser, char
xfree(&wpr);
return NULL;
}
+
+ hostname = xGetStringAsChar(STR_HOSTNAME_ID);
+ if (hostname == NULL) {
+ progress(NULL);
+ warn("No Wikipedia hostname set, check Settings");
+ xfree(&wpr);
+ return NULL;
+ }
snprintf(url, sizeof(url), "http://%s/w/api.php?action=query&"
"prop=revisions&rvslots=*&rvprop=size|content&"
- "format=xml&titles=%s", WIKIPEDIA_HOST, nencoded);
+ "format=xml&titles=%s", hostname, nencoded);
xfree(&nencoded);
+ xfree(&hostname);
wpr->http_request = http_get(url);
if (wpr->http_request == NULL) {
progress(NULL);
@@ -96,7 +102,7 @@ wikipedia_fetch_search_results(struct browser *browser
{
static char url[256];
struct http_request *req;
- char *qencoded;
+ char *qencoded, *hostname;
char **rets = NULL, **trets = NULL;
char *str = NULL, *nstr = NULL, c;
unsigned char *uquery;
@@ -121,10 +127,17 @@ wikipedia_fetch_search_results(struct browser *browser
if (qencoded == NULL)
return 0;
+ hostname = xGetStringAsChar(STR_HOSTNAME_ID);
+ if (hostname == NULL) {
+ warn("No Wikipedia hostname set, check Settings");
+ return 0;
+ }
+
len = snprintf(url, sizeof(url), "http://%s/w/api.php?"
"action=opensearch&format=xml&namespace=0&limit=10&"
- "redirects=return&search=%s", WIKIPEDIA_HOST, qencoded);
+ "redirects=return&search=%s", hostname, qencoded);
xfree(&qencoded);
+ xfree(&hostname);
if (len > sizeof(url))
return 0;
--- wikipedia.h Wed Aug 30 14:19:31 2023
+++ wikipedia.h Mon Oct 16 17:11:46 2023
@@ -20,24 +20,31 @@
#include "browser.h"
#include "http.h"
-#define PROGRAM_NAME "Wikipedia"
+#define PROGRAM_NAME "Wikipedia"
-#define MBAR_ID 128
+#define MBAR_ID 128
-#define APPLE_MENU_ID 128
-#define APPLE_MENU_ABOUT_ID 1
+#define SETTINGS_DLOG_ID 129
+#define SETTINGS_HOSTNAME_ID 3
-#define FILE_MENU_ID 129
-#define FILE_MENU_QUIT_ID 1
+#define STR_HOSTNAME_ID 1000
+#define DEFAULT_HOSTNAME "wikipedia.jcs.org"
-#define EDIT_MENU_ID 130
-#define EDIT_MENU_CUT_ID 1
-#define EDIT_MENU_COPY_ID 2
-#define EDIT_MENU_PASTE_ID 3
-#define EDIT_MENU_SELECT_ALL_ID 4
+#define APPLE_MENU_ID 128
+#define APPLE_MENU_ABOUT_ID 1
-#define VIEW_MENU_ID 131
-#define VIEW_MENU_DEBUG_ID 1
+#define FILE_MENU_ID 129
+#define FILE_MENU_SETTINGS_ID 1
+#define FILE_MENU_QUIT_ID 3
+
+#define EDIT_MENU_ID 130
+#define EDIT_MENU_CUT_ID 1
+#define EDIT_MENU_COPY_ID 2
+#define EDIT_MENU_PASTE_ID 3
+#define EDIT_MENU_SELECT_ALL_ID 4
+
+#define VIEW_MENU_ID 131
+#define VIEW_MENU_DEBUG_ID 1
extern MenuHandle file_menu, edit_menu, view_menu;