2002-02-19 18:11:48 +00:00
|
|
|
/*************************************************************************
|
|
|
|
*
|
|
|
|
* $RCSfile: accportions.cxx,v $
|
|
|
|
*
|
2002-02-21 13:55:31 +00:00
|
|
|
* $Revision: 1.3 $
|
2002-02-19 18:11:48 +00:00
|
|
|
*
|
2002-02-21 13:55:31 +00:00
|
|
|
* last change: $Author: dvo $ $Date: 2002-02-21 14:55:31 $
|
2002-02-19 18:11:48 +00:00
|
|
|
*
|
|
|
|
* The Contents of this file are made available subject to the terms of
|
|
|
|
* either of the following licenses
|
|
|
|
*
|
|
|
|
* - GNU Lesser General Public License Version 2.1
|
|
|
|
* - Sun Industry Standards Source License Version 1.1
|
|
|
|
*
|
|
|
|
* Sun Microsystems Inc., October, 2000
|
|
|
|
*
|
|
|
|
* GNU Lesser General Public License Version 2.1
|
|
|
|
* =============================================
|
|
|
|
* Copyright 2000 by Sun Microsystems, Inc.
|
|
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it 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 this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
|
|
* MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Sun Industry Standards Source License Version 1.1
|
|
|
|
* =================================================
|
|
|
|
* The contents of this file are subject to the Sun Industry Standards
|
|
|
|
* Source License Version 1.1 (the "License"); You may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of the
|
|
|
|
* License at http://www.openoffice.org/license.html.
|
|
|
|
*
|
|
|
|
* Software provided under this License is provided on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
|
|
|
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
|
|
|
|
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
|
|
|
|
* See the License for the specific provisions governing your rights and
|
|
|
|
* obligations concerning the Software.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
|
|
|
|
*
|
|
|
|
* Copyright: 2000 by Sun Microsystems, Inc.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s): _______________________________________
|
|
|
|
*
|
|
|
|
*
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
#ifndef _ACCPORTIONS_HXX
|
|
|
|
#include "accportions.hxx"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _TOOLS_DEBUG_HXX
|
|
|
|
#include <tools/debug.hxx>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _RTL_USTRING_HXX_
|
|
|
|
#include <rtl/ustring.hxx>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _COM_SUN_STAR_I18N_BOUNDARY_HPP_
|
|
|
|
#include <com/sun/star/i18n/Boundary.hpp>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// for GetWordBoundary, GetSentenceBoundary:
|
|
|
|
#ifndef _BREAKIT_HXX
|
|
|
|
#include <breakit.hxx>
|
|
|
|
#endif
|
|
|
|
#ifndef _COM_SUN_STAR_I18N_WORDTYPE_HPP_
|
|
|
|
#include <com/sun/star/i18n/WordType.hpp>
|
|
|
|
#endif
|
|
|
|
#ifndef _COM_SUN_STAR_I18N_XBREAKITERATOR_HPP_
|
|
|
|
#include <com/sun/star/i18n/XBreakIterator.hpp>
|
|
|
|
#endif
|
|
|
|
#ifndef _NDTXT_HXX
|
|
|
|
#include <ndtxt.hxx>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using rtl::OUString;
|
|
|
|
using com::sun::star::i18n::Boundary;
|
|
|
|
|
|
|
|
// for GetWordBoundary
|
|
|
|
using com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES;
|
|
|
|
|
|
|
|
SwAccessiblePortionData::SwAccessiblePortionData(
|
|
|
|
const String& rCoreString ) :
|
|
|
|
SwPortionHandler(),
|
|
|
|
aBuffer(),
|
|
|
|
nModelPosition( 0 ),
|
|
|
|
bFinished( sal_False ),
|
|
|
|
sModelString( rCoreString ),
|
|
|
|
sAccessibleString(),
|
|
|
|
aLineBreaks(),
|
|
|
|
aModelPositions(),
|
|
|
|
aAccessiblePositions(),
|
|
|
|
pWords( NULL ),
|
|
|
|
pSentences( NULL )
|
|
|
|
{
|
|
|
|
// reserve some space to reduce memory allocations
|
|
|
|
aLineBreaks.reserve( 5 );
|
|
|
|
aModelPositions.reserve( 10 );
|
|
|
|
aAccessiblePositions.reserve( 10 );
|
|
|
|
|
|
|
|
// always include 'first' line-break position
|
|
|
|
aLineBreaks.push_back( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
SwAccessiblePortionData::~SwAccessiblePortionData()
|
|
|
|
{
|
|
|
|
delete pWords;
|
|
|
|
delete pSentences;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::Text(USHORT nLength)
|
|
|
|
{
|
|
|
|
DBG_ASSERT( nLength >= 0, "illegal length" );
|
|
|
|
DBG_ASSERT( (nModelPosition + nLength) <= sModelString.getLength(),
|
|
|
|
"portion exceeds model string!" )
|
|
|
|
|
|
|
|
DBG_ASSERT( !bFinished, "We are already done!" );
|
|
|
|
|
|
|
|
// store 'old' positions
|
|
|
|
aModelPositions.push_back( nModelPosition );
|
|
|
|
aAccessiblePositions.push_back( aBuffer.getLength() );
|
|
|
|
|
|
|
|
// update buffer + nModelPosition
|
|
|
|
aBuffer.append( sModelString.copy(nModelPosition, nLength) );
|
|
|
|
nModelPosition += nLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::Special(
|
|
|
|
USHORT nLength, const String& rText, USHORT nType)
|
|
|
|
{
|
|
|
|
DBG_ASSERT( nLength >= 0, "illegal length" );
|
|
|
|
DBG_ASSERT( (nModelPosition + nLength) <= sModelString.getLength(),
|
|
|
|
"portion exceeds model string!" )
|
|
|
|
|
|
|
|
DBG_ASSERT( !bFinished, "We are already done!" );
|
|
|
|
|
|
|
|
// for now, ignore the nType variable
|
|
|
|
|
|
|
|
// store 'old' positions
|
|
|
|
aModelPositions.push_back( nModelPosition );
|
|
|
|
aAccessiblePositions.push_back( aBuffer.getLength() );
|
|
|
|
|
|
|
|
// update buffer + nModelPosition
|
|
|
|
aBuffer.append( OUString(rText) );
|
|
|
|
nModelPosition += nLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::LineBreak()
|
|
|
|
{
|
|
|
|
DBG_ASSERT( !bFinished, "We are already done!" );
|
|
|
|
|
|
|
|
aLineBreaks.push_back( aBuffer.getLength() );
|
|
|
|
}
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
void SwAccessiblePortionData::Skip(USHORT nLength)
|
|
|
|
{
|
|
|
|
DBG_ASSERT( !bFinished, "We are already done!" );
|
|
|
|
DBG_ASSERT( aModelPositions.size() == 0, "Never Skip() after portions" );
|
|
|
|
|
|
|
|
nModelPosition += nLength;
|
|
|
|
}
|
|
|
|
|
2002-02-19 18:11:48 +00:00
|
|
|
void SwAccessiblePortionData::Finish()
|
|
|
|
{
|
|
|
|
DBG_ASSERT( !bFinished, "We are already done!" );
|
|
|
|
|
2002-02-20 14:22:26 +00:00
|
|
|
// include terminator values: always include two 'last character'
|
|
|
|
// markers in the position arrays to make sure we always find one
|
|
|
|
// position before the end
|
|
|
|
Text( 0 );
|
|
|
|
Text( 0 );
|
|
|
|
LineBreak();
|
|
|
|
LineBreak();
|
2002-02-19 18:11:48 +00:00
|
|
|
|
2002-02-20 14:22:26 +00:00
|
|
|
sAccessibleString = aBuffer.makeStringAndClear();
|
|
|
|
bFinished = sal_True;
|
2002-02-19 18:11:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const OUString& SwAccessiblePortionData::GetAccesibleString()
|
|
|
|
{
|
|
|
|
DBG_ASSERT( bFinished, "Shouldn't call this before we are done!" );
|
|
|
|
|
|
|
|
return sAccessibleString;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::GetLineBoundary(
|
|
|
|
Boundary& rBound,
|
|
|
|
sal_Int32 nPos )
|
|
|
|
{
|
|
|
|
FillBoundary( rBound, aLineBreaks,
|
|
|
|
FindBreak( aLineBreaks, nPos ) );
|
|
|
|
}
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
USHORT SwAccessiblePortionData::GetModelPosition( sal_Int32 nPos )
|
2002-02-19 18:11:48 +00:00
|
|
|
{
|
|
|
|
DBG_ASSERT( nPos >= 0, "illegal position" );
|
2002-02-20 14:22:26 +00:00
|
|
|
DBG_ASSERT( nPos <= sAccessibleString.getLength(), "illegal position" );
|
2002-02-19 18:11:48 +00:00
|
|
|
|
|
|
|
// find the portion number
|
2002-02-21 13:55:31 +00:00
|
|
|
size_t nPortionNo = FindBreak( aAccessiblePositions, nPos );
|
2002-02-19 18:11:48 +00:00
|
|
|
|
|
|
|
// get model portion size
|
|
|
|
sal_Int32 nStartPos = aModelPositions[nPortionNo];
|
|
|
|
sal_Int32 nEndPos = aModelPositions[nPortionNo+1];
|
|
|
|
|
|
|
|
// if the model portion has more than one position, go into it;
|
|
|
|
// else return that position
|
|
|
|
if( (nEndPos - nStartPos) > 1 )
|
|
|
|
{
|
|
|
|
// 'wide' portions have to be of the same with
|
|
|
|
DBG_ASSERT( ( nEndPos - nStartPos ) ==
|
|
|
|
( aAccessiblePositions[nPortionNo+1] -
|
|
|
|
aAccessiblePositions[nPortionNo] ),
|
|
|
|
"accesability portion disagrees with text model" );
|
|
|
|
|
|
|
|
sal_Int32 nWithinPortion = nPos - aAccessiblePositions[nPortionNo];
|
|
|
|
nStartPos += nWithinPortion;
|
|
|
|
}
|
|
|
|
// else: return startPos unmodified
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
DBG_ASSERT( (nStartPos >= 0) && (nStartPos < USHRT_MAX),
|
|
|
|
"Why can the SwTxtNode have so many characters?" );
|
|
|
|
return static_cast<USHORT>(nStartPos);
|
2002-02-19 18:11:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::FillBoundary(
|
|
|
|
Boundary& rBound,
|
|
|
|
const Positions_t& rPositions,
|
2002-02-21 13:55:31 +00:00
|
|
|
size_t nPos )
|
2002-02-19 18:11:48 +00:00
|
|
|
{
|
|
|
|
rBound.startPos = rPositions[nPos];
|
|
|
|
rBound.endPos = rPositions[nPos+1];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
size_t SwAccessiblePortionData::FindBreak(
|
2002-02-19 18:11:48 +00:00
|
|
|
const Positions_t& rPositions,
|
|
|
|
sal_Int32 nValue )
|
|
|
|
{
|
2002-02-21 13:55:31 +00:00
|
|
|
DBG_ASSERT( rPositions.size() >= 2, "need min + max value" );
|
|
|
|
DBG_ASSERT( rPositions[0] <= nValue, "need min value" );
|
2002-02-20 14:22:26 +00:00
|
|
|
DBG_ASSERT( rPositions[rPositions.size()-1] >= nValue,
|
|
|
|
"need first terminator value" );
|
|
|
|
DBG_ASSERT( rPositions[rPositions.size()-2] >= nValue,
|
|
|
|
"need second terminator value" );
|
2002-02-19 18:11:48 +00:00
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
size_t nMin = 0;
|
|
|
|
size_t nMax = rPositions.size()-2;
|
2002-02-19 18:11:48 +00:00
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
// loop until no more than two candidates are left
|
|
|
|
while( nMin+1 < nMax )
|
2002-02-19 18:11:48 +00:00
|
|
|
{
|
2002-02-21 13:55:31 +00:00
|
|
|
// check loop invariants
|
|
|
|
DBG_ASSERT( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) ||
|
|
|
|
( (nMin != 0) && (rPositions[nMin] < nValue) ),
|
|
|
|
"minvalue not minimal" );
|
|
|
|
DBG_ASSERT( nValue <= rPositions[nMax], "max value not maximal" );
|
|
|
|
|
|
|
|
// get middle (and ensure progress)
|
|
|
|
size_t nMiddle = (nMin + nMax)/2;
|
|
|
|
DBG_ASSERT( nMin < nMiddle, "progress?" );
|
|
|
|
DBG_ASSERT( nMiddle < nMax, "progress?" );
|
|
|
|
|
|
|
|
// check array
|
|
|
|
DBG_ASSERT( rPositions[nMin] <= rPositions[nMiddle],
|
|
|
|
"garbled positions array" );
|
|
|
|
DBG_ASSERT( rPositions[nMiddle] <= rPositions[nMax],
|
|
|
|
"garbled positions array" );
|
|
|
|
|
|
|
|
if( nValue > rPositions[nMiddle] )
|
|
|
|
nMin = nMiddle;
|
|
|
|
else
|
|
|
|
nMax = nMiddle;
|
2002-02-19 18:11:48 +00:00
|
|
|
}
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
// only two are left; we only need to check which one is the winner
|
|
|
|
DBG_ASSERT( (nMax == nMin) || (nMax == nMin+1), "only two left" );
|
|
|
|
if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) )
|
|
|
|
nMin = nMin+1;
|
|
|
|
|
|
|
|
// finally, check to see whether the returned value is the 'right' position
|
|
|
|
DBG_ASSERT( rPositions[nMin] <= nValue, "not smaller or equal" );
|
|
|
|
DBG_ASSERT( nValue <= rPositions[nMin+1], "not equal or larger" );
|
|
|
|
DBG_ASSERT( (nMin == 0) || (rPositions[nMin-1] <= nValue),
|
|
|
|
"earlier value should have been returned" );
|
|
|
|
|
2002-02-20 14:22:26 +00:00
|
|
|
DBG_ASSERT( nMin < rPositions.size()-1,
|
|
|
|
"shouldn't return last position (due to termintator values)" );
|
|
|
|
|
2002-02-19 18:11:48 +00:00
|
|
|
return nMin;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::GetWordBoundary(
|
|
|
|
Boundary& rBound,
|
|
|
|
sal_Int32 nPos,
|
|
|
|
const SwTxtNode* pNode )
|
|
|
|
{
|
|
|
|
DBG_ASSERT( nPos >= 0, "illegal position; check before" );
|
|
|
|
DBG_ASSERT( nPos < sAccessibleString.getLength(), "illegal position" );
|
|
|
|
|
|
|
|
if( pWords == NULL )
|
|
|
|
{
|
|
|
|
DBG_ASSERT( pBreakIt != NULL, "We always need a break." );
|
|
|
|
DBG_ASSERT( pBreakIt->xBreak.is(), "No break-iterator." );
|
|
|
|
if( pBreakIt->xBreak.is() )
|
|
|
|
{
|
|
|
|
pWords = new Positions_t();
|
|
|
|
pWords->reserve(100);
|
|
|
|
|
|
|
|
// use xBreak->nextWord to iterate over all words; store
|
|
|
|
// positions in pWords
|
|
|
|
sal_Int32 nCurrent = 0;
|
|
|
|
sal_Int32 nLength = sAccessibleString.getLength();
|
|
|
|
const USHORT nWordType = ANYWORD_IGNOREWHITESPACES;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
pWords->push_back( nCurrent );
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
USHORT nModelPos = GetModelPosition( nCurrent );
|
2002-02-19 18:11:48 +00:00
|
|
|
|
|
|
|
nCurrent = pBreakIt->xBreak->nextWord(
|
|
|
|
sAccessibleString, nCurrent,
|
|
|
|
pBreakIt->GetLocale( pNode->GetLang( nModelPos ) ),
|
|
|
|
nWordType ).startPos;
|
|
|
|
|
|
|
|
if( (nCurrent < 0) && (nCurrent > nLength) )
|
|
|
|
nCurrent = nLength;
|
|
|
|
}
|
|
|
|
while (nCurrent < nLength);
|
2002-02-21 13:55:31 +00:00
|
|
|
|
|
|
|
// finish with two terminators
|
|
|
|
pWords->push_back( nLength );
|
2002-02-19 18:11:48 +00:00
|
|
|
pWords->push_back( nLength );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no break iterator -> empty word
|
|
|
|
rBound.startPos = 0;
|
|
|
|
rBound.endPos = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FillBoundary( rBound, *pWords, FindBreak( *pWords, nPos ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void SwAccessiblePortionData::GetSentenceBoundary(
|
|
|
|
Boundary& rBound,
|
|
|
|
sal_Int32 nPos,
|
|
|
|
const SwTxtNode* pNode )
|
|
|
|
{
|
|
|
|
DBG_ASSERT( nPos >= 0, "illegal position; check before" );
|
|
|
|
DBG_ASSERT( nPos < sAccessibleString.getLength(), "illegal position" );
|
|
|
|
|
|
|
|
if( pSentences == NULL )
|
|
|
|
{
|
|
|
|
DBG_ASSERT( pBreakIt != NULL, "We always need a break." );
|
|
|
|
DBG_ASSERT( pBreakIt->xBreak.is(), "No break-iterator." );
|
|
|
|
if( pBreakIt->xBreak.is() )
|
|
|
|
{
|
|
|
|
pSentences = new Positions_t();
|
|
|
|
pSentences->reserve(10);
|
|
|
|
|
|
|
|
// use xBreak->endOfSentence to iterate over all words; store
|
|
|
|
// positions in pSentences
|
|
|
|
sal_Int32 nCurrent = 0;
|
|
|
|
sal_Int32 nLength = sAccessibleString.getLength();
|
|
|
|
do
|
|
|
|
{
|
|
|
|
pSentences->push_back( nCurrent );
|
|
|
|
|
2002-02-21 13:55:31 +00:00
|
|
|
USHORT nModelPos = GetModelPosition( nCurrent );
|
2002-02-19 18:11:48 +00:00
|
|
|
|
|
|
|
nCurrent = pBreakIt->xBreak->endOfSentence(
|
|
|
|
sAccessibleString, nCurrent,
|
|
|
|
pBreakIt->GetLocale( pNode->GetLang( nModelPos ) ) ) + 1;
|
|
|
|
|
|
|
|
if( (nCurrent < 0) && (nCurrent > nLength) )
|
|
|
|
nCurrent = nLength;
|
|
|
|
}
|
|
|
|
while (nCurrent < nLength);
|
2002-02-21 13:55:31 +00:00
|
|
|
|
|
|
|
// finish with two terminators
|
|
|
|
pSentences->push_back( nLength );
|
2002-02-19 18:11:48 +00:00
|
|
|
pSentences->push_back( nLength );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no break iterator -> empty word
|
|
|
|
rBound.startPos = 0;
|
|
|
|
rBound.endPos = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FillBoundary( rBound, *pSentences, FindBreak( *pSentences, nPos ) );
|
|
|
|
}
|