7 February 1989 Technical Discussion of the BALCOL Macro (Version 1.01) Rufus S. Hendon (CompuServe ID 73250,2674) This memo contains annotated copies of the two files that make up the BALCOL macro: BALCOL.WPM and BALCOL1.WPM. The annotations are enclosed between brackets. I was surprised to discover that there are limits on the size of a macro file, with comments counting toward the limit. BALCOL, even with comments eliminated, was too large, so I had to split it into two files. I have found that the behavior of BALCOL is sensitive to {DISPLAY ON} and {DISPLAY OFF} settings. Anyone who wants to tinker with the macro should be aware that altering what is displayed and what isn't displayed may produce peculiar or disastrous results. The macro uses the characters [11,98] and [11,99] as temporary markers. A note as to why I chose these particular characters may be useful. Since these markers are inserted into the text that is to be reformatted into two columns, it is necessary to choose characters that will not alter the wrapping of lines. The characters must therefore be ones that are unlikely to be defined in the character maps and proportional-space tables used by the printer driver to format the text in the base font that is in effect. When WordPerfect finds that a character is undefined and therefore unprintable, it treats it as occupying zero space for purposes of word-wrapping and justification. I therefore selected the two final characters in character set 11, which is the Japanese katakana set. In the unlikely event that anyone is going to use BALCOL on text that employs character set 11, the macro should be modified to use two undefined characters from some other character set. [BALCOL.WPM ==================================================================] [This is the main part of the macro, with which execution begins.] {;} BALCOL.WPM 1.01 (2/7/89, Rufus S. Hendon)~ {;} Needs BALCOL1.WPM.~ [Set VAR 5 to true if Typeover is in effect, to false otherwise. If Typeover is in effect, switch to Insert.] {IF}{STATE}&256~{ASSIGN}5~-1~{Typeover}{ELSE}{ASSIGN}5~0~{END IF} [Turn side-by-side display of columns off.] {DISPLAY OFF} {Setup}38n{Enter}{Enter} {DISPLAY ON} [Go to the beginning of the current line and insert character [11,98] to mark the beginning of the two-column section.] {Home}{Home}{Left}[11,98] {;}[11,98]~ [Prompt the user to move the cursor to the last line of the section and press Enter. When the user has done this, insert character [11,99] at the end of the line to mark the end of the section.] {PROMPT}{^R} Move cursor to last line of two-column text, then press Enter. {^S}~ {PAUSE} {End}[11,99] {;}[11,99]~ [Go back to beginning marker [11,98] and insert [Col On] ahead of it.] {ON NOT FOUND}{GO}nf~~ {ASSIGN}6~1~ {Search Left}[11,98]{Search}{Left}{Math/Columns}3 {;}[11,98]~ [Go forward to end marker [11,99] and insert [Col Off] after it.] {ASSIGN}6~2~ {Search}[11,99]{Search}{Math/Columns}3 {;}[11,99]~ [Reposition the cursor to beginning marker [11,98] and invoke BALCOL1. (BALCOL1 will examine non-final pages of the two-column section for blank lines at the tops of columns. If such lines are found, the user will be given the opportunity to delete them.)] {ASSIGN}6~3~ {Search Left}[11,98]{Search}{Left}{NEST}balcol1~ {;}[11,98]~ [Locate beginning marker [11,98] and delete it.] {ASSIGN}6~4~ {Search Left}[11,98]{Search}{Backspace} {;}[11,98]~ [Position the cursor after end marker [11,99], thus ensuring that we are on the last or final page of the two-column section.] {ASSIGN}6~5~ {Search}[11,99]{Search} {;}[11,99]~ [Set VAR 9 to true to indicate that DELETE is to operate on the left column. Then call DELETE to give the user the chance to delete blank lines at the top of the column.] {ASSIGN}9~-1~{CALL}delete~ [Set VAR 9 to the number of lines of two-column text on the last or only page. (When side-by-side display is off, {Down} continues from the last line of the left column to the first line of the right column. It was in order to make it possible to use this method of going from left column to right column during the counting of lines that side-by-side display was turned off earlier.)] {ASSIGN}9~1~ {Goto}{Left}{Goto}{Up} {LABEL}count~ {End}{Left}{Block}{Right}{Macro Commands}a0 {IF}'{VAR 0}'='[11,99]'~{GO}onecol~{END IF} {;}[11,99]~ {ASSIGN}9~{VAR 9}+1~ {Down}{GO}count~ [Go to end marker [11,99] so that we're in the part of the two-column section that occupies the last or only page. Then go to the bottom line of the left column. If the last character of this line is end marker [11,99], there is only one column on the last or only page: set VAR 2 (the number of lines in the right column) to 0 and skip to HALVE.] {LABEL}onecol~ {Goto}{Goto} {Goto}{Left}{Goto}{Down} {End}{Left}{Block}{Right}{Macro Commands}a0 {IF}'{VAR 0}'='[11,99]'~{ASSIGN}2~0~{GO}halve~{END IF} {;}[11,99]~ [Otherwise count the lines in the right column and set VAR 2 to the result.] {ASSIGN}2~1~ {Goto}{Right}{Goto}{Up} {LABEL}rcount~ {End}{Left}{Block}{Right}{Macro Commands}a0 {IF}'{VAR 0}'='[11,99]'~{GO}halve~{END IF} {;}[11,99]~ {ASSIGN}2~{VAR 2}+1~ {Down}{GO}rcount~ [If there is only one line of two-column text on this page (VAR 9 < 2), set VAR 1 to 0, move the cursor to the beginning of the current line and skip to DELPAGE.] {LABEL}halve~ {IF}{VAR 9}<2~ {ASSIGN}1~0~ {Home}{Home}{Left}{GO}delpage~ {END IF} [Otherwise set VAR 1 to one-half the total number of lines of two-column text (VAR 9), rounded up to the next higher integer if the result is not an integer. Call this number n.] {ASSIGN}1~{VAR 9}+1~{ASSIGN}1~{VAR 1}/2~ [Go to the top of the left column, then move down to line n. Then move down to the beginning of line n+1 and insert Hard Page ahead of it. This gives two columns that are approximately equal in length, with the left column ending with line n and the right column beginning with line n+1.] {Goto}{Left}{Goto}{Up} {LABEL}gomid~ {ASSIGN}1~{VAR 1}-1~ {IF}{VAR 1}=0~{BREAK} {ELSE}{Home}{Home}{Left}{Down}{GO}gomid~ {END IF} {Down}{HPg} [Examine the code at the end of the bottom line of the left column. If the code is [HRt], set VAR 1 to false and skip to RDEL, since nothing has to be done to ensure that this line will be justified. Otherwise set VAR 1 to true.] {ASSIGN}6~6~ {Goto}{Left}{Goto}{Up}{Search}{HPg}{Search}{Left}{Left} {Home}{Home}{Left}{End}{Block}{Right}{Macro Commands}a1 {IF}'{VAR 1}'='{Enter}'~{ASSIGN}1~0~{ELSE}{ASSIGN}1~-1~{END IF} {IF}!{VAR 1}~{GO}rdel~{END IF} [The bottom line of the left column is not followed by [HRt], and hence must be justified (if justification is in effect). However, it is now followed (directly or with an intervening [SRt]) by [HPg], which will prevent the justification of the line. To correct this, insert a space at the end of the line if one isn't there already; then keep adding Hard Spaces until they constitute a word long enough to force the initiation of a new line, to which the word is moved. (The initiation of a new line is detected when the leftmost character of the line being operated on is no longer the same as the original leftmost character of the bottom line.) The original bottom line will now be followed by [SRt], and hence will be justified. The line containing the word composed of Hard Spaces is now the bottom line of the left column; it will look like a blank line when printed.] {ASSIGN}6~7~ {DISPLAY ON} {Goto}{Left}{Goto}{Up}{Search}{Search}{Left}{Left} {Home}{Home}{Left}{Block}{Right}{Macro Commands}a4 {End}{Left}{Block}{Right}{Macro Commands}a0 {IF}'{VAR 0}'!=' '~ {END IF} {;}space~ {LABEL}extend~ {Home}{Home}{Left}{Block}{Right}{Macro Commands}a0 {End}{Home} {;}hard space~ {IF}'{VAR 0}'='{VAR 4}'~{GO}extend~{END IF} [Set VAR 9 to 0 to indicate to DELETE that it is to operate on the right column. Then call DELETE to give the user to chance to delete blank lines at the top of the right column.] {LABEL}rdel~ {DISPLAY OFF} {ASSIGN}9~0~{CALL}delete~ [Look at the code following end marker [11,99]. If it is [HPg] (inserted by WordPerfect along with [Col Off]), delete it. (This makes it possible for additional text to be placed on the same page that contains the final part of the two-column section.)] {LABEL}delpage~ {ASSIGN}6~8~ {Search}[11,99]{Search} {;}[11,99]~ {Block}{Right}{Macro Commands}a0 {IF}'{VAR 0}'='{HPg}'~{Goto}{Goto}{Del}{END IF} [Find and delete two [HRt] codes, to compensate for the extra blank lines generated by the balancing process.] {LABEL}delline~ {ON NOT FOUND}{GO}reset~~ {Left}{Search}{Enter}{Search}{Backspace} {Left}{Search}Enter}{Search}{Backspace} [Prepare to exit.] {LABEL}reset~ [If Typeover was in effect upon entry (VAR 5 = true), switch back from Insert to Typeover.] {IF}{VAR 5}~{Typeover}{END IF} [Switch to side-by-side display of columns.] {DISPLAY OFF}{Setup}38y{Enter}{Enter}{DISPLAY ON} [Go back a page (to make sure we're ahead of end marker [11,99]), then find end marker [11,99] and delete it.] {Page Up}{Search}[11,99]{Search}{Backspace} [Move the cursor to the bottom line of the left column of two-column text on the final page occupied by this section of text.] {Goto}{Left}{Down} [End macro execution.] {QUIT} [-----------------------------------------------------------------------------] [This subroutine checks for blank lines at the top of a column on the last page occupied by the two-column text and gives the user the opportunity to delete them. It operates on the left column if VAR 9 = true, on the right column if VAR 9 = false.] {LABEL}delete~ [Set VAR 8 to "left" if VAR 9 = true, to "right" if VAR 9 = false (for use in the prompt).] {IF}{VAR 9}~{ASSIGN}8~left~{ELSE}{ASSIGN}8~right~{END IF} [Go to the top line of the column.] {LABEL}redel~ {DISPLAY OFF} {Goto} {IF}{VAR 9}~{Left}{ELSE}{Right}{END IF} {Goto}{Up} [If the line begins with [HRt], it is blank: ask the user if the line is to be deleted. If the reply is affirmative, delete the [HRt] and go back to REDEL to look at the line that is the new top line of the column. But if the line doesn't begin with [HRt], or if it does but the user doesn't want to delete it, return. (Note that visually blank lines consisting of spaces or tabs preceding [HRt] won't be detected as blank lines.)] {Home}{Home}{Left}{Block}{Right}{Macro Commands}a0 {IF}'{VAR 0}'!='{Enter}'~{GO}enddel~{END IF} {DISPLAY ON}{BELL}{CHAR}7~{^R} Delete blank line at top of {VAR 8} column? (y/n) {^S}~ {ASSIGN}0~0~ {IF}'{VAR 7}'='y'~{ASSIGN}0~-1~{END IF} {IF}'{VAR 7}'='Y'~{ASSIGN}0~-1~{END IF} {IF}{VAR 0}~{Goto}{Up}{Home}{Home}{Left}{Del}{GO}redel~{END IF} {LABEL}enddel~ {DISPLAY OFF} {RETURN} [-----------------------------------------------------------------------------] [Control comes here if a search fails to find its target. Terminate execution with a message giving the identifying number assigned to VAR 6. (This is a debugging aid. If all goes well, this exit should never be taken.)] {LABEL}nf~ {BELL} {DISPLAY ON}{PROMPT}Search error {VAR 6}! (press Enter)~{PAUSE} {RETURN} [BALCOL1.WPM =================================================================] [Upon entry, the cursor is on beginning marker [11,98], i.e. on the top line of the left column of the first page occupied by this section of two-column text.] {;} BALCOL1.WPM (2/5/89) (Rufus S. Hendon)~ {;} Subroutine used by BALCOL.WPM.~ {;} Examines two-column text on nonfinal pages and gives user the chance to delete blank lines at the top of columns.~ [If the current page is the last page, return. Otherwise check whether the top line of the left column is blank. If it isn't, go to RDEL. Otherwise ask the user if the blank line is to be deleted. If the answer is negative, go to RDEL. Otherwise delete the line and repeat the process for the line which has become the new top line as the result of the deletion.] {LABEL}ldel~ {DISPLAY OFF} {CALL}lastpage~ {IF}{VAR 0}~{RETURN}{END IF} {Goto}{Left}{CALL}nonblank~ {IF}{VAR 0}~{GO}rdel~{END IF} {DISPLAY ON}{ASSIGN}0~left~{CALL}yesno~ {IF}{VAR 0}~{Goto}{Up}{Home}{Left}{Del}{GO}ldel~{END IF} [Check whether the top line of the right column is blank. If it isn't, advance to the next page and go back to LDEL. Otherwise ask the user if the blank line is to be deleted. If the answer is negative, advance to the next page and go back to LDEL. Otherwise delete the line and repeat the process for the line which has become the new top line as the result of the deletion.] {LABEL}rdel~ {DISPLAY OFF} {Goto}{Right}{CALL}nonblank~ {IF}{VAR 0}~{Page Down}{GO}ldel~{END IF} {DISPLAY ON}{ASSIGN}0~right~{CALL}yesno~ {IF}{VAR 0}~ {Goto}{Up}{Home}{Left}{Del}{GO}rdel~ {ELSE}{Page Down}{GO}ldel~ {END IF} [-----------------------------------------------------------------------------] [This subroutine sets VAR 0 to true if the current page is the last page occupied by this section of two-column text, to false if it is not.] {LABEL}lastpage~ [Provisionally set VAR 0 to false.] {ASSIGN}0~0~ [Examine the character at the end of the bottom line of the left column on the current page. If it is end marker [11,99], reset VAR 0 to true and return.] {Goto}{Left} {Goto}{Down}{End}{Left}{Block}{Right}{Macro Commands}a9{Goto}{Goto} {IF}'{VAR 9}'='[11,99]'~ {;}[11,99]~ {ASSIGN}0~-1~{RETURN} {END IF} [Otherwise examine the character at the end of the bottom line of the right column. If it is end marker [11,99], reset VAR 0 to true and return. Otherwise return with VAR 0 = false.] {Goto}{Right} {Goto}{Down}{End}{Left}{Block}{Right}{Macro Commands}a9{Goto}{Goto} {IF}'{VAR 9}'='[11,99]'~ {;}[11,99]~ {ASSIGN}0~-1~ {END IF} {RETURN} [-----------------------------------------------------------------------------] [This subroutine sets VAR 0 to true if the top line of the current column is not a blank line, to false if it is. (A line is considered to be blank if it begins with a [HRt] code. Lines in which [HRt] is preceded by spaces or tabs will not be recognized as blank lines.)] {LABEL}nonblank~ {Goto}{Up}{Home}{Left}{Block}{Right}{Macro Commands}a0{Goto}{Goto} {IF}'{VAR 0}'='{Enter}'~{ASSIGN}0~0~ {ELSE}{ASSIGN}0~-1~ {END IF} {RETURN} [-----------------------------------------------------------------------------] [This subroutine asks the user if the blank line at the top of the current column (VAR 0 = "left" or "right", depending on which column is the current one) is to be deleted. VAR 0 is set to true if the reply is "y", to false if the reply is "n".] {LABEL}yesno~ {BELL}{CHAR}9~{^R} Delete blank line at top of {VAR 0} column? (y/n) {^S}~ {ASSIGN}0~0~ {IF}'{VAR 9}'='y'~{ASSIGN}0~-1~{END IF} {IF}'{VAR 9}'='Y'~{ASSIGN}0~-1~{END IF} {RETURN}