[comp.sys.mac] TENew & StdTxMeas

brian@ut-sally.UUCP (Brian H. Powell) (07/29/87)

     I've been working on adding tabs to my TextEdit-based editor.  (They
work, by the way.  If you want to know the details, I'll say a little bit
below after I ask my question.)
     I've replaced the StdTxMeas (low-level quickdraw) and StdText routines
for the window with my own.  For development purposes, I put in all sorts of
assertions and things to take care of boundary conditions while I got all the
loops working.  Now that I've gotten all the interior code taken care of, I'm
removing some of those cases.  I removed a couple I thought wouldn't make any
difference and I got an address error.  Here's what I had:

	if (it doesn't appear to be my window)
	   return(StdTxMeas(...));	/* return whatever the standard
					   proc would have.  */
	else if (byteCount == 0)
	   return(0);		   /* no characters to count == 0 length */
	else
		...all the hard part...

     When I took out the "if" and the "else if", it bombed in HUnlock soon
after this routine was called.  I noticed that this routine was being called
with byteCount == 0 and textAddr == 0.  (!).  It is apparently called
indirectly by TENew.  (via GetFontInfo.)  That seemed like a bug to me (to use
a zero address.)  I looked to see what StdTxMeas would return in this case.
It returned zero, as expected.
     Sooo, I thought maybe my interior code didn't really handle the case
where byteCount was zero correctly.  I added some code back:

	if (byteCount == 0)
	   return(0);
	else
		...the hard part...

     Still bombed.  I double-checked that I hadn't modified any of my formerly
working code.  I ruled out lots of stuff.  It came down to the fact that I had
to call StdTxMeas even if byteCount == 0.  (The reason it worked before this,
is that TENew is called before the window "appears to be my window", so
StdTxMeas was always called.)

     My conjecture is that StdTxMeas is supposed to load the font, and
GetFontInfo relies upon the font being in memory.  Sounds like a bug to me,
but the low-level QuickDraw routines (the Std... procs) are so poorly
documented...; I guess that's what I get for messing with things.
     Does anybody know if I'm right?  Or is there something else going on
here?  Am I safe just calling StdTxMeas with byteCount == 0 only if textAddr
is also == 0?  (probably not, just in case Apple changes things in five
years when they bring out the 16Meg ROMs :-)
     Who (meaning which ROM call), is actually in charge of loading fonts that
aren't in memory?  Is that all StdTxMeas's responsibility, or do other people
get to take part?

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

     If you aren't interested in how I did tabs in TextEdit, you can stop
reading now.
     First, I looked at all the scary code in the November 1986 MacTutor that
described how to do tabs.  Fortunately, my editor was simpler than his general
case:  I have an infinite right margin (destRect), and therefore I only wrap
at carriage returns (the crOnly flag).  Therefore, I don't have to worry about
tabs that wrap to the next line.  All of the author's changes to TE were used
to handle this line-wrapping stuff.
     All that's left, then, is to install new StdTxMeas and StdText routines
to each TE window's grafPort.  You don't have to make any changes to TextEdit.
Also, I didn't have to use any of the undocumented things the MacTutor author
used.

What IM doesn't tell you:
	* StdTxMeas returns the length of the text specified.  CR's have zero
	  width.  If StdTxMeas is called with several lines worth of data,
	  it's supposed to return the entire length as if there were no CR's.
	  (I don't see much use in this behavior, but ours is not to question
	  why...)
	* As described above, StdTxMeas may have to load in the font.
	* As described in MacTutor, the 64K ROM TextEdit don't necessarily
	  measure text starting at the beginning of a line.  This will cause
	  problems when we try to figure out tab lengths.
	* StdText, the drawing procedure, will not necessarily be asked to
	  draw an entire TE line, so you again have to worry about where the
	  beginnings of lines are.

     These routines, especially the TxMeas routine, need to be fast.
Therefore, there are certain cases you want to handle efficiently.  In
particular, most of the time, you *will* get called to measure from the
beginning of a line.  You shouldn't waste too much time backtracking to find
the beginning of the line if you can help it.  You will also probably run into
cases where you call the StdTxMeas with byteCount == 0.  You can check for
these cases and skip the call.

     First, search for the first tab.  Keep track of the most recent CR
position while you're searching.
     If you reach the end without finding a tab, just call StdTxMeas.
     Else
	If you ran across a CR, call StdTxMeas to measure from the beginning
				to the last CR.  Add this to your total
				length.
	Else you'll have to back up and find the most recent CR.  (Don't
	   forget you may be on the first line, so you want to stop when you
	   get to the beginning of the text.  I had the current TEHandle
	   as a global, so this wasn't hard.)
	   If you had to backup at least a character, you have to recurse to
	   find the length up to where you are.  This gets added to the
	   current line length, but not the total result.
(The objective here, besides finding the total result, is to find the length
between the beginning of the line and the tab.)
	Now measure from the beginning of the line (or if you had to backup,
	   textAddr) and add the result to both the total result and the
	   current line length.
	Compute the tab length and add to the lengths.
and loop all this until you run out of text.

     (for efficiency, you can avoid measuring the same thing twice by moving
your pointer to the last CR up to the last thing you actually measured.  You
have to be a little bit more careful about resetting the current line length
to zero at the proper times, though.)
     If it seems complicated, it is.

     Text drawing is relatively easy.  You draw until you find a tab.  Then
you just issue a Move with dh = to the computed tab length.  Note that you may
have to back up to find the beginning of the line to find the current line
length (which you feed to the function that computes the tab length.)

    If you really want to see the C code I used to do this, I'll send it to
you.  (Or if there's a big desire, I'll post it to comp.sys.mac.  (It's not
worthy of comp.sources.mac, especially since there's a 2-month delay to post
to that group.))  It's rather ugly code.  I would be thrilled if some young
algorithms student would prove it correct for me.  (Wait, let's fire up the C
to Lisp translator and run it through the Boyer-Moore prover. :-)  There are
probably a few places I could pare it down.  Now that I've got it working,
I'll probably start cleaning it up.

Brian H. Powell
		UUCP:	{ihnp4,seismo,ctvax}!ut-sally!brian
		ARPA:	brian@sally.UTEXAS.EDU

   _Work_					 _Not Work_
  Department of Computer Sciences		P.O. Box 5899
  Taylor Hall 2.124				Austin, TX 78763-5899
  The University of Texas at Austin		(512) 346-0835
  Austin, TX 78712-1188
  (512) 471-9536

brian@ut-sally.UUCP (Brian H. Powell) (07/29/87)

     Of course, how silly of me.  StdTxMeas also affects its parameters numer,
denom and FontInfo.  That's why I can't just return 0 in the case I described.
I was fiddling around with my program when it showed me an insertion point the
size of the Empire state building.
     I kind of feel like I just put on "The Teacher" of Star Trek's "Spock's
Brain" episode.  I'm sure that this feeling of intelligence will fade, just
like it did for Dr. McCoy.