1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/lib/DtHelp/LayoutUtil.c
2021-12-24 10:50:27 -07:00

2366 lines
61 KiB
C

/*
* 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: LayoutUtil.c /main/26 1996/11/06 12:25:09 cde-hp $ */
/************************************<+>*************************************
****************************************************************************
**
** File: LayoutUtil.c
**
** Project: Cde DtHelp
**
** Description:
**
** (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>
#include <limits.h>
/*
* Canvas Engine includes
*/
#include "CanvasP.h"
#include "CanvasSegP.h"
/*
* private includes
*/
#include "CanvasI.h"
#include "CvStringI.h"
#include "LayoutUtilI.h"
#include "StringFuncsI.h"
#include "VirtFuncsI.h"
/******************************************************************************
*
* Private Defines
*
*****************************************************************************/
#define GROW_SIZE 10
#define CheckFormat(x) \
(((x)->format_y == -1 || (x)->format_y > (x)->y_pos) ? False : True)
/******************************************************************************
*
* Private Variables
*
*****************************************************************************/
static char *OneByteCantBeginList = "])}`\"\'.,;?:!";
static char *OneByteCantEndList = "[({`\"";
static _DtCvLayoutInfo DefLayInfo =
{
NULL, /* _DtCvSegmentI *line_seg; */
0, /* unsigned int line_start; */
0, /* unsigned int line_bytes; */
0, /* _DtCvUnit cur_len; */
0, /* _DtCvUnit max_x_pos; */
0, /* _DtCvUnit cur_max_x; */
0, /* _DtCvUnit y_pos; */
0, /* _DtCvUnit text_x_pos; */
0, /* _DtCvUnit leading; */
-1, /* int lst_hyper; */
_CEFORMAT_ALL, /* int format_y; */
-1, /* int join_line; */
FALSE, /* _DtCvValue lst_vis; */
FALSE, /* _DtCvValue join; */
FALSE, /* _DtCvValue align_flag; */
NULL, /* const char *align_char; */
-1, /* _DtCvUnit align_pos; */
0, /* int delayed_search_saves */
};
static const _DtCvSelectData DefSelectData = { -1, -1, -1, -1 };
static const _DtCvTraversalInfo DefTravData =
{
_DtCvFALSE /* active */,
_DtCvTraversalNone /* type */,
-1 /* idx */,
0 /* x_pos */,
0 /* y_pos */,
0 /* width */,
0 /* height */,
NULL /* *seg_ptr */
};
/******************************************************************************
*
* Private Functions
*
*****************************************************************************/
/******************************************************************************
* Function: IsTrueMultiByte
*
* Returns: True if the character is a multibyte character
* False if the character is a single byte character.
*****************************************************************************/
static _DtCvValue
IsTrueMultiByte (wchar_t wc_char)
{
char buf[MB_LEN_MAX];
/*
* check to see if this is a one byte character
* There might not be a multibyte list for this locale.
* Can't break on single byte characters.
*/
if (1 != wctomb(buf, wc_char))
return True;
return False;
}
/******************************************************************************
* Function: CheckList
*
* Returns: True if the character matches one of the characters in
* the MultiCantEndList.
* False if the character does not match an item in
* the MultiCantEndList.
*****************************************************************************/
static _DtCvValue
CheckList (
wchar_t wc_char,
const wchar_t *list)
{
/*
* check the multibyte list for the character
*/
if (list != NULL)
{
while ('\0' != *list)
{
/*
* it matches, return true
*/
if (*list == wc_char)
return True;
list++;
}
}
return False;
}
/*****************************************************************************
* Function: static int CompareTraversalPos (_DtCvHandle canvas);
*
* Parameters:
*
* Returns:
*
* Purpose:
*
*****************************************************************************/
static int
CompareTraversalPos (
const void *a,
const void *b)
{
_DtCvTraversalInfo *linkA = (_DtCvTraversalInfo *) a;
_DtCvTraversalInfo *linkB = (_DtCvTraversalInfo *) b;
_DtCvUnit centA = linkA->y_pos + (linkA->height >> 1);
_DtCvUnit centB = linkB->y_pos + (linkB->height >> 1);
if (linkA->y_pos + linkA->height < centB && centA < linkB->y_pos)
return -1;
if (linkB->y_pos + linkB->height < centA && centB < linkA->y_pos)
return 1;
if (linkA->x_pos != linkB->x_pos)
return ((linkA->x_pos < linkB->x_pos) ? -1 : 1);
if (linkA->y_pos != linkB->y_pos)
return ((linkA->y_pos < linkB->y_pos) ? -1 : 1);
if (linkA->height != linkB->height)
return ((linkA->height < linkB->height) ? -1 : 1);
if (linkA->width != linkB->width)
return ((linkA->width < linkB->width) ? -1 : 1);
return 0;
}
/******************************************************************************
*
* Private Layout Utility Functions
*
*****************************************************************************/
/******************************************************************************
* Function: void _DtCvInitLayoutInfo ()
*
* Parameters:
*
* Returns: Nothing.
*
*****************************************************************************/
void
_DtCvInitLayoutInfo (
_DtCanvasStruct *canvas,
_DtCvLayoutInfo *layout)
{
*layout = DefLayInfo;
layout->y_pos = canvas->metrics.top_margin;
}
/******************************************************************************
* Function: int _DtCvGetTraversalWidth ()
*
* Parameters:
*
* Returns: The total amount of space to add before and after the
* segment to take into account traversal/link metrics on
* this segment including any necessary to 'close' out the
* link on the previous segment.
*
*****************************************************************************/
int
_DtCvGetTraversalWidth (
_DtCanvasStruct *canvas,
_DtCvSegmentI *p_seg,
int lst_hyper)
{
int value = 0;
int lnkBefore = 0;
int lnkAfter = 0;
/*
* does this segment have a different link than the previous one?
*/
if (lst_hyper != p_seg->link_idx)
{
/*
* is the link visible?
*/
if (_DtCvIsSegVisibleLink(p_seg))
{
/*
* get the visible link metrics
*/
lnkBefore = canvas->link_info.space_before;
lnkAfter = canvas->link_info.space_after;
}
if (_DtCvIsSegALink(p_seg))
{
/*
* if the last 'link' was really a link, close it out by
* leaving room for the traversal and link end indicators
*/
if (lst_hyper != -1)
value += (canvas->traversal_info.space_after + lnkAfter);
/*
* leave space for the traversal/link begin and end
* indicators for this segment.
*/
value += (canvas->traversal_info.space_before
+ canvas->traversal_info.space_after
+ lnkBefore
+ lnkAfter);
}
}
return value;
}
/******************************************************************************
* Function: _DtCvAddLines
*
* makes sure the last x number of lines are blank.
*****************************************************************************/
void
_DtCvAddSpace (
_DtCvUnit number,
_DtCvUnit *ret_y)
{
/*
* anything to do?
*/
if (0 >= number)
return;
/*
* adjust the global Y position to allow the extra room
*/
*ret_y = *ret_y + number;
}
/******************************************************************************
* Function: CheckOneByteCantEndList
*
* Returns: True if the character matches one of the characters in
* the OneByteCantEndList.
* False if the character does not match an item in
* the OneByteCantEndList.
*****************************************************************************/
_DtCvValue
_DtCvCheckOneByteCantEndList (
char c,
char *cant_end_list)
{
int i;
for (i = 0; cant_end_list[i]; i++)
if (cant_end_list[i] == c)
return True;
return False;
}
/******************************************************************************
* Function: CheckOneByteCantBeginList
*
* Returns: True if the character matches one of the characters in
* the OneByteCantBeginList.
* False if the character does not match an item in
* the OneByteCantBeginList.
*****************************************************************************/
_DtCvValue
_DtCvCheckOneByteCantBeginList (
char c,
char *cant_begin_list)
{
int i;
for (i = 0; cant_begin_list[i]; i++)
if (cant_begin_list[i] == c)
return True;
return False;
}
/******************************************************************************
* Function: CheckLineSyntax
*
* Returns: True if the segment can end a line.
* False if the segment can not end a line.
*
* Purpose: Checks the line syntax. Will not allow a segment to end
* a line if:
* the segment does not end with a hypen.
* the segment does not end with a space and the
* next segment does not begin with a space.
* the segment ends with a two byte characters that
* can not end a line.
* The next segment starts with a two byte character
* that can not begin a line.
* the segment ends with an one-byte open type and
* the next segment starts with a
* two byte character.
* the segment ends with a two byte character and
* the next segment starts with a one-byte
* close type.
* the next segment is a non-breaking string or region.
*
*****************************************************************************/
_DtCvValue
_DtCvCheckLineSyntax (
_DtCanvasStruct *canvas,
_DtCvSegmentI *pSeg,
int start,
int str_len,
_DtCvValue skip_hypen_ck)
{
int myStrLen = 0;
int wcFlag = 0;
void *pChar = NULL;
wchar_t nextChar;
wchar_t lastChar = 0;
_DtCvValue lstCharMb = False;
_DtCvValue nxtCharMb = False;
/*
* while this is a marker or a noop without a end-of-line, go to the
* next segment.
*/
while (NULL != pSeg && (_DtCvIsSegMarker(pSeg) ||
(_DtCvIsSegNoop(pSeg) && !_DtCvIsSegNewLine(pSeg))))
pSeg = pSeg->next_seg;
/*
* if this segment is null or not a string or region, stop the
* test right now.
*/
if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
return True;
/*
* Get the string segment stats
*/
if (_DtCvIsSegString(pSeg))
{
wcFlag = _DtCvIsSegWideChar(pSeg);
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg), wcFlag, start);
myStrLen = _DtCvStrLen (pChar, wcFlag);
}
/*
* if this is a region or a string segment (at the end of its string)
* and it has a newline on it, then it can end a line.
*/
if ((_DtCvIsSegRegion(pSeg) ||
(_DtCvIsSegString(pSeg) && myStrLen == str_len))
&& (_DtCvIsSegNewLine (pSeg) || pSeg->next_seg == NULL))
return True;
/*
* if this is a region, then check it's breaking flag.
*/
if (_DtCvIsSegRegion(pSeg))
{
if (_DtCvIsSegNonBreakingChar(pSeg))
return False;
return True;
}
/*
* so, to get this far, this is a string segment.
*
* Problems with indexing?
*/
if (str_len <= 0)
return True;
/*
* do we need to check the last character in the string?
* If skip_hypen_ck is true, it means that 'lastChar' would be a hypen.
*/
if (False == skip_hypen_ck)
{
/*
* this region is a string, get its string information.
*/
lastChar = _DtCvChar(pChar, wcFlag, str_len - 1);
/*
* check to make sure the last character is a valid last
* character.
*/
if (' ' == lastChar || '-' == lastChar)
return True;
/*
* If this string is a multi-byte, check the list of multi-bytes
* that can't end a line. If one is found it can't end a line.
*/
if (wcFlag &&
CheckList(lastChar, canvas->locale.cant_end_chars) == True)
return False;
/*
* so at the end of these tests, the last character is
* -) not a blank.
* -) not a hypen.
* -) either a single byte character or multibyte character
* that can end the line (including a single byte in
* wide char form).
*
* set the flag for the type of character lastChar is.
* if skip_hypen_ck was True, then lstCharMb remains False
* which is logical since it means that the caller has already
* processed a hypen (a single byte character).
*/
lstCharMb = IsTrueMultiByte(lastChar);
}
/*
* Check for more characters in the string and
* check its next character for breakable space.
*/
if (myStrLen > str_len)
{
/*
* go to the next character.
*/
nextChar = _DtCvChar(pChar, wcFlag, str_len);
/*
* Is it a valid break point?
*/
if (' ' == nextChar)
return True;
/*
* set the multibyte flag for the next character
*/
nxtCharMb = IsTrueMultiByte(nextChar);
/*
* If this is wide char string, check the list of multi-byte
* that can't begin a line.
*
* But only if the last character wasn't a hypen! Otherwise
* it's a character after a hypen and should not be broken on.
*
* if this character is in the 'cannot begin line' list, then it
* can't be broken on (a return value of true). (A wide char
* encoding of a single byte character should come back as False
* as long as the character is not in the list.)
*
* Have to double check to make sure it is a multibyte
* character (want it to go through the CheckMulti list just in
* case it's specified in there, before eliminating it).
*/
if (False == skip_hypen_ck && wcFlag
&& CheckList(nextChar,canvas->locale.cant_begin_chars) == False
&& True == nxtCharMb)
return True;
/*
* either the character is after a hypen (starting a line) OR it
* is a multibyte character in the 'cannot begin a line' list OR
* it is a single byte character. Therefore, this is a
* nonbreakable character.
*/
return False;
}
/*
* We were at the last character of the string.
* go to the next segment and see if it can start a new line.
*/
do
{
pSeg = pSeg->next_seg;
} while (pSeg != NULL && (_DtCvIsSegMarker(pSeg) ||
(_DtCvIsSegNoop (pSeg) && !(_DtCvIsSegNewLine(pSeg)))));
/*
* If there isn't another valid segment, then the original segment
* can end the line.
*/
if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
return True;
/*
* if the last if fell through, then pSeg is a string or region.
* check to see if you can break on it.
*/
if (_DtCvIsSegNonBreakingChar(pSeg))
return False;
/*
* if the last if fell through, then this is a breaking string or
* region. Therefore, if a region, you can break on it.
*/
if (_DtCvIsSegRegion(pSeg))
return True;
/*
* To get this far, the next segment must be a string. Check the
* first character of the string to see if it can start a new line.
*/
nextChar = _DtCvChar(_DtCvStringOfStringSeg(pSeg),
_DtCvIsSegWideChar(pSeg), 0);
if (' ' == nextChar)
return True;
/*
* If the previous character was a single byte character (or a hypen),
* it couldn't end a line. If this is a single byte string, then
* this string can't start a line......
*/
if (_DtCvIsSegRegChar(pSeg) &&
(True == skip_hypen_ck || False == lstCharMb))
return False;
/*
* If this is multi-byte, check the list of multi-byte
* that can't begin a line.
*/
if (_DtCvIsSegWideChar(pSeg))
{
/*
* plus checking the 'can not begin a line' list, check
* if the previous character was a hypen, then this can't be
* broken on either.
*/
if (True == skip_hypen_ck ||
CheckList(nextChar, canvas->locale.cant_begin_chars) == True)
return False;
/*
* if the previous character was a multi-byte and this
* character is a multibyte, then it is a valid break.
*/
nxtCharMb = IsTrueMultiByte(nextChar);
if (True == lstCharMb && True == nxtCharMb)
return True;
}
/*
* if the last character was a single byte character, then there
* is still more to check - 1 byte punctuation around multi-byte.
*/
if (False == lstCharMb &&
_DtCvCheckOneByteCantEndList((char)lastChar,OneByteCantEndList) == True)
return False;
/*
* or was the last character a multibyte and is followed by single byte
* punctuation?
*/
if (True == lstCharMb && False == nxtCharMb &&
_DtCvCheckOneByteCantBeginList((char)nextChar, OneByteCantBeginList)
== True)
return False;
return True;
}
/******************************************************************************
* Function: _DtCvGetNextWidth
*
* Purpose: Determines the width of the next legal segment.
*
* Returns: The width of the next legal segment.
*
*****************************************************************************/
int
_DtCvGetNextWidth (
_DtCanvasStruct *canvas,
int old_type,
int lst_hyper,
_DtCvSegmentI *pSeg,
int start,
_DtCvSegmentI *prev_seg,
_DtCvSegmentI **nextSeg,
int *nextStart,
int *widthCount)
{
int result;
int len = 0;
int tLen;
int wcFlag;
int curWidth;
int myLength;
int nextLen = 0;
void *pChar;
char *tChar;
_DtCvValue good_len;
/*
* pass over noops that don't have newlines and markers
*/
while (pSeg != NULL && (_DtCvIsSegMarker(pSeg) ||
(_DtCvIsSegNoop (pSeg) && !(_DtCvIsSegNewLine(pSeg)))))
{
pSeg = pSeg->next_seg;
start = 0;
}
if (nextSeg != NULL)
*nextSeg = pSeg;
if (nextStart != NULL)
*nextStart = start;
/*
* if the next segment is null or anything else but a string or region;
* return that there is no more after this segment.
*/
if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
return 0;
/*
* this segment is a region or string
* check for region...anything left is a string.
*/
if (_DtCvIsSegRegion(pSeg))
{
/*
* can I break on this region
*/
if (_DtCvIsSegNonBreakingChar(pSeg))
{
/*
* no...set the lengths and continue
*/
len = 1;
curWidth = _DtCvWidthOfRegionSeg(pSeg);
}
else
return 0;
}
/*
* is this a non breaking string?
*/
else if (_DtCvIsSegNonBreakingChar(pSeg))
{
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
_DtCvIsSegWideChar(pSeg), start);
len = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));
curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
+ _DtCvGetTraversalWidth(canvas, pSeg, lst_hyper);
}
/*
* so this is a string with possible breaks in it.
*/
else
{
/*
* get the string stats
*/
wcFlag = _DtCvIsSegWideChar (pSeg);
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg), wcFlag, start);
myLength = _DtCvStrLen (pChar, wcFlag);
/*
* if a single byte string, zoom through it looking for
* specific breaking characters.
*/
if (0 == wcFlag)
{
tChar = pChar;
len = 0;
do
{
/*
* checking for a hypen or space
*/
good_len = True;
result = _DtCvStrcspn ((void *) tChar, " -", 0, &tLen);
len += tLen;
/*
* check for '-'. Some of the possible combinations are:
* -text
* - text
* -/text/
* text/-text/
* text-text
* text text
*
* if it is the first character to check and there is no
* previous segment, then it is starting a line and can
* not be broken on.
*
* _DtCvStrcpn return 0 if one of the characters in the
* test string was found.
*/
if (0 == result && '-' == tChar[tLen] && 0 == len &&
NULL == prev_seg &&
_DtCvCheckLineSyntax(canvas,pSeg,start,1,True) == False)
{
len++;
tLen++;
tChar += tLen;
good_len = False;
}
} while (!good_len);
/*
* found either a space or a hypen or null byte.
* If we found a hypen, include it.
*/
if ('-' == *tChar)
len++;
curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
+ _DtCvGetTraversalWidth(canvas, pSeg, lst_hyper);
/*
* Did we find a space or hypen?
* If not, can this segment stand alone?
*/
if (result == 0 ||
_DtCvCheckLineSyntax(canvas,pSeg,start,len,False) == True)
{
if (nextSeg != NULL)
*nextSeg = pSeg;
if (nextStart != NULL)
*nextStart = start + len;
if (widthCount != NULL)
*widthCount = len;
return curWidth;
}
}
/*
* multibyte (wide char string), look for a break the hard way.
*/
else
{
len = 0;
while (len < myLength)
{
len++;
if (_DtCvCheckLineSyntax(canvas,pSeg,start,len,False) == True)
{
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
_DtCvIsSegWideChar(pSeg), start);
curWidth = _DtCvGetStringWidth(canvas,pSeg,pChar,len)
+ _DtCvGetTraversalWidth(canvas,pSeg,lst_hyper);
if (nextSeg != NULL)
*nextSeg = pSeg;
if (nextStart != NULL)
*nextStart = start + len;
if (widthCount != NULL)
*widthCount = len;
return curWidth;
}
}
/*
* Didn't find a smaller segment that satisfied the requirements.
* Determine the length of the current segment.
*/
curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
+ _DtCvGetTraversalWidth(canvas, pSeg,
lst_hyper);
}
}
/*
* sigh...need to go further...this segment can't end a line
* either.
*/
prev_seg = pSeg;
pSeg = pSeg->next_seg;
if (pSeg != NULL)
{
start = 0;
curWidth += _DtCvGetNextWidth (canvas,
_DtCvPrimaryTypeOfSeg (prev_seg), lst_hyper,
pSeg, start, prev_seg,
nextSeg, nextStart, &nextLen);
}
if (widthCount != NULL)
*widthCount = len + nextLen;
return (curWidth);
}
/******************************************************************************
* Function: _DtCvSaveInfo
*
* Initializes a line table element to the segment it should display.
*****************************************************************************/
void
_DtCvSaveInfo (
_DtCanvasStruct *canvas,
_DtCvLayoutInfo *layout,
_DtCvUnit max_width,
_DtCvUnit r_margin,
_DtCvFrmtOption txt_justify)
{
/*****************************************************************
* The ascent for a line is described as the number of units
* above the baseline.
*
* The descent for a line is described as the number of units
* below the baseline.
*
* Neither the ascent or decent value includes the baseline
****************************************************************/
int len;
int start = layout->line_start;
int count = layout->line_bytes;
long txtCnt = canvas->txt_cnt;
_DtCvUnit maxAscent = 0;
_DtCvUnit maxDescent = 0;
_DtCvUnit maxRegion = 0;
_DtCvUnit superY = 0;
_DtCvUnit subY = 0;
_DtCvUnit fontAscent;
_DtCvUnit fontDescent;
_DtCvValue fndLnk = False;
_DtCvValue visLnk = False;
void *pChar;
_DtCvSegmentI *pSeg = layout->line_seg;
if (txtCnt >= canvas->txt_max)
{
canvas->txt_max += GROW_SIZE;
if (canvas->txt_lst)
canvas->txt_lst = (_DtCvDspLine *) realloc (
(void *) canvas->txt_lst,
(sizeof(_DtCvDspLine) * canvas->txt_max));
else
canvas->txt_lst = (_DtCvDspLine *) malloc (
(sizeof(_DtCvDspLine) * canvas->txt_max));
/*
* NOTE....should this routine return a value?
* If (re)alloc error occurs, this simply ignores the problem.
*/
if (canvas->txt_lst == NULL)
{
canvas->txt_max = 0;
canvas->txt_cnt = 0;
return;
}
}
while (pSeg != NULL && count > 0)
{
/*
* set which line will this segment sit on, iff this is the
* first access to the segment.
*/
if ((void *) -1 == pSeg->internal_use)
pSeg->internal_use = (void *) txtCnt;
/*
* now get the segment's sizing so we can determine
* the height and depth of the line.
*/
len = 1;
fontAscent = 0;
fontDescent = 0;
if (_DtCvIsSegVisibleLink(pSeg))
visLnk = True;
if (_DtCvIsSegALink(pSeg))
fndLnk = True;
/*
* get the ascent and descent of the segment along with a length
*/
if (_DtCvIsSegString(pSeg))
{
_DtCvFontMetrics(canvas,_DtCvFontOfStringSeg(pSeg),
&fontAscent, &fontDescent, NULL, NULL, NULL);
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
_DtCvIsSegWideChar(pSeg), start);
len = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));
if (len > count)
len = count;
}
else if (_DtCvIsSegRegion(pSeg))
{
if (-1 == _DtCvAscentOfRegionSeg(pSeg))
{
if (maxRegion < _DtCvHeightOfRegionSeg(pSeg))
maxRegion = _DtCvHeightOfRegionSeg(pSeg);
}
else
{
fontAscent = _DtCvAscentOfRegionSeg(pSeg);
fontDescent = _DtCvHeightOfRegionSeg(pSeg) - fontAscent;
}
}
/*
* adjust the ascent and descent values by their subscript
* or superscript adjustments.
*/
if (_DtCvIsSegSuperScript(pSeg))
{
fontAscent += superY;
fontDescent -= superY;
if (_DtCvIsSegRegion(pSeg) && -1 == _DtCvAscentOfRegionSeg(pSeg)
&& maxRegion < _DtCvHeightOfRegionSeg(pSeg) + superY)
maxRegion = _DtCvHeightOfRegionSeg(pSeg) + superY;
}
else if (_DtCvIsSegSubScript(pSeg))
{
fontAscent -= subY;
fontDescent += subY;
if (_DtCvIsSegRegion(pSeg) && -1 == _DtCvAscentOfRegionSeg(pSeg)
&& maxRegion < _DtCvHeightOfRegionSeg(pSeg) + subY)
maxRegion = _DtCvHeightOfRegionSeg(pSeg) + subY;
}
else /* not a subscript or superscript */
{
/*
* set up the super and sub script offsets for following
* segments.
*/
if (_DtCvIsSegString (pSeg))
_DtCvFontMetrics(canvas,_DtCvFontOfStringSeg(pSeg),
NULL, NULL, NULL, &superY, &subY);
else if (_DtCvIsSegRegion(pSeg))
{
superY = _DtCvHeightOfRegionSeg(pSeg) * 4 / 10;
subY = superY;
}
}
/*
* now determine the maximums for ascent and descent.
*/
if (fontAscent > maxAscent)
maxAscent = fontAscent;
if (fontDescent > maxDescent)
maxDescent = fontDescent;
/*
* decrement the count
*/
count -= len;
/*
* If this segment terminates the paragraph
* force the end of the loop.
*/
pSeg = pSeg->next_disp;
start = 0;
}
if (txt_justify == _DtCvJUSTIFY_RIGHT || _DtCvJUSTIFY_CENTER == txt_justify)
{
/*
* justify the line.
*/
_DtCvUnit workWidth = max_width - layout->text_x_pos -
r_margin - layout->cur_len;
if (txt_justify == _DtCvJUSTIFY_CENTER)
workWidth = workWidth / 2;
if (workWidth < 0)
workWidth = 0;
layout->text_x_pos += workWidth;
}
/*
* adjust for any special characters found
*/
if (maxRegion > maxAscent + maxDescent + 1)
maxAscent = maxRegion - maxDescent - 1;
/*
* check to see if the max values have even been touched.
*/
if (layout->line_bytes == 0 && maxAscent == 0 && maxDescent == 0)
maxAscent = canvas->metrics.line_height;
/*
* adjust ascent and descent by the traversal and link info
*/
maxDescent += layout->leading;
if (fndLnk)
{
maxAscent += canvas->traversal_info.space_above;
maxDescent += canvas->traversal_info.space_below;
if (visLnk)
{
maxAscent += canvas->link_info.space_above;
maxDescent += canvas->link_info.space_below;
}
}
/*
* save the line information, if there is a string here.
*/
if (layout->line_bytes > 0)
{
canvas->txt_lst[txtCnt].processed = _DtCvFALSE;
canvas->txt_lst[txtCnt].text_x = layout->text_x_pos;
canvas->txt_lst[txtCnt].max_x = layout->text_x_pos;
canvas->txt_lst[txtCnt].baseline = layout->y_pos + maxAscent;
canvas->txt_lst[txtCnt].descent = maxDescent;
canvas->txt_lst[txtCnt].ascent = maxAscent;
canvas->txt_lst[txtCnt].byte_index = layout->line_start;
canvas->txt_lst[txtCnt].length = layout->line_bytes;
canvas->txt_lst[txtCnt].seg_ptr = layout->line_seg;
canvas->txt_cnt++;
}
/*
* blank line is one half the normal size line
*/
else
{
maxAscent = (maxAscent + maxDescent) / 2;
maxDescent = 0;
}
if (layout->text_x_pos + layout->cur_len > layout->cur_max_x)
layout->cur_max_x = layout->text_x_pos + layout->cur_len;
if (layout->text_x_pos + layout->cur_len > layout->max_x_pos)
layout->max_x_pos = layout->text_x_pos + layout->cur_len;
/*
* zero the string info
*/
layout->line_bytes = 0;
layout->cur_len = 0;
layout->lst_hyper = -1;
layout->lst_vis = False;
_DtCvSetJoinInfo(layout, False, -1);
/*
* adjust where the next line is positioned.
*/
layout->y_pos = layout->y_pos + maxAscent + maxDescent + 1;
}
/******************************************************************************
* Function: _DtCvCheckAddHyperToTravList
*
*****************************************************************************/
void
_DtCvCheckAddHyperToTravList (
_DtCanvasStruct *canvas,
_DtCvSegmentI *p_seg,
_DtCvValue flag,
_DtCvValue *lst_vis,
int *lst_hyper,
_DtCvUnit *cur_len)
{
int nxtHyper;
int prevIdx;
_DtCvValue junk;
_DtCvUnit retLen = *cur_len;
if (_DtCvIsSegALink (p_seg))
{
nxtHyper = _DtCvGetNextTravEntry(canvas);
if (-1 == nxtHyper)
/*
* NOTE....should this routine return a value?
* If (re)alloc error occurs, this simply ignores the problem.
*/
return;
prevIdx = nxtHyper - 1;
if (prevIdx < 0
|| _DtCvTraversalLink != canvas->trav_lst[prevIdx].type
|| p_seg->link_idx != canvas->trav_lst[prevIdx].seg_ptr->link_idx)
{
/*
* save this hypertext link in the traversal list
*/
_DtCvSetTravEntryInfo (canvas, nxtHyper, _DtCvTraversalLink, p_seg,
canvas->txt_cnt, _DtCvTRUE);
}
}
/*
* take into account the link metrics.
*/
junk = _DtCvIsSegVisibleLink(p_seg);
*lst_vis = _DtCvModifyXpos(canvas->link_info, p_seg, junk,
*lst_vis, *lst_hyper,
&retLen);
/*
* take into account the traversal metrics
*/
junk = _DtCvIsSegALink(p_seg);
(void) _DtCvModifyXpos(canvas->traversal_info, p_seg, junk,
((_DtCvValue) True), *lst_hyper,
&retLen);
*lst_hyper = p_seg->link_idx;
if (_DtCvTRUE == flag)
*cur_len = retLen;
}
/******************************************************************************
* Function: ProcessStringSegment
*
* chops a string segment up until its completely used.
*
* Returns:
* 0 if the entire string segment was processed.
* 1 if the required number of lines were processed.
*****************************************************************************/
int
_DtCvProcessStringSegment(
_DtCanvasStruct *canvas,
_DtCvLayoutInfo *lay_info,
_DtCvUnit max_width,
_DtCvUnit l_margin,
_DtCvUnit r_margin,
_DtCvSegmentI *cur_seg,
unsigned int *cur_start,
_DtCvFrmtOption txt_justify,
_DtCvValue stat_flag)
{
_DtCvUnit workWidth;
_DtCvUnit stringLen;
_DtCvUnit textWidth;
_DtCvUnit nWidth;
_DtCvUnit spaceSize = 0;
int oldType;
int retStart;
int retCount;
wchar_t *wcp;
void *pChar;
char *strPtr;
_DtCvValue done = False;
_DtCvSegmentI *retSeg;
if (NULL != _DtCvStringOfStringSeg(cur_seg))
{
if (lay_info->cur_len == 0)
{
lay_info->line_seg = cur_seg;
lay_info->line_start = *cur_start;
}
if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
lay_info->delayed_search_saves++;
oldType = _DtCvPrimaryTypeOfSeg (cur_seg);
/*
* is alignment in effect?
*/
if (TRUE == lay_info->align_flag)
{
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg), *cur_start);
nWidth = _DtCvStrcspn (pChar, lay_info->align_char,
_DtCvIsSegWideChar(cur_seg),
&stringLen);
if (-1 == nWidth)
return -1;
/*
* we got a valid length back, calculate the length
*/
textWidth = 0;
if (0 != stringLen)
textWidth = _DtCvGetStringWidth(canvas,cur_seg,pChar,stringLen);
/*
* check to see if this a hypertext that needs
* to be remembered.
*/
_DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
&(lay_info->lst_vis),
&(lay_info->lst_hyper),
&(lay_info->cur_len));
/*
* update the length and position information
* to skip past the characters before the alignment character.
*/
lay_info->line_bytes += stringLen;
lay_info->cur_len += (textWidth
+ _DtCvGetTraversalWidth(canvas,
cur_seg, lay_info->lst_hyper));
*cur_start += stringLen;
/*
* if we didn't find the character, check to see if this forces
* a newline - honor it if it does. We'll check the next
* string segment for the alignment character.
*/
if (1 == nWidth && _DtCvIsSegNewLine (cur_seg)
&& lay_info->line_bytes)
{
_DtCvSaveInfo (canvas,lay_info,max_width,r_margin,txt_justify);
while (lay_info->delayed_search_saves > 0) {
_DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
lay_info->delayed_search_saves--;
}
return 0;
}
/*
* so we found the character, now get it's width.
*/
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg), *cur_start);
textWidth = _DtCvGetStringWidth(canvas, cur_seg, pChar, 1)
+ _DtCvGetTraversalWidth(canvas, cur_seg,
lay_info->lst_hyper);
/*
* is this the second or more align position?
* if so, need to shift the character to align with others.
*/
if (lay_info->align_pos >
lay_info->text_x_pos + lay_info->cur_len + textWidth / 2)
lay_info->text_x_pos = lay_info->align_pos - lay_info->cur_len
- textWidth / 2;
/*
* otherwise, does this exceed the previous alignments?
* if so, the table processing should catch that we've
* changed the alignment position and re-format the others.
*/
else if (lay_info->align_pos <
lay_info->text_x_pos + lay_info->cur_len + textWidth / 2)
lay_info->align_pos =
lay_info->text_x_pos + lay_info->cur_len + textWidth / 2;
/*
* indicate that the character has been found.
*/
lay_info->align_flag = False;
/*
* check to see if this item can end a line.
* if can't end the line, force a join for the next segment or
* for the rest of this segment.
*/
if (False == _DtCvCheckLineSyntax(canvas,cur_seg,*cur_start,1,False))
lay_info->join = True;
/*
* update the length and position information to
* include the character.
*/
lay_info->line_bytes++;
lay_info->cur_len += textWidth;
*cur_start += 1;
/*
* check to see if this is the end of the segment.
*/
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg), *cur_start);
if ((_DtCvIsSegWideChar(cur_seg) && 0 == *((wchar_t *) pChar))
||
(_DtCvIsSegRegChar(cur_seg) && '\0' == *((char *) pChar)))
return 0;
}
while (1)
{
/*
* recalculate the width
*/
workWidth = max_width - lay_info->text_x_pos -
lay_info->cur_len - r_margin;
/*
* adjust the character pointer and get the
* length of the string.
*/
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg), *cur_start);
stringLen = _DtCvStrLen (pChar, _DtCvIsSegWideChar(cur_seg));
/*
* get the pixel width of the text string.
*/
textWidth = _DtCvGetStringWidth(canvas,cur_seg,pChar,stringLen)
+ _DtCvGetTraversalWidth(canvas, cur_seg,
lay_info->lst_hyper);
/*
* Will it fit in the current width?
*/
if (stat_flag == True || textWidth <= workWidth)
{
/*
* Yes, this segment or part of a segment can fit in the
* current width. But can the last character of this
* segment end a line and can the beginning of the next
* segment start a new line?
*/
if (stat_flag == True ||
_DtCvCheckLineSyntax (canvas, cur_seg,
*cur_start, stringLen, False) == TRUE)
{
/*
* check to see if this a hypertext that needs
* to be remembered.
*/
_DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
&(lay_info->lst_vis),
&(lay_info->lst_hyper),
&(lay_info->cur_len));
/*
* The line syntax is good.
* Update the global and width variables.
*/
lay_info->line_bytes += stringLen;
lay_info->cur_len += textWidth;
_DtCvSetJoinInfo(lay_info,
_DtCvIsSegNonBreakingChar(cur_seg),
-1);
/*
* Check to see if this segment forces an end
*/
if (_DtCvIsSegNewLine (cur_seg) && lay_info->line_bytes) {
_DtCvSaveInfo (canvas, lay_info, max_width,
r_margin, txt_justify);
while (lay_info->delayed_search_saves > 0) {
_DtCvSetSearchEntryInfo(canvas,
canvas->txt_cnt - 1);
lay_info->delayed_search_saves--;
}
}
return 0;
}
/*
* CheckLineSyntax says that either this line couldn't
* end a line or the next segment couldn't start a line.
* Therefore, find out how much of the next segment or
* segments we need to incorporate to satisfy the Line
* Syntax rules.
*/
nWidth = _DtCvGetNextWidth (canvas, oldType,
lay_info->lst_hyper,
cur_seg->next_seg, 0, cur_seg,
&retSeg, &retStart, &retCount);
/*
* will this segment + the next segment fit?
*/
if (textWidth + nWidth <= workWidth)
{
/*
* check to see if this a hypertext that needs
* to be remembered.
*/
_DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
&(lay_info->lst_vis),
&(lay_info->lst_hyper),
&(lay_info->cur_len));
/*
* YEAH Team!! It Fits!!
*
* Update the global and width variables.
*/
lay_info->line_bytes += stringLen;
lay_info->cur_len += textWidth;
_DtCvSetJoinInfo(lay_info, False, -1);
return 0;
}
}
/*
* the text width plus the next segment is tooo big
* to fit. Reduce the current segment if possible
*/
done = False;
textWidth = 0;
stringLen = 0;
while (!done)
{
nWidth = _DtCvGetNextWidth (canvas, oldType,
lay_info->lst_hyper,
cur_seg, *cur_start, NULL,
&retSeg, &retStart, &retCount);
if (retSeg == cur_seg && textWidth + nWidth <= workWidth)
{
/*
* check to see if this a hypertext that needs
* to be remembered.
*/
_DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
&(lay_info->lst_vis),
&(lay_info->lst_hyper),
&(lay_info->cur_len));
_DtCvSetJoinInfo(lay_info, False, -1);
*cur_start = retStart;
stringLen += retCount;
textWidth += nWidth;
spaceSize = 0;
/*
* take into account a space if that is where it breaks.
*/
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg),
*cur_start);
if ((_DtCvIsSegWideChar(cur_seg) &&
(' ' == *((wchar_t *) pChar)))
||
(_DtCvIsSegRegChar(cur_seg) &&
(' ' == *((char *) pChar))))
{
spaceSize = _DtCvGetStringWidth(canvas,
cur_seg, pChar, 1)
+ _DtCvGetTraversalWidth (canvas,
cur_seg, lay_info->lst_hyper);
textWidth += spaceSize;
stringLen++;
(*cur_start)++;
}
}
else
{
/*
* Done trying to find a segment that will
* fit in the size given
*/
done = True;
}
}
/*
* Update the global variables
*/
lay_info->line_bytes += stringLen;
lay_info->cur_len += textWidth;
if (lay_info->join == True || lay_info->line_bytes == 0)
{
/*
* This line would be empty if we followed the rules.
* Or it would break a line improperly.
* Force this onto the line.
* check to see if this a hypertext that needs
* to be remembered.
*/
_DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
&(lay_info->lst_vis),
&(lay_info->lst_hyper),
&(lay_info->cur_len));
/*
* Couldn't find a smaller, have to
* go with the larger segment.
*/
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg),
*cur_start);
stringLen = _DtCvStrLen (pChar, _DtCvIsSegWideChar(cur_seg));
if (retCount > 0 && retCount < stringLen)
stringLen = retCount;
lay_info->line_bytes += stringLen;
lay_info->cur_len += (_DtCvGetStringWidth(canvas, cur_seg,
pChar, stringLen)
+ _DtCvGetTraversalWidth (canvas,
cur_seg, lay_info->lst_hyper));
_DtCvSetJoinInfo(lay_info, False, -1);
/*
* If we had to do a bigger segment,
* then we're done processing the target segment.
*/
if (stringLen == _DtCvStrLen(pChar,_DtCvIsSegWideChar(cur_seg)))
{
if (_DtCvCheckLineSyntax (canvas, cur_seg,
*cur_start, stringLen, False) == False)
_DtCvSetJoinInfo(lay_info, True, -1);
else if (_DtCvIsSegNewLine (cur_seg)) {
_DtCvSaveInfo (canvas, lay_info, max_width,
r_margin, txt_justify);
while (lay_info->delayed_search_saves > 0) {
_DtCvSetSearchEntryInfo(canvas,
canvas->txt_cnt - 1);
lay_info->delayed_search_saves--;
}
}
return 0;
}
*cur_start = retStart;
}
else if (spaceSize)
{
/*
* If a space was included as the last character,
* remove it now.
*/
lay_info->line_bytes--;
lay_info->cur_len -= spaceSize;
}
/*
* Save the information
*/
_DtCvSaveInfo (canvas, lay_info, max_width, r_margin, txt_justify);
if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
lay_info->delayed_search_saves--;
while (lay_info->delayed_search_saves > 0) {
_DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
lay_info->delayed_search_saves--;
}
/*
* Skip the spaces.
*/
pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
_DtCvIsSegWideChar(cur_seg),
*cur_start);
if (_DtCvIsSegWideChar(cur_seg))
{
wcp = pChar;
while (' ' == *wcp)
{
wcp++;
(*cur_start)++;
}
pChar = wcp;
}
else /* single byte string */
{
strPtr = pChar;
while (' ' == *strPtr)
{
strPtr++;
(*cur_start)++;
}
pChar = strPtr;
}
/*
* are we at the end of the segment?
*/
if ((_DtCvIsSegWideChar(cur_seg) && 0 == *((wchar_t *) pChar))
||
(_DtCvIsSegRegChar(cur_seg) && 0 == *((char *) pChar)))
return 0;
if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
lay_info->delayed_search_saves++;
/*
* Initialize the global variables
*/
lay_info->line_seg = cur_seg;
lay_info->line_start = *cur_start;
lay_info->text_x_pos = l_margin;
if (CheckFormat(lay_info) == True)
return 1;
/*
* check to see if this a hypertext that needs
* to be remembered.
*/
_DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
&(lay_info->lst_vis),
&(lay_info->lst_hyper),
&(lay_info->cur_len));
}
}
else if (_DtCvIsSegNewLine (cur_seg))
{
/*
* Force a save - even if it is an empty line.
*/
_DtCvSaveInfo (canvas, lay_info, max_width, r_margin, txt_justify);
while (lay_info->delayed_search_saves > 0) {
_DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
lay_info->delayed_search_saves--;
}
}
return 0;
} /* End _DtCvProcessStringSegment */
/******************************************************************************
* Function: _DtCvSetJoinInfo
*
* Returns: sets the joining information to the given information.
*
*****************************************************************************/
void
_DtCvSetJoinInfo (
_DtCvLayoutInfo *lay_info,
_DtCvValue flag,
int txt_ln)
{
lay_info->join = flag;
lay_info->join_line = txt_ln;
}
/******************************************************************************
* Function: _DtCvGetNextTravEntry
*
* Returns: >= 0 if success,
* -1 if failure.
*
* Purpose: Return the next available entry in the traversal list.
*
*****************************************************************************/
int
_DtCvGetNextTravEntry (
_DtCanvasStruct *canvas)
{
int nxtEntry = canvas->trav_cnt;
/*
* does the list need to grow?
*/
if (nxtEntry >= canvas->trav_max)
{
/*
* grow by a set amount
*/
canvas->trav_max += GROW_SIZE;
/*
* realloc or malloc?
*/
if (NULL != canvas->trav_lst)
canvas->trav_lst = (_DtCvTraversalInfo *) realloc (
(char *) canvas->trav_lst,
((sizeof(_DtCvTraversalInfo)) * canvas->trav_max));
else
canvas->trav_lst = (_DtCvTraversalInfo *) malloc (
((sizeof(_DtCvTraversalInfo)) * canvas->trav_max));
/*
* did the memory allocation work? if not return error code.
*/
if (NULL == canvas->trav_lst)
{
canvas->trav_max = 0;
canvas->trav_cnt = 0;
nxtEntry = -1;
}
}
canvas->trav_lst[nxtEntry] = DefTravData;
return nxtEntry;
}
/******************************************************************************
* Function: _DtCvSetTravEntryInfo
*
* Returns: 0 if success,
* -1 if failure.
*
* Purpose: Set the high level information in an entry of the traversal
* list.
*****************************************************************************/
int
_DtCvSetTravEntryInfo (
_DtCanvasStruct *canvas,
int entry,
_DtCvTraversalType type,
_DtCvSegmentI *p_seg,
int line_idx,
_DtCvValue inc)
{
int result = -1;
if (-1 != entry && entry <= canvas->trav_cnt)
{
_DtCvTraversalInfo *travEntry = &(canvas->trav_lst[entry]);
travEntry->type = type;
travEntry->seg_ptr = p_seg;
travEntry->idx = line_idx;
if (_DtCvTRUE == inc)
canvas->trav_cnt++;
result = 0;
}
return result;
}
int
_DtCvGetNextSearchEntry(_DtCanvasStruct* canvas)
{
if (canvas->search_cnt >= canvas->search_max) {
canvas->search_max += GROW_SIZE;
if (canvas->searchs)
canvas->searchs = (_DtCvSearchData *)
realloc((void*)canvas->searchs,
canvas->search_max * sizeof(_DtCvSearchData));
else
canvas->searchs = (_DtCvSearchData *)
malloc(canvas->search_max * sizeof(_DtCvSearchData));
}
canvas->searchs[canvas->search_cnt].idx = -1;
return canvas->search_cnt++;
}
int
_DtCvSetSearchEntryInfo(_DtCanvasStruct* canvas, int line_idx)
{
int search_idx;
/* get a next available slot for search */
search_idx = _DtCvGetNextSearchEntry(canvas);
/* save information (i.e. line_idx) */
canvas->searchs[search_idx].idx = line_idx;
}
/******************************************************************************
* Function: _DtCvSetTravEntryPos
*
* Returns: 0 if success,
* -1 if failure.
*
* Purpose: Set the position and dimension information of an entry in
* the traversal list.
*
*****************************************************************************/
int
_DtCvSetTravEntryPos (
_DtCanvasStruct *canvas,
int entry,
_DtCvUnit x,
_DtCvUnit y,
_DtCvUnit width,
_DtCvUnit height)
{
int result = -1;
if (-1 != entry && entry <= canvas->trav_cnt)
{
_DtCvTraversalInfo *travEntry = &(canvas->trav_lst[entry]);
travEntry->x_pos = x;
travEntry->y_pos = y;
travEntry->width = width;
travEntry->height = height;
result = 0;
}
return result;
}
/******************************************************************************
* Function: _DtCvCalcMarkPos
*
* Returns: 0 if success,
* -1 if failure.
*
* Purpose: Calcalate the position and dimension information of a mark.
*
*****************************************************************************/
int
_DtCvCalcMarkPos (
_DtCanvasStruct *canvas,
int entry,
_DtCvUnit *ret_x,
_DtCvUnit *ret_y,
_DtCvUnit *ret_width,
_DtCvUnit *ret_height)
{
int result = -1;
if (-1 != entry && entry <= canvas->mark_cnt)
{
_DtCvMarkData *mark = &(canvas->marks[entry]);
/*
* if we've got a line index for the mark, get the positions.
*/
if (-1 != mark->beg.line_idx && -1 != mark->end.line_idx)
{
_DtCvDspLine *line = &(canvas->txt_lst[mark->beg.line_idx]);
*ret_x = mark->beg.x;
*ret_y = mark->beg.y - line->ascent;
if (mark->beg.line_idx == mark->end.line_idx)
*ret_width = mark->end.x - *ret_x;
else
*ret_width = canvas->txt_lst[mark->beg.line_idx].max_x - *ret_x;
*ret_height = line->ascent + line->descent + 1;
result = 0;
}
}
return result;
}
/******************************************************************************
* Function: _DtCvSortTraversalList
*
* Returns: nothing
*
* Purpose: Sort the traversal list
*
*****************************************************************************/
void
_DtCvSortTraversalList (
_DtCanvasStruct *canvas,
_DtCvValue retain)
{
int curTrav = canvas->cur_trav;
if (1 < canvas->trav_cnt)
{
/*
* indicate this is the current traversal
*/
if (-1 != curTrav)
canvas->trav_lst[curTrav].active = retain;
/*
* sort the items.
*/
qsort (canvas->trav_lst, canvas->trav_cnt, sizeof(_DtCvTraversalInfo),
CompareTraversalPos);
if (_DtCvTRUE == retain && -1 != curTrav &&
_DtCvFALSE == canvas->trav_lst[curTrav].active)
{
curTrav = 0;
while (_DtCvFALSE == canvas->trav_lst[curTrav].active)
curTrav++;
canvas->cur_trav = curTrav;
}
/*
* clear the active flag
*/
if (-1 != curTrav)
canvas->trav_lst[curTrav].active = _DtCvFALSE;
}
}
/*****************************************************************************
* Function: _DtCvCvtSegsToPts()
*
* Purpose: Given a set of segments, determine the ending points.
*
*****************************************************************************/
_DtCvStatus
_DtCvCvtSegsToPts (
_DtCanvasStruct *canvas,
_DtCvSegPtsI **segs,
_DtCvSelectData *beg,
_DtCvSelectData *end,
_DtCvUnit *ret_y1,
_DtCvUnit *ret_y2,
_DtCvSegmentI **ret_seg)
{
int count;
int cnt;
int start;
int length;
long lineIdx;
int linkIdx = -1;
_DtCvValue lastVisLnk = _DtCvFALSE;
_DtCvUnit minY = -1;
_DtCvUnit maxY = 0;
_DtCvUnit startX;
_DtCvUnit endX;
_DtCvUnit segWidth;
_DtCvSegmentI *pSeg;
_DtCvSegmentI *saveSeg;
_DtCvSegmentI **retSeg;
_DtCvDspLine *lines = canvas->txt_lst;
_DtCvFlags result = _DtCvSTATUS_NONE;
_DtCvSelectData *tmpBeg;
_DtCvSelectData *tmpEnd;
_DtCvSelectData bReg;
_DtCvSelectData eReg;
/*
* initialize the structures.
*/
bReg = DefSelectData;
eReg = DefSelectData;
*beg = DefSelectData;
*end = DefSelectData;
/*
* go through each segment and determine the starting positions.
*/
while (NULL != *segs)
{
result = _DtCvSTATUS_OK;
/*
* what line is this segment on?
*/
lineIdx = (long) ((*segs)->segment->internal_use);
/*
* get some information about the line
*/
length = lines[lineIdx].length;
start = lines[lineIdx].byte_index;
startX = _DtCvGetStartXOfLine(&(lines[lineIdx]), &pSeg);
pSeg = lines[lineIdx].seg_ptr;
/*
* now skip the segments on this line that aren't in the data pt.
*/
while (NULL != pSeg && pSeg != (*segs)->segment)
{
/*
* advance past any hypertext link offsets.
*/
startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
&linkIdx, &lastVisLnk);
/*
* we know that this is not the segment we are looking for,
* so go past it.
*/
_DtCvGetWidthOfSegment(canvas, pSeg, start, length,
&cnt, &segWidth, NULL);
/*
* skip the segment's width, decrease the overall length by
* the segment's count, reset the character start point and
* go to the next segment.
*/
startX += segWidth;
length -= cnt;
start = 0;
pSeg = pSeg->next_disp;
}
/*
* This segment should be all or partially selected.
*/
if (NULL == pSeg)
return _DtCvSTATUS_BAD;
/*
* now figure the start location.
*/
startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
&linkIdx, &lastVisLnk);
/*
* guarenteed that this is the *first* line that the segment
* exists on. Therefore, may have to go to another line for
* the correct offset
*/
while (start + length < (*segs)->offset)
{
do { lineIdx++; } while (lineIdx < canvas->txt_cnt
&& pSeg != lines[lineIdx].seg_ptr);
if (lineIdx >= canvas->txt_cnt)
return _DtCvSTATUS_BAD;
length = lines[lineIdx].length;
start = lines[lineIdx].byte_index;
startX = lines[lineIdx].text_x;
linkIdx = -1;
lastVisLnk = False;
startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
&linkIdx, &lastVisLnk);
}
/*
* how many characters do we need to skip?
*/
count = (*segs)->offset - start;
segWidth = 0;
if (0 < count)
_DtCvGetWidthOfSegment(canvas, pSeg, start, count,
&cnt, &segWidth, NULL);
/*
* adjust the info by the width of the skipped characters.
*/
start += count;
length -= count;
startX += segWidth;
/*
* is this a region? If so set the region information instead.
*/
tmpBeg = beg;
tmpEnd = end;
retSeg = ret_seg;
if (_DtCvIsSegRegion((*segs)->segment))
{
tmpBeg = &bReg;
tmpEnd = &eReg;
retSeg = &saveSeg;
}
/*
* does this segment start the selection? text or region?
*/
if (tmpBeg->x == -1 || tmpBeg->y > lines[lineIdx].baseline ||
(tmpBeg->line_idx == lineIdx && tmpBeg->x > startX))
{
tmpBeg->x = startX;
tmpBeg->y = lines[lineIdx].baseline;
tmpBeg->line_idx = lineIdx;
tmpBeg->char_idx = lines[lineIdx].length - length;
if (NULL != retSeg)
*retSeg = (*segs)->segment;
}
/*
* get the amount of this segment that is selected.
*/
count = (*segs)->len;
/*
* is it longer than what's (left) on this line?
*/
while (count > length)
{
/*
* go to the next line containing the segment
*/
do {
/*
* does this line have the minium y?
*/
if (minY == -1 ||
minY > lines[lineIdx].baseline - lines[lineIdx].ascent)
minY = lines[lineIdx].baseline - lines[lineIdx].ascent;
lineIdx++;
} while (lineIdx < canvas->txt_cnt
&& pSeg != lines[lineIdx].seg_ptr);
/*
* did we run out of lines?
*/
if (lineIdx >= canvas->txt_cnt)
return _DtCvSTATUS_BAD;
/*
* start over on this line
*/
segWidth = 0;
/*
* get the true count to the next offset
*/
cnt = lines[lineIdx].byte_index - start;
/*
* get the next lines starting info.
*/
start = lines[lineIdx].byte_index;
length = lines[lineIdx].length;
startX = _DtCvGetStartXOfLine(&(lines[lineIdx]), &pSeg);
linkIdx = -1;
lastVisLnk = False;
startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
&linkIdx, &lastVisLnk);
/*
* subtract the previous length
*/
count -= cnt;
}
/*
* now go down the line, examining each segment.
*/
while (0 < count)
{
/*
* findout how many characters are in the next segment, and its
* width.
*/
_DtCvGetWidthOfSegment(canvas,pSeg,start,count,&cnt,&segWidth,NULL);
/*
* there are less than in the count, go to the next segment.
*/
if (cnt < count)
{
pSeg = pSeg->next_disp;
start = 0;
startX += segWidth;
}
length -= cnt;
count -= cnt;
}
endX = startX + segWidth;
/*
* does this segment end a segment?
*/
if (tmpEnd->x == -1 || tmpEnd->y < lines[lineIdx].baseline ||
(tmpEnd->line_idx == lineIdx && tmpEnd->x < endX))
{
tmpEnd->x = endX;
tmpEnd->y = lines[lineIdx].baseline;
tmpEnd->line_idx = lineIdx;
tmpEnd->char_idx = lines[lineIdx].length - length;
}
/*
* check for min and max values
*/
if (minY == -1 ||
minY > lines[lineIdx].baseline - lines[lineIdx].ascent)
minY = lines[lineIdx].baseline - lines[lineIdx].ascent;
if (maxY < lines[lineIdx].baseline + lines[lineIdx].descent)
maxY = lines[lineIdx].baseline + lines[lineIdx].descent;
/*
* go to the next segment
*/
segs++;
}
/*
* now determine if a region really starts the beginning of a
* selection or a text does.
*
* was a region found?
*/
if (-1 != bReg.x)
{
/*
* if no text was found, take the region information.
*/
if (-1 == beg->x)
{
*beg = bReg;
if (NULL != ret_seg)
*ret_seg = saveSeg;
}
/*
* or if the region is inline to the other
* text and it is before the text, then take it's x value.
*/
else if (bReg.x < beg->x &&
(bReg.line_idx == beg->line_idx ||
/*
* Or if the region is 'standalone' (a bullet of a list, a
* graphic to wrap around, etc.) then check to see if it
* straddles the other information and is before the text. If
* it does, take it's x value.
*/
_DtCvStraddlesPt(beg->y,
bReg.y - lines[bReg.line_idx].ascent,
bReg.y - lines[bReg.line_idx].descent)))
{
beg->x = bReg.x;
if (NULL != ret_seg)
*ret_seg = saveSeg;
}
}
/*
* now determine if a region really ends the selection or a text does.
*
* was a region found?
*/
if (-1 != eReg.x)
{
/*
* if no text was found, take the region information.
*/
if (-1 == end->x)
*end = eReg;
/*
* or if the region is inline to the other
* text and it is before the text, then take it's x value.
*/
else if (eReg.x > end->x &&
(eReg.line_idx == end->line_idx ||
/*
* Or if the region is 'standalone' (a bullet of a list, a
* graphic to wrap around, etc.) then check to see if it
* straddles the other information and is before the text. If
* it does, take it's x value.
*/
_DtCvStraddlesPt(end->y,
eReg.y - lines[eReg.line_idx].ascent,
eReg.y - lines[eReg.line_idx].descent)))
end->x = eReg.x;
}
if (NULL != ret_y1)
*ret_y1 = minY;
if (NULL != ret_y2)
*ret_y2 = maxY;
return result;
}
/*****************************************************************************
* Function: _DtCvAddToMarkList()
*
* Purpose: Add a mark to the list of marks.
*
*****************************************************************************/
int
_DtCvAddToMarkList (
_DtCanvasStruct *canvas,
_DtCvPointer client_data,
_DtCvValue flag,
_DtCvSelectData *beg,
_DtCvSelectData *end)
{
_DtCvMarkData *nxtMark;
/*
* does the array need more memory?
*/
if (canvas->mark_cnt >= canvas->mark_max)
{
canvas->mark_max += GROW_SIZE;
if (NULL == canvas->marks)
canvas->marks = (_DtCvMarkData *) malloc(
sizeof(_DtCvMarkData) * canvas->mark_max);
else
canvas->marks = (_DtCvMarkData *) realloc((void *) canvas->marks,
sizeof(_DtCvMarkData) * canvas->mark_max);
/*
* memory loss - bail
*/
if (NULL == canvas->marks)
return -1;
}
/*
* set the mark information
*/
nxtMark = &(canvas->marks[canvas->mark_cnt]);
nxtMark->on = flag;
nxtMark->client_data = client_data;
nxtMark->beg = *beg;
nxtMark->end = *end;
canvas->mark_cnt++;
return (canvas->mark_cnt - 1);
}