AmendHub

jcs

/

amend

/

amendments

/

13

tetab: Add standalone tab handling module for TextEdit controls

This uses TE's TECustomHook functionality to hook into width
calculation, drawing, and hit testing to expand tabs into a
configurable number of spaces (defaulting to 4). This requires
using the new styled TextEdit control available in System 6+.

jcs made amendment 13 11 months ago
--- tetab.c Thu Oct 21 17:10:02 2021 +++ tetab.c Thu Oct 21 17:10:02 2021 @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Implements tab spacing in TextEdit controls with a fixed-width font. + * Only works on styled TextEdit controls (created with TEStylNew) and + * assumes that functions get called with a text buffer that starts at the + * beginning of the line. + * + * Usage is per-TextEdit control, so these hooks need to be established + * on all controls where tabs should be expanded: + * + * TEHandle te; + * ProcPtr teproc; + * + * [..] + * your_te = TEStylNew(&bounds, &bounds); + * TEAutoView(true, te); + * teproc = StripAddress(TETabDrawHook); + * TECustomHook(intDrawHook, &teproc, te); + * teproc = StripAddress(TETabWidthHook); + * TECustomHook(intWidthHook, &teproc, te); + * teproc = StripAddress(TETabHitTestHook); + * TECustomHook(intHitTestHook, &teproc, te); + * + * The tab width is set to 4 by default in the TETabWidth global. + * + * Thanks to Ari Halberstadt for their 1989 Usenet post on + * comp.sys.mac.programmer with HitTestHook assembly. + */ + +#include <Script.h> +#include <SetUpA4.h> + +#include "tetab.h" + +pascal void TETabDrawHook(void); +pascal void TETabWidthHook(void); +pascal void TETabHitTestHook(void); + +short TETabWidth = 4; + +void +TETabEnable(TEHandle te) +{ + ProcPtr teproc; + + TEAutoView(true, te); + teproc = StripAddress(TETabWidthHook); + TECustomHook(intWidthHook, &teproc, te); + teproc = StripAddress(TETabDrawHook); + TECustomHook(intDrawHook, &teproc, te); + teproc = StripAddress(TETabHitTestHook); + TECustomHook(intHitTestHook, &teproc, te); +} + +pascal void +TETabDrawHook(void) +{ + /* entry registers, in order */ + short offset; + short length; + Ptr text; + + /* locals */ + short i, cwidth, cpos, space, startdraw; + char c; + + asm { + movem.l d3-d7/a0-a3, -(a7) + move.w d0, offset + move.w d1, length + move.l a0, text + } + SetUpA4(); + + cwidth = CharWidth(' '); + + /* + * This startdraw stuff is to try to draw long blocks of non-tab text + * with one call to DrawText() instead of DrawChar()ing every character + * one at a time, for speed. + */ + for (i = 0, cpos = 0, startdraw = 0; i < length; i++) { + c = text[offset + i]; + + if (c == '\t') { + if (i - startdraw) + DrawText(text, startdraw, i - startdraw); + + space = (TETabWidth - (cpos % TETabWidth)); + if (!space) + space = TETabWidth; + cpos += space; + Move(cwidth * space, 0); + startdraw = i + 1; + } else { + //DrawChar(c); + cpos++; + } + } + + if (startdraw < i - 1) + DrawText(text, startdraw, i - startdraw); + + /* return nothing */ + + /* restore registers */ + RestoreA4(); + asm { + movem.l (a7)+, d3-d7/a0-a3 + } +} + +pascal void +TETabWidthHook(void) +{ + /* entry registers, in order */ + short length; + short offset; + Ptr text; + + /* exit */ + short width; + + short i, cwidth, cpos, space; + char c; + + asm { + movem.l d3-d7/a0-a3, -(a7) + move.w d0, length + move.w d1, offset + move.l a0, text + } + SetUpA4(); + + cwidth = CharWidth(' '); + + for (i = 0, cpos = 0; i < length; i++) { + c = text[offset + i]; + + if (c == '\t') { + space = (TETabWidth - (cpos % TETabWidth)); + if (!space) + space = TETabWidth; + cpos += space; + } else { + cpos++; + } + } + + width = cwidth * cpos; + + /* return length */ + asm { + move.w width, d1 + } + + /* restore registers */ + RestoreA4(); + asm { + movem.l (a7)+, d3-d7/a0-a3 + } +} + +pascal void +TETabHitTestHook(void) +{ + /* entry registers, in order */ + short length; + short poffset; + Ptr text; + + /* exit */ + short width; + short offset = 0; + short leftside = 0; + + /* locals */ + short i, cwidth, cpos, wpos, space, found; + char c; + + asm { + movem.l d3-d7/a0-a3, -(a7) + move.w d0, length + move.w d1, poffset + move.l a0, text + } + SetUpA4(); + + cwidth = CharWidth(' '); + + for (i = 0, wpos = 0, cpos = 0, found = 0; i < length; i++) { + c = text[i]; + + if (c == '\t') { + space = (TETabWidth - (wpos % TETabWidth)); + if (!space) + space = TETabWidth; + wpos += space; + } else if (c == '\r') + break; + else + wpos++; + + cpos++; + + if (poffset <= (wpos * cwidth)) { + found = 1; + break; + } + } + + /* put found in hi word */ + width = (wpos * cwidth) | (found << 8); + offset = cpos; + leftside = (poffset < (width - (cwidth / 2))); + + asm { + move.w width, d0 + move.w offset, d1 + move.w leftside, d2 + } + + /* restore registers */ + RestoreA4(); + asm { + movem.l (a7)+, d3-d7/a0-a3 + } +} --- tetab.h Thu Oct 21 14:04:42 2021 +++ tetab.h Thu Oct 21 14:04:42 2021 @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void TETabEnable(TEHandle te); +extern short TETabWidth;