/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: Selection.c /main/22 1996/11/12 11:44:48 cde-hp $ */
/************************************<+>*************************************
 ****************************************************************************
 **
 **   File:     Selection.c
 **
 **   Project:  Cde DtHelp
 **
 **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
 **
 **  (c) Copyright 1993, 1994 Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Novell, Inc.
 **
 **
 ****************************************************************************
 ************************************<+>*************************************/

/*
 * system includes
 */
#include <stdlib.h>
#include <string.h>

/*
 * Canvas Engine includes
 */
#include "CanvasP.h"
#include "CanvasSegP.h"

/*
 * private includes
 */
#include "CanvasI.h"
#include "CvStringI.h"
#include "LayoutUtilI.h"
#include "SelectionI.h"
#include "VirtFuncsI.h"

#ifdef NLS16
#endif

/********    Private Function Declarations    ********/
static	void	AdjustSelection (
			_DtCanvasStruct	*canvas,
			_DtCvSelectData	 next);
static	int	GetSelectedText(
			_DtCanvasStruct	*canvas,
			_DtCvSelectData	 start,
			_DtCvSelectData	 end,
			unsigned int         mask,
			_DtCvPointer	*ret_data);
/********    End Private Function Declarations    ********/

/********    Private Defines                 ********/
#define	GROW_SIZE	5
/********    End Private Defines             ********/

/********    Macros                          ********/
#define	Equal(a,b)		(a.y == b.y && a.x == b.x)
#define	LessThan(a,b)		((a.y < b.y) || (a.y == b.y && a.x < b.x))
#define	GreaterThan(a,b)	((a.y > b.y) || (a.y == b.y && a.x > b.x))
#define	LessThanEq(a,b)		(Equal(a,b) || LessThan(a,b))
#define	GreaterThanEq(a,b)	(Equal(a,b) || GreaterThan(a,b))

#define	InRegion(top,bot,min,max)	((min) <= (bot) && (top) <= (max))

/********    End Macros                      ********/

/********    Private Variable Declarations    ********/
static	const _DtCvSelectData	defaultSelect = { -1, -1, -1, -1};

/********    End Private Variable Declarations    ********/

/******************************************************************************
 *                             Private Functions
 ******************************************************************************/
/******************************************************************************
 * Function: StartXOfLine
 *
 * Purpose: Determine the start of a line, takes into consideration
 *          the traversal and link before values. The 'x' returned is
 *          exactly where the text/graphic is to be placed on the canvas.
 *****************************************************************************/
static _DtCvUnit
StartXOfLine(
    _DtCanvasStruct	*canvas,
    _DtCvDspLine	 line)
{
    _DtCvValue	 lastLinkVisible = FALSE;
    int		 lnkInd = -1;
    _DtCvUnit	 xPos;
    _DtCvSegmentI   *pSeg;

    xPos = _DtCvGetStartXOfLine(&line, &pSeg);
    return (_DtCvAdvanceXOfLine( canvas, pSeg, xPos,
			&lnkInd, &lastLinkVisible));

} /* End StartXOfLine */

/*****************************************************************************
 * Function: SearchForClosestLine
 *
 * Purpose:  Initializes the 'new' structure with information indicating
 *	     what line is closest to the target_y.
 *		next->y         Set to target_y if no line straddles it.
 *				Otherwise, it will be set to the minimum
 *				y of all lines straddling the target_y.
 *		next->x         Set to target_x if no line straddles
 *				target_y or if target_x is before the
 *				first line straddling target_y.
 *		next->line_idx  Set to -1 if no line straddles target_y.
 *				Otherwise, set to the first line that
 *				straddles target_x or is the minimum x
 *				that is greater than target_x of all the
 *				lines straddling target_x.
 *		next->char_idx  Set to -1 if no straddles target_y.
 *				Otherwise, set to the character that
 *				resides at target_x if target_x is in
 *				the middle of the line.  Set to zero if
 *				target_x is before the line, and set to
 *				the line count if target_x is after the
 *				line.
 *
 *****************************************************************************/
static	void
SearchForClosestLine (
    _DtCanvasStruct	*canvas,
    _DtCvUnit		 target_x,
    _DtCvUnit		 target_y,
    _DtCvSelectData *next)
{
    int		 i;
    int		 maxI;
    _DtCvUnit	 lineY;
    _DtCvUnit	 endX;
    _DtCvUnit	 begX;
    _DtCvUnit	 maxX = -1;
    _DtCvDspLine *lines = canvas->txt_lst;

    *next = defaultSelect;
    for (i = 0; i < canvas->txt_cnt; i++)
      {
	/*
	 * get the maximum y of the line
	 * if it straddles the target y, process it.
	 */
	lineY = lines[i].baseline + lines[i].descent;
	if (_DtCvStraddlesPt(target_y,lines[i].baseline-lines[i].ascent,lineY))
	  {
	    /*
	     * Is this the minimum y of all the maximum y values of the
	     * line straddling the target y?
	     */
	    if (next->y  == -1 || next->y > lineY)
	        next->y = lineY;

	    /*
	     * Get the maximum X position of the line.
	     * If this is the maximum X of all the lines straddling
	     * the target y, remember it.
	     */
	    endX = canvas->txt_lst[i].max_x;
	    if (maxX < endX && endX < target_x)
	      {
		maxX = endX;
		maxI = i;
	      }

	    /*
	     * Does this line straddle the x?
	     */
	    begX = StartXOfLine(canvas, lines[i]);
	    if (_DtCvStraddlesPt(target_x, begX, endX))
	      {
		next->line_idx = i;
		next->char_idx = _DtCvGetCharIdx(canvas,lines[i],target_x);
	      }
	  }
      }

    /*
     * remember what the target x was for this line.  If the target x is
     * less than the start of the line, then the selection process will
     * highlight the space before the line.  If its in the middle, it
     * will just highlight starting at the character.  If it's after the
     * end, the rest will be cut off at the end of the line.
     */
    next->x = target_x;

    /*
     * If we found a line straddling the target y, but it does not
     * straddle the target_x, check max x for the correct info.
     */
    if (next->line_idx == -1 && maxX > -1)
      {
	next->line_idx = maxI;
	next->char_idx = lines[maxI].length;
      }

    /*
     * didn't find a line straddling the target_y, set y.
     */
    if (next->y == -1)
        next->y = target_y;
}

/*****************************************************************************
 * Function: MarkLinesOutsideBoundary
 *
 *****************************************************************************/
static	void
MarkLinesOutsideBoundary (
    _DtCanvasStruct	*canvas,
    _DtCvUnit		 top_y,
    _DtCvUnit		 top_x,
    _DtCvUnit		 bot_y,
    _DtCvUnit		 bot_x)
{
    int       i;
    _DtCvUnit      maxY;
    _DtCvUnit      minY;
    _DtCvDspLine  *lines = canvas->txt_lst;

    for (i = 0; i < canvas->txt_cnt; i++)
      {
        maxY = lines[i].baseline + lines[i].descent;
	minY = lines[i].baseline - lines[i].ascent;

	/*
	 * is this line outside the boundary?
	 * If so, mark it so it's not processed.
	 */
        if (maxY < top_y || minY > bot_y )
	    _DtCvSetProcessed(lines[i]);

	else
	  {
	    /*
	     * does it straddle the top?
	     */
	    if (_DtCvStraddlesPt(top_y, minY, maxY))
	      {
	        /*
	         * Does it begin before the selection?
	         * If so, mark it so it's not processed.
	         */
	        if (canvas->txt_lst[i].max_x <= top_x)
		    _DtCvSetProcessed(lines[i]);
	      }
	
	    /*
	     * does it straddle the bottom?
	     */
	    if (_DtCvStraddlesPt(bot_y, minY, maxY))
	      {
	        /*
	         * Does it start after the selection?
	         * If so, mark it so it's not processed.
	         */
	        if (StartXOfLine(canvas, lines[i]) >= bot_x)
		    _DtCvSetProcessed(lines[i]);
	      }
          }
      }
}

/*****************************************************************************
 * Function: AdjustSelection
 *
 *****************************************************************************/
static	void
AdjustSelection (
    _DtCanvasStruct	*canvas,
    _DtCvSelectData next)
{
    _DtCvSelectData  start = canvas->select_start;
    _DtCvSelectData  end   = canvas->select_end;

    if (!(Equal(next, end)))
      {
	if (next.line_idx != -1 && next.line_idx == canvas->select_end.line_idx
		&&
	    next.char_idx != -1 && next.char_idx == canvas->select_end.char_idx)
	    return;

	if (GreaterThan(next, end))
	  {
	    if (LessThanEq(start, end))
		_DtCvDrawAreaWithFlags (canvas, end, next,
						0, _DtCvSELECTED_FLAG,
						_DtCvBAD_TYPE, NULL);

	    else if (GreaterThanEq(start, next))
		_DtCvDrawAreaWithFlags (canvas, end, next,
						_DtCvSELECTED_FLAG, 0,
						_DtCvBAD_TYPE, NULL);

	    else /* end < start < next */
	      {
		_DtCvDrawAreaWithFlags (canvas, end  , start,
						_DtCvSELECTED_FLAG, 0,
						_DtCvBAD_TYPE, NULL);
		_DtCvDrawAreaWithFlags (canvas, start, next ,
						0, _DtCvSELECTED_FLAG,
						_DtCvBAD_TYPE, NULL);
	      }
	  }
	else /* if (next < end) */
	  {
	    if (LessThanEq(start, next))
		_DtCvDrawAreaWithFlags (canvas, next, end,
						_DtCvSELECTED_FLAG, 0,
						_DtCvBAD_TYPE, NULL);

	    else if (GreaterThanEq(start, end))
		_DtCvDrawAreaWithFlags (canvas, next, end,
						0, _DtCvSELECTED_FLAG,
						_DtCvBAD_TYPE, NULL);

	    else /* next < start < end */
	      {
		_DtCvDrawAreaWithFlags (canvas, start, end  ,
						_DtCvSELECTED_FLAG, 0,
						_DtCvBAD_TYPE, NULL);
		_DtCvDrawAreaWithFlags (canvas, next , start,
						0, _DtCvSELECTED_FLAG,
						_DtCvBAD_TYPE, NULL);
	      }
	  }
      }

    canvas->select_end = next;
}

/*****************************************************************************
 * Function: SkipOtherLines
 *
 *****************************************************************************/
static void
SkipOtherLines(
    _DtCvDspLine *lines,
    int		 max_cnt,
    int		 idx,
    _DtCvUnit	 target_y,
    int		*ret_idx)
{
    while (idx < max_cnt && _DtCvIsNotProcessed(lines[idx]) &&
			lines[idx].baseline - lines[idx].ascent > target_y)
        idx++;

    *ret_idx = idx;
}

/*****************************************************************************
 * Function: CheckAndSwitchPoints
 *
 *****************************************************************************/
static int
CheckAndSwitchPoints(
    _DtCvSelectData *pt1,
    _DtCvSelectData *pt2)
{
    _DtCvSelectData temp;

    if (pt1->y > pt2->y || (pt1->y == pt2->y && pt1->x >  pt2->x))
      {
	temp = *pt2;
	*pt2 = *pt1;
	*pt1 = temp;
      }
}

/*****************************************************************************
 * Function: AddSegmentToData
 *
 *****************************************************************************/
static _DtCvUnit
AddSegmentToData(
    _DtCanvasStruct	*canvas,
    unsigned int	 mask,
    _DtCvUnit		 start_x,
    int			 line_idx,
    int			 char_idx,
    int			 copy_cnt,
    _DtCvFlags		 end_flag,
    _DtCvUnit		*ret_y,
    _DtCvPointer	*ret_data)
{
    _DtCvDspLine line   = canvas->txt_lst[line_idx];
    int		 result = _DtCvSTATUS_OK;
    int		 count  = line.length;
    int		 start  = line.byte_index;
    int		 lnkInd = -1;
    int		 cnt;
    int		 len;
    _DtCvUnit	 segWidth;
    _DtCvUnit	 xPos  = line.text_x;
    void 	*pChar;
    _DtCvSegmentI	*pSeg  = line.seg_ptr;
    _DtCvFlags	 flag  = 0;
    _DtCvValue	 done  = False;
    _DtCvValue	 lastLinkVisible = FALSE;
    _DtCvStringInfo strInfo;

    xPos = _DtCvGetStartXOfLine(&line, &pSeg);

    while (done == False && char_idx)
      {
	/*
	 * advance past the link and traversal info
	 */
	xPos = _DtCvAdvanceXOfLine(canvas, pSeg, xPos,
				&lnkInd, &lastLinkVisible);

        /*
         * advance the pointer by the width
         */
        _DtCvGetWidthOfSegment(canvas, pSeg, start, count,
							&cnt, &segWidth, NULL);
	if (cnt < char_idx)
	  {
	    xPos     += segWidth;
	    pSeg      = pSeg->next_disp;
	    count    -= cnt;
	    char_idx -= cnt;
	    start     = 0;
	  }
        else 
          {
            _DtCvGetWidthOfSegment(canvas, pSeg, start, char_idx,
							&cnt, &segWidth, NULL);
	    xPos  += segWidth;
	    start += cnt;
	    count -= cnt;
            done = True;
          }
      }

    if (start_x > xPos)
	start_x = xPos;

    while (_DtCvSTATUS_OK == result && pSeg != NULL && copy_cnt > 0)
      {
	/*
	 * advance past the link and traversal info
	 */
	xPos = _DtCvAdvanceXOfLine(canvas, pSeg, xPos,
				&lnkInd, &lastLinkVisible);

        switch (_DtCvPrimaryTypeOfSeg(pSeg))
          {
            case _DtCvSTRING:

                pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
					_DtCvIsSegWideChar(pSeg), start);
                len   = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));

                if (len > copy_cnt)
                    len = copy_cnt;

                segWidth = _DtCvGetStringWidth(canvas, pSeg, pChar, len);
		if (copy_cnt == len)
		    flag = end_flag;

		strInfo.string   = pChar;
		strInfo.byte_len = len;
		strInfo.wc       = _DtCvIsSegWideChar(pSeg);
		strInfo.font_ptr = _DtCvFontOfStringSeg(pSeg);

		if (canvas->virt_functions.build_selection != NULL)
		    result = (*(canvas->virt_functions.build_selection))(
				canvas->client_data,
				_DtCvSTRING_TYPE,
				mask,
				ret_data,
				xPos - start_x,
				segWidth,
				flag,
				(_DtCvPointer) &strInfo);

		if (_DtCvSTATUS_OK == result)
		  {
		    if (line.baseline + line.descent > *ret_y)
		    *ret_y  = line.baseline + line.descent;
                    start_x = xPos + segWidth;
		  }
		else if (_DtCvSTATUS_NONE == result)
		    result = _DtCvSTATUS_OK;

                xPos     += segWidth;
                copy_cnt -= len;
                start     = 0;
                break;

            case _DtCvREGION:
		if (copy_cnt == 1)
		    flag = end_flag;

		if (canvas->virt_functions.build_selection != NULL)
		    result = (*(canvas->virt_functions.build_selection))(
				canvas->client_data,
				_DtCvREGION_TYPE,
				mask,
				ret_data,
				xPos - start_x,
				_DtCvWidthOfRegionSeg(pSeg),
				flag,
				_DtCvInfoOfRegionSeg(pSeg));

		if (_DtCvSTATUS_OK == result)
		  {
		    if (line.baseline + line.descent > *ret_y)
		    *ret_y = line.baseline + line.descent;
                    start_x = xPos + segWidth;
		  }
		else if (_DtCvSTATUS_NONE == result)
		    result = _DtCvSTATUS_OK;

                copy_cnt--;
                xPos    += _DtCvWidthOfRegionSeg(pSeg);
                break;
          }
        pSeg = pSeg->next_disp;
      }

    if (result != _DtCvSTATUS_OK)
	return -1;

    return start_x;
}

/*****************************************************************************
 * Function: BuildLine
 *
 *****************************************************************************/
static int
BuildLine(
    _DtCanvasStruct	*canvas,
    unsigned int	 mask,
    _DtCvUnit		 prev_y,
    _DtCvUnit		 target_x,
    int			 line_idx,
    int			 char_idx,
    int			 copy_cnt,
    _DtCvFlags		 end_flag,
    _DtCvUnit		*ret_x,
    _DtCvUnit		*ret_y,
    _DtCvPointer	*ret_data)
{
    _DtCvUnit		 topY;
    _DtCvDspLine	*lines = canvas->txt_lst;
    _DtCvStringInfo	 strInfo = { NULL, 0, 1, NULL };

    topY = lines[line_idx].baseline - lines[line_idx].ascent;
    if (topY > prev_y && canvas->virt_functions.build_selection != NULL)
      {
	int   newLines;
	_DtCvUnit  lineSize = canvas->metrics.line_height / 2;
	if (lineSize < 1)
	    lineSize = 1;

	newLines = (topY - prev_y) / lineSize;
	while (newLines > 0)
	  {
	    newLines--;
	    if ((*(canvas->virt_functions.build_selection))(
				canvas->client_data, _DtCvSTRING_TYPE,
				mask, ret_data, 0, 0, _DtCvEND_OF_LINE,
				(_DtCvPointer) &strInfo) != _DtCvSTATUS_OK)
		return -1;
	  }
      }

    *ret_y = 0;
    *ret_x = AddSegmentToData (canvas, mask, target_x, line_idx, char_idx,
					copy_cnt, end_flag, ret_y, ret_data);
    _DtCvSetProcessed(lines[line_idx]);

    if (*ret_x == -1)
	return -1;
    return 0;
}

/*****************************************************************************
 * Function: FindMinX
 *
 *****************************************************************************/
static	int
FindMinX (
    _DtCvDspLine *lines,
    int		 txt_cnt,
    _DtCvUnit	 target_y,
    int		*ret_line)
{
    int	  i;
    int   cnt    = 0;
    _DtCvUnit  curMin = -1;
    _DtCvUnit  curX;

    for (i = 0; i < txt_cnt; i++)
      {
	if (_DtCvIsNotProcessed(lines[i]))
	  {
	    if (lines[i].baseline - lines[i].ascent < target_y &&
			target_y <= lines[i].baseline + lines[i].descent)
	      {
	        cnt++;
	        curX = lines[i].text_x;

	        if (curMin == -1 || curMin > curX)
		  {
	            curMin    = curX;
		    *ret_line = i;
		  }
              }
          }
      }
    return cnt;
}

/*****************************************************************************
 * Function: FindNextMinY
 *
 *****************************************************************************/
static _DtCvValue
FindNextMinY(
    _DtCvDspLine *lines,
    int		 max_cnt,
    _DtCvUnit	 target_y,
    _DtCvUnit	*ret_y)
{
    int		i     = 0;
    _DtCvUnit	maxY;
    _DtCvValue	found = False;

    while (i < max_cnt)
     {
	if (_DtCvIsNotProcessed(lines[i]))
	  {
	    maxY = lines[i].baseline + lines[i].descent;
	    if (target_y == -1 || maxY < target_y)
	      {
		found    = True;
		target_y = maxY;
	      }
	    SkipOtherLines (lines, max_cnt, i+1, target_y, &i);
	  }
	else
	    i++;
     }

    *ret_y = target_y;
    return found;
}

/*****************************************************************************
 * Function: GetSelectedText
 *
 *****************************************************************************/
static int
GetSelectedText(
    _DtCanvasStruct	*canvas,
    _DtCvSelectData next,
    _DtCvSelectData end,
    unsigned int         mask,
    _DtCvPointer	*ret_data)
{
    _DtCvUnit   maxY;
    _DtCvUnit   botY;
    int    i;
    int    lineCnt  = 0;
    int    junk;
    int    result   = 0;
    int    cpyCnt   = 0;
    int    txtCnt   = canvas->txt_cnt;
    _DtCvFlags endFlag;
    _DtCvValue  processing = True;
    _DtCvDspLine   *lines = canvas->txt_lst;

    for (i = 0; i < txtCnt; i++)
	_DtCvClearProcessed(lines[i]);

    MarkLinesOutsideBoundary(canvas, next.y, next.x, end.y, end.x);

    maxY = next.y;
    if (next.line_idx == -1)
      {
	/*
	 * find the first selected line
	 */
	if (FindNextMinY(lines, txtCnt, -1, &next.y) == False)
	    return 0;

	next.x  = 0;
	lineCnt = FindMinX(lines, txtCnt, next.y, &next.line_idx);
	next.char_idx = 0;
      }
    else
	lineCnt = FindMinX(lines, txtCnt, next.y, &junk);

    while (processing == True && result == 0)
      {
	/*
	 * process the next line of text.
	 */
	do
	  {
	    endFlag = 0;
	    cpyCnt  = lines[next.line_idx].length - next.char_idx;
	    if (next.line_idx == end.line_idx)
		cpyCnt = cpyCnt - lines[next.line_idx].length + end.char_idx;
	    else if (lineCnt == 1)
		endFlag = _DtCvEND_OF_LINE;

	    result  = BuildLine(canvas, mask, maxY, next.x,
					next.line_idx, next.char_idx,
					cpyCnt, endFlag,
					&next.x, &botY, ret_data);

	    if (botY > maxY)
		maxY = botY;

	    next.char_idx = 0;
	    lineCnt       = FindMinX(lines, txtCnt, next.y, &next.line_idx);

	  } while (result == 0 && lineCnt > 0);

	if (result == 0)
	  {
	    next.x = 0;
	    processing = FindNextMinY(lines, txtCnt, -1, &next.y);
	    if (processing == True)
		lineCnt = FindMinX(lines, txtCnt, next.y, &next.line_idx);
	  }
      }

    return result;

} /* End GetSelectedText */

/*****************************************************************************
 * Function:	GetSegsInArea()
 *
 * Purpose:	Retrieve the segments making up the selection.
 *
 *****************************************************************************/
static _DtCvStatus
GetSegsInArea (
    _DtCanvasStruct	*canvas,
    _DtCvSelectData	  *beg,
    _DtCvSelectData	  *end,
    _DtCvSegPts		***ret_segs,
    _DtCvUnit		  *ret_y1,
    _DtCvUnit		  *ret_y2)
{
    int			 cnt;
    int			 count;
    int			 start;
    int			 length;
    int			 lineCnt;
    int			 result = 0;
    _DtCvValue		 processing = True;
    _DtCvUnit		 minY;
    _DtCvUnit		 maxY;
    _DtCvUnit		 botY;
    _DtCvSelectData	 next;
    _DtCvSegPts		*newPt;
    _DtCvSegmentI	*pSeg;
    _DtCvDspLine	*lines = canvas->txt_lst;

    *ret_segs = NULL;

    if (beg->x == -1)
	return _DtCvSTATUS_NONE;

    /*
     * make sure the selection points are in the correct order.
     */
    CheckAndSwitchPoints(beg, end);

    /*
     * clear the processed bit
     */
    for (cnt = 0; cnt < canvas->txt_cnt; cnt++)
	_DtCvClearProcessed(lines[cnt]);

    /*
     * initialize the working structure
     * mark all the lines outside the selection regiion as invalid
     */
    next = *beg;
    MarkLinesOutsideBoundary(canvas, next.y, next.x, end->y, end->x);

    /*
     * start the minimum and maximum Y at this location.
     */
    minY = next.y;
    maxY = end->y;

    /*
     * is there a line at this location?
     */
    if (next.line_idx == -1)
      {
	/*
	 * find the first selected line within the region.
	 */
	if (FindNextMinY(lines, canvas->txt_cnt, -1, &next.y) == False)
	    processing = False;		/* empty of any text */
	else
	  {
	    /*
	     * now find the first line that is on this 'line' and
	     * the number of lines.
	     */
	    next.x  = 0;
	    lineCnt = FindMinX(lines, canvas->txt_cnt, next.y, &next.line_idx);
	    next.char_idx = 0;
          }
      }
    else /* find the number of lines on this 'line' */
	lineCnt = FindMinX(lines, canvas->txt_cnt, next.y, &cnt);

    /*
     * loop will there are segments to process
     */
    while (processing == True && result == 0)
      {
	/*
	 * process the next line of text.
	 */
	while (result == 0 && lineCnt > 0)
	  {
	    /*
	     * for each segment in this line (that is selected)
	     * create a segment point for it.
	     */
	    length = lines[next.line_idx].length;
	    start  = lines[next.line_idx].byte_index;

	    /*
	     * if this is the last line, shorten the length
	     * by the ending index.
	     */
	    if (next.line_idx == end->line_idx)
		length = end->char_idx;

	    /*
	     * move through the line's segments until we
	     * hit the segment starting the selection
	     */
	    pSeg = lines[next.line_idx].seg_ptr;
	    count = next.char_idx;
	    while (NULL != pSeg && 0 < count)
	      {
		/*
		 * get the byte count of this segment
		 */
		_DtCvGetWidthOfSegment(canvas, pSeg, start, length,
					&cnt, NULL, NULL);

		/*
		 * is the byte count of this segment larger than
		 * the starting index of the selection? If not,
		 * the selection is after this segment.
		 */
		if (count >= cnt)
		  {
		    start          = 0;
		    length        -= cnt;
		    pSeg           = pSeg->next_disp;
		  }
		else
		  {
		    length        -= count;
		    start          = start + count;
		  }
		count -= cnt;
	      }

	    while (0 == result && NULL != pSeg && 0 < length)
	      {
	        /*
		 * start with error condition. If the malloc works
	         * the error result gets reset to valid.
		 */
	        result = -1;
	        newPt = (_DtCvSegPts *) malloc (sizeof(_DtCvSegPts));
	        if (NULL != newPt)
		  {
		    /*
		     * indicate everything is okay.
		     */
		    result = 0;

		    /*
		     * get the width of this segment
		     */
		    _DtCvGetWidthOfSegment(canvas, pSeg, start, length,
							&cnt, NULL, NULL);

		    /*
		     * now set the segment point information and add it to the
		     * array of segment points.
		     */
	            newPt->offset  = start;
	            newPt->len     = cnt;
		    newPt->segment = pSeg;

		    *ret_segs = (_DtCvSegPts **) _DtCvAddPtrToArray(
							(void **) *ret_segs,
							(void  *)  newPt);
		    if (NULL == *ret_segs)
		        result = -1;

		    pSeg    = pSeg->next_disp;
		    length -= cnt;
		    start   = 0;
		  }
	      }

	    /*
	     * does this line extend below the selection y?
	     * if so, report it as the maximum y.
	     */
	    botY = lines[next.line_idx].baseline + lines[next.line_idx].descent;
	    if (botY > maxY)
		maxY = botY;

	    /*
	     * indicate this line has been processed.
	     */
	    _DtCvSetProcessed(lines[next.line_idx]);

	    /*
	     * get the next line
	     */
	    next.char_idx = 0;
	    lineCnt       = FindMinX(lines, canvas->txt_cnt, next.y,
							&next.line_idx);
	  }

	if (result == 0)
	  {
	    next.x = 0;
	    processing = FindNextMinY(lines,canvas->txt_cnt, -1, &next.y);
	    if (processing == True)
		lineCnt = FindMinX(lines,canvas->txt_cnt,next.y,&next.line_idx);
	  }
      }

    /*
     * if no errors, add a null to the array
     */
    if (0 != result)
      {
        *ret_segs = (_DtCvSegPts **) _DtCvAddPtrToArray((void **) *ret_segs,
							(void  *) NULL);
	if (NULL == *ret_segs)
	    result = -1; 
      }

    /*
     * if errors, free the segment points and return a bad status.
     */
    if (0 != result)
      {
	if (NULL != *ret_segs)
	  {
	    for (lineCnt = 0; NULL != (*ret_segs)[lineCnt]; lineCnt++)
		free((*ret_segs)[lineCnt]);
	    free(*ret_segs);
	  }
	return _DtCvSTATUS_BAD;
      }

    if (NULL != ret_y1)
	*ret_y1 = minY;
    
    if (NULL != ret_y2)
	*ret_y2 = minY;

    return _DtCvSTATUS_OK;
}

/******************************************************************************
 *                             Semi-Public Functions
 ******************************************************************************/
/*****************************************************************************
 * Function: _DtCvDrawAreaWithFlags
 *
 *****************************************************************************/
void
_DtCvDrawAreaWithFlags (
    _DtCanvasStruct	*canvas,
    _DtCvSelectData	 start,
    _DtCvSelectData	 end,
    _DtCvFlags		 old_flags,
    _DtCvFlags		 new_flags,
    _DtCvElemType	 trav_type,
    _DtCvPointer	 trav_data)
{
    int    i;
    int    len;
    int    count;
    int    startChar;
    int    lnkInd;
    _DtCvUnit   dstX;
    _DtCvUnit   topY;
    _DtCvUnit   botY;
    _DtCvUnit   superWidth;
    _DtCvUnit   subWidth;
    _DtCvUnit   superY;
    _DtCvUnit   subY;
    _DtCvUnit   scriptX;
    _DtCvUnit   segWidth;
    _DtCvSegmentI  *pSeg;
    _DtCvValue   lstLinkVis;
    _DtCvValue   lstWasSuper;
    _DtCvValue   lstWasSub;
    _DtCvValue   trimmed;
    _DtCvFlags	 flagMask = old_flags | new_flags;
    _DtCvFlags	 endFlag  = flagMask & _DtCvTRAVERSAL_END;

    _DtCvDspLine   *lines = canvas->txt_lst;

    /*
     * now use the flagMask to determine what else to look for.
     * I.e. if flagMask has _DtCvMARK_FLAG set, then it becomes
     * set to _DtCvSELECTED_FLAG and visa versa.
     */
    flagMask ^= (_DtCvSELECTED_FLAG | _DtCvMARK_FLAG);

    /*
     * strip the end flag from the other flags
     */
    new_flags &= ~(_DtCvTRAVERSAL_END);
    old_flags &= ~(_DtCvTRAVERSAL_END);

    if (Equal(start, end))
	return;

    for (i = 0; i < canvas->txt_cnt; i++)
      {
	topY  = lines[i].baseline - lines[i].ascent;
	botY  = lines[i].baseline + lines[i].descent;

	if (InRegion(topY, botY, start.y, end.y))
	  {
	    /*
	     * get the start of the text.
	     */
	    lstLinkVis  = False;
	    lstWasSuper = False;
	    lstWasSub   = False;
	    lnkInd      = -1;
	    dstX        = _DtCvGetStartXOfLine(&(lines[i]), &pSeg);
	    startChar   = lines[i].byte_index;
	    count       = lines[i].length;

	    while (pSeg != NULL && _DtCvIsSegNoop(pSeg))
	      {
	        startChar  = 0;
	        pSeg       = pSeg->next_disp;
	      }

	    /*
	     * advance the starting point
	     */
	    dstX = _DtCvAdvanceXOfLine(canvas, pSeg, dstX,
					&lnkInd, &lstLinkVis);
	    /*
	     * take into account super/sub scripting
	     */
	    dstX = _DtCvAdjustForSuperSub(canvas, pSeg, dstX, &scriptX,
					&superWidth, &superY, &subWidth, &subY,
					&lstWasSuper, &lstWasSub);

	    /*
	     * set this flag so that the first pass of 'while (cnt > 0)'
	     * doesn't do it again.
	     */
	    trimmed = True;

	    if (_DtCvStraddlesPt(start.y, topY, botY))
	      {
		/*
		 * skip this item?
		 * I.E. is this line before the start or after the end?
		 */
		if (canvas->txt_lst[i].max_x < start.x ||
					(end.y == start.y && end.x <= dstX) )
		    continue;

		/*
		 * does this line start the mark/selection?
		 */
		if (i == start.line_idx && start.x >= dstX)
		  {
		    int cnt = start.char_idx;

		    while (cnt > 0)
		      {
			if (trimmed == False)
			  {
			    /*
			     * advance the starting point
			     */
			    dstX = _DtCvAdvanceXOfLine(canvas, pSeg, dstX,
					&lnkInd, &lstLinkVis);
			    /*
			     * take into account super/sub scripting
			     */
			    dstX = _DtCvAdjustForSuperSub(canvas,
					pSeg, dstX, &scriptX,
					&superWidth, &superY, &subWidth, &subY,
					&lstWasSuper, &lstWasSub);
			  }

			/*
			 * take into account the length of the segment
			 */
			_DtCvGetWidthOfSegment(canvas, pSeg,
					startChar , cnt,
					&len, &segWidth, &trimmed);

			dstX       += segWidth;
			startChar  += len;
			if (trimmed == False)
			  {
			    startChar  = 0;
			    pSeg       = pSeg->next_disp;
			  }

			trimmed = False;

			cnt -= len;

		      }

		    count -= start.char_idx;
		  }

		/*
		 * otherwise this line is after the line that starts
		 * the mark/selection. Stick with its start x.
		 */
	      }

	    /*
	     * does this straddle the end point?
	     */
	    if (_DtCvStraddlesPt(end.y, topY, botY))
	      {
		/*
		 * does this start after the end of the mark/selection?
		 * if so, skip.
		 */
		if (end.x <= dstX)
		    continue;

		/*
		 * Does this segment end after the end of the mark/selection?
		 * If so, trim how much gets highlighted.
		 */
		if (canvas->txt_lst[i].max_x > end.x)
		    count -= (lines[i].length - end.char_idx);
	      }

	    /*
	     * while there is something to draw (un)mark/selected.
	     */
	    old_flags = old_flags | _DtCvMARK_BEGIN;
	    new_flags = new_flags | _DtCvMARK_BEGIN;
	    while (count > 0)
	      {
		/*
		 * the original count to render
		 */
		len = count;

		/*
		 * check for other marks and selection.
		 */
		_DtCvCheckLineMarks(canvas, i, startChar - lines[i].byte_index,
						count, dstX, flagMask,
						&len, &old_flags, &new_flags);

		/*
		 * if this is the last segment(s) of the (un)mark/selection
		 * set the end flags.
		 */
		if (len == count)
		  {
		    new_flags |= (endFlag | _DtCvLINK_END | _DtCvMARK_END);
		    old_flags |= (endFlag | _DtCvLINK_END | _DtCvMARK_END);
		  }

		/*
		 * draw the segments that are marked/unmarked.
		 */
	        dstX = _DtCvDrawSegments(canvas, lines[i],
					pSeg, startChar , len, &lnkInd,
					dstX, dstX, &scriptX,
					&superWidth, &superY, &subWidth, &subY,
					&lstWasSub, &lstWasSuper,
					&lstLinkVis, old_flags, new_flags,
					trav_type, trav_data);
		/*
		 * modify the count by the length processed
		 */
		count  -= len;

		/*
		 * did this do the entire length? If not, set the
		 * indexes ahead and do again.
		 */
		if (count > 0)
		    _DtCvSkipLineChars(canvas, pSeg, startChar , count + len,
						len, &startChar , &pSeg);

		/*
		 * strip the any begin flags.
		 */
		_DtCvRemoveBeginFlags(old_flags);
		_DtCvRemoveBeginFlags(new_flags);
	      }
	  }
      }
}

/*****************************************************************************
 * Function:	_DtCanvasGetSelectionPoints()
 *
 * Purpose:	Retrieve the segments making up the selection.
 *
 *****************************************************************************/
_DtCvStatus
_DtCvGetMarkSegs (
    _DtCanvasStruct	*canvas,
    _DtCvPointInfo	***ret_info)
{
    int			  i;

    *ret_info = NULL;

    for (i = 0; i < canvas->mark_cnt; i++)
      {
	_DtCvPointInfo	*nxtInfo;

	/*
	 * allocate mark information structure
	 */
	nxtInfo = (_DtCvPointInfo *) malloc (sizeof(_DtCvPointInfo));
	if (NULL == nxtInfo)
	    return _DtCvSTATUS_BAD;

	nxtInfo->client_data = canvas->marks[i].client_data;

	if (_DtCvSTATUS_BAD == GetSegsInArea(canvas, &(canvas->marks[i].beg),
						&(canvas->marks[i].end),
						&(nxtInfo->segs),
						NULL, NULL))
	    return _DtCvSTATUS_BAD;

	*ret_info = (_DtCvPointInfo **) _DtCvAddPtrToArray((void **) *ret_info,
							(void *) nxtInfo);
	if (NULL == *ret_info)
	    return _DtCvSTATUS_BAD;
	  }

    return _DtCvSTATUS_OK;
}

/******************************************************************************
 *                             Public Functions
 ******************************************************************************/
/*****************************************************************************
 * Function:	_DtCanvasGetSelection()
 *
 * Purpose:	Indicate the end point for a selection.
 *
 *****************************************************************************/
_DtCvStatus
_DtCanvasGetSelection (
    _DtCvHandle		 canvas_handle,
    unsigned int	 mask,
    _DtCvPointer	*ret_select)
{
    _DtCanvasStruct     *canvas = (_DtCanvasStruct *) canvas_handle;

    *ret_select = NULL;
    return(GetSelectedText(canvas, canvas->select_start, canvas->select_end,
							mask, ret_select));
}

/*****************************************************************************
 * Function:	_DtCanvasProcessSelection()
 *
 * Purpose:	Indicate an new point for a selection.
 *
 *****************************************************************************/
void
_DtCanvasProcessSelection (
    _DtCvHandle		canvas_handle,
    _DtCvUnit		x,
    _DtCvUnit		y,
    _DtCvSelectMode	mode)
{
    _DtCanvasStruct	*canvas = (_DtCanvasStruct *) canvas_handle;
    _DtCvSelectData	 temp;

    switch (mode)
	  {
	case _DtCvSELECTION_CLEAR:
		CheckAndSwitchPoints(&(canvas->select_start),
							&(canvas->select_end));

	case _DtCvSELECTION_START:
		_DtCvDrawAreaWithFlags(canvas, canvas->select_start,
				canvas->select_end,
				_DtCvSELECTED_FLAG, 0,
				_DtCvBAD_TYPE, NULL);

		canvas->select_start = defaultSelect;
		if (mode == _DtCvSELECTION_START)
		    SearchForClosestLine(canvas, x, y, &(canvas->select_start));

		canvas->select_end   = canvas->select_start;
		break;

	case _DtCvSELECTION_END:
	case _DtCvSELECTION_UPDATE:
		SearchForClosestLine(canvas, x, y, &temp);

		AdjustSelection (canvas, temp);
		if (mode == _DtCvSELECTION_END)
		    CheckAndSwitchPoints(&(canvas->select_start),
							&(canvas->select_end));
		break;
      }
}

/*****************************************************************************
 * Function:	_DtCanvasGetSelectionPoints()
 *
 * Purpose:	Retrieve the segments making up the selection.
 *
 *****************************************************************************/
_DtCvStatus
_DtCanvasGetSelectionPoints (
    _DtCvHandle		   canvas_handle,
    _DtCvSegPts		***ret_segs,
    _DtCvUnit		  *ret_y1,
    _DtCvUnit		  *ret_y2)
{
    _DtCanvasStruct	*canvas = (_DtCanvasStruct *) canvas_handle;

    return (GetSegsInArea(canvas, &(canvas->select_start),
			&(canvas->select_end), ret_segs, ret_y1, ret_y2));
}

/*****************************************************************************
 * Function:	_DtCanvasActivatePts()
 *
 * Purpose:	Activate the points given.
 *
 *****************************************************************************/
_DtCvStatus
_DtCanvasActivatePts (
    _DtCvHandle		 canvas_handle,
    unsigned int	 mask,
    _DtCvPointInfo	*info,
    _DtCvUnit		*ret_y1,
    _DtCvUnit		*ret_y2)
{
    int			 markIdx;
    _DtCanvasStruct	*canvas = (_DtCanvasStruct *) canvas_handle;
    _DtCvSelectData	 startSel = defaultSelect;
    _DtCvSelectData	 endSel   = defaultSelect;
    _DtCvFlags		 flag;
    _DtCvSegmentI	*firstSeg;

#define	REQUIRE_SEGS \
		(_DtCvACTIVATE_MARK | _DtCvACTIVATE_SELECTION)
    /*
     * check to see if there is anything to do
     */
    if (0 == mask)
	return _DtCvSTATUS_NONE;

    if ((mask & _DtCvACTIVATE_MARK) && (mask & _DtCvDEACTIVATE))
	return _DtCvSTATUS_BAD;

    /*
     * Convert the segments into starting and ending positions.
     */
    if (((mask & _DtCvDEACTIVATE) && NULL == info->client_data)
		|| (mask & REQUIRE_SEGS))
      {
	if (NULL == info || NULL == info->segs ||
		_DtCvSTATUS_BAD == _DtCvCvtSegsToPts(canvas, info->segs,
						&startSel, &endSel,
						ret_y1, ret_y2, &firstSeg))
	    return _DtCvSTATUS_BAD;
      }

    /*
     * Activate as a selection
     */
    if (mask & _DtCvACTIVATE_SELECTION)
      {
	_DtCanvasProcessSelection (canvas_handle, 0, 0, _DtCvSELECTION_CLEAR);

	canvas->select_start = startSel;
	canvas->select_end   = endSel;

	_DtCvDrawAreaWithFlags (canvas, startSel, endSel,
						0, _DtCvSELECTED_FLAG,
						_DtCvBAD_TYPE, NULL);
      }

    /*
     * Activate as a mark
     */
    if (mask & _DtCvACTIVATE_MARK)
      {
	int		travIdx;
	_DtCvUnit	x;
	_DtCvUnit	y;
	_DtCvUnit	width;
	_DtCvUnit	height;

	markIdx = _DtCvAddToMarkList(canvas, info->client_data,
				_DtCvIsMarkMaskOn(mask), &startSel, &endSel);
	if (-1 == markIdx)
	    return _DtCvSTATUS_BAD;

	/*
	 * now put the mark in the traversal list and merge it into the
	 * proper place.
	 */
	travIdx = _DtCvGetNextTravEntry(canvas);
	if (-1 == travIdx
		|| 0 != _DtCvSetTravEntryInfo(canvas, travIdx,
						_DtCvTraversalMark, firstSeg,
						markIdx, _DtCvTRUE)
		|| 0 != _DtCvCalcMarkPos(canvas, markIdx,
						&x, &y, &width, &height)
		|| 0 != _DtCvSetTravEntryPos(canvas, travIdx,
						x, y, width, height))
	    return _DtCvSTATUS_BAD;

	_DtCvSortTraversalList(canvas, _DtCvTRUE);

	/*
	 * draw these segments marked.
	 */
	flag = _DtCvMARK_FLAG;
	if (_DtCvTRUE == canvas->marks[markIdx].on)
	    flag |= _DtCvMARK_ON;

	_DtCvDrawAreaWithFlags (canvas, startSel, endSel,
							0, flag,
							_DtCvBAD_TYPE, NULL);
      }

    /*
     * Clear the mark flag.
     */
    else if (mask & _DtCvDEACTIVATE)
      {
	int	travIdx;

	/*
	 * is there anything to deacivate?
	 */
	if (NULL == canvas->marks || 0 == canvas->mark_cnt)
	    return _DtCvSTATUS_BAD;

	/*
	 * was client data specified? If so, then look for it and ignore
	 * the segment data.
	 */
	markIdx = 0;
	if (NULL != info->client_data)
	  {
	    while (markIdx < canvas->mark_cnt &&
		   canvas->marks[markIdx].client_data != info->client_data)
		markIdx++;

	    /*
	     * initialize the selection points
	     */
	    if (markIdx < canvas->mark_cnt)
	      {
		startSel = canvas->marks[markIdx].beg;
		endSel   = canvas->marks[markIdx].end;
	      }
	  }
	/*
	 * look for the marked set using the segments.
	 */
	else
	  {
	    while (markIdx < canvas->mark_cnt
		   && startSel.line_idx != canvas->marks[markIdx].beg.line_idx
		   && startSel.char_idx != canvas->marks[markIdx].beg.char_idx
		   && endSel.line_idx != canvas->marks[markIdx].end.line_idx
		   && endSel.line_idx != canvas->marks[markIdx].end.char_idx)
		markIdx++;
	  }

	if (markIdx >= canvas->mark_cnt)
	    return _DtCvSTATUS_BAD;

	/*
	 * draw these segments unmarked.
	 */
	flag = _DtCvMARK_FLAG;
	if (_DtCvTRUE == canvas->marks[markIdx].on)
	    flag |= _DtCvMARK_ON;

	canvas->marks[markIdx].on = _DtCvFALSE;
	_DtCvDrawAreaWithFlags (canvas, startSel, endSel, flag, 0,
						_DtCvBAD_TYPE, NULL);

	/*
	 * remove the mark from the traversal list
	 *
	 * first find the traversal entry of the mark and adjust any
	 * traversal mark index values to reflect that the mark
	 * list is about to shrink by 1.
	 */
	for (travIdx = 0; _DtCvTraversalMark != canvas->trav_lst[travIdx].type
			|| markIdx != canvas->trav_lst[travIdx].idx; travIdx++)
	  {
	    /*
	     * is this mark after the one being removed?
	     * if so, decrease its index because it's about to move.
	     */
	    if (_DtCvTraversalMark == canvas->trav_lst[travIdx].type &&
					markIdx < canvas->trav_lst[travIdx].idx)
		canvas->trav_lst[travIdx].idx--;
	  }

	/*
	 * move the list of traversal entries to eliminate the mark entry.
	 */
	while (travIdx + 1 < canvas->trav_cnt)
	  {
	    canvas->trav_lst[travIdx] = canvas->trav_lst[travIdx + 1];

	    /*
	     * is this a mark after the one being removed?
	     * if so, decrease it's index because it's about to move.
	     */
	    if (_DtCvTraversalMark == canvas->trav_lst[travIdx].type &&
					markIdx < canvas->trav_lst[travIdx].idx)
		canvas->trav_lst[travIdx].idx--;

	    travIdx++;
	  }

	/*
	 * update the traversal count and back up to the previous traversal
	 * if the mark was at the end of the traversal list.
	 */
	canvas->trav_cnt--;
	if (canvas->cur_trav >= canvas->trav_cnt)
	    canvas->cur_trav--;

	/*
	 * move the list of marks up
	 */
	while (markIdx + 1 < canvas->mark_cnt)
	  {
	    canvas->marks[markIdx] = canvas->marks[markIdx + 1];
	    markIdx++;
	  }

	canvas->mark_cnt--;
      }
    else if ((_DtCvACTIVATE_MARK_ON | _DtCvACTIVATE_MARK_OFF) & mask)
      {
	markIdx = 0;
	if (NULL != info && NULL != info->client_data)
	  {
	    while (markIdx < canvas->mark_cnt &&
		   canvas->marks[markIdx].client_data != info->client_data)
		markIdx++;

	    /*
	     * was a mark with this client data found?
	     */
	    if (markIdx >= canvas->mark_cnt)
	        return _DtCvSTATUS_BAD;
	  }
	else
	  {
	    /*
	     * are there any traversals? Is the current one sitting on a mark?
	     */
	    if (0 == canvas->trav_cnt || -1 == canvas->cur_trav ||
		_DtCvTraversalMark != canvas->trav_lst[canvas->cur_trav].type)
	        return _DtCvSTATUS_BAD;

	    /*
	     * get the mark index
	     */
	    markIdx = canvas->trav_lst[canvas->cur_trav].idx;
	  }

	/*
	 * is this different than what it is set at now? If not, do nothing.
	 */
	if (_DtCvIsMarkMaskOn(mask) == canvas->marks[markIdx].on)
	    return _DtCvSTATUS_NONE;

	/*
	 * set to mask value.
	 */
	canvas->marks[markIdx].on = _DtCvIsMarkMaskOn(mask);

	/*
	 * set the flags correctly.
	 */
	flag = _DtCvMARK_FLAG;
	if (_DtCvTRUE == canvas->marks[markIdx].on)
	    flag |= _DtCvMARK_ON;
	if (_DtCvTRUE == canvas->trav_on &&
			markIdx == canvas->trav_lst[canvas->cur_trav].idx)
	    flag |= _DtCvTRAVERSAL_FLAG;

	/*
	 * draw the mark opposite what it was
	 */
	_DtCvDrawAreaWithFlags (canvas, canvas->marks[markIdx].beg,
				canvas->marks[markIdx].end,
				(flag ^ _DtCvMARK_ON), flag,
				_DtCvBAD_TYPE, NULL);
      }

    return _DtCvSTATUS_OK;
}

/*****************************************************************************
 * Function:	_DtCanvasGetMarkPositions()
 *
 * Purpose:	Return the position in the canvas of the marks.
 *
 *****************************************************************************/
_DtCvStatus
_DtCanvasGetMarkPositions (
    _DtCvHandle		    canvas_handle,
    _DtCvMarkPos	 ***ret_pos)
{
    int  i;
    _DtCanvasStruct	*canvas = (_DtCanvasStruct *) canvas_handle;
    _DtCvMarkPos	*nextPos;
    _DtCvMarkData	*markLst = canvas->marks;

    *ret_pos = NULL;

    if (0 == canvas->mark_cnt)
	return _DtCvSTATUS_NONE;

    for (i = 0; i < canvas->mark_cnt; i++, markLst++)
      {
	/*
	 * malloc memory for the information
	 */
	nextPos = (_DtCvMarkPos *) malloc (sizeof(_DtCvMarkPos));
	if (NULL == nextPos)
	    return _DtCvSTATUS_BAD;

	/*
	 * client data and baselines
	 */
	nextPos->client_data = markLst->client_data;
	nextPos->baseline1   = canvas->txt_lst[markLst->beg.line_idx].baseline;
	nextPos->baseline2   = canvas->txt_lst[markLst->end.line_idx].baseline;

	/*
	 * top left corner
	 */
	nextPos->x1 = markLst->beg.x;
	nextPos->y1 = nextPos->baseline1 -
				canvas->txt_lst[markLst->beg.line_idx].ascent;

	/*
	 * bottom right corner
	 */
	nextPos->x2 = markLst->end.x;
	nextPos->y2 = nextPos->baseline2 +
				canvas->txt_lst[markLst->end.line_idx].descent;

	*ret_pos = (_DtCvMarkPos **) _DtCvAddPtrToArray((void **) *ret_pos,
							(void *) nextPos);
	if (NULL == *ret_pos)
	    return _DtCvSTATUS_BAD;
      }

    return _DtCvSTATUS_OK;
}
