[comp.sys.mac.programmer] Apple deceives us + Useful code snippet HitTestHook for TextEdit

ari@eleazar.dartmouth.edu (Ari Halberstadt) (11/18/89)

Hello all ye mac hackers. I recently spent countless hours figuring out
what Apple didn't tell us. I've been working on adapting TextEdit for
a specific application, and needed to write most of the hooks for
the new styled text edit in system 6.0. Following is a quote
from TechNote #207, describing the HitTestHook:

=============================
TEHitTestHook

This routine is called to determine the character position in a line given the horizontal offset, in pixels, from the beginning of a line. The default action is to call Pixel2Char and return. For more information, see the description of Pixel2Char in the Script Manager chapter of Inside Macintosh Volume 5.

On entry: D0  length of text to hit test       (word)
          D1  pixel offset from start of text  (word)
          A0  pointer to start of text         (long)
          A3  pointer to the TextEdit record   (long)
          A4  handle to the TextEdit record    (long)

On exit:  D0  pixel width to last offset       (low word)
              Boolean = TRUE if a character    (high word)
              offset corresponding to the
              pixel width was found.
          D1  character offset                 (word)
          D2  Boolean = TRUE if the pixel      (word)
              offset falls within the left side
              of the character.
=============================

Notice the words "the default action is to call Pixel2Char and return".
If Apple actually succeeds in doing this, I'll be truly amazed. If you
look at the code I ended up writing, you'll see there are many special
casses with which I had to cope. Why Apple purposefuly mislead us,
I have no idea. As to why Apple could not simply show us some sample code, 
I haven't a clue.

===========================

Following is the hit test hook which I finally ended up writing.
It was written with THINK C 4.0, and will run correctly even
from an XCMD or device driver. To use it, you must install it
using TECustomHook, which is described in TechNote #207. The
code should run ok in MPW, if you remove the calls SetUpA4()
and RestoreA4() and adapt the assembly code snippets. If you
use this code from a code resource in THINK C, you must do
a RememberA4() somewhere in the same file as this function is
in, and at a time when the value of A4 is correct. Currently,
the function does the same things that the default HitTestHook
does, but, of course, you may enhance this function as needed.

Disclaimer: I'm not sure the code is 100% correct, but it seems
to live very happily with good old TextEdit. Notice that you
can't run profiling when using this hook, since the THINK C
profiler inserts a function call at the start of each function
in your program, which means the registers are destroyed when
it returns.

===========================

/* Please acknowledge source if you use this code in your programs... */
void
HitTestHook()
{
	int	length;	/* length of text to hit test */
	int	poffset;/* pixel offset from start of line */
	Ptr	text;	/* pointer to start of text */
	Style	hsStyles;/* the hot spot styles */
	Boolean	leftSide;/* true if pixel width falls within left
			side of a character */
	int	offset;	/* offset of character that pixel is closest to */
	GrafPtr	currentPort;/* the current port */
	int	width;		/* width of text */
	
	/* get stuff from registers, and save all other registers */
	asm {
		movem.l	d3-d7/a0-a3, -(a7)
		move.w	d0, length
		move.w	d1, poffset
		move.l	a0, text
	}
	SetUpA4();
	
	/* do interesting stuff, such as disabling certain text styles */

	/* calculate offset into text corresponding to pixel */
	offset = Pixel2Char(text, length, 0, poffset, &leftSide);
	/* offset must [usually] be incremented; this copes with clicks in most
	areas of the text */
	if (leftSide && offset+1 <= length)
		offset++;
	/* this copes with click after last character in file */
	if (offset > 0 && offset == length && text[offset] != '\r')
		leftSide = false;
	/* this copes with click before first character of a line */
	if (offset == 0 && text[-1] == '\r') {
		leftSide = true;
		offset++;
	}
	width = Char2Pixel(text, offset, 0, poffset, 1);

#ifdef DEBUG
	/* if you enable this code, you can see some pretty
	interesting things! If you don't have THINK C, this may
	not work very nicely (THINK C lets you do printf's in an
	inactive window).
	*/
	printf("width=%d, poffset=%d, leftSide=%d, offset=%d\n",
		width, poffset, leftSide, offset);
	{ char str[256];
		strncpy(str, text, length);
		str[length] = 0;
		if (str[length-1] == '\r')
			str[length-1] = '\n';
		printf("text='%s', length=%d\n", str, length);
	}
	SetPort(currentPort);
#endif

	/* save return values */
	if (width < poffset)
		asm { move.w #false, d0 }/* no offset was found */
	else
		asm { move.w #true, d0 }/* else, set d0 to true */
 	
	asm {
		swap	d0		; stuff condition into high word of d0
		move.w	width, d0	; set low word of d0 to pixel width
					; to the last offset
		move.w	offset, d1	; store character offset in d1
		move.w	leftSide, d2	; set d2 to true if pixel falls on
					; left side of a character
	}

	/* cleanup and exit */
	RestoreA4();
	asm {
		movem.l	(a7)+, d3-d7/a0-a3
	}
}	
--

Ari Halberstadt '91, "Long live succinct signatures"
ari@eleazar.dartmouth.edu	Disclaimer: "Live Free or Die"