/* This file is part of the KDE project
Copyright ( C ) 2001 - 2006 David Faure < faure @ kde . org >
Copyright ( C ) 2005 - 2006 Martin Ellis < martin . ellis @ kdemail . net >
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation ; either
version 2 of the License , or ( at your option ) any later version .
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
Library General Public License for more details .
You should have received a copy of the GNU Library General Public License
along with this library ; see the file COPYING . LIB . If not , write to
the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# include "KoTextParag.h"
# include "KoTextDocument.h"
# include "KoParagCounter.h"
# include "KoTextZoomHandler.h"
# include "KoStyleCollection.h"
# include "KoVariable.h"
# include <KoOasisContext.h>
# include <KoXmlWriter.h>
# include <KoGenStyles.h>
# include <KoDom.h>
# include <KoXmlNS.h>
# include <tdeglobal.h>
# include <tdelocale.h>
# include <kdebug.h>
# include <tdeglobalsettings.h>
# include <assert.h>
//#define DEBUG_PAINT
KoTextParag : : KoTextParag ( KoTextDocument * d , KoTextParag * pr , KoTextParag * nx , bool updateIds )
: p ( pr ) , n ( nx ) , doc ( d ) ,
m_invalid ( true ) ,
changed ( FALSE ) ,
fullWidth ( TRUE ) ,
newLinesAllowed ( TRUE ) , // default in kotext
visible ( TRUE ) , //breakable( TRUE ),
movedDown ( FALSE ) ,
m_toc ( false ) ,
align ( 0 ) ,
m_lineChanged ( - 1 ) ,
m_wused ( 0 ) ,
mSelections ( 0 ) ,
mFloatingItems ( 0 ) ,
tArray ( 0 )
{
defFormat = formatCollection ( ) - > defaultFormat ( ) ;
/*if ( !doc ) {
tabStopWidth = defFormat - > width ( ' x ' ) * 8 ;
commandHistory = new KoTextDocCommandHistory ( 100 ) ;
} */
if ( p ) {
p - > n = this ;
}
if ( n ) {
n - > p = this ;
}
if ( ! p & & doc )
doc - > setFirstParag ( this ) ;
if ( ! n & & doc )
doc - > setLastParag ( this ) ;
//firstFormat = TRUE; //// unused
//firstPProcess = TRUE;
//state = -1;
//needPreProcess = FALSE;
if ( p )
id = p - > id + 1 ;
else
id = 0 ;
if ( n & & updateIds ) {
KoTextParag * s = n ;
while ( s ) {
s - > id = s - > p - > id + 1 ;
//s->lm = s->rm = s->tm = s->bm = -1, s->flm = -1;
s = s - > n ;
}
}
str = new KoTextString ( ) ;
str - > insert ( 0 , " " , formatCollection ( ) - > defaultFormat ( ) ) ;
setJoinBorder ( true ) ;
}
KoTextParag : : ~ KoTextParag ( )
{
//kdDebug(32500) << "KoTextParag::~KoTextParag " << this << " id=" << paragId() << endl;
// #107961: unregister custom items; KoTextString::clear() will delete them
const int len = str - > length ( ) ;
for ( int i = 0 ; i < len ; + + i ) {
KoTextStringChar * c = at ( i ) ;
if ( doc & & c - > isCustom ( ) ) {
doc - > unregisterCustomItem ( c - > customItem ( ) , this ) ;
//removeCustomItem();
}
}
delete str ;
str = 0 ;
// if ( doc && p == doc->minwParag ) {
// doc->minwParag = 0;
// doc->minw = 0;
// }
if ( ! doc ) {
//delete pFormatter;
//delete commandHistory;
}
delete [ ] tArray ;
//delete eData;
TQMap < int , KoTextParagLineStart * > : : Iterator it = lineStarts . begin ( ) ;
for ( ; it ! = lineStarts . end ( ) ; + + it )
delete * it ;
if ( mSelections ) delete mSelections ;
if ( mFloatingItems ) delete mFloatingItems ;
if ( p )
p - > setNext ( n ) ;
if ( n )
n - > setPrev ( p ) ;
//// kotext
if ( doc & & ! doc - > isDestroying ( ) )
{
doc - > informParagraphDeleted ( this ) ;
}
//kdDebug(32500) << "KoTextParag::~KoTextParag " << this << " done" << endl;
////
}
void KoTextParag : : setNext ( KoTextParag * s )
{
n = s ;
if ( ! n & & doc )
doc - > setLastParag ( this ) ;
}
void KoTextParag : : setPrev ( KoTextParag * s )
{
p = s ;
if ( ! p & & doc )
doc - > setFirstParag ( this ) ;
}
void KoTextParag : : invalidate ( int /*chr, ignored*/ )
{
m_invalid = true ;
#if 0
if ( invalid < 0 )
invalid = chr ;
else
invalid = TQMIN ( invalid , chr ) ;
# endif
}
void KoTextParag : : setChanged ( bool b , bool /*recursive*/ )
{
changed = b ;
m_lineChanged = - 1 ; // all
}
void KoTextParag : : setLineChanged ( short int line )
{
if ( m_lineChanged = = - 1 ) {
if ( ! changed ) // only if the whole parag wasn't "changed" already
m_lineChanged = line ;
}
else
m_lineChanged = TQMIN ( m_lineChanged , line ) ; // also works if line=-1
changed = true ;
//kdDebug(32500) << "KoTextParag::setLineChanged line=" << line << " -> m_lineChanged=" << m_lineChanged << endl;
}
void KoTextParag : : insert ( int index , const TQString & s )
{
str - > insert ( index , s , formatCollection ( ) - > defaultFormat ( ) ) ;
invalidate ( index ) ;
//needPreProcess = TRUE;
}
void KoTextParag : : truncate ( int index )
{
str - > truncate ( index ) ;
insert ( length ( ) , " " ) ;
//needPreProcess = TRUE;
}
void KoTextParag : : remove ( int index , int len )
{
if ( index + len - str - > length ( ) > 0 )
return ;
for ( int i = index ; i < index + len ; + + i ) {
KoTextStringChar * c = at ( i ) ;
if ( doc & & c - > isCustom ( ) ) {
doc - > unregisterCustomItem ( c - > customItem ( ) , this ) ;
//removeCustomItem();
}
}
str - > remove ( index , len ) ;
invalidate ( 0 ) ;
//needPreProcess = TRUE;
}
void KoTextParag : : join ( KoTextParag * s )
{
//kdDebug(32500) << "KoTextParag::join this=" << paragId() << " (length " << length() << ") with " << s->paragId() << " (length " << s->length() << ")" << endl;
int oh = r . height ( ) + s - > r . height ( ) ;
n = s - > n ;
if ( n )
n - > p = this ;
else if ( doc )
doc - > setLastParag ( this ) ;
int start = str - > length ( ) ;
if ( length ( ) > 0 & & at ( length ( ) - 1 ) - > c = = ' ' ) {
remove ( length ( ) - 1 , 1 ) ;
- - start ;
}
append ( s - > str - > toString ( ) , TRUE ) ;
for ( int i = 0 ; i < s - > length ( ) ; + + i ) {
if ( doc - > useFormatCollection ( ) ) {
s - > str - > at ( i ) . format ( ) - > addRef ( ) ;
str - > setFormat ( i + start , s - > str - > at ( i ) . format ( ) , TRUE ) ;
}
if ( s - > str - > at ( i ) . isCustom ( ) ) {
KoTextCustomItem * item = s - > str - > at ( i ) . customItem ( ) ;
str - > at ( i + start ) . setCustomItem ( item ) ;
s - > str - > at ( i ) . loseCustomItem ( ) ;
doc - > unregisterCustomItem ( item , s ) ; // ### missing in TQRT
doc - > registerCustomItem ( item , this ) ;
}
}
Q_ASSERT ( str - > at ( str - > length ( ) - 1 ) . c = = ' ' ) ;
/*if ( !extraData() && s->extraData() ) {
setExtraData ( s - > extraData ( ) ) ;
s - > setExtraData ( 0 ) ;
} else if ( extraData ( ) & & s - > extraData ( ) ) {
extraData ( ) - > join ( s - > extraData ( ) ) ;
} */
delete s ;
invalidate ( 0 ) ;
//// kotext
invalidateCounters ( ) ;
////
r . setHeight ( oh ) ;
//needPreProcess = TRUE;
if ( n ) {
KoTextParag * s = n ;
while ( s ) {
s - > id = s - > p - > id + 1 ;
//s->state = -1;
//s->needPreProcess = TRUE;
s - > changed = TRUE ;
s = s - > n ;
}
}
format ( ) ;
//state = -1;
}
void KoTextParag : : move ( int & dy )
{
//kdDebug(32500) << "KoTextParag::move paragId=" << paragId() << " dy=" << dy << endl;
if ( dy = = 0 )
return ;
changed = TRUE ;
r . moveBy ( 0 , dy ) ;
if ( mFloatingItems ) {
for ( KoTextCustomItem * i = mFloatingItems - > first ( ) ; i ; i = mFloatingItems - > next ( ) ) {
i - > finalize ( ) ;
}
}
//if ( p )
// p->lastInFrame = TRUE; // TQt does this, but the loop at the end of format() calls move a lot!
movedDown = FALSE ;
// do page breaks if required
if ( doc & & doc - > isPageBreakEnabled ( ) ) {
int shift ;
if ( ( shift = doc - > formatter ( ) - > formatVertically ( doc , this ) ) ) {
if ( p )
p - > setChanged ( TRUE ) ;
dy + = shift ;
}
}
}
void KoTextParag : : format ( int start , bool doMove )
{
if ( ! str | | str - > length ( ) = = 0 | | ! formatter ( ) )
return ;
if ( isValid ( ) )
return ;
//kdDebug(32500) << "KoTextParag::format " << this << " id:" << paragId() << endl;
r . moveTopLeft ( TQPoint ( documentX ( ) , p ? p - > r . y ( ) + p - > r . height ( ) : documentY ( ) ) ) ;
//if ( p )
// p->lastInFrame = FALSE;
movedDown = FALSE ;
bool formattedAgain = FALSE ;
formatAgain :
r . setWidth ( documentWidth ( ) ) ;
// Not really useful....
if ( doc & & mFloatingItems ) {
for ( KoTextCustomItem * i = mFloatingItems - > first ( ) ; i ; i = mFloatingItems - > next ( ) ) {
if ( i - > placement ( ) = = KoTextCustomItem : : PlaceRight )
i - > move ( r . x ( ) + r . width ( ) - i - > width , r . y ( ) ) ;
else
i - > move ( i - > x ( ) , r . y ( ) ) ;
}
}
TQMap < int , KoTextParagLineStart * > oldLineStarts = lineStarts ;
lineStarts . clear ( ) ;
int y ;
bool formatterWorked = formatter ( ) - > format ( doc , this , start , oldLineStarts , y , m_wused ) ;
// It can't happen that width < minimumWidth -- hopefully.
//r.setWidth( TQMAX( r.width(), formatter()->minimumWidth() ) );
//m_minw = formatter()->minimumWidth();
TQMap < int , KoTextParagLineStart * > : : Iterator it = oldLineStarts . begin ( ) ;
for ( ; it ! = oldLineStarts . end ( ) ; + + it )
delete * it ;
/* if ( hasBorder() || str->isRightToLeft() )
////kotext: border extends to doc width
//// and, bidi parags might have a counter, which will be right-aligned...
{
setWidth ( textDocument ( ) - > width ( ) - 1 ) ;
}
else */
{
if ( lineStarts . count ( ) = = 1 ) { //&& ( !doc || doc->flow()->isEmpty() ) ) {
// kotext: for proper parag borders, we want all parags to be as wide as linestart->w
/* if ( !str->isBidi() ) {
KoTextStringChar * c = & str - > at ( str - > length ( ) - 1 ) ;
r . setWidth ( c - > x + c - > width ) ;
} else */ {
r . setWidth ( lineStarts [ 0 ] - > w ) ;
}
}
if ( newLinesAllowed ) {
it = lineStarts . begin ( ) ;
int usedw = 0 ; int lineid = 0 ;
for ( ; it ! = lineStarts . end ( ) ; + + it , + + lineid ) {
usedw = TQMAX ( usedw , ( * it ) - > w ) ;
}
if ( r . width ( ) < = 0 ) {
// if the user specifies an invalid rect, this means that the
// bounding box should grow to the width that the text actually
// needs
r . setWidth ( usedw ) ;
} else {
r . setWidth ( TQMIN ( usedw , r . width ( ) ) ) ;
}
}
}
if ( y ! = r . height ( ) )
r . setHeight ( y ) ;
if ( ! visible )
r . setHeight ( 0 ) ;
// do page breaks if required
if ( doc & & doc - > isPageBreakEnabled ( ) ) {
int shift = doc - > formatter ( ) - > formatVertically ( doc , this ) ;
//kdDebug(32500) << "formatVertically returned shift=" << shift << endl;
if ( shift & & ! formattedAgain ) {
formattedAgain = TRUE ;
goto formatAgain ;
}
}
if ( doc )
doc - > formatter ( ) - > postFormat ( this ) ;
if ( n & & doMove & & n - > isValid ( ) & & r . y ( ) + r . height ( ) ! = n - > r . y ( ) ) {
//kdDebug(32500) << "r=" << r << " n->r=" << n->r << endl;
int dy = ( r . y ( ) + r . height ( ) ) - n - > r . y ( ) ;
KoTextParag * s = n ;
bool makeInvalid = false ; //p && p->lastInFrame;
//kdDebug(32500) << "might move of dy=" << dy << ". previous's lastInFrame (=makeInvalid): " << makeInvalid << endl;
while ( s & & dy ) {
if ( s - > movedDown ) { // (not in TQRT) : moved down -> invalidate and stop moving down
s - > invalidate ( 0 ) ; // (there is no point in moving down a parag that has a frame break...)
break ;
}
if ( ! s - > isFullWidth ( ) )
makeInvalid = TRUE ;
if ( makeInvalid )
s - > invalidate ( 0 ) ;
s - > move ( dy ) ;
//if ( s->lastInFrame )
// makeInvalid = TRUE;
s = s - > n ;
}
}
//#define DEBUG_CI_PLACEMENT
if ( mFloatingItems ) {
# ifdef DEBUG_CI_PLACEMENT
kdDebug ( 32500 ) < < lineStarts . count ( ) < < " lines " < < endl ;
# endif
// Place custom items - after the formatting is finished
int len = length ( ) ;
int line = - 1 ;
int lineY = 0 ; // the one called "cy" in other algos
int baseLine = 0 ;
TQMap < int , KoTextParagLineStart * > : : Iterator it = lineStarts . begin ( ) ;
for ( int i = 0 ; i < len ; + + i ) {
KoTextStringChar * chr = & str - > at ( i ) ;
if ( chr - > lineStart ) {
+ + line ;
if ( line > 0 )
+ + it ;
lineY = ( * it ) - > y ;
baseLine = ( * it ) - > baseLine ;
# ifdef DEBUG_CI_PLACEMENT
kdDebug ( 32500 ) < < " New line ( " < < line < < " ): lineStart= " < < ( * it ) < < " lineY= " < < lineY < < " baseLine= " < < baseLine < < " height= " < < ( * it ) - > h < < endl ;
# endif
}
if ( chr - > isCustom ( ) ) {
int x = chr - > x ;
KoTextCustomItem * item = chr - > customItem ( ) ;
Q_ASSERT ( baseLine > = item - > ascent ( ) ) ; // something went wrong in KoTextFormatter if this isn't the case
int y = lineY + baseLine - item - > ascent ( ) ;
# ifdef DEBUG_CI_PLACEMENT
kdDebug ( 32500 ) < < " Custom item: i= " < < i < < " x= " < < x < < " lineY= " < < lineY < < " baseLine= " < < baseLine < < " ascent= " < < item - > ascent ( ) < < " -> y= " < < y < < endl ;
# endif
item - > move ( x , y ) ;
item - > finalize ( ) ;
}
}
}
//firstFormat = FALSE; //// unused
if ( formatterWorked ) // only if it worked, i.e. we had some width to format it
{
m_invalid = false ;
}
changed = TRUE ;
//#### str->setTextChanged( FALSE );
}
int KoTextParag : : lineHeightOfChar ( int i , int * bl , int * y ) const
{
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . end ( ) ;
- - it ;
for ( ; ; ) {
if ( i > = it . key ( ) ) {
if ( bl )
* bl = ( * it ) - > baseLine ;
if ( y )
* y = ( * it ) - > y ;
return ( * it ) - > h ;
}
if ( it = = lineStarts . begin ( ) )
break ;
- - it ;
}
kdWarning ( 32500 ) < < " KoTextParag::lineHeightOfChar: couldn't find lh for " < < i < < endl ;
return 15 ;
}
KoTextStringChar * KoTextParag : : lineStartOfChar ( int i , int * index , int * line ) const
{
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
int l = ( int ) lineStarts . count ( ) - 1 ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . end ( ) ;
- - it ;
for ( ; ; ) {
if ( i > = it . key ( ) ) {
if ( index )
* index = it . key ( ) ;
if ( line )
* line = l ;
return & str - > at ( it . key ( ) ) ;
}
if ( it = = lineStarts . begin ( ) )
break ;
- - it ;
- - l ;
}
kdWarning ( 32500 ) < < " KoTextParag::lineStartOfChar: couldn't find " < < i < < endl ;
return 0 ;
}
int KoTextParag : : lines ( ) const
{
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
return ( int ) lineStarts . count ( ) ;
}
KoTextStringChar * KoTextParag : : lineStartOfLine ( int line , int * index ) const
{
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
if ( line > = 0 & & line < ( int ) lineStarts . count ( ) ) {
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( line - - > 0 )
+ + it ;
int i = it . key ( ) ;
if ( index )
* index = i ;
return & str - > at ( i ) ;
}
kdWarning ( 32500 ) < < " KoTextParag::lineStartOfLine: couldn't find " < < line < < endl ;
return 0 ;
}
int KoTextParag : : leftGap ( ) const
{
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
int line = 0 ;
int x = str - > at ( 0 ) . x ; /* set x to x of first char */
if ( str - > isBidi ( ) ) {
for ( int i = 1 ; i < str - > length ( ) ; + + i )
x = TQMIN ( x , str - > at ( i ) . x ) ;
return x ;
}
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( line < ( int ) lineStarts . count ( ) ) {
int i = it . key ( ) ; /* char index */
x = TQMIN ( x , str - > at ( i ) . x ) ;
+ + it ;
+ + line ;
}
return x ;
}
void KoTextParag : : setFormat ( int index , int len , const KoTextFormat * _f , bool useCollection , int flags )
{
Q_ASSERT ( useCollection ) ; // just for info
if ( index < 0 )
index = 0 ;
if ( index > str - > length ( ) - 1 )
index = str - > length ( ) - 1 ;
if ( index + len > = str - > length ( ) )
len = str - > length ( ) - index ;
KoTextFormatCollection * fc = 0 ;
if ( useCollection )
fc = formatCollection ( ) ;
KoTextFormat * of ;
for ( int i = 0 ; i < len ; + + i ) {
of = str - > at ( i + index ) . format ( ) ;
if ( ! changed & & _f - > key ( ) ! = of - > key ( ) )
changed = TRUE ;
// Check things that need the textformatter to run
// (e.g. not color changes)
// ######## Is this test exhaustive?
if ( m_invalid = = false & &
( _f - > font ( ) . family ( ) ! = of - > font ( ) . family ( ) | |
_f - > pointSize ( ) ! = of - > pointSize ( ) | |
_f - > font ( ) . weight ( ) ! = of - > font ( ) . weight ( ) | |
_f - > font ( ) . italic ( ) ! = of - > font ( ) . italic ( ) | |
_f - > vAlign ( ) ! = of - > vAlign ( ) | |
_f - > relativeTextSize ( ) ! = of - > relativeTextSize ( ) | |
_f - > offsetFromBaseLine ( ) ! = of - > offsetFromBaseLine ( ) | |
_f - > wordByWord ( ) ! = of - > wordByWord ( ) | |
_f - > attributeFont ( ) ! = of - > attributeFont ( ) | |
_f - > language ( ) ! = of - > language ( ) | |
_f - > hyphenation ( ) ! = of - > hyphenation ( ) | |
_f - > shadowDistanceX ( ) ! = of - > shadowDistanceX ( ) | |
_f - > shadowDistanceY ( ) ! = of - > shadowDistanceY ( )
) ) {
invalidate ( 0 ) ;
}
if ( flags = = - 1 | | flags = = KoTextFormat : : Format | | ! fc ) {
# ifdef DEBUG_COLLECTION
kdDebug ( 32500 ) < < " KoTextParag::setFormat, will use format(f) " < < f < < " " < < _f - > key ( ) < < endl ;
# endif
KoTextFormat * f = fc ? fc - > format ( _f ) : const_cast < KoTextFormat * > ( _f ) ;
str - > setFormat ( i + index , f , useCollection , true ) ;
} else {
# ifdef DEBUG_COLLECTION
kdDebug ( 32500 ) < < " KoTextParag::setFormat, will use format(of,f,flags) of= " < < of < < " " < < of - > key ( ) < < " , f= " < < _f < < " " < < _f - > key ( ) < < endl ;
# endif
KoTextFormat * fm = fc - > format ( of , _f , flags ) ;
# ifdef DEBUG_COLLECTION
kdDebug ( 32500 ) < < " KoTextParag::setFormat, format(of,f,flags) returned " < < fm < < " " < < fm - > key ( ) < < " " < < endl ;
# endif
str - > setFormat ( i + index , fm , useCollection ) ;
}
}
}
void KoTextParag : : drawCursorDefault ( TQPainter & painter , KoTextCursor * cursor , int curx , int cury , int curh , const TQColorGroup & cg )
{
painter . fillRect ( TQRect ( curx , cury , 1 , curh ) , cg . color ( TQColorGroup : : Text ) ) ;
painter . save ( ) ;
if ( str - > isBidi ( ) ) {
const int d = 4 ;
if ( at ( cursor - > index ( ) ) - > rightToLeft ) {
painter . setPen ( TQt : : black ) ;
painter . drawLine ( curx , cury , curx - d / 2 , cury + d / 2 ) ;
painter . drawLine ( curx , cury + d , curx - d / 2 , cury + d / 2 ) ;
} else {
painter . setPen ( TQt : : black ) ;
painter . drawLine ( curx , cury , curx + d / 2 , cury + d / 2 ) ;
painter . drawLine ( curx , cury + d , curx + d / 2 , cury + d / 2 ) ;
}
}
painter . restore ( ) ;
}
int * KoTextParag : : tabArray ( ) const
{
int * ta = tArray ;
if ( ! ta & & doc )
ta = doc - > tabArray ( ) ;
return ta ;
}
int KoTextParag : : nextTabDefault ( int , int x )
{
int * ta = tArray ;
//if ( doc ) {
if ( ! ta )
ta = doc - > tabArray ( ) ;
int tabStopWidth = doc - > tabStopWidth ( ) ;
//}
if ( tabStopWidth ! = 0 )
return tabStopWidth * ( x / tabStopWidth + 1 ) ;
else
return x ;
}
KoTextFormatCollection * KoTextParag : : formatCollection ( ) const
{
if ( doc )
return doc - > formatCollection ( ) ;
//if ( !qFormatCollection )
// qFormatCollection = new KoTextFormatCollection;
//return qFormatCollection;
return 0L ;
}
void KoTextParag : : show ( )
{
if ( visible | | ! doc )
return ;
visible = TRUE ;
}
void KoTextParag : : hide ( )
{
if ( ! visible | | ! doc )
return ;
visible = FALSE ;
}
void KoTextParag : : setDirection ( TQChar : : Direction d )
{
if ( str & & str - > direction ( ) ! = d ) {
str - > setDirection ( d ) ;
invalidate ( 0 ) ;
//// kotext
m_layout . direction = d ;
invalidateCounters ( ) ; // #47178
////
}
}
TQChar : : Direction KoTextParag : : direction ( ) const
{
return ( str ? str - > direction ( ) : TQChar : : DirON ) ;
}
void KoTextParag : : setSelection ( int id , int start , int end )
{
TQMap < int , KoTextParagSelection > : : ConstIterator it = selections ( ) . find ( id ) ;
if ( it ! = mSelections - > end ( ) ) {
if ( start = = ( * it ) . start & & end = = ( * it ) . end )
return ;
}
KoTextParagSelection sel ;
sel . start = start ;
sel . end = end ;
( * mSelections ) [ id ] = sel ;
setChanged ( TRUE , TRUE ) ;
}
void KoTextParag : : removeSelection ( int id )
{
if ( ! hasSelection ( id ) )
return ;
if ( mSelections )
mSelections - > remove ( id ) ;
setChanged ( TRUE , TRUE ) ;
}
int KoTextParag : : selectionStart ( int id ) const
{
if ( ! mSelections )
return - 1 ;
TQMap < int , KoTextParagSelection > : : ConstIterator it = mSelections - > find ( id ) ;
if ( it = = mSelections - > end ( ) )
return - 1 ;
return ( * it ) . start ;
}
int KoTextParag : : selectionEnd ( int id ) const
{
if ( ! mSelections )
return - 1 ;
TQMap < int , KoTextParagSelection > : : ConstIterator it = mSelections - > find ( id ) ;
if ( it = = mSelections - > end ( ) )
return - 1 ;
return ( * it ) . end ;
}
bool KoTextParag : : hasSelection ( int id ) const
{
if ( ! mSelections )
return FALSE ;
TQMap < int , KoTextParagSelection > : : ConstIterator it = mSelections - > find ( id ) ;
if ( it = = mSelections - > end ( ) )
return FALSE ;
return ( * it ) . start ! = ( * it ) . end | | length ( ) = = 1 ;
}
bool KoTextParag : : fullSelected ( int id ) const
{
if ( ! mSelections )
return FALSE ;
TQMap < int , KoTextParagSelection > : : ConstIterator it = mSelections - > find ( id ) ;
if ( it = = mSelections - > end ( ) )
return FALSE ;
return ( * it ) . start = = 0 & & ( * it ) . end = = str - > length ( ) - 1 ;
}
int KoTextParag : : lineY ( int l ) const
{
if ( l > ( int ) lineStarts . count ( ) - 1 ) {
kdWarning ( 32500 ) < < " KoTextParag::lineY: line " < < l < < " out of range! " < < endl ;
return 0 ;
}
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( l - - > 0 )
+ + it ;
return ( * it ) - > y ;
}
int KoTextParag : : lineBaseLine ( int l ) const
{
if ( l > ( int ) lineStarts . count ( ) - 1 ) {
kdWarning ( 32500 ) < < " KoTextParag::lineBaseLine: line " < < l < < " out of range! " < < endl ;
return 10 ;
}
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( l - - > 0 )
+ + it ;
return ( * it ) - > baseLine ;
}
int KoTextParag : : lineHeight ( int l ) const
{
if ( l > ( int ) lineStarts . count ( ) - 1 ) {
kdWarning ( 32500 ) < < " KoTextParag::lineHeight: line " < < l < < " out of range! " < < endl ;
return 15 ;
}
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( l - - > 0 )
+ + it ;
return ( * it ) - > h ;
}
void KoTextParag : : lineInfo ( int l , int & y , int & h , int & bl ) const
{
if ( l > ( int ) lineStarts . count ( ) - 1 ) {
kdWarning ( 32500 ) < < " KoTextParag::lineInfo: line " < < l < < " out of range! " < < endl ;
kdDebug ( 32500 ) < < ( int ) lineStarts . count ( ) - 1 < < " " < < l < < endl ;
y = 0 ;
h = 15 ;
bl = 10 ;
return ;
}
if ( ! isValid ( ) )
( ( KoTextParag * ) this ) - > format ( ) ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( l - - > 0 )
+ + it ;
y = ( * it ) - > y ;
h = ( * it ) - > h ;
bl = ( * it ) - > baseLine ;
}
uint KoTextParag : : alignment ( ) const
{
return align ;
}
void KoTextParag : : setFormat ( KoTextFormat * fm )
{
#if 0
bool doUpdate = FALSE ;
if ( defFormat & & ( defFormat ! = formatCollection ( ) - > defaultFormat ( ) ) )
doUpdate = TRUE ;
# endif
defFormat = formatCollection ( ) - > format ( fm ) ;
#if 0
if ( ! doUpdate )
return ;
for ( int i = 0 ; i < length ( ) ; + + i ) {
if ( at ( i ) - > format ( ) - > styleName ( ) = = defFormat - > styleName ( ) )
at ( i ) - > format ( ) - > updateStyle ( ) ;
}
# endif
}
KoTextFormatterBase * KoTextParag : : formatter ( ) const
{
if ( doc )
return doc - > formatter ( ) ;
return 0 ;
}
/*int KoTextParag::minimumWidth() const
{
//return doc ? doc->minimumWidth() : 0;
return m_minw ;
} */
int KoTextParag : : widthUsed ( ) const
{
return m_wused ;
}
void KoTextParag : : setTabArray ( int * a )
{
delete [ ] tArray ;
tArray = a ;
}
void KoTextParag : : setTabStops ( int tw )
{
if ( doc )
doc - > setTabStops ( tw ) ;
//else
// tabStopWidth = tw;
}
TQMap < int , KoTextParagSelection > & KoTextParag : : selections ( ) const
{
if ( ! mSelections )
( ( KoTextParag * ) this ) - > mSelections = new TQMap < int , KoTextParagSelection > ;
return * mSelections ;
}
TQPtrList < KoTextCustomItem > & KoTextParag : : floatingItems ( ) const
{
if ( ! mFloatingItems )
( ( KoTextParag * ) this ) - > mFloatingItems = new TQPtrList < KoTextCustomItem > ;
return * mFloatingItems ;
}
void KoTextCursor : : setIndex ( int i , bool /*restore*/ )
{
// Note: TQRT doesn't allow to position the cursor at string->length
// However we need it, when applying a style to a paragraph, so that
// the trailing space gets the style change applied as well.
// Obviously "right of the trailing space" isn't a good place for a real
// cursor, but this needs to be checked somewhere else.
if ( i < 0 | | i > string - > length ( ) ) {
# if defined(TQT_CHECK_RANGE)
kdWarning ( 32500 ) < < " KoTextCursor::setIndex: " < < i < < " out of range " < < endl ;
//abort();
# endif
i = i < 0 ? 0 : string - > length ( ) - 1 ;
}
tmpIndex = - 1 ;
idx = i ;
}
///////////////////////////////////// kotext extensions ///////////////////////
// Return the counter associated with this paragraph.
KoParagCounter * KoTextParag : : counter ( )
{
if ( ! m_layout . counter )
return 0L ;
// Garbage collect un-needed counters.
if ( m_layout . counter - > numbering ( ) = = KoParagCounter : : NUM_NONE
// [keep it for unnumbered outlines (the depth is useful)]
& & ( ! m_layout . style | | ! m_layout . style - > isOutline ( ) ) )
setNoCounter ( ) ;
return m_layout . counter ;
}
void KoTextParag : : setMargin ( TQStyleSheetItem : : Margin m , double _i )
{
//kdDebug(32500) << "KoTextParag::setMargin " << m << " margin " << _i << endl;
m_layout . margins [ m ] = _i ;
if ( m = = TQStyleSheetItem : : MarginTop & & prev ( ) )
prev ( ) - > invalidate ( 0 ) ; // for top margin (post-1.1: remove this, not necessary anymore)
invalidate ( 0 ) ;
}
void KoTextParag : : setMargins ( const double * margins )
{
for ( int i = 0 ; i < 5 ; + + i )
m_layout . margins [ i ] = margins [ i ] ;
invalidate ( 0 ) ;
}
void KoTextParag : : setAlign ( int align )
{
Q_ASSERT ( align < = TQt : : AlignJustify ) ;
align & = TQt : : AlignHorizontal_Mask ;
setAlignment ( align ) ;
m_layout . alignment = align ;
}
int KoTextParag : : resolveAlignment ( ) const
{
if ( ( int ) m_layout . alignment = = TQt : : AlignAuto )
return str - > isRightToLeft ( ) ? TQt : : AlignRight : TQt : : AlignLeft ;
return m_layout . alignment ;
}
void KoTextParag : : setLineSpacing ( double _i )
{
m_layout . setLineSpacingValue ( _i ) ;
invalidate ( 0 ) ;
}
void KoTextParag : : setLineSpacingType ( KoParagLayout : : SpacingType _type )
{
m_layout . lineSpacingType = _type ;
invalidate ( 0 ) ;
}
void KoTextParag : : setTopBorder ( const KoBorder & _brd )
{
m_layout . topBorder = _brd ;
invalidate ( 0 ) ;
}
void KoTextParag : : setBottomBorder ( const KoBorder & _brd )
{
m_layout . bottomBorder = _brd ;
invalidate ( 0 ) ;
}
void KoTextParag : : setJoinBorder ( bool join )
{
m_layout . joinBorder = join ;
invalidate ( 0 ) ;
}
void KoTextParag : : setBackgroundColor ( const TQColor & color )
{
m_layout . backgroundColor = color ;
invalidate ( 0 ) ;
}
void KoTextParag : : setNoCounter ( )
{
delete m_layout . counter ;
m_layout . counter = 0L ;
invalidateCounters ( ) ;
}
void KoTextParag : : setCounter ( const KoParagCounter * pCounter )
{
// Preserve footnote numbering when applying a style
const bool isFootNote = m_layout . counter & &
m_layout . counter - > numbering ( ) = = KoParagCounter : : NUM_FOOTNOTE ;
if ( isFootNote ) {
const TQString footNotePrefix = m_layout . counter - > prefix ( ) ; // this is where the number is
delete m_layout . counter ;
m_layout . counter = pCounter ? new KoParagCounter ( * pCounter ) : new KoParagCounter ( ) ;
m_layout . counter - > setNumbering ( KoParagCounter : : NUM_FOOTNOTE ) ;
m_layout . counter - > setStyle ( KoParagCounter : : STYLE_NONE ) ; // no number after the 'prefix'
m_layout . counter - > setPrefix ( footNotePrefix ) ;
m_layout . counter - > setSuffix ( TQString ( ) ) ;
invalidateCounters ( ) ;
} else {
if ( pCounter )
setCounter ( * pCounter ) ;
else
setNoCounter ( ) ;
}
}
void KoTextParag : : setCounter ( const KoParagCounter & counter )
{
// Garbage collect unnneeded counters.
if ( counter . numbering ( ) = = KoParagCounter : : NUM_NONE
// [keep it for unnumbered outlines (the depth is useful)]
& & ( ! m_layout . style | | ! m_layout . style - > isOutline ( ) ) )
{
setNoCounter ( ) ;
}
else
{
delete m_layout . counter ;
m_layout . counter = new KoParagCounter ( counter ) ;
// Invalidate the counters
invalidateCounters ( ) ;
}
}
void KoTextParag : : invalidateCounters ( )
{
// Invalidate this paragraph and all the following ones
// (Numbering may have changed)
invalidate ( 0 ) ;
if ( m_layout . counter )
m_layout . counter - > invalidate ( ) ;
KoTextParag * s = next ( ) ;
// #### Possible optimization: since any invalidation propagates down,
// it's enough to stop at the first paragraph with an already-invalidated counter, isn't it?
// This is only true if nobody else calls counter->invalidate...
while ( s ) {
if ( s - > m_layout . counter )
s - > m_layout . counter - > invalidate ( ) ;
s - > invalidate ( 0 ) ;
s = s - > next ( ) ;
}
}
int KoTextParag : : counterWidth ( ) const
{
if ( ! m_layout . counter )
return 0 ;
return m_layout . counter - > width ( this ) ;
}
// Draw the complete label (i.e. heading/list numbers/bullets) for this paragraph.
// This is called by KoTextParag::paint.
void KoTextParag : : drawLabel ( TQPainter * p , int xLU , int yLU , int /*wLU*/ , int /*hLU*/ , int baseLU , const TQColorGroup & /*cg*/ )
{
if ( ! m_layout . counter ) // shouldn't happen
return ;
if ( m_layout . counter - > numbering ( ) = = KoParagCounter : : NUM_NONE )
return ;
int counterWidthLU = m_layout . counter - > width ( this ) ;
// We use the formatting of the first char as the formatting of the counter
KoTextFormat counterFormat ( * KoParagCounter : : counterFormat ( this ) ) ;
if ( ! m_layout . style | | ! m_layout . style - > isOutline ( ) )
{
// But without bold/italic for normal lists, since some items could be bold and others not.
// For headings we must keep the bold when the heading is bold.
counterFormat . setBold ( false ) ;
counterFormat . setItalic ( false ) ;
}
KoTextFormat * format = & counterFormat ;
p - > save ( ) ;
TQColor textColor ( format - > color ( ) ) ;
if ( ! textColor . isValid ( ) ) // Resolve the color at this point
textColor = KoTextFormat : : defaultTextColor ( p ) ;
p - > setPen ( TQPen ( textColor ) ) ;
KoTextZoomHandler * zh = textDocument ( ) - > paintingZoomHandler ( ) ;
assert ( zh ) ;
//bool forPrint = ( p->device()->devType() == TQInternal::Printer );
bool rtl = str - > isRightToLeft ( ) ; // when true, we put suffix+counter+prefix at the RIGHT of the paragraph.
int xLeft = zh - > layoutUnitToPixelX ( xLU - ( rtl ? 0 : counterWidthLU ) ) ;
int y = zh - > layoutUnitToPixelY ( yLU ) ;
//int h = zh->layoutUnitToPixelY( yLU, hLU );
int base = zh - > layoutUnitToPixelY ( yLU , baseLU ) ;
int counterWidth = zh - > layoutUnitToPixelX ( xLU , counterWidthLU ) ;
int height = zh - > layoutUnitToPixelY ( yLU , format - > height ( ) ) ;
TQFont font ( format - > screenFont ( zh ) ) ;
// Footnote numbers are in superscript (in WP and Word, not in OO)
if ( m_layout . counter - > numbering ( ) = = KoParagCounter : : NUM_FOOTNOTE )
{
int pointSize = ( ( font . pointSize ( ) * 2 ) / 3 ) ;
font . setPointSize ( pointSize ) ;
y - = ( height - TQFontMetrics ( font ) . height ( ) ) ;
}
p - > setFont ( font ) ;
// Now draw any bullet that is required over the space left for it.
if ( m_layout . counter - > isBullet ( ) )
{
int xBullet = xLeft + zh - > layoutUnitToPixelX ( m_layout . counter - > bulletX ( ) ) ;
//kdDebug(32500) << "KoTextParag::drawLabel xLU=" << xLU << " counterWidthLU=" << counterWidthLU << endl;
// The width and height of the bullet is the width of one space
int width = zh - > layoutUnitToPixelX ( xLeft , format - > width ( ' ' ) ) ;
//kdDebug(32500) << "Pix: xLeft=" << xLeft << " counterWidth=" << counterWidth
// << " xBullet=" << xBullet << " width=" << width << endl;
TQString prefix = m_layout . counter - > prefix ( ) ;
if ( ! prefix . isEmpty ( ) )
{
if ( rtl )
prefix . prepend ( ' ' /*the space before the bullet in RTL mode*/ ) ;
KoTextParag : : drawFontEffects ( p , format , zh , format - > screenFont ( zh ) , textColor , xLeft , base , width , y , height , prefix [ 0 ] ) ;
int posY = y + base - format - > offsetFromBaseLine ( ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
p - > drawText ( xLeft , posY , prefix ) ;
}
TQRect er ( xBullet + ( rtl ? width : 0 ) , y + height / 2 - width / 2 , width , width ) ;
// Draw the bullet.
int posY = 0 ;
switch ( m_layout . counter - > style ( ) )
{
case KoParagCounter : : STYLE_DISCBULLET :
p - > setBrush ( TQBrush ( textColor ) ) ;
p - > drawEllipse ( er ) ;
p - > setBrush ( TQt : : NoBrush ) ;
break ;
case KoParagCounter : : STYLE_SQUAREBULLET :
p - > fillRect ( er , TQBrush ( textColor ) ) ;
break ;
case KoParagCounter : : STYLE_BOXBULLET :
p - > drawRect ( er ) ;
break ;
case KoParagCounter : : STYLE_CIRCLEBULLET :
p - > drawEllipse ( er ) ;
break ;
case KoParagCounter : : STYLE_CUSTOMBULLET :
{
// The user has selected a symbol from a special font. Override the paragraph
// font with the given family. This conserves the right size etc.
if ( ! m_layout . counter - > customBulletFont ( ) . isEmpty ( ) )
{
TQFont bulletFont ( p - > font ( ) ) ;
bulletFont . setFamily ( m_layout . counter - > customBulletFont ( ) ) ;
p - > setFont ( bulletFont ) ;
}
KoTextParag : : drawFontEffects ( p , format , zh , format - > screenFont ( zh ) , textColor , xBullet , base , width , y , height , ' ' ) ;
posY = y + base - format - > offsetFromBaseLine ( ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
p - > drawText ( xBullet , posY , TQString ( m_layout . counter - > customBulletCharacter ( ) ) ) ;
break ;
}
default :
break ;
}
TQString suffix = m_layout . counter - > suffix ( ) ;
if ( ! suffix . isEmpty ( ) )
{
if ( ! rtl )
suffix + = ' ' /*the space after the bullet*/ ;
KoTextParag : : drawFontEffects ( p , format , zh , format - > screenFont ( zh ) , textColor , xBullet + width , base , counterWidth , y , height , suffix [ 0 ] ) ;
int posY = y + base - format - > offsetFromBaseLine ( ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
p - > drawText ( xBullet + width , posY , suffix , - 1 ) ;
}
}
else
{
TQString counterText = m_layout . counter - > text ( this ) ;
// There are no bullets...any parent bullets have already been suppressed.
// Just draw the text! Note: one space is always appended.
if ( ! counterText . isEmpty ( ) )
{
KoTextParag : : drawFontEffects ( p , format , zh , format - > screenFont ( zh ) , textColor , xLeft , base , counterWidth , y , height , counterText [ 0 ] ) ;
counterText + = ' ' /*the space after the bullet (before in RTL mode)*/ ;
int posY = y + base - format - > offsetFromBaseLine ( ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
p - > drawText ( xLeft , posY , counterText , - 1 ) ;
}
}
p - > restore ( ) ;
}
int KoTextParag : : breakableTopMargin ( ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
return zh - > ptToLayoutUnitPixY (
m_layout . margins [ TQStyleSheetItem : : MarginTop ] ) ;
}
int KoTextParag : : topMargin ( ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
return zh - > ptToLayoutUnitPixY (
m_layout . margins [ TQStyleSheetItem : : MarginTop ]
+ ( ( prev ( ) & & prev ( ) - > joinBorder ( ) & & prev ( ) - > bottomBorder ( ) = = m_layout . bottomBorder & &
prev ( ) - > topBorder ( ) = = m_layout . topBorder & & prev ( ) - > leftBorder ( ) = = m_layout . leftBorder & &
prev ( ) - > rightBorder ( ) = = m_layout . rightBorder ) ? 0 : m_layout . topBorder . width ( ) ) ) ;
}
int KoTextParag : : bottomMargin ( ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
return zh - > ptToLayoutUnitPixY (
m_layout . margins [ TQStyleSheetItem : : MarginBottom ]
+ ( ( joinBorder ( ) & & next ( ) & & next ( ) - > bottomBorder ( ) = = m_layout . bottomBorder & &
next ( ) - > topBorder ( ) = = m_layout . topBorder & & next ( ) - > leftBorder ( ) = = m_layout . leftBorder & &
next ( ) - > rightBorder ( ) = = m_layout . rightBorder ) ? 0 : m_layout . bottomBorder . width ( ) ) ) ;
}
int KoTextParag : : leftMargin ( ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
return zh - > ptToLayoutUnitPixX (
m_layout . margins [ TQStyleSheetItem : : MarginLeft ]
+ m_layout . leftBorder . width ( ) ) ;
}
int KoTextParag : : rightMargin ( ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
int cw = 0 ;
if ( m_layout . counter & & str - > isRightToLeft ( ) & &
( ( m_layout . counter - > alignment ( ) = = TQt : : AlignRight ) | | ( m_layout . counter - > alignment ( ) = = TQt : : AlignAuto ) ) )
cw = counterWidth ( ) ;
return zh - > ptToLayoutUnitPixX (
m_layout . margins [ TQStyleSheetItem : : MarginRight ]
+ m_layout . rightBorder . width ( ) )
+ cw ; /* in layout units already */
}
int KoTextParag : : firstLineMargin ( ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
return zh - > ptToLayoutUnitPixY (
m_layout . margins [ TQStyleSheetItem : : MarginFirstLine ] ) ;
}
int KoTextParag : : lineSpacing ( int line ) const
{
Q_ASSERT ( isValid ( ) ) ;
if ( m_layout . lineSpacingType = = KoParagLayout : : LS_SINGLE )
return 0 ; // or shadow, see calculateLineSpacing
else {
if ( line > = ( int ) lineStarts . count ( ) )
{
kdError ( ) < < " KoTextParag::lineSpacing assert(line<lines) failed: line= " < < line < < " lines= " < < lineStarts . count ( ) < < endl ;
return 0 ;
}
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( line - - > 0 )
+ + it ;
return ( * it ) - > lineSpacing ;
}
}
// Called by KoTextFormatter
int KoTextParag : : calculateLineSpacing ( int line , int startChar , int lastChar ) const
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
// TODO add shadow in KoTextFormatter!
int shadow = 0 ; //TQABS( zh->ptToLayoutUnitPixY( shadowDistanceY() ) );
if ( m_layout . lineSpacingType = = KoParagLayout : : LS_SINGLE )
return shadow ;
else if ( m_layout . lineSpacingType = = KoParagLayout : : LS_CUSTOM )
return zh - > ptToLayoutUnitPixY ( m_layout . lineSpacingValue ( ) ) + shadow ;
else {
if ( line > = ( int ) lineStarts . count ( ) )
{
kdError ( ) < < " KoTextParag::lineSpacing assert(line<lines) failed: line= " < < line < < " lines= " < < lineStarts . count ( ) < < endl ;
return 0 + shadow ;
}
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( line - - > 0 )
+ + it ;
//kdDebug(32500) << " line spacing type: " << m_layout.lineSpacingType << " value:" << m_layout.lineSpacingValue() << " line_height=" << (*it)->h << " startChar=" << startChar << " lastChar=" << lastChar << endl;
switch ( m_layout . lineSpacingType )
{
case KoParagLayout : : LS_MULTIPLE :
{
double n = m_layout . lineSpacingValue ( ) - 1.0 ; // yes, can be negative
return shadow + tqRound ( n * heightForLineSpacing ( startChar , lastChar ) ) ;
}
case KoParagLayout : : LS_ONEANDHALF :
{
// Special case of LS_MULTIPLE, with n=1.5
return shadow + heightForLineSpacing ( startChar , lastChar ) / 2 ;
}
case KoParagLayout : : LS_DOUBLE :
{
// Special case of LS_MULTIPLE, with n=1
return shadow + heightForLineSpacing ( startChar , lastChar ) ;
}
case KoParagLayout : : LS_AT_LEAST :
{
int atLeast = zh - > ptToLayoutUnitPixY ( m_layout . lineSpacingValue ( ) ) ;
const int lineHeight = ( * it ) - > h ;
int h = TQMAX ( lineHeight , atLeast ) ;
// height is now the required total height
return shadow + h - lineHeight ;
}
case KoParagLayout : : LS_FIXED :
{
const int lineHeight = ( * it ) - > h ;
return shadow + zh - > ptToLayoutUnitPixY ( m_layout . lineSpacingValue ( ) ) - lineHeight ;
}
// Silence compiler warnings
case KoParagLayout : : LS_SINGLE :
case KoParagLayout : : LS_CUSTOM :
break ;
}
}
kdWarning ( ) < < " Unhandled linespacing type : " < < m_layout . lineSpacingType < < endl ;
return 0 + shadow ;
}
TQRect KoTextParag : : pixelRect ( KoTextZoomHandler * zh ) const
{
TQRect rct ( zh - > layoutUnitToPixel ( rect ( ) ) ) ;
//kdDebug(32500) << " pixelRect for parag " << paragId()
// << ": rect=" << rect() << " pixelRect=" << rct << endl;
// After division we almost always end up with the top overwriting the bottom of the parag above
if ( prev ( ) )
{
TQRect prevRect ( zh - > layoutUnitToPixel ( prev ( ) - > rect ( ) ) ) ;
if ( rct . top ( ) < prevRect . bottom ( ) + 1 )
{
//kdDebug(32500) << " pixelRect: rct.top() adjusted to " << prevRect.bottom() + 1 << " (was " << rct.top() << ")" << endl;
rct . setTop ( prevRect . bottom ( ) + 1 ) ;
}
}
return rct ;
}
// Paint this paragraph. This is called by KoTextDocument::drawParagWYSIWYG
// (KoTextDocument::drawWithoutDoubleBuffer when printing)
void KoTextParag : : paint ( TQPainter & painter , const TQColorGroup & cg , KoTextCursor * cursor , bool drawSelections ,
int clipx , int clipy , int clipw , int cliph )
{
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " KoTextParag::paint ===== id= " < < paragId ( ) < < " clipx= " < < clipx < < " clipy= " < < clipy < < " clipw= " < < clipw < < " cliph= " < < cliph < < endl ;
kdDebug ( 32500 ) < < " clipw in pix (approx) : " < < textDocument ( ) - > paintingZoomHandler ( ) - > layoutUnitToPixelX ( clipw ) < < " cliph in pix (approx) : " < < textDocument ( ) - > paintingZoomHandler ( ) - > layoutUnitToPixelX ( cliph ) < < endl ;
# endif
KoTextZoomHandler * zh = textDocument ( ) - > paintingZoomHandler ( ) ;
assert ( zh ) ;
TQRect paraRect = pixelRect ( zh ) ;
// Find left margin size, first line offset and right margin in pixels
int leftMarginPix = zh - > layoutUnitToPixelX ( leftMargin ( ) ) ;
int firstLineOffset = zh - > layoutUnitToPixelX ( firstLineMargin ( ) ) ;
// The furthest left and right x-coords of the paragraph,
// including the bullet/counter, but not the borders.
int leftExtent = TQMIN ( leftMarginPix , leftMarginPix + firstLineOffset ) ;
int rightExtent = paraRect . width ( ) - zh - > layoutUnitToPixelX ( rightMargin ( ) ) ;
// Draw the paragraph background color
if ( backgroundColor ( ) . isValid ( ) )
{
// Render background from either left margin indent, or first line indent,
// whichever is nearer the left.
int backgroundWidth = rightExtent - leftExtent + 1 ;
int backgroundHeight = pixelRect ( zh ) . height ( ) ;
painter . fillRect ( leftExtent , 0 ,
backgroundWidth , backgroundHeight ,
backgroundColor ( ) ) ;
}
// Let's call drawLabel ourselves, rather than having to deal with TQStyleSheetItem to get paintLines to call it!
if ( m_layout . counter & & m_layout . counter - > numbering ( ) ! = KoParagCounter : : NUM_NONE & & m_lineChanged < = 0 )
{
int cy , h , baseLine ;
lineInfo ( 0 , cy , h , baseLine ) ;
int xLabel = at ( 0 ) - > x ;
if ( str - > isRightToLeft ( ) )
xLabel + = at ( 0 ) - > width ;
drawLabel ( & painter , xLabel , cy , 0 , 0 , baseLine , cg ) ;
}
paintLines ( painter , cg , cursor , drawSelections , clipx , clipy , clipw , cliph ) ;
// Now draw paragraph border
if ( m_layout . hasBorder ( ) )
{
bool const drawTopBorder = ! prev ( ) | | ! prev ( ) - > joinBorder ( ) | | prev ( ) - > bottomBorder ( ) ! = bottomBorder ( ) | | prev ( ) - > topBorder ( ) ! = topBorder ( ) | | prev ( ) - > leftBorder ( ) ! = leftBorder ( ) | | prev ( ) - > rightBorder ( ) ! = rightBorder ( ) ;
bool const drawBottomBorder = ! joinBorder ( ) | | ! next ( ) | | next ( ) - > bottomBorder ( ) ! = bottomBorder ( ) | | next ( ) - > topBorder ( ) ! = topBorder ( ) | | next ( ) - > leftBorder ( ) ! = leftBorder ( ) | | next ( ) - > rightBorder ( ) ! = rightBorder ( ) ;
// Paragraph borders surround the paragraph and its
// counters/bullets, but they only touch the frame border if
// the paragraph margins are of non-zero length.
// This is what OpenOffice does (no really, it is this time).
//
// drawBorders paints outside the give rect, so for the
// x-coords, it just needs to know the left and right extent
// of the paragraph.
TQRect r ;
r . setLeft ( leftExtent ) ;
r . setRight ( rightExtent ) ;
r . setTop ( zh - > layoutUnitToPixelY ( lineY ( 0 ) ) ) ;
int lastLine = lines ( ) - 1 ;
// We need to start from the pixelRect, to make sure the bottom border is entirely painted.
// This is a case where we DO want to subtract pixels to pixels...
int paragBottom = pixelRect ( zh ) . height ( ) - 1 ;
// If we don't have a bottom border, we need go as low as possible ( to touch the next parag's border ).
// If we have a bottom border, then we rather exclude the linespacing. Looks nicer. OO does that too.
if ( m_layout . bottomBorder . width ( ) > 0 & & drawBottomBorder )
paragBottom - = zh - > layoutUnitToPixelY ( lineSpacing ( lastLine ) ) ;
paragBottom - = KoBorder : : zoomWidthY ( m_layout . bottomBorder . width ( ) , zh , 0 ) ;
//kdDebug(32500) << "Parag border: paragBottom=" << paragBottom
// << " bottom border width = " << KoBorder::zoomWidthY( m_layout.bottomBorder.width(), zh, 0 ) << endl;
r . setBottom ( paragBottom ) ;
//kdDebug(32500) << "KoTextParag::paint documentWidth=" << documentWidth() << " LU (" << zh->layoutUnitToPixelX(documentWidth()) << " pixels) bordersRect=" << r << endl;
KoBorder : : drawBorders ( painter , zh , r ,
m_layout . leftBorder , m_layout . rightBorder , m_layout . topBorder , m_layout . bottomBorder ,
0 , TQPen ( ) , drawTopBorder , drawBottomBorder ) ;
}
}
void KoTextParag : : paintLines ( TQPainter & painter , const TQColorGroup & cg , KoTextCursor * cursor , bool drawSelections ,
int clipx , int clipy , int clipw , int cliph )
{
if ( ! visible )
return ;
//KoTextStringChar *chr = at( 0 );
//if (!chr) { kdDebug(32500) << "paragraph " << (void*)this << " " << paragId() << ", can't paint, EMPTY !" << endl;
// This is necessary with the current code, but in theory it shouldn't
// be necessary, if Xft really gives us fully proportionnal chars....
# define CHECK_PIXELXADJ
int curx = - 1 , cury = 0 , curh = 0 , curline = 0 ;
int xstart , xend = 0 ;
TQString qstr = str - > toString ( ) ;
qstr . replace ( TQChar ( 0x00a0U ) , ' ' ) ; // Not all fonts have non-breakable-space glyph
const int nSels = doc ? doc - > numSelections ( ) : 1 ;
TQMemArray < int > selectionStarts ( nSels ) ;
TQMemArray < int > selectionEnds ( nSels ) ;
if ( drawSelections ) {
bool hasASelection = FALSE ;
for ( int i = 0 ; i < nSels ; + + i ) {
if ( ! hasSelection ( i ) ) {
selectionStarts [ i ] = - 1 ;
selectionEnds [ i ] = - 1 ;
} else {
hasASelection = TRUE ;
selectionStarts [ i ] = selectionStart ( i ) ;
int end = selectionEnd ( i ) ;
if ( end = = length ( ) - 1 & & n & & n - > hasSelection ( i ) )
end + + ;
selectionEnds [ i ] = end ;
}
}
if ( ! hasASelection )
drawSelections = FALSE ;
}
// Draw the lines!
int line = m_lineChanged ;
if ( line < 0 ) line = 0 ;
int numLines = lines ( ) ;
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " paintLines: from line " < < line < < " to " < < numLines - 1 < < endl ;
# endif
for ( ; line < numLines ; line + + )
{
// get the start and length of the line
int nextLine ;
int startOfLine ;
lineStartOfLine ( line , & startOfLine ) ;
if ( line = = numLines - 1 )
nextLine = length ( ) ;
else
lineStartOfLine ( line + 1 , & nextLine ) ;
// init this line
int cy , h , baseLine ;
lineInfo ( line , cy , h , baseLine ) ;
if ( clipy ! = - 1 & & cy > clipy - r . y ( ) + cliph ) // outside clip area, leave
break ;
// Vars related to the current "run of text"
int paintStart = startOfLine ;
KoTextStringChar * chr = at ( startOfLine ) ;
KoTextStringChar * nextchr = chr ;
// okay, paint the line!
for ( int i = startOfLine ; i < nextLine ; i + + )
{
chr = nextchr ;
if ( i < nextLine - 1 )
nextchr = at ( i + 1 ) ;
// we flush at end of line
bool flush = ( i = = nextLine - 1 ) ;
// Optimization note: TQRT uses "flush |=", which doesn't have shortcut optimization
// we flush on format changes
flush = flush | | ( nextchr - > format ( ) ! = chr - > format ( ) ) ;
// we flush on link changes
//flush = flush || ( nextchr->isLink() != chr->isLink() );
// we flush on small caps changes
if ( ! flush & & chr - > format ( ) - > attributeFont ( ) = = KoTextFormat : : ATT_SMALL_CAPS )
{
bool isLowercase = chr - > c . upper ( ) ! = chr - > c ;
bool nextLowercase = nextchr - > c . upper ( ) ! = nextchr - > c ;
flush = isLowercase ! = nextLowercase ;
}
// we flush on start of run
flush = flush | | nextchr - > startOfRun ;
// we flush on bidi changes
flush = flush | | ( nextchr - > rightToLeft ! = chr - > rightToLeft ) ;
# ifdef CHECK_PIXELXADJ
// we flush when the value of pixelxadj changes
// [unless inside a ligature]
flush = flush | | ( nextchr - > pixelxadj ! = chr - > pixelxadj & & nextchr - > charStop ) ;
# endif
// we flush before and after tabs
flush = flush | | ( chr - > c = = ' \t ' | | nextchr - > c = = ' \t ' ) ;
// we flush on soft hypens
flush = flush | | ( chr - > c . unicode ( ) = = 0xad ) ;
// we flush on custom items
flush = flush | | chr - > isCustom ( ) ;
// we flush before custom items
flush = flush | | nextchr - > isCustom ( ) ;
// when painting justified we flush on spaces
if ( ( alignment ( ) & TQt : : AlignJustify ) = = TQt : : AlignJustify )
//flush = flush || TQTextFormatter::isBreakable( str, i );
flush = flush | | chr - > whiteSpace ;
// when underlining or striking "word by word" we flush before/after spaces
if ( ! flush & & chr - > format ( ) - > wordByWord ( ) & & chr - > format ( ) - > isStrikedOrUnderlined ( ) )
flush = flush | | chr - > whiteSpace | | nextchr - > whiteSpace ;
// we flush when the string is getting too long
flush = flush | | ( i - paintStart > = 256 ) ;
// we flush when the selection state changes
if ( drawSelections ) {
// check if selection state changed - TODO update from TQRT
bool selectionChange = FALSE ;
if ( drawSelections ) {
for ( int j = 0 ; j < nSels ; + + j ) {
selectionChange = selectionStarts [ j ] = = i + 1 | | selectionEnds [ j ] = = i + 1 ;
if ( selectionChange )
break ;
}
}
flush = flush | | selectionChange ;
}
// check for cursor mark
if ( cursor & & this = = cursor - > parag ( ) & & i = = cursor - > index ( ) ) {
curx = cursor - > x ( ) ;
curline = line ;
KoTextStringChar * c = chr ;
if ( i > 0 )
- - c ;
curh = c - > height ( ) ;
cury = cy + baseLine - c - > ascent ( ) ;
}
if ( flush ) { // something changed, draw what we have so far
KoTextStringChar * cStart = at ( paintStart ) ;
if ( chr - > rightToLeft ) {
xstart = chr - > x ;
xend = cStart - > x + cStart - > width ;
} else {
xstart = cStart - > x ;
if ( i < length ( ) - 1 & & ! str - > at ( i + 1 ) . lineStart & &
str - > at ( i + 1 ) . rightToLeft = = chr - > rightToLeft )
xend = str - > at ( i + 1 ) . x ;
else
xend = chr - > x + chr - > width ;
}
if ( ( clipx = = - 1 | | clipw = = - 1 ) | | ( xend > = clipx & & xstart < = clipx + clipw ) ) {
if ( ! chr - > isCustom ( ) ) {
drawParagString ( painter , qstr , paintStart , i - paintStart + 1 , xstart , cy ,
baseLine , xend - xstart , h , drawSelections ,
chr - > format ( ) , selectionStarts , selectionEnds ,
cg , chr - > rightToLeft , line ) ;
}
else
if ( chr - > customItem ( ) - > placement ( ) = = KoTextCustomItem : : PlaceInline ) {
chr - > customItem ( ) - > draw ( & painter , chr - > x , cy + baseLine - chr - > customItem ( ) - > ascent ( ) ,
clipx - r . x ( ) , clipy - r . y ( ) , clipw , cliph , cg ,
drawSelections & & nSels & & selectionStarts [ 0 ] < = i & & selectionEnds [ 0 ] > i ) ;
}
}
paintStart = i + 1 ;
}
} // end of character loop
} // end of line loop
// if we should draw a cursor, draw it now
if ( curx ! = - 1 & & cursor ) {
drawCursor ( painter , cursor , curx , cury , curh , cg ) ;
}
}
// Called by KoTextParag::paintLines
// Draw a set of characters with the same formattings.
// Reimplemented here to convert coordinates first, and call @ref drawFormattingChars.
void KoTextParag : : drawParagString ( TQPainter & painter , const TQString & str , int start , int len , int startX ,
int lastY , int baseLine , int bw , int h , bool drawSelections ,
KoTextFormat * format , const TQMemArray < int > & selectionStarts ,
const TQMemArray < int > & selectionEnds , const TQColorGroup & cg , bool rightToLeft , int line )
{
KoTextZoomHandler * zh = textDocument ( ) - > paintingZoomHandler ( ) ;
assert ( zh ) ;
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " KoTextParag::drawParagString drawing from " < < start < < " to " < < start + len < < endl ;
kdDebug ( 32500 ) < < " startX in LU: " < < startX < < " lastY in LU: " < < lastY
< < " baseLine in LU: " < < baseLine < < endl ;
# endif
// Calculate offset (e.g. due to shadow on left or top)
// Important: don't use the 2-args methods here, offsets are not heights
// (0 should be 0, not 1) (#63256)
int shadowOffsetX_pix = zh - > layoutUnitToPixelX ( format - > offsetX ( ) ) ;
int shadowOffsetY_pix = zh - > layoutUnitToPixelY ( format - > offsetY ( ) ) ;
// Calculate startX in pixels
int startX_pix = zh - > layoutUnitToPixelX ( startX ) /* + at( rightToLeft ? start+len-1 : start )->pixelxadj */ ;
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " KoTextParag::drawParagString startX in pixels : " < < startX_pix /*<< " adjustment:" << at( rightToLeft ? start+len-1 : start )->pixelxadj*/ < < " bw= " < < bw < < endl ;
# endif
int bw_pix = zh - > layoutUnitToPixelX ( startX , bw ) ;
int lastY_pix = zh - > layoutUnitToPixelY ( lastY ) ;
int baseLine_pix = zh - > layoutUnitToPixelY ( lastY , baseLine ) ; // 2 args=>+1. Is that correct?
int h_pix = zh - > layoutUnitToPixelY ( lastY , h ) ;
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " KoTextParag::drawParagString h(LU)= " < < h < < " lastY(LU)= " < < lastY
< < " h(PIX)= " < < h_pix < < " lastY(PIX)= " < < lastY_pix
< < " baseLine(PIX)= " < < baseLine_pix < < endl ;
# endif
if ( format - > textBackgroundColor ( ) . isValid ( ) )
painter . fillRect ( startX_pix , lastY_pix , bw_pix , h_pix , format - > textBackgroundColor ( ) ) ;
// don't want to draw line breaks but want them when drawing formatting chars
int draw_len = len ;
int draw_startX = startX ;
int draw_bw = bw_pix ;
if ( at ( start + len - 1 ) - > c = = ' \n ' )
{
draw_len - - ;
draw_bw - = at ( start + len - 1 ) - > pixelwidth ;
if ( rightToLeft & & draw_len > 0 )
draw_startX = at ( start + draw_len - 1 ) - > x ;
}
// Draw selection (moved here to do it before applying the offset from the shadow)
// (and because it's not part of the shadow drawing)
if ( drawSelections ) {
bool inSelection = false ;
const int nSels = doc ? doc - > numSelections ( ) : 1 ;
for ( int j = 0 ; j < nSels ; + + j ) {
if ( start > = selectionStarts [ j ] & & start < selectionEnds [ j ] ) {
inSelection = true ;
switch ( j ) {
case KoTextDocument : : Standard :
painter . fillRect ( startX_pix , lastY_pix , bw_pix , h_pix , cg . color ( TQColorGroup : : Highlight ) ) ;
break ;
case KoTextDocument : : InputMethodPreedit :
// no highlight
break ;
default :
painter . fillRect ( startX_pix , lastY_pix , bw_pix , h_pix , doc ? doc - > selectionColor ( j ) : cg . color ( TQColorGroup : : Highlight ) ) ;
break ;
}
}
}
if ( ! inSelection )
drawSelections = false ; // save time in drawParagStringInternal
}
// Draw InputMethod Preedit Underline
const int nSels = doc ? doc - > numSelections ( ) : 1 ;
if ( KoTextDocument : : InputMethodPreedit < nSels
& & doc - > hasSelection ( KoTextDocument : : InputMethodPreedit )
& & start > = selectionStarts [ KoTextDocument : : InputMethodPreedit ]
& & start < selectionEnds [ KoTextDocument : : InputMethodPreedit ] )
{
TQColor textColor ( format - > color ( ) ) ;
painter . setPen ( TQPen ( textColor ) ) ;
TQPoint p1 ( startX_pix , lastY_pix + h_pix - 1 ) ;
TQPoint p2 ( startX_pix + bw_pix , lastY_pix + h_pix - 1 ) ;
painter . drawLine ( p1 , p2 ) ;
}
if ( draw_len > 0 )
{
int draw_startX_pix = zh - > layoutUnitToPixelX ( draw_startX ) /* + at( rightToLeft ? start+draw_len-1 : start )->pixelxadj*/ ;
draw_startX_pix + = shadowOffsetX_pix ;
lastY_pix + = shadowOffsetY_pix ;
if ( format - > shadowDistanceX ( ) ! = 0 | | format - > shadowDistanceY ( ) ! = 0 ) {
int sx = format - > shadowX ( zh ) ;
int sy = format - > shadowY ( zh ) ;
if ( sx ! = 0 | | sy ! = 0 )
{
painter . save ( ) ;
painter . translate ( sx , sy ) ;
drawParagStringInternal ( painter , str , start , draw_len , draw_startX_pix ,
lastY_pix , baseLine_pix ,
draw_bw ,
h_pix , FALSE /*drawSelections*/ ,
format , selectionStarts ,
selectionEnds , cg , rightToLeft , line , zh , true ) ;
painter . restore ( ) ;
}
}
drawParagStringInternal ( painter , str , start , draw_len , draw_startX_pix ,
lastY_pix , baseLine_pix ,
draw_bw ,
h_pix , drawSelections , format , selectionStarts ,
selectionEnds , cg , rightToLeft , line , zh , false ) ;
}
bool forPrint = ( painter . device ( ) - > devType ( ) = = TQInternal : : Printer ) ;
if ( textDocument ( ) - > drawFormattingChars ( ) & & ! forPrint )
{
drawFormattingChars ( painter , start , len ,
lastY_pix , baseLine_pix , h_pix ,
drawSelections ,
format , selectionStarts ,
selectionEnds , cg , rightToLeft ,
line , zh , AllFormattingChars ) ;
}
}
// Copied from the original KoTextParag
// (we have to copy it here, so that color & font changes don't require changing
// a local copy of the text format)
// And we have to keep it separate from drawParagString to avoid s/startX/startX_pix/ etc.
void KoTextParag : : drawParagStringInternal ( TQPainter & painter , const TQString & s , int start , int len , int startX ,
int lastY , int baseLine , int bw , int h , bool drawSelections ,
KoTextFormat * format , const TQMemArray < int > & selectionStarts ,
const TQMemArray < int > & selectionEnds , const TQColorGroup & cg , bool rightToLeft , int line , KoTextZoomHandler * zh , bool drawingShadow )
{
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " KoTextParag::drawParagStringInternal start= " < < start < < " len= " < < len < < " : ' " < < s . mid ( start , len ) < < " ' " < < endl ;
kdDebug ( 32500 ) < < " In pixels: startX= " < < startX < < " lastY= " < < lastY < < " baseLine= " < < baseLine
< < " bw= " < < bw < < " h= " < < h < < " rightToLeft= " < < rightToLeft < < endl ;
# endif
if ( drawingShadow & & format - > shadowDistanceX ( ) = = 0 & & format - > shadowDistanceY ( ) = = 0 )
return ;
// 1) Sort out the color
TQColor textColor ( drawingShadow ? format - > shadowColor ( ) : format - > color ( ) ) ;
if ( ! textColor . isValid ( ) ) // Resolve the color at this point
textColor = KoTextFormat : : defaultTextColor ( & painter ) ;
// 2) Sort out the font
TQFont font ( format - > screenFont ( zh ) ) ;
if ( format - > attributeFont ( ) = = KoTextFormat : : ATT_SMALL_CAPS & & s [ start ] . upper ( ) ! = s [ start ] )
font = format - > smallCapsFont ( zh , true ) ;
#if 0
TQFontInfo fi ( font ) ;
kdDebug ( 32500 ) < < " KoTextParag::drawParagStringInternal requested font " < < font . pointSizeFloat ( ) < < " using font " < < fi . pointSize ( ) < < " pt (format font: " < < format - > font ( ) . pointSizeFloat ( ) < < " pt) " < < endl ;
TQFontMetrics fm ( font ) ;
kdDebug ( 32500 ) < < " Real font: " < < fi . family ( ) < < " . Font height in pixels: " < < fm . height ( ) < < endl ;
# endif
// 3) Paint
TQString str ( s ) ;
if ( str [ ( int ) str . length ( ) - 1 ] . unicode ( ) = = 0xad )
str . remove ( str . length ( ) - 1 , 1 ) ;
painter . setPen ( TQPen ( textColor ) ) ;
painter . setFont ( font ) ;
KoTextDocument * doc = document ( ) ;
if ( drawSelections ) {
const int nSels = doc ? doc - > numSelections ( ) : 1 ;
for ( int j = 0 ; j < nSels ; + + j ) {
if ( start > = selectionStarts [ j ] & & start < selectionEnds [ j ] ) {
if ( ! doc | | doc - > invertSelectionText ( j ) )
textColor = cg . color ( TQColorGroup : : HighlightedText ) ;
painter . setPen ( TQPen ( textColor ) ) ;
break ;
}
}
}
TQPainter : : TextDirection dir = rightToLeft ? TQPainter : : RTL : TQPainter : : LTR ;
if ( dir ! = TQPainter : : RTL & & start + len = = length ( ) ) // don't draw the last character (trailing space)
{
len - - ;
if ( len < = 0 )
return ;
bw - = at ( length ( ) - 1 ) - > pixelwidth ;
}
KoTextParag : : drawFontEffects ( & painter , format , zh , font , textColor , startX , baseLine , bw , lastY , h , str [ start ] ) ;
if ( str [ start ] ! = ' \t ' & & str [ start ] . unicode ( ) ! = 0xad ) {
str = format - > displayedString ( str ) ; // #### This converts the whole string, instead of from start to start+len!
if ( format - > vAlign ( ) = = KoTextFormat : : AlignNormal ) {
int posY = lastY + baseLine ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
painter . drawText ( startX , posY , str , start , len , dir ) ;
# ifdef BIDI_DEBUG
painter . save ( ) ;
painter . setPen ( TQt : : red ) ;
painter . drawLine ( startX , lastY , startX , lastY + baseLine ) ;
painter . drawLine ( startX , lastY + baseLine / 2 , startX + 10 , lastY + baseLine / 2 ) ;
int w = 0 ;
int i = 0 ;
while ( i < len )
w + = painter . fontMetrics ( ) . charWidth ( str , start + i + + ) ;
painter . setPen ( TQt : : blue ) ;
painter . drawLine ( startX + w - 1 , lastY , startX + w - 1 , lastY + baseLine ) ;
painter . drawLine ( startX + w - 1 , lastY + baseLine / 2 , startX + w - 1 - 10 , lastY + baseLine / 2 ) ;
painter . restore ( ) ;
# endif
} else if ( format - > vAlign ( ) = = KoTextFormat : : AlignSuperScript ) {
int posY = lastY + baseLine - ( painter . fontMetrics ( ) . height ( ) / 2 ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
painter . drawText ( startX , posY , str , start , len , dir ) ;
} else if ( format - > vAlign ( ) = = KoTextFormat : : AlignSubScript ) {
int posY = lastY + baseLine + ( painter . fontMetrics ( ) . height ( ) / 6 ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
painter . drawText ( startX , posY , str , start , len , dir ) ;
} else if ( format - > vAlign ( ) = = KoTextFormat : : AlignCustom ) {
int posY = lastY + baseLine - format - > offsetFromBaseLine ( ) ;
//we must move to bottom text because we create
//shadow to 'top'.
int sy = format - > shadowY ( zh ) ;
if ( sy < 0 )
posY - = sy ;
painter . drawText ( startX , posY , str , start , len , dir ) ;
}
}
if ( str [ start ] = = ' \t ' & & m_tabCache . contains ( start ) ) {
painter . save ( ) ;
KoTextZoomHandler * zh = textDocument ( ) - > paintingZoomHandler ( ) ;
const KoTabulator & tab = m_layout . tabList ( ) [ m_tabCache [ start ] ] ;
int lineWidth = zh - > zoomItY ( tab . ptWidth ) ;
switch ( tab . filling ) {
case TF_DOTS :
painter . setPen ( TQPen ( textColor , lineWidth , TQt : : DotLine ) ) ;
painter . drawLine ( startX , lastY + baseLine , startX + bw , lastY + baseLine ) ;
break ;
case TF_LINE :
painter . setPen ( TQPen ( textColor , lineWidth , TQt : : SolidLine ) ) ;
painter . drawLine ( startX , lastY + baseLine , startX + bw , lastY + baseLine ) ;
break ;
case TF_DASH :
painter . setPen ( TQPen ( textColor , lineWidth , TQt : : DashLine ) ) ;
painter . drawLine ( startX , lastY + baseLine , startX + bw , lastY + baseLine ) ;
break ;
case TF_DASH_DOT :
painter . setPen ( TQPen ( textColor , lineWidth , TQt : : DashDotLine ) ) ;
painter . drawLine ( startX , lastY + baseLine , startX + bw , lastY + baseLine ) ;
break ;
case TF_DASH_DOT_DOT :
painter . setPen ( TQPen ( textColor , lineWidth , TQt : : DashDotDotLine ) ) ;
painter . drawLine ( startX , lastY + baseLine , startX + bw , lastY + baseLine ) ;
break ;
default :
break ;
}
painter . restore ( ) ;
}
if ( start + len < length ( ) & & at ( start + len ) - > lineStart )
{
# ifdef DEBUG_PAINT
//kdDebug(32500) << "we are drawing the end of line " << line << ". Auto-hyphenated: " << lineHyphenated( line ) << endl;
# endif
bool drawHyphen = at ( start + len - 1 ) - > c . unicode ( ) = = 0xad ;
drawHyphen = drawHyphen | | lineHyphenated ( line ) ;
if ( drawHyphen ) {
# ifdef DEBUG_PAINT
kdDebug ( 32500 ) < < " drawing hyphen at x= " < < startX + bw < < endl ;
# endif
painter . drawText ( startX + bw , lastY + baseLine , TQString ( " - " ) ) ; // \xad gives squares with some fonts (!?)
}
}
// Paint a zigzag line for "wrong" background spellchecking checked words:
if (
painter . device ( ) - > devType ( ) ! = TQInternal : : Printer & &
format - > isMisspelled ( ) & &
! drawingShadow & &
textDocument ( ) - > drawingMissingSpellLine ( ) )
{
painter . save ( ) ;
painter . setPen ( TQPen ( TQt : : red , 1 ) ) ;
// Draw 3 pixel lines with increasing offset and distance 4:
for ( int zigzag_line = 0 ; zigzag_line < 3 ; + + zigzag_line )
{
for ( int zigzag_x = zigzag_line ; zigzag_x < bw ; zigzag_x + = 4 )
{
painter . drawPoint (
startX + zigzag_x ,
lastY + baseLine + h / 12 - 1 + zigzag_line ) ;
}
}
// "Double" the pixel number for the middle line:
for ( int zigzag_x = 3 ; zigzag_x < bw ; zigzag_x + = 4 )
{
painter . drawPoint (
startX + zigzag_x ,
lastY + baseLine + h / 12 ) ;
}
painter . restore ( ) ;
}
}
bool KoTextParag : : lineHyphenated ( int l ) const
{
if ( l > ( int ) lineStarts . count ( ) - 1 ) {
kdWarning ( ) < < " KoTextParag::lineHyphenated: line " < < l < < " out of range! " < < endl ;
return false ;
}
if ( ! isValid ( ) )
const_cast < KoTextParag * > ( this ) - > format ( ) ;
TQMap < int , KoTextParagLineStart * > : : ConstIterator it = lineStarts . begin ( ) ;
while ( l - - > 0 )
+ + it ;
return ( * it ) - > hyphenated ;
}
/** Draw the cursor mark. Reimplemented from KoTextParag to convert coordinates first. */
void KoTextParag : : drawCursor ( TQPainter & painter , KoTextCursor * cursor , int curx , int cury , int curh , const TQColorGroup & cg )
{
KoTextZoomHandler * zh = textDocument ( ) - > paintingZoomHandler ( ) ;
int x = zh - > layoutUnitToPixelX ( curx ) /*+ cursor->parag()->at( cursor->index() )->pixelxadj*/ ;
//kdDebug(32500) << " drawCursor: LU: [cur]x=" << curx << ", cury=" << cury << " -> PIX: x=" << x << ", y=" << zh->layoutUnitToPixelY( cury ) << endl;
KoTextParag : : drawCursorDefault ( painter , cursor , x ,
zh - > layoutUnitToPixelY ( cury ) ,
zh - > layoutUnitToPixelY ( cury , curh ) , cg ) ;
}
// Reimplemented from KoTextParag
void KoTextParag : : copyParagData ( KoTextParag * parag )
{
// Style of the previous paragraph
KoParagStyle * style = parag - > style ( ) ;
// Obey "following style" setting
bool styleApplied = false ;
if ( style )
{
KoParagStyle * newStyle = style - > followingStyle ( ) ;
if ( newStyle & & style ! = newStyle ) // if same style, keep paragraph-specific changes as usual
{
setParagLayout ( newStyle - > paragLayout ( ) ) ;
KoTextFormat * format = & newStyle - > format ( ) ;
setFormat ( format ) ;
format - > addRef ( ) ;
str - > setFormat ( 0 , format , true ) ; // prepare format for text insertion
styleApplied = true ;
}
}
// This should never happen in KWord, but it happens in KPresenter
//else
// kdWarning() << "Paragraph has no style " << paragId() << endl;
// No "following style" setting, or same style -> copy layout & format of previous paragraph
if ( ! styleApplied )
{
setParagLayout ( parag - > paragLayout ( ) ) ;
// Remove pagebreak flags from initial parag - they got copied to the new parag
parag - > m_layout . pageBreaking & = ~ KoParagLayout : : HardFrameBreakBefore ;
parag - > m_layout . pageBreaking & = ~ KoParagLayout : : HardFrameBreakAfter ;
// Remove footnote counter text from second parag
if ( m_layout . counter & & m_layout . counter - > numbering ( ) = = KoParagCounter : : NUM_FOOTNOTE )
setNoCounter ( ) ;
// Do not copy 'restart numbering at this paragraph' option (would be silly)
if ( m_layout . counter )
m_layout . counter - > setRestartCounter ( false ) ;
// set parag format to the format of the trailing space of the previous parag
setFormat ( parag - > at ( parag - > length ( ) - 1 ) - > format ( ) ) ;
// KoTextCursor::splitAndInsertEmptyParag takes care of setting the format
// for the chars in the new parag
}
// Note: we don't call the original KoTextParag::copyParagData on purpose.
// We don't want setListStyle to get called - it ruins our stylesheetitems
// And we don't care about copying the stylesheetitems directly,
// applying the parag layout will create them
}
void KoTextParag : : setTabList ( const KoTabulatorList & tabList )
{
KoTabulatorList lst ( tabList ) ;
m_layout . setTabList ( lst ) ;
if ( ! tabList . isEmpty ( ) )
{
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
int * tabs = new int [ tabList . count ( ) + 1 ] ; // will be deleted by ~KoTextParag
KoTabulatorList : : Iterator it = lst . begin ( ) ;
unsigned int i = 0 ;
for ( ; it ! = lst . end ( ) ; + + it , + + i )
tabs [ i ] = zh - > ptToLayoutUnitPixX ( ( * it ) . ptPos ) ;
tabs [ i ] = 0 ;
assert ( i = = tabList . count ( ) ) ;
setTabArray ( tabs ) ;
} else
{
setTabArray ( 0 ) ;
}
invalidate ( 0 ) ;
}
/** "Reimplemented" (compared to nextTabDefault) to implement non-left-aligned tabs */
int KoTextParag : : nextTab ( int chnum , int x , int availableWidth )
{
if ( ! m_layout . tabList ( ) . isEmpty ( ) )
{
// Fetch the zoomed and sorted tab positions from KoTextParag
// We stored them there for faster access
int * tArray = tabArray ( ) ;
int i = 0 ;
if ( str - > isRightToLeft ( ) )
i = m_layout . tabList ( ) . size ( ) - 1 ;
KoTextZoomHandler * zh = textDocument ( ) - > formattingZoomHandler ( ) ;
while ( i > = 0 & & i < ( int ) m_layout . tabList ( ) . size ( ) ) {
//kdDebug(32500) << "KoTextParag::nextTab tArray[" << i << "]=" << tArray[i] << " type " << m_layout.tabList()[i].type << endl;
int tab = tArray [ i ] ;
// If a right-aligned tab is after the right edge then assume
// that it -is- on the right edge, otherwise the last letters will fall off.
// This is compatible with OOo's behavior.
if ( tab > availableWidth ) {
//kdDebug(32500) << "Tab position adjusted to availableWidth=" << availableWidth << endl;
tab = availableWidth ;
}
if ( str - > isRightToLeft ( ) )
tab = availableWidth - tab ;
if ( tab > x ) {
int type = m_layout . tabList ( ) [ i ] . type ;
// fix the tab type for right to left text
if ( str - > isRightToLeft ( ) )
if ( type = = T_RIGHT )
type = T_LEFT ;
else if ( type = = T_LEFT )
type = T_RIGHT ;
switch ( type ) {
case T_RIGHT :
case T_CENTER :
{
// Look for the next tab (or EOL)
int c = chnum + 1 ;
int w = 0 ;
while ( c < str - > length ( ) - 1 & & str - > at ( c ) . c ! = ' \t ' & & str - > at ( c ) . c ! = ' \n ' )
{
KoTextStringChar & ch = str - > at ( c ) ;
// Determine char width
// This must be done in the same way as in KoTextFormatter::format() or there can be different rounding errors.
if ( ch . isCustom ( ) )
w + = ch . customItem ( ) - > width ;
else
{
KoTextFormat * charFormat = ch . format ( ) ;
int ww = charFormat - > charWidth ( zh , false , & ch , this , c ) ;
ww = KoTextZoomHandler : : ptToLayoutUnitPt ( ww ) ;
w + = ww ;
}
+ + c ;
}
m_tabCache [ chnum ] = i ;
if ( type = = T_RIGHT )
return tab - w ;
else // T_CENTER
return tab - w / 2 ;
}
case T_DEC_PNT :
{
// Look for the next tab (or EOL), and for alignChar
// Default to right-aligned if no decimal point found (behavior from msword)
int c = chnum + 1 ;
int w = 0 ;
while ( c < str - > length ( ) - 1 & & str - > at ( c ) . c ! = ' \t ' & & str - > at ( c ) . c ! = ' \n ' )
{
KoTextStringChar & ch = str - > at ( c ) ;
if ( ch . c = = m_layout . tabList ( ) [ i ] . alignChar )
{
// Can't use ch.width yet, since the formatter hasn't run over those chars
int ww = ch . format ( ) - > charWidth ( zh , false , & ch , this , c ) ;
ww = KoTextZoomHandler : : ptToLayoutUnitPt ( ww ) ;
if ( str - > isRightToLeft ( ) )
{
w = ww / 2 ; // center around the decimal point
+ + c ;
continue ;
}
else
{
w + = ww / 2 ; // center around the decimal point
break ;
}
}
// Determine char width
if ( ch . isCustom ( ) )
w + = ch . customItem ( ) - > width ;
else
{
int ww = ch . format ( ) - > charWidth ( zh , false , & ch , this , c ) ;
w + = KoTextZoomHandler : : ptToLayoutUnitPt ( ww ) ;
}
+ + c ;
}
m_tabCache [ chnum ] = i ;
return tab - w ;
}
default : // case T_LEFT:
m_tabCache [ chnum ] = i ;
return tab ;
}
}
if ( str - > isRightToLeft ( ) )
- - i ;
else
+ + i ;
}
}
// No tab list, use tab-stop-width. qrichtext.cpp has the code :)
return KoTextParag : : nextTabDefault ( chnum , x ) ;
}
void KoTextParag : : applyStyle ( KoParagStyle * style )
{
setParagLayout ( style - > paragLayout ( ) ) ;
KoTextFormat * newFormat = & style - > format ( ) ;
setFormat ( 0 , str - > length ( ) , newFormat ) ;
setFormat ( newFormat ) ;
}
void KoTextParag : : setParagLayout ( const KoParagLayout & layout , int flags , int marginIndex )
{
//kdDebug(32500) << "KoTextParag::setParagLayout flags=" << flags << endl;
if ( flags & KoParagLayout : : Alignment )
setAlign ( layout . alignment ) ;
if ( flags & KoParagLayout : : Margins ) {
if ( marginIndex = = - 1 )
setMargins ( layout . margins ) ;
else
setMargin ( ( TQStyleSheetItem : : Margin ) marginIndex , layout . margins [ marginIndex ] ) ;
}
if ( flags & KoParagLayout : : LineSpacing )
{
setLineSpacingType ( layout . lineSpacingType ) ;
setLineSpacing ( layout . lineSpacingValue ( ) ) ;
}
if ( flags & KoParagLayout : : Borders )
{
setLeftBorder ( layout . leftBorder ) ;
setRightBorder ( layout . rightBorder ) ;
setTopBorder ( layout . topBorder ) ;
setBottomBorder ( layout . bottomBorder ) ;
setJoinBorder ( layout . joinBorder ) ;
}
if ( flags & KoParagLayout : : BackgroundColor )
{
setBackgroundColor ( layout . backgroundColor ) ;
}
if ( flags & KoParagLayout : : BulletNumber )
setCounter ( layout . counter ) ;
if ( flags & KoParagLayout : : Tabulator )
setTabList ( layout . tabList ( ) ) ;
if ( flags = = KoParagLayout : : All )
{
setDirection ( static_cast < TQChar : : Direction > ( layout . direction ) ) ;
// Don't call applyStyle from here, it would overwrite any paragraph-specific settings
setStyle ( layout . style ) ;
}
}
void KoTextParag : : setCustomItem ( int index , KoTextCustomItem * custom , KoTextFormat * currentFormat )
{
//kdDebug(32500) << "KoTextParag::setCustomItem " << index << " " << (void*)custom
// << " currentFormat=" << (void*)currentFormat << endl;
if ( currentFormat )
setFormat ( index , 1 , currentFormat ) ;
at ( index ) - > setCustomItem ( custom ) ;
//addCustomItem();
document ( ) - > registerCustomItem ( custom , this ) ;
custom - > recalc ( ) ; // calc value (e.g. for variables) and set initial size
invalidate ( 0 ) ;
setChanged ( true ) ;
}
void KoTextParag : : removeCustomItem ( int index )
{
Q_ASSERT ( at ( index ) - > isCustom ( ) ) ;
KoTextCustomItem * item = at ( index ) - > customItem ( ) ;
at ( index ) - > loseCustomItem ( ) ;
//KoTextParag::removeCustomItem();
document ( ) - > unregisterCustomItem ( item , this ) ;
}
int KoTextParag : : findCustomItem ( const KoTextCustomItem * custom ) const
{
int len = str - > length ( ) ;
for ( int i = 0 ; i < len ; + + i )
{
KoTextStringChar & ch = str - > at ( i ) ;
if ( ch . isCustom ( ) & & ch . customItem ( ) = = custom )
return i ;
}
kdWarning ( ) < < " KoTextParag::findCustomItem custom item " < < ( void * ) custom
< < " not found in paragraph " < < paragId ( ) < < endl ;
return 0 ;
}
# ifndef NDEBUG
void KoTextParag : : printRTDebug ( int info )
{
TQString specialFlags ;
if ( str - > needsSpellCheck ( ) )
specialFlags + = " needsSpellCheck=true " ;
if ( wasMovedDown ( ) )
specialFlags + = " wasMovedDown=true " ;
if ( partOfTableOfContents ( ) )
specialFlags + = " part-of-TOC=true " ;
kdDebug ( 32500 ) < < " Paragraph " < < this < < " ( " < < paragId ( ) < < " ) [changed= "
< < hasChanged ( ) < < " , valid= " < < isValid ( )
< < specialFlags
< < " ] ------------------ " < < endl ;
if ( prev ( ) & & prev ( ) - > paragId ( ) + 1 ! = paragId ( ) )
kdWarning ( ) < < " Previous paragraph " < < prev ( ) < < " has ID " < < prev ( ) - > paragId ( ) < < endl ;
if ( next ( ) & & next ( ) - > paragId ( ) ! = paragId ( ) + 1 )
kdWarning ( ) < < " Next paragraph " < < next ( ) < < " has ID " < < next ( ) - > paragId ( ) < < endl ;
//if ( !next() )
// kdDebug(32500) << " next is 0L" << endl;
kdDebug ( 32500 ) < < " Style: " < < style ( ) < < " " < < ( style ( ) ? style ( ) - > name ( ) . local8Bit ( ) . data ( ) : " NO STYLE " ) < < endl ;
kdDebug ( 32500 ) < < " Text: ' " < < str - > toString ( ) < < " ' " < < endl ;
if ( info = = 0 ) // paragraph info
{
if ( m_layout . counter )
{
m_layout . counter - > printRTDebug ( this ) ;
}
static const char * const s_align [ ] = { " Auto " , " Left " , " Right " , " ERROR " , " HCenter " , " ERR " , " ERR " , " ERR " , " Justify " , } ;
static const char * const s_linespacing [ ] = { " Single " , " 1.5 " , " 2 " , " custom " , " atLeast " , " Multiple " , " Fixed " } ;
static const char * const s_dir [ ] = { " DirL " , " DirR " , " DirEN " , " DirES " , " DirET " , " DirAN " , " DirCS " , " DirB " , " DirS " , " DirWS " , " DirON " , " DirLRE " , " DirLRO " , " DirAL " , " DirRLE " , " DirRLO " , " DirPDF " , " DirNSM " , " DirBN " } ;
kdDebug ( 32500 ) < < " align: " < < s_align [ alignment ( ) ] < < " resolveAlignment: " < < s_align [ resolveAlignment ( ) ]
< < " isRTL: " < < str - > isRightToLeft ( )
< < " dir: " < < s_dir [ direction ( ) ] < < endl ;
TQRect pixr = pixelRect ( textDocument ( ) - > paintingZoomHandler ( ) ) ;
kdDebug ( 32500 ) < < " rect() : " < < DEBUGRECT ( rect ( ) )
< < " pixelRect() : " < < DEBUGRECT ( pixr ) < < endl ;
kdDebug ( 32500 ) < < " topMargin()= " < < topMargin ( )
< < " breakableTopMargin()= " < < breakableTopMargin ( )
< < " bottomMargin()= " < < bottomMargin ( )
< < " leftMargin()= " < < leftMargin ( ) < < " firstLineMargin()= " < < firstLineMargin ( )
< < " rightMargin()= " < < rightMargin ( ) < < endl ;
if ( kwLineSpacingType ( ) ! = KoParagLayout : : LS_SINGLE )
kdDebug ( 32500 ) < < " linespacing type= " < < s_linespacing [ - kwLineSpacingType ( ) ]
< < " value= " < < kwLineSpacing ( ) < < endl ;
const int pageBreaking = m_layout . pageBreaking ;
TQStringList pageBreakingFlags ;
if ( pageBreaking & KoParagLayout : : KeepLinesTogether )
pageBreakingFlags . append ( " KeepLinesTogether " ) ;
if ( pageBreaking & KoParagLayout : : HardFrameBreakBefore )
pageBreakingFlags . append ( " HardFrameBreakBefore " ) ;
if ( pageBreaking & KoParagLayout : : HardFrameBreakAfter )
pageBreakingFlags . append ( " HardFrameBreakAfter " ) ;
if ( pageBreaking & KoParagLayout : : KeepWithPrevious )
pageBreakingFlags . append ( " KeepWithPrevious " ) ;
if ( pageBreaking & KoParagLayout : : KeepWithNext )
pageBreakingFlags . append ( " KeepWithNext " ) ;
if ( ! pageBreakingFlags . isEmpty ( ) )
kdDebug ( 32500 ) < < " page Breaking: " < < pageBreakingFlags . join ( " , " ) < < endl ;
static const char * const tabtype [ ] = { " T_LEFT " , " T_CENTER " , " T_RIGHT " , " T_DEC_PNT " , " error!!! " } ;
KoTabulatorList tabList = m_layout . tabList ( ) ;
if ( tabList . isEmpty ( ) ) {
if ( str - > toString ( ) . find ( ' \t ' ) ! = - 1 )
kdDebug ( 32500 ) < < " Tab width: " < < textDocument ( ) - > tabStopWidth ( ) < < endl ;
} else {
KoTabulatorList : : Iterator it = tabList . begin ( ) ;
for ( ; it ! = tabList . end ( ) ; it + + )
kdDebug ( 32500 ) < < " Tab type: " < < tabtype [ ( * it ) . type ] < < " at: " < < ( * it ) . ptPos < < endl ;
}
} else if ( info = = 1 ) // formatting info
{
kdDebug ( 32500 ) < < " Paragraph format= " < < paragFormat ( ) < < " " < < paragFormat ( ) - > key ( )
< < " fontsize: " < < dynamic_cast < KoTextFormat * > ( paragFormat ( ) ) - > pointSize ( ) < < endl ;
for ( int line = 0 ; line < lines ( ) ; + + line ) {
int y , h , baseLine ;
lineInfo ( line , y , h , baseLine ) ;
int startOfLine ;
lineStartOfLine ( line , & startOfLine ) ;
kdDebug ( 32500 ) < < " Line " < < line < < " y= " < < y < < " height= " < < h < < " baseLine= " < < baseLine < < " startOfLine(index)= " < < startOfLine < < endl ;
}
kdDebug ( 32500 ) < < endl ;
KoTextString * s = string ( ) ;
int lastX = 0 ; // pixels
int lastW = 0 ; // pixels
for ( int i = 0 ; i < s - > length ( ) ; + + i )
{
KoTextStringChar & ch = s - > at ( i ) ;
int pixelx = textDocument ( ) - > formattingZoomHandler ( ) - > layoutUnitToPixelX ( ch . x )
+ ch . pixelxadj ;
if ( ch . lineStart )
kdDebug ( 32500 ) < < " LINESTART " < < endl ;
TQString attrs = " " ;
if ( ch . whiteSpace )
attrs + = " whitespace " ;
if ( ! ch . charStop )
attrs + = " notCharStop " ;
if ( ch . wordStop )
attrs + = " wordStop " ;
attrs . truncate ( attrs . length ( ) - 1 ) ;
kdDebug ( 32500 ) < < i < < " : ' " < < TQString ( ch . c ) . rightJustify ( 2 )
< < " ' ( " < < TQString : : number ( ch . c . unicode ( ) ) . rightJustify ( 3 ) < < " ) "
< < " x(LU)= " < < ch . x
< < " w(LU)= " < < ch . width //s->width(i)
< < " x(PIX)= " < < pixelx
< < " (xadj= " < < + ch . pixelxadj < < " ) "
< < " w(PIX)= " < < ch . pixelwidth
< < " height= " < < ch . height ( )
< < attrs
// << " format=" << ch.format()
// << " \"" << ch.format()->key() << "\" "
//<< " fontsize:" << dynamic_cast<KoTextFormat *>(ch.format())->pointSize()
< < endl ;
// Check that the format is in the collection (i.e. its defaultFormat or in the dict)
if ( ch . format ( ) ! = textDocument ( ) - > formatCollection ( ) - > defaultFormat ( ) )
Q_ASSERT ( textDocument ( ) - > formatCollection ( ) - > dict ( ) [ ch . format ( ) - > key ( ) ] ) ;
if ( ! str - > isBidi ( ) & & ! ch . lineStart )
Q_ASSERT ( lastX + lastW = = pixelx ) ; // looks like some rounding problem with justified spaces
lastX = pixelx ;
lastW = ch . pixelwidth ;
if ( ch . isCustom ( ) )
{
KoTextCustomItem * item = ch . customItem ( ) ;
kdDebug ( 32500 ) < < " - custom item " < < item
< < " ownline= " < < item - > ownLine ( )
< < " size= " < < item - > width < < " x " < < item - > height
< < " ascent= " < < item - > ascent ( )
< < endl ;
}
}
}
}
# endif
void KoTextParag : : drawFontEffects ( TQPainter * p , KoTextFormat * format , KoTextZoomHandler * zh , TQFont font , const TQColor & color , int startX , int baseLine , int bw , int lastY , int /*h*/ , TQChar firstChar )
{
// This is about drawing underlines and strikeouts
// So abort immediately if there's none to draw.
if ( ! format - > isStrikedOrUnderlined ( ) )
return ;
//kdDebug(32500) << "drawFontEffects wordByWord=" << format->wordByWord() <<
// " firstChar='" << TQString(firstChar) << "'" << endl;
// paintLines ensures that we're called word by word if wordByWord is true.
if ( format - > wordByWord ( ) & & firstChar . isSpace ( ) )
return ;
double dimd ;
int y ;
int offset = 0 ;
if ( format - > vAlign ( ) = = KoTextFormat : : AlignSubScript )
offset = p - > fontMetrics ( ) . height ( ) / 6 ;
else if ( format - > vAlign ( ) = = KoTextFormat : : AlignSuperScript )
offset = - p - > fontMetrics ( ) . height ( ) / 2 ;
dimd = KoBorder : : zoomWidthY ( format - > underLineWidth ( ) , zh , 1 ) ;
if ( ( format - > vAlign ( ) = = KoTextFormat : : AlignSuperScript ) | |
( format - > vAlign ( ) = = KoTextFormat : : AlignSubScript ) | | ( format - > vAlign ( ) = = KoTextFormat : : AlignCustom ) )
dimd * = format - > relativeTextSize ( ) ;
y = lastY + baseLine + offset - ( ( format - > vAlign ( ) = = KoTextFormat : : AlignCustom ) ? format - > offsetFromBaseLine ( ) : 0 ) ;
if ( format - > doubleUnderline ( ) )
{
TQColor col = format - > textUnderlineColor ( ) . isValid ( ) ? format - > textUnderlineColor ( ) : color ;
int dim = static_cast < int > ( 0.75 * dimd ) ;
dim = dim ? dim : 1 ; //width of line should be at least 1
p - > save ( ) ;
switch ( format - > underlineStyle ( ) )
{
case KoTextFormat : : U_SOLID :
p - > setPen ( TQPen ( col , dim , TQt : : SolidLine ) ) ;
break ;
case KoTextFormat : : U_DASH :
p - > setPen ( TQPen ( col , dim , TQt : : DashLine ) ) ;
break ;
case KoTextFormat : : U_DOT :
p - > setPen ( TQPen ( col , dim , TQt : : DotLine ) ) ;
break ;
case KoTextFormat : : U_DASH_DOT :
p - > setPen ( TQPen ( col , dim , TQt : : DashDotLine ) ) ;
break ;
case KoTextFormat : : U_DASH_DOT_DOT :
p - > setPen ( TQPen ( col , dim , TQt : : DashDotDotLine ) ) ;
break ;
default :
p - > setPen ( TQPen ( color , dim , TQt : : SolidLine ) ) ;
}
y + = static_cast < int > ( 1.125 * dimd ) ; // slightly under the baseline if possible
p - > drawLine ( startX , y , startX + bw , y ) ;
y + = static_cast < int > ( 1.5 * dimd ) ;
p - > drawLine ( startX , y , startX + bw , y ) ;
p - > restore ( ) ;
if ( font . underline ( ) ) { // can this happen?
font . setUnderline ( FALSE ) ;
p - > setFont ( font ) ;
}
}
else if ( format - > underline ( ) | |
format - > underlineType ( ) = = KoTextFormat : : U_SIMPLE_BOLD )
{
TQColor col = format - > textUnderlineColor ( ) . isValid ( ) ? format - > textUnderlineColor ( ) : color ;
p - > save ( ) ;
int dim = ( format - > underlineType ( ) = = KoTextFormat : : U_SIMPLE_BOLD ) ? static_cast < int > ( 2 * dimd ) : static_cast < int > ( dimd ) ;
dim = dim ? dim : 1 ; //width of line should be at least 1
y + = static_cast < int > ( 1.875 * dimd ) ;
switch ( format - > underlineStyle ( ) )
{
case KoTextFormat : : U_SOLID :
p - > setPen ( TQPen ( col , dim , TQt : : SolidLine ) ) ;
break ;
case KoTextFormat : : U_DASH :
p - > setPen ( TQPen ( col , dim , TQt : : DashLine ) ) ;
break ;
case KoTextFormat : : U_DOT :
p - > setPen ( TQPen ( col , dim , TQt : : DotLine ) ) ;
break ;
case KoTextFormat : : U_DASH_DOT :
p - > setPen ( TQPen ( col , dim , TQt : : DashDotLine ) ) ;
break ;
case KoTextFormat : : U_DASH_DOT_DOT :
p - > setPen ( TQPen ( col , dim , TQt : : DashDotDotLine ) ) ;
break ;
default :
p - > setPen ( TQPen ( col , dim , TQt : : SolidLine ) ) ;
}
p - > drawLine ( startX , y , startX + bw , y ) ;
p - > restore ( ) ;
font . setUnderline ( FALSE ) ;
p - > setFont ( font ) ;
}
else if ( format - > waveUnderline ( ) )
{
int dim = static_cast < int > ( dimd ) ;
dim = dim ? dim : 1 ; //width of line should be at least 1
y + = dim ;
TQColor col = format - > textUnderlineColor ( ) . isValid ( ) ? format - > textUnderlineColor ( ) : color ;
p - > save ( ) ;
int offset = 2 * dim ;
TQPen pen ( col , dim , TQt : : SolidLine ) ;
pen . setCapStyle ( TQt : : RoundCap ) ;
p - > setPen ( pen ) ;
Q_ASSERT ( offset ) ;
double anc = acos ( 1.0 - 2 * ( static_cast < double > ( offset - ( startX ) % offset ) / static_cast < double > ( offset ) ) ) / 3.1415 * 180 ;
int pos = 1 ;
//set starting position
if ( 2 * ( ( startX / offset ) / 2 ) = = startX / offset )
pos * = - 1 ;
//draw first part of wave
p - > drawArc ( ( startX / offset ) * offset , y , offset , offset , 0 , - tqRound ( pos * anc * 16 ) ) ;
//now the main part
int zigzag_x = ( startX / offset + 1 ) * offset ;
for ( ; zigzag_x + offset < = bw + startX ; zigzag_x + = offset )
{
p - > drawArc ( zigzag_x , y , offset , offset , 0 , pos * 180 * 16 ) ;
pos * = - 1 ;
}
//and here we finish
anc = acos ( 1.0 - 2 * ( static_cast < double > ( ( startX + bw ) % offset ) / static_cast < double > ( offset ) ) ) / 3.1415 * 180 ;
p - > drawArc ( zigzag_x , y , offset , offset , 180 * 16 , - tqRound ( pos * anc * 16 ) ) ;
p - > restore ( ) ;
font . setUnderline ( FALSE ) ;
p - > setFont ( font ) ;
}
dimd = KoBorder : : zoomWidthY ( static_cast < double > ( format - > pointSize ( ) ) / 18.0 , zh , 1 ) ;
if ( ( format - > vAlign ( ) = = KoTextFormat : : AlignSuperScript ) | |
( format - > vAlign ( ) = = KoTextFormat : : AlignSubScript ) | | ( format - > vAlign ( ) = = KoTextFormat : : AlignCustom ) )
dimd * = format - > relativeTextSize ( ) ;
y = lastY + baseLine + offset - ( ( format - > vAlign ( ) = = KoTextFormat : : AlignCustom ) ? format - > offsetFromBaseLine ( ) : 0 ) ;
if ( format - > strikeOutType ( ) = = KoTextFormat : : S_SIMPLE
| | format - > strikeOutType ( ) = = KoTextFormat : : S_SIMPLE_BOLD )
{
unsigned int dim = ( format - > strikeOutType ( ) = = KoTextFormat : : S_SIMPLE_BOLD ) ? static_cast < int > ( 2 * dimd ) : static_cast < int > ( dimd ) ;
p - > save ( ) ;
switch ( format - > strikeOutStyle ( ) )
{
case KoTextFormat : : S_SOLID :
p - > setPen ( TQPen ( color , dim , TQt : : SolidLine ) ) ;
break ;
case KoTextFormat : : S_DASH :
p - > setPen ( TQPen ( color , dim , TQt : : DashLine ) ) ;
break ;
case KoTextFormat : : S_DOT :
p - > setPen ( TQPen ( color , dim , TQt : : DotLine ) ) ;
break ;
case KoTextFormat : : S_DASH_DOT :
p - > setPen ( TQPen ( color , dim , TQt : : DashDotLine ) ) ;
break ;
case KoTextFormat : : S_DASH_DOT_DOT :
p - > setPen ( TQPen ( color , dim , TQt : : DashDotDotLine ) ) ;
break ;
default :
p - > setPen ( TQPen ( color , dim , TQt : : SolidLine ) ) ;
}
y - = static_cast < int > ( 5 * dimd ) ;
p - > drawLine ( startX , y , startX + bw , y ) ;
p - > restore ( ) ;
font . setStrikeOut ( FALSE ) ;
p - > setFont ( font ) ;
}
else if ( format - > strikeOutType ( ) = = KoTextFormat : : S_DOUBLE )
{
unsigned int dim = static_cast < int > ( dimd ) ;
p - > save ( ) ;
switch ( format - > strikeOutStyle ( ) )
{
case KoTextFormat : : S_SOLID :
p - > setPen ( TQPen ( color , dim , TQt : : SolidLine ) ) ;
break ;
case KoTextFormat : : S_DASH :
p - > setPen ( TQPen ( color , dim , TQt : : DashLine ) ) ;
break ;
case KoTextFormat : : S_DOT :
p - > setPen ( TQPen ( color , dim , TQt : : DotLine ) ) ;
break ;
case KoTextFormat : : S_DASH_DOT :
p - > setPen ( TQPen ( color , dim , TQt : : DashDotLine ) ) ;
break ;
case KoTextFormat : : S_DASH_DOT_DOT :
p - > setPen ( TQPen ( color , dim , TQt : : DashDotDotLine ) ) ;
break ;
default :
p - > setPen ( TQPen ( color , dim , TQt : : SolidLine ) ) ;
}
y - = static_cast < int > ( 4 * dimd ) ;
p - > drawLine ( startX , y , startX + bw , y ) ;
y - = static_cast < int > ( 2 * dimd ) ;
p - > drawLine ( startX , y , startX + bw , y ) ;
p - > restore ( ) ;
font . setStrikeOut ( FALSE ) ;
p - > setFont ( font ) ;
}
}
// ### is this method correct for RTL text?
TQString KoTextParag : : toString ( int from , int length ) const
{
TQString str ;
if ( from = = 0 & & m_layout . counter & & m_layout . counter - > numbering ( ) ! = KoParagCounter : : NUM_FOOTNOTE )
str + = m_layout . counter - > text ( this ) + ' ' ;
if ( length = = - 1 )
length = this - > length ( ) - 1 /*trailing space*/ - from ;
for ( int i = from ; i < ( length + from ) ; + + i )
{
KoTextStringChar * ch = at ( i ) ;
if ( ch - > isCustom ( ) )
{
KoVariable * var = dynamic_cast < KoVariable * > ( ch - > customItem ( ) ) ;
if ( var )
str + = var - > text ( true ) ;
else //frame inline
str + = ' ' ;
}
else
str + = ch - > c ;
}
return str ;
}
// we cannot use TQString::simplifyWhiteSpace() because it removes
// leading and trailing whitespace, but such whitespace is significant
// in ODF -- so we use this function to compress sequences of space characters
// into single spaces
static TQString normalizeWhitespace ( const TQString & in , bool leadingSpace )
{
TQString text = in ;
int r , w = 0 ;
int len = text . length ( ) ;
for ( r = 0 ; r < len ; + + r )
{
TQCharRef ch = text [ r ] ;
// check for space, tab, line feed, carriage return
if ( ch = = ' ' | | ch = = ' \t ' | | ch = = ' \r ' | | ch = = ' \n ' )
{
// if we were lead by whitespace in some parent or previous sibling element,
// we completely collapse this space
if ( r ! = 0 | | ! leadingSpace )
text [ w + + ] = TQChar ( ' ' ) ;
// find the end of the whitespace run
while ( r < len & & text [ r ] . isSpace ( ) )
+ + r ;
// and then record the next non-whitespace character
if ( r < len )
text [ w + + ] = text [ r ] ;
}
else
{
text [ w + + ] = ch ;
}
}
// and now trim off the unused part of the string
text . truncate ( w ) ;
return text ;
}
void KoTextParag : : loadOasisSpan ( const TQDomElement & parent , KoOasisContext & context , uint & pos , bool stripLeadingSpace )
{
bool dummy ;
return loadOasisSpan ( parent , context , pos , stripLeadingSpace , & dummy ) ;
}
void KoTextParag : : loadOasisSpan ( const TQDomElement & parent , KoOasisContext & context , uint & pos , bool stripLeadingSpace , bool * hasTrailingSpace )
{
// Parse every child node of the parent
// Can't use forEachElement here since we also care about text nodes
TQDomNode node ;
for ( node = parent . firstChild ( ) ; ! node . isNull ( ) ; node = node . nextSibling ( ) )
{
TQDomElement ts = node . toElement ( ) ;
TQString textData ;
const TQString localName ( ts . localName ( ) ) ;
const bool isTextNS = ts . namespaceURI ( ) = = KoXmlNS : : text ;
KoTextCustomItem * customItem = 0 ;
// allow loadSpanTag to modify the stylestack
context . styleStack ( ) . save ( ) ;
// Try to keep the order of the tag names by probability of happening
if ( node . isText ( ) )
{
textData = normalizeWhitespace ( node . toText ( ) . data ( ) , stripLeadingSpace ) ;
* hasTrailingSpace = stripLeadingSpace = textData [ textData . length ( ) - 1 ] . isSpace ( ) ;
}
else if ( isTextNS & & localName = = " span " ) // text:span
{
context . styleStack ( ) . save ( ) ;
context . fillStyleStack ( ts , KoXmlNS : : text , " style-name " , " text " ) ;
// the ODF spec states that whitespace is compressed through tags: e.g.
// "Foo <text:span> Bar </text:span> Baz"
// should only have one space between each of "Foo", "Bar", and "Baz"
// so we need to keep track of whether there was any trailing whitespace
// in sub-spans so that we can propogate the whitespace compression state
// back up to the parent element
loadOasisSpan ( ts , context , pos , stripLeadingSpace , hasTrailingSpace ) ; // recurse
stripLeadingSpace = * hasTrailingSpace ;
context . styleStack ( ) . restore ( ) ;
}
else if ( isTextNS & & localName = = " s " ) // text:s
{
int howmany = 1 ;
if ( ts . hasAttributeNS ( KoXmlNS : : text , " c " ) )
howmany = ts . attributeNS ( KoXmlNS : : text , " c " , TQString ( ) ) . toInt ( ) ;
textData . fill ( 32 , howmany ) ;
}
else if ( isTextNS & & localName = = " tab " ) // text:tab (it's tab-stop in OO-1.1 but tab in oasis)
{
textData = ' \t ' ;
}
else if ( isTextNS & & localName = = " line-break " ) // text:line-break
{
textData = ' \n ' ;
}
else if ( isTextNS & & localName = = " number " ) // text:number
{
// This is the number in front of a numbered paragraph,
// written out to help export filters. We can ignore it.
}
else if ( node . isProcessingInstruction ( ) )
{
TQDomProcessingInstruction pi = node . toProcessingInstruction ( ) ;
if ( pi . target ( ) = = " opendocument " & & pi . data ( ) . startsWith ( " cursor-position " ) )
{
context . setCursorPosition ( this , pos ) ;
}
}
else
{
bool handled = false ;
// Check if it's a variable
KoVariable * var = context . variableCollection ( ) . loadOasisField ( textDocument ( ) , ts , context ) ;
if ( var )
{
textData = " # " ; // field placeholder
customItem = var ;
handled = true ;
}
if ( ! handled )
{
handled = textDocument ( ) - > loadSpanTag ( ts , context ,
this , pos ,
textData , customItem ) ;
if ( ! handled )
{
kdWarning ( 32500 ) < < " Ignoring tag " < < ts . tagName ( ) < < endl ;
context . styleStack ( ) . restore ( ) ;
continue ;
}
}
}
const uint length = textData . length ( ) ;
if ( length )
{
insert ( pos , textData ) ;
if ( customItem )
setCustomItem ( pos , customItem , 0 ) ;
KoTextFormat f ;
f . load ( context ) ;
//kdDebug(32500) << "loadOasisSpan: applying formatting from " << pos << " to " << pos+length << "\n format=" << f.key() << endl;
setFormat ( pos , length , document ( ) - > formatCollection ( ) - > format ( & f ) , TRUE ) ;
pos + = length ;
}
context . styleStack ( ) . restore ( ) ;
}
}
KoParagLayout KoTextParag : : loadParagLayout ( KoOasisContext & context , KoStyleCollection * styleCollection , bool findStyle )
{
KoParagLayout layout ;
// Only when loading paragraphs, not when loading styles
if ( findStyle )
{
KoParagStyle * style ;
// Name of the style. If there is no style, then we do not supply
// any default!
TQString styleName = context . styleStack ( ) . userStyleName ( " paragraph " ) ;
if ( ! styleName . isEmpty ( ) )
{
style = styleCollection - > findStyle ( styleName ) ;
// When pasting the style names are random, the display names matter
if ( ! style )
style = styleCollection - > findStyleByDisplayName ( context . styleStack ( ) . userStyleDisplayName ( " paragraph " ) ) ;
if ( ! style )
{
kdError ( 32500 ) < < " Cannot find style \" " < < styleName < < " \" - using Standard " < < endl ;
style = styleCollection - > findStyle ( " Standard " ) ;
}
//else kdDebug() << "KoParagLayout::KoParagLayout setting style to " << style << " " << style->name() << endl;
}
else
{
kdError ( 32500 ) < < " No style name !? - using Standard " < < endl ;
style = styleCollection - > findStyle ( " Standard " ) ;
}
Q_ASSERT ( style ) ;
layout . style = style ;
}
KoParagLayout : : loadOasisParagLayout ( layout , context ) ;
return layout ;
}
void KoTextParag : : loadOasis ( const TQDomElement & parent , KoOasisContext & context , KoStyleCollection * styleCollection , uint & pos )
{
// First load layout from style
KoParagLayout paragLayout = loadParagLayout ( context , styleCollection , true ) ;
setParagLayout ( paragLayout ) ;
// Load paragraph format
KoTextFormat defaultFormat ;
defaultFormat . load ( context ) ;
setFormat ( document ( ) - > formatCollection ( ) - > format ( & defaultFormat ) ) ;
// Load text
// OO.o compatibility: ignore leading whitespace in <p> and <h> elements
loadOasisSpan ( parent , context , pos , true ) ;
// Apply default format to trailing space
const int len = str - > length ( ) ;
Q_ASSERT ( len > = 1 ) ;
setFormat ( len - 1 , 1 , paragFormat ( ) , TRUE ) ;
setChanged ( true ) ;
invalidate ( 0 ) ;
}
void KoTextParag : : saveOasis ( KoXmlWriter & writer , KoSavingContext & context ,
int from /* default 0 */ , int to /* usually length()-2 */ ,
bool /*saveAnchorsFramesets*/ /* default false */ ) const
{
KoGenStyles & mainStyles = context . mainStyles ( ) ;
// Write paraglayout to styles (with parent == the parag's style)
TQString parentStyleName ;
if ( m_layout . style )
parentStyleName = m_layout . style - > name ( ) ;
KoGenStyle autoStyle ( KoGenStyle : : STYLE_AUTO , " paragraph " , parentStyleName ) ;
paragFormat ( ) - > save ( autoStyle , context ) ;
m_layout . saveOasis ( autoStyle , context , false ) ;
// First paragraph is special, it includes page-layout info (for word-processing at least)
if ( ! prev ( ) ) {
if ( context . variableSettings ( ) )
autoStyle . addProperty ( " style:page-number " , context . variableSettings ( ) - > startingPageNumber ( ) ) ;
// Well we support only one page layout, so the first parag always points to "Standard".
autoStyle . addAttribute ( " style:master-page-name " , " Standard " ) ;
}
TQString autoParagStyleName = mainStyles . lookup ( autoStyle , " P " , KoGenStyles : : ForceNumbering ) ;
KoParagCounter * paragCounter = m_layout . counter ;
// outline (text:h) assumes paragCounter != 0 (because depth is mandatory)
bool outline = m_layout . style & & m_layout . style - > isOutline ( ) & & paragCounter ;
bool normalList = paragCounter & & paragCounter - > style ( ) ! = KoParagCounter : : STYLE_NONE & & ! outline ;
if ( normalList ) // non-heading list
{
writer . startElement ( " text:numbered-paragraph " ) ;
writer . addAttribute ( " text:level " , ( int ) paragCounter - > depth ( ) + 1 ) ;
if ( paragCounter - > restartCounter ( ) )
writer . addAttribute ( " text:start-value " , paragCounter - > startNumber ( ) ) ;
KoGenStyle listStyle ( KoGenStyle : : STYLE_AUTO_LIST /*, no family*/ ) ;
paragCounter - > saveOasis ( listStyle ) ;
TQString autoListStyleName = mainStyles . lookup ( listStyle , " L " , KoGenStyles : : ForceNumbering ) ;
writer . addAttribute ( " text:style-name " , autoListStyleName ) ;
TQString textNumber = m_layout . counter - > text ( this ) ;
if ( ! textNumber . isEmpty ( ) )
{
// This is to help export filters
writer . startElement ( " text:number " ) ;
writer . addTextNode ( textNumber ) ;
writer . endElement ( ) ;
}
}
else if ( outline ) // heading
{
writer . startElement ( " text:h " , false /*no indent inside this tag*/ ) ;
writer . addAttribute ( " text:style-name " , autoParagStyleName ) ;
writer . addAttribute ( " text:outline-level " , ( int ) paragCounter - > depth ( ) + 1 ) ;
if ( paragCounter - > numbering ( ) = = KoParagCounter : : NUM_NONE )
writer . addAttribute ( " text:is-list-header " , " true " ) ;
TQString textNumber = paragCounter - > text ( this ) ;
if ( ! textNumber . isEmpty ( ) )
{
// This is to help export filters
writer . startElement ( " text:number " ) ;
writer . addTextNode ( textNumber ) ;
writer . endElement ( ) ;
}
}
if ( ! outline ) // normal (non-numbered) paragraph, or normalList
{
writer . startElement ( " text:p " , false /*no indent inside this tag*/ ) ;
writer . addAttribute ( " text:style-name " , autoParagStyleName ) ;
}
TQString text = str - > toString ( ) ;
Q_ASSERT ( text . right ( 1 ) [ 0 ] = = ' ' ) ;
const int cursorIndex = context . cursorTextParagraph ( ) = = this ? context . cursorTextIndex ( ) : - 1 ;
//kdDebug() << k_funcinfo << "'" << text << "' from=" << from << " to=" << to << " cursorIndex=" << cursorIndex << endl;
// A helper method would need no less than 7 params...
# define WRITESPAN( next ) { \
if ( curFormat = = paragFormat ( ) ) { \
writer . addTextSpan ( text . mid ( startPos , next - startPos ) , m_tabCache ) ; \
} else { \
KoGenStyle gs ( KoGenStyle : : STYLE_AUTO , " text " ) ; \
curFormat - > save ( gs , context , paragFormat ( ) ) ; \
writer . startElement ( " text:span " ) ; \
if ( ! gs . isEmpty ( ) ) { \
const TQString autoStyleName = mainStyles . lookup ( gs , " T " ) ; \
writer . addAttribute ( " text:style-name " , autoStyleName ) ; \
} \
writer . addTextSpan ( text . mid ( startPos , next - startPos ) , m_tabCache ) ; \
writer . endElement ( ) ; \
} \
}
# define ISSTARTBOOKMARK( i ) bkStartIter != bookmarkStarts.end() && (*bkStartIter).pos == i
# define ISENDBOOKMARK( i ) bkEndIter != bookmarkEnds.end() && (*bkEndIter).pos == i
# define CHECKPOS( i ) \
if ( cursorIndex = = i ) { \
writer . addProcessingInstruction ( " opendocument cursor-position " ) ; \
} \
if ( ISSTARTBOOKMARK ( i ) ) { \
if ( ( * bkStartIter ) . startEqualsEnd ) \
writer . startElement ( " text:bookmark " ) ; \
else \
writer . startElement ( " text:bookmark-start " ) ; \
writer . addAttribute ( " text:name " , ( * bkStartIter ) . name ) ; \
writer . endElement ( ) ; \
+ + bkStartIter ; \
} \
if ( ISENDBOOKMARK ( i ) ) { \
writer . startElement ( " text:bookmark-end " ) ; \
writer . addAttribute ( " text:name " , ( * bkEndIter ) . name ) ; \
writer . endElement ( ) ; \
+ + bkEndIter ; \
}
// Make (shallow) copy of bookmark list, since saving an inline frame might overwrite it
// from the context while we're saving this paragraph.
typedef KoSavingContext : : BookmarkPositions BookmarkPositions ;
BookmarkPositions bookmarkStarts = context . bookmarkStarts ( ) ;
BookmarkPositions : : const_iterator bkStartIter = bookmarkStarts . begin ( ) ;
while ( bkStartIter ! = bookmarkStarts . end ( ) & & ( * bkStartIter ) . pos < from )
+ + bkStartIter ;
//int nextBookmarkStart = bkStartIter == bookmarkStarts.end() ? -1 : (*bkStartIter).pos;
BookmarkPositions bookmarkEnds = context . bookmarkEnds ( ) ;
BookmarkPositions : : const_iterator bkEndIter = bookmarkEnds . begin ( ) ;
while ( bkEndIter ! = bookmarkEnds . end ( ) & & ( * bkEndIter ) . pos < from )
+ + bkEndIter ;
KoTextFormat * curFormat = 0 ;
KoTextFormat * lastFormatRaw = 0 ; // this is for speeding up "removing misspelled" from each char
KoTextFormat * lastFormatFixed = 0 ; // raw = as stored in the chars; fixed = after removing misspelled
int startPos = from ;
for ( int i = from ; i < = to ; + + i ) {
KoTextStringChar & ch = str - > at ( i ) ;
KoTextFormat * newFormat = static_cast < KoTextFormat * > ( ch . format ( ) ) ;
if ( newFormat - > isMisspelled ( ) ) {
if ( newFormat = = lastFormatRaw )
newFormat = lastFormatFixed ; // the fast way
else
{
lastFormatRaw = newFormat ;
// Remove isMisspelled from format, to avoid useless derived styles
// (which would be indentical to their parent style)
KoTextFormat tmpFormat ( * newFormat ) ;
tmpFormat . setMisspelled ( false ) ;
newFormat = formatCollection ( ) - > format ( & tmpFormat ) ;
lastFormatFixed = newFormat ;
}
}
if ( ! curFormat )
curFormat = newFormat ;
if ( newFormat ! = curFormat // Format changed, save previous one.
| | ch . isCustom ( ) | | cursorIndex = = i | | ISSTARTBOOKMARK ( i ) | | ISENDBOOKMARK ( i ) )
{
WRITESPAN ( i ) // write text up to i-1
startPos = i ;
curFormat = newFormat ;
}
CHECKPOS ( i ) // do cursor position and bookmarks
if ( ch . isCustom ( ) ) {
KoGenStyle gs ( KoGenStyle : : STYLE_AUTO , " text " ) ;
curFormat - > save ( gs , context , paragFormat ( ) ) ;
writer . startElement ( " text:span " ) ;
if ( ! gs . isEmpty ( ) ) {
const TQString autoStyleName = mainStyles . lookup ( gs , " T " ) ;
writer . addAttribute ( " text:style-name " , autoStyleName ) ;
}
KoTextCustomItem * customItem = ch . customItem ( ) ;
customItem - > saveOasis ( writer , context ) ;
writer . endElement ( ) ;
startPos = i + 1 ;
}
}
//kdDebug() << k_funcinfo << "startPos=" << startPos << " to=" << to << " curFormat=" << curFormat << endl;
if ( to > = startPos ) { // Save last format
WRITESPAN ( to + 1 )
}
CHECKPOS ( to + 1 ) // do cursor position and bookmarks
writer . endElement ( ) ; // text:p or text:h
if ( normalList )
writer . endElement ( ) ; // text:numbered-paragraph (englobing a text:p)
}
void KoTextParag : : applyListStyle ( KoOasisContext & context , int restartNumbering , bool orderedList , bool heading , int level )
{
//kdDebug(32500) << k_funcinfo << "applyListStyle to parag " << this << " heading=" << heading << endl;
delete m_layout . counter ;
m_layout . counter = new KoParagCounter ;
m_layout . counter - > loadOasis ( context , restartNumbering , orderedList , heading , level ) ;
// We emulate space-before with a left paragraph indent (#109223)
const TQDomElement listStyleProperties = context . listStyleStack ( ) . currentListStyleProperties ( ) ;
if ( listStyleProperties . hasAttributeNS ( KoXmlNS : : text , " space-before " ) )
{
double spaceBefore = KoUnit : : parseValue ( listStyleProperties . attributeNS ( KoXmlNS : : text , " space-before " , TQString ( ) ) ) ;
m_layout . margins [ TQStyleSheetItem : : MarginLeft ] + = spaceBefore ; // added to left-margin, see 15.12 in spec.
}
// need to call invalidateCounters() ? Not during the initial loading at least.
}
int KoTextParag : : documentWidth ( ) const
{
return doc ? doc - > width ( ) : 0 ; //docRect.width();
}
//int KoTextParag::documentVisibleWidth() const
//{
// return doc ? doc->visibleWidth() : 0; //docRect.width();
//}
int KoTextParag : : documentX ( ) const
{
return doc ? doc - > x ( ) : 0 ; //docRect.x();
}
int KoTextParag : : documentY ( ) const
{
return doc ? doc - > y ( ) : 0 ; //docRect.y();
}
void KoTextParag : : fixParagWidth ( bool viewFormattingChars )
{
// Fixing the parag rect for the formatting chars (only CR here, KWord handles framebreak).
if ( viewFormattingChars & & lineStartList ( ) . count ( ) = = 1 ) // don't use lines() here, parag not formatted yet
{
KoTextFormat * lastFormat = at ( length ( ) - 1 ) - > format ( ) ;
setWidth ( TQMIN ( rect ( ) . width ( ) + lastFormat - > width ( ' x ' ) , doc - > width ( ) ) ) ;
}
// Warning, if adding anything else here, adjust KWTextFrameSet::fixParagWidth
}
// Called by KoTextParag::drawParagString - all params are in pixel coordinates
void KoTextParag : : drawFormattingChars ( TQPainter & painter , int start , int len ,
int lastY_pix , int baseLine_pix , int h_pix , // in pixels
bool /*drawSelections*/ ,
KoTextFormat * /*lastFormat*/ , const TQMemArray < int > & /*selectionStarts*/ ,
const TQMemArray < int > & /*selectionEnds*/ , const TQColorGroup & /*cg*/ ,
bool rightToLeft , int /*line*/ , KoTextZoomHandler * zh ,
int whichFormattingChars )
{
if ( ! whichFormattingChars )
return ;
painter . save ( ) ;
//TQPen pen( cg.color( TQColorGroup::Highlight ) );
TQPen pen ( TDEGlobalSettings : : linkColor ( ) ) ; // #101820
painter . setPen ( pen ) ;
//kdDebug() << "KWTextParag::drawFormattingChars start=" << start << " len=" << len << " length=" << length() << endl;
if ( start + len = = length ( ) & & ( whichFormattingChars & FormattingEndParag ) )
{
// drawing the end of the parag
KoTextStringChar & ch = str - > at ( length ( ) - 1 ) ;
KoTextFormat * format = static_cast < KoTextFormat * > ( ch . format ( ) ) ;
int w = format - > charWidth ( zh , true , & ch , this , ' X ' ) ;
int size = TQMIN ( w , h_pix * 3 / 4 ) ;
// x,y is the bottom right corner of the
//kdDebug() << "startX=" << startX << " bw=" << bw << " w=" << w << endl;
int x ;
if ( rightToLeft )
x = zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ + ch . pixelwidth - 1 ;
else
x = zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ + w ;
int y = lastY_pix + baseLine_pix ;
//kdDebug() << "KWTextParag::drawFormattingChars drawing CR at " << x << "," << y << endl;
painter . drawLine ( ( int ) ( x - size * 0.2 ) , y - size , ( int ) ( x - size * 0.2 ) , y ) ;
painter . drawLine ( ( int ) ( x - size * 0.5 ) , y - size , ( int ) ( x - size * 0.5 ) , y ) ;
painter . drawLine ( x , y , ( int ) ( x - size * 0.7 ) , y ) ;
painter . drawLine ( x , y - size , ( int ) ( x - size * 0.5 ) , y - size ) ;
painter . drawArc ( x - size , y - size , size , ( int ) ( size / 2 ) , - 90 * 16 , - 180 * 16 ) ;
# ifdef DEBUG_FORMATTING
painter . setPen ( TQt : : blue ) ;
painter . drawRect ( zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ - 1 , lastY_pix , ch . pixelwidth , baseLine_pix ) ;
TQPen pen ( cg . color ( TQColorGroup : : Highlight ) ) ;
painter . setPen ( pen ) ;
# endif
}
// Now draw spaces, tabs and newlines
if ( ( whichFormattingChars & FormattingSpace ) | |
( whichFormattingChars & FormattingTabs ) | |
( whichFormattingChars & FormattingBreak ) )
{
int end = TQMIN ( start + len , length ( ) - 1 ) ; // don't look at the trailing space
for ( int i = start ; i < end ; + + i )
{
KoTextStringChar & ch = str - > at ( i ) ;
# ifdef DEBUG_FORMATTING
painter . setPen ( ( i % 2 ) ? TQt : : red : TQt : : green ) ;
painter . drawRect ( zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ - 1 , lastY_pix , ch . pixelwidth , baseLine_pix ) ;
TQPen pen ( cg . color ( TQColorGroup : : Highlight ) ) ;
painter . setPen ( pen ) ;
# endif
if ( ch . isCustom ( ) )
continue ;
if ( ( ch . c = = ' ' | | ch . c . unicode ( ) = = 0x00a0U )
& & ( whichFormattingChars & FormattingSpace ) )
{
// Don't use ch.pixelwidth here. We want a square with
// the same size for all spaces, even the justified ones
int w = zh - > layoutUnitToPixelX ( ch . format ( ) - > width ( ' ' ) ) ;
int height = zh - > layoutUnitToPixelY ( ch . ascent ( ) ) ;
int size = TQMAX ( 2 , TQMIN ( w / 2 , height / 3 ) ) ; // Enfore that it's a square, and that it's visible
int x = zh - > layoutUnitToPixelX ( ch . x ) ; // + ch.pixelxadj;
TQRect spcRect ( x + ( ch . pixelwidth - size ) / 2 , lastY_pix + baseLine_pix - ( height - size ) / 2 , size , size ) ;
if ( ch . c = = ' ' )
painter . drawRect ( spcRect ) ;
else // nbsp
painter . fillRect ( spcRect , pen . color ( ) ) ;
}
else if ( ch . c = = ' \t ' & & ( whichFormattingChars & FormattingTabs ) )
{
/*KoTextStringChar &nextch = str->at(i+1);
int nextx = ( nextch . x > ch . x ) ? nextch . x : rect ( ) . width ( ) ;
//kdDebug() << "tab x=" << ch.x << " nextch.x=" << nextch.x
// << " nextx=" << nextx << " startX=" << startX << " bw=" << bw << endl;
int availWidth = nextx - ch . x - 1 ;
availWidth = zh - > layoutUnitToPixelX ( availWidth ) ; */
int availWidth = ch . pixelwidth ;
KoTextFormat * format = ch . format ( ) ;
int x = zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ + availWidth / 2 ;
int charWidth = format - > screenFontMetrics ( zh ) . width ( ' W ' ) ;
int size = TQMIN ( availWidth , charWidth ) / 2 ; // actually the half size
int y = lastY_pix + baseLine_pix - zh - > layoutUnitToPixelY ( ch . ascent ( ) / 2 ) ;
int arrowsize = zh - > zoomItY ( 2 ) ;
painter . drawLine ( x - size , y , x + size , y ) ;
if ( rightToLeft )
{
painter . drawLine ( x - size , y , x - size + arrowsize , y - arrowsize ) ;
painter . drawLine ( x - size , y , x - size + arrowsize , y + arrowsize ) ;
}
else
{
painter . drawLine ( x + size , y , x + size - arrowsize , y - arrowsize ) ;
painter . drawLine ( x + size , y , x + size - arrowsize , y + arrowsize ) ;
}
}
else if ( ch . c = = ' \n ' & & ( whichFormattingChars & FormattingBreak ) )
{
// draw line break
KoTextFormat * format = static_cast < KoTextFormat * > ( ch . format ( ) ) ;
int w = format - > charWidth ( zh , true , & ch , this , ' X ' ) ;
int size = TQMIN ( w , h_pix * 3 / 4 ) ;
int arrowsize = zh - > zoomItY ( 2 ) ;
// x,y is the bottom right corner of the reversed L
//kdDebug() << "startX=" << startX << " bw=" << bw << " w=" << w << endl;
int y = lastY_pix + baseLine_pix - arrowsize ;
//kdDebug() << "KWTextParag::drawFormattingChars drawing Line Break at " << x << "," << y << endl;
if ( rightToLeft )
{
int x = zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ + ch . pixelwidth - 1 ;
painter . drawLine ( x - size , y - size , x - size , y ) ;
painter . drawLine ( x - size , y , ( int ) ( x - size * 0.3 ) , y ) ;
// Now the arrow
painter . drawLine ( ( int ) ( x - size * 0.3 ) , y , ( int ) ( x - size * 0.3 - arrowsize ) , y - arrowsize ) ;
painter . drawLine ( ( int ) ( x - size * 0.3 ) , y , ( int ) ( x - size * 0.3 - arrowsize ) , y + arrowsize ) ;
}
else
{
int x = zh - > layoutUnitToPixelX ( ch . x ) /*+ ch.pixelxadj*/ + w - 1 ;
painter . drawLine ( x , y - size , x , y ) ;
painter . drawLine ( x , y , ( int ) ( x - size * 0.7 ) , y ) ;
// Now the arrow
painter . drawLine ( ( int ) ( x - size * 0.7 ) , y , ( int ) ( x - size * 0.7 + arrowsize ) , y - arrowsize ) ;
painter . drawLine ( ( int ) ( x - size * 0.7 ) , y , ( int ) ( x - size * 0.7 + arrowsize ) , y + arrowsize ) ;
}
}
}
painter . restore ( ) ;
}
}
int KoTextParag : : heightForLineSpacing ( int startChar , int lastChar ) const
{
int h = 0 ;
int end = TQMIN ( lastChar , length ( ) - 1 ) ; // don't look at the trailing space
for ( int i = startChar ; i < = end ; + + i )
{
const KoTextStringChar & chr = str - > at ( i ) ;
if ( ! chr . isCustom ( ) )
h = TQMAX ( h , chr . format ( ) - > height ( ) ) ;
}
return h ;
}