home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / YellowBox / Kits / MiscTableScroll-138.1 / Palettes / MiscTableScroll / Framework / MiscLineWrapper.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-31  |  14.7 KB  |  548 lines

  1. //=============================================================================
  2. //
  3. //    Copyright (C) 1996-1997 by Paul S. McCarthy and Eric Sunshine.
  4. //        Written by Paul S. McCarthy and Eric Sunshine.
  5. //                All Rights Reserved.
  6. //
  7. //    This notice may not be removed from this source code.
  8. //
  9. //    This object is included in the MiscKit by permission from the authors
  10. //    and its use is governed by the MiscKit license, found in the file
  11. //    "License.rtf" in the MiscKit distribution.  Please refer to that file
  12. //    for a list of all applicable permissions and restrictions.
  13. //    
  14. //=============================================================================
  15. //-----------------------------------------------------------------------------
  16. // MiscLineWrapper.h
  17. //
  18. //    A C++ object for calculating line breaks in text.
  19. //
  20. //-----------------------------------------------------------------------------
  21. //-----------------------------------------------------------------------------
  22. // $Id: MiscLineWrapper.cc,v 1.4 97/04/04 04:57:34 sunshine Exp $
  23. // $Log:    MiscLineWrapper.cc,v $
  24. // Revision 1.4  97/04/04  04:57:34  sunshine
  25. // 0.125.6: Removed unused <assert.h> header.
  26. // 
  27. // Revision 1.3  96/12/30  06:30:23  sunshine
  28. // v105.1: Line height is now based purely on point size.  No longer uses the
  29. // broken NeXT bounding box.
  30. // 
  31. // Revision 1.2  96/12/30  03:11:28  sunshine
  32. // v104.1: Ported to OPENSTEP 4.1 (gamma).
  33. //-----------------------------------------------------------------------------
  34. #ifdef __GNUC__
  35. #pragma implementation
  36. #endif
  37. #include "MiscLineWrapper.h"
  38.  
  39. extern "Objective-C" {
  40. #import    <AppKit/NSText.h>    // NSLeftTextAlignment
  41. #import <AppKit/psops.h>
  42. }
  43.  
  44. extern "C" {
  45. #include <math.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. }
  50.  
  51. float const MiscLineWrapper::DEFAULT_LEFT_MARGIN   = 2.0;
  52. float const MiscLineWrapper::DEFAULT_TOP_MARGIN    = 0.0;
  53. float const MiscLineWrapper::DEFAULT_RIGHT_MARGIN  = 2.0;
  54. float const MiscLineWrapper::DEFAULT_BOTTOM_MARGIN = 0.0;
  55.  
  56.  
  57. //-----------------------------------------------------------------------------
  58. // Destructor
  59. //-----------------------------------------------------------------------------
  60. MiscLineWrapper::~MiscLineWrapper()
  61.     {
  62.     free( text );
  63.     free( lines );
  64.     }
  65.  
  66.  
  67. //-----------------------------------------------------------------------------
  68. // Constructor
  69. //-----------------------------------------------------------------------------
  70. MiscLineWrapper::MiscLineWrapper()
  71.     {
  72.     text_len = 0;
  73.     text_max = 1024;
  74.     text = (char*) malloc( text_max );
  75.     font = 0;
  76.     alignment = NSLeftTextAlignment;
  77.     num_lines = 0;
  78.     max_lines = 16;
  79.     lines = (Line*) malloc( max_lines * sizeof(*lines) );
  80.     rect.origin.x = 0;
  81.     rect.origin.y = 0;
  82.     rect.size.width = 0;
  83.     rect.size.height = 0;
  84.     left_margin = DEFAULT_LEFT_MARGIN;
  85.     top_margin = DEFAULT_TOP_MARGIN;
  86.     right_margin = DEFAULT_RIGHT_MARGIN;
  87.     bottom_margin = DEFAULT_BOTTOM_MARGIN;
  88.     ascender = 0;
  89.     descender = 0;
  90.     line_height = 0;
  91.     char_wrap = false;
  92.     no_partial = false;
  93.     needs_wrap = false;
  94.     }
  95.  
  96.  
  97. //-----------------------------------------------------------------------------
  98. // setCharWrap
  99. //-----------------------------------------------------------------------------
  100. void MiscLineWrapper::setCharWrap( bool b )
  101.     {
  102.     if (char_wrap != b)
  103.     {
  104.     char_wrap = b;
  105.     needs_wrap = true;
  106.     }
  107.     }
  108.  
  109.  
  110. //-----------------------------------------------------------------------------
  111. // setRect
  112. //-----------------------------------------------------------------------------
  113. void MiscLineWrapper::setRect( NSRect r )
  114.     {
  115.     r.origin.x = floor( r.origin.x + left_margin );
  116.     r.origin.y = floor( r.origin.y + top_margin );
  117.     r.size.width = floor( r.size.width - (left_margin + right_margin) );
  118.     r.size.height = floor( r.size.height - (top_margin + bottom_margin) );
  119.     if (r.size.width != rect.size.width)
  120.     needs_wrap = true;
  121.     rect = r;
  122.     }
  123.  
  124.  
  125. //-----------------------------------------------------------------------------
  126. // setLeftMargin
  127. //-----------------------------------------------------------------------------
  128. void MiscLineWrapper::setLeftMargin( float f )
  129.     {
  130.     if (left_margin != f)
  131.     {
  132.     rect.origin.x = floor( rect.origin.x - left_margin + f );
  133.     left_margin = f;
  134.     needs_wrap = true;
  135.     }
  136.     }
  137.  
  138.  
  139. //-----------------------------------------------------------------------------
  140. // setTopMargin
  141. //-----------------------------------------------------------------------------
  142. void MiscLineWrapper::setTopMargin( float f )
  143.     {
  144.     if (top_margin != f)
  145.     {
  146.     rect.origin.y = floor( rect.origin.y - top_margin + f );
  147.     top_margin = f;
  148.     needs_wrap = true;
  149.     }
  150.     }
  151.  
  152.  
  153. //-----------------------------------------------------------------------------
  154. // setRightMargin
  155. //-----------------------------------------------------------------------------
  156. void MiscLineWrapper::setRightMargin( float f )
  157.     {
  158.     if (right_margin != f)
  159.     {
  160.     rect.size.width = floor( rect.size.width - right_margin + f );
  161.     right_margin = f;
  162.     needs_wrap = true;
  163.     }
  164.     }
  165.  
  166.  
  167. //-----------------------------------------------------------------------------
  168. // setBottomMargin
  169. //-----------------------------------------------------------------------------
  170. void MiscLineWrapper::setBottomMargin( float f )
  171.     {
  172.     if (bottom_margin != f)
  173.     {
  174.     rect.size.height = floor( rect.size.height - bottom_margin + f );
  175.     bottom_margin = f;
  176.     needs_wrap = true;
  177.     }
  178.     }
  179.  
  180.  
  181. //-----------------------------------------------------------------------------
  182. // setText
  183. //-----------------------------------------------------------------------------
  184. void MiscLineWrapper::setText( NSString* str )
  185.     {
  186.     char const* t = [str lossyCString]; // FIXME: Not Unicode compliant
  187.     if (t == 0 || *t == 0)
  188.     {
  189.     if (text_len != 0)
  190.         needs_wrap = true;
  191.     text_len = 0;
  192.     }
  193.     else
  194.     {
  195.     int len = strlen( t );
  196.     if (len >= MAX_TEXT_LENGTH)
  197.         len = MAX_TEXT_LENGTH - 1;
  198.     if (len != text_len || strncmp( t, text, len ) != 0)
  199.         {
  200.         len++;            // Include space for null byte.
  201.         if (text_max < len)
  202.         {
  203.         while (text_max < len)
  204.             text_max += text_max;
  205.         text = (char*) realloc( text, text_max );
  206.         }
  207.         len--;            // Don't include null byte in length.
  208.         memcpy( text, t, len );
  209.         text[ len ] = '\0';
  210.         text_len = len;
  211.         needs_wrap = true;
  212.         }
  213.     }
  214.     }
  215.  
  216.  
  217. //-----------------------------------------------------------------------------
  218. // setFont
  219. //-----------------------------------------------------------------------------
  220. void MiscLineWrapper::setFont( NSFont* f )
  221.     {
  222.     if (font != f)
  223.     {
  224.     [font autorelease];
  225.     font = [f retain];
  226.     float const LINE_SPACING = 1.20;
  227.     needs_wrap = true;
  228.  
  229.     ascender    = ceil( [font ascender] );
  230.     descender   = ceil( - [font descender] );
  231.     line_height = ceil( [font pointSize] * LINE_SPACING );
  232.     }
  233.     }
  234.  
  235.  
  236. //-----------------------------------------------------------------------------
  237. // setAlignment
  238. //-----------------------------------------------------------------------------
  239. void MiscLineWrapper::setAlignment( int a )
  240.     {
  241.     if (a != NSCenterTextAlignment && a != NSRightTextAlignment)
  242.     a = NSLeftTextAlignment;
  243.     if (alignment != a)
  244.     {
  245.     alignment = a;
  246.     needs_wrap = true;
  247.     }
  248.     }
  249.  
  250.  
  251. //-----------------------------------------------------------------------------
  252. // calc_width
  253. //-----------------------------------------------------------------------------
  254. float MiscLineWrapper::calc_width( int i, int lim ) const
  255.     {
  256.     float w = 0;
  257.  
  258.     if ([font isFixedPitch])
  259.     {
  260.     int const TAB_STOPS = 8;
  261.     int n = 0;
  262.     for (int j = i; j < lim; j++)
  263.         if (text[j] == '\t' && alignment == NSLeftTextAlignment)
  264.         n += TAB_STOPS - (n & (TAB_STOPS - 1));
  265.         else
  266.         n++;
  267.     w = n * [font widthOfString:@"W"];
  268.     }
  269.     else
  270.     {
  271.     float const space_width = [font widthOfString:@" "];
  272.     float const TAB_SIZE = 8 * space_width;
  273.  
  274.     float sz = [font pointSize];
  275.     float const* widths = [font widths];
  276.     BOOL const is_screen_font = (font == [font screenFont]);
  277.     
  278.     for ( ; i < lim; i++)
  279.         {
  280.         unsigned char c = (unsigned char) text[i];
  281.         if (c == '\t')
  282.         if (alignment == NSLeftTextAlignment)
  283.             w = (floor(w / TAB_SIZE) + 1.0) * TAB_SIZE;
  284.         else
  285.             w += space_width;
  286.         else
  287.         {
  288.         float cw = widths[c];
  289.         if (!is_screen_font)
  290.             cw *= sz;
  291.         w += cw;
  292.         }
  293.         }
  294.     }
  295.     return w;
  296.     }
  297.  
  298.  
  299. //-----------------------------------------------------------------------------
  300. // wrap_segment
  301. //    Wrap a line segment from the text.  Line segments are determined by
  302. //    explicit newlines in the text.
  303. //
  304. // NOTE *1*
  305. //    This case arises when a single character is wider than the width of
  306. //    the rectangle.  Force the character onto the line even though it
  307. //    exceeds the rectangle width.  Otherwise, the character will never
  308. //    be consumed, and we will go into an infinite loop.
  309. //-----------------------------------------------------------------------------
  310. void MiscLineWrapper::wrap_segment( int seg_start, int seg_end )
  311.     {
  312.     bool do_char_wrap = char_wrap && alignment == NSLeftTextAlignment;
  313.     float max_width = rect.size.width;
  314.     do  {
  315.     if (num_lines >= max_lines)
  316.         {
  317.         max_lines += max_lines;
  318.         lines = (Line*) realloc( lines, max_lines * sizeof(*lines) );
  319.         }
  320.  
  321.     Line& line = lines[ num_lines++ ];
  322.     line.width = 0;
  323.     line.start = seg_start;
  324.     int white_start = seg_start;
  325.     int black_start = seg_start;
  326.     int black_end   = seg_start;
  327.     while (black_start < seg_end)
  328.         {
  329.         while (black_start < seg_end && !isgraph( text[ black_start ] ))
  330.         black_start++;
  331.         if (black_start < seg_end)
  332.         {
  333.         black_end = black_start;
  334.         while (black_end < seg_end && isgraph( text[ black_end ] ))
  335.             black_end++;
  336.  
  337.         float black_width = calc_width( seg_start, black_end );
  338.  
  339.         if (black_width <= max_width)    // Word fits on line.
  340.             {
  341.             line.width = black_width;
  342.             white_start = black_end;
  343.             black_start = black_end;
  344.             }
  345.         else                // Word does not fit.
  346.             {
  347.             // Line is empty. Split word.
  348.             if (do_char_wrap || white_start == seg_start)
  349.             {
  350.             while (black_end > black_start &&
  351.                 black_width > max_width)
  352.                 black_width = calc_width(seg_start,--black_end);
  353.             if (black_end == seg_start)
  354.                 {
  355.                 black_end++;    // NOTE *1*
  356.                 black_width = calc_width( seg_start, black_end );
  357.                 }
  358.             if (black_end > black_start)
  359.                 line.width = black_width;
  360.             // else there are preceeding whitespace characters on
  361.             // the line.  The width remains zero, and we move the
  362.             // whole word to the next line.
  363.             white_start = black_end;
  364.             black_start = black_end;
  365.             }
  366.             // else line is not empty, move word to next line.
  367.  
  368.             break;            // *** BREAK *** finish line
  369.             }
  370.         }
  371.         }
  372.  
  373.     line.len = white_start - seg_start;    // Trailing spaces not included
  374.     seg_start = black_start;
  375.     }
  376.     while (seg_start < seg_end);
  377.     }
  378.  
  379.  
  380. //-----------------------------------------------------------------------------
  381. // do_wrap
  382. //-----------------------------------------------------------------------------
  383. void MiscLineWrapper::do_wrap()
  384.     {
  385.     int line_start = 0;
  386.     int line_end;
  387.     num_lines = 0;
  388.     while (line_start < text_len)
  389.     {
  390.     line_end = line_start;
  391.     while (line_end < text_len)
  392.         if (text[line_end++] == '\n')
  393.         break;
  394.     wrap_segment( line_start, line_end );
  395.     line_start = line_end;
  396.     }
  397.     }
  398.  
  399.  
  400. //-----------------------------------------------------------------------------
  401. // wrap
  402. //-----------------------------------------------------------------------------
  403. void MiscLineWrapper::wrap()
  404.     {
  405.     if (needs_wrap)
  406.     {
  407.     needs_wrap = false;
  408.     do_wrap();
  409.     }
  410.     }
  411.  
  412.  
  413. //-----------------------------------------------------------------------------
  414. // dump
  415. //-----------------------------------------------------------------------------
  416. void MiscLineWrapper::dump() const
  417.     {
  418.     for (int i = 0; i < num_lines; i++)
  419.     {
  420.     Line& line = lines[i];
  421.     fprintf( stderr, "%2d: w=%g (%.*s)\n", i, line.width,
  422.             line.len, text + line.start );
  423.     }
  424.     }
  425.  
  426.  
  427. //-----------------------------------------------------------------------------
  428. // width_check
  429. //-----------------------------------------------------------------------------
  430. bool MiscLineWrapper::width_check() const
  431.     {
  432.     for (int i = 0; i < num_lines; i++)
  433.     if (lines[i].width > rect.size.width)
  434.         return true;
  435.     return false;
  436.     }
  437.  
  438.  
  439. //-----------------------------------------------------------------------------
  440. // has_tabs
  441. //-----------------------------------------------------------------------------
  442. bool MiscLineWrapper::has_tabs( Line const& line ) const
  443.     {
  444.     int i = line.start;
  445.     int const lim = i + line.len;
  446.     for ( ; i < lim; i++)
  447.     if (text[i] == '\t')
  448.         return true;
  449.     return false;
  450.     }
  451.  
  452.  
  453. //-----------------------------------------------------------------------------
  454. // draw
  455. //-----------------------------------------------------------------------------
  456. void MiscLineWrapper::draw( float x, float y, int start, int len )
  457.     {
  458.     int const lim = start + len;
  459.     char const save_ch = text[ lim ];
  460.     text[ lim ] = '\0';
  461.     PSmoveto( x, y );
  462.     PSshow( text + start );
  463.     text[ lim ] = save_ch;
  464.     }
  465.  
  466.  
  467. //-----------------------------------------------------------------------------
  468. // draw_tabs
  469. //    Need to draw tab-separated segments.
  470. //-----------------------------------------------------------------------------
  471. void MiscLineWrapper::draw_tabs( float x0, float y, Line const& line )
  472.     {
  473.     int const i0 = line.start;
  474.     int const lim = i0 + line.len;
  475.     int i = i0;
  476.     while (i < lim)
  477.     {
  478.     while (i < lim && text[i] == '\t')
  479.         i++;
  480.     int j = i;
  481.     while (i < lim && text[i] != '\t')
  482.         i++;
  483.     int const n = i - j;
  484.     if (n > 0)
  485.         {
  486.         float x = x0;
  487.         if (j > i0)
  488.         x += calc_width( i0, j );
  489.         draw( x, y, j, n );
  490.         }
  491.     }
  492.     }
  493.  
  494.  
  495. //-----------------------------------------------------------------------------
  496. // draw
  497. //-----------------------------------------------------------------------------
  498. void MiscLineWrapper::draw()
  499.     {
  500.     wrap();
  501.  
  502.     float const wmax = rect.size.width;
  503.     float const x0 = rect.origin.x;
  504.     float const xmax = x0 + wmax;
  505.     float x = x0;
  506.     float const y0 = rect.origin.y;
  507.     float const ymax = y0 + rect.size.height;
  508.     float y = y0 + line_height - descender;
  509.  
  510.     bool did_clip = false;
  511.     if (width_check())
  512.     {
  513.     did_clip = true;
  514.     PSgsave();
  515.     NSRectClip( rect );
  516.     }
  517.  
  518.     for (int i = 0; i < num_lines; i++, y += line_height)
  519.     {
  520.     bool is_partial = y + descender >= ymax;
  521.     if (is_partial && (no_partial || y - ascender >= ymax))
  522.         break;
  523.     Line const& line = lines[i];
  524.     if (line.len > 0)
  525.         {
  526.         if (is_partial && !did_clip)
  527.         {
  528.         did_clip = true;
  529.         PSgsave();
  530.         NSRectClip( rect );
  531.         }
  532.         x = x0;
  533.         if (line.width < wmax)
  534.         if (alignment == NSCenterTextAlignment)
  535.             x = floor( x0 + (wmax - line.width) / 2 );
  536.         else if (alignment == NSRightTextAlignment)
  537.             x = floor( xmax - line.width );
  538.         if (alignment == NSLeftTextAlignment && has_tabs( line ))
  539.         draw_tabs( x, y, line );
  540.         else
  541.         draw( x, y, line.start, line.len );
  542.         }
  543.     }
  544.  
  545.     if (did_clip)
  546.     PSgrestore();
  547.     }
  548.