home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1996 January / macformat-033.iso / mac / Shareware City / Developers / ABox.v1.8 / CPlus Files / ABUTextBox.c < prev   
Encoding:
C/C++ Source or Header  |  1995-05-23  |  13.7 KB  |  431 lines  |  [TEXT/MMCC]

  1. /*    
  2.     Copyright © 1991-1995 by TopSoft Inc.  All rights reserved.
  3.  
  4.     You may distribute this file under the terms of the TopSoft
  5.     Artistic License, accompanying this package.
  6.     
  7.     This file was developed by George (ty) Tempel in connection with TopSoft, Inc..
  8.     See the Modification History for more details.
  9.  
  10.     This file is based upon code developed by Bryan K. Ressler (Beaker) 
  11.     of Apple DTS as a replacement for TextBox
  12.     
  13. Product
  14.     About Box
  15.  
  16. FILE
  17.     ABUTextBox.c
  18.  
  19. NAME
  20.     ABUTextBox.c, part of the ABox project source code,
  21.     responsible for mix-in handling the AboutBox textbox stuff.
  22.  
  23. DESCRIPTION
  24.     This file contains defines for the about box modules.
  25.     
  26. DEVELOPED BY
  27.     George (ty) Tempel                netromancr@aol.com
  28.     All code in this file, and its associated header file was
  29.     Created by George (ty) Tempel in connection with the TopSoft, Inc.
  30.     "FilterTop" application development, except where noted.
  31.  
  32.     This file is based upon code developed by Bryan K. Ressler (Beaker) 
  33.     of Apple DTS as a replacement for TextBox
  34.     
  35.  
  36. CARETAKER - George (ty) Tempel <netromancr@aol.com>
  37.      Please consult this person for any changes or suggestions to this file.
  38.  
  39. MODIFICATION HISTORY
  40.  
  41.     dd mmm yy    -    xxx    -    patchxx: description of patch
  42.     10 June 94    -    ty    -    Initial Version Created
  43.     20-july-94    -    ty    -    initial version released
  44.     23-may-95    -    ty    -    changes for compatibility with the CodeWarrior CW6
  45.                             release and the associated Universal Headers from Apple:
  46.                             most methods that returned references now have "Ref" at
  47.                             the end of their methods names to prevent possible collisions
  48.                             with datatypes and classes of the same name (older versions
  49.                             of the compiler didn't have a problem with this).
  50.  
  51. */
  52.  
  53. /*===========================================================================*/
  54.  
  55. /*======= Segmentation directives ========*/
  56.  
  57. #ifdef USE_MANUAL_SEGMENTATION
  58. #pragma segment ty
  59. #endif
  60.  
  61. /*============ Header files ==============*/
  62.     
  63. #include     "ABUTextBox.h"
  64.  
  65. /*=============== Globals ================*/
  66.  
  67.  
  68. /*================ CODE ==================*/
  69.  
  70.  
  71. /*=============================== ABUTextBox::ABUTextBox ================================*/
  72. ABUTextBox::ABUTextBox(void)
  73. {
  74. }    // end ABUTextBox
  75.  
  76.  
  77. /*=============================== ABUTextBox::~ABUTextBox ================================*/
  78. ABUTextBox::~ABUTextBox(void)
  79. {
  80. }    // end ~ABUTextBox
  81.  
  82.  
  83.  
  84.  
  85.  
  86.  
  87.  
  88.  
  89. /*=============================== ABUTextBox::NTBLineHeight ==================================*/
  90. //
  91. // NTBLineHeight - figures line height
  92. //
  93. // Input:    theText        the entire text that was given to the NeoTextBox call
  94. //            textLen        the length in bytes of the text
  95. //            lhCode        the line height code that was passed to NeoTextBox
  96. //            startY        VAR - we return the starting vertical pen location here
  97. //
  98. // Output:    returns the line height to use
  99. //            
  100. //
  101. unsigned short ABUTextBox::NTBLineHeight(unsigned char *theText, 
  102.                                         unsigned long textLen,
  103.                                         Rect *wrapBox, 
  104.                                         short lhCode, 
  105.                                         short *startY)
  106. {
  107.     short            asc,desc;    /* Used in the OutlineMetrics calls */
  108.     FontInfo        fInfo;        /* The old-style font information record */
  109.     Point            frac;        /* The fraction for the TrueType calls */
  110.     unsigned short    lineHeight = 0;    /* The return value */
  111.     OSErr            error;
  112.     
  113.     //    begin here...
  114.     
  115.     if (!(theText && wrapBox))
  116.         return lineHeight;
  117.         
  118.     ::GetFontInfo(&fInfo);
  119.     if (lhCode < 0) {
  120.  
  121.         /*
  122.             If the user has specified variable-height lines, we need to try
  123.             to determine the tallest ascent in the given text.  We can only
  124.             really do this if the font is a TrueType font.  Otherwise, we
  125.             punt and use old-fashioned GetFontInfo numbers.
  126.         */
  127.  
  128.         frac.h = frac.v = 1;
  129.         if (gHasTrueType && IsOutline(frac, frac)) {
  130.  
  131.             /*
  132.                 At this point we know the current font is a TrueType font, so
  133.                 we do an OutlineMetrics call with our full text.  It will put
  134.                 the tallest character ascent into asc, and the deepest descent
  135.                 into desc.  Then we choose between whichever's most between
  136.                 the old-style ascent/descent and the numbers we get from
  137.                 the OutlineMetrics call.
  138.             */
  139.  
  140.             error = OutlineMetrics((short) textLen, theText, frac, frac, &asc, &desc,
  141.                                     nil, nil, nil);
  142.             if (error)
  143.                 return lineHeight;
  144.                 
  145.             lineHeight = MAXOF(fInfo.ascent, asc) + MAXOF(fInfo. descent,-desc) +
  146.                 fInfo.leading;
  147.             *startY = wrapBox->top + MAXOF(fInfo.ascent, asc);
  148.             *startY += fInfo.leading;
  149.  
  150.         } else {
  151.         
  152.             /*
  153.                 At this point we know the current font isn't TrueType, so we
  154.                 just use the old way of calculating line height.
  155.             */
  156.             
  157.             lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  158.             *startY = wrapBox->top + fInfo.ascent + fInfo.leading;
  159.         }
  160.  
  161.     } else if (lhCode == 0) {
  162.  
  163.         /*
  164.             If the user has specified "default" line height, he just wants us
  165.             to get the line height from the FontInfo record.
  166.         */
  167.         
  168.         lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  169.         *startY = wrapBox->top + fInfo.ascent + fInfo.leading;
  170.         
  171.     } else {
  172.         
  173.         /* If the user has provided a specific line height, we just trust
  174.             them.  We can't really generate too good of a starting vertical
  175.             coordinate, but we munge one together anyway.
  176.         */
  177.  
  178.         lineHeight = lhCode;
  179.         *startY = wrapBox->top + lhCode + fInfo.leading;
  180.  
  181.     }
  182.  
  183.     return(lineHeight);
  184. }
  185.  
  186. /*=============================== ABUTextBox::NTBDraw ==================================*/
  187. //
  188. //
  189. // NTBDraw - draws a line with appropriate justification
  190. //
  191. // Input:    breakCode    the break code that was returned from StyledLineBreak
  192. //            lineStart    pointer to the beginning of the text for the current line
  193. //            lineBytes    the length in bytes of the the text for this line
  194. //            wrapBox        the box within which we're wrapping
  195. //            align        the text alignment as specified by the user
  196. //            curY        our current vertical pen coordinate
  197. //            boxWidth    the width of wrapBox (since NeoTextBox already calculated it)
  198. //
  199. // Output:    none (draws on the screen)
  200. //            
  201. //
  202. void     ABUTextBox::NTBDraw(StyledLineBreakCode breakCode, 
  203.                             unsigned char *lineStart,
  204.                             long lineBytes, 
  205.                             Rect *wrapBox, 
  206.                             short align, 
  207.                             short curY, 
  208.                             short boxWidth)
  209. {
  210.     unsigned long    blackLen;    /* Length of non-white characters */
  211.     short            slop;        /* Number of pixels of slop for full just */
  212.  
  213.     /*
  214.         The first thing we do here is determine the length of the "black" part
  215.         of the current line.  This excludes spaces, carriage returns, and other
  216.         white stuff depending on the language.  How do we know what to elim-
  217.         inate?  We DON'T!  So we ask our friend the Script Manager to do it
  218.         for us.  VisibleLength returns the number of bytes that we should use
  219.         for pixel width calculations.
  220.     */
  221.     
  222.     if (!(lineStart && wrapBox))
  223.         return;
  224.         
  225.     //pascal long VisibleLength(Ptr textPtr,long textLen)
  226.     blackLen = ::VisibleLength((Ptr) lineStart, lineBytes);
  227.     
  228.     if (align == kNTBJustFull) {
  229.     
  230.         /*
  231.             For full justification, we need to calculate the "slop" space on
  232.             the line that's not filled up by the text.  Then we move to the
  233.             margin and get ready to draw BUT WAIT!  If this is the last line of
  234.             a paragraph, we need to draw it with whatever the system justifi-
  235.             cation is.  So if the break code indicates we're at the end of the
  236.             text, or we can find a carriage return there ourselves, we just
  237.             change the text alignment to the current default system text align-
  238.             ment and fall through without drawing.  If it's just another line
  239.             within a paragraph, we use the handy Script Manager routine DrawJust
  240.             to draw it justified.  In languages like Arabic, full justification
  241.             is performed differently than just spacing out the words.  DrawJust
  242.             handles cases like these correctly.
  243.             
  244.             Note that when we go looking for the carriage return at the end of
  245.             the line, it's okay to simply index to the last byte of the string.
  246.             This is because the carriage return character, 0x0d, is in the
  247.             control-code range, which is defined to never be the low byte of a
  248.             two byte character.
  249.         */
  250.  
  251.         slop = boxWidth - ::TextWidth(lineStart, 0, blackLen);
  252.         ::MoveTo(wrapBox->left, curY);
  253.         if (breakCode == smBreakOverflow ||
  254.             *(lineStart + (lineBytes - 1)) == kNTBReturnChar)
  255.             align = ::GetSysJust();
  256.         else 
  257.             ::DrawJust((Ptr) lineStart, blackLen, slop);
  258.     }
  259.  
  260.     /*
  261.         For the rest of the text alignments (left, center, and right), we just
  262.         move the pen to the right place and draw the text with DrawText.  Note
  263.         that the alignments that could have come into the NeoTextBox call have
  264.         been resoved down to one of the four constants teForceLeft, teJustRight
  265.         teJustCenter, or ntbJustFull.
  266.     */
  267.     
  268.     switch(align) {
  269.         case teForceLeft:
  270.         case teJustLeft:
  271.             ::MoveTo(wrapBox->left, curY);
  272.             break;
  273.         case teJustRight:
  274.             ::MoveTo(wrapBox->right - ::TextWidth(lineStart, 0, blackLen), curY);
  275.             break;
  276.         case teJustCenter:
  277.             ::MoveTo(wrapBox->left + (boxWidth - ::TextWidth(lineStart, 0, blackLen)) / 2,
  278.                 curY);
  279.             break;
  280.     }
  281.     if (align != kNTBJustFull)
  282.         ::DrawText(lineStart, 0, lineBytes);
  283. }
  284.  
  285.  
  286.  
  287. /*=============================== ABUTextBox::DrawTextBox ==================================*/
  288. //
  289. // NeoTextBox - word-wraps text inside a given box
  290. //
  291. // Input:    theText        the text we need to wrap
  292. //            textLen        the length in bytes of the text
  293. //            wrapBox        the box within which we're wrapping
  294. //            align        the text alignment
  295. //                            teForceLeft, teFlushLeft    left justified
  296. //                            teJustCenter, teCenter        center justified
  297. //                            teJustRight, teFlushRight    right justified
  298. //                            ntbJustFull                    full justified
  299. //                            teJustLeft, teFlushDefault    system justified
  300. //            lhCode        the line height code that was passed to NeoTextBox
  301. //                            < 0        variable - based on tallest character
  302. //                            0        default - based on GetFontInfo
  303. //                            > 0        fixed - use lhCode as the line height
  304. //            endY        VAR - if non-nil, the vertical coord of the last line
  305. //            lhUsed        VAR - if non-nil, the line height used to draw the text
  306. //
  307. // Output:    returns the number of line drawn total (even if they drew outside of
  308. //            the boundries of wrapBox)
  309. //            
  310. //
  311. short ABUTextBox::DrawTextBox(unsigned char *theText, unsigned long textLen, Rect *wrapBox,
  312.     short align, short lhCode, short *endY, short *lhUsed)
  313. {
  314.     RgnHandle            oldClip;        /* Saved clipping region */
  315.     StyledLineBreakCode    breakCode;        /* Returned code from StyledLineBreak */
  316.     Fixed                fixedMax;        /* boxWidth converted to fixed point */
  317.     Fixed                wrapWid;        /* Width to wrap to */
  318.     short                boxWidth;        /* Width of the wrapBox */
  319.     long                lineBytes;        /* Number of bytes in one line */
  320.     unsigned short        lineHeight;        /* Calculated line height */
  321.     short                curY;            /* Current vertical pen location */
  322.     unsigned short        lineCount = 0;        /* Number of lines we've drawn */
  323.     long                textLeft;        /* Pointer to remaining bytes of text */
  324.     unsigned char        *lineStart;        /* Pointer to beginning of a line */
  325.     unsigned char        *textEnd;        /* Pointer to the end of input text */
  326.  
  327.     //    begin here...
  328.     if (!(theText && wrapBox))
  329.         return lineCount;
  330.         
  331.     /*
  332.         First, we save the old clipping region and clip to wrapBox.  Then, figure
  333.         the width of wrapBox, and make a fixed point version of it.  Also, resolve
  334.         the text alignment teFlushDefault to be the default system text alignment.
  335.     */
  336.  
  337.     ::GetClip((oldClip = ::NewRgn()));
  338.     ::ClipRect(wrapBox);
  339.     boxWidth = wrapBox->right - wrapBox->left;
  340.     fixedMax = ::Long2Fix((long) boxWidth);
  341.     if (align == teFlushDefault)
  342.         align = GetSysJust();
  343.     
  344.     /*
  345.         Now we call NTBLineHeight to calculate the appropriate line height.  It
  346.         also figures our starting vertical pen location in curY based on the
  347.         line height and other metric parameters.  Clear lineCount, set
  348.         lineStart to point to the beginning of the text, calculate textEnd for
  349.         comparison to know when we're done, and preset textLeft to be all the
  350.         text.
  351.     */
  352.  
  353.     lineHeight = ABUTextBox::NTBLineHeight(theText, textLen, wrapBox, lhCode, &curY);
  354.     lineCount = 0;
  355.     lineStart = theText;
  356.     textEnd = theText + textLen;
  357.     textLeft = textLen;
  358.     
  359.     /*
  360.         This is the main wrap-and-draw loop.  I bet you never thought wrapping
  361.         text could be so easy...
  362.     */
  363.  
  364.     do {
  365.         
  366.         /*
  367.             Every line, we have to preset lineBytes to something non-zero.
  368.             This tells StyledLineBreak that we're drawing the first format
  369.             run on the line (of course, for us, there's only ONE format run
  370.             total).  Also preset wrapWid.  StyledLineBreak will always modify
  371.             lineBytes (to tell you how many bytes are on this line), and will
  372.             modify wrapWid, so we have to reset them each line.
  373.         */
  374.         
  375.         lineBytes = 1;
  376.         wrapWid = fixedMax;
  377.  
  378.         breakCode = ::StyledLineBreak((Ptr) lineStart, textLeft, 0, textLeft, 0,
  379.             &wrapWid, &lineBytes);
  380.         
  381.         /*
  382.             Now that the Script Manager has done all the really hard work for
  383.             us, we draw the line.  We already knew lineStart, StyledLineBreak
  384.             gave us lineBytes, which we pass to NTBDraw.  It'll handle the
  385.             different text alignments itself.
  386.         */
  387.         
  388.         ABUTextBox::NTBDraw(breakCode, lineStart, lineBytes, wrapBox, align, curY, boxWidth);
  389.         
  390.         /*
  391.             Now we advance our vertical position down by the height of one
  392.             line, advance lineStart by the number of bytes we just drew,
  393.             calculate a new textLeft, and increment our line count.
  394.         */
  395.         
  396.         curY += lineHeight;
  397.         lineStart += lineBytes;
  398.         textLeft -= lineBytes;
  399.         lineCount++;
  400.         
  401.     } while (lineStart < textEnd);
  402.     
  403.     /*
  404.         Well that was a job well done.  Let's return some useful values, too.
  405.         If the user gave pointers for endY and lhUsed, we stuff our ending
  406.         vertical coordinate and the line height we calculated into those,
  407.         respectively.  These allow the guy to put something after the wrapped
  408.         text (or at least know the right place to put it).
  409.     */
  410.  
  411.     if (endY)
  412.         *endY = curY - lineHeight;
  413.     if (lhUsed)
  414.         *lhUsed = lineHeight;
  415.     
  416.     /*
  417.         Finally, restore the clipping region, dispose of the region, and
  418.         return the TOTAL number of lines drawn (note that we didn't stop
  419.         drawing when curY advanced past wrapBox->bottom.  This way, the user
  420.         could tell that the text overflowed wrapBox.  If you want to know how
  421.         many lines fit, divide wrapBox by lhUsed.  This way, you get the best
  422.         of both worlds.
  423.     */
  424.  
  425.     ::SetClip(oldClip);
  426.     ::DisposeRgn(oldClip);
  427.  
  428.     return(lineCount);
  429. }
  430.  
  431.