AmendHub

Download

jcs

/

wikipedia

/

main.c

 

(View History)

jcs   main+browser: Make cmd+click on links open them in a new window Latest amendment: 54 on 2023-10-30

1 /*
2 * Copyright (c) 2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "wikipedia.h"
21 #include "browser.h"
22 #include "focusable.h"
23 #include "util.h"
24
25 enum {
26 CONFIG_TYPE_STRING,
27 CONFIG_TYPE_SHORT,
28 CONFIG_TYPE_PASSWORD,
29 CONFIG_TYPE_BOOL
30 };
31
32 struct config_field {
33 char name[32];
34 short type;
35 unsigned short min;
36 unsigned short max;
37 short ditl_id;
38 short res_id;
39 char sdefault[64];
40 char password_storage[256];
41 } config_fields[] = {
42 { "Hostname", CONFIG_TYPE_STRING, 1, 255,
43 SETTINGS_HOSTNAME_ID, STR_HOSTNAME_ID, DEFAULT_HOSTNAME },
44 };
45
46 MenuHandle apple_menu, file_menu, edit_menu, view_menu;
47 bool quitting = false;
48
49 void handle_menu(long menu_id);
50 void settings_apply_defaults(void);
51 void settings_show(void);
52
53 int
54 main(void)
55 {
56 Handle mbar;
57 EventRecord event;
58 WindowPtr event_win;
59 GrafPtr old_port;
60 struct focusable *focusable;
61 short event_in, n;
62 char key;
63
64 InitGraf(&thePort);
65 InitFonts();
66 FlushEvents(everyEvent, 0);
67 InitWindows();
68 InitMenus();
69 TEInit();
70 InitDialogs(0);
71 InitCursor();
72 MaxApplZone();
73
74 util_init();
75 _atexit(focusables_atexit);
76
77 mbar = GetNewMBar(MBAR_ID);
78 SetMenuBar(mbar);
79 apple_menu = GetMHandle(APPLE_MENU_ID);
80 AddResMenu(apple_menu, 'DRVR');
81 file_menu = GetMHandle(FILE_MENU_ID);
82 edit_menu = GetMHandle(EDIT_MENU_ID);
83 view_menu = GetMHandle(VIEW_MENU_ID);
84 menu_defaults();
85 DrawMenuBar();
86
87 settings_apply_defaults();
88
89 if (_TCPInit() != 0)
90 panic("Failed initializing MacTCP");
91
92 browser_init(NULL);
93
94 while (!quitting) {
95 WaitNextEvent(everyEvent, &event, 0L, 0L);
96
97 switch (event.what) {
98 case nullEvent:
99 for (n = 0; n < nfocusables; n++) {
100 if (focusables[n]->idle)
101 focusables[n]->idle(focusables[n], &event);
102 }
103 break;
104 case keyDown:
105 case autoKey:
106 key = event.message & charCodeMask;
107 if ((event.modifiers & cmdKey) != 0)
108 handle_menu(MenuKey(key));
109 else if ((focusable = focusable_focused()) &&
110 focusable->key_down)
111 focusable->key_down(focusable, &event);
112 break;
113 case mouseDown:
114 event_in = FindWindow(event.where, &event_win);
115
116 switch (event_in) {
117 case inMenuBar:
118 handle_menu(MenuSelect(event.where));
119 break;
120 case inSysWindow:
121 SystemClick(&event, event_win);
122 break;
123 case inDrag:
124 if ((focusable = focusable_find(event_win)) != NULL) {
125 if (!focusable_show(focusable))
126 break;
127
128 DragWindow(event_win, event.where, &screenBits.bounds);
129 }
130 break;
131 case inGoAway:
132 if (TrackGoAway(event_win, event.where)) {
133 if ((focusable = focusable_find(event_win)) != NULL)
134 focusable_close(focusable);
135 }
136 break;
137 case inContent:
138 if ((focusable = focusable_find(event_win)) != NULL) {
139 if (!focusable_show(focusable))
140 break;
141 if (focusable->mouse_down)
142 focusable->mouse_down(focusable, &event);
143 }
144 break;
145 }
146 break;
147 case updateEvt:
148 event_win = (WindowPtr)event.message;
149 GetPort(&old_port);
150 SetPort(event_win);
151 BeginUpdate(event_win);
152 focusable = focusable_find(event_win);
153 if (focusable && focusable->update)
154 focusable->update(focusable, &event);
155 EndUpdate(event_win);
156 SetPort(old_port);
157 break;
158 case activateEvt:
159 break;
160 case app4Evt:
161 if (HiWord(event.message) & (1 << 8)) {
162 /* multifinder suspend/resume */
163 switch (event.message & (1 << 0)) {
164 case 0:
165 /* suspend */
166 for (n = 0; n < nfocusables; n++) {
167 if (focusables[n]->suspend)
168 focusables[n]->suspend(focusables[n], &event);
169 }
170 break;
171 case 1:
172 /* resume */
173 for (n = 0; n < nfocusables; n++) {
174 if (focusables[n]->resume)
175 focusables[n]->resume(focusables[n], &event);
176 }
177 break;
178 }
179 }
180 break;
181 }
182 }
183
184 return 0;
185 }
186
187 void
188 handle_menu(long menu_id)
189 {
190 struct focusable *focused;
191 short menu, item;
192
193 menu = HiWord(menu_id);
194 item = LoWord(menu_id);
195
196 if ((focused = focusable_focused()) && focused->menu &&
197 focused->menu(focused, menu, item))
198 goto handled;
199
200 switch (menu) {
201 case APPLE_MENU_ID:
202 switch (item) {
203 case APPLE_MENU_ABOUT_ID:
204 about(PROGRAM_NAME);
205 break;
206 default: {
207 Str255 da;
208 GrafPtr save_port;
209
210 GetItem(apple_menu, LoWord(menu_id), &da);
211 GetPort(&save_port);
212 OpenDeskAcc(da);
213 SetPort(save_port);
214 break;
215 }
216 }
217 break;
218 case FILE_MENU_ID:
219 switch (item) {
220 case FILE_MENU_NEW_ID:
221 browser_init(NULL);
222 break;
223 case FILE_MENU_SETTINGS_ID:
224 settings_show();
225 break;
226 case FILE_MENU_QUIT_ID:
227 if (focusables_quit())
228 quitting = true;
229 break;
230 }
231 break;
232 }
233
234 handled:
235 HiliteMenu(0);
236 }
237
238 void
239 menu_defaults(void)
240 {
241 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
242 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
243 DisableItem(edit_menu, EDIT_MENU_PASTE_ID);
244 DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
245 }
246
247 void
248 settings_apply_defaults(void)
249 {
250 Str255 txt;
251 StringHandle h;
252 DialogTHndl dlgh;
253 DialogPtr dlg;
254 Handle ihandle;
255 Rect irect;
256 size_t size, n, m;
257 short hit, itype, ret;
258
259 for (n = 0; n < nitems(config_fields); n++) {
260 struct config_field *cf = &config_fields[n];
261 long lval;
262 short sval;
263
264 if (cf->sdefault[0] == '\0')
265 continue;
266
267 h = GetString(cf->res_id);
268 if (h != NULL) {
269 HLock(h);
270 if ((*h)[0] > 0)
271 continue;
272
273 RmveResource(h);
274 ReleaseResource(h);
275 }
276
277 size = strlen(cf->sdefault);
278 h = (StringHandle)xNewHandle(size + 1);
279 HLock(h);
280 strlcpy(((char *)*h) + 1, cf->sdefault, size + 1);
281 (*h)[0] = size;
282 strlcpy((char *)txt, cf->name, sizeof(txt));
283 CtoPstr(txt);
284 AddResource(h, 'STR ', cf->res_id, txt);
285 ReleaseResource(h);
286 }
287 }
288
289 void
290 settings_show(void)
291 {
292 Str255 txt, hostname;
293 GrafPtr old_port;
294 StringHandle h;
295 DialogTHndl dlgh;
296 DialogPtr dlg;
297 Handle ihandle;
298 Rect irect;
299 size_t size, n, m;
300 short hit, itype, ret;
301
302 /* center dialog in screen */
303 dlgh = (DialogTHndl)xGetResource('DLOG', SETTINGS_DLOG_ID);
304 HLock(dlgh);
305 center_in_screen((**dlgh).boundsRect.right - (**dlgh).boundsRect.left,
306 (**dlgh).boundsRect.bottom - (**dlgh).boundsRect.top,
307 true, &(**dlgh).boundsRect);
308 HUnlock(dlgh);
309
310 if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL)
311 panic("Can't find connection DLOG %d", SETTINGS_DLOG_ID);
312
313 for (n = 0; n < nitems(config_fields); n++) {
314 struct config_field *cf = &config_fields[n];
315 short sval;
316
317 GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
318
319 if (cf->type == CONFIG_TYPE_PASSWORD) {
320 PasswordDialogFieldFilterSetup(cf->ditl_id,
321 cf->password_storage, sizeof(cf->password_storage));
322 }
323
324 if (cf->res_id && (h = GetString(cf->res_id))) {
325 HLock(h);
326 if (cf->type == CONFIG_TYPE_PASSWORD) {
327 size = (*h)[0];
328 if (size >= sizeof(cf->password_storage) - 1)
329 size = 0;
330 for (m = 0; m < size; m++)
331 cf->password_storage[m] = '•';
332 cf->password_storage[m] = '\0';
333 CtoPstr(cf->password_storage);
334 SetIText(ihandle, cf->password_storage);
335 memcpy(cf->password_storage, *h, size);
336 cf->password_storage[size] = '\0';
337 PtoCstr(cf->password_storage);
338 } else if (cf->type == CONFIG_TYPE_BOOL) {
339 SetCtlValue(ihandle, ((*h)[1] == '1'));
340 } else {
341 SetIText(ihandle, *h);
342 }
343 HUnlock(h);
344 ReleaseResource(h);
345 } else if (cf->type == CONFIG_TYPE_BOOL) {
346 SetCtlValue(ihandle, (cf->sdefault[0] == '1'));
347 } else if (cf->sdefault[0] != '\0') {
348 strlcpy((char *)&txt, cf->sdefault, sizeof(txt));
349 CtoPstr(txt);
350 SetIText(ihandle, txt);
351 }
352 }
353
354 ShowWindow(dlg);
355
356 get_input:
357 /* outline ok button */
358 GetPort(&old_port);
359 SetPort(dlg);
360 GetDItem(dlg, ok, &itype, &ihandle, &irect);
361 PenSize(3, 3);
362 InsetRect(&irect, -4, -4);
363 FrameRoundRect(&irect, 16, 16);
364 PenNormal();
365
366 ModalDialog(PasswordDialogFieldFilter, &hit);
367 SetPort(old_port);
368
369 switch (hit) {
370 case -1:
371 case cancel:
372 DisposeDialog(dlg);
373 ReleaseResource(dlgh);
374 return;
375 case OK:
376 goto verify;
377 default:
378 GetDItem(dlg, hit, &itype, &ihandle, &irect);
379 if (itype == (ctrlItem + chkCtrl))
380 /* flip checkboxes */
381 SetCtlValue(ihandle, 1 - GetCtlValue(ihandle));
382 goto get_input;
383 }
384
385 verify:
386 for (n = 0; n < nitems(config_fields); n++) {
387 struct config_field *cf = &config_fields[n];
388 long lval;
389 short sval;
390
391 GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect);
392
393 if (cf->type == CONFIG_TYPE_PASSWORD) {
394 memcpy((char *)&txt, cf->password_storage, sizeof(txt));
395 } else if (cf->type == CONFIG_TYPE_BOOL) {
396 snprintf((char *)&txt, sizeof(txt), "%d",
397 GetCtlValue(ihandle));
398 } else {
399 GetIText(ihandle, txt);
400 PtoCstr(txt);
401 }
402
403 switch (cf->type) {
404 case CONFIG_TYPE_STRING:
405 case CONFIG_TYPE_PASSWORD:
406 if (cf->min && strlen((char *)txt) < cf->min) {
407 warn("%s is too short (minimum %d)", cf->name, cf->min);
408 goto get_input;
409 }
410 if (cf->max && strlen((char *)txt) > cf->max) {
411 warn("%s is too long (maximum %d)", cf->name, cf->max);
412 goto get_input;
413 }
414 break;
415 case CONFIG_TYPE_SHORT:
416 lval = atol((char *)txt);
417 if (lval < cf->min) {
418 warn("%s must be at least %d", cf->name, cf->min);
419 goto get_input;
420 }
421 if (lval > cf->max) {
422 warn("%s must be less than %d", cf->name, cf->max);
423 goto get_input;
424 }
425 break;
426 case CONFIG_TYPE_BOOL:
427 break;
428 }
429
430 switch (cf->ditl_id) {
431 case SETTINGS_HOSTNAME_ID:
432 strlcpy((char *)&hostname, (char *)txt, sizeof(hostname));
433 break;
434 }
435
436 if ((h = GetString(cf->res_id)) != NULL) {
437 RmveResource(h);
438 ReleaseResource(h);
439 }
440 size = strlen((char *)txt) + 1;
441 h = (StringHandle)xNewHandle(size);
442 strlcpy((char *)*h, (char *)txt, size);
443 CtoPstr(*h);
444 strlcpy((char *)txt, cf->name, sizeof(txt));
445 CtoPstr(txt);
446 AddResource(h, 'STR ', cf->res_id, txt);
447 ReleaseResource(h);
448 }
449
450 PasswordDialogFieldFinish();
451 DisposeDialog(dlg);
452 ReleaseResource(dlgh);
453 }