Outputting text with word-wrap
by Richard Russell, August 2009
BBC BASIC (by default) automatically 'wraps' text that is output to the screen or the printer. If a character will not fit on the current line (or within the current text viewport) a 'new line' is generated and the character is displayed or printed at the beginning of the next line. The text viewport scrolls, or a new page is ejected from the printer, if necessary.
Although this ensures that all text output is visible, it does often result in a word being split between two lines. It would usually be more desirable to perform a word wrap, that is if a character will not fit the entire word containing that character is moved to the next line. This is the kind of behaviour that a word processor usually has.
The routines listed below implement word-wrap for text output to the screen or the printer. There are four procedures: PROCww outputs a string to the screen, PROCvww outputs a single character to the screen, PROCwwp outputs a string to the printer and PROCvwwp outputs a single character to the printer:
PROCww(A$) : REM Equivalent to PRINT A$; PROCvww(C%) : REM Equivalent to VDU C% PROCwwp(A$) : REM Equivalent to OSCLI "OUTPUT 15" : PRINT A$; : OSCLI "OUTPUT 0" PROCvwwp(C%) : REM Equivalent to VDU 2,1,C%,3
Note that PROCww and PROCwwp do not output a newline after the string; to do that you must concatenate CHR$13+CHR$10 to the end of the string (or output them separately using PROCvww or PROCvwwp).
Here are the routines necessary to implement word-wrap:
DEF PROCww(A$) LOCAL I% IF A$ = "" THEN ENDPROC FOR I% = 1 TO LEN(A$) PROCvww(ASCMID$(A$,I%)) NEXT ENDPROC DEF PROCvww(C%) PRIVATE X%, Y%, B$ VDU C% IF C% > 32 B$ += CHR$C% ELSE B$ = "" : X% = @vdu.c.x% : Y% = @vdu.c.y% IF X% > @vdu.c.x% IF B$ <> "" THEN LOCAL @vdu%!216 : @vdu%!216 = 1 IF Y% = @vdu.c.y% Y% -= @vdu%!220 REPEAT @vdu.c.x% = X% : @vdu.c.y% = Y% VDU 32 : X% += 1 UNTIL X% > @vdu.tr% PRINT B$; X% = -1 ENDIF ENDPROC DEF PROCwwp(A$) LOCAL I% IF A$ = "" THEN ENDPROC FOR I% = 1 TO LEN(A$) PROCvwwp(ASCMID$(A$,I%)) NEXT ENDPROC DEF PROCvwwp(C%) PRIVATE X%, B$ LOCAL M%, ?444 : ?444 = 64 M% = @vdu%!-12 VDU 2,1,C%,3 IF C% > 32 B$ += CHR$C% ELSE B$ = "" : X% = @vdu%!-12 IF @vdu%!-12 > @vdu%!236 IF B$ <> "" THEN IF X% < 0 X% = M% : B$ = RIGHT$(B$) SWAP @vdu%!-12, X% SYS "SetBkMode", @prthdc%, 2 REPEAT VDU 2,1,32,3 UNTIL @vdu%!-12 > X% SYS "SetBkMode", @prthdc%, 1 ?444 = 0 *OUTPUT 15 PRINT 'B$; *OUTPUT 0 X% = -1 ENDIF ENDPROC
Note that the usual restrictions caused by the use of PRIVATE apply. You must not attempt to resume execution if an error occurs (even an ESCape error) within the PROCvww or PROCvwwp routines, unless you execute a RESTORE LOCAL as part of your error handler. Ensure that any ON ERROR handler in your program aborts execution in such a case.