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.