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"