/* Copyright 1986 - 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

/* Forward declarations in TextEdit.h (DO NOT DELETE THIS LINE) */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_teIMV[] =
	    "$Id: teIMV.c 63 2004-12-24 18:19:43Z ctm $";
#endif

#include "rsys/common.h"
#include "QuickDraw.h"
#include "WindowMgr.h"
#include "ControlMgr.h"
#include "ToolboxUtil.h"
#include "FontMgr.h"
#include "TextEdit.h"
#include "MemoryMgr.h"
#include "ScrapMgr.h"

#include "rsys/cquick.h"
#include "rsys/mman.h"
#include "rsys/tesave.h"
#include "rsys/hook.h"

void
generic_elt_copy (generic_elt_t *dst, generic_elt_t *src)
{
  *dst = *src;
}

void
generic_elt_calc_height_ascent (generic_elt_t *elt)
{
  int16 savesize, savefont;
  Style saveface;
  FontInfo font_info;
  
  savesize = PORT_TX_SIZE (thePort);
  savefont = PORT_TX_FONT (thePort);
  saveface = PORT_TX_FACE_X (thePort);
  TextSize (GENERIC_ELT_SIZE (elt));
  TextFont (GENERIC_ELT_FONT (elt));
  TextFace (GENERIC_ELT_FACE (elt));
  GetFontInfo (&font_info);
  GENERIC_ELT_HEIGHT_X (elt) = CW (  CW (font_info.ascent)
				   + CW (font_info.descent)
				   + CW (font_info.leading));
  GENERIC_ELT_ASCENT_X (elt) = font_info.ascent;
  TextSize (savesize);
  TextFace (saveface);
  TextFont (savefont);
}

boolean_t
adjust_attrs (TextStyle *orig_attrs, TextStyle *new_attrs,
	      TextStyle *dst_attrs, TextStyle *continuous_attrs,
	      int16 mode)
{
  int16 orig_font, orig_size;
  
  orig_font = TS_FONT_X (orig_attrs);
  orig_size = TS_SIZE_X (orig_attrs);
  
  /* compute the new style for this run */
  if (mode & doFont)
    TS_FONT_X (dst_attrs) = TS_FONT_X (new_attrs);
  else
    TS_FONT_X (dst_attrs) = TS_FONT_X (orig_attrs);
  
  /* ### we don't handle doToggle correctly */
  if (mode & doFace)
    {
      if (mode & doToggle && continuous_attrs)
	TS_FACE (dst_attrs) = ((TS_FACE (orig_attrs) & ~TS_FACE (new_attrs))
			       | (TS_FACE (new_attrs)
				  ^ TS_FACE (continuous_attrs)));
      else
	TS_FACE (dst_attrs) = TS_FACE (new_attrs);
    }
  else
    TS_FACE (dst_attrs) = TS_FACE (orig_attrs);
  
  if (mode & addSize)
    TS_SIZE_X (dst_attrs) = CW (TS_SIZE (new_attrs) + TS_SIZE (orig_attrs));
  else if (mode & doSize)
    TS_SIZE_X (dst_attrs) = TS_SIZE_X (new_attrs);
  else
    TS_SIZE_X (dst_attrs) = TS_SIZE_X (orig_attrs);
  
  if (mode & doColor)
    TS_COLOR (dst_attrs) = TS_COLOR (new_attrs);
  else
    TS_COLOR (dst_attrs) = TS_COLOR (orig_attrs);

  return (orig_font != TS_FONT_X (dst_attrs)
	  || orig_size != TS_SIZE_X (dst_attrs));
}

/* return the index into `runs' that has a starting char `sel'.  if no
   such run exists, create it */

int16
make_style_run_at (TEStyleHandle te_style, int16 sel)
{
  int run_index;

  run_index = te_char_to_run_index (te_style, sel);
  
  {
    StyleRun *run = TE_STYLE_RUN (te_style, run_index);
    if (STYLE_RUN_START_CHAR (run) == sel)
      return run_index;
  }

  {
    StyleRun *runs;
    int n_runs;
    STHandle style_table;
    STElement *style;
    int style_index;
    
    /* split the current style into two */
    n_runs = TE_STYLE_N_RUNS (te_style) + 1;
    TE_STYLE_N_RUNS_X (te_style) = CW (n_runs);
    SetHandleSize ((Handle) te_style,
		   TE_STYLE_SIZE_FOR_N_RUNS (n_runs));
    runs = TE_STYLE_RUNS (te_style);
    memmove (&runs[run_index + 2],
	     &runs[run_index + 1],
	     (n_runs - run_index - 1) * sizeof *runs);
    
    style_index = STYLE_RUN_STYLE_INDEX (&runs[run_index]);
    
    /* we created a new run with this style index, update the
       reference count appropriately */
    style_table = TE_STYLE_STYLE_TABLE (te_style);
    style = ST_ELT (style_table, style_index);
    
    ST_ELT_COUNT_X (style) = CW (ST_ELT_COUNT (style) + 1);
    
    STYLE_RUN_START_CHAR_X (&runs[run_index + 1])  = CW (sel);
    STYLE_RUN_STYLE_INDEX_X (&runs[run_index + 1]) = CW (style_index);
    
    return run_index + 1;
  }
}

/* return the style index for the style that has the text attributes
   `attrs'.  if no such style exists, create it.

   assign `count_changed_p' to TRUE if no such style existed, and it
   was necessary to allocate a new style

   if `incr_count_p' this function increases the style reference on
   the returned style by one */
   
int16
get_style_index (TEStyleHandle te_style, TextStyle *attrs, int incr_count_p)
{
  /* these hold the swapped cached height, ascent for the style we are
     searching for */
  int16 cached_height = -1, cached_ascent = -1;
  int cache_filled_p = FALSE;
  STHandle style_table;
  STElement *st_elt;
  int st_i;
  int n_styles;

  n_styles = TE_STYLE_N_STYLES (te_style);
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  for (st_i = 0; st_i < n_styles; st_i ++)
    {
      st_elt = ST_ELT (style_table, st_i);
      
      if (TS_FONT_X (attrs) == ST_ELT_FONT_X (st_elt)
	  && TS_SIZE_X (attrs) == ST_ELT_SIZE_X (st_elt))
	{
	  if (TS_FACE (attrs) == ST_ELT_FACE (st_elt)
	      && (TS_COLOR (attrs).red == ST_ELT_COLOR (st_elt).red
		  && TS_COLOR (attrs).green == ST_ELT_COLOR (st_elt).green
		  && TS_COLOR (attrs).blue == ST_ELT_COLOR (st_elt).blue))
	    {
	      if (incr_count_p)
		ST_ELT_COUNT_X (st_elt) = CW (ST_ELT_COUNT (st_elt) + 1);
	      return st_i;
	    }
	  else if (!cache_filled_p)
	    {
	      cached_height = ST_ELT_HEIGHT_X (st_elt);
	      cached_ascent = ST_ELT_ASCENT_X (st_elt);
	      cache_filled_p = TRUE;
	    }
	}
    }

  /* a style not already in the style table was asked for.  create it */
  n_styles ++;
  TE_STYLE_N_STYLES_X (te_style) = CW (n_styles);
  SetHandleSize ((Handle) style_table,
		 STYLE_TABLE_SIZE_FOR_N_STYLES (n_styles));
  st_elt = ST_ELT (style_table, n_styles - 1);
  
  ST_ELT_COUNT_X (st_elt) = CWC (incr_count_p ? 1 : 0);
  
  ST_ELT_FONT_X (st_elt) = TS_FONT_X (attrs);
  ST_ELT_FACE (st_elt)   = TS_FACE (attrs);
  ST_ELT_SIZE_X (st_elt) = TS_SIZE_X (attrs);
  ST_ELT_COLOR (st_elt)  = TS_COLOR (attrs);

  if (cache_filled_p)
    {
      ST_ELT_HEIGHT_X (st_elt) = cached_height;
      ST_ELT_ASCENT_X (st_elt) = cached_ascent;
    }
  else
    generic_elt_calc_height_ascent (ST_ELT_TO_GENERIC_ELT (st_elt));

  return n_styles - 1;
}

/* decrease the reference count of style at `style_index' by one */

void
release_style_index (TEStyleHandle te_style, int16 style_index)
{
  STHandle style_table;
  STElement *st_elt;
  
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  st_elt = ST_ELT (style_table, style_index);
  gui_assert (ST_ELT_COUNT (st_elt) > 0);
  ST_ELT_COUNT_X (st_elt) = CW (ST_ELT_COUNT (st_elt) - 1);
}

/* `release_style_index ()' only decreases the reference count, so
   there may some styles with zero reference counts.  remove those
   styles from the style table, and update the style count
   appropriately */
  
void
stabilize_style_info (TEStyleHandle te_style)
{
  STHandle style_table;
  STElement *st_elt;
  /* map from original style indexes to new style indexes */
  int16 *index_map;
  int16 n_styles, n_runs;
  int i;
  
  n_styles = TE_STYLE_N_STYLES (te_style);
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  index_map = alloca (n_styles * sizeof *index_map);
  
  for (i = 0; i < n_styles; i ++)
    index_map[i] = CW (i);
  
  for (i = 0; i < n_styles; i ++)
    {
      st_elt = ST_ELT (style_table, i);
      
      if (! ST_ELT_COUNT_X (st_elt))
	{
	  /* make sure that the last style element in the table is
	     used */
	  for (;;)
	    {
	      STElement *last_st_elt;
	      
	      last_st_elt = ST_ELT (style_table, n_styles - 1);
	      if (ST_ELT_COUNT_X (last_st_elt))
		break;
	      
	      n_styles --;
	      
	      if (last_st_elt == st_elt)
		goto done;
	    }
	  
	  /* there are no runs with the style index `i', so give that
	     index to the last style in the style table, and shrink
	     the style table by 1 */
	  *ST_ELT (style_table, i) = *ST_ELT (style_table, n_styles - 1);
	  index_map[n_styles - 1] = CW (i);

	  /* so that we can verify, when we change the run style
	     indexes, that noone refers to this map index */
	  index_map[i] = CWC (-1);
	  
	  n_styles --;
	}
    }
 done:
  
  TE_STYLE_N_STYLES_X (te_style) = CW (n_styles);
  SetHandleSize ((Handle) style_table,
		 STYLE_TABLE_SIZE_FOR_N_STYLES (n_styles));
  
  n_runs = TE_STYLE_N_RUNS (te_style);
  
  /* now map over every run, and map the orig style index to
     it's new style index */
  for (i = 0; i < n_runs; i ++)
    {
      StyleRun *run;
      
      run = TE_STYLE_RUN (te_style, i);
      STYLE_RUN_STYLE_INDEX_X (run)
	= index_map[STYLE_RUN_STYLE_INDEX (run)];
      gui_assert (STYLE_RUN_STYLE_INDEX_X (run) != CWC (-1));
    }
}

static void
combine_run_with_next (TEStyleHandle te_style, int16 run_index)
{
  StyleRun *runs;
  int16 n_runs;
  STHandle style_table;
  STElement *style;
  int16 style_index;
  
  n_runs = TE_STYLE_N_RUNS (te_style);
  runs = TE_STYLE_RUNS (te_style);
  
  memmove (&runs[run_index + 1], &runs[run_index + 2],
	   (n_runs - run_index - 1) * sizeof *runs);
  
  n_runs --;
  TE_STYLE_N_RUNS_X (te_style) = CW (n_runs);
  SetHandleSize ((Handle) te_style,
		 TE_STYLE_SIZE_FOR_N_RUNS (n_runs));
  
  style_index = STYLE_RUN_STYLE_INDEX (&runs[run_index]);
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  style = ST_ELT (style_table, style_index);
  ST_ELT_COUNT_X (style) = CW (ST_ELT_COUNT (style) - 1);
}

void
te_style_combine_runs (TEStyleHandle te_style)
{
  int16 current_run_index, n_runs;
  
  /* remove any extra run fragmentation; if two adjacent runs are the
     same style, combine them */
  n_runs = TE_STYLE_N_RUNS (te_style);
  for (current_run_index = 0;
       current_run_index < n_runs - 1;)
    {
      StyleRun *current_run, *next_run;
      
      current_run = TE_STYLE_RUN (te_style, current_run_index);
      next_run = TE_STYLE_RUN (te_style, current_run_index + 1);
      
      if (STYLE_RUN_STYLE_INDEX (current_run) == STYLE_RUN_STYLE_INDEX (next_run))
	{
	  /* styles are the same, combine them */
	  combine_run_with_next (te_style, current_run_index);
	  
	  n_runs --;
	}
      else
	current_run_index ++;
    }
  
}

/* add the attributes specified by `mode' in `attrs' to the range of
   text between `start' and `end' in the text edit record `te'.  note
   mode may also include `addSize' */

static void
te_add_attrs_to_range (TEHandle te,
		       int16 start, int16 end,
		       TextStyle *attrs, int16 mode)
{
  TEStyleHandle te_style;
  STHandle style_table;
  int16 start_run_index, end_run_index;
  int16 current_run_index;
  
  TextStyle continuous_attrs;
  
  if (mode & doToggle)
    {
      int16 continuous_mode = CWC (doFace);
      
      TS_FACE (&continuous_attrs) = TS_FACE (attrs);
      TEContinuousStyle (&continuous_mode, &continuous_attrs, te);
    }
  
  te_style = TE_GET_STYLE (te);
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  
  /* make sure that a style run starts and `start' and that a style
     run starts at `end' */
  start_run_index = make_style_run_at (te_style, start);
  end_run_index = make_style_run_at (te_style, end);
  
  LOCK_HANDLE_EXCURSION_1
    (te_style,
     {
       StyleRun *runs = TE_STYLE_RUNS (te_style);
       
       /* now go through each style and make the appropriate change */
       for (current_run_index = start_run_index;
	    current_run_index < end_run_index;
	    current_run_index ++)
	 {
	   StyleRun *current_run;
	   TextStyle new_attrs;
	   STElement *orig_style;
	   int16 orig_style_index;
	   int16 new_style_index;
	   
	   current_run = &runs[current_run_index];
	   orig_style_index = STYLE_RUN_STYLE_INDEX (current_run);
	   orig_style = ST_ELT (style_table, orig_style_index);
	   
	   adjust_attrs (ST_ELT_TO_ATTR (orig_style), attrs, &new_attrs,
			 &continuous_attrs, mode);
	   
	   /* `current_run' valid over call to `get_style_index ()'
	      since `te_style' is locked */
	   new_style_index = get_style_index (te_style, &new_attrs, TRUE);
	   release_style_index (te_style, orig_style_index);
	   
	   STYLE_RUN_STYLE_INDEX_X (current_run) = CW (new_style_index);
	 }
     });
  
  stabilize_style_info (te_style);
  te_style_combine_runs (te_style);
}

P2 (PUBLIC pascal trap, TEHandle, TEStylNew, Rect *, dst, Rect *, view)
{
  FontInfo font_info;
  int16 font_height;
  TEHandle teh;
  TEStyleHandle te_style;
  StScrpHandle stsh;
  ScrpSTElement *stp;
  LHHandle lh_table;
  LHPtr lh;
  NullSTHandle tempnullsth;
  STHandle style_table;
  
  teh = TENew (dst, view);
  
  HASSIGN_3
    (teh,
     lineHeight, CWC (-1),
     fontAscent, CWC (-1),
     txSize, CWC (-1));
  
  te_style = (TEStyleHandle) NewHandle (TE_STYLE_SIZE_FOR_N_RUNS (1));
  
  HxX (te_style, nRuns) = CWC (1);
  HxX (te_style, runs[0].startChar)  = CWC (0);
  HxX (te_style, runs[0].styleIndex) = CWC (0);
  HxX (te_style, runs[1].startChar)  = CWC (1);
  HxX (te_style, runs[1].styleIndex) = CWC (-1);
  
  HxX (te_style, nStyles) = CWC (1);
  
  /* font info used to fill in the fisrt style element */
  GetFontInfo (&font_info);
  font_height = (CW (font_info.ascent)
		 + CW (font_info.descent)
		 + CW (font_info.leading));
  style_table = (STHandle) NewHandle (sizeof (STElement));
  HASSIGN_7
    (style_table,
     stCount, CWC (1),
     stFont, PORT_TX_FONT_X (thePort),
     stFace, PORT_TX_FACE (thePort),
     stSize, PORT_TX_SIZE_X (thePort),
     stColor, ROMlib_black_rgb_color,
     stHeight, CW (font_height),
     stAscent, font_info.ascent);
  
  TE_STYLE_STYLE_TABLE_X (te_style) = RM (style_table);
  
  lh_table = (LHHandle) NewHandle (sizeof (LHElement));
  lh = STARH (lh_table);
  LH_HEIGHT_X (lh) = font_height;
  LH_ASCENT_X (lh) = font_info.ascent;
  
  TE_STYLE_LH_TABLE_X (te_style) = RM (lh_table);
  
  HxX(te_style, teRefCon) = 0;
  
  tempnullsth = (NullSTHandle) NewHandle(sizeof(NullSTRec));
  HxX (te_style, nullStyle) = RM(tempnullsth);
  stsh = (StScrpHandle) NewHandle(sizeof(StScrpRec));
  HxX (tempnullsth, nullScrap) = (StScrpHandle) RM(stsh);
  HxX (tempnullsth, TEReserved) = CLC(0);
  HxX (stsh, scrpNStyles) = CWC(0);

  stp = HxX(stsh, scrpStyleTab);
  stp->scrpFont = PORT_TX_FONT_X (thePort);
  stp->scrpFace = PORT_TX_FACE_X (thePort);
  stp->scrpSize = PORT_TX_SIZE_X (thePort);
  stp->scrpColor.red = 0;		/* black ? */
  stp->scrpColor.green = 0;		/* black ? */
  stp->scrpColor.blue = 0;		/* black ? */
  stp->scrpStartChar = CLC(0);
  
  stp->scrpHeight = CW (font_height);
  stp->scrpAscent = font_info.ascent;
  
  SetStylHandle (te_style, teh);
  
  TE_SLAM (teh);
  
  return teh;
}

P2(PUBLIC pascal trap, void, SetStylHandle, TEStyleHandle, theHandle,
								 TEHandle, teh)
{
  if (!TE_STYLIZED_P (teh))
      return;
  *(TEStyleHandle *) &HxX (teh, txFont) = RM (theHandle);
}

P1 (PUBLIC pascal trap, TEStyleHandle, GetStylHandle, TEHandle, teh)
{
  return TE_GET_STYLE (teh);
}

P1 (PUBLIC pascal trap, StScrpHandle, GetStylScrap, TEHandle, te)
{
  StScrpHandle scrap;
  StyleRun *runs;
  TEStyleHandle te_style;
  STHandle style_table;
  int16 start, end, length;
  int16 start_run_index, end_run_index;
  int16 scrap_n_styles;
  int i;
  
  TE_SLAM (te);
  
  start = TE_SEL_START (te); 
  end = TE_SEL_END (te);
  length = TE_LENGTH (te);
  if (!TE_STYLIZED_P (te))
    return NULL;

  if (end < start)
    {
      warning_unexpected ("end < start");
      end = start;
    }

  te_style = TE_GET_STYLE (te);
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  if (end > length)
    end = length;
  
  start_run_index = make_style_run_at (te_style, start);
  end_run_index = make_style_run_at (te_style, end);
  
  scrap_n_styles = MAX (end_run_index - start_run_index, 1);
  scrap = (StScrpHandle) NewHandle (SCRAP_SIZE_FOR_N_STYLES (scrap_n_styles));
  SCRAP_N_STYLES_X (scrap) = CW (scrap_n_styles);
  
  if (start == end)
    warning_unimplemented ("should check null scrap, first");

  runs = TE_STYLE_RUNS (te_style);
  
  for (i = 0; i < scrap_n_styles; i ++)
    {
      ScrpSTElement *scrap_elt;
      StyleRun *current_run;
      STElement *style;
      
      current_run = &runs[start_run_index + i];
      style = ST_ELT (style_table, RUN_STYLE_INDEX (current_run));
      scrap_elt = SCRAP_ST_ELT (scrap, i);
      
      generic_elt_copy (SCRAP_ELT_TO_GENERIC_ELT (scrap_elt),
			ST_ELT_TO_GENERIC_ELT (style));
      SCRAP_ELT_START_CHAR_X (scrap_elt)
	= CL (RUN_START_CHAR (current_run) - start);
    }
  te_style_combine_runs (te_style);
  
  return scrap;
}

P4 (PUBLIC pascal trap, void, TEStylInsert, Ptr, text, LONGINT, length,
    StScrpHandle, hST, TEHandle, te)
{
  TE_SLAM (te);
  ROMlib_tedoitall (te, text, length, TRUE, hST);
  TE_SLAM (te);
}

P2 (PUBLIC pascal trap, INTEGER, TEGetOffset, Point, pt, TEHandle, te)
{
  int16 retval;
  Point sp;
  
  sp = TE_SEL_POINT (te);
  TE_SEL_POINT (te).h = CW (pt.h);
  TE_SEL_POINT (te).v = CW (pt.v);
  retval = TE_DO_TEXT (te, 0, TE_LENGTH (te), teFind);
  TE_SEL_POINT (te) = sp;
  
  return retval;
}

P2 (PUBLIC pascal trap, LONGINT, TEGetPoint, INTEGER, offset, TEHandle, teh)
{
  Point p;
  int ascent;
  
  TE_CHAR_TO_POINT (teh, offset, &p);
  LOCK_HANDLE_EXCURSION_1
    (teh,
     {
       TEPtr tep = STARH (teh);
       int lineno;
       
       lineno = TEP_CHAR_TO_LINENO (tep, offset);
       ascent = TEP_ASCENT_FOR_LINE (tep, lineno);
     });

  return ((int32) (p.v + ascent) << 16) + (int32) p.h;
}


P3 (PUBLIC pascal trap, int32, TEGetHeight,
    LONGINT, endLine, LONGINT, startLine, TEHandle, teh)
{
  int32 retval;
  
  if (startLine > 0)
    startLine --;
  else
    startLine = 0;

  endLine = MIN (TE_N_LINES (teh), endLine);
  if (endLine < 0)
    endLine = 0;
  else if (endLine > 0)
    endLine --;
  
  /* ### 1. write a `swap' macro, that should go into `rsys/macros.h',
     and 2. pin start and end by `TE_N_LINES ()' */
  if (startLine > endLine)
    {
      uint32 temp;
      
      temp = startLine;
      startLine = endLine;
      endLine = temp;
    }
  
  if (TE_STYLIZED_P (teh))
    {
      TEStyleHandle te_style;
      LHElement *l, *le;
      
      retval = 0;
      
      te_style = TE_GET_STYLE (teh);
      
      l = STARH (MR (STARH (te_style)->lhTab)) + startLine;
      le = l + endLine - startLine;
      for ( ; l <= le ; l++)
	retval += CW (l->lhHeight);
    }
  else
    retval = TE_LINE_HEIGHT (teh) * (endLine - startLine + 1);
  
  return retval;
}

P5 (PUBLIC pascal trap, void, TEGetStyle, int16, sel,
    TextStyle *, attrs, int16 *, line_height, int16 *, font_ascent,
    TEHandle, te)
{
  if (TE_STYLIZED_P (te))
    {
      TEStyleHandle te_style;
      STElement *style;
      StyleRun *runs;
      int run_index;
      
      te_style = TE_GET_STYLE (te);
      runs = TE_STYLE_RUNS (te_style);
      run_index = te_char_to_run_index (te_style, sel);
      style = ST_ELT (TE_STYLE_STYLE_TABLE (te_style),
		      RUN_STYLE_INDEX (&runs[run_index]));
      
      TS_FONT_X (attrs) = ST_ELT_FONT_X (style);
      TS_FACE (attrs)   = ST_ELT_FACE (style);
      TS_SIZE_X (attrs) = ST_ELT_SIZE_X (style);
      TS_COLOR (attrs)   = ST_ELT_COLOR (style);
      
      *line_height = ST_ELT_HEIGHT_X (style);
      *font_ascent = ST_ELT_ASCENT_X (style);
    }
  else
    {
      TS_FONT_X (attrs) = TE_TX_FONT_X (te);
      TS_FACE (attrs)   = TE_TX_FACE (te);
      TS_SIZE_X (attrs) = TE_TX_SIZE_X (te);
      TS_COLOR (attrs) = ROMlib_black_rgb_color;
      *line_height = TE_LINE_HEIGHT_X (te);
      *font_ascent = TE_FONT_ASCENT_X (te);
    }
}

P1 (PUBLIC pascal trap, void, TEStylPaste, TEHandle, te)
{
  Handle hText;
  int32 dummy;
  /* length of the scrap text */
  int16 length, retval;
  StScrpHandle scrap;
  

  hText = NewHandle (1);
  length = GetScrap (hText, TICK ("TEXT"), &dummy);
  if (length < 0)
    {
      /* error, there is no scrap element */
      DisposHandle (hText);
      
      /* remove the selected text */
      ROMlib_tedoitall (te, NULL, 0, FALSE, NULL);
      return;
    }
  
  scrap = (StScrpHandle) NewHandle (sizeof (StScrpRec));
  retval = GetScrap ((Handle) scrap, TICK ("styl"), &dummy);
  if (retval < 0)
    {
      DisposHandle ((Handle) scrap);
      scrap = NULL;
    }
  
  LOCK_HANDLE_EXCURSION_1
    (hText,
     {
       ROMlib_tedoitall (te, STARH (hText), length, FALSE, scrap);
     });
  if (scrap)
    DisposHandle ((Handle) scrap);
  DisposHandle (hText);
}

static void
te_do_redraw (TEHandle te)
{
  int16 cal_start, cal_end, sel_start, sel_end;
  
  TESAVE (te);
  
  sel_start = TE_SEL_START (te);
  sel_end = TE_SEL_END (te);
  
  ROMlib_caltext (te, sel_start, 0, &cal_start, &cal_end);
  if (cal_start > sel_start)
    cal_start = sel_start;
  if (cal_end < sel_end)
    cal_end = sel_end;
  
  TE_DO_TEXT (te, cal_start, cal_end, teDraw);
  TE_DO_TEXT (te, sel_start, sel_end, teHilite);
  
  TERESTORE ();
}

P4 (PUBLIC pascal trap, void, TESetStyle, int16, mode, TextStyle *, new_attrs,
    BOOLEAN, redraw, TEHandle, te)
{
  int16 start, end;
  
  if (!TE_STYLIZED_P (te))
    return;
  
  TE_SLAM (te);
  
  start = TE_SEL_START (te);
  end = TE_SEL_END (te);
  
  if (start == end)
    {
      TEStyleHandle te_style;
      StScrpHandle null_scrap;
      ScrpSTElement *scrap_st_elt;

      /* store new attributes into the null scrap style */
      
      te_style = TE_GET_STYLE (te);
      null_scrap = TE_STYLE_NULL_SCRAP (te_style);
      scrap_st_elt = SCRAP_ST_ELT (null_scrap, 0);
      if (!SCRAP_N_STYLES (null_scrap))
	{
	  STHandle style_table;	  
	  StyleRun *runs;
	  int start_run_index, start_style_index;
	  
	  style_table = TE_STYLE_STYLE_TABLE (te_style);
	  runs = TE_STYLE_RUNS (te_style);
	  start_run_index = te_char_to_run_index (te_style, start);
	  start_style_index = RUN_STYLE_INDEX (&runs[start_run_index]);
	  
	  /* ### if the scrap has no styles, the old TESetStyle code
	     copied the style from the insertion point.  although this
	     seems reasonable, i haven't been able to find this
	     documented anywhere */
	  generic_elt_copy (ST_ELT_TO_GENERIC_ELT (ST_ELT (style_table,
							   start_style_index)),
			    SCRAP_ELT_TO_GENERIC_ELT (scrap_st_elt));
	  
	  SCRAP_N_STYLES_X (null_scrap) = CWC (1);
	}
      if (adjust_attrs (SCRAP_ELT_TO_ATTR (scrap_st_elt), new_attrs,
			SCRAP_ELT_TO_ATTR (scrap_st_elt),
			SCRAP_ELT_TO_ATTR (scrap_st_elt), mode))
	generic_elt_calc_height_ascent
	  (SCRAP_ELT_TO_GENERIC_ELT (scrap_st_elt));
      
      return;
    }
  
  te_add_attrs_to_range (te, start, end, new_attrs, mode);

  if (redraw)
    te_do_redraw (te);
  
  TE_SLAM (te);
}

P5 (PUBLIC pascal trap, void, TEReplaceStyle, int16, mode,
    TextStyle *, attrs_to_replace, TextStyle *, replacement_attrs, BOOLEAN, redraw,
    TEHandle, te)
{
  TEStyleHandle te_style;
  SignedByte te_style_flags;
  int16 sel_start, sel_end;
  int16 start_run_index, end_run_index;
  STHandle style_table;
  StyleRun *runs;
  int16 run_i;
  
  TE_SLAM (te);
  
  sel_start = TE_SEL_START (te);
  sel_end = TE_SEL_END (te);
  if (! TE_STYLIZED_P (te)
      || sel_start == sel_end)
    return;
  
  te_style = TE_GET_STYLE(te);

  /* create the new runs before locking `te_style' */
  start_run_index = make_style_run_at (te_style, sel_start);
  end_run_index = make_style_run_at (te_style, sel_end);
  
  te_style_flags = HGetState ((Handle) te_style);
  HLock ((Handle) te_style);
  
  runs = TE_STYLE_RUNS (te_style);
  
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  
  for (run_i = start_run_index; run_i < end_run_index; run_i ++)
    {
      StyleRun *run;
      int16 orig_style_index;
      STElement *style;
      
      run = &runs[run_i];
      orig_style_index = RUN_STYLE_INDEX (run);
      style = ST_ELT (style_table, orig_style_index);
      
      /* ### from the IM documentation, it isn't clear if
	 only those attributes specified by `mode' need to match,
	 or if all attributes need to match */
      
      if ((! (mode & doFont)
	   || ST_ELT_FONT_X (style) == TS_FONT_X (attrs_to_replace))
	  && (! (mode & doFace)
	      || ST_ELT_FACE (style) == TS_FACE (attrs_to_replace))
	  && (! (mode & (doSize | addSize))
	      || ST_ELT_SIZE_X (style) == TS_SIZE_X (attrs_to_replace))
	  && (! (mode & doColor)
	      || !memcmp (&ST_ELT_COLOR (style),
			  &TS_COLOR (attrs_to_replace),
			  sizeof (RGBColor))))
	{
	  int16 new_style_index;
	  
	  TextStyle *new_attrs = alloca (sizeof *new_attrs);
	  
	  adjust_attrs (ST_ELT_TO_ATTR (style),	replacement_attrs, new_attrs,
			NULL, mode);

	  /* `get_style_index' may resize `style_table', so `style'
	     is no longer valid */
	  new_style_index = get_style_index (te_style, new_attrs, TRUE);
	  release_style_index (te_style, orig_style_index);
	  
	  RUN_STYLE_INDEX_X (run) = CW (new_style_index);
	}
    }
  HSetState ((Handle) te_style, te_style_flags);
  
  stabilize_style_info (te_style);
  te_style_combine_runs (te_style);
  
  if (redraw)
    te_do_redraw (te);

  TE_SLAM (te);
}

P3 (PUBLIC pascal trap, BOOLEAN, TEContinuousStyle, INTEGER *, modep,
    TextStyle *, ts_out, TEHandle, teh)
{
  int16 sel_start, sel_end;
  TEStyleHandle te_style;
  STHandle style_table;
  STElement *style;
  int style_index = -1;
  int run_i;
  int16 orig_mode;

  if (!TE_STYLIZED_P (teh))
    {
      warning_unimplemented (NULL_STRING);
      if (*modep & CWC (doFont))
	TS_FONT_X (ts_out) = PORT_TX_FONT_X (thePort);
      if (*modep & CWC (doFace))
	TS_FACE (ts_out) = PORT_TX_FACE_X (thePort);
      if (*modep & CWC (doSize))
	TS_SIZE_X (ts_out) = PORT_TX_SIZE_X (thePort);
      if (*modep & CWC (doColor))
	TS_COLOR (ts_out) = ROMlib_black_rgb_color;
      return TRUE;
    }

  /* just slam on entry, this function doesn't modify `teh' */
  TE_SLAM (teh);
  
  te_style = TE_GET_STYLE (teh);
  style_table = TE_STYLE_STYLE_TABLE (te_style);
  
  sel_start = TE_SEL_START (teh);
  sel_end = TE_SEL_END (teh);
  
  orig_mode = *modep;

  /* selection is `insertion point' */
  if (sel_start > sel_end)
    {
      warning_unexpected ("start = %d end = %d", sel_start, sel_end);
      sel_start = sel_end;
    }
  if (sel_start == sel_end)
    {
      TextStyle *attr;
      StScrpHandle null_scrap;
      int insertion_point;
      
      null_scrap = TE_STYLE_NULL_SCRAP (te_style);
      if (SCRAP_N_STYLES_X (null_scrap))
	{
	  ScrpSTElement *scrap_elt;
	  
	  scrap_elt = SCRAP_ST_ELT (null_scrap, 0);
	  attr = SCRAP_ELT_TO_ATTR (scrap_elt);
	}
      else
	{
	  /* get the style of the character preceeding the insertion
	     point; in this case we always return `TRUE', since a single
	     character is contiguous */
	  insertion_point = sel_start;
	  for (run_i = TE_STYLE_N_RUNS (te_style) - 1; run_i >= 0; run_i --)
	    {
	      StyleRun *run;
	      
	      run = TE_STYLE_RUN (te_style, run_i);
	      if (STYLE_RUN_START_CHAR (run) <= MAX (insertion_point - 1, 0))
		{
		  style_index = STYLE_RUN_STYLE_INDEX (run);
		  break;
		}
	    }
	  /* warning, this pulls out a pointer into an unlocked handle! */
	  attr = ST_ELT_TO_ATTR (ST_ELT (style_table, style_index));
	}
      
      if (*modep & CWC (doFont))
	TS_FONT_X (ts_out) = TS_FONT_X (attr);
      if (*modep & CWC (doFace))
	TS_FACE (ts_out) = TS_FACE (attr);
      if (*modep & CWC (doSize))
	TS_SIZE_X (ts_out) = TS_SIZE_X (attr);
      if (*modep & CWC (doColor))
	TS_COLOR (ts_out) = TS_COLOR (attr);
      return TRUE;
    }
  else
    {
      StyleRun *run = NULL;
      int16 font = 0;
      Style face = 0;
      int16 size = 0;
      RGBColor color;
      
      /* locate the starting run */
      for (run_i = TE_STYLE_N_RUNS (te_style) - 1; run_i >= 0; run_i --)
	{
	  run = TE_STYLE_RUN (te_style, run_i);
	  if (STYLE_RUN_START_CHAR (run) < sel_end)
	    {
	      style_index = STYLE_RUN_STYLE_INDEX (run);
	      break;
	    }
	}
      
      /* must have a style */
      gui_assert (style_index > -1
		  && style_index < TE_STYLE_N_STYLES (te_style));
      style = ST_ELT (style_table, style_index);
      if (*modep & CWC (doFont))
	font = ST_ELT_FONT_X (style);
      if (*modep & CWC (doFace))
	face = ST_ELT_FACE (style);
      if (*modep & CWC (doSize))
	size = ST_ELT_SIZE_X (style);
      if (*modep & CWC (doColor))
	color = ST_ELT_COLOR (style);
      
      for (; run_i >= 0
	     && sel_start < STYLE_RUN_START_CHAR (run); run_i --)
	{
	  run = TE_STYLE_RUN (te_style, run_i);
	  style_index = STYLE_RUN_STYLE_INDEX (run);
	  gui_assert (style_index < TE_STYLE_N_STYLES (te_style));
	  style = ST_ELT (style_table, style_index);
	  if (*modep & CWC (doFont)
	      && font != ST_ELT_FONT_X (style))
	    *modep &= ~CWC (doFont);
	  
	  face &= ST_ELT_FACE (style);
	  if (*modep & CWC (doFace)
	      && !face)
	    {
	      *modep &= ~CWC (doFace);
	      TS_FACE (ts_out) = face;
	    }
	  
	  if (*modep & CWC (doSize)
	      && size != ST_ELT_SIZE_X (style))
	    *modep &= ~CWC (doSize);
	  if (*modep & CWC (doColor)
	      && !!memcmp (&color, &ST_ELT_COLOR (style), sizeof color))
	    *modep &= ~CWC (doColor);
	}
      
      if (*modep & CWC (doFont))
	TS_FONT_X (ts_out) = font;
      if (*modep & CWC (doFace))
	TS_FACE (ts_out) = face;
      if (*modep & CWC (doSize))
	TS_SIZE_X (ts_out) = size;
      if (*modep & CWC (doColor))
	TS_COLOR (ts_out) = color;
      
      return orig_mode == *modep;
    }
}

P5 (PUBLIC pascal trap, void, SetStylScrap, int32, start, int32, stop,
    StScrpHandle, newstyles, BOOLEAN, redraw, TEHandle, teh)
{
  ROMlib_hook (te_notsupported);
  warning_unimplemented (NULL_STRING);
}

P3 (PUBLIC pascal trap, void, TECustomHook, int16, sel, HIDDEN_ProcPtr *,
    addr, TEHandle, te)
{
  ROMlib_hook (te_notsupported);
  warning_unimplemented (NULL_STRING);
}

P3 (PUBLIC pascal trap, LONGINT, TENumStyles, int32, start, int32, stop,
    TEHandle, te)
{
  ROMlib_hook (te_notsupported);
  warning_unimplemented (NULL_STRING);
  return 0;
}
