AmendHub

Download

jcs

/

wikipedia

/

browser.c

 

(View History)

jcs   browser: Only xfree links if there are any Latest amendment: 37 on 2022-09-28

1 /*
2 * Copyright (c) 2021-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 <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "wikipedia.h"
22 #include "browser.h"
23 #include "focusable.h"
24 #include "http.h"
25 #include "util.h"
26
27 #define PADDING 10
28 #define BROWSER_FONT_SIZE 10
29 #define BROWSER_FONT geneva
30
31 bool browser_close(struct focusable *focusable);
32 void browser_idle(struct focusable *focusable, EventRecord *event);
33 void browser_update_menu(struct browser *browser);
34 void browser_update(struct focusable *focusable, EventRecord *event);
35 void browser_key_down(struct focusable *focusable, EventRecord *event);
36 void browser_mouse_down(struct focusable *focusable, EventRecord *event);
37 bool browser_handle_menu(struct focusable *focusable, short menu,
38 short item);
39 void browser_atexit(struct focusable *focusable);
40 bool browser_avoid_te_overflow(struct browser *browser, TEHandle te,
41 short line_height);
42 bool browser_debug_enabled(struct browser *browser);
43 void browser_live_search(struct browser *browser);
44 void browser_hide_search_results(struct browser *browser);
45
46 Pattern fill_pattern;
47
48 void
49 browser_idle(struct focusable *focusable, EventRecord *event)
50 {
51 struct browser *browser = (struct browser *)focusable->cookie;
52 size_t len;
53
54 switch (browser->state) {
55 case BROWSER_STATE_IDLE:
56 TEIdle(browser->input_te);
57
58 if (browser->last_input_for_search != 0 &&
59 (Ticks - browser->last_input_for_search > 30)) {
60 browser->last_input_for_search = 0;
61 browser_live_search(browser);
62 }
63 break;
64 case BROWSER_STATE_ARTICLE_GET: {
65 TERec *te;
66 char *input;
67
68 HLock(browser->input_te);
69 te = *(browser->input_te);
70 HLock(te->hText);
71 (*(te->hText))[te->teLength] = '\0';
72 input = xstrdup(*(te->hText), "browser te input");
73 HUnlock(te->hText);
74 HUnlock(browser->input_te);
75
76 SetCursor(*(GetCursor(watchCursor)));
77 browser->wpr = wikipedia_fetch_article(browser, input);
78 xfree(&input);
79 browser->state = BROWSER_STATE_ARTICLE_PROCESS;
80 break;
81 }
82 case BROWSER_STATE_ARTICLE_PROCESS:
83 if (browser->wpr == NULL) {
84 browser->state = BROWSER_STATE_IDLE;
85 break;
86 }
87
88 wikipedia_request_process(browser->wpr);
89
90 if (browser->wpr->state == WP_STATE_DONE)
91 browser->state = BROWSER_STATE_ARTICLE_DONE;
92 else if (browser->wpr->state == WP_STATE_HAVE_REDIRECT) {
93 progress("Following redirect to %s...",
94 browser->wpr->normalized_title);
95 TESetText(browser->wpr->normalized_title,
96 strlen(browser->wpr->normalized_title), browser->input_te);
97 HLock(browser->input_te);
98 InvalRect(&(*(browser->input_te))->viewRect);
99 HUnlock(browser->input_te);
100 browser->state = BROWSER_STATE_ARTICLE_GET;
101 xfree(&browser->wpr);
102 }
103
104 break;
105 case BROWSER_STATE_ARTICLE_DONE:
106 UpdateScrollbarForTE(browser->win, browser->te_scroller,
107 browser->te, false);
108 progress(NULL);
109 SetCursor(&arrow);
110 browser->state = BROWSER_STATE_IDLE;
111 break;
112 }
113 }
114
115 struct browser *
116 browser_init(void)
117 {
118 char title[256];
119 struct browser *browser;
120 struct focusable *focusable;
121 Rect bounds = { 0 }, te_bounds = { 0 };
122 Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */
123 Point cell_size = { 0, 0 };
124 Cell cell = { 0 };
125 short n, width, height;
126
127 browser = xmalloczero(sizeof(struct browser), "browser");
128 browser->state = BROWSER_STATE_IDLE;
129
130 GetIndPattern(&fill_pattern, sysPatListID, 22);
131
132 /* main window */
133 width = screenBits.bounds.right - screenBits.bounds.left - PADDING;
134 if (width > 540)
135 width = 540;
136 height = screenBits.bounds.bottom - screenBits.bounds.top -
137 PADDING - (GetMBarHeight() * 2);
138 if (height > 340)
139 height = 340;
140 center_in_screen(width, height, true, &bounds);
141
142 snprintf(title, sizeof(title), "%s", PROGRAM_NAME);
143 CtoPstr(title);
144 browser->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
145 (WindowPtr)-1L, true, 0);
146 if (!browser->win)
147 err(1, "Can't create window");
148 SetPort(browser->win);
149
150 /* search input TE */
151 bounds.top = PADDING;
152 bounds.left = PADDING;
153 bounds.right = browser->win->portRect.right - PADDING;
154 bounds.bottom = bounds.top + 16;
155 te_bounds = bounds;
156 InsetRect(&te_bounds, 2, 2);
157 TextFont(geneva);
158 TextSize(10);
159 browser->input_te = TENew(&te_bounds, &bounds);
160 TEAutoView(true, browser->input_te);
161 TEActivate(browser->input_te);
162
163 /* main article TE bounds */
164 browser->te_bounds.top = (*(browser->input_te))->viewRect.bottom +
165 PADDING;
166 browser->te_bounds.left = PADDING;
167 browser->te_bounds.right = browser->win->portRect.right -
168 SCROLLBAR_WIDTH - PADDING;
169 browser->te_bounds.bottom = browser->win->portRect.bottom - PADDING;
170
171 /* debug TE, off-screen until enabled */
172 bounds = browser->te_bounds;
173 bounds.top += browser->win->portRect.bottom;
174 bounds.bottom += browser->win->portRect.bottom;
175 te_bounds = bounds;
176 InsetRect(&te_bounds, 2, 2);
177 browser->debug_te = TENew(&te_bounds, &bounds);
178 TEAutoView(false, browser->debug_te);
179 (*(browser->debug_te))->caretHook = NullCaretHook;
180 TEActivate(browser->debug_te);
181
182 /* main article TE */
183 bounds = browser->te_bounds;
184 te_bounds = bounds;
185 InsetRect(&te_bounds, 2, 2);
186 browser->te = TEStylNew(&te_bounds, &bounds);
187 TEAutoView(false, browser->te);
188 (*(browser->te))->caretHook = NullCaretHook;
189 TEActivate(browser->te);
190
191 /* scrollbar for diff text */
192 bounds.right = browser->win->portRect.right - PADDING;
193 bounds.left = bounds.right - SCROLLBAR_WIDTH;
194 bounds.bottom++;
195 bounds.top--;
196 browser->te_scroller = NewControl(browser->win, &bounds, "\p", true,
197 1, 1, 1, scrollBarProc, 0L);
198
199 browser_update_menu(browser);
200 UpdateScrollbarForTE(browser->win, browser->te_scroller,
201 browser->te, true);
202
203 focusable = xmalloczero(sizeof(struct focusable), "focusable");
204 focusable->cookie = browser;
205 focusable->win = browser->win;
206 focusable->idle = browser_idle;
207 focusable->update = browser_update;
208 focusable->mouse_down = browser_mouse_down;
209 focusable->key_down = browser_key_down;
210 focusable->menu = browser_handle_menu;
211 focusable->close = browser_close;
212 focusable->atexit = browser_atexit;
213 focusable_add(focusable);
214
215 return browser;
216 }
217
218 bool
219 browser_close(struct focusable *focusable)
220 {
221 struct browser *browser = (struct browser *)focusable->cookie;
222
223 if (browser->wpr) {
224 wikipedia_request_abort(browser->wpr);
225 browser->wpr = NULL;
226 }
227
228 TEDispose(browser->te);
229 DisposeWindow(browser->win);
230
231 xfree(&browser);
232
233 return true;
234 }
235
236 void
237 browser_atexit(struct focusable *focusable)
238 {
239 struct browser *browser = (struct browser *)focusable->cookie;
240
241 if (browser->wpr) {
242 wikipedia_request_abort(browser->wpr);
243 browser->wpr = NULL;
244 }
245 }
246
247 void
248 browser_update_menu(struct browser *browser)
249 {
250 size_t vlines;
251 TERec *te;
252 Cell cell = { 0, 0 };
253
254 TextFont(systemFont);
255 TextSize(12);
256
257 te = *(browser->te);
258
259 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
260
261 if (te->selStart == te->selEnd)
262 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
263 else
264 EnableItem(edit_menu, EDIT_MENU_COPY_ID);
265
266 DisableItem(edit_menu, EDIT_MENU_PASTE_ID);
267
268 if (te->nLines == 0) {
269 DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
270 DisableItem(edit_menu, VIEW_MENU_DEBUG_ID);
271 } else {
272 EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
273 EnableItem(edit_menu, VIEW_MENU_DEBUG_ID);
274 }
275 }
276
277 void
278 browser_update(struct focusable *focusable, EventRecord *event)
279 {
280 struct browser *browser = (struct browser *)focusable->cookie;
281 Str255 buf;
282 Rect r;
283 short what = -1;
284
285 if (event != NULL)
286 what = event->what;
287
288 switch (what) {
289 case -1:
290 case updateEvt:
291 FillRect(&browser->win->portRect, fill_pattern);
292
293 HLock(browser->input_te);
294 r = (*(browser->input_te))->viewRect;
295 HUnlock(browser->input_te);
296 FillRect(&r, white);
297 TEUpdate(&r, browser->input_te);
298 InsetRect(&r, -1, -1);
299 FrameRect(&r);
300
301 HLock(browser->te);
302 r = (*(browser->te))->viewRect;
303 HUnlock(browser->te);
304 FillRect(&r, white);
305 TEUpdate(&r, browser->te);
306 InsetRect(&r, -1, -1);
307 FrameRect(&r);
308
309 if (browser->search_results != NULL) {
310 HLock(browser->search_results);
311 r = (*(browser->search_results))->rView;
312 HUnlock(browser->search_results);
313 InsetRect(&r, -1, -1);
314 FillRect(&r, white);
315 FrameRect(&r);
316 LUpdate(browser->win->visRgn, browser->search_results);
317 }
318
319 UpdtControl(browser->win, browser->win->visRgn);
320
321 browser_update_menu(browser);
322
323 break;
324 }
325 }
326
327 void
328 browser_mouse_down(struct focusable *focusable, EventRecord *event)
329 {
330 struct browser *browser = (struct browser *)focusable->cookie;
331 char str[255];
332 Cell selected = { 0 };
333 Point p;
334 ControlHandle control;
335 Rect r;
336 short val, adj, page, len, part, off;
337 size_t n;
338
339 p = event->where;
340 GlobalToLocal(&p);
341
342 HLock(browser->input_te);
343 r = (*(browser->input_te))->viewRect;
344 HUnlock(browser->input_te);
345 if (PtInRect(p, &r)) {
346 TEClick(p, ((event->modifiers & shiftKey) != 0), browser->input_te);
347 browser_update_menu(browser);
348 return;
349 }
350
351 if (browser->search_results != NULL) {
352 HLock(browser->search_results);
353 r = (*(browser->search_results))->rView;
354 HUnlock(browser->search_results);
355 r.right += SCROLLBAR_WIDTH;
356 if (PtInRect(p, &r)) {
357 LClick(p, event->modifiers, browser->search_results);
358 selected.v = 0;
359 if (LGetSelect(true, &selected, browser->search_results)) {
360 len = sizeof(str);
361 LGetCell(&str, &len, selected, browser->search_results);
362 TESetText(str, len, browser->input_te);
363 HLock(browser->input_te);
364 InvalRect(&(*(browser->input_te))->viewRect);
365 HUnlock(browser->input_te);
366 browser_hide_search_results(browser);
367 browser->state = BROWSER_STATE_ARTICLE_GET;
368 }
369
370 return;
371 }
372 }
373
374 if (browser_debug_enabled(browser)) {
375 HLock(browser->debug_te);
376 r = (*(browser->debug_te))->viewRect;
377 HUnlock(browser->debug_te);
378 if (PtInRect(p, &r)) {
379 TEClick(p, ((event->modifiers & shiftKey) != 0), browser->debug_te);
380 browser_update_menu(browser);
381 return;
382 }
383 } else {
384 HLock(browser->te);
385 r = (*(browser->te))->viewRect;
386 HUnlock(browser->te);
387 if (PtInRect(p, &r)) {
388 TEClick(p, ((event->modifiers & shiftKey) != 0), browser->te);
389
390 off = TEGetOffset(p, browser->te);
391 for (n = 0; n < browser->links_count; n++) {
392 struct browser_link *link = &browser->links[n];
393
394 if ((link->pos <= off) && (off < link->pos + link->len)) {
395 TESetText(link->link, strlen(link->link), browser->input_te);
396 HLock(browser->input_te);
397 InvalRect(&(*(browser->input_te))->viewRect);
398 HUnlock(browser->input_te);
399 browser->state = BROWSER_STATE_ARTICLE_GET;
400 break;
401 }
402 }
403
404 browser_update_menu(browser);
405 return;
406 }
407 }
408
409 switch (part = FindControl(p, browser->win, &control)) {
410 case inButton:
411 break;
412 case inUpButton:
413 case inDownButton:
414 case inPageUp:
415 case inPageDown:
416 if (control == browser->te_scroller) {
417 if (browser_debug_enabled(browser))
418 SetTrackControlTE(browser->debug_te);
419 else
420 SetTrackControlTE(browser->te);
421 } else
422 break;
423 TrackControl(control, p, TrackMouseDownInControl);
424 break;
425 case inThumb:
426 val = GetCtlValue(control);
427 if (TrackControl(control, p, 0L) == 0)
428 break;
429 adj = val - GetCtlValue(control);
430 if (adj != 0) {
431 val -= adj;
432 if (control == browser->te_scroller) {
433 if (browser_debug_enabled(browser))
434 TEScroll(0, adj * TEGetHeight(0, 0,
435 browser->debug_te), browser->debug_te);
436 else
437 TEScroll(0, adj * TEGetHeight(0, 0,
438 browser->te), browser->te);
439 }
440 SetCtlValue(control, val);
441 }
442 break;
443 }
444 }
445
446 void
447 browser_key_down(struct focusable *focusable, EventRecord *event)
448 {
449 struct browser *browser = (struct browser *)(focusable->cookie);
450 char k;
451
452 k = (event->message & charCodeMask);
453 if (k == '\r') {
454 browser->state = BROWSER_STATE_ARTICLE_GET;
455 } else {
456 TEKey(k, browser->input_te);
457 TESelView(browser->input_te);
458 browser->last_input_for_search = Ticks;
459 }
460 }
461
462 void
463 browser_live_search(struct browser *browser)
464 {
465 TERec *te;
466 Rect bounds = { 0 };
467 Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */
468 char *input, **results, k;
469 size_t nresults, n;
470 Point cell_size = { 0, 0 };
471 Cell cell = { 0, 0 };
472 Rect r;
473
474 HLock(browser->input_te);
475 te = *(browser->input_te);
476
477 if (te->teLength == 0) {
478 HUnlock(browser->input_te);
479 browser_hide_search_results(browser);
480 return;
481 }
482
483 SetCursor(*(GetCursor(watchCursor)));
484
485 HLock(te->hText);
486 (*(te->hText))[te->teLength] = '\0';
487 input = xstrdup(*(te->hText), "browser te input");
488 HUnlock(te->hText);
489 HUnlock(browser->input_te);
490 nresults = wikipedia_fetch_search_results(browser, input, &results);
491 xfree(&input);
492
493 if (browser->search_results == NULL) {
494 bounds.top = (*(browser->input_te))->viewRect.bottom + 1;
495 bounds.left = PADDING;
496 bounds.bottom = bounds.top + 70;
497 bounds.right = bounds.left +
498 (((*(browser->input_te))->viewRect.right -
499 (*(browser->input_te))->viewRect.left) / 2);
500
501 browser->search_results = LNew(&bounds, &data_bounds, cell_size, 0,
502 browser->win, false, false, false, true);
503 if (!browser->search_results)
504 panic("LNew failed");
505 (*(browser->search_results))->selFlags = lOnlyOne;
506 LAddColumn(1, 0, browser->search_results);
507 } else {
508 LDelRow(0, 0, browser->search_results);
509 }
510
511 for (n = 0; n < nresults; n++) {
512 size_t len;
513 len = strlen(results[n]);
514 LAddRow(1, cell.v, browser->search_results);
515 LSetCell(results[n], len, cell,
516 browser->search_results);
517 cell.v++;
518 xfree(&results[n]);
519 }
520
521 r = (*(browser->search_results))->rView;
522 FillRect(&r, white);
523 InsetRect(&r, -1, -1);
524 FrameRect(&r);
525 LDoDraw(true, browser->search_results);
526 LUpdate(browser->win->visRgn, browser->search_results);
527
528 SetCursor(&arrow);
529 }
530
531 void
532 browser_hide_search_results(struct browser *browser)
533 {
534 Rect r;
535
536 if (browser->search_results == NULL)
537 return;
538
539 r = (*(browser->search_results))->rView;
540 r.right += SCROLLBAR_WIDTH;
541 InsetRect(&r, -1, -1);
542 InvalRect(&r);
543
544 LDispose(browser->search_results);
545 browser->search_results = NULL;
546 }
547
548 bool
549 browser_handle_menu(struct focusable *focusable, short menu, short item)
550 {
551 struct browser *browser = (struct browser *)focusable->cookie;
552
553 switch (menu) {
554 case EDIT_MENU_ID:
555 switch (item) {
556 case EDIT_MENU_COPY_ID:
557 if (browser_debug_enabled(browser))
558 TECopy(browser->debug_te);
559 else
560 TECopy(browser->te);
561 return true;
562 case EDIT_MENU_SELECT_ALL_ID:
563 if (browser_debug_enabled(browser))
564 TESetSelect(0, 1024 * 32, browser->debug_te);
565 else
566 TESetSelect(0, 1024 * 32, browser->te);
567 return true;
568 }
569 break;
570 case VIEW_MENU_ID:
571 switch (item) {
572 case VIEW_MENU_DEBUG_ID: {
573 Rect bounds, te_bounds;
574
575 bounds = browser->te_bounds;
576 te_bounds = bounds;
577 InsetRect(&te_bounds, 2, 2);
578
579 HLock(browser->debug_te);
580 HLock(browser->te);
581
582 SetCtlValue(browser->te_scroller, GetCtlMin(browser->te_scroller));
583
584 if (browser_debug_enabled(browser)) {
585 /* disable debugging */
586 SetItemMark(view_menu, VIEW_MENU_DEBUG_ID, noMark);
587
588 (*(browser->debug_te))->destRect = (*(browser->te))->destRect;
589 (*(browser->debug_te))->viewRect = (*(browser->te))->viewRect;
590 (*(browser->te))->destRect = te_bounds;
591 (*(browser->te))->viewRect = bounds;
592
593 EraseRect(&(*(browser->te))->destRect);
594 TEUpdate(&(*(browser->te))->destRect, browser->te);
595 UpdateScrollbarForTE(browser->win, browser->te_scroller,
596 browser->te, false);
597 } else {
598 /* enable debugging */
599 SetItemMark(view_menu, VIEW_MENU_DEBUG_ID, checkMark);
600
601 (*(browser->te))->destRect = (*(browser->debug_te))->destRect;
602 (*(browser->te))->viewRect = (*(browser->debug_te))->viewRect;
603 (*(browser->debug_te))->destRect = te_bounds;
604 (*(browser->debug_te))->viewRect = bounds;
605
606 EraseRect(&(*(browser->debug_te))->destRect);
607 TEUpdate(&(*(browser->debug_te))->destRect, browser->debug_te);
608 UpdateScrollbarForTE(browser->win, browser->te_scroller,
609 browser->debug_te, false);
610 }
611
612 HUnlock(browser->debug_te);
613 HLock(browser->te);
614 break;
615 }
616 }
617 break;
618 }
619
620 return false;
621 }
622
623 bool
624 browser_debug_enabled(struct browser *browser)
625 {
626 short mark;
627
628 GetItemMark(view_menu, VIEW_MENU_DEBUG_ID, &mark);
629
630 return (mark != noMark);
631 }
632
633 size_t
634 browser_debug_print(struct browser *browser, const char *str,
635 size_t len)
636 {
637 char tstr[1024];
638 short line_height;
639 short was_len;
640 size_t n = 0;
641
642 line_height = BROWSER_FONT_SIZE + 3;
643
644 HLock(browser->debug_te);
645 was_len = (*(browser->debug_te))->teLength;
646 HUnlock(browser->debug_te);
647
648 browser_avoid_te_overflow(browser, browser->debug_te, line_height);
649
650 while (len) {
651 if (*str == '\n')
652 tstr[n++] = '\r';
653 else
654 tstr[n++] = *str;
655
656 str++;
657 len--;
658
659 if (n == sizeof(tstr) || len == 0) {
660 TESetSelect(SHRT_MAX, SHRT_MAX, browser->debug_te);
661 TEInsert(tstr, n, browser->debug_te);
662 if (len == 0)
663 break;
664 n = 0;
665 }
666 }
667
668 if (was_len == 0) {
669 SetCtlValue(browser->te_scroller, GetCtlMin(browser->te_scroller));
670 UpdateScrollbarForTE(browser->win, browser->te_scroller,
671 browser->debug_te, false);
672 }
673
674 HUnlock(browser->debug_te);
675
676 return len;
677 }
678
679 bool
680 browser_avoid_te_overflow(struct browser *browser, TEHandle te,
681 short line_height)
682 {
683 RgnHandle savergn;
684 Rect zerorect = { 0, 0, 0, 0 };
685
686 HLock(te);
687
688 /* too many lines */
689 if ((*te)->nLines >= (nitems((*te)->lineStarts) - 10))
690 goto te_overflow;
691
692 /* too many characters */
693 if ((*te)->teLength >= (SHRT_MAX - 500))
694 goto te_overflow;
695
696 /* rect of all lines is too tall */
697 if ((*te)->nLines * line_height >= (SHRT_MAX - 100))
698 goto te_overflow;
699
700 HUnlock(te);
701
702 return false;
703
704 te_overflow:
705 savergn = NewRgn();
706 GetClip(savergn);
707 /* create an empty clip region so all TE updates are hidden */
708 ClipRect(&zerorect);
709
710 /* select some lines at the start, delete them */
711 TESetSelect(0, (*te)->lineStarts[5], te);
712 TEDelete(te);
713
714 /* scroll up, causing a repaint */
715 TEPinScroll(0, INT_MAX, te);
716
717 /* then scroll back down to what it looked like before we did anything */
718 TEPinScroll(0, -INT_MAX, te);
719
720 /* resume normal drawing */
721 SetClip(savergn);
722 DisposeRgn(savergn);
723
724 HUnlock(te);
725
726 return true;
727 }
728
729 #define BROWSER_SCRAP_ELEMENTS 20
730 static Handle scrp_rec_h = NULL;
731
732 size_t
733 browser_print(struct browser *browser, const char *str, size_t len,
734 unsigned long style)
735 {
736 StScrpRec *scrp_rec;
737 ScrpSTElement *scrp_ele;
738 RgnHandle savergn;
739 Rect zerorect = { 0, 0, 0, 0 };
740 size_t n;
741 short line_height = 0, was_len = 0;
742 static unsigned long last_style = 0;
743 struct browser_link *link = NULL;
744
745 if (scrp_rec_h == NULL) {
746 scrp_rec_h = xNewHandle(sizeof(short) +
747 (sizeof(ScrpSTElement) * BROWSER_SCRAP_ELEMENTS));
748 HLock(scrp_rec_h);
749 memset(*scrp_rec_h, 0, GetHandleSize(scrp_rec_h));
750 } else {
751 HLock(scrp_rec_h);
752 }
753
754 line_height = BROWSER_FONT_SIZE + 3;
755
756 scrp_rec = (StScrpRec *)(*scrp_rec_h);
757 scrp_rec->scrpNStyles = 1;
758 scrp_ele = &scrp_rec->scrpStyleTab[0];
759 scrp_ele->scrpHeight = line_height;
760 scrp_ele->scrpAscent = BROWSER_FONT_SIZE;
761 scrp_ele->scrpFont = BROWSER_FONT;
762 scrp_ele->scrpSize = BROWSER_FONT_SIZE;
763 scrp_ele->scrpFace = 0;
764
765 if (style & STYLE_BOLD)
766 scrp_ele->scrpFace |= bold | condense;
767 if (style & (STYLE_H1 | STYLE_H2 | STYLE_H3 | STYLE_H4 | STYLE_H5))
768 scrp_ele->scrpFace |= bold;
769 if (style & STYLE_ITALIC)
770 scrp_ele->scrpFace |= italic;
771 if (style & STYLE_LINK)
772 scrp_ele->scrpFace |= underline;
773 if (style & STYLE_H1) {
774 scrp_ele->scrpSize += 8;
775 scrp_ele->scrpHeight += 10;
776 scrp_ele->scrpAscent += 8;
777 } else if (style & STYLE_H2) {
778 scrp_ele->scrpSize += 4;
779 scrp_ele->scrpHeight += 6;
780 scrp_ele->scrpAscent += 4;
781 } else if (style & STYLE_H3) {
782 scrp_ele->scrpSize += 2;
783 scrp_ele->scrpHeight += 4;
784 scrp_ele->scrpAscent += 2;
785 }
786
787 if (style & STYLE_LINK) {
788 if (browser->links_count == browser->links_size) {
789 browser->links_size += 256;
790 browser->links = xreallocarray(browser->links,
791 browser->links_size, sizeof(struct browser_link));
792 memset(&browser->links[browser->links_count], 0,
793 sizeof(struct browser_link) * 256);
794 }
795
796 link = &browser->links[browser->links_count++];
797
798 /* Computer Storage|Storage -> Computer Storage */
799 /* Atari 2600 -> Atari 2600 */
800 for (n = 0; n <= len; n++) {
801 if (n == len) {
802 link->link = xstrndup(str, n, "link");
803 break;
804 }
805
806 if (str[n] == '|') {
807 link->link = xstrndup(str, n, "link");
808 str += n + 1;
809 len -= n + 1;
810 break;
811 }
812 }
813
814 link->len = len;
815 }
816
817 HUnlock(scrp_rec_h);
818
819 browser_avoid_te_overflow(browser, browser->te, line_height);
820
821 HLock(browser->te);
822 was_len = (*(browser->te))->teLength;
823
824 if (style & STYLE_LINK)
825 link->pos = was_len;
826
827 TESetSelect(SHRT_MAX, SHRT_MAX, browser->te);
828 if ((last_style & STYLE_ITALIC) && !(style & STYLE_ITALIC))
829 TEStylInsert(" ", 1, scrp_rec_h, browser->te);
830
831 if (style & (STYLE_H1 | STYLE_H2 | STYLE_H3 | STYLE_H4 | STYLE_H5)) {
832 while (len && (str[0] == ' ' || str[0] == '\r')) {
833 str++;
834 len--;
835 }
836 while (len && (str[len - 1] == ' ' || str[len - 1] == '\r')) {
837 len--;
838 }
839 }
840
841 TEStylInsert(str, len, scrp_rec_h, browser->te);
842
843 if (style & (STYLE_H1 | STYLE_H2))
844 browser_draw_line(browser);
845 else if (style & (STYLE_H3 | STYLE_H4 | STYLE_H5))
846 TEStylInsert("\r", 1, scrp_rec_h, browser->te);
847
848 if (was_len == 0) {
849 SetCtlValue(browser->te_scroller, GetCtlMin(browser->te_scroller));
850 UpdateScrollbarForTE(browser->win, browser->te_scroller,
851 browser->te, false);
852 }
853
854 HUnlock(browser->te);
855
856 last_style = style;
857
858 return len;
859 }
860
861 void
862 browser_clear(struct browser *browser)
863 {
864 size_t n;
865
866 for (n = 0; n < browser->links_count; n++)
867 xfree(&browser->links[n].link);
868
869 if (browser->links != NULL)
870 xfree(&browser->links);
871 browser->links_count = 0;
872 browser->links_size = 0;
873
874 TEPinScroll(0, -SHRT_MAX, browser->debug_te);
875 TESetText("", 0, browser->te);
876 TESetText("", 0, browser->debug_te);
877
878 UpdateScrollbarForTE(browser->win, browser->te_scroller,
879 browser->te, true);
880 }
881
882 void
883 browser_draw_line(struct browser *browser)
884 {
885 char line[255];
886 size_t n, lsize;
887 short curfont, cursize, cwidth;
888 unsigned char emd = '—'; /* em-dash 0xd1 */
889
890 curfont = thePort->txFont;
891 cursize = thePort->txSize;
892 TextFont(BROWSER_FONT);
893 TextSize(BROWSER_FONT_SIZE);
894 cwidth = CharWidth(emd);
895 TextFont(curfont);
896 TextSize(cursize);
897
898 HLock(browser->te);
899 lsize = (*(browser->te))->viewRect.right - (*(browser->te))->viewRect.left;
900 HUnlock(browser->te);
901
902 lsize /= cwidth;
903 if (lsize > sizeof(line))
904 lsize = sizeof(line);
905
906 line[0] = '\r';
907 for (n = 1; n < lsize - 1; n++)
908 line[n] = emd;
909 line[lsize - 1] = '\r';
910 browser_print(browser, line, lsize, STYLE_BOLD);
911 }