cdesktopenv/cde/lib/DtHelp/Selection.c

1730 lines
44 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 librararies 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;
}