diff options
Diffstat (limited to 'sw/source/core/txtnode')
21 files changed, 18947 insertions, 0 deletions
diff --git a/sw/source/core/txtnode/SwGrammarContact.cxx b/sw/source/core/txtnode/SwGrammarContact.cxx new file mode 100644 index 000000000000..5a444161a702 --- /dev/null +++ b/sw/source/core/txtnode/SwGrammarContact.cxx @@ -0,0 +1,221 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +#include <vcl/timer.hxx> +#include <hints.hxx> +#include <IGrammarContact.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <SwGrammarMarkUp.hxx> +#include <txtfrm.hxx> +#include <rootfrm.hxx> +#include <viewsh.hxx> + +extern void repaintTextFrames( SwModify& rModify ); + + +/* SwGrammarContact + This class is responsible for the delayed display of grammar checks when a paragraph is edited + It's a client of the paragraph the cursor points to. + If the cursor position changes, updateCursorPosition has to be called + If the grammar checker wants to set a grammar marker at a paragraph, he has to request + the grammar list from this class. If the requested paragraph is not edited, it returns + the normal grammar list. But if the paragraph is the active one, a proxy list will be returned and + all changes are set in this proxy list. If the cursor leaves the paragraph the proxy list + will replace the old list. If the grammar checker has completed the paragraph ('setChecked') + then a timer is setup which replaces the old list as well. +*/ + +class SwGrammarContact : public IGrammarContact, public SwClient +{ + Timer aTimer; + SwGrammarMarkUp *mpProxyList; + bool mbFinished; + SwTxtNode* getMyTxtNode() { return (SwTxtNode*)pRegisteredIn; } + DECL_LINK( TimerRepaint, Timer * ); + +public: + SwGrammarContact(); + ~SwGrammarContact() { aTimer.Stop(); delete mpProxyList; } + + // (pure) virtual functions of IGrammarContact + virtual void updateCursorPosition( const SwPosition& rNewPos ); + virtual SwGrammarMarkUp* getGrammarCheck( SwTxtNode& rTxtNode, bool bCreate ); + virtual void finishGrammarCheck( SwTxtNode& rTxtNode ); + + // virtual function of SwClient + virtual void Modify( SfxPoolItem *pOld, SfxPoolItem *pNew); +}; + +SwGrammarContact::SwGrammarContact() : mpProxyList(0), mbFinished( false ) +{ + aTimer.SetTimeout( 2000 ); // Repaint of grammar check after 'setChecked' + aTimer.SetTimeoutHdl( LINK(this, SwGrammarContact, TimerRepaint) ); +} + +IMPL_LINK( SwGrammarContact, TimerRepaint, Timer *, pTimer ) +{ + if( pTimer ) + { + pTimer->Stop(); + if( pRegisteredIn ) + { //Replace the old wrong list by the proxy list and repaint all frames + getMyTxtNode()->SetGrammarCheck( mpProxyList, true ); + mpProxyList = 0; + repaintTextFrames( *pRegisteredIn ); + } + } + return 0; +} + +/* I'm always a client of the current paragraph */ +void SwGrammarContact::updateCursorPosition( const SwPosition& rNewPos ) +{ + SwTxtNode* pTxtNode = rNewPos.nNode.GetNode().GetTxtNode(); + if( pTxtNode != GetRegisteredIn() ) // paragraph has been changed + { + aTimer.Stop(); + if( GetRegisteredIn() ) // My last paragraph has been left + { + if( mpProxyList ) + { // replace old list by the proxy list and repaint + getMyTxtNode()->SetGrammarCheck( mpProxyList, true ); + repaintTextFrames( *pRegisteredIn ); + } + pRegisteredIn->Remove( this ); // good bye old paragraph + mpProxyList = 0; + } + if( pTxtNode ) + pTxtNode->Add( this ); // welcome new paragraph + } +} + +/* deliver a grammar check list for the given text node */ +SwGrammarMarkUp* SwGrammarContact::getGrammarCheck( SwTxtNode& rTxtNode, bool bCreate ) +{ + SwGrammarMarkUp *pRet = 0; + if( GetRegisteredIn() == &rTxtNode ) // hey, that's my current paragraph! + { // so you will get a proxy list... + if( bCreate ) + { + if( mbFinished ) + { + delete mpProxyList; + mpProxyList = 0; + } + if( !mpProxyList ) + { + if( rTxtNode.GetGrammarCheck() ) + mpProxyList = (SwGrammarMarkUp*)rTxtNode.GetGrammarCheck()->Clone(); + else + { + mpProxyList = new SwGrammarMarkUp(); + mpProxyList->SetInvalid( 0, STRING_LEN ); + } + } + mbFinished = false; + } + pRet = mpProxyList; + } + else + { + pRet = rTxtNode.GetGrammarCheck(); // do you have already a list? + if( bCreate && !pRet ) // do you want to create a list? + { + pRet = new SwGrammarMarkUp(); + pRet->SetInvalid( 0, STRING_LEN ); + rTxtNode.SetGrammarCheck( pRet ); + rTxtNode.SetGrammarCheckDirty( true ); + } + } + return pRet; +} + +void SwGrammarContact::Modify( SfxPoolItem *pOld, SfxPoolItem * ) +{ + if( !pOld || pOld->Which() != RES_OBJECTDYING ) + return; + + SwPtrMsgPoolItem *pDead = (SwPtrMsgPoolItem *)pOld; + if( pDead->pObject == pRegisteredIn ) + { // if my current paragraph dies, I throw the proxy list away + aTimer.Stop(); + pRegisteredIn->Remove( this ); + delete mpProxyList; + mpProxyList = 0; + } +} + +void SwGrammarContact::finishGrammarCheck( SwTxtNode& rTxtNode ) +{ + if( &rTxtNode != pRegisteredIn ) // not my paragraph + repaintTextFrames( rTxtNode ); // can be repainted directly + else + { + if( mpProxyList ) + { + mbFinished = true; + aTimer.Start(); // will replace old list and repaint with delay + } + else if( getMyTxtNode()->GetGrammarCheck() ) + { // all grammar problems seems to be gone, no delay needed + getMyTxtNode()->SetGrammarCheck( 0, true ); + repaintTextFrames( *pRegisteredIn ); + } + } +} + +IGrammarContact* createGrammarContact() +{ + return new SwGrammarContact(); +} + +/* repaint all text frames of the given text node */ +void repaintTextFrames( SwModify& rModify ) +{ + SwClientIter aIter( rModify ); + for( const SwTxtFrm *pFrm = (const SwTxtFrm*)aIter.First( TYPE(SwTxtFrm) ); + pFrm; pFrm = (const SwTxtFrm*)aIter.Next() ) + { + SwRect aRec( pFrm->PaintArea() ); + const SwRootFrm *pRootFrm = pFrm->FindRootFrm(); + ViewShell *pCurShell = pRootFrm ? pRootFrm->GetCurrShell() : NULL; + if( pCurShell ) + pCurShell->InvalidateWindows( aRec ); + } +} + +void finishGrammarCheck( SwTxtNode& rTxtNode ) +{ + IGrammarContact* pGrammarContact = getGrammarContact( rTxtNode ); + if( pGrammarContact ) + pGrammarContact->finishGrammarCheck( rTxtNode ); +} + diff --git a/sw/source/core/txtnode/atrfld.cxx b/sw/source/core/txtnode/atrfld.cxx new file mode 100644 index 000000000000..3ddd3189bf6c --- /dev/null +++ b/sw/source/core/txtnode/atrfld.cxx @@ -0,0 +1,389 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +#include "fldbas.hxx" // fuer FieldType +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <docufld.hxx> + +#include "reffld.hxx" +#include "ddefld.hxx" +#include "usrfld.hxx" +#include "expfld.hxx" +#include "swfont.hxx" // fuer GetFldsColor +#include "ndtxt.hxx" // SwTxtNode +#include "calc.hxx" // Update fuer UserFields +#include "hints.hxx" +#include <IDocumentFieldsAccess.hxx> + +#include <svl/smplhint.hxx> + +TYPEINIT3( SwFmtFld, SfxPoolItem, SwClient,SfxBroadcaster) +TYPEINIT1(SwFmtFldHint, SfxHint); + +/**************************************************************************** + * + * class SwFmtFld + * + ****************************************************************************/ + + // Konstruktor fuers Default vom Attribut-Pool +SwFmtFld::SwFmtFld() + : SfxPoolItem( RES_TXTATR_FIELD ), + SwClient( 0 ), + pField( 0 ), + pTxtAttr( 0 ) +{ +} + +SwFmtFld::SwFmtFld( const SwField &rFld ) + : SfxPoolItem( RES_TXTATR_FIELD ), + SwClient( rFld.GetTyp() ), + pTxtAttr( 0 ) +{ + pField = rFld.Copy(); +} + +// #i24434# +// Since Items are used in ItemPool and in default constructed ItemSets with +// full pool range, all items need to be clonable. Thus, this one needed to be +// corrected +SwFmtFld::SwFmtFld( const SwFmtFld& rAttr ) + : SfxPoolItem( RES_TXTATR_FIELD ), SwClient(), SfxBroadcaster(), + pField( 0 ), + pTxtAttr( 0 ) +{ + if(rAttr.GetFld()) + { + rAttr.GetFld()->GetTyp()->Add(this); + pField = rAttr.GetFld()->Copy(); + } +} + +SwFmtFld::~SwFmtFld() +{ + SwFieldType* pType = pField ? pField->GetTyp() : 0; + + if (pType && pType->Which() == RES_DBFLD) + pType = 0; // DB-Feldtypen zerstoeren sich selbst + + Broadcast( SwFmtFldHint( this, SWFMTFLD_REMOVED ) ); + delete pField; + + // bei einige FeldTypen muessen wir den FeldTypen noch loeschen + if( pType && pType->IsLastDepend() ) + { + BOOL bDel = FALSE; + switch( pType->Which() ) + { + case RES_USERFLD: + bDel = ((SwUserFieldType*)pType)->IsDeleted(); + break; + + case RES_SETEXPFLD: + bDel = ((SwSetExpFieldType*)pType)->IsDeleted(); + break; + + case RES_DDEFLD: + bDel = ((SwDDEFieldType*)pType)->IsDeleted(); + break; + } + + if( bDel ) + { + // vorm loeschen erstmal austragen + pType->Remove( this ); + delete pType; + } + } +} + +// #111840# +void SwFmtFld::SetFld(SwField * _pField) +{ + if (NULL != pField) + delete pField; + + pField = _pField; + Broadcast( SwFmtFldHint( this, SWFMTFLD_CHANGED ) ); +} + +int SwFmtFld::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + // OD 2004-05-14 #i29146# - correction: check, if <pField> and + // <((SwFmtFld&)rAttr).GetFld()> are set. + // OD 2004-05-14 #i29146# - items are equal, if both fields aren't set. + return ( pField && ((SwFmtFld&)rAttr).GetFld() && + pField->GetTyp() == ((SwFmtFld&)rAttr).GetFld()->GetTyp() && + pField->GetFormat() == ((SwFmtFld&)rAttr).GetFld()->GetFormat() ) || + ( !pField && !((SwFmtFld&)rAttr).GetFld() ); +} + +SfxPoolItem* SwFmtFld::Clone( SfxItemPool* ) const +{ + return new SwFmtFld( *this ); +} + +void SwFmtFld::Modify( SfxPoolItem* pOld, SfxPoolItem* pNew ) +{ + if( !pTxtAttr ) + return; + + SwTxtNode* pTxtNd = (SwTxtNode*)&pTxtAttr->GetTxtNode(); + ASSERT( pTxtNd, "wo ist denn mein Node?" ); + if( pNew ) + { + switch( pNew->Which() ) + { + case RES_OBJECTDYING: + return; // don't do anything, especially not expand! + case RES_TXTATR_FLDCHG: + // "Farbe hat sich geaendert !" + // this, this fuer "nur Painten" + pTxtNd->Modify( this, this ); + return; + case RES_REFMARKFLD_UPDATE: + // GetReferenz-Felder aktualisieren + if( RES_GETREFFLD == GetFld()->GetTyp()->Which() ) + { + // --> OD 2007-09-06 #i81002# +// ((SwGetRefField*)GetFld())->UpdateField(); + dynamic_cast<SwGetRefField*>(GetFld())->UpdateField( pTxtAttr ); + // <-- + } + break; + case RES_DOCPOS_UPDATE: + // Je nach DocPos aktualisieren (SwTxtFrm::Modify()) + pTxtNd->Modify( pNew, this ); + return; + + case RES_ATTRSET_CHG: + case RES_FMT_CHG: + pTxtNd->Modify( pOld, pNew ); + return; + } + } + + switch (GetFld()->GetTyp()->Which()) + { + case RES_HIDDENPARAFLD: + if( !pOld || RES_HIDDENPARA_PRINT != pOld->Which() ) + break; + case RES_DBSETNUMBERFLD: + case RES_DBNUMSETFLD: + case RES_DBNEXTSETFLD: + case RES_DBNAMEFLD: + pTxtNd->Modify( 0, pNew); + return; + } + + if( RES_USERFLD == GetFld()->GetTyp()->Which() ) + { + SwUserFieldType* pType = (SwUserFieldType*)GetFld()->GetTyp(); + if(!pType->IsValid()) + { + SwCalc aCalc( *pTxtNd->GetDoc() ); + pType->GetValue( aCalc ); + } + } + pTxtAttr->Expand(); +} + +BOOL SwFmtFld::GetInfo( SfxPoolItem& rInfo ) const +{ + const SwTxtNode* pTxtNd; + if( RES_AUTOFMT_DOCNODE != rInfo.Which() || + !pTxtAttr || 0 == ( pTxtNd = pTxtAttr->GetpTxtNode() ) || + &pTxtNd->GetNodes() != ((SwAutoFmtGetDocNode&)rInfo).pNodes ) + return TRUE; + + ((SwAutoFmtGetDocNode&)rInfo).pCntntNode = pTxtNd; + return FALSE; +} + + +BOOL SwFmtFld::IsFldInDoc() const +{ + const SwTxtNode* pTxtNd; + return pTxtAttr && 0 != ( pTxtNd = pTxtAttr->GetpTxtNode() ) && + pTxtNd->GetNodes().IsDocNodes(); +} + +BOOL SwFmtFld::IsProtect() const +{ + const SwTxtNode* pTxtNd; + return pTxtAttr && 0 != ( pTxtNd = pTxtAttr->GetpTxtNode() ) && + pTxtNd->IsProtect(); +} + +/************************************************************************* +|* +|* SwTxtFld::SwTxtFld() +|* +|* Beschreibung Attribut fuer automatischen Text, Ctor +|* Ersterstellung BP 30.04.92 +|* Letzte Aenderung JP 15.08.94 +|* +*************************************************************************/ + +SwTxtFld::SwTxtFld( SwFmtFld& rAttr, xub_StrLen nStartPos ) + : SwTxtAttr( rAttr, nStartPos ) + , m_aExpand( rAttr.GetFld()->Expand() ) + , m_pTxtNode( 0 ) +{ + rAttr.pTxtAttr = this; + SetHasDummyChar(true); +} + +SwTxtFld::~SwTxtFld( ) +{ +} + +/************************************************************************* +|* +|* SwTxtFld::Expand() +|* +|* Beschreibung exandiert das Feld und tauscht den Text im Node +|* Ersterstellung BP 30.04.92 +|* Letzte Aenderung JP 15.08.94 +|* +*************************************************************************/ + +void SwTxtFld::Expand() const +{ + // Wenn das expandierte Feld sich nicht veraendert hat, wird returnt + ASSERT( m_pTxtNode, "SwTxtFld: where is my TxtNode?" ); + + const SwField* pFld = GetFld().GetFld(); + XubString aNewExpand( pFld->Expand() ); + + if( aNewExpand == m_aExpand ) + { + // Bei Seitennummernfeldern + const USHORT nWhich = pFld->GetTyp()->Which(); + if( RES_CHAPTERFLD != nWhich && RES_PAGENUMBERFLD != nWhich && + RES_REFPAGEGETFLD != nWhich && + // --> FME 2005-05-23 #122919# Page count fields to not use aExpand + // during formatting, therefore an invalidation of the text frame + // has to be triggered even if aNewExpand == aExpand: + ( RES_DOCSTATFLD != nWhich || DS_PAGE != static_cast<const SwDocStatField*>(pFld)->GetSubType() ) && + // <-- + ( RES_GETEXPFLD != nWhich || ((SwGetExpField*)pFld)->IsInBodyTxt() ) ) + { + // BP: das muesste man noch optimieren! + //JP 12.06.97: stimmt, man sollte auf jedenfall eine Status- + // aenderung an die Frames posten + if( m_pTxtNode->CalcHiddenParaField() ) + { + m_pTxtNode->Modify( 0, 0 ); + } + return; + } + } + + m_aExpand = aNewExpand; + + // 0, this for formatting + m_pTxtNode->Modify( 0, const_cast<SwFmtFld*>( &GetFld() ) ); +} + +/************************************************************************* + * SwTxtFld::CopyFld() + *************************************************************************/ + +void SwTxtFld::CopyFld( SwTxtFld *pDest ) const +{ + ASSERT( m_pTxtNode, "SwTxtFld: where is my TxtNode?" ); + ASSERT( pDest->m_pTxtNode, "SwTxtFld: where is pDest's TxtNode?" ); + + IDocumentFieldsAccess* pIDFA = m_pTxtNode->getIDocumentFieldsAccess(); + IDocumentFieldsAccess* pDestIDFA = pDest->m_pTxtNode->getIDocumentFieldsAccess(); + + SwFmtFld& rFmtFld = (SwFmtFld&)pDest->GetFld(); + const USHORT nFldWhich = rFmtFld.GetFld()->GetTyp()->Which(); + + if( pIDFA != pDestIDFA ) + { + // Die Hints stehen in unterschiedlichen Dokumenten, + // der Feldtyp muss im neuen Dokument angemeldet werden. + // Z.B: Kopieren ins ClipBoard. + SwFieldType* pFldType; + if( nFldWhich != RES_DBFLD && nFldWhich != RES_USERFLD && + nFldWhich != RES_SETEXPFLD && nFldWhich != RES_DDEFLD && + RES_AUTHORITY != nFldWhich ) + pFldType = pDestIDFA->GetSysFldType( nFldWhich ); + else + pFldType = pDestIDFA->InsertFldType( *rFmtFld.GetFld()->GetTyp() ); + + // Sonderbehandlung fuer DDE-Felder + if( RES_DDEFLD == nFldWhich ) + { + if( rFmtFld.GetTxtFld() ) + ((SwDDEFieldType*)rFmtFld.GetFld()->GetTyp())->DecRefCnt(); + ((SwDDEFieldType*)pFldType)->IncRefCnt(); + } + + ASSERT( pFldType, "unbekannter FieldType" ); + pFldType->Add( &rFmtFld ); // ummelden + rFmtFld.GetFld()->ChgTyp( pFldType ); + } + + // Expressionfelder Updaten + if( nFldWhich == RES_SETEXPFLD || nFldWhich == RES_GETEXPFLD || + nFldWhich == RES_HIDDENTXTFLD ) + { + SwTxtFld* pFld = (SwTxtFld*)this; + pDestIDFA->UpdateExpFlds( pFld, true ); + } + // Tabellenfelder auf externe Darstellung + else if( RES_TABLEFLD == nFldWhich && + ((SwTblField*)rFmtFld.GetFld())->IsIntrnlName() ) + { + // erzeuge aus der internen (fuer CORE) die externe (fuer UI) Formel + const SwTableNode* pTblNd = m_pTxtNode->FindTableNode(); + if( pTblNd ) // steht in einer Tabelle + ((SwTblField*)rFmtFld.GetFld())->PtrToBoxNm( &pTblNd->GetTable() ); + } +} + +/* -----------------26.06.2003 13:54----------------- + + --------------------------------------------------*/ +void SwTxtFld::NotifyContentChange(SwFmtFld& rFmtFld) +{ + //if not in undo section notify the change + if (m_pTxtNode && m_pTxtNode->GetNodes().IsDocNodes()) + { + m_pTxtNode->Modify(0, &rFmtFld); + } +} + + diff --git a/sw/source/core/txtnode/atrflyin.cxx b/sw/source/core/txtnode/atrflyin.cxx new file mode 100644 index 000000000000..02da2c32be8a --- /dev/null +++ b/sw/source/core/txtnode/atrflyin.cxx @@ -0,0 +1,290 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + + +#include "hintids.hxx" +#include "cntfrm.hxx" // _GetFly +#include "doc.hxx" +#include "pam.hxx" // fuer SwTxtFlyCnt +#include "flyfrm.hxx" // fuer SwTxtFlyCnt +#include "ndtxt.hxx" // SwFlyFrmFmt +#include "frmfmt.hxx" // SwFlyFrmFmt +#include <fmtflcnt.hxx> +#include <txtflcnt.hxx> +#include <fmtanchr.hxx> +#include "swfont.hxx" +#include "txtfrm.hxx" +#include "flyfrms.hxx" +// --> OD 2004-11-09 #i26945# +#include <objectformatter.hxx> +// <-- + +SwFmtFlyCnt::SwFmtFlyCnt( SwFrmFmt *pFrmFmt ) + : SfxPoolItem( RES_TXTATR_FLYCNT ), + pTxtAttr( 0 ), + pFmt( pFrmFmt ) +{ +} + +int __EXPORT SwFmtFlyCnt::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + return( pTxtAttr && ((SwFmtFlyCnt&)rAttr).pTxtAttr && + *pTxtAttr->GetStart() == *((SwFmtFlyCnt&)rAttr).pTxtAttr->GetStart() && + pFmt == ((SwFmtFlyCnt&)rAttr).GetFrmFmt() ); +} + +SfxPoolItem* __EXPORT SwFmtFlyCnt::Clone( SfxItemPool* ) const +{ + return new SwFmtFlyCnt( pFmt ); +} + +SwTxtFlyCnt::SwTxtFlyCnt( SwFmtFlyCnt& rAttr, xub_StrLen nStartPos ) + : SwTxtAttr( rAttr, nStartPos ) +{ + rAttr.pTxtAttr = this; + SetHasDummyChar(true); +} + + + +/************************************************************************* + * SwTxtFlyCnt::MakeTxtHint() + * + * An dieser Stelle soll einmal der Gesamtzusammenhang bei der Erzeugung + * eines neuen SwTxtFlyCnt erlaeutert werden. + * Das MakeTxtHint() wird z.B. im SwTxtNode::Copy() gerufen. + * Fuer die komplette Verdopplung sind folgende Schritte notwendig: + * 1) Duplizieren des pFmt incl. Inhalt, Attributen etc. + * 2) Setzen des Ankers + * 3) Benachrichtigung + * Da fuer die Bewaeltigung der Aufgaben nicht immer alle Informationen + * bereitstehen und darueber hinaus bestimmte Methoden erst zu einem + * spaeteren Zeitpunkt gerufen werden duerfen (weil nocht nicht alle + * Nodeinformationen vorliegen), verteilt sich der Ablauf. + * ad 1) MakeTxtHint() wird durch den Aufruf von SwDoc::CopyLayout() + * der das neue FlyFrmFmt erzeugt und mit dem duplizierten Inhalt des + * FlyFrm verbunden. + * ad 2) SetAnchor() wird von SwTxtNode::Insert() gerufen und sorgt fuer das + * setzen des Ankers (die SwPosition des Dummy-Zeichens wird dem FlyFrmFmt + * per SetAttr bekannt gegeben). Dies kann nicht im MakeTxtHint erledigt + * werden, da der Zielnode unbestimmt ist. + * ad 3) _GetFlyFrm() wird im Formatierungsprozess vom LineIter gerufen + * und sucht den FlyFrm zum Dummyzeichen des aktuellen CntntFrm. Wird keiner + * gefunden, so wird ein neuer FlyFrm angelegt. + * Kritisch an diesem Vorgehen ist, dass das pCntnt->AppendFly() eine + * sofortige Neuformatierung von pCntnt anstoesst. Die Rekursion kommt + * allerdings durch den Lockmechanismus in SwTxtFrm::Format() nicht + * zu stande. + * Attraktiv ist der Umstand, dass niemand ueber die vom Node abhaengigen + * CntntFrms iterieren braucht, um die FlyInCntFrm anzulegen. Dies geschieht + * bei der Arbeit. + *************************************************************************/ + +void SwTxtFlyCnt::CopyFlyFmt( SwDoc* pDoc ) +{ + SwFrmFmt* pFmt = GetFlyCnt().GetFrmFmt(); + ASSERT( pFmt, "von welchem Format soll ich eine Kopie erzeugen?" ) + // Das FlyFrmFmt muss dupliziert werden. + // In CopyLayoutFmt (siehe doclay.cxx) wird das FlyFrmFmt erzeugt + // und der Inhalt dupliziert. + + // fuers kopieren vom Attribut das Undo immer abschalten + BOOL bUndo = pDoc->DoesUndo(); + pDoc->DoUndo( FALSE ); + SwFmtAnchor aAnchor( pFmt->GetAnchor() ); + if ((FLY_AT_PAGE != aAnchor.GetAnchorId()) && + (pDoc != pFmt->GetDoc())) // different documents? + { + // JP 03.06.96: dann sorge dafuer, das der koperierte Anker auf + // gueltigen Content zeigt! Die Umsetzung auf die + // richtige Position erfolgt spaeter. + SwNodeIndex aIdx( pDoc->GetNodes().GetEndOfExtras(), +2 ); + SwCntntNode* pCNd = aIdx.GetNode().GetCntntNode(); + if( !pCNd ) + pCNd = pDoc->GetNodes().GoNext( &aIdx ); + + SwPosition* pPos = (SwPosition*)aAnchor.GetCntntAnchor(); + pPos->nNode = aIdx; + if (FLY_AS_CHAR == aAnchor.GetAnchorId()) + { + pPos->nContent.Assign( pCNd, 0 ); + } + else + { + pPos->nContent.Assign( 0, 0 ); + ASSERT( !this, "CopyFlyFmt: Was fuer ein Anker?" ); + } + } + + SwFrmFmt* pNew = pDoc->CopyLayoutFmt( *pFmt, aAnchor, false, false ); + pDoc->DoUndo( bUndo ); + ((SwFmtFlyCnt&)GetFlyCnt()).SetFlyFmt( pNew ); +} + +/************************************************************************* + * SwTxtFlyCnt::SetAnchor() + * + * SetAnchor() wird von SwTxtNode::Insert() gerufen und sorgt fuer das + * setzen des Ankers (die SwPosition des Dummy-Zeichens wird dem FlyFrmFmt + * per SetAttr bekannt gegeben). Dies kann nicht im MakeTxtHint erledigt + * werden, da der Zielnode unbestimmt ist. + * (siehe Kommentar in SwTxtFlyCnt::MakeTxtHint) + *************************************************************************/ + +void SwTxtFlyCnt::SetAnchor( const SwTxtNode *pNode ) +{ + // fuers Undo muss der neue Anker schon bekannt sein ! + + // Wir ermitteln den Index im Nodesarray zum Node + + SwDoc* pDoc = (SwDoc*)pNode->GetDoc(); + + SwIndex aIdx( (SwTxtNode*)pNode, *GetStart() ); + SwPosition aPos( *pNode->StartOfSectionNode(), aIdx ); + SwFrmFmt* pFmt = GetFlyCnt().GetFrmFmt(); + SwFmtAnchor aAnchor( pFmt->GetAnchor() ); + + if( !aAnchor.GetCntntAnchor() || + !aAnchor.GetCntntAnchor()->nNode.GetNode().GetNodes().IsDocNodes() || + &aAnchor.GetCntntAnchor()->nNode.GetNode() != (SwNode*)pNode ) + aPos.nNode = *pNode; + else + aPos.nNode = aAnchor.GetCntntAnchor()->nNode; + + aAnchor.SetType( FLY_AS_CHAR ); // default! + aAnchor.SetAnchor( &aPos ); + + // beim Ankerwechsel werden immer alle FlyFrms vom Attribut geloescht + // JP 25.04.95: wird innerhalb des SplitNodes die Frames verschoben + // koennen die Frames erhalten bleiben. + if( ( !pNode->GetpSwpHints() || !pNode->GetpSwpHints()->IsInSplitNode() ) + && RES_DRAWFRMFMT != pFmt->Which() ) + pFmt->DelFrms(); + + // stehen wir noch im falschen Dokument ? + if( pDoc != pFmt->GetDoc() ) + { + // fuers kopieren vom Attribut das Undo immer abschalten + BOOL bUndo = pDoc->DoesUndo(); + pDoc->DoUndo( FALSE ); + SwFrmFmt* pNew = pDoc->CopyLayoutFmt( *pFmt, aAnchor, false, false ); + pDoc->DoUndo( bUndo ); + + bUndo = pFmt->GetDoc()->DoesUndo(); + pFmt->GetDoc()->DoUndo( FALSE ); + pFmt->GetDoc()->DelLayoutFmt( pFmt ); + pFmt->GetDoc()->DoUndo( bUndo ); + ((SwFmtFlyCnt&)GetFlyCnt()).SetFlyFmt( pNew ); + } + else if( pNode->GetpSwpHints() && + pNode->GetpSwpHints()->IsInSplitNode() && + RES_DRAWFRMFMT != pFmt->Which() ) + { + pFmt->LockModify(); + pFmt->SetFmtAttr( aAnchor ); // nur den Anker neu setzen + pFmt->UnlockModify(); + } + else + pFmt->SetFmtAttr( aAnchor ); // nur den Anker neu setzen + + // Am Node haengen u.a. abhaengige CntFrms. + // Fuer jeden CntFrm wird ein SwFlyInCntFrm angelegt. +} + +/************************************************************************* + * SwTxtFlyCnt::_GetFlyFrm() + * + * _GetFlyFrm() wird im Formatierungsprozess vom LineIter gerufen + * und sucht den FlyFrm zum Dummyzeichen des aktuellen CntntFrm. Wird keiner + * gefunden, so wird ein neuer FlyFrm angelegt. + * (siehe Kommentar ind SwTxtFlyCnt::MakeTxtHint) + *************************************************************************/ + +SwFlyInCntFrm *SwTxtFlyCnt::_GetFlyFrm( const SwFrm *pCurrFrm ) +{ + SwFrmFmt* pFrmFmt = GetFlyCnt().GetFrmFmt(); + if( RES_DRAWFRMFMT == pFrmFmt->Which() ) + { + ASSERT( !this, "SwTxtFlyCnt::_GetFlyFrm: DrawInCnt-Baustelle!" ); + return NULL; + } + + SwClientIter aIter( *GetFlyCnt().pFmt ); + ASSERT( pCurrFrm->IsTxtFrm(), "SwTxtFlyCnt::_GetFlyFrm for TxtFrms only." ); + + if( aIter.GoStart() ) + { + SwTxtFrm *pFirst = (SwTxtFrm*)pCurrFrm; + while ( pFirst->IsFollow() ) + pFirst = pFirst->FindMaster(); + do + { SwFrm * pFrm = PTR_CAST( SwFrm, aIter() ); + if ( pFrm ) + { + SwTxtFrm *pTmp = pFirst; + do + { if( ( (SwFlyFrm*)pFrm )->GetAnchorFrm() == (SwFrm*) pTmp ) + { + if ( pTmp != pCurrFrm ) + { + pTmp->RemoveFly( (SwFlyFrm*)pFrm ); + ((SwTxtFrm*)pCurrFrm)->AppendFly( (SwFlyFrm*)pFrm ); + } + return (SwFlyInCntFrm*)pFrm; + } + pTmp = pTmp->GetFollow(); + } while ( pTmp ); + } + } while( aIter++ ); + } + + // Wir haben keinen passenden FlyFrm gefunden, deswegen wird ein + // neuer angelegt. + // Dabei wird eine sofortige Neuformatierung von pCurrFrm angestossen. + // Die Rekursion wird durch den Lockmechanismus in SwTxtFrm::Format() + // abgewuergt. + SwFlyInCntFrm *pFly = new SwFlyInCntFrm( (SwFlyFrmFmt*)pFrmFmt, (SwFrm*)pCurrFrm ); + ((SwFrm*)pCurrFrm)->AppendFly( pFly ); + pFly->RegistFlys(); + + // 7922: Wir muessen dafuer sorgen, dass der Inhalt des FlyInCnt + // nach seiner Konstruktion stramm durchformatiert wird. + // --> OD 2004-11-09 #i26945# - Use new object formatter to format Writer + // fly frame and its content. + SwObjectFormatter::FormatObj( *pFly, const_cast<SwFrm*>(pCurrFrm), + pCurrFrm->FindPageFrm() ); + // <-- + + return pFly; +} + + diff --git a/sw/source/core/txtnode/atrftn.cxx b/sw/source/core/txtnode/atrftn.cxx new file mode 100644 index 000000000000..7d6b8959787b --- /dev/null +++ b/sw/source/core/txtnode/atrftn.cxx @@ -0,0 +1,537 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + + +#define _SVSTDARR_USHORTS +#define _SVSTDARR_USHORTSSORT +#include <svl/svstdarr.hxx> +#include <doc.hxx> +#include <cntfrm.hxx> // ASSERT in ~SwTxtFtn() +#include <pagefrm.hxx> // RemoveFtn() +#include <fmtftn.hxx> +#include <txtftn.hxx> +#include <ftnidx.hxx> +#include <ftninfo.hxx> +#include <swfont.hxx> +#include <ndtxt.hxx> +#include <poolfmt.hxx> +#include <ftnfrm.hxx> +#include <ndindex.hxx> +#include <fmtftntx.hxx> +#include <section.hxx> + +/************************************************************************* +|* +|* class SwFmtFtn +|* +|* Beschreibung +|* Ersterstellung JP 09.08.94 +|* Letzte Aenderung JP 08.08.94 +|* +*************************************************************************/ + + +SwFmtFtn::SwFmtFtn( bool bEndNote ) + : SfxPoolItem( RES_TXTATR_FTN ), + pTxtAttr( 0 ), + nNumber( 0 ), + m_bEndNote( bEndNote ) +{ +} + + +int SwFmtFtn::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + return nNumber == ((SwFmtFtn&)rAttr).nNumber && + aNumber == ((SwFmtFtn&)rAttr).aNumber && + m_bEndNote == ((SwFmtFtn&)rAttr).m_bEndNote; +} + + +SfxPoolItem* SwFmtFtn::Clone( SfxItemPool* ) const +{ + SwFmtFtn* pNew = new SwFmtFtn; + pNew->aNumber = aNumber; + pNew->nNumber = nNumber; + pNew->m_bEndNote = m_bEndNote; + return pNew; +} + +void SwFmtFtn::SetEndNote( bool b ) +{ + if ( b != m_bEndNote ) + { + if ( GetTxtFtn() ) + { + GetTxtFtn()->DelFrms(); + } + m_bEndNote = b; + } +} + +SwFmtFtn::~SwFmtFtn() +{ +} + + +void SwFmtFtn::GetFtnText( XubString& rStr ) const +{ + if( pTxtAttr->GetStartNode() ) + { + SwNodeIndex aIdx( *pTxtAttr->GetStartNode(), 1 ); + SwCntntNode* pCNd = aIdx.GetNode().GetTxtNode(); + if( !pCNd ) + pCNd = aIdx.GetNodes().GoNext( &aIdx ); + + if( pCNd->IsTxtNode() ) + rStr = ((SwTxtNode*)pCNd)->GetExpandTxt(); + } +} + + // returnt den anzuzeigenden String der Fuss-/Endnote +XubString SwFmtFtn::GetViewNumStr( const SwDoc& rDoc, BOOL bInclStrings ) const +{ + XubString sRet( GetNumStr() ); + if( !sRet.Len() ) + { + // dann ist die Nummer von Interesse, also ueber die Info diese + // besorgen. + BOOL bMakeNum = TRUE; + const SwSectionNode* pSectNd = pTxtAttr + ? SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr( *pTxtAttr ) + : 0; + + if( pSectNd ) + { + const SwFmtFtnEndAtTxtEnd& rFtnEnd = (SwFmtFtnEndAtTxtEnd&) + pSectNd->GetSection().GetFmt()->GetFmtAttr( + IsEndNote() ? + static_cast<USHORT>(RES_END_AT_TXTEND) : + static_cast<USHORT>(RES_FTN_AT_TXTEND) ); + + if( FTNEND_ATTXTEND_OWNNUMANDFMT == rFtnEnd.GetValue() ) + { + bMakeNum = FALSE; + sRet = rFtnEnd.GetSwNumType().GetNumStr( GetNumber() ); + if( bInclStrings ) + { + sRet.Insert( rFtnEnd.GetPrefix(), 0 ); + sRet += rFtnEnd.GetSuffix(); + } + } + } + + if( bMakeNum ) + { + const SwEndNoteInfo* pInfo; + if( IsEndNote() ) + pInfo = &rDoc.GetEndNoteInfo(); + else + pInfo = &rDoc.GetFtnInfo(); + sRet = pInfo->aFmt.GetNumStr( GetNumber() ); + if( bInclStrings ) + { + sRet.Insert( pInfo->GetPrefix(), 0 ); + sRet += pInfo->GetSuffix(); + } + } + } + return sRet; +} + +/************************************************************************* + * class SwTxt/FmtFnt + *************************************************************************/ + +SwTxtFtn::SwTxtFtn( SwFmtFtn& rAttr, xub_StrLen nStartPos ) + : SwTxtAttr( rAttr, nStartPos ) + , m_pStartNode( 0 ) + , m_pTxtNode( 0 ) + , m_nSeqNo( USHRT_MAX ) +{ + rAttr.pTxtAttr = this; + SetHasDummyChar(true); +} + + +SwTxtFtn::~SwTxtFtn() +{ + SetStartNode( 0 ); +} + + + +void SwTxtFtn::SetStartNode( const SwNodeIndex *pNewNode, BOOL bDelNode ) +{ + if( pNewNode ) + { + if ( !m_pStartNode ) + { + m_pStartNode = new SwNodeIndex( *pNewNode ); + } + else + { + *m_pStartNode = *pNewNode; + } + } + else if ( m_pStartNode ) + { + // Zwei Dinge muessen erledigt werden: + // 1) Die Fussnoten muessen bei ihren Seiten abgemeldet werden + // 2) Die Fussnoten-Sektion in den Inserts muss geloescht werden. + SwDoc* pDoc; + if ( m_pTxtNode ) + { + pDoc = m_pTxtNode->GetDoc(); + } + else + { + //JP 27.01.97: der sw3-Reader setzt einen StartNode aber das + // Attribut ist noch nicht im TextNode verankert. + // Wird es geloescht (z.B. bei Datei einfuegen mit + // Ftn in einen Rahmen), muss auch der Inhalt + // geloescht werden + pDoc = m_pStartNode->GetNodes().GetDoc(); + } + + // Wir duerfen die Fussnotennodes nicht loeschen + // und brauchen die Fussnotenframes nicht loeschen, wenn + // wir im ~SwDoc() stehen. + if( !pDoc->IsInDtor() ) + { + if( bDelNode ) + { + // 1) Die Section fuer die Fussnote wird beseitigt + // Es kann sein, dass die Inserts schon geloescht wurden. + pDoc->DeleteSection( &m_pStartNode->GetNode() ); + } + else + // Werden die Nodes nicht geloescht mussen sie bei den Seiten + // abmeldet (Frms loeschen) werden, denn sonst bleiben sie + // stehen (Undo loescht sie nicht!) + DelFrms(); + } + DELETEZ( m_pStartNode ); + + // loesche die Fussnote noch aus dem Array am Dokument + for( USHORT n = 0; n < pDoc->GetFtnIdxs().Count(); ++n ) + if( this == pDoc->GetFtnIdxs()[n] ) + { + pDoc->GetFtnIdxs().Remove( n ); + // gibt noch weitere Fussnoten + if( !pDoc->IsInDtor() && n < pDoc->GetFtnIdxs().Count() ) + { + SwNodeIndex aTmp( pDoc->GetFtnIdxs()[n]->GetTxtNode() ); + pDoc->GetFtnIdxs().UpdateFtn( aTmp ); + } + break; + } + } +} + + +void SwTxtFtn::SetNumber( const USHORT nNewNum, const XubString* pStr ) +{ + SwFmtFtn& rFtn = (SwFmtFtn&)GetFtn(); + if( pStr && pStr->Len() ) + rFtn.aNumber = *pStr; + else + { + rFtn.nNumber = nNewNum; + rFtn.aNumber = aEmptyStr; + } + + ASSERT( m_pTxtNode, "SwTxtFtn: where is my TxtNode?" ); + SwNodes &rNodes = m_pTxtNode->GetDoc()->GetNodes(); + m_pTxtNode->Modify( 0, &rFtn ); + if ( m_pStartNode ) + { + // must iterate over all TxtNodes because of footnotes on other pages + SwNode* pNd; + ULONG nSttIdx = m_pStartNode->GetIndex() + 1; + ULONG nEndIdx = m_pStartNode->GetNode().EndOfSectionIndex(); + for( ; nSttIdx < nEndIdx; ++nSttIdx ) + { + // Es koennen ja auch Grafiken in der Fussnote stehen ... + if( ( pNd = rNodes[ nSttIdx ] )->IsTxtNode() ) + ((SwTxtNode*)pNd)->Modify( 0, &rFtn ); + } + } +} + +// Die Fussnoten duplizieren +void SwTxtFtn::CopyFtn( SwTxtFtn *pDest ) const +{ + if ( m_pStartNode && pDest->GetStartNode() ) + { + // die Fussnoten koennen in unterschiedlichen Dokumenten stehen !! + SwDoc* pDstDoc = pDest->m_pTxtNode->GetDoc(); + SwNodes &rDstNodes = pDstDoc->GetNodes(); + + // Wir kopieren nur den Inhalt der Sektion + SwNodeRange aRg( *m_pStartNode, 1, + *m_pStartNode->GetNode().EndOfSectionNode() ); + + // Wir fuegen auf dem Ende von pDest ein, d.h. die Nodes + // werden angehaengt. nDestLen haelt die Anzahl der CntNodes + // in pDest _vor_ dem Kopieren. + SwNodeIndex aStart( *(pDest->GetStartNode()) ); + SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() ); + ULONG nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1; + + m_pTxtNode->GetDoc()->CopyWithFlyInFly( aRg, 0, aEnd, TRUE ); + + // Wenn die Dest-Sektion nicht leer war, so muessen die alten + // Nodes geloescht werden: + // Vorher: Src: SxxxE, Dst: SnE + // Nachher: Src: SxxxE, Dst: SnxxxE + // und Src: SxxxE, Dst: SxxxE + aStart++; + rDstNodes.Delete( aStart, nDestLen ); + } + + // Der benutzerdefinierte String muss auch uebertragen werden. + if( GetFtn().aNumber.Len() ) + ((SwFmtFtn&)pDest->GetFtn()).aNumber = GetFtn().aNumber; +} + + + // lege eine neue leere TextSection fuer diese Fussnote an +void SwTxtFtn::MakeNewTextSection( SwNodes& rNodes ) +{ + if ( m_pStartNode ) + return; + + // Nun verpassen wir dem TxtNode noch die Fussnotenvorlage. + SwTxtFmtColl *pFmtColl; + const SwEndNoteInfo* pInfo; + USHORT nPoolId; + + if( GetFtn().IsEndNote() ) + { + pInfo = &rNodes.GetDoc()->GetEndNoteInfo(); + nPoolId = RES_POOLCOLL_ENDNOTE; + } + else + { + pInfo = &rNodes.GetDoc()->GetFtnInfo(); + nPoolId = RES_POOLCOLL_FOOTNOTE; + } + + if( 0 == (pFmtColl = pInfo->GetFtnTxtColl() ) ) + pFmtColl = rNodes.GetDoc()->GetTxtCollFromPool( nPoolId ); + + SwStartNode* pSttNd = rNodes.MakeTextSection( SwNodeIndex( rNodes.GetEndOfInserts() ), + SwFootnoteStartNode, pFmtColl ); + m_pStartNode = new SwNodeIndex( *pSttNd ); +} + + +void SwTxtFtn::DelFrms() +{ + // delete the FtnFrames from the pages + ASSERT( m_pTxtNode, "SwTxtFtn: where is my TxtNode?" ); + if ( !m_pTxtNode ) + return; + + BOOL bFrmFnd = FALSE; + { + SwClientIter aIter( *m_pTxtNode ); + for( SwCntntFrm* pFnd = (SwCntntFrm*)aIter.First( TYPE( SwCntntFrm )); + pFnd; pFnd = (SwCntntFrm*)aIter.Next() ) + { + SwPageFrm* pPage = pFnd->FindPageFrm(); + if( pPage ) + { + pPage->RemoveFtn( pFnd, this ); + bFrmFnd = TRUE; + } + } + } + //JP 13.05.97: falls das Layout vorm loeschen der Fussnoten entfernt + // wird, sollte man das ueber die Fussnote selbst tun + if ( !bFrmFnd && m_pStartNode ) + { + SwNodeIndex aIdx( *m_pStartNode ); + SwCntntNode* pCNd = m_pTxtNode->GetNodes().GoNext( &aIdx ); + if( pCNd ) + { + SwClientIter aIter( *pCNd ); + for( SwCntntFrm* pFnd = (SwCntntFrm*)aIter.First( TYPE( SwCntntFrm )); + pFnd; pFnd = (SwCntntFrm*)aIter.Next() ) + { + SwPageFrm* pPage = pFnd->FindPageFrm(); + + SwFrm *pFrm = pFnd->GetUpper(); + while ( pFrm && !pFrm->IsFtnFrm() ) + pFrm = pFrm->GetUpper(); + + SwFtnFrm *pFtn = (SwFtnFrm*)pFrm; + while ( pFtn && pFtn->GetMaster() ) + pFtn = pFtn->GetMaster(); + ASSERT( pFtn->GetAttr() == this, "Ftn mismatch error." ); + + while ( pFtn ) + { + SwFtnFrm *pFoll = pFtn->GetFollow(); + pFtn->Cut(); + delete pFtn; + pFtn = pFoll; + } + + // #i20556# During hiding of a section, the connection + // to the layout is already lost. pPage may be 0: + if ( pPage ) + pPage->UpdateFtnNum(); + } + } + } +} + + +USHORT SwTxtFtn::SetSeqRefNo() +{ + if( !m_pTxtNode ) + return USHRT_MAX; + + SwDoc* pDoc = m_pTxtNode->GetDoc(); + if( pDoc->IsInReading() ) + return USHRT_MAX; + + USHORT n, nFtnCnt = pDoc->GetFtnIdxs().Count(); + + const BYTE nTmp = 255 < nFtnCnt ? 255 : static_cast<BYTE>(nFtnCnt); + SvUShortsSort aArr( nTmp, nTmp ); + + // dann testmal, ob die Nummer schon vergeben ist oder ob eine neue + // bestimmt werden muss. + SwTxtFtn* pTxtFtn; + for( n = 0; n < nFtnCnt; ++n ) + { + pTxtFtn = pDoc->GetFtnIdxs()[ n ]; + if ( pTxtFtn != this ) + { + aArr.Insert( pTxtFtn->m_nSeqNo ); + } + } + + // test if number is already in use + if ( USHRT_MAX != m_nSeqNo ) + { + for( n = 0; n < aArr.Count(); ++n ) + { + if ( aArr[ n ] > m_nSeqNo ) + { + return m_nSeqNo; // free -> use + } + else if ( aArr[ n ] == m_nSeqNo ) + { + break; // used -> create new one + } + } + + if ( n == aArr.Count() ) + { + return m_nSeqNo; // free -> use + } + } + + // alle Nummern entsprechend geflag, also bestimme die richtige Nummer + for( n = 0; n < aArr.Count(); ++n ) + if( n != aArr[ n ] ) + break; + + return m_nSeqNo = n; +} + +void SwTxtFtn::SetUniqueSeqRefNo( SwDoc& rDoc ) +{ + USHORT n, nStt = 0, nFtnCnt = rDoc.GetFtnIdxs().Count(); + + const BYTE nTmp = 255 < nFtnCnt ? 255 : static_cast<BYTE>(nFtnCnt); + SvUShortsSort aArr( nTmp, nTmp ); + + // dann alle Nummern zusammensammeln die schon existieren + SwTxtFtn* pTxtFtn; + for( n = 0; n < nFtnCnt; ++n ) + { + pTxtFtn = rDoc.GetFtnIdxs()[ n ]; + if ( USHRT_MAX != pTxtFtn->m_nSeqNo ) + { + aArr.Insert( pTxtFtn->m_nSeqNo ); + } + } + + + for( n = 0; n < nFtnCnt; ++n ) + { + pTxtFtn = rDoc.GetFtnIdxs()[ n ]; + if ( USHRT_MAX == pTxtFtn->m_nSeqNo ) + { + for( ; nStt < aArr.Count(); ++nStt ) + { + if ( nStt != aArr[ nStt ] ) + { + pTxtFtn->m_nSeqNo = nStt; + break; + } + } + + if ( USHRT_MAX == pTxtFtn->m_nSeqNo ) + { + break; // found nothing + } + } + } + + // alle Nummern schon vergeben, also mit nStt++ weitermachen + for( ; n < nFtnCnt; ++n ) + { + pTxtFtn = rDoc.GetFtnIdxs()[ n ]; + if ( USHRT_MAX == pTxtFtn->m_nSeqNo ) + { + pTxtFtn->m_nSeqNo = nStt++; + } + } +} + +void SwTxtFtn::CheckCondColl() +{ +//FEATURE::CONDCOLL + if( GetStartNode() ) + ((SwStartNode&)GetStartNode()->GetNode()).CheckSectionCondColl(); +//FEATURE::CONDCOLL +} + + + + diff --git a/sw/source/core/txtnode/atrref.cxx b/sw/source/core/txtnode/atrref.cxx new file mode 100644 index 000000000000..979a810b018b --- /dev/null +++ b/sw/source/core/txtnode/atrref.cxx @@ -0,0 +1,103 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <hintids.hxx> +#include <txtrfmrk.hxx> +#include <fmtrfmrk.hxx> +#include <swfont.hxx> + + +/**************************************************************************** + * + * class SwFmtRefMark + * + ****************************************************************************/ + +SwFmtRefMark::~SwFmtRefMark( ) +{ +} + +SwFmtRefMark::SwFmtRefMark( const XubString& rName ) + : SfxPoolItem( RES_TXTATR_REFMARK ), + pTxtAttr( 0 ), + aRefName( rName ) +{ +} + +SwFmtRefMark::SwFmtRefMark( const SwFmtRefMark& rAttr ) + : SfxPoolItem( RES_TXTATR_REFMARK ), + pTxtAttr( 0 ), + aRefName( rAttr.aRefName ) +{ +} + +int SwFmtRefMark::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + return aRefName == ((SwFmtRefMark&)rAttr).aRefName; +} + +SfxPoolItem* SwFmtRefMark::Clone( SfxItemPool* ) const +{ + return new SwFmtRefMark( *this ); +} + +/************************************************************************* + * class SwTxtRefMark + *************************************************************************/ + +// Attribut fuer Inhalts-/Positions-Referenzen im Text + +SwTxtRefMark::SwTxtRefMark( SwFmtRefMark& rAttr, + xub_StrLen nStartPos, xub_StrLen* pEnd ) + : SwTxtAttrEnd( rAttr, nStartPos, nStartPos ) + , m_pTxtNode( 0 ) + , m_pEnd( 0 ) +{ + rAttr.pTxtAttr = this; + if ( pEnd ) + { + m_nEnd = *pEnd; + m_pEnd = & m_nEnd; + } + else + { + SetHasDummyChar(true); + } + SetDontMoveAttr( true ); + SetOverlapAllowedAttr( true ); +} + +xub_StrLen* SwTxtRefMark::GetEnd() +{ + return m_pEnd; +} + diff --git a/sw/source/core/txtnode/atrtox.cxx b/sw/source/core/txtnode/atrtox.cxx new file mode 100644 index 000000000000..c02478fe1014 --- /dev/null +++ b/sw/source/core/txtnode/atrtox.cxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <doc.hxx> +#include <txttxmrk.hxx> +#include <swfont.hxx> +#include <tox.hxx> +#include <ndtxt.hxx> + +SwTxtTOXMark::SwTxtTOXMark( SwTOXMark& rAttr, + xub_StrLen nStartPos, xub_StrLen* pEnd ) + : SwTxtAttrEnd( rAttr, nStartPos, nStartPos ) + , m_pTxtNode( 0 ) + , m_pEnd( 0 ) +{ + rAttr.pTxtAttr = this; + if ( !rAttr.GetAlternativeText().Len() ) + { + m_nEnd = *pEnd; + m_pEnd = & m_nEnd; + } + else + { + SetHasDummyChar(true); + } + SetDontMoveAttr( true ); + SetOverlapAllowedAttr( true ); +} + +SwTxtTOXMark::~SwTxtTOXMark() +{ +} + +xub_StrLen* SwTxtTOXMark::GetEnd() +{ + return m_pEnd; +} + +void SwTxtTOXMark::CopyTOXMark( SwDoc* pDoc ) +{ + SwTOXMark& rTOX = (SwTOXMark&)GetTOXMark(); + TOXTypes eType = rTOX.GetTOXType()->GetType(); + USHORT nCount = pDoc->GetTOXTypeCount( eType ); + const SwTOXType* pType = 0; + const XubString& rNm = rTOX.GetTOXType()->GetTypeName(); + + // kein entsprechender Verzeichnistyp vorhanden -> anlegen + // sonst verwenden + for(USHORT i=0; i < nCount; ++i) + { + const SwTOXType* pSrcType = pDoc->GetTOXType(eType, i); + if(pSrcType->GetTypeName() == rNm ) + { + pType = pSrcType; + break; + } + } + // kein entsprechender Typ vorhanden -> neu erzeugen + // + if(!pType) + { + pDoc->InsertTOXType( SwTOXType( eType, rNm ) ); + pType = pDoc->GetTOXType(eType, 0); + } + // Verzeichnistyp umhaengen + // + ((SwTOXType*)pType)->Add( &rTOX ); +} diff --git a/sw/source/core/txtnode/chrfmt.cxx b/sw/source/core/txtnode/chrfmt.cxx new file mode 100644 index 000000000000..eb2f4be5c3e8 --- /dev/null +++ b/sw/source/core/txtnode/chrfmt.cxx @@ -0,0 +1,38 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <charfmt.hxx> + + +TYPEINIT1( SwCharFmt, SwFmt ); //rtti fuer SwCharFmt + + + diff --git a/sw/source/core/txtnode/fmtatr2.cxx b/sw/source/core/txtnode/fmtatr2.cxx new file mode 100644 index 000000000000..245ec916fd4a --- /dev/null +++ b/sw/source/core/txtnode/fmtatr2.cxx @@ -0,0 +1,908 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +#include "hintids.hxx" +#include "unomid.h" + +#include <basic/sbxvar.hxx> +#include <svl/macitem.hxx> +#include <svl/stritem.hxx> +#include <svl/stylepool.hxx> +#include <fmtautofmt.hxx> +#include <fchrfmt.hxx> +#include <fmtinfmt.hxx> +#include <txtatr.hxx> +#include <fmtruby.hxx> +#include <charfmt.hxx> +#include <hints.hxx> // SwUpdateAttr +#include <unostyle.hxx> +#include <unoevent.hxx> // SwHyperlinkEventDescriptor +#include <com/sun/star/text/RubyAdjust.hdl> + +#include <cmdid.h> +#include <com/sun/star/uno/Any.h> +#include <SwStyleNameMapper.hxx> + +#include <fmtmeta.hxx> +#include <ndtxt.hxx> // for meta +#include <doc.hxx> // for meta +#include <unometa.hxx> +#include <docsh.hxx> +#include <svl/zforlist.hxx> // GetNumberFormat + +#include <boost/bind.hpp> +#include <algorithm> + + +using namespace ::com::sun::star; +using ::rtl::OUString; + +TYPEINIT1_AUTOFACTORY(SwFmtINetFmt, SfxPoolItem); +TYPEINIT1_AUTOFACTORY(SwFmtAutoFmt, SfxPoolItem); + +/************************************************************************* +|* +|* class SwFmtCharFmt +|* Beschreibung +|* Ersterstellung JP 23.11.90 +|* Letzte Aenderung JP 09.08.94 +|* +*************************************************************************/ + +SwFmtCharFmt::SwFmtCharFmt( SwCharFmt *pFmt ) + : SfxPoolItem( RES_TXTATR_CHARFMT ), + SwClient(pFmt), + pTxtAttr( 0 ) +{ +} + + + +SwFmtCharFmt::SwFmtCharFmt( const SwFmtCharFmt& rAttr ) + : SfxPoolItem( RES_TXTATR_CHARFMT ), + SwClient( rAttr.GetCharFmt() ), + pTxtAttr( 0 ) +{ +} + + + +SwFmtCharFmt::~SwFmtCharFmt() {} + + + +int SwFmtCharFmt::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + return GetCharFmt() == ((SwFmtCharFmt&)rAttr).GetCharFmt(); +} + + + +SfxPoolItem* SwFmtCharFmt::Clone( SfxItemPool* ) const +{ + return new SwFmtCharFmt( *this ); +} + + + +// weiterleiten an das TextAttribut +void SwFmtCharFmt::Modify( SfxPoolItem* pOld, SfxPoolItem* pNew ) +{ + if( pTxtAttr ) + pTxtAttr->Modify( pOld, pNew ); +} + + + +// weiterleiten an das TextAttribut +BOOL SwFmtCharFmt::GetInfo( SfxPoolItem& rInfo ) const +{ + return pTxtAttr ? pTxtAttr->GetInfo( rInfo ) : FALSE; +} +BOOL SwFmtCharFmt::QueryValue( uno::Any& rVal, BYTE ) const +{ + String sCharFmtName; + if(GetCharFmt()) + SwStyleNameMapper::FillProgName(GetCharFmt()->GetName(), sCharFmtName, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT, sal_True ); + rVal <<= OUString( sCharFmtName ); + return TRUE; +} +BOOL SwFmtCharFmt::PutValue( const uno::Any& , BYTE ) +{ + DBG_ERROR("Zeichenvorlage kann mit PutValue nicht gesetzt werden!"); + return FALSE; +} + +/************************************************************************* +|* +|* class SwFmtAutoFmt +|* Beschreibung +|* Ersterstellung AMA 12.05.06 +|* Letzte Aenderung AMA 12.05.06 +|* +*************************************************************************/ + +SwFmtAutoFmt::SwFmtAutoFmt( USHORT nInitWhich ) + : SfxPoolItem( nInitWhich ) +{ +} + +SwFmtAutoFmt::SwFmtAutoFmt( const SwFmtAutoFmt& rAttr ) + : SfxPoolItem( rAttr.Which() ), mpHandle( rAttr.mpHandle ) +{ +} + +SwFmtAutoFmt::~SwFmtAutoFmt() +{ +} + +int SwFmtAutoFmt::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "different attributes" ); + return mpHandle == ((SwFmtAutoFmt&)rAttr).mpHandle; +} + +SfxPoolItem* SwFmtAutoFmt::Clone( SfxItemPool* ) const +{ + return new SwFmtAutoFmt( *this ); +} + +BOOL SwFmtAutoFmt::QueryValue( uno::Any& rVal, BYTE ) const +{ + String sCharFmtName = StylePool::nameOf( mpHandle ); + rVal <<= OUString( sCharFmtName ); + return TRUE; +} + +BOOL SwFmtAutoFmt::PutValue( const uno::Any& , BYTE ) +{ + //the format is not renameable via API + return FALSE; +} + +/************************************************************************* +|* +|* class SwFmtINetFmt +|* Beschreibung +|* Ersterstellung AMA 02.08.96 +|* Letzte Aenderung AMA 02.08.96 +|* +*************************************************************************/ + +SwFmtINetFmt::SwFmtINetFmt() + : SfxPoolItem( RES_TXTATR_INETFMT ), + pMacroTbl( 0 ), + pTxtAttr( 0 ), + nINetId( 0 ), + nVisitedId( 0 ) +{} + +SwFmtINetFmt::SwFmtINetFmt( const XubString& rURL, const XubString& rTarget ) + : SfxPoolItem( RES_TXTATR_INETFMT ), + aURL( rURL ), + aTargetFrame( rTarget ), + pMacroTbl( 0 ), + pTxtAttr( 0 ), + nINetId( 0 ), + nVisitedId( 0 ) +{ +} + +SwFmtINetFmt::SwFmtINetFmt( const SwFmtINetFmt& rAttr ) + : SfxPoolItem( RES_TXTATR_INETFMT ), + aURL( rAttr.GetValue() ), + aTargetFrame( rAttr.aTargetFrame ), + aINetFmt( rAttr.aINetFmt ), + aVisitedFmt( rAttr.aVisitedFmt ), + aName( rAttr.aName ), + pMacroTbl( 0 ), + pTxtAttr( 0 ), + nINetId( rAttr.nINetId ), + nVisitedId( rAttr.nVisitedId ) +{ + if( rAttr.GetMacroTbl() ) + pMacroTbl = new SvxMacroTableDtor( *rAttr.GetMacroTbl() ); +} + +SwFmtINetFmt::~SwFmtINetFmt() +{ + delete pMacroTbl; +} + + + +int SwFmtINetFmt::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + BOOL bRet = SfxPoolItem::operator==( (SfxPoolItem&) rAttr ) + && aURL == ((SwFmtINetFmt&)rAttr).aURL + && aName == ((SwFmtINetFmt&)rAttr).aName + && aTargetFrame == ((SwFmtINetFmt&)rAttr).aTargetFrame + && aINetFmt == ((SwFmtINetFmt&)rAttr).aINetFmt + && aVisitedFmt == ((SwFmtINetFmt&)rAttr).aVisitedFmt + && nINetId == ((SwFmtINetFmt&)rAttr).nINetId + && nVisitedId == ((SwFmtINetFmt&)rAttr).nVisitedId; + + if( !bRet ) + return FALSE; + + const SvxMacroTableDtor* pOther = ((SwFmtINetFmt&)rAttr).pMacroTbl; + if( !pMacroTbl ) + return ( !pOther || !pOther->Count() ); + if( !pOther ) + return 0 == pMacroTbl->Count(); + + const SvxMacroTableDtor& rOwn = *pMacroTbl; + const SvxMacroTableDtor& rOther = *pOther; + + // Anzahl unterschiedlich => auf jeden Fall ungleich + if( rOwn.Count() != rOther.Count() ) + return FALSE; + + // einzeln vergleichen; wegen Performance ist die Reihenfolge wichtig + for( USHORT nNo = 0; nNo < rOwn.Count(); ++nNo ) + { + const SvxMacro *pOwnMac = rOwn.GetObject(nNo); + const SvxMacro *pOtherMac = rOther.GetObject(nNo); + if ( rOwn.GetKey(pOwnMac) != rOther.GetKey(pOtherMac) || + pOwnMac->GetLibName() != pOtherMac->GetLibName() || + pOwnMac->GetMacName() != pOtherMac->GetMacName() ) + return FALSE; + } + return TRUE; +} + + + +SfxPoolItem* SwFmtINetFmt::Clone( SfxItemPool* ) const +{ + return new SwFmtINetFmt( *this ); +} + + + +void SwFmtINetFmt::SetMacroTbl( const SvxMacroTableDtor* pNewTbl ) +{ + if( pNewTbl ) + { + if( pMacroTbl ) + *pMacroTbl = *pNewTbl; + else + pMacroTbl = new SvxMacroTableDtor( *pNewTbl ); + } + else if( pMacroTbl ) + delete pMacroTbl, pMacroTbl = 0; +} + + + +void SwFmtINetFmt::SetMacro( USHORT nEvent, const SvxMacro& rMacro ) +{ + if( !pMacroTbl ) + pMacroTbl = new SvxMacroTableDtor; + + SvxMacro *pOldMacro; + if( 0 != ( pOldMacro = pMacroTbl->Get( nEvent )) ) + { + delete pOldMacro; + pMacroTbl->Replace( nEvent, new SvxMacro( rMacro ) ); + } + else + pMacroTbl->Insert( nEvent, new SvxMacro( rMacro ) ); +} + + + +const SvxMacro* SwFmtINetFmt::GetMacro( USHORT nEvent ) const +{ + const SvxMacro* pRet = 0; + if( pMacroTbl && pMacroTbl->IsKeyValid( nEvent ) ) + pRet = pMacroTbl->Get( nEvent ); + return pRet; +} + + + +BOOL SwFmtINetFmt::QueryValue( uno::Any& rVal, BYTE nMemberId ) const +{ + BOOL bRet = TRUE; + XubString sVal; + nMemberId &= ~CONVERT_TWIPS; + switch(nMemberId) + { + case MID_URL_URL: + sVal = aURL; + break; + case MID_URL_TARGET: + sVal = aTargetFrame; + break; + case MID_URL_HYPERLINKNAME: + sVal = aName; + break; + case MID_URL_VISITED_FMT: + sVal = aVisitedFmt; + if( !sVal.Len() && nVisitedId != 0 ) + SwStyleNameMapper::FillUIName( nVisitedId, sVal ); + if( sVal.Len() ) + SwStyleNameMapper::FillProgName( sVal, sVal, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT, sal_True ); + break; + case MID_URL_UNVISITED_FMT: + sVal = aINetFmt; + if( !sVal.Len() && nINetId != 0 ) + SwStyleNameMapper::FillUIName( nINetId, sVal ); + if( sVal.Len() ) + SwStyleNameMapper::FillProgName( sVal, sVal, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT, sal_True ); + break; + case MID_URL_HYPERLINKEVENTS: + { + // create (and return) event descriptor + SwHyperlinkEventDescriptor* pEvents = + new SwHyperlinkEventDescriptor(); + pEvents->copyMacrosFromINetFmt(*this); + uno::Reference<container::XNameReplace> xNameReplace(pEvents); + + // all others return a string; so we just set rVal here and exit + rVal <<= xNameReplace; + return bRet; + } + default: + break; + } + rVal <<= OUString(sVal); + return bRet; +} +BOOL SwFmtINetFmt::PutValue( const uno::Any& rVal, BYTE nMemberId ) +{ + BOOL bRet = TRUE; + nMemberId &= ~CONVERT_TWIPS; + + // all properties except HyperlinkEvents are of type string, hence + // we treat HyperlinkEvents specially + if (MID_URL_HYPERLINKEVENTS == nMemberId) + { + uno::Reference<container::XNameReplace> xReplace; + rVal >>= xReplace; + if (xReplace.is()) + { + // Create hyperlink event descriptor. Then copy events + // from argument into descriptor. Then copy events from + // the descriptor into the format. + SwHyperlinkEventDescriptor* pEvents = new SwHyperlinkEventDescriptor(); + uno::Reference< lang::XServiceInfo> xHold = pEvents; + pEvents->copyMacrosFromNameReplace(xReplace); + pEvents->copyMacrosIntoINetFmt(*this); + } + else + { + // wrong type! + bRet = FALSE; + } + } + else + { + // all string properties: + if(rVal.getValueType() != ::getCppuType((rtl::OUString*)0)) + return FALSE; + XubString sVal = *(rtl::OUString*)rVal.getValue(); + switch(nMemberId) + { + case MID_URL_URL: + aURL = sVal; + break; + case MID_URL_TARGET: + aTargetFrame = sVal; + break; + case MID_URL_HYPERLINKNAME: + aName = sVal; + break; + case MID_URL_VISITED_FMT: + { + String aString; + SwStyleNameMapper::FillUIName( sVal, aString, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT, sal_True ); + aVisitedFmt = OUString ( aString ); + nVisitedId = SwStyleNameMapper::GetPoolIdFromUIName( aVisitedFmt, + nsSwGetPoolIdFromName::GET_POOLID_CHRFMT ); + } + break; + case MID_URL_UNVISITED_FMT: + { + String aString; + SwStyleNameMapper::FillUIName( sVal, aString, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT, sal_True ); + aINetFmt = OUString ( aString ); + nINetId = SwStyleNameMapper::GetPoolIdFromUIName( aINetFmt, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT ); + } + break; + default: + bRet = FALSE; + } + } + return bRet; +} + + +/************************************************************************* +|* class SwFmtRuby +*************************************************************************/ + +SwFmtRuby::SwFmtRuby( const String& rRubyTxt ) + : SfxPoolItem( RES_TXTATR_CJK_RUBY ), + sRubyTxt( rRubyTxt ), + pTxtAttr( 0 ), + nCharFmtId( 0 ), + nPosition( 0 ), + nAdjustment( 0 ) +{ +} + +SwFmtRuby::SwFmtRuby( const SwFmtRuby& rAttr ) + : SfxPoolItem( RES_TXTATR_CJK_RUBY ), + sRubyTxt( rAttr.sRubyTxt ), + sCharFmtName( rAttr.sCharFmtName ), + pTxtAttr( 0 ), + nCharFmtId( rAttr.nCharFmtId), + nPosition( rAttr.nPosition ), + nAdjustment( rAttr.nAdjustment ) +{ +} + +SwFmtRuby::~SwFmtRuby() +{ +} + +SwFmtRuby& SwFmtRuby::operator=( const SwFmtRuby& rAttr ) +{ + sRubyTxt = rAttr.sRubyTxt; + sCharFmtName = rAttr.sCharFmtName; + nCharFmtId = rAttr.nCharFmtId; + nPosition = rAttr.nPosition; + nAdjustment = rAttr.nAdjustment; + pTxtAttr = 0; + return *this; +} + +int SwFmtRuby::operator==( const SfxPoolItem& rAttr ) const +{ + ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" ); + return sRubyTxt == ((SwFmtRuby&)rAttr).sRubyTxt && + sCharFmtName == ((SwFmtRuby&)rAttr).sCharFmtName && + nCharFmtId == ((SwFmtRuby&)rAttr).nCharFmtId && + nPosition == ((SwFmtRuby&)rAttr).nPosition && + nAdjustment == ((SwFmtRuby&)rAttr).nAdjustment; +} + +SfxPoolItem* SwFmtRuby::Clone( SfxItemPool* ) const +{ + return new SwFmtRuby( *this ); +} + +BOOL SwFmtRuby::QueryValue( uno::Any& rVal, + BYTE nMemberId ) const +{ + BOOL bRet = TRUE; + nMemberId &= ~CONVERT_TWIPS; + switch( nMemberId ) + { + case MID_RUBY_TEXT: rVal <<= (OUString)sRubyTxt; break; + case MID_RUBY_ADJUST: rVal <<= (sal_Int16)nAdjustment; break; + case MID_RUBY_CHARSTYLE: + { + String aString; + SwStyleNameMapper::FillProgName(sCharFmtName, aString, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT, sal_True ); + rVal <<= OUString ( aString ); + } + break; + case MID_RUBY_ABOVE: + { + sal_Bool bAbove = !nPosition; + rVal.setValue(&bAbove, ::getBooleanCppuType()); + } + break; + default: + bRet = FALSE; + } + return bRet; +} +BOOL SwFmtRuby::PutValue( const uno::Any& rVal, + BYTE nMemberId ) +{ + BOOL bRet = TRUE; + nMemberId &= ~CONVERT_TWIPS; + switch( nMemberId ) + { + case MID_RUBY_TEXT: + { + OUString sTmp; + bRet = rVal >>= sTmp; + sRubyTxt = sTmp; + } + break; + case MID_RUBY_ADJUST: + { + sal_Int16 nSet = 0; + rVal >>= nSet; + if(nSet >= 0 && nSet <= text::RubyAdjust_INDENT_BLOCK) + nAdjustment = nSet; + else + bRet = sal_False; + } + break; + case MID_RUBY_ABOVE: + { + const uno::Type& rType = ::getBooleanCppuType(); + if(rVal.hasValue() && rVal.getValueType() == rType) + { + sal_Bool bAbove = *(sal_Bool*)rVal.getValue(); + nPosition = bAbove ? 0 : 1; + } + } + break; + case MID_RUBY_CHARSTYLE: + { + OUString sTmp; + bRet = rVal >>= sTmp; + if(bRet) + sCharFmtName = SwStyleNameMapper::GetUIName(sTmp, nsSwGetPoolIdFromName::GET_POOLID_CHRFMT ); + } + break; + default: + bRet = FALSE; + } + return bRet; +} + + +/************************************************************************* + class SwFmtMeta + ************************************************************************/ + +SwFmtMeta * SwFmtMeta::CreatePoolDefault(const USHORT i_nWhich) +{ + return new SwFmtMeta(i_nWhich); +} + +SwFmtMeta::SwFmtMeta(const USHORT i_nWhich) + : SfxPoolItem( i_nWhich ) + , m_pMeta() + , m_pTxtAttr( 0 ) +{ + ASSERT((RES_TXTATR_META == i_nWhich) || (RES_TXTATR_METAFIELD == i_nWhich), + "ERROR: SwFmtMeta: invalid which id!"); +} + +SwFmtMeta::SwFmtMeta( ::boost::shared_ptr< ::sw::Meta > const & i_pMeta, + const USHORT i_nWhich ) + : SfxPoolItem( i_nWhich ) + , m_pMeta( i_pMeta ) + , m_pTxtAttr( 0 ) +{ + ASSERT((RES_TXTATR_META == i_nWhich) || (RES_TXTATR_METAFIELD == i_nWhich), + "ERROR: SwFmtMeta: invalid which id!"); + ASSERT(m_pMeta, "SwFmtMeta: no Meta ?"); + // DO NOT call m_pMeta->SetFmtMeta(this) here; only from SetTxtAttr! +} + +SwFmtMeta::~SwFmtMeta() +{ + if (m_pMeta && (m_pMeta->GetFmtMeta() == this)) + { + m_pMeta->SetFmtMeta(0); + } +} + +int SwFmtMeta::operator==( const SfxPoolItem & i_rOther ) const +{ + ASSERT( SfxPoolItem::operator==( i_rOther ), "i just copied this assert" ); + return SfxPoolItem::operator==( i_rOther ) + && (m_pMeta == static_cast<SwFmtMeta const &>( i_rOther ).m_pMeta); +} + +SfxPoolItem * SwFmtMeta::Clone( SfxItemPool * /*pPool*/ ) const +{ + // if this is indeed a copy, then DoCopy must be called later! + return (m_pMeta) // #i105148# pool default may be cloned also! + ? new SwFmtMeta( m_pMeta, Which() ) : new SwFmtMeta( Which() ); +} + +void SwFmtMeta::SetTxtAttr(SwTxtMeta * const i_pTxtAttr) +{ + ASSERT(!(m_pTxtAttr && i_pTxtAttr), + "SwFmtMeta::SetTxtAttr: already has text attribute?"); + ASSERT( m_pTxtAttr || i_pTxtAttr , + "SwFmtMeta::SetTxtAttr: no attribute to remove?"); + m_pTxtAttr = i_pTxtAttr; + ASSERT(m_pMeta, "inserted SwFmtMeta has no sw::Meta?"); + // the sw::Meta must be able to find the current text attribute! + if (i_pTxtAttr && m_pMeta) + { + m_pMeta->SetFmtMeta(this); + } +} + +void SwFmtMeta::NotifyChangeTxtNode(SwTxtNode *const pTxtNode) +{ + // N.B.: do not reset m_pTxtAttr here: see call in nodes.cxx, + // where the hint is not deleted! + ASSERT(m_pMeta, "NotifyRemoval: no meta ?"); + if (m_pMeta) + { + if (!pTxtNode) + { + SwPtrMsgPoolItem aMsgHint( RES_REMOVE_UNO_OBJECT, + &static_cast<SwModify&>(*m_pMeta) ); // cast to base class! + m_pMeta->Modify(&aMsgHint, &aMsgHint); + } + else + { // do not call Modify, that would call SwXMeta::Modify! + m_pMeta->NotifyChangeTxtNode(); + } + } +} + +// UGLY: this really awful method fixes up an inconsistent state, +// and if it is not called when copying, total chaos will undoubtedly ensue +void SwFmtMeta::DoCopy(SwFmtMeta & rOriginalMeta) +{ + ASSERT(m_pMeta, "DoCopy called for SwFmtMeta with no sw::Meta?"); + if (m_pMeta) + { + const ::boost::shared_ptr< ::sw::Meta> pOriginal( m_pMeta ); + // UGLY: original sw::Meta now points at _this_ due to being already + // inserted via MakeTxtAttr! so fix it up to point at the original item + // (maybe would be better to tell MakeTxtAttr that it creates a copy?) + pOriginal->SetFmtMeta(&rOriginalMeta); + // force pOriginal to register in original text node! + pOriginal->NotifyChangeTxtNode(); + if (RES_TXTATR_META == Which()) + { + m_pMeta.reset( new ::sw::Meta(this) ); + } + else + { + ::sw::MetaField *const pMetaField( + static_cast< ::sw::MetaField* >(pOriginal.get())); + SwDoc * const pTargetDoc( GetTxtAttr()->GetTxtNode()->GetDoc() ); + m_pMeta = pTargetDoc->GetMetaFieldManager().makeMetaField( this, + pMetaField->m_nNumberFormat, pMetaField->IsFixedLanguage() ); + } + // this cannot be done in Clone: a Clone is not necessarily a copy! + m_pMeta->RegisterAsCopyOf(*pOriginal); + // force copy Meta to register in target text node! + m_pMeta->NotifyChangeTxtNode(); + } +} + + +namespace sw { + +/************************************************************************* + class sw::Meta + ************************************************************************/ + +Meta::Meta(SwFmtMeta * const i_pFmt) + : ::sfx2::Metadatable() + , SwModify() + , m_pFmt( i_pFmt ) +{ +} + +Meta::~Meta() +{ +} + +SwTxtMeta * Meta::GetTxtAttr() const +{ + return (m_pFmt) ? m_pFmt->GetTxtAttr() : 0; +} + +SwTxtNode * Meta::GetTxtNode() const +{ + SwTxtMeta * const pTxtAttr( GetTxtAttr() ); + return (pTxtAttr) ? pTxtAttr->GetTxtNode() : 0; +} + +void Meta::NotifyChangeTxtNode() +{ + SwTxtNode * const pTxtNode( GetTxtNode() ); + if (pTxtNode && (GetRegisteredIn() != pTxtNode)) + { + pTxtNode->Add(this); + } + else if (!pTxtNode && GetRegisteredIn()) + { + const_cast<SwModify *>(GetRegisteredIn())->Remove(this); + } +} + +// SwClient +void Meta::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew ) +{ + NotifyChangeTxtNode(); + SwModify::Modify(pOld, pNew); + if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which())) + { // invalidate cached uno object + SetXMeta(uno::Reference<rdf::XMetadatable>(0)); + } +} + +// sw::Metadatable +::sfx2::IXmlIdRegistry& Meta::GetRegistry() +{ + SwTxtNode * const pTxtNode( GetTxtNode() ); + // GetRegistry may only be called on a meta that is actually in the + // document, which means it has a pointer to its text node + OSL_ENSURE(pTxtNode, "ERROR: GetRegistry: no text node?"); + if (!pTxtNode) + throw uno::RuntimeException(); + return pTxtNode->GetRegistry(); +} + +bool Meta::IsInClipboard() const +{ + const SwTxtNode * const pTxtNode( GetTxtNode() ); +// no text node: in UNDO OSL_ENSURE(pTxtNode, "IsInClipboard: no text node?"); + return (pTxtNode) ? pTxtNode->IsInClipboard() : false; +} + +bool Meta::IsInUndo() const +{ + const SwTxtNode * const pTxtNode( GetTxtNode() ); +// no text node: in UNDO OSL_ENSURE(pTxtNode, "IsInUndo: no text node?"); + return (pTxtNode) ? pTxtNode->IsInUndo() : true; +} + +bool Meta::IsInContent() const +{ + const SwTxtNode * const pTxtNode( GetTxtNode() ); + OSL_ENSURE(pTxtNode, "IsInContent: no text node?"); + return (pTxtNode) ? pTxtNode->IsInContent() : true; +} + +::com::sun::star::uno::Reference< ::com::sun::star::rdf::XMetadatable > +Meta::MakeUnoObject() +{ + return SwXMeta::CreateXMeta(*this); +} + +/************************************************************************* + class sw::MetaField + ************************************************************************/ + +MetaField::MetaField(SwFmtMeta * const i_pFmt, + const sal_uInt32 nNumberFormat, const bool bIsFixedLanguage) + : Meta(i_pFmt) + , m_nNumberFormat( nNumberFormat ) + , m_bIsFixedLanguage( bIsFixedLanguage ) +{ +} + +void MetaField::GetPrefixAndSuffix( + ::rtl::OUString *const o_pPrefix, ::rtl::OUString *const o_pSuffix) +{ + try + { + const uno::Reference<rdf::XMetadatable> xMetaField( MakeUnoObject() ); + OSL_ENSURE(dynamic_cast<SwXMetaField*>(xMetaField.get()), + "GetPrefixAndSuffix: no SwXMetaField?"); + if (xMetaField.is()) + { + SwTxtNode * const pTxtNode( GetTxtNode() ); + SwDocShell const * const pShell(pTxtNode->GetDoc()->GetDocShell()); + const uno::Reference<frame::XModel> xModel( + (pShell) ? pShell->GetModel() : 0, uno::UNO_SET_THROW); + getPrefixAndSuffix(xModel, xMetaField, o_pPrefix, o_pSuffix); + } + } catch (uno::Exception) { + OSL_ENSURE(false, "exception?"); + } +} + +sal_uInt32 MetaField::GetNumberFormat(::rtl::OUString const & rContent) const +{ + //TODO: this probably lacks treatment for some special cases + sal_uInt32 nNumberFormat( m_nNumberFormat ); + SwTxtNode * const pTxtNode( GetTxtNode() ); + if (pTxtNode) + { + SvNumberFormatter *const pNumberFormatter( + pTxtNode->GetDoc()->GetNumberFormatter() ); + double number; + (void) pNumberFormatter->IsNumberFormat( + rContent, nNumberFormat, number ); + } + return nNumberFormat; +} + +void MetaField::SetNumberFormat(sal_uInt32 nNumberFormat) +{ + // effectively, the member is only a default: + // GetNumberFormat checks if the text actually conforms + m_nNumberFormat = nNumberFormat; +} + + +/************************************************************************* + class sw::MetaFieldManager + ************************************************************************/ + + +MetaFieldManager::MetaFieldManager() +{ +} + +::boost::shared_ptr<MetaField> +MetaFieldManager::makeMetaField(SwFmtMeta * const i_pFmt, + const sal_uInt32 nNumberFormat, const bool bIsFixedLanguage) +{ + const ::boost::shared_ptr<MetaField> pMetaField( + new MetaField(i_pFmt, nNumberFormat, bIsFixedLanguage) ); + m_MetaFields.push_back(pMetaField); + return pMetaField; +} + +struct IsInUndo +{ + bool operator()(::boost::weak_ptr<MetaField> const & pMetaField) { + return pMetaField.lock()->IsInUndo(); + } +}; + +struct MakeUnoObject +{ + uno::Reference<text::XTextField> + operator()(::boost::weak_ptr<MetaField> const & pMetaField) { + return uno::Reference<text::XTextField>( + pMetaField.lock()->MakeUnoObject(), uno::UNO_QUERY); + } +}; + +::std::vector< uno::Reference<text::XTextField> > +MetaFieldManager::getMetaFields() +{ + // erase deleted fields + const MetaFieldList_t::iterator iter( + ::std::remove_if(m_MetaFields.begin(), m_MetaFields.end(), + ::boost::bind(&::boost::weak_ptr<MetaField>::expired, _1))); + m_MetaFields.erase(iter, m_MetaFields.end()); + // filter out fields in UNDO + MetaFieldList_t filtered(m_MetaFields.size()); + const MetaFieldList_t::iterator iter2( + ::std::remove_copy_if(m_MetaFields.begin(), m_MetaFields.end(), + filtered.begin(), IsInUndo())); + filtered.erase(iter2, filtered.end()); + // create uno objects + ::std::vector< uno::Reference<text::XTextField> > ret(filtered.size()); + ::std::transform(filtered.begin(), filtered.end(), ret.begin(), + MakeUnoObject()); + return ret; +} + +} // namespace sw + diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx new file mode 100644 index 000000000000..7b08a0cb87e4 --- /dev/null +++ b/sw/source/core/txtnode/fntcache.cxx @@ -0,0 +1,2910 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#ifndef _OUTDEV_HXX //autogen +#include <vcl/outdev.hxx> +#endif +#ifndef _PRINT_HXX //autogen +#include <vcl/print.hxx> +#endif +#include <vcl/lineinfo.hxx> +#ifndef _METRIC_HXX //autogen +#include <vcl/metric.hxx> +#endif +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#ifndef _COM_SUN_STAR_I18N_CHARACTERITERATORMODE_HDL_ +#include <com/sun/star/i18n/CharacterIteratorMode.hdl> +#endif +#ifndef _COM_SUN_STAR_I18N_WORDTYPE_HDL +#include <com/sun/star/i18n/WordType.hdl> +#endif +#include <breakit.hxx> +#include <viewsh.hxx> // Bildschirmabgleich +#include <viewopt.hxx> // Bildschirmabgleich abschalten, ViewOption +#include <fntcache.hxx> +#include <IDocumentSettingAccess.hxx> +#include <swfont.hxx> // CH_BLANK + CH_BULLET +#include <wrong.hxx> +#include "dbg_lay.hxx" +#include <txtfrm.hxx> // SwTxtFrm +#include <pagefrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <scriptinfo.hxx> +#include <editeng/brshitem.hxx> +#include <tools/shl.hxx> +#include <swmodule.hxx> +#include <accessibilityoptions.hxx> +#include <svtools/accessibilityoptions.hxx> +#include <doc.hxx> +#include <editeng/fhgtitem.hxx> +#include <docsh.hxx> +#ifndef _POOLFMT_HRC +#include <poolfmt.hrc> +#endif + +using namespace ::com::sun::star; + +// globale Variablen, werden in FntCache.Hxx bekanntgegeben +// Der FontCache wird in TxtInit.Cxx _TXTINIT erzeugt und in _TXTEXIT geloescht +SwFntCache *pFntCache = NULL; +// Letzter Font, der durch ChgFntCache eingestellt wurde. +SwFntObj *pLastFont = NULL; +// Die "MagicNumber", die den Fonts zur Identifizierung verpasst wird +BYTE* pMagicNo = NULL; + +Color *pWaveCol = 0; + +long SwFntObj::nPixWidth; +MapMode* SwFntObj::pPixMap = NULL; +OutputDevice* SwFntObj::pPixOut = NULL; + +extern USHORT UnMapDirection( USHORT nDir, const BOOL bVertFormat ); +USHORT GetDefaultFontHeight( SwDrawTextInfo &rInf ) +{ + SwDocShell* pDocShell = rInf.GetShell()->GetDoc()->GetDocShell(); + SfxStyleSheetBasePool* pBasePool = pDocShell->GetStyleSheetPool(); + + String aString(SW_RES(STR_POOLCOLL_STANDARD)); + + SfxStyleSheetBase* pStyle = pBasePool->Find( aString, (SfxStyleFamily)SFX_STYLE_FAMILY_PARA ); + SfxItemSet& aTmpSet = pStyle->GetItemSet(); + SvxFontHeightItem &aDefaultFontItem = (SvxFontHeightItem&)aTmpSet.Get(RES_CHRATR_CJK_FONTSIZE); + return (USHORT)aDefaultFontItem.GetHeight(); +} + + + +/************************************************************************* +|* +|* SwFntCache::Flush() +|* +|* Ersterstellung AMA 16. Dez. 94 +|* Letzte Aenderung AMA 16. Dez. 94 +|* +|*************************************************************************/ + +void SwFntCache::Flush( ) +{ + if ( pLastFont ) + { + pLastFont->Unlock(); + pLastFont = NULL; + } + SwCache::Flush( ); +} + +/************************************************************************* +|* +|* SwFntObj::SwFntObj(), ~SwFntObj() +|* +|* Ersterstellung AMA 7. Nov. 94 +|* Letzte Aenderung AMA 7. Nov. 94 +|* +|*************************************************************************/ + +SwFntObj::SwFntObj( const SwSubFont &rFont, const void *pOwn, ViewShell *pSh ) : + SwCacheObj( (void*)pOwn ), + aFont( rFont ), + pScrFont( NULL ), + pPrtFont( &aFont ), + pPrinter( NULL ), + nPropWidth( rFont.GetPropWidth() ) +{ + nZoom = pSh ? pSh->GetViewOptions()->GetZoom() : USHRT_MAX; + nGuessedLeading = USHRT_MAX; + nExtLeading = USHRT_MAX; + nPrtAscent = USHRT_MAX; + nPrtHeight = USHRT_MAX; + bPaintBlank = ( UNDERLINE_NONE != aFont.GetUnderline() + || UNDERLINE_NONE != aFont.GetOverline() + || STRIKEOUT_NONE != aFont.GetStrikeout() ) + && !aFont.IsWordLineMode(); + aFont.SetLanguage(rFont.GetLanguage()); +} + +SwFntObj::~SwFntObj() +{ + if ( pScrFont != pPrtFont ) + delete pScrFont; + if ( pPrtFont != &aFont ) + delete pPrtFont; +} + +void SwFntObj::CreatePrtFont( const OutputDevice& rPrt ) +{ + if ( nPropWidth != 100 && pPrinter != &rPrt ) + { + if( pScrFont != pPrtFont ) + delete pScrFont; + if( pPrtFont != &aFont ) + delete pPrtFont; + + const Font aOldFnt( rPrt.GetFont() ); + ((OutputDevice&)rPrt).SetFont( aFont ); + const FontMetric aWinMet( rPrt.GetFontMetric() ); + ((OutputDevice&)rPrt).SetFont( aOldFnt ); + long nWidth = ( aWinMet.GetSize().Width() * nPropWidth ) / 100; + + if( !nWidth ) + ++nWidth; + pPrtFont = new Font( aFont ); + pPrtFont->SetSize( Size( nWidth, aFont.GetSize().Height() ) ); + pScrFont = NULL; + } +} + +/************************************************************************* + * + * bool lcl_IsFontAdjustNecessary( rOutDev, rRefDev ) + * + * returns whether we have to adjust the output font to resemble + * the formatting font + * + * _Not_ necessary if + * + * 1. RefDef == OutDev (text formatting, online layout...) + * 2. PDF export from online layout + * 3. Prospect/PagePreview pringing + * + *************************************************************************/ + +bool lcl_IsFontAdjustNecessary( const OutputDevice& rOutDev, + const OutputDevice& rRefDev ) +{ + return &rRefDev != &rOutDev && + OUTDEV_WINDOW != rRefDev.GetOutDevType() && + ( OUTDEV_PRINTER != rRefDev.GetOutDevType() || + OUTDEV_PRINTER != rOutDev.GetOutDevType() ); +} + +struct CalcLinePosData +{ + SwDrawTextInfo& rInf; + Font& rFont; + xub_StrLen nCnt; + const BOOL bSwitchH2V; + const BOOL bSwitchL2R; + long nHalfSpace; + sal_Int32* pKernArray; + const BOOL bBidiPor; + + CalcLinePosData( SwDrawTextInfo& _rInf, Font& _rFont, + xub_StrLen _nCnt, const BOOL _bSwitchH2V, const BOOL _bSwitchL2R, + long _nHalfSpace, sal_Int32* _pKernArray, const BOOL _bBidiPor) : + rInf( _rInf ), + rFont( _rFont ), + nCnt( _nCnt ), + bSwitchH2V( _bSwitchH2V ), + bSwitchL2R( _bSwitchL2R ), + nHalfSpace( _nHalfSpace ), + pKernArray( _pKernArray ), + bBidiPor( _bBidiPor ) + { + } +}; + +/** Function: lcl_calcLinePos + + Computes the start and end position of an underline. This function is called + from the DrawText-method (for underlining misspelled words or smarttag terms). +*/ + +void lcl_calcLinePos( const CalcLinePosData &rData, + Point &rStart, Point &rEnd, xub_StrLen nStart, xub_StrLen nWrLen ) +{ + long nBlank = 0; + const xub_StrLen nEnd = nStart + nWrLen; + const long nTmpSpaceAdd = rData.rInf.GetSpace() / SPACING_PRECISION_FACTOR; + + if ( nEnd < rData.nCnt + && CH_BLANK == rData.rInf.GetText().GetChar( rData.rInf.GetIdx() + nEnd ) ) + { + if( nEnd + 1 == rData.nCnt ) + nBlank -= nTmpSpaceAdd; + else + nBlank -= rData.nHalfSpace; + } + + // determine start, end and length of wave line + sal_Int32 nKernStart = nStart ? rData.pKernArray[ USHORT( nStart - 1 ) ] : 0; + sal_Int32 nKernEnd = rData.pKernArray[ USHORT( nEnd - 1 ) ]; + + USHORT nDir = rData.bBidiPor ? 1800 : + UnMapDirection( rData.rFont.GetOrientation(), rData.bSwitchH2V ); + + switch ( nDir ) + { + case 0 : + rStart.X() += nKernStart; + rEnd.X() = nBlank + rData.rInf.GetPos().X() + nKernEnd; + rEnd.Y() = rData.rInf.GetPos().Y(); + break; + case 900 : + rStart.Y() -= nKernStart; + rEnd.X() = rData.rInf.GetPos().X(); + rEnd.Y() = nBlank + rData.rInf.GetPos().Y() - nKernEnd; + break; + case 1800 : + rStart.X() -= nKernStart; + rEnd.X() = rData.rInf.GetPos().X() - nKernEnd - nBlank; + rEnd.Y() = rData.rInf.GetPos().Y(); + break; + case 2700 : + rStart.Y() += nKernStart; + rEnd.X() = rData.rInf.GetPos().X(); + rEnd.Y() = nBlank + rData.rInf.GetPos().Y() + nKernEnd; + break; + } + + if ( rData.bSwitchL2R ) + { + rData.rInf.GetFrm()->SwitchLTRtoRTL( rStart ); + rData.rInf.GetFrm()->SwitchLTRtoRTL( rEnd ); + } + + if ( rData.bSwitchH2V ) + { + rData.rInf.GetFrm()->SwitchHorizontalToVertical( rStart ); + rData.rInf.GetFrm()->SwitchHorizontalToVertical( rEnd ); + } +} + +/************************************************************************* + * + * USHORT SwFntObj::GetFontAscent( const OutputDevice& rOut ) + * + * Ersterstellung AMA 7. Nov. 94 + * Letzte Aenderung AMA 7. Nov. 94 + * + * Beschreibung: liefern den Ascent des Fonts auf dem + * gewuenschten Outputdevice zurueck, ggf. muss der Bildschirmfont erst + * erzeugt werden. + *************************************************************************/ + +USHORT SwFntObj::GetFontAscent( const ViewShell *pSh, const OutputDevice& rOut ) +{ + USHORT nRet = 0; + const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut; + + if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) ) + { + CreateScrFont( *pSh, rOut ); + ASSERT( USHRT_MAX != nScrAscent, "nScrAscent is going berzerk" ) + nRet = nScrAscent; + } + else + { + if ( nPrtAscent == USHRT_MAX ) // DruckerAscent noch nicht bekannt? + { + CreatePrtFont( rOut ); + const Font aOldFnt( rRefDev.GetFont() ); + ((OutputDevice&)rRefDev).SetFont( *pPrtFont ); + const FontMetric aOutMet( rRefDev.GetFontMetric() ); + nPrtAscent = (USHORT) aOutMet.GetAscent(); + ( (OutputDevice&)rRefDev).SetFont( aOldFnt ); + } + + nRet = nPrtAscent; + } + +#if !defined(MACOSX) // #i89844# extleading is below the line for Mac + // TODO: move extleading below the line for all platforms too + nRet += GetFontLeading( pSh, rRefDev ); +#endif + + ASSERT( USHRT_MAX != nRet, "GetFontAscent returned USHRT_MAX" ) + return nRet; +} + +/************************************************************************* + * + * USHORT SwFntObj::GetFontHeight( const OutputDevice* pOut ) + * + * Ersterstellung AMA 7. Nov. 94 + * Letzte Aenderung AMA 7. Nov. 94 + * + * Beschreibung: liefern die H?he des Fonts auf dem + * gewuenschten Outputdevice zurueck, ggf. muss der Bildschirmfont erst + * erzeugt werden. + *************************************************************************/ + +USHORT SwFntObj::GetFontHeight( const ViewShell* pSh, const OutputDevice& rOut ) +{ + USHORT nRet = 0; + const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut; + + if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) ) + { + CreateScrFont( *pSh, rOut ); + ASSERT( USHRT_MAX != nScrHeight, "nScrHeight is going berzerk" ) + nRet = nScrHeight + GetFontLeading( pSh, rRefDev ); + } + else + { + if ( nPrtHeight == USHRT_MAX ) // PrinterHeight noch nicht bekannt? + { + CreatePrtFont( rOut ); + const Font aOldFnt( rRefDev.GetFont() ); + ((OutputDevice&)rRefDev).SetFont( *pPrtFont ); + nPrtHeight = static_cast<USHORT>(rRefDev.GetTextHeight()); + +#if OSL_DEBUG_LEVEL > 1 + // Check if vcl did not change the meading of GetTextHeight + const FontMetric aOutMet( rRefDev.GetFontMetric() ); + long nTmpPrtHeight = (USHORT)aOutMet.GetAscent() + aOutMet.GetDescent(); + (void) nTmpPrtHeight; + // #i106098#: do not compare with == here due to rounding error + ASSERT( abs(nTmpPrtHeight - nPrtHeight) < 3, + "GetTextHeight != Ascent + Descent" ); +#endif + + ((OutputDevice&)rRefDev).SetFont( aOldFnt ); + } + + nRet = nPrtHeight + GetFontLeading( pSh, rRefDev ); + } + + ASSERT( USHRT_MAX != nRet, "GetFontHeight returned USHRT_MAX" ) + return nRet; +} + +USHORT SwFntObj::GetFontLeading( const ViewShell *pSh, const OutputDevice& rOut ) +{ + USHORT nRet = 0; + + if ( pSh ) + { + if ( USHRT_MAX == nGuessedLeading || USHRT_MAX == nExtLeading ) + { + const Font aOldFnt( rOut.GetFont() ); + ((OutputDevice&)rOut).SetFont( *pPrtFont ); + const FontMetric aMet( rOut.GetFontMetric() ); + ((OutputDevice&)rOut).SetFont( aOldFnt ); + bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet(); + GuessLeading( *pSh, aMet ); + nExtLeading = static_cast<USHORT>(aMet.GetExtLeading()); + } + + const IDocumentSettingAccess& rIDSA = *pSh->getIDocumentSettingAccess(); + const bool bBrowse = ( pSh->GetWin() && + rIDSA.get(IDocumentSettingAccess::BROWSE_MODE) && + !pSh->GetViewOptions()->IsPrtFormat() ); + + if ( !bBrowse && rIDSA.get(IDocumentSettingAccess::ADD_EXT_LEADING) ) + nRet = nExtLeading; + else + nRet = nGuessedLeading; + } + + ASSERT( USHRT_MAX != nRet, "GetFontLeading returned USHRT_MAX" ) + return nRet; +} + + +/************************************************************************* + * + * SwFntObj::CreateScrFont( const ViewShell& rSh, const OutputDevice& rOut ) + * + * Ersterstellung AMA 7. Nov. 94 + * Letzte Aenderung AMA 7. Nov. 94 + * + * pOut is the output device, not the reference device + * + *************************************************************************/ + +void SwFntObj::CreateScrFont( const ViewShell& rSh, const OutputDevice& rOut ) +{ +static sal_Char __READONLY_DATA sStandardString[] = "Dies ist der Teststring"; + + if ( pScrFont ) + return; + + // any changes to the output device are reset at the end of the function + OutputDevice* pOut = (OutputDevice*)&rOut; + + // Save old font + Font aOldOutFont( pOut->GetFont() ); + + nScrHeight = USHRT_MAX; + + // Condition for output font / refdev font adjustment + OutputDevice* pPrt = &rSh.GetRefDev(); + + if( !rSh.GetWin() || + !rSh.getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE) || + rSh.GetViewOptions()->IsPrtFormat() ) + { + // After CreatePrtFont pPrtFont is the font which is actually used + // by the reference device + CreatePrtFont( *pPrt ); + pPrinter = pPrt; + + // save old reference device font + Font aOldPrtFnt( pPrt->GetFont() ); + + // set the font used at the reference device at the reference device + // and the output device + pPrt->SetFont( *pPrtFont ); + pOut->SetFont( *pPrtFont ); + + // This should be the default for pScrFont. + pScrFont = pPrtFont; + + FontMetric aMet = pPrt->GetFontMetric( ); + + bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet(); + + if ( USHRT_MAX == nGuessedLeading ) + GuessLeading( rSh, aMet ); + + if ( USHRT_MAX == nExtLeading ) + nExtLeading = static_cast<USHORT>(aMet.GetExtLeading()); + +#if OSL_DEBUG_LEVEL > 1 + const XubString aDbgTxt1( pPrtFont->GetName() ); + const XubString aDbgTxt2( aMet.GetName() ); +#endif + + if ( aMet.IsDeviceFont( ) ) + { + if ( (RTL_TEXTENCODING_DONTKNOW == pPrtFont->GetCharSet() || + FAMILY_DONTKNOW == pPrtFont->GetFamily() || + PITCH_DONTKNOW == pPrtFont->GetPitch() ) && + (RTL_TEXTENCODING_DONTKNOW == aMet.GetCharSet() || + FAMILY_DONTKNOW == aMet.GetFamily() || + PITCH_DONTKNOW == aMet.GetPitch() ) ) + { + // Das folgende ist teuer, aber selten: ein unbekannter Font + // kann vom Drucker nicht vernuenftig zugeordnet werden. Dann + // nehmen wir eben das Mapping des Bildschirms in Anspruch und + // setzen den Familyname, Charset und Pitch wie dort. Dieser + // Font wird nun nochmals auf dem Drucker eingestellt. + Font aFnt1 = pOut->GetFontMetric(); + Font aFnt2( *pPrtFont ); + + if (RTL_TEXTENCODING_DONTKNOW == pPrtFont->GetCharSet()) + aFnt2.SetCharSet( aFnt1.GetCharSet() ); + if (FAMILY_DONTKNOW == pPrtFont->GetFamily()) + aFnt2.SetFamily( aFnt1.GetFamily() ); + if (PITCH_DONTKNOW == pPrtFont->GetPitch()) + aFnt2.SetPitch( aFnt1.GetPitch() ); + + pPrt->SetFont( aFnt2 ); + aMet = pPrt->GetFontMetric( ); + } + + const XubString aStandardStr( sStandardString, + RTL_TEXTENCODING_MS_1252 ); + + // This is the reference width + const long nOWidth = pPrt->GetTextWidth( aStandardStr ); + + // Let's have a look what's the difference to the width + // calculated for the output device using the font set at the + // reference device + long nSWidth = nOWidth - pOut->GetTextWidth( aStandardStr ); + nScrHeight = (USHORT) pOut->GetTextHeight(); + + // Um Aerger mit dem Generic Printer aus dem Wege zu gehen. + if( aMet.GetSize().Height() ) + { + BOOL bScrSymbol = FALSE; + CharSet ePrtChSet = aMet.GetCharSet(); + // NoSymbol bedeutet, dass der Drucker sich fuer einen + // Nicht-Symbol-Font entschieden hat. + BOOL bNoSymbol = ( RTL_TEXTENCODING_DONTKNOW != ePrtChSet && + RTL_TEXTENCODING_SYMBOL != ePrtChSet ); + if ( bNoSymbol ) + bScrSymbol = RTL_TEXTENCODING_SYMBOL == + pOut->GetFontMetric().GetCharSet(); + Size aTmp( aMet.GetSize() ); + + if( aTmp.Width() && !pPrtFont->GetSize().Width() ) + { + aTmp.Width() = 0; + aMet.SetSize( aTmp ); + } + + // Now we set the metrics used at the reference device at the + // output device + pOut->SetFont( aMet ); + + if( bNoSymbol && ( bScrSymbol != ( RTL_TEXTENCODING_SYMBOL == + pOut->GetFontMetric().GetCharSet() ) ) ) + { + // Hier landen wir, wenn der Drucker keinen Symbolfont waehlt, + // aber genau einer der beiden Screenfonts ein Symbolfont ist. + // Wir nehmen dann eben den anderen. + if ( bScrSymbol ) + pScrFont = new Font( aMet ); // mit Abgleich + else + pOut->SetFont( *pPrtFont ); // ohne Abgleich + } + else + { + // Let's have a look what's the difference to the width + // calculated for the output device using the metrics set at + // the reference device + long nPWidth = nOWidth - pOut->GetTextWidth( aStandardStr ); + + // We prefer smaller fonts + BYTE nNeg = 0; + if ( nSWidth<0 ) { nSWidth *= -2; nNeg = 1; } + if ( nPWidth<0 ) { nPWidth *= -2; nNeg |= 2; } + + // nSWidth = Difference between string width on reference device + // and string width on output device with user font set. + // nPWidth = Difference between string width on reference device + // and string width on output device with metric obtained + // from reference device. + // We prefer to take the font with the smaller deviation, + // exception: keep the original font unless the deviation + // is really bad (at least 3%) + // Since the test string is neither localized nor has a high resemblance + // of the "real text for this font" a higher deviation is reasonable + + if ( (nSWidth <= nPWidth) + || (nSWidth * 32 <= nOWidth ) ) + { + // No adjustment, we take the same font for the output + // device like for the reference device + pOut->SetFont( *pPrtFont ); + pScrFont = pPrtFont; + nPWidth = nSWidth; + nNeg &= 1; + } + else + { + // The metrics give a better result. So we build + // a new font for the output device based on the + // metrics used at the reference device + pScrFont = new Font( aMet ); // mit Abgleich + nSWidth = nPWidth; + nNeg &= 2; + } + + // + // now pScrFont is set to the better font and this should + // be set at the output device + // + + // we still have to check if the choosed font is not to wide + if( nNeg && nOWidth ) + { + nPWidth *= 100; + nPWidth /= nOWidth; + + // if the screen font is too wide, we try to reduce + // the font height and get a smaller one + if( nPWidth > 25 ) + { + if( nPWidth > 80 ) + nPWidth = 80; + nPWidth = 100 - nPWidth/4; + Size aTmpSize = pScrFont->GetSize(); + aTmpSize.Height() *= nPWidth; + aTmpSize.Height() /= 100; + if( aTmpSize.Width() ) + { + aTmpSize.Width() *= nPWidth; + aTmpSize.Width() /= 100; + } + Font *pNew = new Font( *pScrFont ); + pNew->SetSize( aTmpSize ); + pOut->SetFont( *pNew ); + nPWidth = nOWidth - + pOut->GetTextWidth( aStandardStr ); + if( nPWidth < 0 ) { nPWidth *= -2; } + if( nPWidth < nSWidth ) + { + if( pScrFont != pPrtFont ) + delete pScrFont; + pScrFont = pNew; + } + else + { + delete pNew; + pOut->SetFont( *pScrFont ); + } + } + } + } + } + } + + // reset the original reference device font + pPrt->SetFont( aOldPrtFnt ); + } + else + { + bSymbol = RTL_TEXTENCODING_SYMBOL == aFont.GetCharSet(); + if ( nGuessedLeading == USHRT_MAX ) + nGuessedLeading = 0; + + // no external leading in browse mode + if ( nExtLeading == USHRT_MAX ) + nExtLeading = 0; + + pScrFont = pPrtFont; + } + + // Zoomfaktor ueberpruefen, z.B. wg. PrtOle2 beim Speichern + { + // Sollte der Zoomfaktor des OutputDevices nicht mit dem der View- + // Options uebereinstimmen, so darf dieser Font nicht gecacht + // werden, deshalb wird der Zoomfaktor auf einen "ungueltigen" Wert + // gesetzt. + long nTmp; + if( pOut->GetMapMode().GetScaleX().IsValid() && + pOut->GetMapMode().GetScaleY().IsValid() && + pOut->GetMapMode().GetScaleX() == pOut->GetMapMode().GetScaleY() ) + { + nTmp = ( 100 * pOut->GetMapMode().GetScaleX().GetNumerator() ) / + pOut->GetMapMode().GetScaleX().GetDenominator(); + } + else + nTmp = 0; + if( nTmp != nZoom ) + nZoom = USHRT_MAX - 1; + } + + nScrAscent = (USHORT)pOut->GetFontMetric().GetAscent(); + if ( USHRT_MAX == nScrHeight ) + nScrHeight = (USHORT)pOut->GetTextHeight(); + + // reset original output device font + pOut->SetFont( aOldOutFont ); +} + + +void SwFntObj::GuessLeading( const ViewShell& +#if defined(WNT) || defined(WIN) || defined(PM2) + rSh +#endif + , const FontMetric& rMet ) +{ + // If leading >= 5, this seems to be enough leading. + // Nothing has to be done. + if ( rMet.GetIntLeading() >= 5 ) + { + nGuessedLeading = 0; + return; + } + +#if defined(WNT) || defined(WIN) || defined(PM2) + OutputDevice *pWin = rSh.GetWin() ? + rSh.GetWin() : + GetpApp()->GetDefaultDevice(); + if ( pWin ) + { + MapMode aTmpMap( MAP_TWIP ); + MapMode aOldMap = pWin->GetMapMode( ); + pWin->SetMapMode( aTmpMap ); + const Font aOldFnt( pWin->GetFont() ); + pWin->SetFont( *pPrtFont ); + const FontMetric aWinMet( pWin->GetFontMetric() ); + const USHORT nWinHeight = USHORT( aWinMet.GetSize().Height() ); + if( pPrtFont->GetName().Search( aWinMet.GetName() ) < USHRT_MAX ) + { + // Wenn das Leading auf dem Window auch 0 ist, dann + // muss es auch so bleiben (vgl. StarMath!). + long nTmpLeading = (long)aWinMet.GetIntLeading(); + // einen Versuch haben wir noch wg. 31003: + if( nTmpLeading <= 0 ) + { + pWin->SetFont( rMet ); + nTmpLeading = (long)pWin->GetFontMetric().GetIntLeading(); + if( nTmpLeading < 0 ) + nGuessedLeading = 0; + else + nGuessedLeading = USHORT(nTmpLeading); + } + else + { + nGuessedLeading = USHORT(nTmpLeading); + // Manta-Hack #50153#: + // Wer beim Leading luegt, luegt moeglicherweise auch beim + // Ascent/Descent, deshalb wird hier ggf. der Font ein wenig + // tiefergelegt, ohne dabei seine Hoehe zu aendern. + long nDiff = Min( rMet.GetDescent() - aWinMet.GetDescent(), + aWinMet.GetAscent() - rMet.GetAscent() - nTmpLeading ); + if( nDiff > 0 ) + { + ASSERT( nPrtAscent < USHRT_MAX, "GuessLeading: PrtAscent-Fault" ); + if ( nPrtAscent < USHRT_MAX ) + nPrtAscent = nPrtAscent + (USHORT)(( 2 * nDiff ) / 5); + } + } + } + else + { + // Wenn alle Stricke reissen, nehmen wir 15% der + // Hoehe, ein von CL empirisch ermittelter Wert. + nGuessedLeading = (nWinHeight * 15) / 100; + } + pWin->SetFont( aOldFnt ); + pWin->SetMapMode( aOldMap ); + } + else +#endif + nGuessedLeading = 0; +} + +/************************************************************************* + * + * void SwFntObj::SetDeviceFont( const OutputDevice *pOut ), + * + * Ersterstellung AMA 7. Nov. 94 + * Letzte Aenderung AMA 7. Nov. 94 + * + * Beschreibung: stellt den Font am gewuenschten OutputDevice ein, + * am Bildschirm muss eventuell erst den Abgleich durchgefuehrt werden. + * + *************************************************************************/ + +void SwFntObj::SetDevFont( const ViewShell *pSh, OutputDevice& rOut ) +{ + const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut; + + if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) ) + { + CreateScrFont( *pSh, rOut ); + if( !GetScrFont()->IsSameInstance( rOut.GetFont() ) ) + rOut.SetFont( *pScrFont ); + if( pPrinter && ( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) ) ) + pPrinter->SetFont( *pPrtFont ); + } + else + { + CreatePrtFont( rOut ); + if( !pPrtFont->IsSameInstance( rOut.GetFont() ) ) + rOut.SetFont( *pPrtFont ); + } + + // Here, we actually do not need the leading values, but by calling + // GetFontLeading() we assure that the values are calculated for later use. + GetFontLeading( pSh, rRefDev ); +} + +#define WRONG_SHOW_MIN 5 +#define WRONG_SHOW_SMALL 11 +#define WRONG_SHOW_MEDIUM 15 + +/************************************************************************* + * + * void SwFntObj::DrawText( ... ) + * + * Ersterstellung AMA 16. Dez. 94 + * Letzte Aenderung AMA 16. Dez. 94 + * + * Beschreibung: Textausgabe + * auf dem Bildschirm => DrawTextArray + * auf dem Drucker, !Kerning => DrawText + * auf dem Drucker + Kerning => DrawStretchText + * + *************************************************************************/ + +BYTE lcl_WhichPunctuation( xub_Unicode cChar ) +{ + if ( ( cChar < 0x3001 || cChar > 0x3002 ) && + ( cChar < 0x3008 || cChar > 0x3011 ) && + ( cChar < 0x3014 || cChar > 0x301F ) && + 0xFF62 != cChar && 0xFF63 != cChar ) + // no punctuation + return SwScriptInfo::NONE; + else if ( 0x3001 == cChar || 0x3002 == cChar || + 0x3009 == cChar || 0x300B == cChar || + 0x300D == cChar || 0x300F == cChar || + 0x3011 == cChar || 0x3015 == cChar || + 0x3017 == cChar || 0x3019 == cChar || + 0x301B == cChar || 0x301E == cChar || + 0x301F == cChar || 0xFF63 == cChar ) + // right punctuation + return SwScriptInfo::SPECIAL_RIGHT; + + return SwScriptInfo::SPECIAL_LEFT; +} + +static sal_Bool lcl_IsMonoSpaceFont( const OutputDevice& rOut ) +{ + const String aStr1( xub_Unicode( 0x3008 ) ); + const String aStr2( xub_Unicode( 0x307C ) ); + const long nWidth1 = rOut.GetTextWidth( aStr1 ); + const long nWidth2 = rOut.GetTextWidth( aStr2 ); + return nWidth1 == nWidth2; +} + +// ER 09.07.95 20:34 +// mit -Ox Optimierung stuerzt's unter win95 ab +// JP 12.07.95: unter WNT auch (i386); Alpha ?? +// global optimization off +#ifdef _MSC_VER +#pragma optimize("g",off) +#endif + +/* This helper structure (SwForbidden) contains the already marked parts of the string + to avoid double lines (e.g grammar + spell check error) */ + +typedef std::vector< std::pair< xub_StrLen, xub_StrLen > > SwForbidden; + +static void lcl_DrawLineForWrongListData( + SwForbidden &rForbidden, + const SwDrawTextInfo &rInf, + const SwWrongList *pWList, + const CalcLinePosData &rCalcLinePosData, + const Size &rPrtFontSize ) +{ + if (!pWList) + return; + + xub_StrLen nStart = rInf.GetIdx(); + xub_StrLen nWrLen = rInf.GetLen(); + + // check if respective data is available in the current text range + if (pWList->Check( nStart, nWrLen )) + { + // get line color to use... + Color aLineColor; + if (pWList == rInf.GetWrong()) // ... for spell checking + aLineColor = SwViewOption::GetSpellColor(); + else if (pWList == rInf.GetGrammarCheck()) // ... for grammar checking + // currently there is no specific color for grammar check errors available in the configuration + aLineColor = Color( COL_LIGHTBLUE ); + else if (pWList == rInf.GetSmartTags()) // ... for smart tags + aLineColor = SwViewOption::GetSmarttagColor(); + + long nHght = rInf.GetOut().LogicToPixel( rPrtFontSize ).Height(); + + // Draw wavy lines for spell and grammar errors only if font is large enough. + // Lines for smart tags will always be drawn. + if (pWList == rInf.GetSmartTags() || WRONG_SHOW_MIN < nHght) + { + SwForbidden::iterator pIter = rForbidden.begin(); + if (rInf.GetOut().GetConnectMetaFile()) + rInf.GetOut().Push(); + + const Color aCol( rInf.GetOut().GetLineColor() ); + const BOOL bColSave = aCol != aLineColor; + if (bColSave) + rInf.GetOut().SetLineColor( aLineColor ); + + // iterate over all ranges stored in the respective SwWrongList + do + { + nStart = nStart - rInf.GetIdx(); + + const xub_StrLen nEnd = nStart + nWrLen; + xub_StrLen nNext = nStart; + while( nNext < nEnd ) + { + while( pIter != rForbidden.end() && pIter->second <= nNext ) + ++pIter; + xub_StrLen nNextStart = nNext; + xub_StrLen nNextEnd = nEnd; + if( pIter == rForbidden.end() || nNextEnd <= pIter->first ) + { + // No overlapping mark up found + std::pair< xub_StrLen, xub_StrLen > aNew; + aNew.first = nNextStart; + aNew.second = nNextEnd; + rForbidden.insert( pIter, aNew ); + pIter = rForbidden.begin(); + nNext = nEnd; + } + else + { + nNext = pIter->second; + if( nNextStart < pIter->first ) + { + nNextEnd = pIter->first; + pIter->first = nNextStart; + } + else + continue; + } + // determine line pos + Point aStart( rInf.GetPos() ); + Point aEnd; + lcl_calcLinePos( rCalcLinePosData, aStart, aEnd, nNextStart, nNextEnd - nNextStart ); + + // draw line for smart tags? + if (pWList == rInf.GetSmartTags()) + { + aStart.Y() +=30; + aEnd.Y() +=30; + + LineInfo aLineInfo( LINE_DASH ); + aLineInfo.SetDistance( 40 ); + aLineInfo.SetDashLen( 1 ); + aLineInfo.SetDashCount(1); + + rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo ); + } + else // draw wavy lines for spell or grammar errors + { + // get wavy line type to use + USHORT nWave = + WRONG_SHOW_MEDIUM < nHght ? WAVE_NORMAL : + ( WRONG_SHOW_SMALL < nHght ? WAVE_SMALL : WAVE_FLAT ); + + rInf.GetOut().DrawWaveLine( aStart, aEnd, nWave ); + } + } + + nStart = nEnd + rInf.GetIdx(); + nWrLen = rInf.GetIdx() + rInf.GetLen() - nStart; + } + while (nWrLen && pWList->Check( nStart, nWrLen )); + + if (bColSave) + rInf.GetOut().SetLineColor( aCol ); + + if (rInf.GetOut().GetConnectMetaFile()) + rInf.GetOut().Pop(); + } + } +} + + +void SwFntObj::DrawText( SwDrawTextInfo &rInf ) +{ + ASSERT( rInf.GetShell(), "SwFntObj::DrawText without shell" ) + + OutputDevice& rRefDev = rInf.GetShell()->GetRefDev(); + OutputDevice* pWin = rInf.GetShell()->GetWin(); + const IDocumentSettingAccess* pIDSA = rInf.GetShell()->getIDocumentSettingAccess(); + + // true if pOut is the printer and the printer has been used for formatting + const BOOL bPrt = OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() && + OUTDEV_PRINTER == rRefDev.GetOutDevType(); + const BOOL bBrowse = ( pWin && + pIDSA->get(IDocumentSettingAccess::BROWSE_MODE) && + !rInf.GetShell()->GetViewOptions()->IsPrtFormat() && + !rInf.GetBullet() && + ( rInf.GetSpace() || !rInf.GetKern() ) && + !rInf.GetWrong() && + !rInf.GetGrammarCheck() && + !rInf.GetSmartTags() && + !rInf.GetGreyWave() ); + + // bDirectPrint indicates that we can enter the branch which calls + // the DrawText functions instead of calling the DrawTextArray functions + const BOOL bDirectPrint = bPrt || bBrowse; + + // Condition for output font / refdev font adjustment + const BOOL bUseScrFont = + lcl_IsFontAdjustNecessary( rInf.GetOut(), rRefDev ); + + Font* pTmpFont = bUseScrFont ? pScrFont : pPrtFont; + + // + // bDirectPrint and bUseScrFont should have these values: + // + // Outdev / RefDef | Printer | VirtPrinter | Window + // ---------------------------------------------------- + // Printer | 1 - 0 | 0 - 1 | - + // ---------------------------------------------------- + // VirtPrinter/PDF | 0 - 1 | 0 - 1 | - + // ---------------------------------------------------- + // Window/VirtWindow| 0 - 1 | 0 - 1 | 1 - 0 + // + // Exception: During painting of a Writer OLE object, we do not have + // a window. Therefore bUseSrcFont is always 0 in this case. + // + +#ifdef DBG_UTIL + + const BOOL bNoAdjust = bPrt || + ( pWin && + pIDSA->get(IDocumentSettingAccess::BROWSE_MODE) && + !rInf.GetShell()->GetViewOptions()->IsPrtFormat() ); + + if ( OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() ) + { + // Printer output + if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 1 && bUseScrFont == 0, "Outdev Check failed" ) + } + else if ( OUTDEV_VIRDEV == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" ) + } + else + { + ASSERT( sal_False, "Outdev Check failed" ) + } + } + else if ( OUTDEV_VIRDEV == rInf.GetOut().GetOutDevType() && ! pWin ) + { + // PDF export + if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" ) + } + else if ( OUTDEV_VIRDEV == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" ) + } + else + { + ASSERT( sal_False, "Outdev Check failed" ) + } + } + else if ( OUTDEV_WINDOW == rInf.GetOut().GetOutDevType() || + ( OUTDEV_VIRDEV == rInf.GetOut().GetOutDevType() && pWin ) ) + { + // Window or virtual window + if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" ) + } + else if ( OUTDEV_VIRDEV == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 0 && bUseScrFont == 1, "Outdev Check failed" ) + } + else if ( OUTDEV_WINDOW == rRefDev.GetOutDevType() ) + { + ASSERT( bNoAdjust == 1 && bUseScrFont == 0, "Outdev Check failed" ) + } + else + { + ASSERT( sal_False, "Outdev Check failed" ) + } + } + else + { + ASSERT( sal_False, "Outdev Check failed" ) + } + +#endif + + // robust: better use the printer font instead of using no font at all + ASSERT( pTmpFont, "No screen or printer font?" ); + if ( ! pTmpFont ) + pTmpFont = pPrtFont; + + // HACK: UNDERLINE_WAVE darf nicht mehr missbraucht werden, daher + // wird die graue Wellenlinie des ExtendedAttributSets zunaechst + // in der Fontfarbe erscheinen. + + const BOOL bSwitchH2V = rInf.GetFrm() && rInf.GetFrm()->IsVertical(); + const BOOL bSwitchL2R = rInf.GetFrm() && rInf.GetFrm()->IsRightToLeft() && + ! rInf.IsIgnoreFrmRTL(); + const ULONG nMode = rInf.GetOut().GetLayoutMode(); + const BOOL bBidiPor = ( bSwitchL2R != + ( 0 != ( TEXT_LAYOUT_BIDI_RTL & nMode ) ) ); + + // be sure to have the correct layout mode at the printer + if ( pPrinter ) + { + pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() ); + pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() ); + } + + Point aPos( rInf.GetPos() ); + if( !bPrt ) + { + if( rInf.GetpOut() != pPixOut || rInf.GetOut().GetMapMode() != *pPixMap ) + { + *pPixMap = rInf.GetOut().GetMapMode(); + pPixOut = rInf.GetpOut(); + Size aTmp( 1, 1 ); + nPixWidth = rInf.GetOut().PixelToLogic( aTmp ).Width(); + } + + aPos.X() += rInf.GetFrm()->IsRightToLeft() ? 0 : nPixWidth; + } + + Color aOldColor( pTmpFont->GetColor() ); + sal_Bool bChgColor = rInf.ApplyAutoColor( pTmpFont ); + if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *pTmpFont ); + if ( bChgColor ) + pTmpFont->SetColor( aOldColor ); + + if ( STRING_LEN == rInf.GetLen() ) + rInf.SetLen( rInf.GetText().Len() ); + + + // + // ASIAN LINE AND CHARACTER GRID MODE START: snap to characters + // + + if ( rInf.GetFrm() && rInf.SnapToGrid() && rInf.GetFont() && + SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars()) + { + //for textgrid refactor + //const USHORT nGridWidth = pGrid->GetBaseHeight(); + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const USHORT nGridWidth = GETGRIDWIDTH(pGrid, pDoc); + sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()]; + + if ( pPrinter ) + pPrinter->GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + else + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + + long nWidthPerChar = pKernArray[ rInf.GetLen() - 1 ] / rInf.GetLen(); + + const ULONG i = nWidthPerChar ? + ( nWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + nWidthPerChar = i * nGridWidth; + + // position of first character, we take the printer position + long nCharWidth = pKernArray[ 0 ]; + ULONG nHalfWidth = nWidthPerChar / 2; + + long nNextFix; + + // punctuation characters are not centered + xub_Unicode cChar = rInf.GetText().GetChar( rInf.GetIdx() ); + BYTE nType = lcl_WhichPunctuation( cChar ); + switch ( nType ) + { + case SwScriptInfo::NONE : + aPos.X() += ( nWidthPerChar - nCharWidth ) / 2; + nNextFix = nCharWidth / 2; + break; + case SwScriptInfo::SPECIAL_RIGHT : + nNextFix = nHalfWidth; + break; + default: + aPos.X() += nWidthPerChar - nCharWidth; + nNextFix = nCharWidth - nHalfWidth; + } + + // calculate offsets + for ( xub_StrLen j = 1; j < rInf.GetLen(); ++j ) + { + long nScr = pKernArray[ j ] - pKernArray[ j - 1 ]; + nNextFix += nWidthPerChar; + + // punctuation characters are not centered + cChar = rInf.GetText().GetChar( rInf.GetIdx() + j ); + nType = lcl_WhichPunctuation( cChar ); + switch ( nType ) + { + case SwScriptInfo::NONE : + pKernArray[ j - 1 ] = nNextFix - ( nScr / 2 ); + break; + case SwScriptInfo::SPECIAL_RIGHT : + pKernArray[ j - 1 ] = nNextFix - nHalfWidth; + break; + default: + pKernArray[ j - 1 ] = nNextFix + nHalfWidth - nScr; + } + } + + // the layout engine requires the total width of the output + pKernArray[ rInf.GetLen() - 1 ] = rInf.GetWidth() - + aPos.X() + rInf.GetPos().X() ; + + if ( bSwitchH2V ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), rInf.GetLen() ); + + delete[] pKernArray; + return; + } + } + + // For text grid refactor + // ASIAN LINE AND CHARACTER GRID MODE START: not snap to characters + // + if ( rInf.GetFrm() && rInf.SnapToGrid() && rInf.GetFont() && + SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + const USHORT nDefaultFontHeight = GetDefaultFontHeight( rInf ); + + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc); + if( SW_LATIN == rInf.GetFont()->GetActual() ) + nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2; + else + nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight; + + sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()]; + + if ( pPrinter ) + pPrinter->GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + else + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + if ( bSwitchH2V ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + if ( rInf.GetSpace() || rInf.GetKanaComp()) + { + long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR; + sal_Bool bSpecialJust = sal_False; + if ( rInf.GetFont() && rInf.GetLen() ) + { + const SwScriptInfo* pSI = rInf.GetScriptInfo(); + const BYTE nActual = rInf.GetFont()->GetActual(); + ///Kana Compression + if( SW_CJK == nActual && rInf.GetKanaComp() && + pSI && pSI->CountCompChg() && + lcl_IsMonoSpaceFont( *(rInf.GetpOut()) ) ) + { + pSI->Compress( pKernArray,rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), (USHORT)aFont.GetSize().Height(),&aPos ); + bSpecialJust = sal_True; + } + ///Asian Justification + if ( ( SW_CJK == nActual || SW_LATIN == nActual ) && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK ); + if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang) + { + long nSpaceSum = nSpaceAdd; + for ( USHORT nI = 0; nI < rInf.GetLen(); ++nI ) + { + pKernArray[ nI ] += nSpaceSum; + nSpaceSum += nSpaceAdd; + } + bSpecialJust = sal_True; + nSpaceAdd = 0; + } + } + long nGridAddSum = nGridWidthAdd; + for(xub_StrLen i = 0; i < rInf.GetLen(); i++,nGridAddSum += nGridWidthAdd ) + { + pKernArray[i] += nGridAddSum; + } + long nKernSum = rInf.GetKern(); + if ( bSpecialJust || rInf.GetKern() ) + { + for( xub_StrLen i = 0; i < rInf.GetLen(); i++, nKernSum += rInf.GetKern() ) + { + if ( CH_BLANK == rInf.GetText().GetChar(rInf.GetIdx()+i) ) + nKernSum += nSpaceAdd; + pKernArray[i] += nKernSum; + } + ///With through/uderstr. Grouped style requires a blank at the end + ///of a text edition special measures: + if( bPaintBlank && rInf.GetLen() && (CH_BLANK == + rInf.GetText().GetChar( rInf.GetIdx() + rInf.GetLen() - 1) ) ) + { + ///If it concerns a singular, underlined space acts, + ///we must spend two: + if( 1 == rInf.GetLen() ) + { + pKernArray[0] = rInf.GetWidth() + nSpaceAdd; + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), 1 ); + } + else + { + pKernArray[ rInf.GetLen() - 2] += nSpaceAdd; + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), rInf.GetLen() ); + } + } + else + { + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), rInf.GetLen() ); + } + } + else + { + Point aTmpPos( aPos ); + xub_StrLen i; + xub_StrLen j = 0; + long nSpaceSum = 0; + for( i = 0; i < rInf.GetLen(); i++ ) + { + if( CH_BLANK == rInf.GetText().GetChar( rInf.GetIdx() + i) ) + { + nSpaceSum += nSpaceAdd; + if( j < i) + rInf.GetOut().DrawText( aTmpPos, rInf.GetText(), + rInf.GetIdx() + j, i - j ); + j = i + 1; + pKernArray[i] = pKernArray[i] + nSpaceSum; + aTmpPos.X() = aPos.X() + pKernArray[ i ] + nKernSum; + } + } + if( j < i ) + rInf.GetOut().DrawText( aTmpPos, rInf.GetText(), + rInf.GetIdx() +j , i - j ); + } + } + } + else + { + //long nKernAdd = rInf.GetKern(); + long nKernAdd = 0; + long nGridAddSum = nGridWidthAdd + nKernAdd; + for(xub_StrLen i = 0; i < rInf.GetLen(); i++,nGridAddSum += nGridWidthAdd + nKernAdd ) + { + pKernArray[i] += nGridAddSum; + } + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), rInf.GetLen() ); + } + delete[] pKernArray; + return; + } + } + + // + // DIRECT PAINTING WITHOUT SCREEN ADJUSTMENT + // + + if ( bDirectPrint ) + { + const Fraction aTmp( 1, 1 ); + BOOL bStretch = rInf.GetWidth() && ( rInf.GetLen() > 1 ) && bPrt + && ( aTmp != rInf.GetOut().GetMapMode().GetScaleX() ); + + if ( bSwitchL2R ) + rInf.GetFrm()->SwitchLTRtoRTL( aPos ); + + if ( bSwitchH2V ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + + // In the good old days we used to have a simple DrawText if the + // output device is the printer. Now we need a DrawTextArray if + // 1. KanaCompression is enabled + // 2. Justified alignment + // Simple kerning is handled by DrawStretchText + if( rInf.GetSpace() || rInf.GetKanaComp() ) + { + sal_Int32 *pKernArray = new sal_Int32[ rInf.GetLen() ]; + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + + if( bStretch ) + { + xub_StrLen nZwi = rInf.GetLen() - 1; + long nDiff = rInf.GetWidth() - pKernArray[ nZwi ] + - rInf.GetLen() * rInf.GetKern(); + long nRest = nDiff % nZwi; + long nAdd; + if( nRest < 0 ) + { + nAdd = -1; + nRest += nZwi; + } + else + { + nAdd = +1; + nRest = nZwi - nRest; + } + nDiff /= nZwi; + long nSum = nDiff; + for( xub_StrLen i = 0; i < nZwi; ) + { + pKernArray[ i ] += nSum; + if( ++i == nRest ) + nDiff += nAdd; + nSum += nDiff; + } + } + + // + // Modify Array for special justifications + // + long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR; + sal_Bool bSpecialJust = sal_False; + + if ( rInf.GetFont() && rInf.GetLen() ) + { + const SwScriptInfo* pSI = rInf.GetScriptInfo(); + const BYTE nActual = rInf.GetFont()->GetActual(); + + // Kana Compression + if ( SW_CJK == nActual && rInf.GetKanaComp() && + pSI && pSI->CountCompChg() && + lcl_IsMonoSpaceFont( rInf.GetOut() ) ) + { + pSI->Compress( pKernArray, rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + (USHORT)aFont.GetSize().Height(), &aPos ); + bSpecialJust = sal_True; + } + + // Asian Justification + if ( SW_CJK == nActual && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK ); + + if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) + { + long nSpaceSum = nSpaceAdd; + for ( USHORT nI = 0; nI < rInf.GetLen(); ++nI ) + { + pKernArray[ nI ] += nSpaceSum; + nSpaceSum += nSpaceAdd; + } + + bSpecialJust = sal_True; + nSpaceAdd = 0; + } + } + + // Kashida Justification + if ( SW_CTL == nActual && nSpaceAdd ) + { + if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) ) + { + if ( pSI && pSI->CountKashida() && + pSI->KashidaJustify( pKernArray, 0, rInf.GetIdx(), + rInf.GetLen(), nSpaceAdd ) != STRING_LEN ) + { + bSpecialJust = sal_True; + nSpaceAdd = 0; + } + } + } + + // Thai Justification + if ( SW_CTL == nActual && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CTL ); + + if ( LANGUAGE_THAI == aLang ) + { + // Use rInf.GetSpace() because it has more precision than + // nSpaceAdd: + SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray, 0, + rInf.GetIdx(), rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); + + // adding space to blanks is already done + bSpecialJust = sal_True; + nSpaceAdd = 0; + } + } + } + + long nKernSum = rInf.GetKern(); + + if ( bStretch || bPaintBlank || rInf.GetKern() || bSpecialJust ) + { + for( xub_StrLen i = 0; i < rInf.GetLen(); i++, + nKernSum += rInf.GetKern() ) + { + if ( CH_BLANK == rInf.GetText().GetChar(rInf.GetIdx()+i) ) + nKernSum += nSpaceAdd; + pKernArray[i] += nKernSum; + } + + // Bei durch/unterstr. Blocksatz erfordert ein Blank am Ende + // einer Textausgabe besondere Massnahmen: + if( bPaintBlank && rInf.GetLen() && ( CH_BLANK == + rInf.GetText().GetChar( rInf.GetIdx()+rInf.GetLen()-1 ) ) ) + { + // Wenn es sich um ein singulaeres, unterstrichenes Space + // handelt, muessen wir zwei ausgeben: + if( 1 == rInf.GetLen() ) + { + pKernArray[0] = rInf.GetWidth() + nSpaceAdd; + + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), 1 ); + } + else + { + pKernArray[ rInf.GetLen() - 2 ] += nSpaceAdd; + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), rInf.GetLen() ); + } + } + else + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), rInf.GetLen() ); + } + else + { + Point aTmpPos( aPos ); + xub_StrLen j = 0; + xub_StrLen i; + for( i = 0; i < rInf.GetLen(); i++ ) + { + if( CH_BLANK == rInf.GetText().GetChar( rInf.GetIdx()+i ) ) + { + nKernSum += nSpaceAdd; + if( j < i ) + rInf.GetOut().DrawText( aTmpPos, rInf.GetText(), + rInf.GetIdx() + j, i - j ); + j = i + 1; + SwTwips nAdd = pKernArray[ i ] + nKernSum; + if ( ( TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_BIDI_RTL ) == nMode ) + nAdd *= -1; + aTmpPos.X() = aPos.X() + nAdd; + } + } + if( j < i ) + rInf.GetOut().DrawText( aTmpPos, rInf.GetText(), + rInf.GetIdx() + j, i - j ); + } + delete[] pKernArray; + } + else if( bStretch ) + { + long nTmpWidth = rInf.GetWidth(); + if( rInf.GetKern() && rInf.GetLen() && nTmpWidth > rInf.GetKern() ) + nTmpWidth -= rInf.GetKern(); + rInf.GetOut().DrawStretchText( aPos, nTmpWidth, + rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ); + } + else if( rInf.GetKern() ) + { + const long nTmpWidth = GetTextSize( rInf ).Width(); + + const Color aSaveColor( pTmpFont->GetColor() ); + const sal_Bool bColorChanged = rInf.ApplyAutoColor( pTmpFont ); + + if( bColorChanged ) + { + if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *pTmpFont ); + pTmpFont->SetColor( aSaveColor ); + } + + rInf.GetOut().DrawStretchText( aPos, (USHORT)nTmpWidth, + rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ); + } + else + rInf.GetOut().DrawText( aPos, rInf.GetText(), + rInf.GetIdx(), rInf.GetLen() ); + } + + // + // PAINTING WITH FORMATTING DEVICE/SCREEN ADJUSTMENT + // + + else + { + const String* pStr = &rInf.GetText(); + String aStr( aEmptyStr ); + BOOL bBullet = rInf.GetBullet(); + if( bSymbol ) + bBullet = FALSE; + sal_Int32 *pKernArray = new sal_Int32[ rInf.GetLen() ]; + CreateScrFont( *rInf.GetShell(), rInf.GetOut() ); + long nScrPos; + + // get screen array + sal_Int32* pScrArray = new sal_Int32[ rInf.GetLen() ]; + rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray, + rInf.GetIdx(), rInf.GetLen() ); + + // OLE: no printer available + // ASSERT( pPrinter, "DrawText needs pPrinter" ) + if ( pPrinter ) + { + // pTmpFont has already been set as current font for rInf.GetOut() + if ( pPrinter != rInf.GetpOut() || pTmpFont != pPrtFont ) + { + if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) ) + pPrinter->SetFont( *pPrtFont ); + } + pPrinter->GetTextArray( rInf.GetText(), pKernArray, rInf.GetIdx(), + rInf.GetLen() ); + } + else + { +// BOOL bRestore = FALSE; +// MapMode aOld( rInf.GetOut().GetMapMode() ); +// if( rInf.GetZoom().GetNumerator() && +// rInf.GetZoom() != aOld.GetScaleX() ) +// { +// MapMode aNew( aOld ); +// aNew.SetScaleX( rInf.GetZoom() ); +// aNew.SetScaleY( rInf.GetZoom() ); +// rInf.GetOut().SetMapMode( aNew ); +// bRestore = TRUE; +// } + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); +// if( bRestore ) +// rInf.GetOut().SetMapMode( aOld ); + } + + // + // Modify Printer and ScreenArrays for special justifications + // + long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR; + bool bNoHalfSpace = false; + + if ( rInf.GetFont() && rInf.GetLen() ) + { + const BYTE nActual = rInf.GetFont()->GetActual(); + const SwScriptInfo* pSI = rInf.GetScriptInfo(); + + // Kana Compression + if ( SW_CJK == nActual && rInf.GetKanaComp() && + pSI && pSI->CountCompChg() && + lcl_IsMonoSpaceFont( rInf.GetOut() ) ) + { + Point aTmpPos( aPos ); + pSI->Compress( pScrArray, rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + (USHORT)aFont.GetSize().Height(), &aTmpPos ); + pSI->Compress( pKernArray, rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + (USHORT)aFont.GetSize().Height(), &aPos ); + } + + // Asian Justification + if ( SW_CJK == nActual && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK ); + + if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) + { + long nSpaceSum = nSpaceAdd; + for ( USHORT nI = 0; nI < rInf.GetLen(); ++nI ) + { + pKernArray[ nI ] += nSpaceSum; + pScrArray[ nI ] += nSpaceSum; + nSpaceSum += nSpaceAdd; + } + + nSpaceAdd = 0; + } + } + + // Kashida Justification + if ( SW_CTL == nActual && nSpaceAdd ) + { + if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) ) + { + if ( pSI && pSI->CountKashida() && + pSI->KashidaJustify( pKernArray, pScrArray, rInf.GetIdx(), + rInf.GetLen(), nSpaceAdd ) != STRING_LEN ) + nSpaceAdd = 0; + else + bNoHalfSpace = true; + } + } + + // Thai Justification + if ( SW_CTL == nActual && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CTL ); + + if ( LANGUAGE_THAI == aLang ) + { + SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray, + pScrArray, rInf.GetIdx(), + rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); + + // adding space to blanks is already done + nSpaceAdd = 0; + } + } + } + + nScrPos = pScrArray[ 0 ]; + + if( bBullet ) + { + // !!! HACK !!! + // The Arabic layout engine requires some context of the string + // which should be painted. + xub_StrLen nCopyStart = rInf.GetIdx(); + if ( nCopyStart ) + --nCopyStart; + + xub_StrLen nCopyLen = rInf.GetLen(); + if ( nCopyStart + nCopyLen < rInf.GetText().Len() ) + ++nCopyLen; + + aStr = rInf.GetText().Copy( nCopyStart, nCopyLen ); + pStr = &aStr; + + for( xub_StrLen i = 0; i < aStr.Len(); ++i ) + if( CH_BLANK == aStr.GetChar( i ) ) + aStr.SetChar( i, CH_BULLET ); + } + + xub_StrLen nCnt = rInf.GetText().Len(); + if ( nCnt < rInf.GetIdx() ) + nCnt = 0; + else + nCnt = nCnt - rInf.GetIdx(); + nCnt = Min( nCnt, rInf.GetLen() ); + long nKernSum = rInf.GetKern(); + xub_Unicode cChPrev = rInf.GetText().GetChar( rInf.GetIdx() ); + + // Wenn es sich um ein singulaeres, unterstrichenes Space + // im Blocksatz handelt, muessen wir zwei ausgeben: + if ( ( nCnt == 1 ) && rInf.GetSpace() && ( cChPrev == CH_BLANK ) ) + { + pKernArray[0] = rInf.GetWidth() + + rInf.GetKern() + + ( rInf.GetSpace() / SPACING_PRECISION_FACTOR ); + + if ( bSwitchL2R ) + rInf.GetFrm()->SwitchLTRtoRTL( aPos ); + + if ( bSwitchH2V ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + + rInf.GetOut().DrawTextArray( aPos, rInf.GetText(), + pKernArray, rInf.GetIdx(), 1 ); + if( bBullet ) + rInf.GetOut().DrawTextArray( aPos, *pStr, pKernArray, + rInf.GetIdx() ? 1 : 0, 1 ); + } + else + { + xub_Unicode nCh; + + // Bei Pairkerning waechst der Printereinfluss auf die Positionierung + USHORT nMul = 3; + + if ( pPrtFont->GetKerning() ) + nMul = 1; + + const USHORT nDiv = nMul+1; + + // In nSpaceSum wird der durch Blocksatz auf die Spaces verteilte + // Zwischenraum aufsummiert. + // Die Spaces selbst werden im Normalfall in der Mitte des + // Zwischenraums positioniert, deshalb die nSpace/2-Mimik. + // Bei wortweiser Unterstreichung muessen sie am Anfang des + // Zwischenraums stehen, damit dieser nicht unterstrichen wird. + // Ein Space am Anfang oder am Ende des Textes muss allerdings + // vor bzw. hinter den kompletten Zwischenraum gesetzt werden, + // sonst wuerde das Durch-/Unterstreichen Luecken aufweisen. + long nSpaceSum = 0; + // in word line mode and for Arabic, we disable the half space trick: + const long nHalfSpace = pPrtFont->IsWordLineMode() || bNoHalfSpace ? 0 : nSpaceAdd / 2; + const long nOtherHalf = nSpaceAdd - nHalfSpace; + if ( nSpaceAdd && ( cChPrev == CH_BLANK ) ) + nSpaceSum = nHalfSpace; + for ( xub_StrLen i=1; i<nCnt; ++i,nKernSum += rInf.GetKern() ) + { + nCh = rInf.GetText().GetChar( rInf.GetIdx() + i ); + + ASSERT( pScrArray, "Where is the screen array?" ) + long nScr; + nScr = pScrArray[ i ] - pScrArray[ i - 1 ]; + + // Wenn vor uns ein (Ex-)SPACE ist, positionieren wir uns optimal, + // d.h. unseren rechten Rand auf die 100% Druckerposition, + // sind wir sogar selbst ein Ex-SPACE, so positionieren wir uns + // linksbuendig zur Druckerposition. + if ( nCh == CH_BLANK ) + { +#ifdef FONT_TEST_DEBUG + lcl_Pos( 3, nScrPos, nScr, pKernArray[i-1], pKernArray[i] ); +#else + nScrPos = pKernArray[i-1] + nScr; +#endif + if ( cChPrev == CH_BLANK ) + nSpaceSum += nOtherHalf; + if ( i + 1 == nCnt ) + nSpaceSum += nSpaceAdd; + else + nSpaceSum += nHalfSpace; + } + else + { + if ( cChPrev == CH_BLANK ) + { +#ifdef FONT_TEST_DEBUG + lcl_Pos( 6, nScrPos, nScr, pKernArray[i-1], pKernArray[i] ); +#else + nScrPos = pKernArray[i-1] + nScr; +#endif + // kein Pixel geht verloren: + nSpaceSum += nOtherHalf; + } + else if ( cChPrev == '-' ) +#ifdef FONT_TEST_DEBUG + lcl_Pos( 6, nScrPos, nScr, pKernArray[i-1], pKernArray[i] ); +#else + nScrPos = pKernArray[i-1] + nScr; +#endif + else + { +#ifdef FONT_TEST_DEBUG + lcl_Pos( 0, nScrPos, nScr, pKernArray[i-1], pKernArray[i] ); +#else + nScrPos += nScr; + nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv; +#endif + } + } + cChPrev = nCh; + pKernArray[i-1] = nScrPos - nScr + nKernSum + nSpaceSum; + // In word line mode and for Arabic, we disabled the half space trick. If a portion + // ends with a blank, the full nSpaceAdd value has been added to the character in + // front of the blank. This leads to painting artifacts, therefore we remove the + // nSpaceAdd value again: + if ( (bNoHalfSpace || pPrtFont->IsWordLineMode()) && i+1 == nCnt && nCh == CH_BLANK ) + pKernArray[i-1] = pKernArray[i-1] - nSpaceAdd; + } + + // the layout engine requires the total width of the output + pKernArray[ rInf.GetLen() - 1 ] += nKernSum + nSpaceSum; + + if( rInf.GetGreyWave() ) + { + if( rInf.GetLen() ) + { + long nHght = rInf.GetOut().LogicToPixel( + pPrtFont->GetSize() ).Height(); + if( WRONG_SHOW_MIN < nHght ) + { + if ( rInf.GetOut().GetConnectMetaFile() ) + rInf.GetOut().Push(); + + USHORT nWave = + WRONG_SHOW_MEDIUM < nHght ? WAVE_NORMAL : + ( WRONG_SHOW_SMALL < nHght ? WAVE_SMALL : + WAVE_FLAT ); + Color aCol( rInf.GetOut().GetLineColor() ); + BOOL bColSave = aCol != *pWaveCol; + if ( bColSave ) + rInf.GetOut().SetLineColor( *pWaveCol ); + + Point aEnd; + long nKernVal = pKernArray[ USHORT( rInf.GetLen() - 1 ) ]; + + USHORT nDir = bBidiPor ? + 1800 : + UnMapDirection( + GetFont()->GetOrientation(), + bSwitchH2V ); + + switch ( nDir ) + { + case 0 : + aEnd.X() = rInf.GetPos().X() + nKernVal; + aEnd.Y() = rInf.GetPos().Y(); + break; + case 900 : + aEnd.X() = rInf.GetPos().X(); + aEnd.Y() = rInf.GetPos().Y() - nKernVal; + break; + case 1800 : + aEnd.X() = rInf.GetPos().X() - nKernVal; + aEnd.Y() = rInf.GetPos().Y(); + break; + case 2700 : + aEnd.X() = rInf.GetPos().X(); + aEnd.Y() = rInf.GetPos().Y() + nKernVal; + break; + } + + Point aCurrPos( rInf.GetPos() ); + + if ( bSwitchL2R ) + { + rInf.GetFrm()->SwitchLTRtoRTL( aCurrPos ); + rInf.GetFrm()->SwitchLTRtoRTL( aEnd ); + } + + if ( bSwitchH2V ) + { + rInf.GetFrm()->SwitchHorizontalToVertical( aCurrPos ); + rInf.GetFrm()->SwitchHorizontalToVertical( aEnd ); + } + rInf.GetOut().DrawWaveLine( aCurrPos, aEnd, nWave ); + + if ( bColSave ) + rInf.GetOut().SetLineColor( aCol ); + + if ( rInf.GetOut().GetConnectMetaFile() ) + rInf.GetOut().Pop(); + } + } + } + else if( !bSymbol && rInf.GetLen() ) + { + // anything to do? + if (rInf.GetWrong() || rInf.GetGrammarCheck() || rInf.GetSmartTags()) + { + CalcLinePosData aCalcLinePosData(rInf, *GetFont(), + nCnt, bSwitchH2V, bSwitchL2R, + nHalfSpace, pKernArray, bBidiPor); + + SwForbidden aForbidden; + // draw line for smart tag data + lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetSmartTags(), aCalcLinePosData, Size() ); + // draw wave line for spell check errors + // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict. + // reason: some grammar errors can only be found if spelling errors are fixed, + // therefore we don't want the user to miss a spelling error. + lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetWrong(), aCalcLinePosData, pPrtFont->GetSize() ); + // draw wave line for grammar check errors + lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetGrammarCheck(), aCalcLinePosData, pPrtFont->GetSize() ); + } + } + + xub_StrLen nOffs = 0; + xub_StrLen nLen = rInf.GetLen(); +#ifdef COMING_SOON + if( aPos.X() < rInf.GetLeft() ) + { + while( nOffs < nLen && + aPos.X() + pKernArray[ nOffs ] < rInf.GetLeft() ) + ++nOffs; + if( nOffs < nLen ) + { + --nLen; + while( nLen > nOffs && + aPos.X() + pKernArray[ nLen ] > rInf.GetRight() ) + --nLen; + ++nLen; + if( nOffs ) + --nOffs; + } + if( nOffs ) + { + long nDiff = pKernArray[ nOffs - 1 ]; + aPos.X() += nDiff; + for( xub_StrLen nX = nOffs; nX < nLen; ++nX ) + pKernArray[ nX ] -= nDiff; + } + } +#endif + if( nOffs < nLen ) + { + // If we paint bullets instead of spaces, we use a copy of + // the paragraph string. For the layout engine, the copy + // of the string has to be an environment of the range which + // is painted + xub_StrLen nTmpIdx = bBullet ? + ( rInf.GetIdx() ? 1 : 0 ) : + rInf.GetIdx(); + + if ( bSwitchL2R ) + rInf.GetFrm()->SwitchLTRtoRTL( aPos ); + + if ( bSwitchH2V ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + + rInf.GetOut().DrawTextArray( aPos, *pStr, pKernArray + nOffs, + nTmpIdx + nOffs , nLen - nOffs ); + } + } + delete[] pScrArray; + delete[] pKernArray; + } +} + + +// Optimierung war fuer DrawText() ausgeschaltet +#ifdef _MSC_VER +#pragma optimize("",on) +#endif + + +/************************************************************************* + * + * Size SwFntObj::GetTextSize( const OutputDevice *pOut, const String &rTxt, + * const USHORT nIdx, const USHORT nLen, const short nKern = 0 ); + * + * Ersterstellung AMA 16. Dez. 94 + * Letzte Aenderung AMA 16. Dez. 94 + * + * Beschreibung: ermittelt die TextSize (des Druckers) + * + *************************************************************************/ + +Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf ) +{ + Size aTxtSize; + const xub_StrLen nLn = ( STRING_LEN != rInf.GetLen() ) ? rInf.GetLen() : + rInf.GetText().Len(); + + // be sure to have the correct layout mode at the printer + if ( pPrinter ) + { + pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() ); + pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() ); + } + + if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && rInf.GetFont() && + SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() ) + { + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const USHORT nGridWidth = GETGRIDWIDTH(pGrid, pDoc); + + OutputDevice* pOutDev; + + if ( pPrinter ) + { + if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) ) + pPrinter->SetFont(*pPrtFont); + pOutDev = pPrinter; + } + else + pOutDev = rInf.GetpOut(); + + aTxtSize.Width() = + pOutDev->GetTextWidth( rInf.GetText(), rInf.GetIdx(), nLn ); + + ASSERT( !rInf.GetShell() || + ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExtLeading() ), + "Leading values should be already calculated" ) + aTxtSize.Height() = pOutDev->GetTextHeight() + + GetFontLeading( rInf.GetShell(), rInf.GetOut() ); + + long nWidthPerChar = aTxtSize.Width() / nLn; + + const ULONG i = nWidthPerChar ? + ( nWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + aTxtSize.Width() = i * nGridWidth * nLn; + rInf.SetKanaDiff( 0 ); + return aTxtSize; + } + } + + //for textgrid refactor + if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && rInf.GetFont() && + SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + const USHORT nDefaultFontHeight = GetDefaultFontHeight( rInf ); + + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc); + if( SW_LATIN == rInf.GetFont()->GetActual() ) + nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2; + else + nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight; + OutputDevice* pOutDev; + if ( pPrinter ) + { + if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) ) + pPrinter->SetFont(*pPrtFont); + pOutDev = pPrinter; + } + else + pOutDev = rInf.GetpOut(); + aTxtSize.Width() = pOutDev->GetTextWidth( rInf.GetText(), rInf.GetIdx(), nLn ); + aTxtSize.Height() = pOutDev->GetTextHeight() + + GetFontLeading( rInf.GetShell(), rInf.GetOut() ); + aTxtSize.Width() += (nLn) * long( nGridWidthAdd ); + //if ( rInf.GetKern() && nLn ) + // aTxtSize.Width() += ( nLn ) * long( rInf.GetKern() ); + + rInf.SetKanaDiff( 0 ); + return aTxtSize; + } + } + + const BOOL bCompress = rInf.GetKanaComp() && nLn && + rInf.GetFont() && + SW_CJK == rInf.GetFont()->GetActual() && + rInf.GetScriptInfo() && + rInf.GetScriptInfo()->CountCompChg() && + lcl_IsMonoSpaceFont( rInf.GetOut() ); + + ASSERT( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()-> + CountCompChg()), "Compression without info" ); + + // This is the part used e.g., for cursor travelling + // See condition for DrawText or DrawTextArray (bDirectPrint) + if ( pPrinter && pPrinter != rInf.GetpOut() ) + { + if( !pPrtFont->IsSameInstance( pPrinter->GetFont() ) ) + pPrinter->SetFont(*pPrtFont); + aTxtSize.Width() = pPrinter->GetTextWidth( rInf.GetText(), + rInf.GetIdx(), nLn ); + aTxtSize.Height() = pPrinter->GetTextHeight(); + sal_Int32 *pKernArray = new sal_Int32[nLn]; + CreateScrFont( *rInf.GetShell(), rInf.GetOut() ); + if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *pScrFont ); + long nScrPos; + + pPrinter->GetTextArray( rInf.GetText(), pKernArray, rInf.GetIdx(),nLn ); + if( bCompress ) + rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray, + rInf.GetIdx(), nLn, rInf.GetKanaComp(), + (USHORT)aFont.GetSize().Height() ) ); + else + rInf.SetKanaDiff( 0 ); + + if ( rInf.GetKanaDiff() ) + nScrPos = pKernArray[ nLn - 1 ]; + else + { + sal_Int32* pScrArray = new sal_Int32[ rInf.GetLen() ]; + rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray, + rInf.GetIdx(), rInf.GetLen() ); + nScrPos = pScrArray[ 0 ]; + xub_StrLen nCnt = rInf.GetText().Len(); + if ( nCnt < rInf.GetIdx() ) + nCnt=0; + else + nCnt = nCnt - rInf.GetIdx(); + nCnt = Min (nCnt, nLn); + xub_Unicode nChPrev = rInf.GetText().GetChar( rInf.GetIdx() ); + + xub_Unicode nCh; + + // Bei Pairkerning waechst der Printereinfluss auf die Positionierung + USHORT nMul = 3; + if ( pPrtFont->GetKerning() ) + nMul = 1; + const USHORT nDiv = nMul+1; + for( xub_StrLen i=1; i<nCnt; i++ ) + { + nCh = rInf.GetText().GetChar( rInf.GetIdx() + i ); + long nScr; + nScr = pScrArray[ i ] - pScrArray[ i - 1 ]; + if ( nCh == CH_BLANK ) + nScrPos = pKernArray[i-1]+nScr; + else + { + if ( nChPrev == CH_BLANK || nChPrev == '-' ) + nScrPos = pKernArray[i-1]+nScr; + else + { + nScrPos += nScr; + nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv; + } + } + nChPrev = nCh; + pKernArray[i-1] = nScrPos - nScr; + } + delete[] pScrArray; + } + + delete[] pKernArray; + aTxtSize.Width() = nScrPos; + } + else + { + if( !pPrtFont->IsSameInstance( rInf.GetOut().GetFont() ) ) + rInf.GetOut().SetFont( *pPrtFont ); + if( bCompress ) + { + sal_Int32 *pKernArray = new sal_Int32[nLn]; + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), nLn ); + rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray, + rInf.GetIdx(), nLn, rInf.GetKanaComp(), + (USHORT) aFont.GetSize().Height() ) ); + aTxtSize.Width() = pKernArray[ nLn - 1 ]; + delete[] pKernArray; + } + else + { + aTxtSize.Width() = rInf.GetOut().GetTextWidth( rInf.GetText(), + rInf.GetIdx(), nLn ); + rInf.SetKanaDiff( 0 ); + } + + aTxtSize.Height() = rInf.GetOut().GetTextHeight(); + } + + if ( rInf.GetKern() && nLn ) + aTxtSize.Width() += ( nLn - 1 ) * long( rInf.GetKern() ); + + ASSERT( !rInf.GetShell() || + ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExtLeading() ), + "Leading values should be already calculated" ) + aTxtSize.Height() += GetFontLeading( rInf.GetShell(), rInf.GetOut() ); + return aTxtSize; +} + + +xub_StrLen SwFntObj::GetCrsrOfst( SwDrawTextInfo &rInf ) +{ + long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR; + const long nSperren = -rInf.GetSperren() / SPACING_PRECISION_FACTOR; + long nKern = rInf.GetKern(); + + if( 0 != nSperren ) + nKern -= nSperren; + + sal_Int32 *pKernArray = new sal_Int32[ rInf.GetLen() ]; + + // be sure to have the correct layout mode at the printer + if ( pPrinter ) + { + pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() ); + pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() ); + pPrinter->GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + } + else + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + + const SwScriptInfo* pSI = rInf.GetScriptInfo(); + if ( rInf.GetFont() && rInf.GetLen() ) + { + const BYTE nActual = rInf.GetFont()->GetActual(); + + // Kana Compression + if ( SW_CJK == nActual && rInf.GetKanaComp() && + pSI && pSI->CountCompChg() && + lcl_IsMonoSpaceFont( rInf.GetOut() ) ) + { + pSI->Compress( pKernArray, rInf.GetIdx(), rInf.GetLen(), + rInf.GetKanaComp(), + (USHORT) aFont.GetSize().Height() ); + } + + // Asian Justification + if ( SW_CJK == rInf.GetFont()->GetActual() ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CJK ); + + if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) + { + long nSpaceSum = nSpaceAdd; + for ( USHORT nI = 0; nI < rInf.GetLen(); ++nI ) + { + pKernArray[ nI ] += nSpaceSum; + nSpaceSum += nSpaceAdd; + } + + nSpaceAdd = 0; + } + + } + + // Kashida Justification + if ( SW_CTL == nActual && rInf.GetSpace() ) + { + if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) ) + { + if ( pSI && pSI->CountKashida() && + pSI->KashidaJustify( pKernArray, 0, rInf.GetIdx(), rInf.GetLen(), + nSpaceAdd ) != STRING_LEN ) + nSpaceAdd = 0; + } + } + + // Thai Justification + if ( SW_CTL == nActual && nSpaceAdd ) + { + LanguageType aLang = rInf.GetFont()->GetLanguage( SW_CTL ); + + if ( LANGUAGE_THAI == aLang ) + { + SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray, 0, + rInf.GetIdx(), rInf.GetLen(), + rInf.GetNumberOfBlanks(), + rInf.GetSpace() ); + + // adding space to blanks is already done + nSpaceAdd = 0; + } + } + } + + long nLeft = 0; + long nRight = 0; + xub_StrLen nCnt = 0; + long nSpaceSum = 0; + long nKernSum = 0; + + if ( rInf.GetFrm() && rInf.GetLen() && rInf.SnapToGrid() && + rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() ) + { + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const USHORT nGridWidth = GETGRIDWIDTH(pGrid, pDoc); + + long nWidthPerChar = pKernArray[ rInf.GetLen() - 1 ] / rInf.GetLen(); + + ULONG i = nWidthPerChar ? + ( nWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + nWidthPerChar = i * nGridWidth; + + nCnt = (USHORT)(rInf.GetOfst() / nWidthPerChar); + if ( 2 * ( rInf.GetOfst() - nCnt * nWidthPerChar ) > nWidthPerChar ) + ++nCnt; + + delete[] pKernArray; + return nCnt; + } + } + + //for textgrid refactor + if ( rInf.GetFrm() && rInf.GetLen() && rInf.SnapToGrid() && + rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + + const USHORT nDefaultFontHeight = GetDefaultFontHeight( rInf ); + + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc); + if( SW_LATIN == rInf.GetFont()->GetActual() ) + nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2; + else + nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight; + + for(xub_StrLen j = 0; j < rInf.GetLen(); j++) + { + long nScr = pKernArray[ j ] + ( nSpaceAdd + nGridWidthAdd ) * ( j + 1 ); + if( nScr >= rInf.GetOfst()) + { + nCnt = j; + break; + } + } + delete[] pKernArray; + return nCnt; + } + } + + sal_uInt16 nItrMode = i18n::CharacterIteratorMode::SKIPCELL; + sal_Int32 nDone = 0; + LanguageType aLang = LANGUAGE_NONE; + bool bSkipCharacterCells = false; + xub_StrLen nIdx = rInf.GetIdx(); + xub_StrLen nLastIdx = nIdx; + const xub_StrLen nEnd = rInf.GetIdx() + rInf.GetLen(); + + // --> OD 2009-12-29 #i105901# + // skip character cells for all script types + if ( pBreakIt->GetBreakIter().is() ) + // <-- + { + aLang = rInf.GetFont()->GetLanguage(); + bSkipCharacterCells = true; + } + + while ( ( nRight < long( rInf.GetOfst() ) ) && ( nIdx < nEnd ) ) + { + if ( nSpaceAdd && CH_BLANK == rInf.GetText().GetChar( nIdx ) ) + nSpaceSum += nSpaceAdd; + + // go to next character (cell). + nLastIdx = nIdx; + + if ( bSkipCharacterCells ) + { + nIdx = (xub_StrLen)pBreakIt->GetBreakIter()->nextCharacters( rInf.GetText(), + nIdx, pBreakIt->GetLocale( aLang ), nItrMode, 1, nDone ); + if ( nIdx <= nLastIdx ) + break; + } + else + ++nIdx; + + nLeft = nRight; + nRight = pKernArray[ nIdx - rInf.GetIdx() - 1 ] + nKernSum + nSpaceSum; + + nKernSum += nKern; + } + + // step back if position is before the middle of the character + // or if we do not want to go to the next character + if ( nIdx > rInf.GetIdx() && + ( rInf.IsPosMatchesBounds() || + ( ( nRight > long( rInf.GetOfst() ) ) && + ( nRight - rInf.GetOfst() > rInf.GetOfst() - nLeft ) ) ) ) + nCnt = nLastIdx - rInf.GetIdx(); // first half + else + nCnt = nIdx - rInf.GetIdx(); // second half + + if ( pSI ) + rInf.SetCursorBidiLevel( pSI->DirType( nLastIdx ) ); + + delete[] pKernArray; + return nCnt; +} + + +/************************************************************************* +|* +|* SwFntAccess::SwFntAccess() +|* +|* Ersterstellung AMA 9. Nov. 94 +|* Letzte Aenderung AMA 9. Nov. 94 +|* +|*************************************************************************/ + +SwFntAccess::SwFntAccess( const void* &rMagic, + USHORT &rIndex, const void *pOwn, ViewShell *pSh, + BOOL bCheck ) : + SwCacheAccess( *pFntCache, rMagic, rIndex ), + pShell( pSh ) +{ + // Der benutzte CTor von SwCacheAccess sucht anhand rMagic+rIndex im Cache + if ( IsAvail() ) + { + // Der schnellste Fall: ein bekannter Font ( rMagic ), + // bei dem Drucker und Zoom nicht ueberprueft werden brauchen. + if ( !bCheck ) + return; + + // Hier ist zwar der Font bekannt, muss aber noch ueberprueft werden. + + } + else + // Hier ist der Font nicht bekannt, muss also gesucht werden. + bCheck = FALSE; + + + { + OutputDevice* pOut = 0; + USHORT nZoom = USHRT_MAX; + + // Get the reference device + if ( pSh ) + { + pOut = &pSh->GetRefDev(); + nZoom = pSh->GetViewOptions()->GetZoom(); + } + + SwFntObj *pFntObj; + if ( bCheck ) + { + pFntObj = Get(); + if ( ( pFntObj->GetZoom( ) == nZoom ) && + ( pFntObj->pPrinter == pOut ) && + pFntObj->GetPropWidth() == + ((SwSubFont*)pOwn)->GetPropWidth() ) + return; // Die Ueberpruefung ergab: Drucker+Zoom okay. + pFntObj->Unlock( ); // Vergiss dies Objekt, es wurde leider + pObj = NULL; // eine Drucker/Zoomaenderung festgestellt. + } + + // Search by font comparison, quite expensive! + // Look for same font and same printer + pFntObj = pFntCache->First(); + while ( pFntObj && !( pFntObj->aFont == *(Font *)pOwn && + pFntObj->GetZoom() == nZoom && + pFntObj->GetPropWidth() == + ((SwSubFont*)pOwn)->GetPropWidth() && + ( !pFntObj->pPrinter || pFntObj->pPrinter == pOut ) ) ) + pFntObj = pFntCache->Next( pFntObj ); + + if( pFntObj && pFntObj->pPrinter != pOut ) + { + // Wir haben zwar einen ohne Drucker gefunden, mal sehen, ob es + // auch noch einen mit identischem Drucker gibt. + SwFntObj *pTmpObj = pFntObj; + while( pTmpObj && !( pTmpObj->aFont == *(Font *)pOwn && + pTmpObj->GetZoom()==nZoom && pTmpObj->pPrinter==pOut && + pTmpObj->GetPropWidth() == + ((SwSubFont*)pOwn)->GetPropWidth() ) ) + pTmpObj = pFntCache->Next( pTmpObj ); + if( pTmpObj ) + pFntObj = pTmpObj; + } + + if ( !pFntObj ) // Font has not been found, create one + { + // Das Objekt muss neu angelegt werden, deshalb muss der Owner ein + // SwFont sein, spaeter wird als Owner die "MagicNumber" gehalten. + SwCacheAccess::pOwner = pOwn; + pFntObj = Get(); // hier wird via NewObj() angelegt und gelockt. + ASSERT(pFntObj, "No Font, no Fun."); + } + else // Font has been found, so we lock it. + { + pFntObj->Lock(); + if( pFntObj->pPrinter != pOut ) // Falls bis dato kein Drucker bekannt + { + ASSERT( !pFntObj->pPrinter, "SwFntAccess: Printer Changed" ); + pFntObj->CreatePrtFont( *pOut ); + pFntObj->pPrinter = pOut; + pFntObj->pScrFont = NULL; + pFntObj->nGuessedLeading = USHRT_MAX; + pFntObj->nExtLeading = USHRT_MAX; + pFntObj->nPrtAscent = USHRT_MAX; + pFntObj->nPrtHeight = USHRT_MAX; + } + pObj = pFntObj; + } + + // egal, ob neu oder gefunden, ab jetzt ist der Owner vom Objekt eine + // MagicNumber und wird auch dem aufrufenden SwFont bekanntgegeben, + // ebenso der Index fuer spaetere direkte Zugriffe + rMagic = pFntObj->GetOwner(); + SwCacheAccess::pOwner = rMagic; + rIndex = pFntObj->GetCachePos(); + } +} + +SwCacheObj *SwFntAccess::NewObj( ) +{ + // Ein neuer Font, eine neue "MagicNumber". + return new SwFntObj( *(SwSubFont *)pOwner, ++pMagicNo, pShell ); +} + +extern xub_StrLen lcl_CalcCaseMap( const SwFont& rFnt, + const XubString& rOrigString, + xub_StrLen nOfst, + xub_StrLen nLen, + xub_StrLen nIdx ); + +xub_StrLen SwFont::GetTxtBreak( SwDrawTextInfo& rInf, long nTextWidth ) +{ + ChgFnt( rInf.GetShell(), rInf.GetOut() ); + + const BOOL bCompress = rInf.GetKanaComp() && rInf.GetLen() && + SW_CJK == GetActual() && + rInf.GetScriptInfo() && + rInf.GetScriptInfo()->CountCompChg() && + lcl_IsMonoSpaceFont( rInf.GetOut() ); + + ASSERT( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()-> + CountCompChg()), "Compression without info" ); + + USHORT nTxtBreak = 0; + long nKern = 0; + + USHORT nLn = ( rInf.GetLen() == STRING_LEN ? rInf.GetText().Len() + : rInf.GetLen() ); + + if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && + rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() ) + { + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + const USHORT nGridWidth = GETGRIDWIDTH(pGrid, pDoc); + + sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()]; + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + + long nWidthPerChar = pKernArray[ rInf.GetLen() - 1 ] / rInf.GetLen(); + + const ULONG i = nWidthPerChar ? + ( nWidthPerChar - 1 ) / nGridWidth + 1: + 1; + + nWidthPerChar = i * nGridWidth; + long nCurrPos = nWidthPerChar; + + while( nTxtBreak < rInf.GetLen() && nTextWidth >= nCurrPos ) + { + nCurrPos += nWidthPerChar; + ++nTxtBreak; + } + + delete[] pKernArray; + return nTxtBreak + rInf.GetIdx(); + } + } + + //for text grid enhancement + if ( rInf.GetFrm() && nLn && rInf.SnapToGrid() && rInf.GetFont() && + SW_CJK == rInf.GetFont()->GetActual() ) + { + GETGRID( rInf.GetFrm()->FindPageFrm() ) + if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() ) + { + const USHORT nDefaultFontHeight = GetDefaultFontHeight( rInf ); + + const SwDoc* pDoc = rInf.GetShell()->GetDoc(); + long nGridWidthAdd = GETGRIDWIDTH(pGrid, pDoc); + if( SW_LATIN == rInf.GetFont()->GetActual() ) + nGridWidthAdd = ( nGridWidthAdd - nDefaultFontHeight ) / 2 ; + else + nGridWidthAdd = nGridWidthAdd - nDefaultFontHeight; + + sal_Int32* pKernArray = new sal_Int32[rInf.GetLen()]; + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), rInf.GetLen() ); + long nCurrPos = pKernArray[nTxtBreak] + nGridWidthAdd; + while( nTxtBreak < rInf.GetLen() && nTextWidth >= nCurrPos) + { + nTxtBreak++; + nCurrPos = pKernArray[nTxtBreak] + nGridWidthAdd * ( nTxtBreak + 1 ); + } + delete[] pKernArray; + return nTxtBreak + rInf.GetIdx(); + } + } + + if( aSub[nActual].IsCapital() && nLn ) + nTxtBreak = GetCapitalBreak( rInf.GetShell(), rInf.GetpOut(), + rInf.GetScriptInfo(), rInf.GetText(), nTextWidth,0, rInf.GetIdx(),nLn ); + else + { + nKern = CheckKerning(); + + const XubString* pTmpText; + XubString aTmpText; + xub_StrLen nTmpIdx; + xub_StrLen nTmpLen; + bool bTextReplaced = false; + + if ( !aSub[nActual].IsCaseMap() ) + { + pTmpText = &rInf.GetText(); + nTmpIdx = rInf.GetIdx(); + nTmpLen = nLn; + } + else + { + const XubString aSnippet( rInf.GetText(), rInf.GetIdx(), nLn ); + aTmpText = aSub[nActual].CalcCaseMap( aSnippet ); + const bool bTitle = SVX_CASEMAP_TITEL == aSub[nActual].GetCaseMap() && + pBreakIt->GetBreakIter().is(); + + // Uaaaaahhhh!!! In title case mode, we would get wrong results + if ( bTitle && nLn ) + { + // check if rInf.GetIdx() is begin of word + if ( !pBreakIt->GetBreakIter()->isBeginWord( + rInf.GetText(), rInf.GetIdx(), + pBreakIt->GetLocale( aSub[nActual].GetLanguage() ), + i18n::WordType::ANYWORD_IGNOREWHITESPACES ) ) + { + // In this case, the beginning of aTmpText is wrong. + XubString aSnippetTmp( aSnippet, 0, 1 ); + aSnippetTmp = aSub[nActual].CalcCaseMap( aSnippetTmp ); + aTmpText.Erase( 0, aSnippetTmp.Len() ); + aTmpText.Insert( aSnippet.GetChar( 0 ), 0 ); + } + } + + pTmpText = &aTmpText; + nTmpIdx = 0; + nTmpLen = aTmpText.Len(); + bTextReplaced = true; + } + + if( rInf.GetHyphPos() ) + nTxtBreak = rInf.GetOut().GetTextBreak( *pTmpText, nTextWidth, + '-', *rInf.GetHyphPos(), + nTmpIdx, nTmpLen, nKern ); + else + nTxtBreak = rInf.GetOut().GetTextBreak( *pTmpText, nTextWidth, + nTmpIdx, nTmpLen, nKern ); + + if ( bTextReplaced && STRING_LEN != nTxtBreak ) + { + if ( nTmpLen != nLn ) + nTxtBreak = lcl_CalcCaseMap( *this, rInf.GetText(), + rInf.GetIdx(), nLn, nTxtBreak ); + else + nTxtBreak = nTxtBreak + rInf.GetIdx(); + } + } + + if ( ! bCompress ) + return nTxtBreak; + + nTxtBreak = nTxtBreak - rInf.GetIdx(); + + if( nTxtBreak < nLn ) + { + if( !nTxtBreak && nLn ) + nLn = 1; + else if( nLn > 2 * nTxtBreak ) + nLn = 2 * nTxtBreak; + sal_Int32 *pKernArray = new sal_Int32[ nLn ]; + rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray, + rInf.GetIdx(), nLn ); + if( rInf.GetScriptInfo()->Compress( pKernArray, rInf.GetIdx(), nLn, + rInf.GetKanaComp(), (USHORT)GetHeight( nActual ) ) ) + { + long nKernAdd = nKern; + xub_StrLen nTmpBreak = nTxtBreak; + if( nKern && nTxtBreak ) + nKern *= nTxtBreak - 1; + while( nTxtBreak<nLn && nTextWidth >= pKernArray[nTxtBreak] +nKern ) + { + nKern += nKernAdd; + ++nTxtBreak; + } + if( rInf.GetHyphPos() ) + *rInf.GetHyphPos() += nTxtBreak - nTmpBreak; // It's not perfect + } + delete[] pKernArray; + } + nTxtBreak = nTxtBreak + rInf.GetIdx(); + + return nTxtBreak; +} + +extern Color aGlobalRetoucheColor; + +sal_Bool SwDrawTextInfo::ApplyAutoColor( Font* pFont ) +{ + const Font& rFnt = pFont ? *pFont : GetOut().GetFont(); + sal_Bool bPrt = GetShell() && ! GetShell()->GetWin(); + ColorData nNewColor = COL_BLACK; + sal_Bool bChgFntColor = sal_False; + sal_Bool bChgLineColor = sal_False; + + if( bPrt && GetShell() && GetShell()->GetViewOptions()->IsBlackFont() ) + { + if ( COL_BLACK != rFnt.GetColor().GetColor() ) + bChgFntColor = sal_True; + + if ( (COL_BLACK != GetOut().GetLineColor().GetColor()) || + (COL_BLACK != GetOut().GetOverlineColor().GetColor()) ) + bChgLineColor = sal_True; + } + else + { + // FontColor has to be changed if: + // 1. FontColor = AUTO or 2. IsAlwaysAutoColor is set + // LineColor has to be changed if: + // 1. IsAlwaysAutoColor is set + + bChgLineColor = ! bPrt && GetShell() && + GetShell()->GetAccessibilityOptions()->IsAlwaysAutoColor(); + + bChgFntColor = COL_AUTO == rFnt.GetColor().GetColor() || bChgLineColor; + + if ( bChgFntColor ) + { + // check if current background has a user defined setting + const Color* pCol = GetFont() ? GetFont()->GetBackColor() : NULL; + if( ! pCol || COL_TRANSPARENT == pCol->GetColor() ) + { + const SvxBrushItem* pItem; + SwRect aOrigBackRect; + + /// OD 21.08.2002 + /// consider, that [GetBackgroundBrush(...)] can set <pCol> + /// - see implementation in /core/layout/paintfrm.cxx + /// OD 21.08.2002 #99657# + /// There is a user defined setting for the background, if there + /// is a background brush and its color is *not* "no fill"/"auto fill". + if( GetFrm()->GetBackgroundBrush( pItem, pCol, aOrigBackRect, FALSE ) ) + { + if ( !pCol ) + { + pCol = &pItem->GetColor(); + } + + /// OD 30.08.2002 #99657# + /// determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it. + if ( pCol->GetColor() == COL_TRANSPARENT) + pCol = NULL; + } + else + pCol = NULL; + } + + // no user defined color at paragraph or font background + if ( ! pCol ) + pCol = &aGlobalRetoucheColor; + + if( GetShell() && GetShell()->GetWin() ) + { + // here we determine the prefered window text color for painting + const SwViewOption* pViewOption = GetShell()->GetViewOptions(); + if(pViewOption->IsPagePreview() && + !SW_MOD()->GetAccessibilityOptions().GetIsForPagePreviews()) + nNewColor = COL_BLACK; + else + // we take the font color from the appearence page + nNewColor = SwViewOption::GetFontColor().GetColor(); + } + + // change painting color depending of dark/bright background + Color aTmpColor( nNewColor ); + if ( pCol->IsDark() && aTmpColor.IsDark() ) + nNewColor = COL_WHITE; + else if ( pCol->IsBright() && aTmpColor.IsBright() ) + nNewColor = COL_BLACK; + } + } + + if ( bChgFntColor || bChgLineColor ) + { + Color aNewColor( nNewColor ); + + if ( bChgFntColor ) + { + if ( pFont && aNewColor != pFont->GetColor() ) + { + // only set the new color at the font passed as argument + pFont->SetColor( aNewColor ); + } + else if ( aNewColor != GetOut().GetFont().GetColor() ) + { + // set new font with new color at output device + Font aFont( rFnt ); + aFont.SetColor( aNewColor ); + GetOut().SetFont( aFont ); + } + } + + // the underline and overline colors have to be set separately + if ( bChgLineColor ) + { + // get current font color or color set at output device + aNewColor = pFont ? pFont->GetColor() : GetOut().GetFont().GetColor(); + if ( aNewColor != GetOut().GetLineColor() ) + GetOut().SetLineColor( aNewColor ); + if ( aNewColor != GetOut().GetOverlineColor() ) + GetOut().SetOverlineColor( aNewColor ); + } + + return sal_True; + } + + return sal_False; +} + diff --git a/sw/source/core/txtnode/fntcap.cxx b/sw/source/core/txtnode/fntcap.cxx new file mode 100644 index 000000000000..e38b26ecb9f7 --- /dev/null +++ b/sw/source/core/txtnode/fntcap.cxx @@ -0,0 +1,855 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <hintids.hxx> +#include <editeng/cmapitem.hxx> + +#ifndef _OUTDEV_HXX //autogen +#include <vcl/outdev.hxx> +#endif +#ifndef _COM_SUN_STAR_I18N_CHARTYPE_HDL +#include <com/sun/star/i18n/CharType.hdl> +#endif +#ifndef _COM_SUN_STAR_I18N_WORDTYPE_HDL +#include <com/sun/star/i18n/WordType.hdl> +#endif + +#ifndef _PRINT_HXX //autogen +#include <vcl/print.hxx> +#endif +#include <errhdl.hxx> +#include <fntcache.hxx> +#include <swfont.hxx> +#include <breakit.hxx> +#include <txtfrm.hxx> // SwTxtFrm +#include <scriptinfo.hxx> + +using namespace ::com::sun::star::i18n; + + +#define KAPITAELCHENPROP 66 + +/************************************************************************* + * class SwCapitalInfo + * + * The information encapsulated in SwCapitalInfo is required + * by the ::Do functions. They contain the information about + * the original string, whereas rDo.GetInf() contains information + * about the display string. + *************************************************************************/ + +class SwCapitalInfo +{ +public: + explicit SwCapitalInfo( const XubString& rOrigText ) : + rString( rOrigText ), nIdx( 0 ), nLen( 0 ) {}; + const XubString& rString; + xub_StrLen nIdx; + xub_StrLen nLen; +}; + +/************************************************************************* + * xub_StrLen lcl_CalcCaseMap() + * + * rFnt: required for CalcCaseMap + * rOrigString: The original string + * nOfst: Position of the substring in rOrigString + * nLen: Length if the substring in rOrigString + * nIdx: Referes to a position in the display string and should be mapped + * to a position in rOrigString + *************************************************************************/ + +xub_StrLen lcl_CalcCaseMap( const SwFont& rFnt, + const XubString& rOrigString, + xub_StrLen nOfst, + xub_StrLen nLen, + xub_StrLen nIdx ) +{ + int j = 0; + const xub_StrLen nEnd = nOfst + nLen; + ASSERT( nEnd <= rOrigString.Len(), "lcl_CalcCaseMap: Wrong parameters" ) + + // special case for title case: + const bool bTitle = SVX_CASEMAP_TITEL == rFnt.GetCaseMap() && + pBreakIt->GetBreakIter().is(); + for ( xub_StrLen i = nOfst; i < nEnd; ++i ) + { + XubString aTmp( rOrigString, i, 1 ); + + if ( !bTitle || + pBreakIt->GetBreakIter()->isBeginWord( + rOrigString, i, + pBreakIt->GetLocale( rFnt.GetLanguage() ), + WordType::ANYWORD_IGNOREWHITESPACES ) ) + aTmp = rFnt.GetActualFont().CalcCaseMap( aTmp ); + + j += aTmp.Len(); + + if ( j > nIdx ) + return i; + } + + return nOfst + nLen; +} + +/************************************************************************* + * class SwDoCapitals + *************************************************************************/ + +class SwDoCapitals +{ +protected: + SwDrawTextInfo &rInf; + SwCapitalInfo* pCapInf; // referes to additional information + // required by the ::Do function +public: + SwDoCapitals ( SwDrawTextInfo &rInfo ) : rInf( rInfo ), pCapInf( 0 ) { } + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) = 0; + virtual void Do() = 0; + inline OutputDevice& GetOut() { return rInf.GetOut(); } + inline SwDrawTextInfo& GetInf() { return rInf; } + inline SwCapitalInfo* GetCapInf() const { return pCapInf; } + inline void SetCapInf( SwCapitalInfo& rNew ) { pCapInf = &rNew; } +}; + +/************************************************************************* + * class SwDoGetCapitalSize + *************************************************************************/ + +class SwDoGetCapitalSize : public SwDoCapitals +{ +protected: + Size aTxtSize; +public: + SwDoGetCapitalSize( SwDrawTextInfo &rInfo ) : SwDoCapitals ( rInfo ) { } + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ); + virtual void Do(); + const Size &GetSize() const { return aTxtSize; } +}; + +void SwDoGetCapitalSize::Init( SwFntObj *, SwFntObj * ) +{ + aTxtSize.Height() = 0; + aTxtSize.Width() = 0; +} + +void SwDoGetCapitalSize::Do() +{ + aTxtSize.Width() += rInf.GetSize().Width(); + if( rInf.GetUpper() ) + aTxtSize.Height() = rInf.GetSize().Height(); +} + +/************************************************************************* + * SwSubFont::GetCapitalSize() + *************************************************************************/ + +Size SwSubFont::GetCapitalSize( SwDrawTextInfo& rInf ) +{ + // Start: + const long nOldKern = rInf.GetKern(); + rInf.SetKern( CheckKerning() ); + Point aPos; + rInf.SetPos( aPos ); + rInf.SetSpace( 0 ); + rInf.SetDrawSpace( FALSE ); + SwDoGetCapitalSize aDo( rInf ); + DoOnCapitals( aDo ); + Size aTxtSize( aDo.GetSize() ); + + // End: + if( !aTxtSize.Height() ) + { + SV_STAT( nGetTextSize ); + aTxtSize.Height() = short ( rInf.GetpOut()->GetTextHeight() ); + } + rInf.SetKern( nOldKern ); + return aTxtSize; +} + +/************************************************************************* + * class SwDoGetCapitalBreak + *************************************************************************/ + +class SwDoGetCapitalBreak : public SwDoCapitals +{ +protected: + xub_StrLen *pExtraPos; + long nTxtWidth; + xub_StrLen nBreak; +public: + SwDoGetCapitalBreak( SwDrawTextInfo &rInfo, long nWidth, xub_StrLen *pExtra) + : SwDoCapitals ( rInfo ), pExtraPos( pExtra ), nTxtWidth( nWidth ), + nBreak( STRING_LEN ) + { } + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ); + virtual void Do(); + xub_StrLen GetBreak() const { return nBreak; } +}; + +void SwDoGetCapitalBreak::Init( SwFntObj *, SwFntObj * ) +{ +} + +void SwDoGetCapitalBreak::Do() +{ + if ( nTxtWidth ) + { + if ( rInf.GetSize().Width() < nTxtWidth ) + nTxtWidth -= rInf.GetSize().Width(); + else + { + xub_StrLen nEnd = rInf.GetEnd(); + if( pExtraPos ) + { + nBreak = GetOut().GetTextBreak( rInf.GetText(), nTxtWidth, '-', + *pExtraPos, rInf.GetIdx(), rInf.GetLen(), rInf.GetKern() ); + if( *pExtraPos > nEnd ) + *pExtraPos = nEnd; + } + else + nBreak = GetOut().GetTextBreak( rInf.GetText(), nTxtWidth, + rInf.GetIdx(), rInf.GetLen(), rInf.GetKern() ); + + if( nBreak > nEnd ) + nBreak = nEnd; + + // nBreak may be relative to the display string. It has to be + // calculated relative to the original string: + if ( GetCapInf() ) + { + if ( GetCapInf()->nLen != rInf.GetLen() ) + nBreak = lcl_CalcCaseMap( *rInf.GetFont(), + GetCapInf()->rString, + GetCapInf()->nIdx, + GetCapInf()->nLen, nBreak ); + else + nBreak = nBreak + GetCapInf()->nIdx; + } + + nTxtWidth = 0; + } + } +} + +/************************************************************************* + * SwFont::GetCapitalBreak() + *************************************************************************/ + +xub_StrLen SwFont::GetCapitalBreak( ViewShell* pSh, const OutputDevice* pOut, + const SwScriptInfo* pScript, const XubString& rTxt, long nTextWidth, + xub_StrLen *pExtra, const xub_StrLen nIdx, const xub_StrLen nLen ) +{ + // Start: + Point aPos( 0, 0 ); + SwDrawTextInfo aInfo(pSh, *(OutputDevice*)pOut, pScript, rTxt, nIdx, nLen, + 0, FALSE); + aInfo.SetPos( aPos ); + aInfo.SetSpace( 0 ); + aInfo.SetWrong( NULL ); + aInfo.SetGrammarCheck( NULL ); + aInfo.SetSmartTags( NULL ); // SMARTTAGS + aInfo.SetDrawSpace( FALSE ); + aInfo.SetKern( CheckKerning() ); + aInfo.SetKanaComp( pScript ? 0 : 100 ); + aInfo.SetFont( this ); + + SwDoGetCapitalBreak aDo( aInfo, nTextWidth, pExtra ); + DoOnCapitals( aDo ); + return aDo.GetBreak(); +} + +/************************************************************************* + * class SwDoDrawCapital + *************************************************************************/ + +class SwDoDrawCapital : public SwDoCapitals +{ +protected: + SwFntObj *pUpperFnt; + SwFntObj *pLowerFnt; +public: + SwDoDrawCapital( SwDrawTextInfo &rInfo ) : + SwDoCapitals( rInfo ) + { } + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ); + virtual void Do(); + void DrawSpace( Point &rPos ); +}; + +void SwDoDrawCapital::Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) +{ + pUpperFnt = pUpperFont; + pLowerFnt = pLowerFont; +} + +void SwDoDrawCapital::Do() +{ + SV_STAT( nDrawText ); + USHORT nOrgWidth = rInf.GetWidth(); + rInf.SetWidth( USHORT(rInf.GetSize().Width()) ); + if ( rInf.GetUpper() ) + pUpperFnt->DrawText( rInf ); + else + { + BOOL bOldBullet = rInf.GetBullet(); + rInf.SetBullet( FALSE ); + pLowerFnt->DrawText( rInf ); + rInf.SetBullet( bOldBullet ); + } + + ASSERT( pUpperFnt, "No upper font, dying soon!"); + rInf.Shift( pUpperFnt->GetFont()->GetOrientation() ); + rInf.SetWidth( nOrgWidth ); +} + +/************************************************************************* + * SwDoDrawCapital::DrawSpace() + *************************************************************************/ + +void SwDoDrawCapital::DrawSpace( Point &rPos ) +{ + static sal_Char __READONLY_DATA sDoubleSpace[] = " "; + + long nDiff = rInf.GetPos().X() - rPos.X(); + + Point aPos( rPos ); + const BOOL bSwitchL2R = rInf.GetFrm()->IsRightToLeft() && + ! rInf.IsIgnoreFrmRTL(); + + + if ( bSwitchL2R ) + rInf.GetFrm()->SwitchLTRtoRTL( aPos ); + + const ULONG nMode = rInf.GetpOut()->GetLayoutMode(); + const BOOL bBidiPor = ( bSwitchL2R != + ( 0 != ( TEXT_LAYOUT_BIDI_RTL & nMode ) ) ); + + if ( bBidiPor ) + nDiff = -nDiff; + + if ( rInf.GetFrm()->IsVertical() ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + + if ( nDiff ) + { + rInf.ApplyAutoColor(); + GetOut().DrawStretchText( aPos, nDiff, + XubString( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ), 0, 2 ); + } + rPos.X() = rInf.GetPos().X() + rInf.GetWidth(); +} + +/************************************************************************* + * SwSubFont::DrawCapital() + *************************************************************************/ + +void SwSubFont::DrawCapital( SwDrawTextInfo &rInf ) +{ + // Es wird vorausgesetzt, dass rPos bereits kalkuliert ist! + // hochgezogen in SwFont: const Point aPos( CalcPos(rPos) ); + rInf.SetDrawSpace( GetUnderline() != UNDERLINE_NONE || + GetOverline() != UNDERLINE_NONE || + GetStrikeout() != STRIKEOUT_NONE ); + SwDoDrawCapital aDo( rInf ); + DoOnCapitals( aDo ); +} + +/************************************************************************* + * class SwDoDrawCapital + *************************************************************************/ + +class SwDoCapitalCrsrOfst : public SwDoCapitals +{ +protected: + SwFntObj *pUpperFnt; + SwFntObj *pLowerFnt; + xub_StrLen nCrsr; + USHORT nOfst; +public: + SwDoCapitalCrsrOfst( SwDrawTextInfo &rInfo, const USHORT nOfs ) : + SwDoCapitals( rInfo ), nCrsr( 0 ), nOfst( nOfs ) + { } + virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ); + virtual void Do(); + + void DrawSpace( const Point &rPos ); + inline xub_StrLen GetCrsr(){ return nCrsr; } +}; + +void SwDoCapitalCrsrOfst::Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) +{ + pUpperFnt = pUpperFont; + pLowerFnt = pLowerFont; +} + +void SwDoCapitalCrsrOfst::Do() +{ + if ( nOfst ) + { + if ( nOfst > rInf.GetSize().Width() ) + { + nOfst = nOfst - USHORT(rInf.GetSize().Width()); + nCrsr = nCrsr + rInf.GetLen(); + } + else + { + SwDrawTextInfo aDrawInf( rInf.GetShell(), *rInf.GetpOut(), + rInf.GetScriptInfo(), + rInf.GetText(), + rInf.GetIdx(), + rInf.GetLen(), 0, FALSE ); + aDrawInf.SetOfst( nOfst ); + aDrawInf.SetKern( rInf.GetKern() ); + aDrawInf.SetKanaComp( rInf.GetKanaComp() ); + aDrawInf.SetFrm( rInf.GetFrm() ); + aDrawInf.SetFont( rInf.GetFont() ); + + if ( rInf.GetUpper() ) + { + aDrawInf.SetSpace( 0 ); + nCrsr = nCrsr + pUpperFnt->GetCrsrOfst( aDrawInf ); + } + else + { + aDrawInf.SetSpace( rInf.GetSpace() ); + nCrsr = nCrsr + pLowerFnt->GetCrsrOfst( aDrawInf ); + } + nOfst = 0; + } + } +} + +/************************************************************************* + * SwSubFont::GetCapitalCrsrOfst() + *************************************************************************/ + +xub_StrLen SwSubFont::GetCapitalCrsrOfst( SwDrawTextInfo& rInf ) +{ + const long nOldKern = rInf.GetKern(); + rInf.SetKern( CheckKerning() ); + SwDoCapitalCrsrOfst aDo( rInf, rInf.GetOfst() ); + Point aPos; + rInf.SetPos( aPos ); + rInf.SetDrawSpace( FALSE ); + DoOnCapitals( aDo ); + rInf.SetKern( nOldKern ); + return aDo.GetCrsr(); +} + +/************************************************************************* + * class SwDoDrawStretchCapital + *************************************************************************/ + +class SwDoDrawStretchCapital : public SwDoDrawCapital +{ + const xub_StrLen nStrLen; + const USHORT nCapWidth; + const USHORT nOrgWidth; +public: + virtual void Do(); + + SwDoDrawStretchCapital( SwDrawTextInfo &rInfo, const USHORT nCapitalWidth ) + : SwDoDrawCapital( rInfo ), + nStrLen( rInfo.GetLen() ), + nCapWidth( nCapitalWidth ), + nOrgWidth( rInfo.GetWidth() ) + { } +}; + +/************************************************************************* + * SwDoDrawStretchCapital + *************************************************************************/ + +void SwDoDrawStretchCapital::Do() +{ + SV_STAT( nDrawStretchText ); + USHORT nPartWidth = USHORT(rInf.GetSize().Width()); + + if( rInf.GetLen() ) + { + // 4023: Kapitaelchen und Kerning. + long nDiff = long(nOrgWidth) - long(nCapWidth); + if( nDiff ) + { + nDiff *= rInf.GetLen(); + nDiff /= (long) nStrLen; + nDiff += nPartWidth; + if( 0 < nDiff ) + nPartWidth = USHORT(nDiff); + } + + rInf.ApplyAutoColor(); + + Point aPos( rInf.GetPos() ); + const BOOL bSwitchL2R = rInf.GetFrm()->IsRightToLeft() && + ! rInf.IsIgnoreFrmRTL(); + + if ( bSwitchL2R ) + rInf.GetFrm()->SwitchLTRtoRTL( aPos ); + + if ( rInf.GetFrm()->IsVertical() ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + + // Optimierung: + if( 1 >= rInf.GetLen() ) + GetOut().DrawText( aPos, rInf.GetText(), rInf.GetIdx(), + rInf.GetLen() ); + else + GetOut().DrawStretchText( aPos, nPartWidth, + rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ); + } + ((Point&)rInf.GetPos()).X() += nPartWidth; +} + +/************************************************************************* + * SwSubFont::DrawStretchCapital() + *************************************************************************/ + +void SwSubFont::DrawStretchCapital( SwDrawTextInfo &rInf ) +{ + // Es wird vorausgesetzt, dass rPos bereits kalkuliert ist! + // hochgezogen in SwFont: const Point aPos( CalcPos(rPos) ); + + if( rInf.GetLen() == STRING_LEN ) + rInf.SetLen( rInf.GetText().Len() ); + + const Point& rOldPos = rInf.GetPos(); + const USHORT nCapWidth = (USHORT)( GetCapitalSize( rInf ).Width() ); + rInf.SetPos( rOldPos ); + + rInf.SetDrawSpace( GetUnderline() != UNDERLINE_NONE || + GetOverline() != UNDERLINE_NONE || + GetStrikeout() != STRIKEOUT_NONE ); + SwDoDrawStretchCapital aDo( rInf, nCapWidth ); + DoOnCapitals( aDo ); +} + +/************************************************************************* + * SwSubFont::DoOnCapitals() const + *************************************************************************/ + +// JP 22.8.2001 - global optimization off - Bug 91245 / 91223 +#ifdef _MSC_VER +#pragma optimize("g",off) +#endif + +void SwSubFont::DoOnCapitals( SwDoCapitals &rDo ) +{ + ASSERT( pLastFont, "SwFont::DoOnCapitals: No LastFont?!" ); + + Size aPartSize; + long nKana = 0; + const XubString aTxt( CalcCaseMap( rDo.GetInf().GetText() ) ); + xub_StrLen nMaxPos = Min( USHORT(rDo.GetInf().GetText().Len() + - rDo.GetInf().GetIdx()), rDo.GetInf().GetLen() ); + rDo.GetInf().SetLen( nMaxPos ); + + const XubString& rOldText = rDo.GetInf().GetText(); + rDo.GetInf().SetText( aTxt ); + rDo.GetInf().SetSize( aPartSize ); + xub_StrLen nPos = rDo.GetInf().GetIdx(); + xub_StrLen nOldPos = nPos; + nMaxPos = nMaxPos + nPos; + + // #107816# + // Look if the length of the original text and the ToUpper-converted + // text is different. If yes, do special handling. + XubString aNewText; + SwCapitalInfo aCapInf( rOldText ); + sal_Bool bCaseMapLengthDiffers( aTxt.Len() != rOldText.Len() ); + if ( bCaseMapLengthDiffers ) + rDo.SetCapInf( aCapInf ); + + SwFntObj *pOldLast = pLastFont; + SwFntAccess *pBigFontAccess = NULL; + SwFntObj *pBigFont; + SwFntAccess *pSpaceFontAccess = NULL; + SwFntObj *pSpaceFont = NULL; + + const void *pMagic2 = NULL; + USHORT nIndex2 = 0; + SwSubFont aFont( *this ); + Point aStartPos( rDo.GetInf().GetPos() ); + + const BOOL bTextLines = aFont.GetUnderline() != UNDERLINE_NONE + || aFont.GetOverline() != UNDERLINE_NONE + || aFont.GetStrikeout() != STRIKEOUT_NONE; + const BOOL bWordWise = bTextLines && aFont.IsWordLineMode() && + rDo.GetInf().GetDrawSpace(); + const long nTmpKern = rDo.GetInf().GetKern(); + + if ( bTextLines ) + { + if ( bWordWise ) + { + aFont.SetWordLineMode( FALSE ); + pSpaceFontAccess = new SwFntAccess( pMagic2, nIndex2, &aFont, + rDo.GetInf().GetShell() ); + pSpaceFont = pSpaceFontAccess->Get(); + } + else + pSpaceFont = pLastFont; + + // Wir basteln uns einen Font fuer die Grossbuchstaben: + aFont.SetUnderline( UNDERLINE_NONE ); + aFont.SetOverline( UNDERLINE_NONE ); + aFont.SetStrikeout( STRIKEOUT_NONE ); + pMagic2 = NULL; + nIndex2 = 0; + pBigFontAccess = new SwFntAccess( pMagic2, nIndex2, &aFont, + rDo.GetInf().GetShell() ); + pBigFont = pBigFontAccess->Get(); + } + else + pBigFont = pLastFont; + + // Hier entsteht der Kleinbuchstabenfont: + aFont.SetProportion( BYTE( (aFont.GetPropr()*KAPITAELCHENPROP) / 100L) ); + pMagic2 = NULL; + nIndex2 = 0; + SwFntAccess *pSmallFontAccess = new SwFntAccess( pMagic2, nIndex2, &aFont, + rDo.GetInf().GetShell() ); + SwFntObj *pSmallFont = pSmallFontAccess->Get(); + + rDo.Init( pBigFont, pSmallFont ); + OutputDevice* pOutSize = pSmallFont->GetPrt(); + if( !pOutSize ) + pOutSize = &rDo.GetOut(); + OutputDevice* pOldOut = &rDo.GetOut(); + + const LanguageType eLng = LANGUAGE_DONTKNOW == GetLanguage() + ? LANGUAGE_SYSTEM : GetLanguage(); + + if( nPos < nMaxPos ) + { + nPos = (xub_StrLen)pBreakIt->GetBreakIter()->endOfCharBlock( rOldText, nPos, + pBreakIt->GetLocale( eLng ), CharType::LOWERCASE_LETTER); + if( nPos == STRING_LEN ) + nPos = nOldPos; + else if( nPos > nMaxPos ) + nPos = nMaxPos; + } + + while( nOldPos < nMaxPos ) + { + + // The lower ones... + if( nOldPos != nPos ) + { + SV_STAT( nGetTextSize ); + pLastFont = pSmallFont; + pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() ); + + // #107816#, #i14820# + if( bCaseMapLengthDiffers ) + { + // Build an own 'changed' string for the given part of the + // source string and use it. That new string may differ in length + // from the source string. + const XubString aSnippet( rOldText, nOldPos, nPos - nOldPos); + aNewText = CalcCaseMap( aSnippet ); + aCapInf.nIdx = nOldPos; + aCapInf.nLen = nPos - nOldPos; + rDo.GetInf().SetIdx( 0 ); + rDo.GetInf().SetLen( aNewText.Len() ); + rDo.GetInf().SetText( aNewText ); + } + else + { + rDo.GetInf().SetIdx( nOldPos ); + rDo.GetInf().SetLen( nPos - nOldPos ); + } + + rDo.GetInf().SetUpper( FALSE ); + rDo.GetInf().SetOut( *pOutSize ); + aPartSize = pSmallFont->GetTextSize( rDo.GetInf() ); + nKana += rDo.GetInf().GetKanaDiff(); + rDo.GetInf().SetOut( *pOldOut ); + if( nTmpKern && nPos < nMaxPos ) + aPartSize.Width() += nTmpKern; + rDo.Do(); + nOldPos = nPos; + } + nPos = (xub_StrLen)pBreakIt->GetBreakIter()->nextCharBlock( rOldText, nPos, + pBreakIt->GetLocale( eLng ), CharType::LOWERCASE_LETTER); + if( nPos == STRING_LEN || nPos > nMaxPos ) + nPos = nMaxPos; + ASSERT( nPos, "nextCharBlock not implemented?" ); +#ifdef DBG_UTIL + if( !nPos ) + nPos = nMaxPos; +#endif + // The upper ones... + if( nOldPos != nPos ) + { + const long nSpaceAdd = rDo.GetInf().GetSpace() / SPACING_PRECISION_FACTOR; + + do + { + rDo.GetInf().SetUpper( TRUE ); + pLastFont = pBigFont; + pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() ); + xub_StrLen nTmp; + if( bWordWise ) + { + nTmp = nOldPos; + while( nTmp < nPos && CH_BLANK == rOldText.GetChar( nTmp ) ) + ++nTmp; + if( nOldPos < nTmp ) + { + pLastFont = pSpaceFont; + pLastFont->SetDevFont( rDo.GetInf().GetShell(), + rDo.GetOut() ); + ((SwDoDrawCapital&)rDo).DrawSpace( aStartPos ); + pLastFont = pBigFont; + pLastFont->SetDevFont( rDo.GetInf().GetShell(), + rDo.GetOut() ); + + // #107816#, #i14820# + if( bCaseMapLengthDiffers ) + { + // Build an own 'changed' string for the given part of the + // source string and use it. That new string may differ in length + // from the source string. + const XubString aSnippet( rOldText, nOldPos, nTmp - nOldPos); + aNewText = CalcCaseMap( aSnippet ); + aCapInf.nIdx = nOldPos; + aCapInf.nLen = nTmp - nOldPos; + rDo.GetInf().SetIdx( 0 ); + rDo.GetInf().SetLen( aNewText.Len() ); + rDo.GetInf().SetText( aNewText ); + } + else + { + rDo.GetInf().SetIdx( nOldPos ); + rDo.GetInf().SetLen( nTmp - nOldPos ); + } + + rDo.GetInf().SetOut( *pOutSize ); + aPartSize = pBigFont->GetTextSize( rDo.GetInf() ); + nKana += rDo.GetInf().GetKanaDiff(); + rDo.GetInf().SetOut( *pOldOut ); + if( nSpaceAdd ) + aPartSize.Width() += nSpaceAdd * ( nTmp - nOldPos ); + if( nTmpKern && nPos < nMaxPos ) + aPartSize.Width() += nTmpKern; + rDo.Do(); + aStartPos = rDo.GetInf().GetPos(); + nOldPos = nTmp; + } + + while( nTmp < nPos && CH_BLANK != rOldText.GetChar( nTmp ) ) + ++nTmp; + } + else + nTmp = nPos; + if( nTmp > nOldPos ) + { + // #107816#, #i14820# + if( bCaseMapLengthDiffers ) + { + // Build an own 'changed' string for the given part of the + // source string and use it. That new string may differ in length + // from the source string. + const XubString aSnippet( rOldText, nOldPos, nTmp - nOldPos); + aNewText = CalcCaseMap( aSnippet ); + aCapInf.nIdx = nOldPos; + aCapInf.nLen = nTmp - nOldPos; + rDo.GetInf().SetIdx( 0 ); + rDo.GetInf().SetLen( aNewText.Len() ); + rDo.GetInf().SetText( aNewText ); + } + else + { + rDo.GetInf().SetIdx( nOldPos ); + rDo.GetInf().SetLen( nTmp - nOldPos ); + } + + rDo.GetInf().SetOut( *pOutSize ); + aPartSize = pBigFont->GetTextSize( rDo.GetInf() ); + nKana += rDo.GetInf().GetKanaDiff(); + rDo.GetInf().SetOut( *pOldOut ); + if( !bWordWise && rDo.GetInf().GetSpace() ) + { + for( xub_StrLen nI = nOldPos; nI < nPos; ++nI ) + { + if( CH_BLANK == rOldText.GetChar( nI ) ) + aPartSize.Width() += nSpaceAdd; + } + } + if( nTmpKern && nPos < nMaxPos ) + aPartSize.Width() += nTmpKern; + rDo.Do(); + nOldPos = nTmp; + } + } while( nOldPos != nPos ); + } + nPos = (xub_StrLen)pBreakIt->GetBreakIter()->endOfCharBlock( rOldText, nPos, + pBreakIt->GetLocale( eLng ), CharType::LOWERCASE_LETTER); + if( nPos == STRING_LEN || nPos > nMaxPos ) + nPos = nMaxPos; + ASSERT( nPos, "endOfCharBlock not implemented?" ); +#ifdef DBG_UTIL + if( !nPos ) + nPos = nMaxPos; +#endif + } + + // Aufraeumen: + if( pBigFont != pOldLast ) + delete pBigFontAccess; + + if( bTextLines ) + { + if( rDo.GetInf().GetDrawSpace() ) + { + pLastFont = pSpaceFont; + pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() ); + ( (SwDoDrawCapital&) rDo ).DrawSpace( aStartPos ); + } + if ( bWordWise ) + delete pSpaceFontAccess; + } + pLastFont = pOldLast; + pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() ); + + delete pSmallFontAccess; + rDo.GetInf().SetText( rOldText ); + rDo.GetInf().SetKanaDiff( nKana ); +} + +// JP 22.8.2001 - global optimization off - Bug 91245 / 91223 +#ifdef _MSC_VER +#pragma optimize("g",on) +#endif + + diff --git a/sw/source/core/txtnode/makefile.mk b/sw/source/core/txtnode/makefile.mk new file mode 100644 index 000000000000..f03f96c42674 --- /dev/null +++ b/sw/source/core/txtnode/makefile.mk @@ -0,0 +1,86 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=sw +TARGET=txtnode + +# --- Settings ----------------------------------------------------- + +.INCLUDE : $(PRJ)$/inc$/swpre.mk +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/inc$/sw.mk + +.IF "$(mydebug)" != "" +CDEFS+=-Dmydebug +.ENDIF + +# --- Files -------------------------------------------------------- + +SLOFILES = \ + $(SLO)$/atrfld.obj \ + $(SLO)$/atrflyin.obj \ + $(SLO)$/atrftn.obj \ + $(SLO)$/atrref.obj \ + $(SLO)$/atrtox.obj \ + $(SLO)$/chrfmt.obj \ + $(SLO)$/fmtatr2.obj \ + $(SLO)$/fntcap.obj \ + $(SLO)$/fntcache.obj \ + $(SLO)$/swfntcch.obj \ + $(SLO)$/SwGrammarContact.obj \ + $(SLO)$/modeltoviewhelper.obj \ + $(SLO)$/ndhints.obj \ + $(SLO)$/ndtxt.obj \ + $(SLO)$/swfont.obj \ + $(SLO)$/thints.obj \ + $(SLO)$/txatbase.obj \ + $(SLO)$/txatritr.obj \ + $(SLO)$/txtatr2.obj \ + $(SLO)$/txtedt.obj + + +EXCEPTIONSFILES = \ + $(SLO)$/fmtatr2.obj \ + $(SLO)$/atrtox.obj \ + $(SLO)$/atrflyin.obj \ + $(SLO)$/docnew.obj \ + $(SLO)$/fntcache.obj \ + $(SLO)$/ndtxt.obj \ + $(SLO)$/thints.obj \ + $(SLO)$/txtedt.obj + + +.IF "$(dbutil)" != "" +OFILES+=$(SLO)$/dbchratr.$(QBJX) +.ENDIF + +# --- Tagets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/sw/source/core/txtnode/modeltoviewhelper.cxx b/sw/source/core/txtnode/modeltoviewhelper.cxx new file mode 100644 index 000000000000..723c12bddbdc --- /dev/null +++ b/sw/source/core/txtnode/modeltoviewhelper.cxx @@ -0,0 +1,120 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" +#include <modeltoviewhelper.hxx> + +namespace ModelToViewHelper +{ + +/** Converts a model position into a view position +*/ +sal_uInt32 ConvertToViewPosition( const ConversionMap* pMap, sal_uInt32 nModelPos ) +{ + sal_uInt32 nRet = nModelPos; + + if ( !pMap ) + return nRet; + + // Search for entry behind nPos: + ConversionMap::const_iterator aIter; + for ( aIter = pMap->begin(); aIter != pMap->end(); ++aIter ) + { + if ( (*aIter).first >= nModelPos ) + { + const sal_uInt32 nPosModel = (*aIter).first; + const sal_uInt32 nPosExpand = (*aIter).second; + + const sal_uInt32 nDistToNextModel = nPosModel - nModelPos; + nRet = nPosExpand - nDistToNextModel; + break; + } + } + + return nRet; +} + + +/** Converts a view position into a model position +*/ +ModelPosition ConvertToModelPosition( const ConversionMap* pMap, sal_uInt32 nViewPos ) +{ + ModelPosition aRet; + aRet.mnPos = nViewPos; + + if ( !pMap ) + return aRet; + + // Search for entry behind nPos: + ConversionMap::const_iterator aIter; + for ( aIter = pMap->begin(); aIter != pMap->end(); ++aIter ) + { + if ( (*aIter).second > nViewPos ) + { + const sal_uInt32 nPosModel = (*aIter).first; + const sal_uInt32 nPosExpand = (*aIter).second; + + // If nViewPos is in front of first field, we are finished. + if ( aIter == pMap->begin() ) + break; + + --aIter; + + // nPrevPosModel is the field position + const sal_uInt32 nPrevPosModel = (*aIter).first; + const sal_uInt32 nPrevPosExpand = (*aIter).second; + + const sal_uInt32 nLengthModel = nPosModel - nPrevPosModel; + const sal_uInt32 nLengthExpand = nPosExpand - nPrevPosExpand; + + const sal_uInt32 nFieldLengthExpand = nLengthExpand - nLengthModel + 1; + const sal_uInt32 nFieldEndExpand = nPrevPosExpand + nFieldLengthExpand; + + // Check if nPos is outside of field: + if ( nFieldEndExpand <= nViewPos ) + { + // nPos is outside of field: + const sal_uInt32 nDistToField = nViewPos - nFieldEndExpand + 1; + aRet.mnPos = nPrevPosModel + nDistToField; + } + else + { + // nViewPos is inside a field: + aRet.mnPos = nPrevPosModel; + aRet.mnSubPos = nViewPos - nPrevPosExpand; + aRet.mbIsField = true; + } + + break; + } + } + + return aRet; +} + +} // namespace ModelToViewStringConverter end diff --git a/sw/source/core/txtnode/ndhints.cxx b/sw/source/core/txtnode/ndhints.cxx new file mode 100644 index 000000000000..0cdf431ca2e6 --- /dev/null +++ b/sw/source/core/txtnode/ndhints.cxx @@ -0,0 +1,489 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + + +#include "txatbase.hxx" +#include "ndhints.hxx" +#include <txtatr.hxx> + +#ifndef PRODUCT +#include <pam.hxx> +#endif + + +_SV_IMPL_SORTAR_ALG( SwpHtStart, SwTxtAttr* ) +_SV_IMPL_SORTAR_ALG( SwpHtEnd, SwTxtAttr* ) + +#ifdef NIE + +void DumpHints( const SwpHtStart &rHtStart, + const SwpHtEnd &rHtEnd ) +{ +#ifdef DBG_UTIL + aDbstream << "DumpHints:" << endl; + (aDbstream << "\tStarts:" ).WriteNumber(rHtStart.Count()) << endl; + for( USHORT i = 0; i < rHtStart.Count(); ++i ) + { + const SwTxtAttr *pHt = rHtStart[i]; + ((((aDbstream << '\t').WriteNumber( i )<< " [").WriteNumber( pHt->Which() ) + << ']' << '\t').WriteNumber( long( pHt ) ) + << '\t').WriteNumber( *pHt->GetStart() ); + if( pHt->GetEnd() ) + (aDbstream << " -> " ).WriteNumber( *pHt->GetEnd() ); + aDbstream << endl; + } + (aDbstream << "\tEnds:").WriteNumber( rHtEnd.Count() )<< endl; + for( i = 0; i < rHtEnd.Count(); ++i ) + { + const SwTxtAttr *pHt = rHtEnd[i]; + (((aDbstream << '\t').WriteNumber( i )<< " [").WriteNumber( pHt->Which() ) + << ']' << '\t' ).WriteNumber( long( pHt ) ); + if( pHt->GetEnd() ) + (aDbstream << '\t').WriteNumber( *pHt->GetEnd() )<< " <- "; + aDbstream.WriteNumber( *pHt->GetStart() )<< endl; + } + aDbstream << endl; +#endif +} +#else +inline void DumpHints(const SwpHtStart &, const SwpHtEnd &) { } +#endif + +/************************************************************************* + * inline IsEqual() + *************************************************************************/ + +inline BOOL IsEqual( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 ) +{ + return (long)(&rHt1) == (long)(&rHt2); +} + +/************************************************************************* + * IsLessStart() + *************************************************************************/ + +// SV_IMPL_OP_PTRARR_SORT( SwpHtStart, SwTxtAttr* ) +// kein SV_IMPL_PTRARR_SORT( name,ArrElement ) +// unser SEEK_PTR_TO_OBJECT_NOTL( name,ArrElement ) + +// Sortierreihenfolge: Start, Ende (umgekehrt!), Which-Wert (umgekehrt!), +// als letztes die Adresse selbst + +static BOOL lcl_IsLessStart( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 ) +{ + if ( *rHt1.GetStart() == *rHt2.GetStart() ) + { + const xub_StrLen nHt1 = *rHt1.GetAnyEnd(); + const xub_StrLen nHt2 = *rHt2.GetAnyEnd(); + if ( nHt1 == nHt2 ) + { + const USHORT nWhich1 = rHt1.Which(); + const USHORT nWhich2 = rHt2.Which(); + if ( nWhich1 == nWhich2 ) + { + if ( RES_TXTATR_CHARFMT == nWhich1 ) + { + const USHORT nS1 = static_cast<const SwTxtCharFmt&>(rHt1).GetSortNumber(); + const USHORT nS2 = static_cast<const SwTxtCharFmt&>(rHt2).GetSortNumber(); + ASSERT( nS1 != nS2, "AUTOSTYLES: lcl_IsLessStart trouble" ) + if ( nS1 != nS2 ) // robust + return nS1 < nS2; + } + + return (long)&rHt1 < (long)&rHt2; + } + // order is important! for requirements see hintids.hxx + return ( nWhich1 > nWhich2 ); + } + return ( nHt1 > nHt2 ); + } + return ( *rHt1.GetStart() < *rHt2.GetStart() ); +} + +/************************************************************************* + * inline IsLessEnd() + *************************************************************************/ + +// Zuerst nach Ende danach nach Ptr +static BOOL lcl_IsLessEnd( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 ) +{ + const xub_StrLen nHt1 = *rHt1.GetAnyEnd(); + const xub_StrLen nHt2 = *rHt2.GetAnyEnd(); + if ( nHt1 == nHt2 ) + { + if ( *rHt1.GetStart() == *rHt2.GetStart() ) + { + const USHORT nWhich1 = rHt1.Which(); + const USHORT nWhich2 = rHt2.Which(); + if ( nWhich1 == nWhich2 ) + { + if ( RES_TXTATR_CHARFMT == nWhich1 ) + { + const USHORT nS1 = static_cast<const SwTxtCharFmt&>(rHt1).GetSortNumber(); + const USHORT nS2 = static_cast<const SwTxtCharFmt&>(rHt2).GetSortNumber(); + ASSERT( nS1 != nS2, "AUTOSTYLES: lcl_IsLessEnd trouble" ) + if ( nS1 != nS2 ) // robust + return nS1 > nS2; + } + + return (long)&rHt1 > (long)&rHt2; + } + // order is important! for requirements see hintids.hxx + return ( nWhich1 < nWhich2 ); + } + else + return ( *rHt1.GetStart() > *rHt2.GetStart() ); + } + return ( nHt1 < nHt2 ); +} + +/************************************************************************* + * SwpHtStart::Seek_Entry() + *************************************************************************/ + +BOOL SwpHtStart::Seek_Entry( const SwTxtAttr *pElement, USHORT *pPos ) const +{ + USHORT nOben = Count(), nMitte, nUnten = 0; + if( nOben > 0 ) + { + nOben--; + while( nUnten <= nOben ) + { + nMitte = nUnten + ( nOben - nUnten ) / 2; + const SwTxtAttr *pMitte = (*this)[nMitte]; + if( IsEqual( *pMitte, *pElement ) ) + { + *pPos = nMitte; + return TRUE; + } + else + if( lcl_IsLessStart( *pMitte, *pElement ) ) + nUnten = nMitte + 1; + else + if( nMitte == 0 ) + { + *pPos = nUnten; + return FALSE; + } + else + nOben = nMitte - 1; + } + } + *pPos = nUnten; + return FALSE; +} + +/************************************************************************* + * SwpHtEnd::Seek_Entry() + *************************************************************************/ + +BOOL SwpHtEnd::Seek_Entry( const SwTxtAttr *pElement, USHORT *pPos ) const +{ + USHORT nOben = Count(), nMitte, nUnten = 0; + if( nOben > 0 ) + { + nOben--; + while( nUnten <= nOben ) + { + nMitte = nUnten + ( nOben - nUnten ) / 2; + const SwTxtAttr *pMitte = (*this)[nMitte]; + if( IsEqual( *pMitte, *pElement ) ) + { + *pPos = nMitte; + return TRUE; + } + else + if( lcl_IsLessEnd( *pMitte, *pElement ) ) + nUnten = nMitte + 1; + else + if( nMitte == 0 ) + { + *pPos = nUnten; + return FALSE; + } + else + nOben = nMitte - 1; + } + } + *pPos = nUnten; + return FALSE; +} + +/************************************************************************* + * class SwpHintsArr + *************************************************************************/ + +void SwpHintsArray::Insert( const SwTxtAttr *pHt ) +{ + Resort(); +#ifdef DBG_UTIL + USHORT nPos; + ASSERT(!m_HintStarts.Seek_Entry( pHt, &nPos ), + "Insert: hint already in HtStart"); + ASSERT(!m_HintEnds.Seek_Entry( pHt, &nPos ), + "Insert: hint already in HtEnd"); +#endif + m_HintStarts.Insert( pHt ); + m_HintEnds.Insert( pHt ); +#ifdef DBG_UTIL +#ifdef NIE + (aDbstream << "Insert: " ).WriteNumber( long( pHt ) ) << endl; + DumpHints( m_HintStarts, m_HintEnds ); +#endif +#endif +} + +void SwpHintsArray::DeleteAtPos( const USHORT nPos ) +{ + // optimization: nPos is the position in the Starts array + const SwTxtAttr *pHt = m_HintStarts[ nPos ]; + m_HintStarts.Remove( nPos ); + + Resort(); + + USHORT nEndPos; + m_HintEnds.Seek_Entry( pHt, &nEndPos ); + m_HintEnds.Remove( nEndPos ); +#ifdef DBG_UTIL +#ifdef NIE + (aDbstream << "DeleteAtPos: " ).WriteNumber( long( pHt ) ) << endl; + DumpHints( m_HintStarts, m_HintEnds ); +#endif +#endif +} + +#ifdef DBG_UTIL + +/************************************************************************* + * SwpHintsArray::Check() + *************************************************************************/ + + +#define CHECK_ERR(cond, text) \ + if(!(cond)) \ + { \ + ASSERT(!this, text); \ + DumpHints(m_HintStarts, m_HintEnds); \ + return !(const_cast<SwpHintsArray*>(this))->Resort(); \ + } + +bool SwpHintsArray::Check() const +{ + // 1) gleiche Anzahl in beiden Arrays + CHECK_ERR( m_HintStarts.Count() == m_HintEnds.Count(), + "HintsCheck: wrong sizes" ); + xub_StrLen nLastStart = 0; + xub_StrLen nLastEnd = 0; + + const SwTxtAttr *pLastStart = 0; + const SwTxtAttr *pLastEnd = 0; + + for( USHORT i = 0; i < Count(); ++i ) + { + // --- Start-Kontrolle --- + + // 2a) gueltiger Pointer? vgl. DELETEFF + const SwTxtAttr *pHt = m_HintStarts[i]; + CHECK_ERR( 0xFF != *(unsigned char*)pHt, "HintsCheck: start ptr was deleted" ); + + // 3a) Stimmt die Start-Sortierung? + xub_StrLen nIdx = *pHt->GetStart(); + CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" ); + + // 4a) IsLessStart-Konsistenz + if( pLastStart ) + CHECK_ERR( lcl_IsLessStart( *pLastStart, *pHt ), "HintsCheck: IsLastStart" ); + + nLastStart = nIdx; + pLastStart = pHt; + + // --- End-Kontrolle --- + + // 2b) gueltiger Pointer? vgl. DELETEFF + const SwTxtAttr *pHtEnd = m_HintEnds[i]; + CHECK_ERR( 0xFF != *(unsigned char*)pHtEnd, "HintsCheck: end ptr was deleted" ); + + // 3b) Stimmt die End-Sortierung? + nIdx = *pHtEnd->GetAnyEnd(); + CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" ); + nLastEnd = nIdx; + + // 4b) IsLessEnd-Konsistenz + if( pLastEnd ) + CHECK_ERR( lcl_IsLessEnd( *pLastEnd, *pHtEnd ), "HintsCheck: IsLastEnd" ); + + nLastEnd = nIdx; + pLastEnd = pHtEnd; + + // --- Ueberkreuzungen --- + + // 5) gleiche Pointer in beiden Arrays + if( !m_HintStarts.Seek_Entry( pHt, &nIdx ) ) + nIdx = STRING_LEN; + + CHECK_ERR( STRING_LEN != nIdx, "HintsCheck: no GetStartOf" ); + + // 6) gleiche Pointer in beiden Arrays + if( !m_HintEnds.Seek_Entry( pHt, &nIdx ) ) + nIdx = STRING_LEN; + + CHECK_ERR( STRING_LEN != nIdx, "HintsCheck: no GetEndOf" ); + + // 7a) character attributes in array? + USHORT nWhich = pHt->Which(); + CHECK_ERR( !isCHRATR(nWhich), + "HintsCheck: Character attribute in start array" ); + + // 7b) character attributes in array? + nWhich = pHtEnd->Which(); + CHECK_ERR( !isCHRATR(nWhich), + "HintsCheck: Character attribute in end array" ); + + // 8) portion check +#if OSL_DEBUG_LEVEL > 1 + const SwTxtAttr* pHtThis = m_HintStarts[i]; + const SwTxtAttr* pHtLast = i > 0 ? m_HintStarts[i-1] : 0; + CHECK_ERR( 0 == i || + ( RES_TXTATR_CHARFMT != pHtLast->Which() && RES_TXTATR_AUTOFMT != pHtLast->Which() ) || + ( RES_TXTATR_CHARFMT != pHtThis->Which() && RES_TXTATR_AUTOFMT != pHtThis->Which() ) || + ( *pHtThis->GetStart() >= *pHtLast->GetEnd() ) || + ( *pHtThis->GetStart() == *pHtLast->GetStart() && *pHtThis->GetEnd() == *pHtLast->GetEnd() ) || + ( *pHtThis->GetStart() == *pHtThis->GetEnd() ), + "HintsCheck: Portion inconsistency. " + "This can be temporarily ok during undo operations" ); + + if (pHtThis->IsNesting()) + { + for ( USHORT j = 0; j < Count(); ++j ) + { + SwTxtAttr const * const pOther( m_HintStarts[j] ); + if ( pOther->IsNesting() && (i != j) ) + { + SwComparePosition cmp = ComparePosition( + *pHtThis->GetStart(), *pHtThis->GetEnd(), + *pOther->GetStart(), *pOther->GetEnd()); + CHECK_ERR( (POS_OVERLAP_BEFORE != cmp) && + (POS_OVERLAP_BEHIND != cmp), + "HintsCheck: overlapping nesting hints!!!" ); + } + } + } + + // 9) dummy char check (unfortunately cannot check SwTxtNode::m_Text) + if (pHtThis->HasDummyChar()) + { + for ( USHORT j = 0; j < i; ++j ) + { + SwTxtAttr const * const pOther( m_HintStarts[j] ); + if (pOther->HasDummyChar()) + { + CHECK_ERR( (*pOther->GetStart() != *pHtThis->GetStart()), + "HintsCheck: multiple hints claim same CH_TXTATR!"); + } + } + } +#endif + } + return true; +} + +#endif /* PRODUCT */ + +/************************************************************************* + * SwpHintsArray::Resort() + *************************************************************************/ + +// Resort() wird vor jedem Insert und Delete gerufen. +// Wenn Textmasse geloescht wird, so werden die Indizes in +// ndtxt.cxx angepasst. Leider erfolgt noch keine Neusortierung +// auf gleichen Positionen. + +bool SwpHintsArray::Resort() +{ + bool bResort = false; + const SwTxtAttr *pLast = 0; + USHORT i; + + for ( i = 0; i < m_HintStarts.Count(); ++i ) + { + const SwTxtAttr *pHt = m_HintStarts[i]; + if( pLast && !lcl_IsLessStart( *pLast, *pHt ) ) + { +#ifdef NIE +#ifdef DBG_UTIL +// ASSERT( bResort, "!Resort/Start: correcting hints-array" ); + aDbstream << "Resort: Starts" << endl; + DumpHints( m_HintStarts, m_HintEnds ); +#endif +#endif + m_HintStarts.Remove( i ); + m_HintStarts.Insert( pHt ); + pHt = m_HintStarts[i]; + if ( pHt != pLast ) + --i; + bResort = true; + } + pLast = pHt; + } + + pLast = 0; + for ( i = 0; i < m_HintEnds.Count(); ++i ) + { + const SwTxtAttr *pHt = m_HintEnds[i]; + if( pLast && !lcl_IsLessEnd( *pLast, *pHt ) ) + { +#ifdef NIE +#ifdef DBG_UTIL + aDbstream << "Resort: Ends" << endl; + DumpHints( m_HintStarts, m_HintEnds ); +#endif +#endif + m_HintEnds.Remove( i ); + m_HintEnds.Insert( pHt ); + pHt = m_HintEnds[i]; // normalerweise == pLast + // Wenn die Unordnung etwas groesser ist (24200), + // muessen wir Position i erneut vergleichen. + if ( pLast != pHt ) + --i; + bResort = true; + } + pLast = pHt; + } +#ifdef DBG_UTIL +#ifdef NIE + aDbstream << "Resorted:" << endl; + DumpHints( m_HintStarts, m_HintEnds ); +#endif +#endif + return bResort; +} + + diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx new file mode 100644 index 000000000000..d6770321768f --- /dev/null +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -0,0 +1,5034 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" +#include <hintids.hxx> +#include <hints.hxx> + +#include <editeng/fontitem.hxx> +#include <editeng/brkitem.hxx> +#include <editeng/escpitem.hxx> +#include <editeng/lrspitem.hxx> +// --> OD 2008-01-17 #newlistlevelattrs# +#include <editeng/tstpitem.hxx> +// <-- +#include <svl/urihelper.hxx> +#ifndef _SVSTDARR_HXX +#define _SVSTDARR_ULONGS +#include <svl/svstdarr.hxx> +#endif +#include <svl/ctloptions.hxx> +#include <swmodule.hxx> +#include <txtfld.hxx> +#include <txtinet.hxx> +#include <fmtinfmt.hxx> +#include <fmtpdsc.hxx> +#include <txtatr.hxx> +#include <fmtrfmrk.hxx> +#include <txttxmrk.hxx> +#include <fchrfmt.hxx> +#include <txtftn.hxx> +#include <fmtflcnt.hxx> +#include <fmtfld.hxx> +#include <frmatr.hxx> +#include <charatr.hxx> +#include <ftnidx.hxx> +#include <ftninfo.hxx> +#include <fmtftn.hxx> +#include <fmtmeta.hxx> +#include <charfmt.hxx> +#include <ndtxt.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <pam.hxx> // fuer SwPosition +#include <fldbas.hxx> +#include <errhdl.hxx> +#include <paratr.hxx> +#include <txtfrm.hxx> +#include <ftnfrm.hxx> +#include <ftnboss.hxx> +#include <rootfrm.hxx> +#include <pagedesc.hxx> // fuer SwPageDesc +#include <expfld.hxx> // fuer SwTblField +#include <section.hxx> // fuer SwSection +#include <mvsave.hxx> +#include <swcache.hxx> +#include <SwGrammarMarkUp.hxx> +#include <dcontact.hxx> +#include <redline.hxx> +#include <doctxm.hxx> +#include <IMark.hxx> +#include <scriptinfo.hxx> +#include <istyleaccess.hxx> +#include <SwStyleNameMapper.hxx> +#include <numrule.hxx> + +//--> #outlinelevel added by zhaojianwei +#include <svl/intitem.hxx> +//<--end +#include <swtable.hxx> +#include <docsh.hxx> +#include <SwNodeNum.hxx> +// --> OD 2008-02-25 #refactorlists# +#include <svl/intitem.hxx> +#include <list.hxx> +// <-- + +SV_DECL_PTRARR( TmpHints, SwTxtAttr*, 0, 4 ) + +TYPEINIT1( SwTxtNode, SwCntntNode ) + +SV_DECL_PTRARR(SwpHts,SwTxtAttr*,1,1) + +// Leider ist das SwpHints nicht ganz wasserdicht: +// Jeder darf an den Hints rumfummeln, ohne die Sortierreihenfolge +// und Verkettung sicherstellen zu muessen. +#ifdef DBG_UTIL +#define CHECK_SWPHINTS(pNd) { if( pNd->GetpSwpHints() && \ + !pNd->GetDoc()->IsInReading() ) \ + pNd->GetpSwpHints()->Check(); } +#else +#define CHECK_SWPHINTS(pNd) +#endif + +SwTxtNode *SwNodes::MakeTxtNode( const SwNodeIndex & rWhere, + SwTxtFmtColl *pColl, + SwAttrSet* pAutoAttr ) +{ + ASSERT( pColl, "Collectionpointer ist 0." ); + + SwTxtNode *pNode = new SwTxtNode( rWhere, pColl, pAutoAttr ); + + SwNodeIndex aIdx( *pNode ); + + // --> OD 2005-11-03 #125329# + // call method <UpdateOutlineNode(..)> only for the document nodes array + if ( IsDocNodes() ) + { + //if ( pColl && NO_NUMBERING != pColl->GetOutlineLevel() ) //#outline level,removed by zhaojianwei + //{ + // UpdateOutlineNode( *pNode, NO_NUMBERING, pColl->GetOutlineLevel() ); + //} +// if ( pColl && 0 != pColl->GetAttrOutlineLevel() )//#outline level,added by zhaojianwei +// { +// UpdateOutlineNode( *pNode, 0, pColl->GetAttrOutlineLevel() ); +// }//<--end +// else + { + UpdateOutlineNode(*pNode); + } + } + // <-- + + //Wenn es noch kein Layout gibt oder in einer versteckten Section + // stehen, brauchen wir uns um das MakeFrms nicht bemuehen. + const SwSectionNode* pSectNd; + if( !GetDoc()->GetRootFrm() || + ( 0 != (pSectNd = pNode->FindSectionNode()) && + pSectNd->GetSection().IsHiddenFlag() )) + return pNode; + + SwNodeIndex aTmp( rWhere ); + do { + // max. 2 Durchlaeufe: + // 1. den Nachfolger nehmen + // 2. den Vorgaenger + + SwNode *pNd; + switch( ( pNd = (*this)[aTmp] )->GetNodeType() ) + { + case ND_TABLENODE: + ((SwTableNode*)pNd)->MakeFrms( aIdx ); + return pNode; + + case ND_SECTIONNODE: + if( ((SwSectionNode*)pNd)->GetSection().IsHidden() || + ((SwSectionNode*)pNd)->IsCntntHidden() ) + { + SwNodeIndex aTmpIdx( *pNode ); + pNd = FindPrvNxtFrmNode( aTmpIdx, pNode ); + if( !pNd ) + return pNode; + aTmp = *pNd; + break; + } + ((SwSectionNode*)pNd)->MakeFrms( aIdx ); + return pNode; + + case ND_TEXTNODE: + case ND_GRFNODE: + case ND_OLENODE: + ((SwCntntNode*)pNd)->MakeFrms( *pNode ); + return pNode; + + case ND_ENDNODE: + if( pNd->StartOfSectionNode()->IsSectionNode() && + aTmp.GetIndex() < rWhere.GetIndex() ) + { + if( pNd->StartOfSectionNode()->GetSectionNode()->GetSection().IsHiddenFlag()) + { + if( !GoPrevSection( &aTmp, TRUE, FALSE ) || + aTmp.GetNode().FindTableNode() != + pNode->FindTableNode() ) + return pNode; // schade, das wars + } + else + aTmp = *pNd->StartOfSectionNode(); + break; + } + else if( pNd->StartOfSectionNode()->IsTableNode() && + aTmp.GetIndex() < rWhere.GetIndex() ) + { + // wir stehen hinter einem TabellenNode + aTmp = *pNd->StartOfSectionNode(); + break; + } + // kein break !!! + default: + if( rWhere == aTmp ) + aTmp -= 2; + else + return pNode; + break; + } + } while( TRUE ); +} + +// -------------------- +// SwTxtNode +// -------------------- + +SwTxtNode::SwTxtNode( const SwNodeIndex &rWhere, + SwTxtFmtColl *pTxtColl, + const SfxItemSet* pAutoAttr ) + : SwCntntNode( rWhere, ND_TEXTNODE, pTxtColl ), + m_pSwpHints( 0 ), + mpNodeNum( 0 ), + m_bLastOutlineState( false ), + m_bNotifiable( false ), + // --> OD 2008-11-19 #i70748# + mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ), + // <-- + // --> OD 2008-05-06 #refactorlists# + mbInSetOrResetAttr( false ), + mpList( 0 ) + // <-- +{ + InitSwParaStatistics( true ); + + // soll eine Harte-Attributierung gesetzt werden? + if( pAutoAttr ) + SetAttr( *pAutoAttr ); + + // --> OD 2008-03-13 #refactorlists# - no longed needed +// SyncNumberAndNumRule(); + if ( !IsInList() && GetNumRule() && GetListId().Len() > 0 ) + { + // --> OD 2009-08-27 #i101516# + // apply paragraph style's assigned outline style list level as + // list level of the paragraph, if it has none set already. + if ( !HasAttrListLevel() && + pTxtColl && pTxtColl->IsAssignedToListLevelOfOutlineStyle() ) + { + SetAttrListLevel( pTxtColl->GetAssignedOutlineStyleLevel() ); + } + // <-- + AddToList(); + } + // <-- + GetNodes().UpdateOutlineNode(*this); + + m_bNotifiable = true; + + m_bContainsHiddenChars = m_bHiddenCharsHidePara = false; + m_bRecalcHiddenCharFlags = true; +} + +SwTxtNode::~SwTxtNode() +{ + // delete loescht nur die Pointer, nicht die Arrayelemente! + if ( m_pSwpHints ) + { + // damit Attribute die ihren Inhalt entfernen nicht doppelt + // geloescht werden. + SwpHints* pTmpHints = m_pSwpHints; + m_pSwpHints = 0; + + for( USHORT j = pTmpHints->Count(); j; ) + // erst muss das Attribut aus dem Array entfernt werden, + // denn sonst wuerde es sich selbst loeschen (Felder) !!!! + DestroyAttr( pTmpHints->GetTextHint( --j ) ); + + delete pTmpHints; + } + + // --> OD 2008-03-13 #refactorlists# +// if ( mpNodeNum ) +// { +// mpNodeNum->RemoveMe(); +// delete mpNodeNum; +// mpNodeNum = 0L; +// } + RemoveFromList(); + // <-- + + InitSwParaStatistics( false ); +} + +SwCntntFrm *SwTxtNode::MakeFrm() +{ + SwCntntFrm *pFrm = new SwTxtFrm(this); + return pFrm; +} + +xub_StrLen SwTxtNode::Len() const +{ + return m_Text.Len(); +} + +/*--------------------------------------------------------------------------- + * lcl_ChangeFtnRef + * After a split node, it's necessary to actualize the ref-pointer of the + * ftnfrms. + * --------------------------------------------------------------------------*/ + +void lcl_ChangeFtnRef( SwTxtNode &rNode ) +{ + SwpHints *pSwpHints = rNode.GetpSwpHints(); + if( pSwpHints && rNode.GetDoc()->GetRootFrm() ) + { + SwTxtAttr* pHt; + SwCntntFrm* pFrm = NULL; + // OD 07.11.2002 #104840# - local variable to remember first footnote + // of node <rNode> in order to invalidate position of its first content. + // Thus, in its <MakeAll()> it will checked its position relative to its reference. + SwFtnFrm* pFirstFtnOfNode = 0; + for( USHORT j = pSwpHints->Count(); j; ) + { + pHt = pSwpHints->GetTextHint(--j); + if (RES_TXTATR_FTN == pHt->Which()) + { + if( !pFrm ) + { + SwClientIter aNew( rNode ); + pFrm = (SwCntntFrm*)aNew.First( TYPE(SwCntntFrm) ); + if( !pFrm ) + return; + } + SwTxtFtn *pAttr = (SwTxtFtn*)pHt; + ASSERT( pAttr->GetStartNode(), "FtnAtr ohne StartNode." ); + SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 ); + SwCntntNode *pNd = aIdx.GetNode().GetCntntNode(); + if ( !pNd ) + pNd = pFrm->GetAttrSet()->GetDoc()-> + GetNodes().GoNextSection( &aIdx, TRUE, FALSE ); + if ( !pNd ) + continue; + SwClientIter aIter( *pNd ); + SwCntntFrm* pCntnt = (SwCntntFrm*)aIter.First(TYPE(SwCntntFrm)); + if( pCntnt ) + { + ASSERT( pCntnt->FindRootFrm() == pFrm->FindRootFrm(), + "lcl_ChangeFtnRef: Layout double?" ); + SwFtnFrm *pFtn = pCntnt->FindFtnFrm(); + if( pFtn && pFtn->GetAttr() == pAttr ) + { + while( pFtn->GetMaster() ) + pFtn = pFtn->GetMaster(); + // OD 07.11.2002 #104840# - remember footnote frame + pFirstFtnOfNode = pFtn; + while ( pFtn ) + { + pFtn->SetRef( pFrm ); + pFtn = pFtn->GetFollow(); + ((SwTxtFrm*)pFrm)->SetFtn( TRUE ); + } + } +#ifdef DBG_UTIL + while( 0 != (pCntnt = (SwCntntFrm*)aIter.Next()) ) + { + SwFtnFrm *pDbgFtn = pCntnt->FindFtnFrm(); + ASSERT( !pDbgFtn || pDbgFtn->GetRef() == pFrm, + "lcl_ChangeFtnRef: Who's that guy?" ); + } +#endif + } + } + } // end of for-loop on <SwpHints> + // OD 08.11.2002 #104840# - invalidate + if ( pFirstFtnOfNode ) + { + SwCntntFrm* pCntnt = pFirstFtnOfNode->ContainsCntnt(); + if ( pCntnt ) + { + pCntnt->_InvalidatePos(); + } + } + } +} + +SwCntntNode *SwTxtNode::SplitCntntNode( const SwPosition &rPos ) +{ + // lege den Node "vor" mir an + const xub_StrLen nSplitPos = rPos.nContent.GetIndex(); + const xub_StrLen nTxtLen = m_Text.Len(); + SwTxtNode* const pNode = + _MakeNewTxtNode( rPos.nNode, FALSE, nSplitPos==nTxtLen ); + + // the first paragraph gets the XmlId, + // _except_ if it is empty and the second is not empty + if (nSplitPos != 0) { + pNode->RegisterAsCopyOf(*this, true); + if (nSplitPos == nTxtLen) + { + this->RemoveMetadataReference(); + // NB: SwUndoSplitNode will call pNode->JoinNext, + // which is sufficient even in this case! + } + } + + // --> OD 2008-03-27 #refactorlists# +// // --> OD 2007-07-09 #i77372# +// // reset numbering attribute at current node, only if it is numbered. +// if ( GetNumRule() != NULL ) +// { +// SetRestart(false); +// SetStart(1); +// SetCounted(true); +// } + ResetAttr( RES_PARATR_LIST_ISRESTART ); + ResetAttr( RES_PARATR_LIST_RESTARTVALUE ); + ResetAttr( RES_PARATR_LIST_ISCOUNTED ); + if ( GetNumRule() == 0 ) + { + ResetAttr( RES_PARATR_LIST_ID ); + ResetAttr( RES_PARATR_LIST_LEVEL ); + } + // <-- + + if ( GetDepends() && m_Text.Len() && (nTxtLen / 2) < nSplitPos ) + { +// JP 25.04.95: Optimierung fuer SplitNode: +// Wird am Ende vom Node gesplittet, dann verschiebe die +// Frames vom akt. auf den neuen und erzeuge fuer den akt. +// neue. Dadurch entfaellt das neu aufbauen vom Layout. + + LockModify(); // Benachrichtigungen abschalten + + // werden FlyFrames mit verschoben, so muessen diese nicht ihre + // Frames zerstoeren. Im SwTxtFly::SetAnchor wird es abgefragt! + if ( HasHints() ) + { + pNode->GetOrCreateSwpHints().SetInSplitNode(true); + } + + //Ersten Teil des Inhalts in den neuen Node uebertragen und + //im alten Node loeschen. + SwIndex aIdx( this ); + CutText( pNode, aIdx, nSplitPos ); + + if( GetWrong() ) + { + pNode->SetWrong( GetWrong()->SplitList( nSplitPos ) ); + } + SetWrongDirty( true ); + + if( GetGrammarCheck() ) + { + pNode->SetGrammarCheck( GetGrammarCheck()->SplitGrammarList( nSplitPos ) ); + } + SetGrammarCheckDirty( true ); + + SetWordCountDirty( true ); + + // SMARTTAGS + if( GetSmartTags() ) + { + pNode->SetSmartTags( GetSmartTags()->SplitList( nSplitPos ) ); + } + SetSmartTagDirty( true ); + + if ( pNode->HasHints() ) + { + if ( pNode->m_pSwpHints->CanBeDeleted() ) + { + delete pNode->m_pSwpHints; + pNode->m_pSwpHints = 0; + } + else + { + pNode->m_pSwpHints->SetInSplitNode(false); + } + + // alle zeichengebundenen Rahmen, die im neuen Absatz laden + // muessen aus den alten Frame entfernt werden: + // JP 01.10.96: alle leeren und nicht zu expandierenden + // Attribute loeschen + if ( HasHints() ) + { + for ( USHORT j = m_pSwpHints->Count(); j; ) + { + SwTxtAttr* const pHt = m_pSwpHints->GetTextHint( --j ); + if ( RES_TXTATR_FLYCNT == pHt ->Which() ) + { + pHt->GetFlyCnt().GetFrmFmt()->DelFrms(); + } + else if ( pHt->DontExpand() ) + { + const xub_StrLen* const pEnd = pHt->GetEnd(); + if (pEnd && *pHt->GetStart() == *pEnd ) + { + // delete it! + m_pSwpHints->DeleteAtPos( j ); + DestroyAttr( pHt ); + } + } + } + } + + } + + SwClientIter aIter( *this ); + SwClient* pLastFrm = aIter.GoStart(); + if( pLastFrm ) + { + do + { SwCntntFrm *pFrm = PTR_CAST( SwCntntFrm, pLastFrm ); + if ( pFrm ) + { + pNode->Add( pFrm ); + if( pFrm->IsTxtFrm() && !pFrm->IsFollow() && + ((SwTxtFrm*)pFrm)->GetOfst() ) + ((SwTxtFrm*)pFrm)->SetOfst( 0 ); + } + pLastFrm = aIter++; + } while ( pLastFrm ); + } + + if ( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( FALSE ); + } + + UnlockModify(); // Benachrichtigungen wieder freischalten + + const SwRootFrm * const pRootFrm = pNode->GetDoc()->GetRootFrm(); + // If there is an accessible layout we must call modify even + // with length zero, because we have to notify about the changed + // text node. + if ( (nTxtLen != nSplitPos) || + ( pRootFrm && pRootFrm->IsAnyShellAccessible() ) ) + + { + // dann sage den Frames noch, das am Ende etwas "geloescht" wurde + if( 1 == nTxtLen - nSplitPos ) + { + SwDelChr aHint( nSplitPos ); + pNode->SwModify::Modify( 0, &aHint ); + } + else + { + SwDelTxt aHint( nSplitPos, nTxtLen - nSplitPos ); + pNode->SwModify::Modify( 0, &aHint ); + } + } + if ( HasHints() ) + { + MoveTxtAttr_To_AttrSet(); + } + pNode->MakeFrms( *this ); // neue Frames anlegen. + lcl_ChangeFtnRef( *this ); + } + else + { + SwWrongList *pList = GetWrong(); + SetWrong( 0, false ); + SetWrongDirty( true ); + + SwGrammarMarkUp *pList3 = GetGrammarCheck(); + SetGrammarCheck( 0, false ); + SetGrammarCheckDirty( true ); + + SetWordCountDirty( true ); + + // SMARTTAGS + SwWrongList *pList2 = GetSmartTags(); + SetSmartTags( 0, false ); + SetSmartTagDirty( true ); + + SwIndex aIdx( this ); + CutText( pNode, aIdx, nSplitPos ); + + // JP 01.10.96: alle leeren und nicht zu expandierenden + // Attribute loeschen + if ( HasHints() ) + { + for ( USHORT j = m_pSwpHints->Count(); j; ) + { + SwTxtAttr* const pHt = m_pSwpHints->GetTextHint( --j ); + const xub_StrLen* const pEnd = pHt->GetEnd(); + if ( pHt->DontExpand() && pEnd && (*pHt->GetStart() == *pEnd) ) + { + // delete it! + m_pSwpHints->DeleteAtPos( j ); + DestroyAttr( pHt ); + } + } + MoveTxtAttr_To_AttrSet(); + } + + if( pList ) + { + pNode->SetWrong( pList->SplitList( nSplitPos ) ); + SetWrong( pList, false ); + } + + if( pList3 ) + { + pNode->SetGrammarCheck( pList3->SplitGrammarList( nSplitPos ) ); + SetGrammarCheck( pList3, false ); + } + + // SMARTTAGS + if( pList2 ) + { + pNode->SetSmartTags( pList2->SplitList( nSplitPos ) ); + SetSmartTags( pList2, false ); + } + + if ( GetDepends() ) + { + MakeFrms( *pNode ); // neue Frames anlegen. + } + lcl_ChangeFtnRef( *pNode ); + } + + { + //Hint fuer Pagedesc versenden. Das mueste eigntlich das Layout im + //Paste der Frames selbst erledigen, aber das fuehrt dann wiederum + //zu weiteren Folgefehlern, die mit Laufzeitkosten geloest werden + //muesten. #56977# #55001# #56135# + const SfxPoolItem *pItem; + if( GetDepends() && SFX_ITEM_SET == pNode->GetSwAttrSet(). + GetItemState( RES_PAGEDESC, TRUE, &pItem ) ) + { + pNode->Modify( (SfxPoolItem*)pItem, (SfxPoolItem*)pItem ); + } + } + return pNode; +} + +void SwTxtNode::MoveTxtAttr_To_AttrSet() +{ + ASSERT( m_pSwpHints, "MoveTxtAttr_To_AttrSet without SwpHints?" ); + for ( USHORT i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i ) + { + SwTxtAttr *pHt = m_pSwpHints->GetTextHint(i); + + if( *pHt->GetStart() ) + break; + + const xub_StrLen* pHtEndIdx = pHt->GetEnd(); + + if( !pHtEndIdx ) + continue; + + if ( *pHtEndIdx < m_Text.Len() || pHt->IsCharFmtAttr() ) + break; + + if( !pHt->IsDontMoveAttr() && + SetAttr( pHt->GetAttr() ) ) + { + m_pSwpHints->DeleteAtPos(i); + DestroyAttr( pHt ); + --i; + } + } + +} + +SwCntntNode *SwTxtNode::JoinNext() +{ + SwNodes& rNds = GetNodes(); + SwNodeIndex aIdx( *this ); + if( SwCntntNode::CanJoinNext( &aIdx ) ) + { + SwDoc* pDoc = rNds.GetDoc(); + SvULongs aBkmkArr( 15, 15 ); + _SaveCntntIdx( pDoc, aIdx.GetIndex(), USHRT_MAX, aBkmkArr, SAVEFLY ); + SwTxtNode *pTxtNode = aIdx.GetNode().GetTxtNode(); + xub_StrLen nOldLen = m_Text.Len(); + + // METADATA: merge + this->JoinMetadatable(*pTxtNode, !this->Len(), !pTxtNode->Len()); + + SwWrongList *pList = GetWrong(); + if( pList ) + { + pList->JoinList( pTxtNode->GetWrong(), nOldLen ); + SetWrongDirty( true ); + SetWrong( 0, false ); + } + else + { + pList = pTxtNode->GetWrong(); + if( pList ) + { + pList->Move( 0, nOldLen ); + SetWrongDirty( true ); + pTxtNode->SetWrong( 0, false ); + } + } + + SwGrammarMarkUp *pList3 = GetGrammarCheck(); + if( pList3 ) + { + pList3->JoinGrammarList( pTxtNode->GetGrammarCheck(), nOldLen ); + SetGrammarCheckDirty( true ); + SetGrammarCheck( 0, false ); + } + else + { + pList3 = pTxtNode->GetGrammarCheck(); + if( pList3 ) + { + pList3->MoveGrammar( 0, nOldLen ); + SetGrammarCheckDirty( true ); + pTxtNode->SetGrammarCheck( 0, false ); + } + } + + // SMARTTAGS + SwWrongList *pList2 = GetSmartTags(); + if( pList2 ) + { + pList2->JoinList( pTxtNode->GetSmartTags(), nOldLen ); + SetSmartTagDirty( true ); + SetSmartTags( 0, false ); + } + else + { + pList2 = pTxtNode->GetSmartTags(); + if( pList2 ) + { + pList2->Move( 0, nOldLen ); + SetSmartTagDirty( true ); + pTxtNode->SetSmartTags( 0, false ); + } + } + + { // wg. SwIndex + pTxtNode->CutText( this, SwIndex(pTxtNode), pTxtNode->Len() ); + } + // verschiebe noch alle Bookmarks/TOXMarks + if( aBkmkArr.Count() ) + _RestoreCntntIdx( pDoc, aBkmkArr, GetIndex(), nOldLen ); + + if( pTxtNode->HasAnyIndex() ) + { + // alle Crsr/StkCrsr/UnoCrsr aus dem Loeschbereich verschieben + pDoc->CorrAbs( aIdx, SwPosition( *this ), nOldLen, TRUE ); + } + rNds.Delete(aIdx); + SetWrong( pList, false ); + SetGrammarCheck( pList3, false ); + SetSmartTags( pList2, false ); // SMARTTAGS + InvalidateNumRule(); + } + else { + ASSERT( FALSE, "kein TxtNode." ); + } + + return this; +} + +SwCntntNode *SwTxtNode::JoinPrev() +{ + SwNodes& rNds = GetNodes(); + SwNodeIndex aIdx( *this ); + if( SwCntntNode::CanJoinPrev( &aIdx ) ) + { + SwDoc* pDoc = rNds.GetDoc(); + SvULongs aBkmkArr( 15, 15 ); + _SaveCntntIdx( pDoc, aIdx.GetIndex(), USHRT_MAX, aBkmkArr, SAVEFLY ); + SwTxtNode *pTxtNode = aIdx.GetNode().GetTxtNode(); + xub_StrLen nLen = pTxtNode->Len(); + + SwWrongList *pList = pTxtNode->GetWrong(); + if( pList ) + { + pList->JoinList( GetWrong(), Len() ); + SetWrongDirty( true ); + pTxtNode->SetWrong( 0, false ); + SetWrong( NULL ); + } + else + { + pList = GetWrong(); + if( pList ) + { + pList->Move( 0, nLen ); + SetWrongDirty( true ); + SetWrong( 0, false ); + } + } + + SwGrammarMarkUp *pList3 = pTxtNode->GetGrammarCheck(); + if( pList3 ) + { + pList3->JoinGrammarList( GetGrammarCheck(), Len() ); + SetGrammarCheckDirty( true ); + pTxtNode->SetGrammarCheck( 0, false ); + SetGrammarCheck( NULL ); + } + else + { + pList3 = GetGrammarCheck(); + if( pList3 ) + { + pList3->MoveGrammar( 0, nLen ); + SetGrammarCheckDirty( true ); + SetGrammarCheck( 0, false ); + } + } + + // SMARTTAGS + SwWrongList *pList2 = pTxtNode->GetSmartTags(); + if( pList2 ) + { + pList2->JoinList( GetSmartTags(), Len() ); + SetSmartTagDirty( true ); + pTxtNode->SetSmartTags( 0, false ); + SetSmartTags( NULL ); + } + else + { + pList2 = GetSmartTags(); + if( pList2 ) + { + pList2->Move( 0, nLen ); + SetSmartTagDirty( true ); + SetSmartTags( 0, false ); + } + } + + { // wg. SwIndex + pTxtNode->CutText( this, SwIndex(this), SwIndex(pTxtNode), nLen ); + } + // verschiebe noch alle Bookmarks/TOXMarks + if( aBkmkArr.Count() ) + _RestoreCntntIdx( pDoc, aBkmkArr, GetIndex() ); + + if( pTxtNode->HasAnyIndex() ) + { + // alle Crsr/StkCrsr/UnoCrsr aus dem Loeschbereich verschieben + pDoc->CorrAbs( aIdx, SwPosition( *this ), nLen, TRUE ); + } + rNds.Delete(aIdx); + SetWrong( pList, false ); + SetGrammarCheck( pList3, false ); + SetSmartTags( pList2, false ); + InvalidateNumRule(); + } + else { + ASSERT( FALSE, "kein TxtNode." ); + } + + return this; +} + +// erzeugt einen AttrSet mit Bereichen fuer Frame-/Para/Char-Attributen +void SwTxtNode::NewAttrSet( SwAttrPool& rPool ) +{ + ASSERT( !mpAttrSet.get(), "AttrSet ist doch gesetzt" ); + SwAttrSet aNewAttrSet( rPool, aTxtNodeSetRange ); + + // put names of parent style and conditional style: + const SwFmtColl* pAnyFmtColl = &GetAnyFmtColl(); + const SwFmtColl* pFmtColl = GetFmtColl(); + String sVal; + SwStyleNameMapper::FillProgName( pAnyFmtColl->GetName(), sVal, nsSwGetPoolIdFromName::GET_POOLID_TXTCOLL, sal_True ); + SfxStringItem aAnyFmtColl( RES_FRMATR_STYLE_NAME, sVal ); + if ( pFmtColl != pAnyFmtColl ) + SwStyleNameMapper::FillProgName( pFmtColl->GetName(), sVal, nsSwGetPoolIdFromName::GET_POOLID_TXTCOLL, sal_True ); + SfxStringItem aFmtColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal ); + aNewAttrSet.Put( aAnyFmtColl ); + aNewAttrSet.Put( aFmtColl ); + + aNewAttrSet.SetParent( &pAnyFmtColl->GetAttrSet() ); + mpAttrSet = GetDoc()->GetIStyleAccess().getAutomaticStyle( aNewAttrSet, IStyleAccess::AUTO_STYLE_PARA ); +} + + +// override SwIndexReg::Update => text hints do not need SwIndex for start/end! +void SwTxtNode::Update( SwIndex const & rPos, const xub_StrLen nChangeLen, + const bool bNegative, const bool bDelete ) +{ + SetAutoCompleteWordDirty( TRUE ); + + ::std::auto_ptr<TmpHints> pCollector; + const xub_StrLen nChangePos = rPos.GetIndex(); + + if ( HasHints() ) + { + if ( bNegative ) + { + const xub_StrLen nChangeEnd = nChangePos + nChangeLen; + for ( USHORT n = 0; n < m_pSwpHints->Count(); ++n ) + { + SwTxtAttr * const pHint = m_pSwpHints->GetTextHint(n); + xub_StrLen * const pStart = pHint->GetStart(); + if ( *pStart > nChangePos ) + { + if ( *pStart > nChangeEnd ) + { + *pStart = *pStart - nChangeLen; + } + else + { + *pStart = nChangePos; + } + } + + xub_StrLen * const pEnd = pHint->GetEnd(); + if (pEnd) + { + if ( *pEnd > nChangePos ) + { + if( *pEnd > nChangeEnd ) + { + *pEnd = *pEnd - nChangeLen; + } + else + { + *pEnd = nChangePos; + } + } + } + } + + m_pSwpHints->MergePortions( *this ); + } + else + { + bool bNoExp = false; + bool bResort = false; + const USHORT coArrSz = static_cast<USHORT>(RES_TXTATR_WITHEND_END) - + static_cast<USHORT>(RES_CHRATR_BEGIN); + + BOOL aDontExp[ coArrSz ]; + memset( &aDontExp, 0, coArrSz * sizeof(BOOL) ); + + for ( USHORT n = 0; n < m_pSwpHints->Count(); ++n ) + { + SwTxtAttr * const pHint = m_pSwpHints->GetTextHint(n); + xub_StrLen * const pStart = pHint->GetStart(); + xub_StrLen * const pEnd = pHint->GetEnd(); + if ( *pStart >= nChangePos ) + { + *pStart = *pStart + nChangeLen; + if ( pEnd ) + { + *pEnd = *pEnd + nChangeLen; + } + } + else if ( pEnd && (*pEnd >= nChangePos) ) + { + if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() ) + { + *pEnd = *pEnd + nChangeLen; + } + else // *pEnd == nChangePos + { + USHORT nWhPos; + const USHORT nWhich = pHint->Which(); + + ASSERT(!isCHRATR(nWhich), "Update: char attr hint?"); + if (isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)) + { + nWhPos = static_cast<USHORT>(nWhich - + RES_CHRATR_BEGIN); + } + else + continue; + + if( aDontExp[ nWhPos ] ) + continue; + + if ( pHint->DontExpand() ) + { + pHint->SetDontExpand( false ); + bResort = true; + if ( pHint->IsCharFmtAttr() ) + { + bNoExp = true; + aDontExp[ static_cast<USHORT>(RES_TXTATR_CHARFMT) - static_cast<USHORT>(RES_CHRATR_BEGIN) ] + = TRUE; + aDontExp[ static_cast<USHORT>(RES_TXTATR_INETFMT) - static_cast<USHORT>(RES_CHRATR_BEGIN) ] + = TRUE; + } + else + aDontExp[ nWhPos ] = TRUE; + } + else if( bNoExp ) + { + if ( !pCollector.get() ) + { + pCollector.reset( new TmpHints ); + } + USHORT nCollCnt = pCollector->Count(); + for( USHORT i = 0; i < nCollCnt; ++i ) + { + SwTxtAttr *pTmp = (*pCollector)[ i ]; + if( nWhich == pTmp->Which() ) + { + pCollector->Remove( i ); + SwTxtAttr::Destroy( pTmp, + GetDoc()->GetAttrPool() ); + break; + } + } + SwTxtAttr * const pTmp = MakeTxtAttr( *GetDoc(), + pHint->GetAttr(), + nChangePos, nChangePos + nChangeLen); + pCollector->C40_INSERT( SwTxtAttr, pTmp, pCollector->Count() ); + } + else + { + *pEnd = *pEnd + nChangeLen; + } + } + } + } + if ( bResort ) + { + m_pSwpHints->Resort(); + } + } + } + + SwIndexReg aTmpIdxReg; + if ( !bNegative && !bDelete ) + { + const SwRedlineTbl& rTbl = GetDoc()->GetRedlineTbl(); + for ( USHORT i = 0; i < rTbl.Count(); ++i ) + { + SwRedline *const pRedl = rTbl[ i ]; + if ( pRedl->HasMark() ) + { + SwPosition* const pEnd = pRedl->End(); + if ( this == &pEnd->nNode.GetNode() && + *pRedl->GetPoint() != *pRedl->GetMark() ) + { + SwIndex & rIdx = pEnd->nContent; + if (nChangePos == rIdx.GetIndex()) + { + rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() ); + } + } + } + else if ( this == &pRedl->GetPoint()->nNode.GetNode() ) + { + SwIndex & rIdx = pRedl->GetPoint()->nContent; + if (nChangePos == rIdx.GetIndex()) + { + rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() ); + // mst: FIXME: why does this adjust the unused position??? + SwIndex * pIdx; + if ( &pRedl->GetBound( true ) == pRedl->GetPoint() ) + { + pRedl->GetBound( false ) = pRedl->GetBound( true ); + pIdx = &pRedl->GetBound( false ).nContent; + } + else + { + pRedl->GetBound( true ) = pRedl->GetBound( false ); + pIdx = &pRedl->GetBound( true ).nContent; + } + pIdx->Assign( &aTmpIdxReg, pIdx->GetIndex() ); + } + } + } + + const IDocumentMarkAccess* const pMarkAccess = getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppMark = + pMarkAccess->getMarksBegin(); + ppMark != pMarkAccess->getMarksEnd(); + ppMark++) + { + // Bookmarks must never grow to either side, when + // editing (directly) to the left or right (#i29942#)! + // And a bookmark with same start and end must remain + // to the left of the inserted text (used in XML import). + const ::sw::mark::IMark* const pMark = ppMark->get(); + const SwPosition* pEnd = &pMark->GetMarkEnd(); + SwIndex & rIdx = const_cast<SwIndex&>(pEnd->nContent); + if( this == &pEnd->nNode.GetNode() && + rPos.GetIndex() == rIdx.GetIndex() ) + { + rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() ); + } + } + } + + // base class + SwIndexReg::Update( rPos, nChangeLen, bNegative, bDelete ); + + if ( pCollector.get() ) + { + const USHORT nCount = pCollector->Count(); + for ( USHORT i = 0; i < nCount; ++i ) + { + m_pSwpHints->TryInsertHint( (*pCollector)[ i ], *this ); + } + } + + aTmpIdxReg.MoveTo( *this ); +} + +void SwTxtNode::_ChgTxtCollUpdateNum( const SwTxtFmtColl *pOldColl, + const SwTxtFmtColl *pNewColl) +{ + SwDoc* pDoc = GetDoc(); + ASSERT( pDoc, "Kein Doc?" ); + // erfrage die OutlineLevel und update gegebenenfalls das Nodes-Array, + // falls sich die Level geaendert haben ! + //const BYTE nOldLevel = pOldColl ? pOldColl->GetOutlineLevel():NO_NUMBERING;//#outline level,removed by zhaojianwei + //const BYTE nNewLevel = pNewColl ? pNewColl->GetOutlineLevel():NO_NUMBERING;//<-end,zhaojianwei + const int nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle() ? + pOldColl->GetAssignedOutlineStyleLevel() : MAXLEVEL; + const int nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ? + pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL; + +// if ( NO_NUMBERING != nNewLevel ) //#outline level,zhaojianwei + if ( MAXLEVEL != nNewLevel ) //<-end,zhaojianwei + { + SetAttrListLevel(nNewLevel); + } + + { + if (pDoc) + pDoc->GetNodes().UpdateOutlineNode(*this); + } + + + SwNodes& rNds = GetNodes(); + // Update beim Level 0 noch die Fussnoten !! + if( ( !nNewLevel || !nOldLevel) && pDoc->GetFtnIdxs().Count() && + FTNNUM_CHAPTER == pDoc->GetFtnInfo().eNum && + rNds.IsDocNodes() ) + { + SwNodeIndex aTmpIndex( rNds, GetIndex()); + + pDoc->GetFtnIdxs().UpdateFtn( aTmpIndex); + } + +//FEATURE::CONDCOLL + if( /*pOldColl != pNewColl && pNewColl && */ + RES_CONDTXTFMTCOLL == pNewColl->Which() ) + { + // Erfrage die akt. Condition des TextNodes: + ChkCondColl(); + } +//FEATURE::CONDCOLL +} + +// Wenn man sich genau am Ende einer Text- bzw. INetvorlage befindet, +// bekommt diese das DontExpand-Flag verpasst + +BOOL SwTxtNode::DontExpandFmt( const SwIndex& rIdx, bool bFlag, + BOOL bFmtToTxtAttributes ) +{ + const xub_StrLen nIdx = rIdx.GetIndex(); + if ( bFmtToTxtAttributes && nIdx == m_Text.Len() ) + { + FmtToTxtAttr( this ); + } + + BOOL bRet = FALSE; + if ( HasHints() ) + { + const USHORT nEndCnt = m_pSwpHints->GetEndCount(); + USHORT nPos = nEndCnt; + while( nPos ) + { + SwTxtAttr *pTmp = m_pSwpHints->GetEnd( --nPos ); + xub_StrLen *pEnd = pTmp->GetEnd(); + if( !pEnd || *pEnd > nIdx ) + continue; + if( nIdx != *pEnd ) + nPos = 0; + else if( bFlag != pTmp->DontExpand() && !pTmp->IsLockExpandFlag() + && *pEnd > *pTmp->GetStart()) + { + bRet = TRUE; + m_pSwpHints->NoteInHistory( pTmp ); + pTmp->SetDontExpand( bFlag ); + } + } + } + return bRet; +} + + +// gebe das vorgegebene Attribut, welches an der TextPosition (rIdx) +// gesetzt ist, zurueck. Gibt es keines, returne 0-Pointer. +// (gesetzt heisst, je nach bExpand ? +// Start < rIdx <= End +// : Start <= rIdx < End ) + +SwTxtAttr* SwTxtNode::GetTxtAttr( const SwIndex& rIdx, USHORT nWhichHt, + BOOL bExpand ) const +{ + const SwTxtAttr* pRet = 0; + const SwTxtAttr* pHt = 0; + const xub_StrLen *pEndIdx = 0; + const xub_StrLen nIdx = rIdx.GetIndex(); + const USHORT nSize = m_pSwpHints ? m_pSwpHints->Count() : 0; + + for( USHORT i = 0; i < nSize; ++i ) + { + // ist der Attribut-Anfang schon groesser als der Idx ? + pHt = (*m_pSwpHints)[i]; + if ( nIdx < *(pHt->GetStart()) ) + break; // beenden, kein gueltiges Attribut + + // ist es das gewuenschte Attribut ? + if( pHt->Which() != nWhichHt ) + continue; // nein, weiter + + pEndIdx = pHt->GetEnd(); + // liegt innerhalb des Bereiches ?? + if( !pEndIdx ) + { + if( *pHt->GetStart() == nIdx ) + { + pRet = pHt; + break; + } + } + else if( *pHt->GetStart() <= nIdx && nIdx <= *pEndIdx ) + { + // Wenn bExpand gesetzt ist, wird das Verhalten bei Eingabe + // simuliert, d.h. der Start wuede verschoben, das Ende expandiert, + if( bExpand ) + { + if( *pHt->GetStart() < nIdx ) + pRet = pHt; + } + else + { + if( nIdx < *pEndIdx ) + pRet = pHt; // den am dichtesten liegenden + } + } + } + return (SwTxtAttr*)pRet; // kein gueltiges Attribut gefunden !! +} + +/************************************************************************* + * CopyHint() + *************************************************************************/ + +SwCharFmt* lcl_FindCharFmt( const SwCharFmts* pCharFmts, const XubString& rName ) +{ + if( rName.Len() ) + { + SwCharFmt* pFmt; + USHORT nArrLen = pCharFmts->Count(); + for( USHORT i = 1; i < nArrLen; i++ ) + { + pFmt = (*pCharFmts)[ i ]; + if( pFmt->GetName().CompareTo( rName ) == COMPARE_EQUAL ) + return pFmt; + } + } + return NULL; +} + +void lcl_CopyHint( const USHORT nWhich, const SwTxtAttr * const pHt, + SwTxtAttr *const pNewHt, SwDoc *const pOtherDoc, SwTxtNode *const pDest ) +{ + ASSERT( nWhich == pHt->Which(), "Falsche Hint-Id" ); + switch( nWhich ) + { + // Wenn wir es mit einem Fussnoten-Attribut zu tun haben, + // muessen wir natuerlich auch den Fussnotenbereich kopieren. + case RES_TXTATR_FTN : + static_cast<const SwTxtFtn*>(pHt)->CopyFtn( + static_cast<SwTxtFtn*>(pNewHt)); + break; + + // Beim Kopieren von Feldern in andere Dokumente + // muessen die Felder bei ihren neuen Feldtypen angemeldet werden. + + // TabellenFormel muessen relativ kopiert werden. + case RES_TXTATR_FIELD : + { + const SwFmtFld& rFld = pHt->GetFld(); + if( pOtherDoc ) + { + static_cast<const SwTxtFld*>(pHt)->CopyFld( + static_cast<SwTxtFld*>(pNewHt) ); + } + + // Tabellenformel ?? + if( RES_TABLEFLD == rFld.GetFld()->GetTyp()->Which() + && static_cast<const SwTblField*>(rFld.GetFld())->IsIntrnlName()) + { + // wandel die interne in eine externe Formel um + const SwTableNode* const pDstTblNd = + static_cast<const SwTxtFld*>(pHt)-> + GetTxtNode().FindTableNode(); + if( pDstTblNd ) + { + SwTblField* const pTblFld = const_cast<SwTblField*>( + static_cast<const SwTblField*>( + pNewHt->GetFld().GetFld())); + pTblFld->PtrToBoxNm( &pDstTblNd->GetTable() ); + } + } + } + break; + + case RES_TXTATR_TOXMARK : + if( pOtherDoc && pDest && pDest->GetpSwpHints() + && USHRT_MAX != pDest->GetpSwpHints()->GetPos( pNewHt ) ) + { + // Beim Kopieren von TOXMarks(Client) in andere Dokumente + // muss der Verzeichnis (Modify) ausgetauscht werden + static_cast<SwTxtTOXMark*>(pNewHt)->CopyTOXMark( pOtherDoc ); + } + break; + + case RES_TXTATR_CHARFMT : + // Wenn wir es mit einer Zeichenvorlage zu tun haben, + // muessen wir natuerlich auch die Formate kopieren. + if( pDest && pDest->GetpSwpHints() + && USHRT_MAX != pDest->GetpSwpHints()->GetPos( pNewHt ) ) + { + SwCharFmt* pFmt = + static_cast<SwCharFmt*>(pHt->GetCharFmt().GetCharFmt()); + + if( pFmt && pOtherDoc ) + { + pFmt = pOtherDoc->CopyCharFmt( *pFmt ); + } + const_cast<SwFmtCharFmt&>( static_cast<const SwFmtCharFmt&>( + pNewHt->GetCharFmt() ) ).SetCharFmt( pFmt ); + } + break; + case RES_TXTATR_INETFMT : + { + // Wenn wir es mit benutzerdefinierten INet-Zeichenvorlagen + // zu tun haben, muessen wir natuerlich auch die Formate kopieren. + if( pOtherDoc && pDest && pDest->GetpSwpHints() + && USHRT_MAX != pDest->GetpSwpHints()->GetPos( pNewHt ) ) + { + const SwDoc* const pDoc = static_cast<const SwTxtINetFmt*>(pHt) + ->GetTxtNode().GetDoc(); + if ( pDoc ) + { + const SwCharFmts* pCharFmts = pDoc->GetCharFmts(); + const SwFmtINetFmt& rFmt = pHt->GetINetFmt(); + SwCharFmt* pFmt; + pFmt = lcl_FindCharFmt( pCharFmts, rFmt.GetINetFmt() ); + if( pFmt ) + pOtherDoc->CopyCharFmt( *pFmt ); + pFmt = lcl_FindCharFmt( pCharFmts, rFmt.GetVisitedFmt() ); + if( pFmt ) + pOtherDoc->CopyCharFmt( *pFmt ); + } + } + //JP 24.04.98: Bug 49753 - ein TextNode muss am Attribut + // gesetzt sein, damit die Vorlagen erzeugt + // werden koenne + SwTxtINetFmt* const pINetHt = static_cast<SwTxtINetFmt*>(pNewHt); + if ( !pINetHt->GetpTxtNode() ) + { + pINetHt->ChgTxtNode( pDest ); + } + + //JP 22.10.97: Bug 44875 - Verbindung zum Format herstellen + pINetHt->GetCharFmt(); + break; + } + case RES_TXTATR_META: + case RES_TXTATR_METAFIELD: + ASSERT(pNewHt, "copying META should not fail! cannot call DoCopy"); + ASSERT(pDest && (CH_TXTATR_INWORD == + pDest->GetTxt().GetChar(*pNewHt->GetStart())), + "missing CH_TXTATR?"); + if (pNewHt) + { + SwFmtMeta & rMeta(static_cast<SwFmtMeta&>(pNewHt->GetAttr())); + rMeta.DoCopy( const_cast<SwFmtMeta&>(pHt->GetMeta()) ); + } + break; + } +} + +/************************************************************************* +|* SwTxtNode::CopyAttr() +|* Beschreibung kopiert Attribute an der Position nStart in pDest. +|* BP 7.6.93: Es werden mit Absicht nur die Attribute _mit_ EndIdx +|* kopiert! CopyAttr wird vornehmlich dann gerufen, +|* wenn Attribute fuer einen Node mit leerem String +|* gesetzt werden sollen. +*************************************************************************/ + +void SwTxtNode::CopyAttr( SwTxtNode *pDest, const xub_StrLen nTxtStartIdx, + const xub_StrLen nOldPos ) +{ + if ( HasHints() ) // keine Attribute, keine Kekse + { + SwDoc* const pOtherDoc = (pDest->GetDoc() != GetDoc()) ? + pDest->GetDoc() : 0; + + for ( USHORT i = 0; i < m_pSwpHints->Count(); i++ ) + { + SwTxtAttr *const pHt = m_pSwpHints->GetTextHint(i); + xub_StrLen const nAttrStartIdx = *pHt->GetStart(); + if ( nTxtStartIdx < nAttrStartIdx ) + break; // ueber das Textende, da nLen == 0 + + const xub_StrLen *const pEndIdx = pHt->GetEnd(); + if ( pEndIdx && !pHt->HasDummyChar() ) + { + if( ( *pEndIdx > nTxtStartIdx || + ( *pEndIdx == nTxtStartIdx && + nAttrStartIdx == nTxtStartIdx ) ) ) + { + USHORT const nWhich = pHt->Which(); + if ( RES_TXTATR_REFMARK != nWhich ) + { + // attribute in the area => copy + SwTxtAttr *const pNewHt = pDest->InsertItem( + pHt->GetAttr(), nOldPos, nOldPos ); + if ( pNewHt ) + { + lcl_CopyHint( nWhich, pHt, pNewHt, + pOtherDoc, pDest ); + } + } + else if( !pOtherDoc ? GetDoc()->IsCopyIsMove() + : 0 == pOtherDoc->GetRefMark( + pHt->GetRefMark().GetRefName() ) ) + { + pDest->InsertItem( pHt->GetAttr(), nOldPos, nOldPos ); + } + } + } + } + } + + if( this != pDest ) + { + // Frames benachrichtigen, sonst verschwinden die Ftn-Nummern + SwUpdateAttr aHint( nOldPos, nOldPos, 0 ); + pDest->Modify( 0, &aHint ); + } +} + +/************************************************************************* +|* SwTxtNode::Copy() +|* Beschreibung kopiert Zeichen und Attibute in pDest, +|* wird angehaengt +*************************************************************************/ + +// --> OD 2008-11-18 #i96213# +// introduction of new optional parameter to control, if all attributes have to be copied. +void SwTxtNode::CopyText( SwTxtNode *const pDest, + const SwIndex &rStart, + const xub_StrLen nLen, + const bool bForceCopyOfAllAttrs ) +{ + SwIndex aIdx( pDest, pDest->m_Text.Len() ); + CopyText( pDest, aIdx, rStart, nLen, bForceCopyOfAllAttrs ); +} +// <-- + +// --> OD 2008-11-18 #i96213# +// introduction of new optional parameter to control, if all attributes have to be copied. +void SwTxtNode::CopyText( SwTxtNode *const pDest, + const SwIndex &rDestStart, + const SwIndex &rStart, + xub_StrLen nLen, + const bool bForceCopyOfAllAttrs ) +// <-- +{ + xub_StrLen nTxtStartIdx = rStart.GetIndex(); + xub_StrLen nDestStart = rDestStart.GetIndex(); // alte Pos merken + + if( !nLen ) + { + // wurde keine Laenge angegeben, dann Kopiere die Attribute + // an der Position rStart. + CopyAttr( pDest, nTxtStartIdx, nDestStart ); + + // harte Absatz umspannende Attribute kopieren + if( HasSwAttrSet() ) + { + // alle, oder nur die CharAttribute ? + // --> OD 2008-11-18 #i96213# + if ( !bForceCopyOfAllAttrs && + ( nDestStart || + pDest->HasSwAttrSet() || + nLen != pDest->GetTxt().Len() ) ) + // <-- + { + SfxItemSet aCharSet( pDest->GetDoc()->GetAttrPool(), + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_TXTATR_INETFMT, RES_TXTATR_INETFMT, + RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 ); + aCharSet.Put( *GetpSwAttrSet() ); + if( aCharSet.Count() ) + { + pDest->SetAttr( aCharSet, nDestStart, nDestStart ); + } + } + else + { + GetpSwAttrSet()->CopyToModify( *pDest ); + } + } + return; + } + + // 1. Text kopieren + const xub_StrLen oldLen = pDest->m_Text.Len(); + //JP 15.02.96: Bug 25537 - Attributbehandlung am Ende fehlt! Darum + // ueber die InsertMethode den Text einfuegen und nicht + // selbst direkt + pDest->InsertText( m_Text.Copy( nTxtStartIdx, nLen ), rDestStart, + IDocumentContentOperations::INS_EMPTYEXPAND ); + + // um reale Groesse Updaten ! + nLen = pDest->m_Text.Len() - oldLen; + if ( !nLen ) // string not longer? + return; + + SwDoc* const pOtherDoc = (pDest->GetDoc() != GetDoc()) ? + pDest->GetDoc() : 0; + + // harte Absatz umspannende Attribute kopieren + if( HasSwAttrSet() ) + { + // alle, oder nur die CharAttribute ? + // --> OD 2008-11-18 #i96213# + if ( !bForceCopyOfAllAttrs && + ( nDestStart || + pDest->HasSwAttrSet() || + nLen != pDest->GetTxt().Len() ) ) + // <-- + { + SfxItemSet aCharSet( pDest->GetDoc()->GetAttrPool(), + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_TXTATR_INETFMT, RES_TXTATR_INETFMT, + RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 ); + aCharSet.Put( *GetpSwAttrSet() ); + if( aCharSet.Count() ) + { + pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen ); + } + } + else + { + GetpSwAttrSet()->CopyToModify( *pDest ); + } + } + + const bool bUndoNodes = !pOtherDoc && GetDoc()->GetUndoNds() == &GetNodes(); + + // Ende erst jetzt holen, weil beim Kopieren in sich selbst der + // Start-Index und alle Attribute vorher aktualisiert werden. + nTxtStartIdx = rStart.GetIndex(); + const xub_StrLen nEnd = nTxtStartIdx + nLen; + + // 2. Attribute kopieren + // durch das Attribute-Array, bis der Anfang des Geltungsbereiches + // des Attributs hinter dem zu kopierenden Bereich liegt + const USHORT nSize = m_pSwpHints ? m_pSwpHints->Count() : 0; + + // wird in sich selbst kopiert, dann kann beim Einfuegen ein + // Attribut geloescht werden. Darum erst ins Tmp-Array kopieren und + // dann erst ins eigene uebertragen. + SwpHts aArr( 5 ); + + // Del-Array fuer alle RefMarks ohne Ausdehnung + SwpHts aRefMrkArr; + + //Achtung: kann ungueltig sein!! + for (USHORT n = 0; ( n < nSize ); ++n) + { + const xub_StrLen nAttrStartIdx = *(*m_pSwpHints)[n]->GetStart(); + if (!( nAttrStartIdx < nEnd)) + break; + + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint(n); + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + const USHORT nWhich = pHt->Which(); + + // JP 26.04.94: REFMARK's werden nie kopiert. Hat das Refmark aber + // keinen Bereich umspannt, so steht im Text ein 255 + // dieses muss entfernt werden. Trick: erst kopieren, + // erkennen und sammeln, nach dem kopieren Loeschen. + // Nimmt sein Zeichen mit ins Grab !! + // JP 14.08.95: Duerfen RefMarks gemovt werden? + int bCopyRefMark = RES_TXTATR_REFMARK == nWhich && ( bUndoNodes || + (!pOtherDoc ? GetDoc()->IsCopyIsMove() + : 0 == pOtherDoc->GetRefMark( + pHt->GetRefMark().GetRefName() ))); + + if( pEndIdx && RES_TXTATR_REFMARK == nWhich && !bCopyRefMark ) + { + continue; + } + + xub_StrLen nAttrStt; + xub_StrLen nAttrEnd; + + if( nAttrStartIdx < nTxtStartIdx ) + { + // start is before selection + // copy hints with end and CH_TXTATR only if dummy char is copied + if ( pEndIdx && (*pEndIdx > nTxtStartIdx) && !pHt->HasDummyChar() ) + { + // attribute with extent and the end is in the selection + nAttrStt = nDestStart; + nAttrEnd = (*pEndIdx > nEnd) + ? rDestStart.GetIndex() + : nDestStart + (*pEndIdx) - nTxtStartIdx; + } + else + { + continue; + } + } + else + { + // start is in the selection + nAttrStt = nDestStart + ( nAttrStartIdx - nTxtStartIdx ); + if( pEndIdx ) + { + nAttrEnd = *pEndIdx > nEnd + ? rDestStart.GetIndex() + : nDestStart + ( *pEndIdx - nTxtStartIdx ); + } + else + { + nAttrEnd = nAttrStt; + } + } + + SwTxtAttr * pNewHt = 0; + + if( pDest == this ) + { + // die Daten kopieren + pNewHt = MakeTxtAttr( *GetDoc(), pHt->GetAttr(), + nAttrStt, nAttrEnd ); + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +//JP 23.04.95: erstmal so gesondert hier behandeln. Am Besten ist es +// aber im CopyFtn wenn die pDestFtn keinen StartNode hat, +// sich diesen dann anlegt. +// Aber so kurz vor der BETA besser nicht anfassen. + if( RES_TXTATR_FTN == nWhich ) + { + SwTxtFtn* pFtn = (SwTxtFtn*)pNewHt; + pFtn->ChgTxtNode( this ); + pFtn->MakeNewTextSection( GetNodes() ); + lcl_CopyHint( nWhich, pHt, pFtn, 0, 0 ); + pFtn->ChgTxtNode( 0 ); + } + else +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + { + lcl_CopyHint( nWhich, pHt, pNewHt, 0, pDest ); + } + aArr.C40_INSERT( SwTxtAttr, pNewHt, aArr.Count() ); + } + else + { + pNewHt = pDest->InsertItem( pHt->GetAttr(), nAttrStt, + nAttrEnd, nsSetAttrMode::SETATTR_NOTXTATRCHR ); + lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest ); + } + + if( RES_TXTATR_REFMARK == nWhich && !pEndIdx && !bCopyRefMark ) + { + aRefMrkArr.C40_INSERT( SwTxtAttr, pNewHt, aRefMrkArr.Count() ); + } + } + + // nur falls im Array Attribute stehen (kann nur beim Kopieren + // sich selbst passieren!!) + for ( USHORT i = 0; i < aArr.Count(); ++i ) + { + InsertHint( aArr[ i ], nsSetAttrMode::SETATTR_NOTXTATRCHR ); + } + + if( pDest->GetpSwpHints() ) + { + for ( USHORT i = 0; i < aRefMrkArr.Count(); ++i ) + { + SwTxtAttr * const pNewHt = aRefMrkArr[i]; + if( pNewHt->GetEnd() ) + { + pDest->GetpSwpHints()->Delete( pNewHt ); + pDest->DestroyAttr( pNewHt ); + } + else + { + const SwIndex aIdx( pDest, *pNewHt->GetStart() ); + pDest->EraseText( aIdx, 1 ); + } + } + } + + CHECK_SWPHINTS(this); +} + + +void SwTxtNode::InsertText( const XubString & rStr, const SwIndex & rIdx, + const IDocumentContentOperations::InsertFlags nMode ) +{ + ASSERT( rIdx <= m_Text.Len(), "SwTxtNode::InsertText: invalid index." ); + ASSERT( (ULONG)m_Text.Len() + (ULONG)rStr.Len() <= STRING_LEN, + "SwTxtNode::InsertText: node text with insertion > STRING_LEN." ); + + xub_StrLen aPos = rIdx.GetIndex(); + xub_StrLen nLen = m_Text.Len() - aPos; + m_Text.Insert( rStr, aPos ); + nLen = m_Text.Len() - aPos - nLen; + + if ( !nLen ) return; + + BOOL bOldExpFlg = IsIgnoreDontExpand(); + if (nMode & IDocumentContentOperations::INS_FORCEHINTEXPAND) + { + SetIgnoreDontExpand( TRUE ); + } + + Update( rIdx, nLen ); // text content changed! + + if (nMode & IDocumentContentOperations::INS_FORCEHINTEXPAND) + { + SetIgnoreDontExpand( bOldExpFlg ); + } + + // analog zu Insert(char) in txtedt.cxx: + // 1) bei bHintExp leere Hints an rIdx.GetIndex suchen und aufspannen + // 2) bei bHintExp == FALSE mitgezogene Feldattribute zuruecksetzen + + if ( HasHints() ) + { + for ( USHORT i = 0; i < m_pSwpHints->Count() && + rIdx >= *(*m_pSwpHints)[i]->GetStart(); ++i ) + { + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint( i ); + xub_StrLen * const pEndIdx = pHt->GetEnd(); + if( !pEndIdx ) + continue; + + if( rIdx == *pEndIdx ) + { + if ( (nMode & IDocumentContentOperations::INS_NOHINTEXPAND) || + (!(nMode & IDocumentContentOperations::INS_FORCEHINTEXPAND) + && pHt->DontExpand()) ) + { + // bei leeren Attributen auch Start veraendern + if( rIdx == *pHt->GetStart() ) + *pHt->GetStart() = *pHt->GetStart() - nLen; + *pEndIdx = *pEndIdx - nLen; + m_pSwpHints->DeleteAtPos(i); + InsertHint( pHt, nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + // empty hints at insert position? + else if ( (nMode & IDocumentContentOperations::INS_EMPTYEXPAND) + && (*pEndIdx == *pHt->GetStart()) ) + { + *pHt->GetStart() = *pHt->GetStart() - nLen; + const USHORT nAktLen = m_pSwpHints->Count(); + m_pSwpHints->DeleteAtPos(i); + InsertHint( pHt/* AUTOSTYLES:, nsSetAttrMode::SETATTR_NOHINTADJUST*/ ); + if ( nAktLen > m_pSwpHints->Count() && i ) + { + --i; + } + continue; + } + else + { + continue; + } + } + if ( !(nMode & IDocumentContentOperations::INS_NOHINTEXPAND) && + rIdx == nLen && *pHt->GetStart() == rIdx.GetIndex() && + !pHt->IsDontExpandStartAttr() ) + { + // Kein Feld, am Absatzanfang, HintExpand + m_pSwpHints->DeleteAtPos(i); + *pHt->GetStart() = *pHt->GetStart() - nLen; + InsertHint( pHt, nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + } + TryDeleteSwpHints(); + } + + if ( GetDepends() ) + { + SwInsTxt aHint( aPos, nLen ); + SwModify::Modify( 0, &aHint ); + } + + // By inserting a character, the hidden flags + // at the TxtNode can become invalid: + SetCalcHiddenCharFlags(); + + CHECK_SWPHINTS(this); +} + +/************************************************************************* +|* +|* SwTxtNode::Cut() +|* +|* Beschreibung text.doc +|* Ersterstellung VB 20.03.91 +|* Letzte Aenderung JP 11.08.94 +|* +*************************************************************************/ + +void SwTxtNode::CutText( SwTxtNode * const pDest, + const SwIndex & rStart, const xub_StrLen nLen ) +{ + if(pDest) + { + SwIndex aDestStt( pDest, pDest->GetTxt().Len() ); + CutImpl( pDest, aDestStt, rStart, nLen, false ); + } + else + { + ASSERT(false, + "mst: entering dead and bitrotted code; fasten your seatbelts!"); + EraseText( rStart, nLen ); + } +} + + +void SwTxtNode::CutImpl( SwTxtNode * const pDest, const SwIndex & rDestStart, + const SwIndex & rStart, /*const*/ xub_StrLen nLen, const bool bUpdate ) +{ + if(!pDest) + { + ASSERT(false, + "mst: entering dead and bitrotted code; fasten your seatbelts!"); + EraseText( rStart, nLen ); + return; + } + + // nicht im Dokument verschieben ? + if( GetDoc() != pDest->GetDoc() ) + { + ASSERT(false, + "mst: entering dead and bitrotted code; fasten your seatbelts!"); + CopyText( pDest, rDestStart, rStart, nLen); + EraseText(rStart, nLen); + return; + } + + if( !nLen ) + { + // wurde keine Laenge angegeben, dann Kopiere die Attribute + // an der Position rStart. + CopyAttr( pDest, rStart.GetIndex(), rDestStart.GetIndex() ); + return; + } + + xub_StrLen nTxtStartIdx = rStart.GetIndex(); + xub_StrLen nDestStart = rDestStart.GetIndex(); // alte Pos merken + const xub_StrLen nInitSize = pDest->m_Text.Len(); + + // wird in sich selbst verschoben, muss es gesondert behandelt werden !! + if( pDest == this ) + { + ASSERT(false, + "mst: entering dead and bitrotted code; fasten your seatbelts!"); + m_Text.Insert( m_Text, nTxtStartIdx, nLen, nDestStart ); + m_Text.Erase( nTxtStartIdx + (nDestStart<nTxtStartIdx ? nLen : 0), nLen ); + + const xub_StrLen nEnd = rStart.GetIndex() + nLen; + + // dann suche mal alle Attribute zusammen, die im verschobenen + // Bereich liegen. Diese werden in das extra Array verschoben, + // damit sich die Indizies beim Updaten nicht veraendern !!! + SwpHts aArr( 5 ); + + // 2. Attribute verschieben + // durch das Attribute-Array, bis der Anfang des Geltungsbereiches + // des Attributs hinter dem zu verschiebenden Bereich liegt + USHORT nAttrCnt = 0; + while ( m_pSwpHints && nAttrCnt < m_pSwpHints->Count() ) + { + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint(nAttrCnt); + const xub_StrLen nAttrStartIdx = *pHt->GetStart(); + if (!( nAttrStartIdx < nEnd )) + break; + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + const USHORT nWhich = pHt->Which(); + SwTxtAttr *pNewHt = 0; + + if(nAttrStartIdx < nTxtStartIdx) + { + // Anfang liegt vor dem Bereich + if ( RES_TXTATR_REFMARK != nWhich && !pHt->HasDummyChar() && + pEndIdx && *pEndIdx > nTxtStartIdx ) + { + // Attribut mit einem Bereich + // und das Ende des Attribut liegt im Bereich + pNewHt = MakeTxtAttr( *GetDoc(), pHt->GetAttr(), 0, + *pEndIdx > nEnd + ? nLen + : *pEndIdx - nTxtStartIdx ); + } + } + else + { + // der Anfang liegt vollstaendig im Bereich + if( !pEndIdx || *pEndIdx < nEnd ) + { + // Attribut verschieben + m_pSwpHints->Delete( pHt ); + // die Start/End Indicies neu setzen + *pHt->GetStart() = nAttrStartIdx - nTxtStartIdx; + if( pEndIdx ) + *pHt->GetEnd() = *pEndIdx - nTxtStartIdx; + aArr.C40_INSERT( SwTxtAttr, pHt, aArr.Count() ); + continue; // while-Schleife weiter, ohne ++ ! + } + // das Ende liegt dahinter + else if (RES_TXTATR_REFMARK != nWhich && !pHt->HasDummyChar()) + { + pNewHt = MakeTxtAttr( *GetDoc(), pHt->GetAttr(), + nAttrStartIdx - nTxtStartIdx, + !pEndIdx ? 0 + : ( *pEndIdx > nEnd + ? nLen + : *pEndIdx - nTxtStartIdx )); + } + } + if( pNewHt ) + { + // die Daten kopieren + lcl_CopyHint( nWhich, pHt, pNewHt, 0, this ); + aArr.C40_INSERT( SwTxtAttr, pNewHt, aArr.Count() ); + } + ++nAttrCnt; + } + + if( bUpdate ) + { + // Update aller Indizies + Update( rDestStart, nLen, FALSE, TRUE ); + } +#ifdef CUTNOEXPAND + else + // wird am Ende eingefuegt, nur die Attribut-Indizies verschieben + if ( 0 < nLen && 0 < nInitSize && m_pSwpHints ) + { + // check if there was the end of an attribute at the insertion + // position: if it is not a field, it must be expanded + for ( USHORT n = 0; n < m_pSwpHints->Count(); n++ ) + { + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint(n); + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + if ( pEndIdx && (*pEndIdx == nInitSize) ) + { + *pEndIdx = *pEndIdx + nLen; + } + } + } +#endif + CHECK_SWPHINTS(this); + + Update( rStart, nLen, TRUE, TRUE ); + + CHECK_SWPHINTS(this); + + // dann setze die kopierten/geloeschten Attribute in den Node + if( nDestStart <= nTxtStartIdx ) + { + nTxtStartIdx = nTxtStartIdx + nLen; + } + else + { + nDestStart = nDestStart - nLen; + } + + for ( USHORT n = 0; n < aArr.Count(); ++n ) + { + SwTxtAttr *const pNewHt = aArr[n]; + *pNewHt->GetStart() = nDestStart + *pNewHt->GetStart(); + xub_StrLen * const pEndIdx = pNewHt->GetEnd(); + if ( pEndIdx ) + { + *pEndIdx = nDestStart + *pEndIdx; + } + InsertHint( pNewHt, nsSetAttrMode::SETATTR_NOTXTATRCHR ); + } + } + else + { + pDest->m_Text.Insert( m_Text, nTxtStartIdx, nLen, nDestStart ); + m_Text.Erase( nTxtStartIdx, nLen ); + nLen = pDest->m_Text.Len() - nInitSize; // update w/ current size! + if( !nLen ) // String nicht gewachsen ?? + return; + + if( bUpdate ) + { + // Update aller Indizies + pDest->Update( rDestStart, nLen, FALSE, TRUE); + } +#ifdef CUTNOEXPAND + else + // wird am Ende eingefuegt, nur die Attribut-Indizies verschieben + if ( 0 < nLen && 0 < nInitSize && pDest->m_pSwpHints ) + { + // check if there was the end of an attribute at the insertion + // position: if it is not a field, it must be expanded + for ( USHORT n = 0; n < pDest->m_pSwpHints->Count(); n++ ) + { + SwTxtAttr * const pHt = pDest->m_pSwpHints->GetTextHint(n); + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + if ( pEndIdx && (*pEndIdx == nInitSize) ) + { + *pEndIdx = *pEndIdx + nLen; + } + } + } +#endif + CHECK_SWPHINTS(pDest); + + const xub_StrLen nEnd = rStart.GetIndex() + nLen; + SwDoc* const pOtherDoc = (pDest->GetDoc() != GetDoc()) + ? pDest->GetDoc() : 0; + const bool bUndoNodes = + !pOtherDoc && GetDoc()->GetUndoNds() == &GetNodes(); + + ASSERT(!pOtherDoc, + "mst: entering dead and bitrotted code; fasten your seatbelts!"); + + // harte Absatz umspannende Attribute kopieren + if( HasSwAttrSet() ) + { + // alle, oder nur die CharAttribute ? + if( nInitSize || pDest->HasSwAttrSet() || + nLen != pDest->GetTxt().Len() ) + { + SfxItemSet aCharSet( pDest->GetDoc()->GetAttrPool(), + RES_CHRATR_BEGIN, RES_CHRATR_END-1, + RES_TXTATR_INETFMT, RES_TXTATR_INETFMT, + RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT, + RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1, + 0 ); + aCharSet.Put( *GetpSwAttrSet() ); + if( aCharSet.Count() ) + pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen ); + } + else + { + GetpSwAttrSet()->CopyToModify( *pDest ); + } + } + + // 2. Attribute verschieben + // durch das Attribute-Array, bis der Anfang des Geltungsbereiches + // des Attributs hinter dem zu verschiebenden Bereich liegt + USHORT nAttrCnt = 0; + while ( m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()) ) + { + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint(nAttrCnt); + const xub_StrLen nAttrStartIdx = *pHt->GetStart(); + if (!( nAttrStartIdx < nEnd )) + break; + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + const USHORT nWhich = pHt->Which(); + SwTxtAttr *pNewHt = 0; + + // if the hint has a dummy character, then it must not be split! + if(nAttrStartIdx < nTxtStartIdx) + { + // Anfang liegt vor dem Bereich + if( !pHt->HasDummyChar() && ( RES_TXTATR_REFMARK != nWhich + || bUndoNodes ) && pEndIdx && *pEndIdx > nTxtStartIdx ) + { + // Attribut mit einem Bereich + // und das Ende des Attribut liegt im Bereich + pNewHt = MakeTxtAttr( *pDest->GetDoc(), pHt->GetAttr(), + nDestStart, + nDestStart + ( + *pEndIdx > nEnd + ? nLen + : *pEndIdx - nTxtStartIdx ) ); + } + } + else + { + // der Anfang liegt vollstaendig im Bereich + if( !pEndIdx || *pEndIdx < nEnd || + (!pOtherDoc && !bUndoNodes && RES_TXTATR_REFMARK == nWhich) + || pHt->HasDummyChar() ) + { + // do not delete note and later add it -> sidebar flickering + if ( GetDoc()->GetDocShell() ) + { + GetDoc()->GetDocShell()->Broadcast( SfxSimpleHint(SFX_HINT_USER04)); + } + // Attribut verschieben + m_pSwpHints->Delete( pHt ); + // die Start/End Indicies neu setzen + *pHt->GetStart() = + nDestStart + (nAttrStartIdx - nTxtStartIdx); + if( pEndIdx ) + { + *pHt->GetEnd() = nDestStart + ( + *pEndIdx > nEnd + ? nLen + : *pEndIdx - nTxtStartIdx ); + } + pDest->InsertHint( pHt, + nsSetAttrMode::SETATTR_NOTXTATRCHR + | nsSetAttrMode::SETATTR_DONTREPLACE ); + if ( GetDoc()->GetDocShell() ) + { + GetDoc()->GetDocShell()->Broadcast( SfxSimpleHint(SFX_HINT_USER04)); + } + continue; // while-Schleife weiter, ohne ++ ! + } + // das Ende liegt dahinter + else if( RES_TXTATR_REFMARK != nWhich || bUndoNodes ) + { + pNewHt = MakeTxtAttr( *GetDoc(), pHt->GetAttr(), + nDestStart + (nAttrStartIdx - nTxtStartIdx), + !pEndIdx ? 0 + : nDestStart + ( *pEndIdx > nEnd + ? nLen + : *pEndIdx - nTxtStartIdx )); + } + } + if ( pNewHt ) + { + const bool bSuccess( pDest->InsertHint( pNewHt, + nsSetAttrMode::SETATTR_NOTXTATRCHR + | nsSetAttrMode::SETATTR_DONTREPLACE ) ); + if (bSuccess) + { + lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest ); + } + } + ++nAttrCnt; + } + // sollten jetzt noch leere Attribute rumstehen, dann haben diese + // eine hoehere Praezedenz. Also herausholen und das Array updaten. + // Die dabei entstehenden leeren Hints werden von den gesicherten + // "uebergeplaettet". (Bug: 6977) + if( m_pSwpHints && nAttrCnt < m_pSwpHints->Count() ) + { + SwpHts aArr( 5 ); + while ( nAttrCnt < m_pSwpHints->Count() ) + { + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint(nAttrCnt); + if ( nEnd != *pHt->GetStart() ) + break; + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + if ( pEndIdx && *pEndIdx == nEnd ) + { + aArr.C40_INSERT( SwTxtAttr, pHt, aArr.Count() ); + m_pSwpHints->Delete( pHt ); + } + else + { + ++nAttrCnt; + } + } + Update( rStart, nLen, TRUE, TRUE ); + + for ( USHORT n = 0; n < aArr.Count(); ++n ) + { + SwTxtAttr * const pHt = aArr[ n ]; + *pHt->GetStart() = *pHt->GetEnd() = rStart.GetIndex(); + InsertHint( pHt ); + } + } + else + { + Update( rStart, nLen, TRUE, TRUE ); + } + + CHECK_SWPHINTS(this); + } + + TryDeleteSwpHints(); + + // Frames benachrichtigen; + SwInsTxt aInsHint( nDestStart, nLen ); + pDest->Modify( 0, &aInsHint ); + SwDelTxt aDelHint( nTxtStartIdx, nLen ); + Modify( 0, &aDelHint ); +} + + +void SwTxtNode::EraseText(const SwIndex &rIdx, const xub_StrLen nCount, + const IDocumentContentOperations::InsertFlags nMode ) +{ + ASSERT( rIdx <= m_Text.Len(), "SwTxtNode::EraseText: invalid index." ); + + const xub_StrLen nStartIdx = rIdx.GetIndex(); + const xub_StrLen nCnt = (STRING_LEN == nCount) + ? m_Text.Len() - nStartIdx : nCount; + const xub_StrLen nEndIdx = nStartIdx + nCnt; + m_Text.Erase( nStartIdx, nCnt ); + + /* GCAttr(); alle leeren weggwerfen ist zu brutal. + * Es duerfen nur die wegggeworfen werden, + * die im Bereich liegen und nicht am Ende des Bereiches liegen + */ + + for ( USHORT i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i ) + { + SwTxtAttr *pHt = m_pSwpHints->GetTextHint(i); + + const xub_StrLen nHintStart = *pHt->GetStart(); + + if ( nHintStart < nStartIdx ) + continue; + + if ( nHintStart > nEndIdx ) + break; // hints are sorted by end, so break here + + const xub_StrLen* pHtEndIdx = pHt->GetEnd(); + const USHORT nWhich = pHt->Which(); + + if( !pHtEndIdx ) + { + ASSERT(pHt->HasDummyChar(), + "attribute with neither end nor CH_TXTATR?"); + if (isTXTATR(nWhich) && + (nHintStart >= nStartIdx) && (nHintStart < nEndIdx)) + { + m_pSwpHints->DeleteAtPos(i); + DestroyAttr( pHt ); + --i; + } + continue; + } + + ASSERT (!( (nHintStart < nEndIdx) && (*pHtEndIdx > nEndIdx) + && pHt->HasDummyChar() ) + // next line: deleting exactly dummy char: DeleteAttributes + || ((nHintStart == nStartIdx) && (nHintStart + 1 == nEndIdx)), + "ERROR: deleting left-overlapped attribute with CH_TXTATR"); + + // Delete the hint if: + // 1. The hint ends before the deletion end position or + // 2. The hint ends at the deletion end position and + // we are not in empty expand mode and + // the hint is a [toxmark|refmark|ruby] text attribute + // 3. deleting exactly the dummy char of an hint with end and dummy + // char deletes the hint + if ( (*pHtEndIdx < nEndIdx) + || ( (*pHtEndIdx == nEndIdx) && + !(IDocumentContentOperations::INS_EMPTYEXPAND & nMode) && + ( (RES_TXTATR_TOXMARK == nWhich) || + (RES_TXTATR_REFMARK == nWhich) || + // --> FME 2006-03-03 #i62668# Ruby text attribute must be + // treated just like toxmark and refmarks + (RES_TXTATR_CJK_RUBY == nWhich) ) ) + // <-- +#if 0 + || ( (nHintStart == nStartIdx) && + (nHintStart + 1 == nEndIdx)&& +#else // generalize this to left-overlapped dummy char hints (see ASSERT) + || ( (nHintStart < nEndIdx) && +#endif + pHt->HasDummyChar() ) + ) + { + m_pSwpHints->DeleteAtPos(i); + DestroyAttr( pHt ); + --i; + } + } + + ASSERT(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?"); + + TryDeleteSwpHints(); + + Update( rIdx, nCnt, TRUE ); + + if( 1 == nCnt ) + { + SwDelChr aHint( nStartIdx ); + SwModify::Modify( 0, &aHint ); + } + else + { + SwDelTxt aHint( nStartIdx, nCnt ); + SwModify::Modify( 0, &aHint ); + } + + ASSERT(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?"); + + // By deleting a character, the hidden flags + // at the TxtNode can become invalid: + SetCalcHiddenCharFlags(); + + CHECK_SWPHINTS(this); +} + +/*********************************************************************** +#* Class : SwTxtNode +#* Methode : GCAttr +#* +#* Beschreibung +#* text.doc +#* +#* Datum : MS 28.11.90 +#* Update : VB 24.07.91 +#***********************************************************************/ + +void SwTxtNode::GCAttr() +{ + if ( !HasHints() ) + return; + + bool bChanged = false; + USHORT nMin = m_Text.Len(), + nMax = 0; + BOOL bAll = nMin != 0; // Bei leeren Absaetzen werden nur die + // INet-Formate entfernt. + + for ( USHORT i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i ) + { + SwTxtAttr * const pHt = m_pSwpHints->GetTextHint(i); + + // wenn Ende und Start gleich sind --> loeschen + const xub_StrLen * const pEndIdx = pHt->GetEnd(); + if (pEndIdx && !pHt->HasDummyChar() && (*pEndIdx == *pHt->GetStart()) + && ( bAll || pHt->Which() == RES_TXTATR_INETFMT ) ) + { + bChanged = true; + nMin = Min( nMin, *pHt->GetStart() ); + nMax = Max( nMax, *pHt->GetEnd() ); + DestroyAttr( m_pSwpHints->Cut(i) ); + --i; + } + else + { + pHt->SetDontExpand( false ); + } + } + TryDeleteSwpHints(); + + if(bChanged) + { + //TxtFrm's reagieren auf aHint, andere auf aNew + SwUpdateAttr aHint( nMin, nMax, 0 ); + SwModify::Modify( 0, &aHint ); + SwFmtChg aNew( GetTxtColl() ); + SwModify::Modify( 0, &aNew ); + } +} + +// #i23726# +SwNumRule* SwTxtNode::_GetNumRule(BOOL bInParent) const +{ + SwNumRule* pRet = 0; + + const SfxPoolItem* pItem = GetNoCondAttr( RES_PARATR_NUMRULE, bInParent ); + bool bNoNumRule = false; + if ( pItem ) + { + String sNumRuleName = static_cast<const SwNumRuleItem *>(pItem)->GetValue(); + if (sNumRuleName.Len() > 0) + { + pRet = GetDoc()->FindNumRulePtr( sNumRuleName ); + } + else // numbering is turned off + bNoNumRule = true; + } + + if ( !bNoNumRule ) + { + if ( pRet && pRet == GetDoc()->GetOutlineNumRule() && + ( !HasSwAttrSet() || + SFX_ITEM_SET != + GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, FALSE ) ) ) + { + SwTxtFmtColl* pColl = GetTxtColl(); + if ( pColl ) + { + const SwNumRuleItem& rDirectItem = pColl->GetNumRule( FALSE ); + if ( rDirectItem.GetValue().Len() == 0 ) + { + pRet = 0L; + } + } + } + + // --> OD 2006-11-20 #i71764# + // Document setting OUTLINE_LEVEL_YIELDS_OUTLINE_RULE has no influence + // any more. +// if ( !pRet && +// GetDoc()->get(IDocumentSettingAccess::OUTLINE_LEVEL_YIELDS_OUTLINE_RULE) && +// GetOutlineLevel() != NO_NUMBERING ) +// { +// pRet = GetDoc()->GetOutlineNumRule(); +// } + // <-- + } + // old code before tuning +// // --> OD 2005-10-25 #126347# +// // determine of numbering/bullet rule, which is set as a hard attribute +// // at the text node +// const SfxPoolItem* pItem( 0L ); +// if ( HasSwAttrSet() ) // does text node has hard attributes ? +// { +// if ( SFX_ITEM_SET != +// GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, FALSE, &pItem ) ) +// { +// pItem = 0L; +// } +// // else: <pItem> contains the numbering/bullet attribute, which is +// // hard set at the paragraph. + +// } +// // <-- +// bool bNoNumRule = false; +// if (pItem) +// { +// String sNumRuleName = static_cast<const SwNumRuleItem *>(pItem)->GetValue(); +// if (sNumRuleName.Len() > 0) +// { +// pRet = GetDoc()->FindNumRulePtr(sNumRuleName); +// } +// else // numbering is turned off by hard attribute +// bNoNumRule = true; +// } + +// if (! bNoNumRule) +// { +// if (! pRet && bInParent) +// { +// SwTxtFmtColl * pColl = GetTxtColl(); + +// if (pColl) +// { +// const SwNumRuleItem & rItem = pColl->GetNumRule(TRUE); + +// pRet = const_cast<SwDoc *>(GetDoc())-> +// FindNumRulePtrWithPool(rItem.GetValue()); +// // --> OD 2005-10-13 #125993# - The outline numbering rule +// // isn't allowed to be derived from a parent paragraph style +// // to a derived one. +// // Thus check, if the found outline numbering rule is directly +// // set at the paragraph style <pColl>. If not, don't return +// // the outline numbering rule. +// if ( pRet && pRet == GetDoc()->GetOutlineNumRule() ) +// { +// const SwNumRuleItem& rDirectItem = pColl->GetNumRule(FALSE); +// SwNumRule* pNumRuleAtParaStyle = const_cast<SwDoc*>(GetDoc())-> +// FindNumRulePtrWithPool(rDirectItem.GetValue()); +// if ( !pNumRuleAtParaStyle ) +// { +// pRet = 0L; +// } +// } +// // <-- +// } +// } + +// if (!pRet && GetDoc()->IsOutlineLevelYieldsOutlineRule() && +// GetOutlineLevel() != NO_NUMBERING) +// pRet = GetDoc()->GetOutlineNumRule(); +// } + // <-- + + return pRet; +} + +SwNumRule* SwTxtNode::GetNumRule(BOOL bInParent) const +{ + SwNumRule * pRet = _GetNumRule(bInParent); + + return pRet; +} + +void SwTxtNode::NumRuleChgd() +{ + // --> OD 2008-04-04 #refactorlists# + if ( IsInList() ) + { + SwNumRule* pNumRule = GetNumRule(); + if ( pNumRule && pNumRule != GetNum()->GetNumRule() ) + { + mpNodeNum->ChangeNumRule( *pNumRule ); + } + } + // <-- + + if( IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( FALSE ); + } + SetInSwFntCache( FALSE ); + + SvxLRSpaceItem& rLR = (SvxLRSpaceItem&)GetSwAttrSet().GetLRSpace(); + + SwModify::Modify( &rLR, &rLR ); +} + +// -> #i27615# +BOOL SwTxtNode::IsNumbered() const +{ + BOOL bResult = FALSE; + + SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule && IsCountedInList() ) + bResult = TRUE; + + return bResult; +} + +// --> OD 2008-04-02 #refactorlists# +bool SwTxtNode::HasMarkedLabel() const +{ + bool bResult = false; + + if ( IsInList() ) + { + bResult = + GetDoc()->getListByName( GetListId() )->IsListLevelMarked( GetActualListLevel() ); + } + + return bResult; +} +// <-- +// <- #i27615# + +SwTxtNode* SwTxtNode::_MakeNewTxtNode( const SwNodeIndex& rPos, BOOL bNext, + BOOL bChgFollow ) +{ + /* hartes PageBreak/PageDesc/ColumnBreak aus AUTO-Set ignorieren */ + SwAttrSet* pNewAttrSet = 0; + // --> OD 2007-07-10 #i75353# + bool bClearHardSetNumRuleWhenFmtCollChanges( false ); + // <-- + if( HasSwAttrSet() ) + { + pNewAttrSet = new SwAttrSet( *GetpSwAttrSet() ); + const SfxItemSet* pTmpSet = GetpSwAttrSet(); + + if( bNext ) // der naechste erbt keine Breaks! + pTmpSet = pNewAttrSet; + + // PageBreaks/PageDesc/ColBreak rausschmeissen. + BOOL bRemoveFromCache = FALSE; + std::vector<USHORT> aClearWhichIds; + if ( bNext ) + bRemoveFromCache = ( 0 != pNewAttrSet->ClearItem( RES_PAGEDESC ) ); + else + aClearWhichIds.push_back( RES_PAGEDESC ); + + if( SFX_ITEM_SET == pTmpSet->GetItemState( RES_BREAK, FALSE ) ) + { + if ( bNext ) + pNewAttrSet->ClearItem( RES_BREAK ); + else + aClearWhichIds.push_back( RES_BREAK ); + bRemoveFromCache = TRUE; + } + if( SFX_ITEM_SET == pTmpSet->GetItemState( RES_KEEP, FALSE ) ) + { + if ( bNext ) + pNewAttrSet->ClearItem( RES_KEEP ); + else + aClearWhichIds.push_back( RES_KEEP ); + bRemoveFromCache = TRUE; + } + if( SFX_ITEM_SET == pTmpSet->GetItemState( RES_PARATR_SPLIT, FALSE ) ) + { + if ( bNext ) + pNewAttrSet->ClearItem( RES_PARATR_SPLIT ); + else + aClearWhichIds.push_back( RES_PARATR_SPLIT ); + bRemoveFromCache = TRUE; + } + if(SFX_ITEM_SET == pTmpSet->GetItemState(RES_PARATR_NUMRULE, FALSE)) + { + SwNumRule * pRule = GetNumRule(); + + if (pRule && IsOutline()) + { + if ( bNext ) + pNewAttrSet->ClearItem(RES_PARATR_NUMRULE); + else + { + // --> OD 2007-07-10 #i75353# + // No clear of hard set numbering rule at an outline paragraph at this point. + // Only if the paragraph style changes - see below. +// aClearWhichIds.push_back( RES_PARATR_NUMRULE ); + bClearHardSetNumRuleWhenFmtCollChanges = true; + // <-- + } + bRemoveFromCache = TRUE; + } + } + + if ( 0 != aClearWhichIds.size() ) + bRemoveFromCache = 0 != ClearItemsFromAttrSet( aClearWhichIds ); + + if( !bNext && bRemoveFromCache && IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( FALSE ); + } + } + SwNodes& rNds = GetNodes(); + + SwTxtFmtColl* pColl = GetTxtColl(); + + SwTxtNode *pNode = new SwTxtNode( rPos, pColl, pNewAttrSet ); + + if( pNewAttrSet ) + delete pNewAttrSet; + + const SwNumRule* pRule = GetNumRule(); + if( pRule && pRule == pNode->GetNumRule() && rNds.IsDocNodes() ) // #115901# + { + // --> OD 2005-10-18 #i55459# + // - correction: parameter <bNext> has to be checked, as it was in the + // previous implementation. + if ( !bNext && !IsCountedInList() ) + SetCountedInList(true); + // <-- + } + + // jetzt kann es sein, das durch die Nummerierung dem neuen Node eine + // Vorlage aus dem Pool zugewiesen wurde. Dann darf diese nicht + // nochmal uebergeplaettet werden !! + if( pColl != pNode->GetTxtColl() || + ( bChgFollow && pColl != GetTxtColl() )) + return pNode; // mehr duerfte nicht gemacht werden oder ???? + + pNode->_ChgTxtCollUpdateNum( 0, pColl ); // fuer Nummerierung/Gliederung + if( bNext || !bChgFollow ) + return pNode; + + SwTxtFmtColl *pNextColl = &pColl->GetNextTxtFmtColl(); + // --> OD 2009-08-12 #i101870# + // perform action on different paragraph styles before applying the new paragraph style + if (pNextColl != pColl) + { + // --> OD 2007-07-10 #i75353# + if ( bClearHardSetNumRuleWhenFmtCollChanges ) + { + std::vector<USHORT> aClearWhichIds; + aClearWhichIds.push_back( RES_PARATR_NUMRULE ); + if ( ClearItemsFromAttrSet( aClearWhichIds ) != 0 && IsInCache() ) + { + SwFrm::GetCache().Delete( this ); + SetInCache( FALSE ); + } + } + // <-- + } + // <-- + ChgFmtColl( pNextColl ); + + return pNode; +} + +SwCntntNode* SwTxtNode::AppendNode( const SwPosition & rPos ) +{ + // Position hinter dem eingefuegt wird + SwNodeIndex aIdx( rPos.nNode, 1 ); + SwTxtNode* pNew = _MakeNewTxtNode( aIdx, TRUE ); + + // --> OD 2008-05-14 #refactorlists# + // reset list attributes at appended text node + pNew->ResetAttr( RES_PARATR_LIST_ISRESTART ); + pNew->ResetAttr( RES_PARATR_LIST_RESTARTVALUE ); + pNew->ResetAttr( RES_PARATR_LIST_ISCOUNTED ); + if ( pNew->GetNumRule() == 0 ) + { + pNew->ResetAttr( RES_PARATR_LIST_ID ); + pNew->ResetAttr( RES_PARATR_LIST_LEVEL ); + } + // <-- + // --> OD 2008-03-13 #refactorlists# +// SyncNumberAndNumRule(); + if ( !IsInList() && GetNumRule() && GetListId().Len() > 0 ) + { + AddToList(); + } + // <-- + + if( GetDepends() ) + MakeFrms( *pNew ); + return pNew; +} + +/************************************************************************* + * SwTxtNode::GetTxtAttr + *************************************************************************/ + +SwTxtAttr * SwTxtNode::GetTxtAttrForCharAt( const xub_StrLen nIndex, + const RES_TXTATR nWhich ) const +{ + if ( HasHints() ) + { + for ( USHORT i = 0; i < m_pSwpHints->Count(); ++i ) + { + SwTxtAttr * const pHint = m_pSwpHints->GetTextHint(i); + const xub_StrLen nStartPos = *pHint->GetStart(); + if ( nIndex < nStartPos ) + { + return 0; + } + if ( (nIndex == nStartPos) && pHint->HasDummyChar() ) + { + return ( RES_TXTATR_END == nWhich || nWhich == pHint->Which() ) + ? pHint : 0; + } + } + } + return 0; +} + +// -> #i29560# +BOOL SwTxtNode::HasNumber() const +{ + BOOL bResult = FALSE; + + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule ) + { + SwNumFmt aFmt(pRule->Get( static_cast<USHORT>(GetActualListLevel()))); + + // #i40041# + bResult = aFmt.IsEnumeration() && + SVX_NUM_NUMBER_NONE != aFmt.GetNumberingType(); + } + + return bResult; +} + +BOOL SwTxtNode::HasBullet() const +{ + BOOL bResult = FALSE; + + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule ) + { + SwNumFmt aFmt(pRule->Get( static_cast<USHORT>(GetActualListLevel()))); + + bResult = aFmt.IsItemize(); + } + + return bResult; +} +// <- #i29560# + +// --> OD 2005-11-17 #128041# - introduce parameter <_bInclPrefixAndSuffixStrings> +//i53420 added max outline parameter +XubString SwTxtNode::GetNumString( const bool _bInclPrefixAndSuffixStrings, const unsigned int _nRestrictToThisLevel ) const +{ + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule && + IsCountedInList() && + pRule->Get( static_cast<USHORT>(GetActualListLevel()) ).IsTxtFmt() ) + { + return pRule->MakeNumString( GetNum()->GetNumberVector(), + _bInclPrefixAndSuffixStrings ? TRUE : FALSE, + FALSE, + _nRestrictToThisLevel ); + } + + return aEmptyStr; +} + +long SwTxtNode::GetLeftMarginWithNum( BOOL bTxtLeft ) const +{ + long nRet = 0; + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if( pRule ) + { + const SwNumFmt& rFmt = pRule->Get(static_cast<USHORT>(GetActualListLevel())); + // --> OD 2008-01-16 #newlistlevelattrs# + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + nRet = rFmt.GetAbsLSpace(); + + if( !bTxtLeft ) + { + if( 0 > rFmt.GetFirstLineOffset() && + nRet > -rFmt.GetFirstLineOffset() ) + nRet = nRet + rFmt.GetFirstLineOffset(); + else + nRet = 0; + } + + if( pRule->IsAbsSpaces() ) + nRet = nRet - GetSwAttrSet().GetLRSpace().GetLeft(); + } + else if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + if ( AreListLevelIndentsApplicable() ) + { + nRet = rFmt.GetIndentAt(); + // --> OD 2008-06-06 #i90401# + // Only negative first line indents have consider for the left margin + if ( !bTxtLeft && + rFmt.GetFirstLineIndent() < 0 ) + { + nRet = nRet + rFmt.GetFirstLineIndent(); + } + // <-- + } + } + // <-- + } + + return nRet; +} + +BOOL SwTxtNode::GetFirstLineOfsWithNum( short& rFLOffset ) const +{ + BOOL bRet( FALSE ); + // --> OD 2009-09-08 #i95907#, #b6879723# + rFLOffset = 0; + // <-- + + // --> OD 2005-11-02 #i51089 - TUNING# + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule ) + { + if ( IsCountedInList() ) + { + // --> OD 2008-01-16 #newlistlevelattrs# + const SwNumFmt& rFmt = pRule->Get(static_cast<USHORT>(GetActualListLevel())); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + rFLOffset = pRule->Get( static_cast<USHORT>(GetActualListLevel() )).GetFirstLineOffset(); + + if (!getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)) + { + SvxLRSpaceItem aItem = GetSwAttrSet().GetLRSpace(); + rFLOffset = rFLOffset + aItem.GetTxtFirstLineOfst(); + } + } + else if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + if ( AreListLevelIndentsApplicable() ) + { + rFLOffset = static_cast<USHORT>(rFmt.GetFirstLineIndent()); + } + else if (!getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)) + { + SvxLRSpaceItem aItem = GetSwAttrSet().GetLRSpace(); + rFLOffset = aItem.GetTxtFirstLineOfst(); + } + } + // <-- + } + + bRet = TRUE; + } + else + { + rFLOffset = GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst(); + } + + return bRet; +} + +// --> OD 2010-01-05 #b6884103# +SwTwips SwTxtNode::GetAdditionalIndentForStartingNewList() const +{ + SwTwips nAdditionalIndent = 0; + + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule ) + { + const SwNumFmt& rFmt = pRule->Get(static_cast<USHORT>(GetActualListLevel())); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) + { + nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft(); + + if (getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)) + { + nAdditionalIndent = nAdditionalIndent - + GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst(); + } + } + else if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + if ( AreListLevelIndentsApplicable() ) + { + nAdditionalIndent = rFmt.GetIndentAt() + rFmt.GetFirstLineIndent(); + } + else + { + nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft(); + if (getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)) + { + nAdditionalIndent = nAdditionalIndent - + GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst(); + } + } + } + } + else + { + nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft(); + } + + return nAdditionalIndent; +} +// <-- + +// --> OD 2008-12-02 #i96772# +void SwTxtNode::ClearLRSpaceItemDueToListLevelIndents( SvxLRSpaceItem& o_rLRSpaceItem ) const +{ + if ( AreListLevelIndentsApplicable() ) + { + const SwNumRule* pRule = GetNumRule(); + if ( pRule && GetActualListLevel() >= 0 ) + { + const SwNumFmt& rFmt = pRule->Get(static_cast<USHORT>(GetActualListLevel())); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + SvxLRSpaceItem aLR( RES_LR_SPACE ); + o_rLRSpaceItem = aLR; + } + } + } +} +// <-- + +// --> OD 2008-07-01 #i91133# +long SwTxtNode::GetLeftMarginForTabCalculation() const +{ + long nLeftMarginForTabCalc = 0; + + bool bLeftMarginForTabCalcSetToListLevelIndent( false ); + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0; + if( pRule ) + { + const SwNumFmt& rFmt = pRule->Get(static_cast<USHORT>(GetActualListLevel())); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + if ( AreListLevelIndentsApplicable() ) + { + nLeftMarginForTabCalc = rFmt.GetIndentAt(); + bLeftMarginForTabCalcSetToListLevelIndent = true; + } + } + } + if ( !bLeftMarginForTabCalcSetToListLevelIndent ) + { + nLeftMarginForTabCalc = GetSwAttrSet().GetLRSpace().GetTxtLeft(); + } + + return nLeftMarginForTabCalc; +} +// <-- + +void SwTxtNode::Replace0xFF( XubString& rTxt, xub_StrLen& rTxtStt, + xub_StrLen nEndPos, BOOL bExpandFlds ) const +{ + if( GetpSwpHints() ) + { + sal_Unicode cSrchChr = CH_TXTATR_BREAKWORD; + for( int nSrchIter = 0; 2 > nSrchIter; ++nSrchIter, + cSrchChr = CH_TXTATR_INWORD ) + { + xub_StrLen nPos = rTxt.Search( cSrchChr ); + while( STRING_NOTFOUND != nPos && nPos < nEndPos ) + { + const SwTxtAttr* const pAttr = + GetTxtAttrForCharAt( rTxtStt + nPos ); + if( pAttr ) + { + switch( pAttr->Which() ) + { + case RES_TXTATR_FIELD: + rTxt.Erase( nPos, 1 ); + if( bExpandFlds ) + { + const XubString aExpand( ((SwTxtFld*)pAttr)->GetFld(). + GetFld()->Expand() ); + rTxt.Insert( aExpand, nPos ); + nPos = nPos + aExpand.Len(); + nEndPos = nEndPos + aExpand.Len(); + rTxtStt = rTxtStt - aExpand.Len(); + } + ++rTxtStt; + break; + case RES_TXTATR_FTN: + rTxt.Erase( nPos, 1 ); + if( bExpandFlds ) + { + const SwFmtFtn& rFtn = pAttr->GetFtn(); + XubString sExpand; + if( rFtn.GetNumStr().Len() ) + sExpand = rFtn.GetNumStr(); + else if( rFtn.IsEndNote() ) + sExpand = GetDoc()->GetEndNoteInfo().aFmt. + GetNumStr( rFtn.GetNumber() ); + else + sExpand = GetDoc()->GetFtnInfo().aFmt. + GetNumStr( rFtn.GetNumber() ); + rTxt.Insert( sExpand, nPos ); + nPos = nPos + sExpand.Len(); + nEndPos = nEndPos + sExpand.Len(); + rTxtStt = rTxtStt - sExpand.Len(); + } + ++rTxtStt; + break; + default: + rTxt.Erase( nPos, 1 ); + ++rTxtStt; + } + } + else + ++nPos, ++nEndPos; + nPos = rTxt.Search( cSrchChr, nPos ); + } + } + } +} + +/************************************************************************* + * SwTxtNode::GetExpandTxt + * Expand fields + *************************************************************************/ +// --> OD 2007-11-15 #i83479# - handling of new parameters +XubString SwTxtNode::GetExpandTxt( const xub_StrLen nIdx, + const xub_StrLen nLen, + const bool bWithNum, + const bool bAddSpaceAfterListLabelStr, + const bool bWithSpacesForLevel ) const +{ + XubString aTxt( GetTxt().Copy( nIdx, nLen ) ); + xub_StrLen nTxtStt = nIdx; + Replace0xFF( aTxt, nTxtStt, aTxt.Len(), TRUE ); + if( bWithNum ) + { + XubString aListLabelStr = GetNumString(); + if ( aListLabelStr.Len() > 0 ) + { + if ( bAddSpaceAfterListLabelStr ) + { + const sal_Unicode aSpace = ' '; + aTxt.Insert( aSpace, 0 ); + } + aTxt.Insert( GetNumString(), 0 ); + } + } + + if ( bWithSpacesForLevel && GetActualListLevel() > 0 ) + { + int nLevel( GetActualListLevel() ); + while ( nLevel > 0 ) + { + const sal_Unicode aSpace = ' '; + aTxt.Insert( aSpace , 0 ); + aTxt.Insert( aSpace , 0 ); + --nLevel; + } + } + + return aTxt; +} +// <-- + +BOOL SwTxtNode::GetExpandTxt( SwTxtNode& rDestNd, const SwIndex* pDestIdx, + xub_StrLen nIdx, xub_StrLen nLen, BOOL bWithNum, + BOOL bWithFtn, BOOL bReplaceTabsWithSpaces ) const +{ + if( &rDestNd == this ) + return FALSE; + + SwIndex aDestIdx( &rDestNd, rDestNd.GetTxt().Len() ); + if( pDestIdx ) + aDestIdx = *pDestIdx; + xub_StrLen nDestStt = aDestIdx.GetIndex(); + + // Text einfuegen + String sTmpText = GetTxt(); + if( bReplaceTabsWithSpaces ) + sTmpText.SearchAndReplaceAll('\t', ' '); + + // mask hidden characters + const xub_Unicode cChar = CH_TXTATR_BREAKWORD; + USHORT nHiddenChrs = + SwScriptInfo::MaskHiddenRanges( *this, sTmpText, 0, sTmpText.Len(), cChar ); + + sTmpText = sTmpText.Copy( nIdx, nLen ); + rDestNd.InsertText( sTmpText, aDestIdx ); + nLen = aDestIdx.GetIndex() - nDestStt; + + // alle FontAttribute mit CHARSET Symbol in dem Bereich setzen + if ( HasHints() ) + { + xub_StrLen nInsPos = nDestStt - nIdx; + for ( USHORT i = 0; i < m_pSwpHints->Count(); i++ ) + { + const SwTxtAttr* pHt = (*m_pSwpHints)[i]; + const xub_StrLen nAttrStartIdx = *pHt->GetStart(); + const USHORT nWhich = pHt->Which(); + if (nIdx + nLen <= nAttrStartIdx) + break; // ueber das Textende + + const xub_StrLen *pEndIdx = pHt->GetEnd(); + if( pEndIdx && *pEndIdx > nIdx && + ( RES_CHRATR_FONT == nWhich || + RES_TXTATR_CHARFMT == nWhich || + RES_TXTATR_AUTOFMT == nWhich )) + { + const SvxFontItem* const pFont = + static_cast<const SvxFontItem*>( + CharFmt::GetItem( *pHt, RES_CHRATR_FONT )); + if ( pFont && RTL_TEXTENCODING_SYMBOL == pFont->GetCharSet() ) + { + // attribute in area => copy + rDestNd.InsertItem( *const_cast<SvxFontItem*>(pFont), + nInsPos + nAttrStartIdx, nInsPos + *pEndIdx ); + } + } + else if ( pHt->HasDummyChar() && (nAttrStartIdx >= nIdx) ) + { + aDestIdx = nInsPos + nAttrStartIdx; + switch( nWhich ) + { + case RES_TXTATR_FIELD: + { + const XubString aExpand( ((SwTxtFld*)pHt)->GetFld().GetFld()->Expand() ); + if( aExpand.Len() ) + { + aDestIdx++; // dahinter einfuegen; + rDestNd.InsertText( aExpand, aDestIdx ); + aDestIdx = nInsPos + nAttrStartIdx; + nInsPos = nInsPos + aExpand.Len(); + } + rDestNd.EraseText( aDestIdx, 1 ); + --nInsPos; + } + break; + + case RES_TXTATR_FTN: + { + if ( bWithFtn ) + { + const SwFmtFtn& rFtn = pHt->GetFtn(); + XubString sExpand; + if( rFtn.GetNumStr().Len() ) + sExpand = rFtn.GetNumStr(); + else if( rFtn.IsEndNote() ) + sExpand = GetDoc()->GetEndNoteInfo().aFmt. + GetNumStr( rFtn.GetNumber() ); + else + sExpand = GetDoc()->GetFtnInfo().aFmt. + GetNumStr( rFtn.GetNumber() ); + if( sExpand.Len() ) + { + aDestIdx++; // insert behind + SvxEscapementItem aItem( + SVX_ESCAPEMENT_SUPERSCRIPT ); + rDestNd.InsertItem(aItem, + aDestIdx.GetIndex(), + aDestIdx.GetIndex() ); + rDestNd.InsertText( sExpand, aDestIdx, + IDocumentContentOperations::INS_EMPTYEXPAND); + aDestIdx = nInsPos + nAttrStartIdx; + nInsPos = nInsPos + sExpand.Len(); + } + } + rDestNd.EraseText( aDestIdx, 1 ); + --nInsPos; + } + break; + + default: + rDestNd.EraseText( aDestIdx, 1 ); + --nInsPos; + } + } + } + } + + if( bWithNum ) + { + aDestIdx = nDestStt; + rDestNd.InsertText( GetNumString(), aDestIdx ); + } + + if ( nHiddenChrs > 0 ) + { + aDestIdx = 0; + while ( aDestIdx < rDestNd.GetTxt().Len() ) + { + if ( cChar == rDestNd.GetTxt().GetChar( aDestIdx.GetIndex() ) ) + { + xub_StrLen nIndex = aDestIdx.GetIndex(); + while ( nIndex < rDestNd.GetTxt().Len() && + cChar == rDestNd.GetTxt().GetChar( ++nIndex ) ) + ; + rDestNd.EraseText( aDestIdx, nIndex - aDestIdx.GetIndex() ); + } + else + ++aDestIdx; + } + } + + return TRUE; +} + +const ModelToViewHelper::ConversionMap* + SwTxtNode::BuildConversionMap( rtl::OUString& rRetText ) const +{ + const rtl::OUString& rNodeText = GetTxt(); + rRetText = rNodeText; + ModelToViewHelper::ConversionMap* pConversionMap = 0; + + const SwpHints* pSwpHints2 = GetpSwpHints(); + xub_StrLen nPos = 0; + + for ( USHORT i = 0; pSwpHints2 && i < pSwpHints2->Count(); ++i ) + { + const SwTxtAttr* pAttr = (*pSwpHints2)[i]; + if ( RES_TXTATR_FIELD == pAttr->Which() ) + { + const XubString aExpand( ((SwTxtFld*)pAttr)->GetFld().GetFld()->Expand() ); + if ( aExpand.Len() > 0 ) + { + const xub_StrLen nFieldPos = *pAttr->GetStart(); + rRetText = rRetText.replaceAt( nPos + nFieldPos, 1, aExpand ); + if ( !pConversionMap ) + pConversionMap = new ModelToViewHelper::ConversionMap; + pConversionMap->push_back( + ModelToViewHelper::ConversionMapEntry( + nFieldPos, nPos + nFieldPos ) ); + nPos += ( aExpand.Len() - 1 ); + } + } + } + + if ( pConversionMap && pConversionMap->size() ) + pConversionMap->push_back( + ModelToViewHelper::ConversionMapEntry( + rNodeText.getLength()+1, rRetText.getLength()+1 ) ); + + return pConversionMap; +} + +XubString SwTxtNode::GetRedlineTxt( xub_StrLen nIdx, xub_StrLen nLen, + BOOL bExpandFlds, BOOL bWithNum ) const +{ + SvUShorts aRedlArr; + const SwDoc* pDoc = GetDoc(); + USHORT nRedlPos = pDoc->GetRedlinePos( *this, nsRedlineType_t::REDLINE_DELETE ); + if( USHRT_MAX != nRedlPos ) + { + // es existiert fuer den Node irgendein Redline-Delete-Object + const ULONG nNdIdx = GetIndex(); + for( ; nRedlPos < pDoc->GetRedlineTbl().Count() ; ++nRedlPos ) + { + const SwRedline* pTmp = pDoc->GetRedlineTbl()[ nRedlPos ]; + if( nsRedlineType_t::REDLINE_DELETE == pTmp->GetType() ) + { + const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End(); + if( pRStt->nNode < nNdIdx ) + { + if( pREnd->nNode > nNdIdx ) + // Absatz ist komplett geloescht + return aEmptyStr; + else if( pREnd->nNode == nNdIdx ) + { + // von 0 bis nContent ist alles geloescht + aRedlArr.Insert( xub_StrLen(0), aRedlArr.Count() ); + aRedlArr.Insert( pREnd->nContent.GetIndex(), aRedlArr.Count() ); + } + } + else if( pRStt->nNode == nNdIdx ) + { + aRedlArr.Insert( pRStt->nContent.GetIndex(), aRedlArr.Count() ); + if( pREnd->nNode == nNdIdx ) + aRedlArr.Insert( pREnd->nContent.GetIndex(), aRedlArr.Count() ); + else + { + aRedlArr.Insert( GetTxt().Len(), aRedlArr.Count() ); + break; // mehr kann nicht kommen + } + } + else + break; // mehr kann nicht kommen + } + } + } + + XubString aTxt( GetTxt().Copy( nIdx, nLen ) ); + + xub_StrLen nTxtStt = nIdx, nIdxEnd = nIdx + aTxt.Len(); + for( USHORT n = 0; n < aRedlArr.Count(); n += 2 ) + { + xub_StrLen nStt = aRedlArr[ n ], nEnd = aRedlArr[ n+1 ]; + if( ( nIdx <= nStt && nStt <= nIdxEnd ) || + ( nIdx <= nEnd && nEnd <= nIdxEnd )) + { + if( nStt < nIdx ) nStt = nIdx; + if( nIdxEnd < nEnd ) nEnd = nIdxEnd; + xub_StrLen nDelCnt = nEnd - nStt; + aTxt.Erase( nStt - nTxtStt, nDelCnt ); + Replace0xFF( aTxt, nTxtStt, nStt - nTxtStt, bExpandFlds ); + nTxtStt = nTxtStt + nDelCnt; + } + else if( nStt >= nIdxEnd ) + break; + } + Replace0xFF( aTxt, nTxtStt, aTxt.Len(), bExpandFlds ); + + if( bWithNum ) + aTxt.Insert( GetNumString(), 0 ); + return aTxt; +} + +/************************************************************************* + * SwTxtNode::ReplaceText + *************************************************************************/ + +void SwTxtNode::ReplaceText( const SwIndex& rStart, const xub_StrLen nDelLen, + const XubString& rText ) +{ + ASSERT( rStart.GetIndex() < m_Text.Len() && + rStart.GetIndex() + nDelLen <= m_Text.Len(), + "SwTxtNode::ReplaceText: index out of bounds" ); + const xub_StrLen nStartPos = rStart.GetIndex(); + xub_StrLen nEndPos = nStartPos + nDelLen; + xub_StrLen nLen = nDelLen; + for ( xub_StrLen nPos = nStartPos; nPos < nEndPos; ++nPos ) + { + if ( ( CH_TXTATR_BREAKWORD == m_Text.GetChar( nPos ) ) || + ( CH_TXTATR_INWORD == m_Text.GetChar( nPos ) ) ) + { + SwTxtAttr *const pHint = GetTxtAttrForCharAt( nPos ); + if (pHint) + { + ASSERT (!( pHint->GetEnd() && pHint->HasDummyChar() + && (*pHint->GetStart() < nEndPos) + && (*pHint->GetEnd() > nEndPos) ), + "ReplaceText: ERROR: " + "deleting left-overlapped attribute with CH_TXTATR"); + DeleteAttribute( pHint ); + --nEndPos; + --nLen; + } + } + } + + BOOL bOldExpFlg = IsIgnoreDontExpand(); + SetIgnoreDontExpand( TRUE ); + + if( nLen && rText.Len() ) + { + // dann das 1. Zeichen ersetzen den Rest loschen und einfuegen + // Dadurch wird die Attributierung des 1. Zeichen expandiert! + m_Text.SetChar( nStartPos, rText.GetChar( 0 ) ); + + ((SwIndex&)rStart)++; + m_Text.Erase( rStart.GetIndex(), nLen - 1 ); + Update( rStart, nLen - 1, true ); + + XubString aTmpTxt( rText ); aTmpTxt.Erase( 0, 1 ); + m_Text.Insert( aTmpTxt, rStart.GetIndex() ); + Update( rStart, aTmpTxt.Len(), false ); + } + else + { + m_Text.Erase( nStartPos, nLen ); + Update( rStart, nLen, true ); + + m_Text.Insert( rText, nStartPos ); + Update( rStart, rText.Len(), false ); + } + + SetIgnoreDontExpand( bOldExpFlg ); + SwDelTxt aDelHint( nStartPos, nDelLen ); + SwModify::Modify( 0, &aDelHint ); + + SwInsTxt aHint( nStartPos, rText.Len() ); + SwModify::Modify( 0, &aHint ); +} + +// --> OD 2008-03-27 #refactorlists# +namespace { + // Helper method for special handling of modified attributes at text node. + // The following is handled: + // (1) on changing the paragraph style - RES_FMT_CHG: + // Check, if list style of the text node is changed. If yes, add respectively + // remove the text node to the corresponding list. + // (2) on changing the attributes - RES_ATTRSET_CHG: + // Same as (1). + // (3) on changing the list style - RES_PARATR_NUMRULE: + // Same as (1). + void HandleModifyAtTxtNode( SwTxtNode& rTxtNode, + const SfxPoolItem* pOldValue, + const SfxPoolItem* pNewValue ) + { + const USHORT nWhich = pOldValue ? pOldValue->Which() : + pNewValue ? pNewValue->Which() : 0 ; + bool bNumRuleSet = false; + bool bParagraphStyleChanged = false; + String sNumRule; + String sOldNumRule; + switch ( nWhich ) + { + case RES_FMT_CHG: + { + bParagraphStyleChanged = true; + if( rTxtNode.GetNodes().IsDocNodes() ) + { + // --> OD 2008-12-17 #i70748# + // The former list style set at the paragraph can not be + // retrieved from the change set. +// sOldNumRule = +// dynamic_cast<const SwFmtChg*>(pOldValue)->pChangedFmt->GetNumRule().GetValue(); + const SwNumRule* pFormerNumRuleAtTxtNode = + rTxtNode.GetNum() ? rTxtNode.GetNum()->GetNumRule() : 0; + if ( pFormerNumRuleAtTxtNode ) + { + sOldNumRule = pFormerNumRuleAtTxtNode->GetName(); + } + // <-- + // --> OD 2008-11-19 #i70748# + if ( rTxtNode.IsEmptyListStyleDueToSetOutlineLevelAttr() ) + { + const SwNumRuleItem& rNumRuleItem = rTxtNode.GetTxtColl()->GetNumRule(); + if ( rNumRuleItem.GetValue().Len() > 0 ) + { + rTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + } + // <-- + const SwNumRule* pNumRuleAtTxtNode = rTxtNode.GetNumRule(); + if ( pNumRuleAtTxtNode ) + { + bNumRuleSet = true; + sNumRule = pNumRuleAtTxtNode->GetName(); + } + } + break; + } + case RES_ATTRSET_CHG: + { + const SfxPoolItem* pItem = 0; + // --> OD 2008-12-19 #i70748# + // The former list style set at the paragraph can not be + // retrieved from the change set. +// if ( dynamic_cast<const SwAttrSetChg*>(pOldValue)->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, FALSE, &pItem ) == +// SFX_ITEM_SET ) +// { +// sOldNumRule = dynamic_cast<const SwNumRuleItem*>(pItem)->GetValue(); +// } + const SwNumRule* pFormerNumRuleAtTxtNode = + rTxtNode.GetNum() ? rTxtNode.GetNum()->GetNumRule() : 0; + if ( pFormerNumRuleAtTxtNode ) + { + sOldNumRule = pFormerNumRuleAtTxtNode->GetName(); + } + // <-- + if ( dynamic_cast<const SwAttrSetChg*>(pNewValue)->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, FALSE, &pItem ) == + SFX_ITEM_SET ) + { + // --> OD 2008-11-19 #i70748# + rTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + // <-- + bNumRuleSet = true; + // The new list style set at the paragraph can not be + // retrieved from the change set. +// sNumRule = dynamic_cast<const SwNumRuleItem*>(pItem)->GetValue(); + // <-- + } + // --> OD 2008-12-17 #i70748# + // The new list style set at the paragraph. + const SwNumRule* pNumRuleAtTxtNode = rTxtNode.GetNumRule(); + if ( pNumRuleAtTxtNode ) + { + sNumRule = pNumRuleAtTxtNode->GetName(); + } + // <-- + break; + } + case RES_PARATR_NUMRULE: + { + if ( rTxtNode.GetNodes().IsDocNodes() ) + { + // The former list style set at the paragraph can not be + // retrieved from the change set. +// if ( pOldValue ) +// { +// sOldNumRule = dynamic_cast<const SwNumRuleItem*>(pOldValue)->GetValue(); +// } + const SwNumRule* pFormerNumRuleAtTxtNode = + rTxtNode.GetNum() ? rTxtNode.GetNum()->GetNumRule() : 0; + if ( pFormerNumRuleAtTxtNode ) + { + sOldNumRule = pFormerNumRuleAtTxtNode->GetName(); + } + // <-- + if ( pNewValue ) + { + // --> OD 2008-11-19 #i70748# + rTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + // <-- + bNumRuleSet = true; + // --> OD 2008-12-17 #i70748# + // The new list style set at the paragraph can not be + // retrieved from the change set. +// sNumRule = dynamic_cast<const SwNumRuleItem*>(pNewValue)->GetValue(); + // <-- + } + // --> OD 2008-12-17 #i70748# + // The new list style set at the paragraph. + const SwNumRule* pNumRuleAtTxtNode = rTxtNode.GetNumRule(); + if ( pNumRuleAtTxtNode ) + { + sNumRule = pNumRuleAtTxtNode->GetName(); + } + // <-- + } + break; + } + } + if ( sNumRule != sOldNumRule ) + { + if ( bNumRuleSet ) + { + if ( sNumRule.Len() == 0 ) + { + rTxtNode.RemoveFromList(); + if ( bParagraphStyleChanged ) + { + SvUShortsSort aResetAttrsArray; + aResetAttrsArray.Insert( RES_PARATR_LIST_ID ); + aResetAttrsArray.Insert( RES_PARATR_LIST_LEVEL ); + aResetAttrsArray.Insert( RES_PARATR_LIST_ISRESTART ); + aResetAttrsArray.Insert( RES_PARATR_LIST_RESTARTVALUE ); + aResetAttrsArray.Insert( RES_PARATR_LIST_ISCOUNTED ); + SwPaM aPam( rTxtNode ); + // --> OD 2008-11-28 #i96644# + // suppress side effect "send data changed events" + rTxtNode.GetDoc()->ResetAttrs( aPam, sal_False, + &aResetAttrsArray, + false ); + // <-- + } + } + else + { + rTxtNode.RemoveFromList(); + // If new list style is the outline style, apply outline + // level as the list level. + if ( sNumRule == + String::CreateFromAscii( SwNumRule::GetOutlineRuleName() ) ) + { + // --> OD 2008-09-10 #i70748# + ASSERT( rTxtNode.GetTxtColl()->IsAssignedToListLevelOfOutlineStyle(), + "<HandleModifyAtTxtNode()> - text node with outline style, but its paragraph style is not assigned to outline style." ); + int nNewListLevel = + rTxtNode.GetTxtColl()->GetAssignedOutlineStyleLevel(); + // <-- + if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL ) + { + rTxtNode.SetAttrListLevel( nNewListLevel ); + } + } + rTxtNode.AddToList(); + } + } + else // <sNumRule.Len() == 0 && sOldNumRule.Len() != 0> + { + rTxtNode.RemoveFromList(); + if ( bParagraphStyleChanged ) + { + SvUShortsSort aResetAttrsArray; + aResetAttrsArray.Insert( RES_PARATR_LIST_ID ); + aResetAttrsArray.Insert( RES_PARATR_LIST_LEVEL ); + aResetAttrsArray.Insert( RES_PARATR_LIST_ISRESTART ); + aResetAttrsArray.Insert( RES_PARATR_LIST_RESTARTVALUE ); + aResetAttrsArray.Insert( RES_PARATR_LIST_ISCOUNTED ); + SwPaM aPam( rTxtNode ); + // --> OD 2008-11-28 #i96644# + // suppress side effect "send data changed events" + rTxtNode.GetDoc()->ResetAttrs( aPam, sal_False, + &aResetAttrsArray, + false ); + // <-- + // --> OD 2008-11-19 #i70748# + if ( dynamic_cast<const SfxUInt16Item &>(rTxtNode.GetAttr( RES_PARATR_OUTLINELEVEL, FALSE )).GetValue() > 0 ) + { + rTxtNode.SetEmptyListStyleDueToSetOutlineLevelAttr(); + } + // <-- + } + } + } + else if ( sNumRule.Len() > 0 && !rTxtNode.IsInList() ) + { + rTxtNode.AddToList(); + } + // <-- + } + // End of method <HandleModifyAtTxtNode> +} +// <-- + +void SwTxtNode::Modify( SfxPoolItem* pOldValue, SfxPoolItem* pNewValue ) +{ + bool bWasNotifiable = m_bNotifiable; + m_bNotifiable = false; + + // Bug 24616/24617: + // Modify ueberladen, damit beim Loeschen von Vorlagen diese + // wieder richtig verwaltet werden (Outline-Numerierung!!) + // Bug25481: + // bei Nodes im Undo nie _ChgTxtCollUpdateNum rufen. + if( pOldValue && pNewValue && RES_FMT_CHG == pOldValue->Which() && + pRegisteredIn == ((SwFmtChg*)pNewValue)->pChangedFmt && + GetNodes().IsDocNodes() ) + { + _ChgTxtCollUpdateNum( + (SwTxtFmtColl*)((SwFmtChg*)pOldValue)->pChangedFmt, + (SwTxtFmtColl*)((SwFmtChg*)pNewValue)->pChangedFmt ); + } + + // --> OD 2008-03-27 #refactorlists# + if ( !mbInSetOrResetAttr ) + { + HandleModifyAtTxtNode( *this, pOldValue, pNewValue ); + } + // <-- + + SwCntntNode::Modify( pOldValue, pNewValue ); + + SwDoc * pDoc = GetDoc(); + // --> OD 2005-11-02 #125329# - assure that text node is in document nodes array + if ( pDoc && !pDoc->IsInDtor() && &pDoc->GetNodes() == &GetNodes() ) + // <-- + { + pDoc->GetNodes().UpdateOutlineNode(*this); + } + + m_bNotifiable = bWasNotifiable; + + if (pOldValue && (RES_REMOVE_UNO_OBJECT == pOldValue->Which())) + { // invalidate cached uno object + SetXParagraph(::com::sun::star::uno::Reference< + ::com::sun::star::text::XTextContent>(0)); + } +} + +SwFmtColl* SwTxtNode::ChgFmtColl( SwFmtColl *pNewColl ) +{ + ASSERT( pNewColl,"ChgFmtColl: Collectionpointer ist 0." ); + ASSERT( HAS_BASE( SwTxtFmtColl, pNewColl ), + "ChgFmtColl: ist kein Text-Collectionpointer." ); + + SwTxtFmtColl *pOldColl = GetTxtColl(); + if( pNewColl != pOldColl ) + { + SetCalcHiddenCharFlags(); + SwCntntNode::ChgFmtColl( pNewColl ); + // --> OD 2008-03-27 #refactorlists# +// NumRuleChgd(); +#if OSL_DEBUG_LEVEL > 1 + ASSERT( !mbInSetOrResetAttr, + "DEBUG ASSERTION - <SwTxtNode::ChgFmtColl(..)> called during <Set/ResetAttr(..)>" ) +#endif + if ( !mbInSetOrResetAttr ) + { + SwFmtChg aTmp1( pOldColl ); + SwFmtChg aTmp2( pNewColl ); + HandleModifyAtTxtNode( *this, &aTmp1, &aTmp2 ); + } + // <-- + } + + // nur wenn im normalen Nodes-Array + if( GetNodes().IsDocNodes() ) + { + _ChgTxtCollUpdateNum( pOldColl, static_cast<SwTxtFmtColl *>(pNewColl) ); + } + + GetNodes().UpdateOutlineNode(*this); + + return pOldColl; +} + +SwNodeNum* SwTxtNode::CreateNum() const +{ + if ( !mpNodeNum ) + { + // --> OD 2008-02-19 #refactorlists# + mpNodeNum = new SwNodeNum( const_cast<SwTxtNode*>(this) ); + // <-- + } + return mpNodeNum; +} + +SwNumberTree::tNumberVector SwTxtNode::GetNumberVector() const +{ + if ( GetNum() ) + { + return GetNum()->GetNumberVector(); + } + else + { + SwNumberTree::tNumberVector aResult; + return aResult; + } +} + +bool SwTxtNode::IsOutline() const +{ + bool bResult = false; + + //if ( GetOutlineLevel() != NO_NUMBERING )//#outline level,removed by zhaojianwei + if ( GetAttrOutlineLevel() > 0 ) //<-end,zhaojianwei + { + bResult = !IsInRedlines(); + } + else + { + const SwNumRule* pRule( GetNum() ? GetNum()->GetNumRule() : 0L ); + if ( pRule && pRule->IsOutlineRule() ) + { + bResult = !IsInRedlines(); + } + } + + return bResult; +} + +bool SwTxtNode::IsOutlineStateChanged() const +{ + return IsOutline() != m_bLastOutlineState; +} + +void SwTxtNode::UpdateOutlineState() +{ + m_bLastOutlineState = IsOutline(); +} + +//#outline level, zhaojianwei +int SwTxtNode::GetAttrOutlineLevel() const +{ + return ((const SfxUInt16Item &)GetAttr(RES_PARATR_OUTLINELEVEL)).GetValue(); +} +void SwTxtNode::SetAttrOutlineLevel(int nLevel) +{ + ASSERT( 0 <= nLevel && nLevel <= MAXLEVEL ,"SwTxtNode: Level Out Of Range" );//#outline level,zhaojianwei + if ( 0 <= nLevel && nLevel <= MAXLEVEL ) + { + SetAttr( SfxUInt16Item( RES_PARATR_OUTLINELEVEL, + static_cast<UINT16>(nLevel) ) ); + } +} +//<-end + +// --> OD 2008-11-19 #i70748# +bool SwTxtNode::IsEmptyListStyleDueToSetOutlineLevelAttr() +{ + return mbEmptyListStyleSetDueToSetOutlineLevelAttr; +} + +void SwTxtNode::SetEmptyListStyleDueToSetOutlineLevelAttr() +{ + if ( !mbEmptyListStyleSetDueToSetOutlineLevelAttr ) + { + SetAttr( SwNumRuleItem() ); + mbEmptyListStyleSetDueToSetOutlineLevelAttr = true; + } +} + +void SwTxtNode::ResetEmptyListStyleDueToResetOutlineLevelAttr() +{ + if ( mbEmptyListStyleSetDueToSetOutlineLevelAttr ) + { + ResetAttr( RES_PARATR_NUMRULE ); + mbEmptyListStyleSetDueToSetOutlineLevelAttr = false; + } +} +// <-- + + +// --> OD 2008-02-27 #refactorlists# +void SwTxtNode::SetAttrListLevel( int nLevel ) +{ + if ( nLevel < 0 || nLevel >= MAXLEVEL ) + { + ASSERT( false, + "<SwTxtNode::SetAttrListLevel()> - value of parameter <nLevel> is out of valid range" ); + return; + } + + SfxInt16Item aNewListLevelItem( RES_PARATR_LIST_LEVEL, + static_cast<INT16>(nLevel) ); + SetAttr( aNewListLevelItem ); +} +// <-- +// --> OD 2008-02-27 #refactorlists# +bool SwTxtNode::HasAttrListLevel() const +{ + return GetpSwAttrSet() && + GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_LEVEL, FALSE ) == SFX_ITEM_SET; +} +// <-- +// --> OD 2008-02-27 #refactorlists# +int SwTxtNode::GetAttrListLevel() const +{ + int nAttrListLevel = 0; + + const SfxInt16Item& aListLevelItem = + dynamic_cast<const SfxInt16Item&>(GetAttr( RES_PARATR_LIST_LEVEL )); + nAttrListLevel = static_cast<int>(aListLevelItem.GetValue()); + + return nAttrListLevel; +} +// <-- + +int SwTxtNode::GetActualListLevel() const +{ + return GetNum() ? GetNum()->GetLevelInListTree() : -1; +} + +// --> OD 2008-02-25 #refactorlists# +void SwTxtNode::SetListRestart( bool bRestart ) +{ +// CreateNum()->SetRestart(bRestart); + if ( !bRestart ) + { + // attribute not contained in paragraph style's attribute set. Thus, + // it can be reset to the attribute pool default by resetting the attribute. + ResetAttr( RES_PARATR_LIST_ISRESTART ); + } + else + { + SfxBoolItem aNewIsRestartItem( RES_PARATR_LIST_ISRESTART, + TRUE ); + SetAttr( aNewIsRestartItem ); + } +} + +// --> OD 2008-02-25 #refactorlists# +bool SwTxtNode::IsListRestart() const +{ +// return GetNum() ? GetNum()->IsRestart() : false; + const SfxBoolItem& aIsRestartItem = + dynamic_cast<const SfxBoolItem&>(GetAttr( RES_PARATR_LIST_ISRESTART )); + + return aIsRestartItem.GetValue() ? true : false; +} +// <-- + +/** Returns if the paragraph has a visible numbering or bullet. + This includes all kinds of numbering/bullet/outlines. + OD 2008-02-28 #newlistlevelattrs# + The concrete list label string has to be checked, too. + */ +bool SwTxtNode::HasVisibleNumberingOrBullet() const +{ + bool bRet = false; + + const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : 0L; + if ( pRule && IsCountedInList()) + { + // --> OD 2008-03-19 #i87154# + // Correction of #newlistlevelattrs#: + // The numbering type has to be checked for bullet lists. + const SwNumFmt& rFmt = pRule->Get( static_cast<USHORT>(GetActualListLevel() )); + if ( SVX_NUM_NUMBER_NONE != rFmt.GetNumberingType() || + pRule->MakeNumString( *(GetNum()) ).Len() > 0 ) + { + bRet = true; + } + // <-- + } + + return bRet; +} + +// --> OD 2008-02-25 #refactorlists# +void SwTxtNode::SetAttrListRestartValue( SwNumberTree::tSwNumTreeNumber nNumber ) +{ +// CreateNum()->SetStart(nNumber); + const bool bChanged( HasAttrListRestartValue() + ? GetAttrListRestartValue() != nNumber + : nNumber != USHRT_MAX ); + + if ( bChanged || !HasAttrListRestartValue() ) + { + if ( nNumber == USHRT_MAX ) + { + ResetAttr( RES_PARATR_LIST_RESTARTVALUE ); + } + else + { + SfxInt16Item aNewListRestartValueItem( RES_PARATR_LIST_RESTARTVALUE, + static_cast<INT16>(nNumber) ); + SetAttr( aNewListRestartValueItem ); + } + } +} +// <-- + +// --> OD 2008-02-27 #refactorlists# +bool SwTxtNode::HasAttrListRestartValue() const +{ + return GetpSwAttrSet() && + GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE, FALSE ) == SFX_ITEM_SET; +} +// <-- +SwNumberTree::tSwNumTreeNumber SwTxtNode::GetAttrListRestartValue() const +{ + ASSERT( HasAttrListRestartValue(), + "<SwTxtNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." ); + + const SfxInt16Item& aListRestartValueItem = + dynamic_cast<const SfxInt16Item&>(GetAttr( RES_PARATR_LIST_RESTARTVALUE )); + return static_cast<SwNumberTree::tSwNumTreeNumber>(aListRestartValueItem.GetValue()); +} + +// --> OD 2008-02-25 #refactorlists# +SwNumberTree::tSwNumTreeNumber SwTxtNode::GetActualListStartValue() const +{ +// return GetNum() ? GetNum()->GetStart() : 1; + SwNumberTree::tSwNumTreeNumber nListRestartValue = 1; + + if ( IsListRestart() && HasAttrListRestartValue() ) + { + nListRestartValue = GetAttrListRestartValue(); + } + else + { + SwNumRule* pRule = GetNumRule(); + if ( pRule ) + { + const SwNumFmt* pFmt = + pRule->GetNumFmt( static_cast<USHORT>(GetAttrListLevel()) ); + if ( pFmt ) + { + nListRestartValue = pFmt->GetStart(); + } + } + } + + return nListRestartValue; +} +// <-- + +bool SwTxtNode::IsNotifiable() const +{ + return m_bNotifiable && IsNotificationEnabled(); +} + +bool SwTxtNode::IsNotificationEnabled() const +{ + bool bResult = false; + const SwDoc * pDoc = GetDoc(); + if( pDoc ) + { + bResult = pDoc->IsInReading() || pDoc->IsInDtor() ? false : true; + } + return bResult; +} + +// --> OD 2008-02-27 #refactorlists# +void SwTxtNode::SetCountedInList( bool bCounted ) +{ + if ( bCounted ) + { + // attribute not contained in paragraph style's attribute set. Thus, + // it can be reset to the attribute pool default by resetting the attribute. + ResetAttr( RES_PARATR_LIST_ISCOUNTED ); + } + else + { + SfxBoolItem aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED, FALSE ); + SetAttr( aIsCountedInListItem ); + } +} +// <-- + +bool SwTxtNode::IsCountedInList() const +{ + const SfxBoolItem& aIsCountedInListItem = + dynamic_cast<const SfxBoolItem&>(GetAttr( RES_PARATR_LIST_ISCOUNTED )); + + return aIsCountedInListItem.GetValue() ? true : false; +} + +// --> OD 2008-03-13 #refactorlists# +void SwTxtNode::AddToList() +{ + if ( IsInList() ) + { + ASSERT( false, + "<SwTxtNode::AddToList()> - the text node is already added to a list. Serious defect -> please inform OD" ); + return; + } + + const String sListId = GetListId(); + if ( sListId.Len() > 0 ) + { + SwList* pList = GetDoc()->getListByName( sListId ); + if ( pList == 0 ) + { + // Create corresponding list. + SwNumRule* pNumRule = GetNumRule(); + if ( pNumRule ) + { + pList = GetDoc()->createList( sListId, GetNumRule()->GetName() ); + } + } + ASSERT( pList != 0, + "<SwTxtNode::AddToList()> - no list for given list id. Serious defect -> please inform OD" ); + if ( pList ) + { + pList->InsertListItem( *CreateNum(), GetAttrListLevel() ); + mpList = pList; + } + } +} + +void SwTxtNode::RemoveFromList() +{ + if ( IsInList() ) + { + mpList->RemoveListItem( *mpNodeNum ); + mpList = 0; + delete mpNodeNum; + mpNodeNum = 0L; + } +} + +bool SwTxtNode::IsInList() const +{ + return GetNum() != 0 && GetNum()->GetParent() != 0; +} +// <-- + +bool SwTxtNode::IsFirstOfNumRule() const +{ + bool bResult = false; + + if ( GetNum() && GetNum()->GetNumRule()) + bResult = GetNum()->IsFirst(); + + return bResult; +} + +// --> OD 2008-02-20 #refactorlists# +void SwTxtNode::SetListId( const String sListId ) +{ + const SfxStringItem& rListIdItem = + dynamic_cast<const SfxStringItem&>(GetAttr( RES_PARATR_LIST_ID )); + if ( rListIdItem.GetValue() != sListId ) + { + if ( sListId.Len() == 0 ) + { + ResetAttr( RES_PARATR_LIST_ID ); + } + else + { + SfxStringItem aNewListIdItem( RES_PARATR_LIST_ID, sListId ); + SetAttr( aNewListIdItem ); + } + } +} + +String SwTxtNode::GetListId() const +{ + String sListId; + + const SfxStringItem& rListIdItem = + dynamic_cast<const SfxStringItem&>(GetAttr( RES_PARATR_LIST_ID )); + sListId = rListIdItem.GetValue(); + + // As long as no explicit list id attribute is set, use the list id of + // the list, which has been created for the applied list style. + if ( sListId.Len() == 0 ) + { + SwNumRule* pRule = GetNumRule(); + if ( pRule ) + { + sListId = pRule->GetDefaultListId(); +//#if OSL_DEBUG_LEVEL > 1 +// ASSERT( false, +// "DEBUG ASSERTION: default list id of list style is applied." ); +//#endif +// // setting list id directly using <SwCntntNode::SetAttr(..)>, +// // because no handling of this attribute set is needed and to avoid +// // recursive calls of <SwTxtNode::SetAttr(..)> +// SfxStringItem aNewListIdItem( RES_PARATR_LIST_ID, sListId ); +// const_cast<SwTxtNode*>(this)->SwCntntNode::SetAttr( aNewListIdItem ); + } + } + + return sListId; +} +// <-- + +/** Determines, if the list level indent attributes can be applied to the + paragraph. + + OD 2008-01-17 #newlistlevelattrs# + The list level indents can be applied to the paragraph under the one + of following conditions: + - the list style is directly applied to the paragraph and the paragraph + has no own indent attributes. + - the list style is applied to the paragraph through one of its paragraph + styles, the paragraph has no own indent attributes and on the paragraph + style hierarchy from the paragraph to the paragraph style with the + list style no indent attributes are found. + + @author OD + + @return boolean +*/ +bool SwTxtNode::AreListLevelIndentsApplicable() const +{ + bool bAreListLevelIndentsApplicable( true ); + + if ( !GetNum() || !GetNum()->GetNumRule() ) + { + // no list style applied to paragraph + bAreListLevelIndentsApplicable = false; + } + else if ( HasSwAttrSet() && + GetpSwAttrSet()->GetItemState( RES_LR_SPACE, FALSE ) == SFX_ITEM_SET ) + { + // paragraph has hard-set indent attributes + bAreListLevelIndentsApplicable = false; + } + else if ( HasSwAttrSet() && + GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, FALSE ) == SFX_ITEM_SET ) + { + // list style is directly applied to paragraph and paragraph has no + // hard-set indent attributes + bAreListLevelIndentsApplicable = true; + } + else + { + // list style is applied through one of the paragraph styles and + // paragraph has no hard-set indent attributes + + // check, paragraph's + const SwTxtFmtColl* pColl = GetTxtColl(); + while ( pColl ) + { + if ( pColl->GetAttrSet().GetItemState( RES_LR_SPACE, FALSE ) == SFX_ITEM_SET ) + { + // indent attributes found in the paragraph style hierarchy. + bAreListLevelIndentsApplicable = false; + break; + } + + if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, FALSE ) == SFX_ITEM_SET ) + { + // paragraph style with the list style found and until now no + // indent attributes are found in the paragraph style hierarchy. + bAreListLevelIndentsApplicable = true; + break; + } + + pColl = dynamic_cast<const SwTxtFmtColl*>(pColl->DerivedFrom()); + ASSERT( pColl, + "<SwTxtNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." ); + } + } + + return bAreListLevelIndentsApplicable; +} + +/** Retrieves the list tab stop position, if the paragraph's list level defines + one and this list tab stop has to merged into the tap stops of the paragraph + + OD 2008-01-17 #newlistlevelattrs# + + @author OD + + @param nListTabStopPosition + output parameter - containing the list tab stop position + + @return boolean - indicating, if a list tab stop position is provided +*/ +bool SwTxtNode::GetListTabStopPosition( long& nListTabStopPosition ) const +{ + bool bListTanStopPositionProvided( false ); + + const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : 0; + if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 ) + { + const SwNumFmt& rFmt = pNumRule->Get( static_cast<USHORT>(GetActualListLevel()) ); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT && + rFmt.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB ) + { + bListTanStopPositionProvided = true; + nListTabStopPosition = rFmt.GetListtabPos(); + + if ( getIDocumentSettingAccess()->get(IDocumentSettingAccess::TABS_RELATIVE_TO_INDENT) ) + { + // tab stop position are treated to be relative to the "before text" + // indent value of the paragraph. Thus, adjust <nListTabStopPos>. + if ( AreListLevelIndentsApplicable() ) + { + nListTabStopPosition -= rFmt.GetIndentAt(); + } + else if (!getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)) + { + SvxLRSpaceItem aItem = GetSwAttrSet().GetLRSpace(); + nListTabStopPosition -= aItem.GetTxtLeft(); + } + } + } + } + + return bListTanStopPositionProvided; +} + +/** Retrieves the character following the list label, if the paragraph's + list level defines one. + + OD 2008-01-17 #newlistlevelattrs# + + @author OD + + @return XubString - the list tab stop position +*/ +XubString SwTxtNode::GetLabelFollowedBy() const +{ + XubString aLabelFollowedBy; + + const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : 0; + if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 ) + { + const SwNumFmt& rFmt = pNumRule->Get( static_cast<USHORT>(GetActualListLevel()) ); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + switch ( rFmt.GetLabelFollowedBy() ) + { + case SvxNumberFormat::LISTTAB: + { + const sal_Unicode aTab = '\t'; + aLabelFollowedBy.Insert( aTab, 0 ); + } + break; + case SvxNumberFormat::SPACE: + { + const sal_Unicode aSpace = ' '; + aLabelFollowedBy.Insert( aSpace, 0 ); + } + break; + case SvxNumberFormat::NOTHING: + { + // intentionally left blank. + } + break; + default: + { + ASSERT( false, + "<SwTxtNode::GetLabelFollowedBy()> - unknown SvxNumberFormat::GetLabelFollowedBy() return value" ); + } + } + } + } + + return aLabelFollowedBy; +} + +void SwTxtNode::CalcHiddenCharFlags() const +{ + xub_StrLen nStartPos; + xub_StrLen nEndPos; + // Update of the flags is done inside GetBoundsOfHiddenRange() + SwScriptInfo::GetBoundsOfHiddenRange( *this, 0, nStartPos, nEndPos ); +} + +// --> FME 2004-06-08 #i12836# enhanced pdf export +bool SwTxtNode::IsHidden() const +{ + if ( HasHiddenParaField() || HasHiddenCharAttribute( true ) ) + return true; + + const SwSectionNode* pSectNd = FindSectionNode(); + if ( pSectNd && pSectNd->GetSection().IsHiddenFlag() ) + return true; + + return false; +} +// <-- + +// --> OD 2008-03-13 #refactorlists# +namespace { + // Helper class for special handling of setting attributes at text node: + // In constructor an instance of the helper class recognize whose attributes + // are set and perform corresponding actions before the intrinsic set of + // attributes has been taken place. + // In the destructor - after the attributes have been set at the text + // node - corresponding actions are performed. + // The following is handled: + // (1) When the list style attribute - RES_PARATR_NUMRULE - is set, + // (A) list style attribute is empty -> the text node is removed from + // its list. + // (B) list style attribute is not empty + // (a) text node has no list style -> add text node to its list after + // the attributes have been set. + // (b) text node has list style -> change of list style is notified + // after the attributes have been set. + // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed, + // the text node is removed from its current list before the attributes + // are set and added to its new list after the attributes have been set. + // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set + // and changed after the attributes have been set + // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set + // and changed after the attributes have been set + // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - + // is set and changed after the attributes have been set + // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set + // and changed after the attributes have been set + // (7) Set or Reset emtpy list style due to changed outline level - RES_PARATR_OUTLINELEVEL. + class HandleSetAttrAtTxtNode + { + public: + HandleSetAttrAtTxtNode( SwTxtNode& rTxtNode, + const SfxPoolItem& pItem ); + HandleSetAttrAtTxtNode( SwTxtNode& rTxtNode, + const SfxItemSet& rItemSet ); + ~HandleSetAttrAtTxtNode(); + + private: + SwTxtNode& mrTxtNode; + bool mbAddTxtNodeToList; + bool mbUpdateListLevel; + bool mbUpdateListRestart; + bool mbUpdateListCount; + // --> OD 2008-11-19 #i70748# + bool mbOutlineLevelSet; + // <-- + }; + + HandleSetAttrAtTxtNode::HandleSetAttrAtTxtNode( SwTxtNode& rTxtNode, + const SfxPoolItem& pItem ) + : mrTxtNode( rTxtNode ), + mbAddTxtNodeToList( false ), + mbUpdateListLevel( false ), + mbUpdateListRestart( false ), + mbUpdateListCount( false ), + // --> OD 2008-11-19 #i70748# + mbOutlineLevelSet( false ) + // <-- + { + switch ( pItem.Which() ) + { + // handle RES_PARATR_NUMRULE + case RES_PARATR_NUMRULE: + { + mrTxtNode.RemoveFromList(); + + const SwNumRuleItem& pNumRuleItem = + dynamic_cast<const SwNumRuleItem&>(pItem); + if ( pNumRuleItem.GetValue().Len() > 0 ) + { + mbAddTxtNodeToList = true; + } + } + break; + + // handle RES_PARATR_LIST_ID + case RES_PARATR_LIST_ID: + { + const SfxStringItem& pListIdItem = + dynamic_cast<const SfxStringItem&>(pItem); + ASSERT( pListIdItem.GetValue().Len() > 0, + "<HandleSetAttrAtTxtNode(..)> - empty list id attribute not excepted. Serious defect -> please inform OD." ); +// const SfxStringItem& rListIdItemOfTxtNode = +// dynamic_cast<const SfxStringItem&>( +// rTxtNode.GetAttr( RES_PARATR_LIST_ID )); +// if ( pListIdItem.GetValue() != rListIdItemOfTxtNode.GetValue() ) + const String sListIdOfTxtNode = rTxtNode.GetListId(); + if ( pListIdItem.GetValue() != sListIdOfTxtNode ) + { + mbAddTxtNodeToList = true; + if ( mrTxtNode.IsInList() ) + { + mrTxtNode.RemoveFromList(); + } + } + } + break; + + // handle RES_PARATR_LIST_LEVEL + case RES_PARATR_LIST_LEVEL: + { + const SfxInt16Item& aListLevelItem = + dynamic_cast<const SfxInt16Item&>(pItem); + if ( aListLevelItem.GetValue() != mrTxtNode.GetAttrListLevel() ) + { + mbUpdateListLevel = true; + } + } + break; + + // handle RES_PARATR_LIST_ISRESTART + case RES_PARATR_LIST_ISRESTART: + { + const SfxBoolItem& aListIsRestartItem = + dynamic_cast<const SfxBoolItem&>(pItem); + if ( aListIsRestartItem.GetValue() != + (mrTxtNode.IsListRestart() ? TRUE : FALSE) ) + { + mbUpdateListRestart = true; + } + } + break; + + // handle RES_PARATR_LIST_RESTARTVALUE + case RES_PARATR_LIST_RESTARTVALUE: + { + const SfxInt16Item& aListRestartValueItem = + dynamic_cast<const SfxInt16Item&>(pItem); + if ( !mrTxtNode.HasAttrListRestartValue() || + aListRestartValueItem.GetValue() != mrTxtNode.GetAttrListRestartValue() ) + { + mbUpdateListRestart = true; + } + } + break; + + // handle RES_PARATR_LIST_ISCOUNTED + case RES_PARATR_LIST_ISCOUNTED: + { + const SfxBoolItem& aIsCountedInListItem = + dynamic_cast<const SfxBoolItem&>(pItem); + if ( aIsCountedInListItem.GetValue() != + (mrTxtNode.IsCountedInList() ? TRUE : FALSE) ) + { + mbUpdateListCount = true; + } + } + break; + + // --> OD 2008-11-19 #i70748# + // handle RES_PARATR_OUTLINELEVEL + case RES_PARATR_OUTLINELEVEL: + { + const SfxUInt16Item& aOutlineLevelItem = + dynamic_cast<const SfxUInt16Item&>(pItem); + if ( aOutlineLevelItem.GetValue() != mrTxtNode.GetAttrOutlineLevel() ) + { + mbOutlineLevelSet = true; + } + } + break; + // <-- + } + + } + + HandleSetAttrAtTxtNode::HandleSetAttrAtTxtNode( SwTxtNode& rTxtNode, + const SfxItemSet& rItemSet ) + : mrTxtNode( rTxtNode ), + mbAddTxtNodeToList( false ), + mbUpdateListLevel( false ), + mbUpdateListRestart( false ), + mbUpdateListCount( false ), + // --> OD 2008-11-19 #i70748# + mbOutlineLevelSet( false ) + // <-- + { + const SfxPoolItem* pItem = 0; + // handle RES_PARATR_NUMRULE + if ( rItemSet.GetItemState( RES_PARATR_NUMRULE, FALSE, &pItem ) == SFX_ITEM_SET ) + { + mrTxtNode.RemoveFromList(); + + const SwNumRuleItem* pNumRuleItem = + dynamic_cast<const SwNumRuleItem*>(pItem); + if ( pNumRuleItem->GetValue().Len() > 0 ) + { + mbAddTxtNodeToList = true; + // --> OD 2008-11-19 #i70748# + mrTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + // <-- + } + } + + // handle RES_PARATR_LIST_ID + if ( rItemSet.GetItemState( RES_PARATR_LIST_ID, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxStringItem* pListIdItem = + dynamic_cast<const SfxStringItem*>(pItem); +// const SfxStringItem& rListIdItemOfTxtNode = +// dynamic_cast<const SfxStringItem&>( +// mrTxtNode.GetAttr( RES_PARATR_LIST_ID )); +// if ( pListIdItem && +// pListIdItem->GetValue() != rListIdItemOfTxtNode.GetValue() ) + const String sListIdOfTxtNode = mrTxtNode.GetListId(); + if ( pListIdItem && + pListIdItem->GetValue() != sListIdOfTxtNode ) + { + mbAddTxtNodeToList = true; + if ( mrTxtNode.IsInList() ) + { + mrTxtNode.RemoveFromList(); + } + } + } + + // handle RES_PARATR_LIST_LEVEL + if ( rItemSet.GetItemState( RES_PARATR_LIST_LEVEL, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxInt16Item* pListLevelItem = + dynamic_cast<const SfxInt16Item*>(pItem); + if ( pListLevelItem->GetValue() != mrTxtNode.GetAttrListLevel() ) + { + mbUpdateListLevel = true; + } + } + + // handle RES_PARATR_LIST_ISRESTART + if ( rItemSet.GetItemState( RES_PARATR_LIST_ISRESTART, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxBoolItem* pListIsRestartItem = + dynamic_cast<const SfxBoolItem*>(pItem); + if ( pListIsRestartItem->GetValue() != + (mrTxtNode.IsListRestart() ? TRUE : FALSE) ) + { + mbUpdateListRestart = true; + } + } + + // handle RES_PARATR_LIST_RESTARTVALUE + if ( rItemSet.GetItemState( RES_PARATR_LIST_RESTARTVALUE, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxInt16Item* pListRestartValueItem = + dynamic_cast<const SfxInt16Item*>(pItem); + if ( !mrTxtNode.HasAttrListRestartValue() || + pListRestartValueItem->GetValue() != mrTxtNode.GetAttrListRestartValue() ) + { + mbUpdateListRestart = true; + } + } + + // handle RES_PARATR_LIST_ISCOUNTED + if ( rItemSet.GetItemState( RES_PARATR_LIST_ISCOUNTED, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxBoolItem* pIsCountedInListItem = + dynamic_cast<const SfxBoolItem*>(pItem); + if ( pIsCountedInListItem->GetValue() != + (mrTxtNode.IsCountedInList() ? TRUE : FALSE) ) + { + mbUpdateListCount = true; + } + } + + // --> OD 2008-11-19 #i70748# + // handle RES_PARATR_OUTLINELEVEL + if ( rItemSet.GetItemState( RES_PARATR_OUTLINELEVEL, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxUInt16Item* pOutlineLevelItem = + dynamic_cast<const SfxUInt16Item*>(pItem); + if ( pOutlineLevelItem->GetValue() != mrTxtNode.GetAttrOutlineLevel() ) + { + mbOutlineLevelSet = true; + } + } + // <-- + } + + HandleSetAttrAtTxtNode::~HandleSetAttrAtTxtNode() + { + if ( mbAddTxtNodeToList ) + { + SwNumRule* pNumRuleAtTxtNode = mrTxtNode.GetNumRule(); + if ( pNumRuleAtTxtNode ) + { + mrTxtNode.AddToList(); + } + } + else + { + if ( mbUpdateListLevel && mrTxtNode.IsInList() ) + { + const_cast<SwNodeNum*>(mrTxtNode.GetNum())->SetLevelInListTree( + mrTxtNode.GetAttrListLevel() ); + } + + if ( mbUpdateListRestart && mrTxtNode.IsInList() ) + { + SwNodeNum* pNodeNum = const_cast<SwNodeNum*>(mrTxtNode.GetNum()); + pNodeNum->InvalidateMe(); + pNodeNum->NotifyInvalidSiblings(); + } + + if ( mbUpdateListCount && mrTxtNode.IsInList() ) + { + const_cast<SwNodeNum*>(mrTxtNode.GetNum())->InvalidateAndNotifyTree(); + } + } + + // --> OD 2008-11-19 #i70748# + if ( mbOutlineLevelSet ) + { + if ( mrTxtNode.GetAttrOutlineLevel() == 0 ) + { + mrTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + else + { + const SfxPoolItem* pItem = 0; + if ( mrTxtNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE, + TRUE, &pItem ) + != SFX_ITEM_SET ) + { + mrTxtNode.SetEmptyListStyleDueToSetOutlineLevelAttr(); + } + } + } + // <-- + } + // End of class <HandleSetAttrAtTxtNode> +} + +BOOL SwTxtNode::SetAttr( const SfxPoolItem& pItem ) +{ + const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr ); + mbInSetOrResetAttr = true; + + HandleSetAttrAtTxtNode aHandleSetAttr( *this, pItem ); + + BOOL bRet = SwCntntNode::SetAttr( pItem ); + + mbInSetOrResetAttr = bOldIsSetOrResetAttr; + + return bRet; +} + +BOOL SwTxtNode::SetAttr( const SfxItemSet& rSet ) +{ + const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr ); + mbInSetOrResetAttr = true; + + HandleSetAttrAtTxtNode aHandleSetAttr( *this, rSet ); + + BOOL bRet = SwCntntNode::SetAttr( rSet ); + + mbInSetOrResetAttr = bOldIsSetOrResetAttr; + + return bRet; +} + +namespace { + // Helper class for special handling of resetting attributes at text node: + // In constructor an instance of the helper class recognize whose attributes + // are reset and perform corresponding actions before the intrinsic reset of + // attributes has been taken place. + // In the destructor - after the attributes have been reset at the text + // node - corresponding actions are performed. + // The following is handled: + // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset, + // the text is removed from its list before the attributes have been reset. + // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset, + // the text is removed from its list before the attributes have been reset. + // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset. + // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset. + // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset. + // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset. + // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset. + class HandleResetAttrAtTxtNode + { + public: + HandleResetAttrAtTxtNode( SwTxtNode& rTxtNode, + const USHORT nWhich1, + const USHORT nWhich2 ); + HandleResetAttrAtTxtNode( SwTxtNode& rTxtNode, + const SvUShorts& rWhichArr ); + HandleResetAttrAtTxtNode( SwTxtNode& rTxtNode ); + + ~HandleResetAttrAtTxtNode(); + + private: + SwTxtNode& mrTxtNode; + bool mbListStyleOrIdReset; + bool mbUpdateListLevel; + bool mbUpdateListRestart; + bool mbUpdateListCount; + }; + + HandleResetAttrAtTxtNode::HandleResetAttrAtTxtNode( SwTxtNode& rTxtNode, + const USHORT nWhich1, + const USHORT nWhich2 ) + : mrTxtNode( rTxtNode ), + mbListStyleOrIdReset( false ), + mbUpdateListLevel( false ), + mbUpdateListRestart( false ), + mbUpdateListCount( false ) + { + bool bRemoveFromList( false ); + if ( nWhich2 != 0 && nWhich2 > nWhich1 ) + { + // RES_PARATR_NUMRULE and RES_PARATR_LIST_ID + if ( nWhich1 <= RES_PARATR_NUMRULE && RES_PARATR_NUMRULE <= nWhich2 ) + { + bRemoveFromList = mrTxtNode.GetNumRule() != 0; + mbListStyleOrIdReset = true; + } + else if ( nWhich1 <= RES_PARATR_LIST_ID && RES_PARATR_LIST_ID <= nWhich2 ) + { + bRemoveFromList = mrTxtNode.GetpSwAttrSet() && + mrTxtNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, FALSE ) == SFX_ITEM_SET; + // --> OD 2008-10-20 #i92898# + mbListStyleOrIdReset = true; + // <-- + } + + if ( !bRemoveFromList ) + { + // RES_PARATR_LIST_LEVEL + mbUpdateListLevel = ( nWhich1 <= RES_PARATR_LIST_LEVEL && + RES_PARATR_LIST_LEVEL <= nWhich2 && + mrTxtNode.HasAttrListLevel() ); + + // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE + mbUpdateListRestart = + ( nWhich1 <= RES_PARATR_LIST_ISRESTART && RES_PARATR_LIST_ISRESTART <= nWhich2 && + mrTxtNode.IsListRestart() ) || + ( nWhich1 <= RES_PARATR_LIST_RESTARTVALUE && RES_PARATR_LIST_RESTARTVALUE <= nWhich2 && + mrTxtNode.HasAttrListRestartValue() ); + + // RES_PARATR_LIST_ISCOUNTED + mbUpdateListCount = + ( nWhich1 <= RES_PARATR_LIST_ISCOUNTED && RES_PARATR_LIST_ISCOUNTED <= nWhich2 && + !mrTxtNode.IsCountedInList() ); + } + + // --> OD 2008-11-19 #i70748# + // RES_PARATR_OUTLINELEVEL + if ( nWhich1 <= RES_PARATR_OUTLINELEVEL && RES_PARATR_OUTLINELEVEL <= nWhich2 ) + { + mrTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + // <-- + } + else + { + // RES_PARATR_NUMRULE and RES_PARATR_LIST_ID + if ( nWhich1 == RES_PARATR_NUMRULE ) + { + bRemoveFromList = mrTxtNode.GetNumRule() != 0; + mbListStyleOrIdReset = true; + } + else if ( nWhich1 == RES_PARATR_LIST_ID ) + { + bRemoveFromList = mrTxtNode.GetpSwAttrSet() && + mrTxtNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, FALSE ) == SFX_ITEM_SET; + // --> OD 2008-10-20 #i92898# + mbListStyleOrIdReset = true; + // <-- + } + // --> OD 2008-11-19 #i70748# + // RES_PARATR_OUTLINELEVEL + else if ( nWhich1 == RES_PARATR_OUTLINELEVEL ) + { + mrTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + // <-- + + if ( !bRemoveFromList ) + { + // RES_PARATR_LIST_LEVEL + mbUpdateListLevel = nWhich1 == RES_PARATR_LIST_LEVEL && + mrTxtNode.HasAttrListLevel(); + + // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE + mbUpdateListRestart = ( nWhich1 == RES_PARATR_LIST_ISRESTART && + mrTxtNode.IsListRestart() ) || + ( nWhich1 == RES_PARATR_LIST_RESTARTVALUE && + mrTxtNode.HasAttrListRestartValue() ); + + // RES_PARATR_LIST_ISCOUNTED + mbUpdateListCount = nWhich1 == RES_PARATR_LIST_ISCOUNTED && + !mrTxtNode.IsCountedInList(); + } + } + + if ( bRemoveFromList && mrTxtNode.IsInList() ) + { + mrTxtNode.RemoveFromList(); + } + } + + HandleResetAttrAtTxtNode::HandleResetAttrAtTxtNode( SwTxtNode& rTxtNode, + const SvUShorts& rWhichArr ) + : mrTxtNode( rTxtNode ), + mbListStyleOrIdReset( false ), + mbUpdateListLevel( false ), + mbUpdateListRestart( false ), + mbUpdateListCount( false ) + { + bool bRemoveFromList( false ); + { + const USHORT nEnd = rWhichArr.Count(); + for ( USHORT n = 0; n < nEnd; ++n ) + { + // RES_PARATR_NUMRULE and RES_PARATR_LIST_ID + if ( rWhichArr[ n ] == RES_PARATR_NUMRULE ) + { + bRemoveFromList = bRemoveFromList || + mrTxtNode.GetNumRule() != 0; + mbListStyleOrIdReset = true; + } + else if ( rWhichArr[ n ] == RES_PARATR_LIST_ID ) + { + bRemoveFromList = bRemoveFromList || + ( mrTxtNode.GetpSwAttrSet() && + mrTxtNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, FALSE ) == SFX_ITEM_SET ); + // --> OD 2008-10-20 #i92898# + mbListStyleOrIdReset = true; + // <-- + } + // --> OD 2008-11-19 #i70748# + // RES_PARATR_OUTLINELEVEL + else if ( rWhichArr[ n ] == RES_PARATR_OUTLINELEVEL ) + { + mrTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + } + // <-- + + if ( !bRemoveFromList ) + { + // RES_PARATR_LIST_LEVEL + mbUpdateListLevel = mbUpdateListLevel || + ( rWhichArr[ n ] == RES_PARATR_LIST_LEVEL && + mrTxtNode.HasAttrListLevel() ); + + // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE + mbUpdateListRestart = mbUpdateListRestart || + ( rWhichArr[ n ] == RES_PARATR_LIST_ISRESTART && + mrTxtNode.IsListRestart() ) || + ( rWhichArr[ n ] == RES_PARATR_LIST_RESTARTVALUE && + mrTxtNode.HasAttrListRestartValue() ); + + // RES_PARATR_LIST_ISCOUNTED + mbUpdateListCount = mbUpdateListCount || + ( rWhichArr[ n ] == RES_PARATR_LIST_ISCOUNTED && + !mrTxtNode.IsCountedInList() ); + } + } + } + + if ( bRemoveFromList && mrTxtNode.IsInList() ) + { + mrTxtNode.RemoveFromList(); + } + } + + HandleResetAttrAtTxtNode::HandleResetAttrAtTxtNode( SwTxtNode& rTxtNode ) + : mrTxtNode( rTxtNode ), + mbListStyleOrIdReset( false ), + mbUpdateListLevel( false ), + mbUpdateListRestart( false ), + mbUpdateListCount( false ) + { + mbListStyleOrIdReset = true; + if ( rTxtNode.IsInList() ) + { + rTxtNode.RemoveFromList(); + } + // --> OD 2008-11-19 #i70748# + mrTxtNode.ResetEmptyListStyleDueToResetOutlineLevelAttr(); + // <-- + } + + HandleResetAttrAtTxtNode::~HandleResetAttrAtTxtNode() + { + if ( mbListStyleOrIdReset && !mrTxtNode.IsInList() ) + { + // check, if in spite of the reset of the list style or the list id + // the paragraph still has to be added to a list. + if ( mrTxtNode.GetNumRule() && + mrTxtNode.GetListId().Len() > 0 ) + { + // --> OD 2009-01-14 #i96062# + // If paragraph has no list level attribute set and list style + // is the outline style, apply outline level as the list level. + if ( !mrTxtNode.HasAttrListLevel() && + mrTxtNode.GetNumRule()->GetName() == + String::CreateFromAscii( SwNumRule::GetOutlineRuleName() ) && + mrTxtNode.GetTxtColl()->IsAssignedToListLevelOfOutlineStyle() ) + { + int nNewListLevel = mrTxtNode.GetTxtColl()->GetAssignedOutlineStyleLevel(); + if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL ) + { + mrTxtNode.SetAttrListLevel( nNewListLevel ); + } + } + // <-- + mrTxtNode.AddToList(); + } + // --> OD 2008-11-19 #i70748# + else if ( dynamic_cast<const SfxUInt16Item &>(mrTxtNode.GetAttr( RES_PARATR_OUTLINELEVEL, FALSE )).GetValue() > 0 ) + { + mrTxtNode.SetEmptyListStyleDueToSetOutlineLevelAttr(); + } + // <-- + } + + if ( mrTxtNode.IsInList() ) + { + if ( mbUpdateListLevel ) + { + SwNodeNum* pNodeNum = const_cast<SwNodeNum*>(mrTxtNode.GetNum()); + pNodeNum->SetLevelInListTree( mrTxtNode.GetAttrListLevel() ); + } + + if ( mbUpdateListRestart ) + { + SwNodeNum* pNodeNum = const_cast<SwNodeNum*>(mrTxtNode.GetNum()); + pNodeNum->InvalidateMe(); + pNodeNum->NotifyInvalidSiblings(); + } + + if ( mbUpdateListCount ) + { + SwNodeNum* pNodeNum = const_cast<SwNodeNum*>(mrTxtNode.GetNum()); + pNodeNum->InvalidateAndNotifyTree(); + } + } + } + // End of class <HandleResetAttrAtTxtNode> +} + +BOOL SwTxtNode::ResetAttr( USHORT nWhich1, USHORT nWhich2 ) +{ + const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr ); + mbInSetOrResetAttr = true; + + HandleResetAttrAtTxtNode aHandleResetAttr( *this, nWhich1, nWhich2 ); + + BOOL bRet = SwCntntNode::ResetAttr( nWhich1, nWhich2 ); + + mbInSetOrResetAttr = bOldIsSetOrResetAttr; + + return bRet; +} + +BOOL SwTxtNode::ResetAttr( const SvUShorts& rWhichArr ) +{ + const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr ); + mbInSetOrResetAttr = true; + + HandleResetAttrAtTxtNode aHandleResetAttr( *this, rWhichArr ); + + BOOL bRet = SwCntntNode::ResetAttr( rWhichArr ); + + mbInSetOrResetAttr = bOldIsSetOrResetAttr; + + return bRet; +} + +USHORT SwTxtNode::ResetAllAttr() +{ + const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr ); + mbInSetOrResetAttr = true; + + HandleResetAttrAtTxtNode aHandleResetAttr( *this ); + + USHORT nRet = SwCntntNode::ResetAllAttr(); + + mbInSetOrResetAttr = bOldIsSetOrResetAttr; + + return nRet; +} +// <-- + + +// sw::Metadatable +::sfx2::IXmlIdRegistry& SwTxtNode::GetRegistry() +{ + return GetDoc()->GetXmlIdRegistry(); +} + +bool SwTxtNode::IsInClipboard() const +{ + return GetDoc()->IsClipBoard(); +} + +bool SwTxtNode::IsInUndo() const +{ + return &GetNodes() == GetDoc()->GetUndoNds(); +} + +bool SwTxtNode::IsInContent() const +{ + return !GetDoc()->IsInHeaderFooter( SwNodeIndex(*this) ); +} + +#include <unoparagraph.hxx> + +using namespace ::com::sun::star; + +uno::Reference< rdf::XMetadatable > +SwTxtNode::MakeUnoObject() +{ + const uno::Reference<rdf::XMetadatable> xMeta( + SwXParagraph::CreateXParagraph(*GetDoc(), *this), uno::UNO_QUERY); + return xMeta; +} + diff --git a/sw/source/core/txtnode/swfntcch.cxx b/sw/source/core/txtnode/swfntcch.cxx new file mode 100644 index 000000000000..202e6d48f763 --- /dev/null +++ b/sw/source/core/txtnode/swfntcch.cxx @@ -0,0 +1,94 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <viewsh.hxx> +#include "swfntcch.hxx" +#include "fmtcol.hxx" +#include "swfont.hxx" + +// aus atrstck.cxx +extern const BYTE StackPos[]; + +// globale Variablen, werden in SwFntCch.Hxx bekanntgegeben +// Der FontCache wird in TxtInit.Cxx _TXTINIT erzeugt und in _TXTEXIT geloescht +SwFontCache *pSwFontCache = NULL; + +/************************************************************************* +|* +|* SwFontObj::SwFontObj(), ~SwFontObj() +|* +|* Ersterstellung AMA 25. Jun. 95 +|* Letzte Aenderung AMA 25. Jun. 95 +|* +|*************************************************************************/ + +SwFontObj::SwFontObj( const void *pOwn, ViewShell *pSh ) : + SwCacheObj( (void*)pOwn ), + aSwFont( &((SwTxtFmtColl *)pOwn)->GetAttrSet(), pSh ? pSh->getIDocumentSettingAccess() : 0 ) +{ + aSwFont.GoMagic( pSh, aSwFont.GetActual() ); + const SwAttrSet& rAttrSet = ((SwTxtFmtColl *)pOwn)->GetAttrSet(); + for (USHORT i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++) + pDefaultArray[ StackPos[ i ] ] = &rAttrSet.Get( i, TRUE ); +} + +SwFontObj::~SwFontObj() +{ +} + +/************************************************************************* +|* +|* SwFontAccess::SwFontAccess() +|* +|* Ersterstellung AMA 25. Jun. 95 +|* Letzte Aenderung AMA 25. Jun. 95 +|* +|*************************************************************************/ + +SwFontAccess::SwFontAccess( const void *pOwn, ViewShell *pSh ) : + SwCacheAccess( *pSwFontCache, pOwn, + ((SwTxtFmtColl*)pOwn)->IsInSwFntCache() ), + pShell( pSh ) +{ +} + +SwFontObj *SwFontAccess::Get( ) +{ + return (SwFontObj *) SwCacheAccess::Get( ); +} + +SwCacheObj *SwFontAccess::NewObj( ) +{ + ((SwTxtFmtColl*)pOwner)->SetInSwFntCache( TRUE ); + return new SwFontObj( pOwner, pShell ); +} + + diff --git a/sw/source/core/txtnode/swfont.cxx b/sw/source/core/txtnode/swfont.cxx new file mode 100644 index 000000000000..49e111e9053f --- /dev/null +++ b/sw/source/core/txtnode/swfont.cxx @@ -0,0 +1,1246 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <hintids.hxx> + +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#ifndef _OUTDEV_HXX //autogen +#include <vcl/outdev.hxx> +#endif +#include <unotools/localedatawrapper.hxx> +#include <editeng/unolingu.hxx> +#include <editeng/brshitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/blnkitem.hxx> +#include <editeng/nhypitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/escpitem.hxx> +#include <editeng/akrnitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/charreliefitem.hxx> +#ifndef _SVX_CNTRITEM_HXX //autogen +#include <editeng/cntritem.hxx> +#endif +#include <editeng/colritem.hxx> +#include <editeng/cscoitem.hxx> +#include <editeng/crsditem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/postitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#ifndef _SVX_EMPHITEM_HXX //autogen +#include <editeng/emphitem.hxx> +#endif +#include <editeng/charscaleitem.hxx> +#include <editeng/charrotateitem.hxx> +#include <editeng/twolinesitem.hxx> +#include <editeng/charhiddenitem.hxx> +#include <IDocumentSettingAccess.hxx> +#include <vcl/window.hxx> +#include <charatr.hxx> +#include <viewsh.hxx> // Bildschirmabgleich +#include <swfont.hxx> +#include <fntcache.hxx> // FontCache +#include <txtfrm.hxx> // SwTxtFrm +#include <scriptinfo.hxx> + +#if defined(WIN) || defined(WNT) || defined(PM2) +#define FNT_LEADING_HACK +#endif + +#if defined(WIN) || defined(WNT) +#define FNT_ATM_HACK +#endif + +#ifdef DBG_UTIL +// globale Variable +SvStatistics aSvStat; +#endif + +using namespace ::com::sun::star; + +/************************************************************************ + * Hintergrundbrush setzen, z.B. bei Zeichenvorlagen + ***********************************************************************/ + +void SwFont::SetBackColor( Color* pNewColor ) +{ + delete pBackColor; + pBackColor = pNewColor; + bFntChg = TRUE; + aSub[SW_LATIN].pMagic = aSub[SW_CJK].pMagic = aSub[SW_CTL].pMagic = 0; +} + +// maps directions for vertical layout +USHORT MapDirection( USHORT nDir, const BOOL bVertFormat ) +{ + if ( bVertFormat ) + { + switch ( nDir ) + { + case 0 : + nDir = 2700; + break; + case 900 : + nDir = 0; + break; + case 2700 : + nDir = 1800; + break; +#if OSL_DEBUG_LEVEL > 1 + default : + ASSERT( sal_False, "Unsupported direction" ); + break; +#endif + } + } + return nDir; +} + +// maps the absolute direction set at the font to its logical conterpart +// in the rotated environment +USHORT UnMapDirection( USHORT nDir, const BOOL bVertFormat ) +{ + if ( bVertFormat ) + { + switch ( nDir ) + { + case 0 : + nDir = 900; + break; + case 1800 : + nDir = 2700; + break; + case 2700 : + nDir = 0; + break; +#if OSL_DEBUG_LEVEL > 1 + default : + ASSERT( sal_False, "Unsupported direction" ); + break; +#endif + } + } + return nDir; +} + +USHORT SwFont::GetOrientation( const BOOL bVertFormat ) const +{ + return UnMapDirection( aSub[nActual].GetOrientation(), bVertFormat ); +} + +void SwFont::SetVertical( USHORT nDir, const BOOL bVertFormat ) +{ + // map direction if frame has vertical layout + nDir = MapDirection( nDir, bVertFormat ); + + if( nDir != aSub[0].GetOrientation() ) + { + bFntChg = TRUE; + aSub[0].SetVertical( nDir, bVertFormat ); + aSub[1].SetVertical( nDir, bVertFormat || nDir > 1000 ); + aSub[2].SetVertical( nDir, bVertFormat ); + } +} + +/************************************************************************* + Escapement: + frEsc: Fraction, Grad des Escapements + Esc = resultierendes Escapement + A1 = Original-Ascent (nOrgAscent) + A2 = verkleinerter Ascent (nEscAscent) + Ax = resultierender Ascent (GetAscent()) + H1 = Original-Hoehe (nOrgHeight) + H2 = verkleinerter Hoehe (nEscHeight) + Hx = resultierender Hoehe (GetHeight()) + Bx = resultierende Baseline fuer die Textausgabe (CalcPos()) + (Vorsicht: Y - A1!) + + Escapement: + Esc = H1 * frEsc; + + Hochstellung: + Ax = A2 + Esc; + Hx = H2 + Esc; + Bx = A1 - Esc; + + Tiefstellung: + Ax = A1; + Hx = A1 + Esc + (H2 - A2); + Bx = A1 + Esc; + +*************************************************************************/ + +/************************************************************************* + * SwSubFont::CalcEscAscent( const USHORT nOldAscent ) + *************************************************************************/ + +// nEsc ist der Prozentwert +USHORT SwSubFont::CalcEscAscent( const USHORT nOldAscent ) const +{ + if( DFLT_ESC_AUTO_SUPER != GetEscapement() && + DFLT_ESC_AUTO_SUB != GetEscapement() ) + { + const long nAscent = nOldAscent + + ( (long) nOrgHeight * GetEscapement() ) / 100L; + if ( nAscent>0 ) + return ( Max( USHORT (nAscent), nOrgAscent )); + } + return nOrgAscent; +} + +/************************************************************************* + * SwFont::SetDiffFnt() + *************************************************************************/ + +void SwFont::SetDiffFnt( const SfxItemSet *pAttrSet, + const IDocumentSettingAccess *pIDocumentSettingAccess ) +{ + delete pBackColor; + pBackColor = NULL; + + if( pAttrSet ) + { + const SfxPoolItem* pItem; + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_FONT, + TRUE, &pItem )) + { + const SvxFontItem *pFont = (const SvxFontItem *)pItem; + aSub[SW_LATIN].SetFamily( pFont->GetFamily() ); + aSub[SW_LATIN].Font::SetName( pFont->GetFamilyName() ); + aSub[SW_LATIN].Font::SetStyleName( pFont->GetStyleName() ); + aSub[SW_LATIN].Font::SetPitch( pFont->GetPitch() ); + aSub[SW_LATIN].Font::SetCharSet( pFont->GetCharSet() ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_FONTSIZE, + TRUE, &pItem )) + { + const SvxFontHeightItem *pHeight = (const SvxFontHeightItem *)pItem; + aSub[SW_LATIN].SvxFont::SetPropr( 100 ); + aSub[SW_LATIN].aSize = aSub[SW_LATIN].Font::GetSize(); + Size aTmpSize = aSub[SW_LATIN].aSize; + aTmpSize.Height() = pHeight->GetHeight(); + aSub[SW_LATIN].SetSize( aTmpSize ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_POSTURE, + TRUE, &pItem )) + aSub[SW_LATIN].Font::SetItalic( ((SvxPostureItem*)pItem)->GetPosture() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_WEIGHT, + TRUE, &pItem )) + aSub[SW_LATIN].Font::SetWeight( ((SvxWeightItem*)pItem)->GetWeight() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_LANGUAGE, + TRUE, &pItem )) + aSub[SW_LATIN].SetLanguage( ((SvxLanguageItem*)pItem)->GetLanguage() ); + + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CJK_FONT, + TRUE, &pItem )) + { + const SvxFontItem *pFont = (const SvxFontItem *)pItem; + aSub[SW_CJK].SetFamily( pFont->GetFamily() ); + aSub[SW_CJK].Font::SetName( pFont->GetFamilyName() ); + aSub[SW_CJK].Font::SetStyleName( pFont->GetStyleName() ); + aSub[SW_CJK].Font::SetPitch( pFont->GetPitch() ); + aSub[SW_CJK].Font::SetCharSet( pFont->GetCharSet() ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CJK_FONTSIZE, + TRUE, &pItem )) + { + const SvxFontHeightItem *pHeight = (const SvxFontHeightItem *)pItem; + aSub[SW_CJK].SvxFont::SetPropr( 100 ); + aSub[SW_CJK].aSize = aSub[SW_CJK].Font::GetSize(); + Size aTmpSize = aSub[SW_CJK].aSize; + aTmpSize.Height() = pHeight->GetHeight(); + aSub[SW_CJK].SetSize( aTmpSize ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CJK_POSTURE, + TRUE, &pItem )) + aSub[SW_CJK].Font::SetItalic( ((SvxPostureItem*)pItem)->GetPosture() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CJK_WEIGHT, + TRUE, &pItem )) + aSub[SW_CJK].Font::SetWeight( ((SvxWeightItem*)pItem)->GetWeight() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CJK_LANGUAGE, + TRUE, &pItem )) + { + LanguageType eNewLang = ((SvxLanguageItem*)pItem)->GetLanguage(); + aSub[SW_CJK].SetLanguage( eNewLang ); + aSub[SW_LATIN].SetCJKContextLanguage( eNewLang ); + aSub[SW_CJK].SetCJKContextLanguage( eNewLang ); + aSub[SW_CTL].SetCJKContextLanguage( eNewLang ); + } + + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CTL_FONT, + TRUE, &pItem )) + { + const SvxFontItem *pFont = (const SvxFontItem *)pItem; + aSub[SW_CTL].SetFamily( pFont->GetFamily() ); + aSub[SW_CTL].Font::SetName( pFont->GetFamilyName() ); + aSub[SW_CTL].Font::SetStyleName( pFont->GetStyleName() ); + aSub[SW_CTL].Font::SetPitch( pFont->GetPitch() ); + aSub[SW_CTL].Font::SetCharSet( pFont->GetCharSet() ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CTL_FONTSIZE, + TRUE, &pItem )) + { + const SvxFontHeightItem *pHeight = (const SvxFontHeightItem *)pItem; + aSub[SW_CTL].SvxFont::SetPropr( 100 ); + aSub[SW_CTL].aSize = aSub[SW_CTL].Font::GetSize(); + Size aTmpSize = aSub[SW_CTL].aSize; + aTmpSize.Height() = pHeight->GetHeight(); + aSub[SW_CTL].SetSize( aTmpSize ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CTL_POSTURE, + TRUE, &pItem )) + aSub[SW_CTL].Font::SetItalic( ((SvxPostureItem*)pItem)->GetPosture() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CTL_WEIGHT, + TRUE, &pItem )) + aSub[SW_CTL].Font::SetWeight( ((SvxWeightItem*)pItem)->GetWeight() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CTL_LANGUAGE, + TRUE, &pItem )) + aSub[SW_CTL].SetLanguage( ((SvxLanguageItem*)pItem)->GetLanguage() ); + + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_UNDERLINE, + TRUE, &pItem )) + { + SetUnderline( ((SvxUnderlineItem*)pItem)->GetLineStyle() ); + SetUnderColor( ((SvxUnderlineItem*)pItem)->GetColor() ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_OVERLINE, + TRUE, &pItem )) + { + SetOverline( ((SvxOverlineItem*)pItem)->GetLineStyle() ); + SetOverColor( ((SvxOverlineItem*)pItem)->GetColor() ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CROSSEDOUT, + TRUE, &pItem )) + SetStrikeout( ((SvxCrossedOutItem*)pItem)->GetStrikeout() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_COLOR, + TRUE, &pItem )) + SetColor( ((SvxColorItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_EMPHASIS_MARK, + TRUE, &pItem )) + SetEmphasisMark( ((SvxEmphasisMarkItem*)pItem)->GetEmphasisMark() ); + + SetTransparent( TRUE ); + SetAlign( ALIGN_BASELINE ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CONTOUR, + TRUE, &pItem )) + SetOutline( ((SvxContourItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_SHADOWED, + TRUE, &pItem )) + SetShadow( ((SvxShadowedItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_RELIEF, + TRUE, &pItem )) + SetRelief( (FontRelief)((SvxCharReliefItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_SHADOWED, + TRUE, &pItem )) + SetPropWidth(((SvxShadowedItem*)pItem)->GetValue() ? 50 : 100 ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_AUTOKERN, + TRUE, &pItem )) + { + if( ((SvxAutoKernItem*)pItem)->GetValue() ) + { + SetAutoKern( ( !pIDocumentSettingAccess || + !pIDocumentSettingAccess->get(IDocumentSettingAccess::KERN_ASIAN_PUNCTUATION) ) ? + KERNING_FONTSPECIFIC : + KERNING_ASIAN ); + } + else + SetAutoKern( 0 ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_WORDLINEMODE, + TRUE, &pItem )) + SetWordLineMode( ((SvxWordLineModeItem*)pItem)->GetValue() ); + + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_ESCAPEMENT, + TRUE, &pItem )) + { + const SvxEscapementItem *pEsc = (const SvxEscapementItem *)pItem; + SetEscapement( pEsc->GetEsc() ); + if( aSub[SW_LATIN].IsEsc() ) + SetProportion( pEsc->GetProp() ); + } + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_CASEMAP, + TRUE, &pItem )) + SetCaseMap( ((SvxCaseMapItem*)pItem)->GetCaseMap() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_KERNING, + TRUE, &pItem )) + SetFixKerning( ((SvxKerningItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_NOHYPHEN, + TRUE, &pItem )) + SetNoHyph( ((SvxNoHyphenItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_BLINK, + TRUE, &pItem )) + SetBlink( ((SvxBlinkItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_ROTATE, + TRUE, &pItem )) + SetVertical( ((SvxCharRotateItem*)pItem)->GetValue() ); + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_BACKGROUND, + TRUE, &pItem )) + pBackColor = new Color( ((SvxBrushItem*)pItem)->GetColor() ); + else + pBackColor = NULL; + const SfxPoolItem* pTwoLinesItem = 0; + if( SFX_ITEM_SET == + pAttrSet->GetItemState( RES_CHRATR_TWO_LINES, TRUE, &pTwoLinesItem )) + if ( ((SvxTwoLinesItem*)pTwoLinesItem)->GetValue() ) + SetVertical( 0 ); + } + else + { + Invalidate(); + bNoHyph = FALSE; + bBlink = FALSE; + } + bPaintBlank = FALSE; + bPaintWrong = FALSE; + ASSERT( aSub[SW_LATIN].IsTransparent(), "SwFont: Transparent revolution" ); +} + +/************************************************************************* + * class SwFont + *************************************************************************/ + +SwFont::SwFont( const SwFont &rFont ) +{ + aSub[SW_LATIN] = rFont.aSub[SW_LATIN]; + aSub[SW_CJK] = rFont.aSub[SW_CJK]; + aSub[SW_CTL] = rFont.aSub[SW_CTL]; + nActual = rFont.nActual; + pBackColor = rFont.pBackColor ? new Color( *rFont.pBackColor ) : NULL; + aUnderColor = rFont.GetUnderColor(); + aOverColor = rFont.GetOverColor(); + nToxCnt = 0; + nRefCnt = 0; + m_nMetaCount = 0; + bFntChg = rFont.bFntChg; + bOrgChg = rFont.bOrgChg; + bPaintBlank = rFont.bPaintBlank; + bPaintWrong = FALSE; + bURL = rFont.bURL; + bGreyWave = rFont.bGreyWave; + bNoColReplace = rFont.bNoColReplace; + bNoHyph = rFont.bNoHyph; + bBlink = rFont.bBlink; +} + +SwFont::SwFont( const SwAttrSet* pAttrSet, + const IDocumentSettingAccess* pIDocumentSettingAccess ) +{ + nActual = SW_LATIN; + nToxCnt = 0; + nRefCnt = 0; + m_nMetaCount = 0; + bPaintBlank = FALSE; + bPaintWrong = FALSE; + bURL = FALSE; + bGreyWave = FALSE; + bNoColReplace = FALSE; + bNoHyph = pAttrSet->GetNoHyphenHere().GetValue(); + bBlink = pAttrSet->GetBlink().GetValue(); + bOrgChg = TRUE; + { + const SvxFontItem& rFont = pAttrSet->GetFont(); + aSub[SW_LATIN].SetFamily( rFont.GetFamily() ); + aSub[SW_LATIN].SetName( rFont.GetFamilyName() ); + aSub[SW_LATIN].SetStyleName( rFont.GetStyleName() ); + aSub[SW_LATIN].SetPitch( rFont.GetPitch() ); + aSub[SW_LATIN].SetCharSet( rFont.GetCharSet() ); + aSub[SW_LATIN].SvxFont::SetPropr( 100 ); // 100% der FontSize + Size aTmpSize = aSub[SW_LATIN].aSize; + aTmpSize.Height() = pAttrSet->GetSize().GetHeight(); + aSub[SW_LATIN].SetSize( aTmpSize ); + aSub[SW_LATIN].SetItalic( pAttrSet->GetPosture().GetPosture() ); + aSub[SW_LATIN].SetWeight( pAttrSet->GetWeight().GetWeight() ); + aSub[SW_LATIN].SetLanguage( pAttrSet->GetLanguage().GetLanguage() ); + } + + { + const SvxFontItem& rFont = pAttrSet->GetCJKFont(); + aSub[SW_CJK].SetFamily( rFont.GetFamily() ); + aSub[SW_CJK].SetName( rFont.GetFamilyName() ); + aSub[SW_CJK].SetStyleName( rFont.GetStyleName() ); + aSub[SW_CJK].SetPitch( rFont.GetPitch() ); + aSub[SW_CJK].SetCharSet( rFont.GetCharSet() ); + aSub[SW_CJK].SvxFont::SetPropr( 100 ); // 100% der FontSize + Size aTmpSize = aSub[SW_CJK].aSize; + aTmpSize.Height() = pAttrSet->GetCJKSize().GetHeight(); + aSub[SW_CJK].SetSize( aTmpSize ); + aSub[SW_CJK].SetItalic( pAttrSet->GetCJKPosture().GetPosture() ); + aSub[SW_CJK].SetWeight( pAttrSet->GetCJKWeight().GetWeight() ); + LanguageType eNewLang = pAttrSet->GetCJKLanguage().GetLanguage(); + aSub[SW_CJK].SetLanguage( eNewLang ); + aSub[SW_LATIN].SetCJKContextLanguage( eNewLang ); + aSub[SW_CJK].SetCJKContextLanguage( eNewLang ); + aSub[SW_CTL].SetCJKContextLanguage( eNewLang ); + } + + { + const SvxFontItem& rFont = pAttrSet->GetCTLFont(); + aSub[SW_CTL].SetFamily( rFont.GetFamily() ); + aSub[SW_CTL].SetName( rFont.GetFamilyName() ); + aSub[SW_CTL].SetStyleName( rFont.GetStyleName() ); + aSub[SW_CTL].SetPitch( rFont.GetPitch() ); + aSub[SW_CTL].SetCharSet( rFont.GetCharSet() ); + aSub[SW_CTL].SvxFont::SetPropr( 100 ); // 100% der FontSize + Size aTmpSize = aSub[SW_CTL].aSize; + aTmpSize.Height() = pAttrSet->GetCTLSize().GetHeight(); + aSub[SW_CTL].SetSize( aTmpSize ); + aSub[SW_CTL].SetItalic( pAttrSet->GetCTLPosture().GetPosture() ); + aSub[SW_CTL].SetWeight( pAttrSet->GetCTLWeight().GetWeight() ); + aSub[SW_CTL].SetLanguage( pAttrSet->GetCTLLanguage().GetLanguage() ); + } + + const FontUnderline eUnderline = pAttrSet->GetUnderline().GetLineStyle(); + if ( pAttrSet->GetCharHidden().GetValue() ) + SetUnderline( UNDERLINE_DOTTED ); + else + SetUnderline( eUnderline ); + SetUnderColor( pAttrSet->GetUnderline().GetColor() ); + SetOverline( pAttrSet->GetOverline().GetLineStyle() ); + SetOverColor( pAttrSet->GetOverline().GetColor() ); + SetEmphasisMark( pAttrSet->GetEmphasisMark().GetEmphasisMark() ); + SetStrikeout( pAttrSet->GetCrossedOut().GetStrikeout() ); + SetColor( pAttrSet->GetColor().GetValue() ); + SetTransparent( TRUE ); + SetAlign( ALIGN_BASELINE ); + SetOutline( pAttrSet->GetContour().GetValue() ); + SetShadow( pAttrSet->GetShadowed().GetValue() ); + SetPropWidth( pAttrSet->GetCharScaleW().GetValue() ); + SetRelief( (FontRelief)pAttrSet->GetCharRelief().GetValue() ); + if( pAttrSet->GetAutoKern().GetValue() ) + { + SetAutoKern( ( !pIDocumentSettingAccess || + !pIDocumentSettingAccess->get(IDocumentSettingAccess::KERN_ASIAN_PUNCTUATION) ) ? + KERNING_FONTSPECIFIC : + KERNING_ASIAN ); + } + else + SetAutoKern( 0 ); + SetWordLineMode( pAttrSet->GetWordLineMode().GetValue() ); + const SvxEscapementItem &rEsc = pAttrSet->GetEscapement(); + SetEscapement( rEsc.GetEsc() ); + if( aSub[SW_LATIN].IsEsc() ) + SetProportion( rEsc.GetProp() ); + SetCaseMap( pAttrSet->GetCaseMap().GetCaseMap() ); + SetFixKerning( pAttrSet->GetKerning().GetValue() ); + const SfxPoolItem* pItem; + if( SFX_ITEM_SET == pAttrSet->GetItemState( RES_CHRATR_BACKGROUND, + TRUE, &pItem )) + pBackColor = new Color( ((SvxBrushItem*)pItem)->GetColor() ); + else + pBackColor = NULL; + const SvxTwoLinesItem& rTwoLinesItem = pAttrSet->Get2Lines(); + if ( ! rTwoLinesItem.GetValue() ) + SetVertical( pAttrSet->GetCharRotate().GetValue() ); + else + SetVertical( 0 ); +} + +SwSubFont& SwSubFont::operator=( const SwSubFont &rFont ) +{ + SvxFont::operator=( rFont ); + pMagic = rFont.pMagic; + nFntIndex = rFont.nFntIndex; + nOrgHeight = rFont.nOrgHeight; + nOrgAscent = rFont.nOrgAscent; + nPropWidth = rFont.nPropWidth; + aSize = rFont.aSize; + return *this; +} + +SwFont& SwFont::operator=( const SwFont &rFont ) +{ + aSub[SW_LATIN] = rFont.aSub[SW_LATIN]; + aSub[SW_CJK] = rFont.aSub[SW_CJK]; + aSub[SW_CTL] = rFont.aSub[SW_CTL]; + nActual = rFont.nActual; + delete pBackColor; + pBackColor = rFont.pBackColor ? new Color( *rFont.pBackColor ) : NULL; + aUnderColor = rFont.GetUnderColor(); + aOverColor = rFont.GetOverColor(); + nToxCnt = 0; + nRefCnt = 0; + m_nMetaCount = 0; + bFntChg = rFont.bFntChg; + bOrgChg = rFont.bOrgChg; + bPaintBlank = rFont.bPaintBlank; + bPaintWrong = FALSE; + bURL = rFont.bURL; + bGreyWave = rFont.bGreyWave; + bNoColReplace = rFont.bNoColReplace; + bNoHyph = rFont.bNoHyph; + bBlink = rFont.bBlink; + return *this; +} + +/************************************************************************* + * SwFont::GoMagic() + *************************************************************************/ + +void SwFont::GoMagic( ViewShell *pSh, BYTE nWhich ) +{ + SwFntAccess aFntAccess( aSub[nWhich].pMagic, aSub[nWhich].nFntIndex, + &aSub[nWhich], pSh, TRUE ); +} + +/************************************************************************* + * SwSubFont::IsSymbol() + *************************************************************************/ + +BOOL SwSubFont::IsSymbol( ViewShell *pSh ) +{ + SwFntAccess aFntAccess( pMagic, nFntIndex, this, pSh, FALSE ); + return aFntAccess.Get()->IsSymbol(); +} + +/************************************************************************* + * SwSubFont::ChgFnt() + *************************************************************************/ + +BOOL SwSubFont::ChgFnt( ViewShell *pSh, OutputDevice& rOut ) +{ + if ( pLastFont ) + pLastFont->Unlock(); + SwFntAccess aFntAccess( pMagic, nFntIndex, this, pSh, TRUE ); + SV_STAT( nChangeFont ); + + pLastFont = aFntAccess.Get(); + + pLastFont->SetDevFont( pSh, rOut ); + + pLastFont->Lock(); + return UNDERLINE_NONE != GetUnderline() || + UNDERLINE_NONE != GetOverline() || + STRIKEOUT_NONE != GetStrikeout(); +} + +/************************************************************************* + * SwFont::ChgPhysFnt() + *************************************************************************/ + +void SwFont::ChgPhysFnt( ViewShell *pSh, OutputDevice& rOut ) +{ + if( bOrgChg && aSub[nActual].IsEsc() ) + { + const BYTE nOldProp = aSub[nActual].GetPropr(); + SetProportion( 100 ); + ChgFnt( pSh, rOut ); + SwFntAccess aFntAccess( aSub[nActual].pMagic, aSub[nActual].nFntIndex, + &aSub[nActual], pSh ); + aSub[nActual].nOrgHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut ); + aSub[nActual].nOrgAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut ); + SetProportion( nOldProp ); + bOrgChg = FALSE; + } + + if( bFntChg ) + { + ChgFnt( pSh, rOut ); + bFntChg = bOrgChg; + } + if( rOut.GetTextLineColor() != aUnderColor ) + rOut.SetTextLineColor( aUnderColor ); + if( rOut.GetOverlineColor() != aOverColor ) + rOut.SetOverlineColor( aOverColor ); +} + +/************************************************************************* + * SwFont::CalcEscHeight() + * Height = MaxAscent + MaxDescent + * MaxAscent = Max (T1_ascent, T2_ascent + (Esc * T1_height) ); + * MaxDescent = Max (T1_height-T1_ascent, + * T2_height-T2_ascent - (Esc * T1_height) + *************************************************************************/ + +USHORT SwSubFont::CalcEscHeight( const USHORT nOldHeight, + const USHORT nOldAscent ) const +{ + if( DFLT_ESC_AUTO_SUPER != GetEscapement() && + DFLT_ESC_AUTO_SUB != GetEscapement() ) + { + long nDescent = nOldHeight - nOldAscent - + ( (long) nOrgHeight * GetEscapement() ) / 100L; + const USHORT nDesc = ( nDescent>0 ) ? Max ( USHORT(nDescent), + USHORT(nOrgHeight - nOrgAscent) ) : nOrgHeight - nOrgAscent; + return ( nDesc + CalcEscAscent( nOldAscent ) ); + } + return nOrgHeight; +} + +short SwSubFont::_CheckKerning( ) +{ + short nKernx = - short( Font::GetSize().Height() / 6 ); + + if ( nKernx < GetFixKerning() ) + return GetFixKerning(); + return nKernx; +} + +/************************************************************************* + * SwSubFont::GetAscent() + *************************************************************************/ + +USHORT SwSubFont::GetAscent( ViewShell *pSh, const OutputDevice& rOut ) +{ + USHORT nAscent; + SwFntAccess aFntAccess( pMagic, nFntIndex, this, pSh ); + nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut ); + if( GetEscapement() ) + nAscent = CalcEscAscent( nAscent ); + return nAscent; +} + +/************************************************************************* + * SwSubFont::GetHeight() + *************************************************************************/ + +USHORT SwSubFont::GetHeight( ViewShell *pSh, const OutputDevice& rOut ) +{ + SV_STAT( nGetTextSize ); + SwFntAccess aFntAccess( pMagic, nFntIndex, this, pSh ); + const USHORT nHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut ); + if ( GetEscapement() ) + { + const USHORT nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut ); + return CalcEscHeight( nHeight, nAscent ); // + nLeading; + } + return nHeight; // + nLeading; +} + +/************************************************************************* + * SwSubFont::_GetTxtSize() + *************************************************************************/ +Size SwSubFont::_GetTxtSize( SwDrawTextInfo& rInf ) +{ + // Robust: Eigentlich sollte der Font bereits eingestellt sein, aber + // sicher ist sicher ... + if ( !pLastFont || pLastFont->GetOwner()!=pMagic || + !IsSameInstance( rInf.GetpOut()->GetFont() ) ) + ChgFnt( rInf.GetShell(), rInf.GetOut() ); + + SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() ); + + Size aTxtSize; + xub_StrLen nLn = ( rInf.GetLen() == STRING_LEN ? rInf.GetText().Len() + : rInf.GetLen() ); + rInf.SetLen( nLn ); + if( IsCapital() && nLn ) + aTxtSize = GetCapitalSize( rInf ); + else + { + SV_STAT( nGetTextSize ); + long nOldKern = rInf.GetKern(); + const XubString &rOldTxt = rInf.GetText(); + rInf.SetKern( CheckKerning() ); + if ( !IsCaseMap() ) + aTxtSize = pLastFont->GetTextSize( rInf ); + else + { + String aTmp = CalcCaseMap( rInf.GetText() ); + const XubString &rOldStr = rInf.GetText(); + sal_Bool bCaseMapLengthDiffers(aTmp.Len() != rOldStr.Len()); + + if(bCaseMapLengthDiffers && rInf.GetLen()) + { + // #108203# + // If the length of the original string and the CaseMapped one + // are different, it is necessary to handle the given text part as + // a single snippet since it�s size may differ, too. + xub_StrLen nOldIdx(rInf.GetIdx()); + xub_StrLen nOldLen(rInf.GetLen()); + const XubString aSnippet(rOldStr, nOldIdx, nOldLen); + XubString aNewText(CalcCaseMap(aSnippet)); + + rInf.SetText( aNewText ); + rInf.SetIdx( 0 ); + rInf.SetLen( aNewText.Len() ); + + aTxtSize = pLastFont->GetTextSize( rInf ); + + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + else + { + rInf.SetText( aTmp ); + aTxtSize = pLastFont->GetTextSize( rInf ); + } + + rInf.SetText( rOldStr ); + } + rInf.SetKern( nOldKern ); + rInf.SetText( rOldTxt ); + // 15142: Ein Wort laenger als eine Zeile, beim Zeilenumbruch + // hochgestellt, muss seine effektive Hoehe melden. + if( GetEscapement() ) + { + const USHORT nAscent = pLastFont->GetFontAscent( rInf.GetShell(), + rInf.GetOut() ); + aTxtSize.Height() = + (long)CalcEscHeight( (USHORT)aTxtSize.Height(), nAscent); + } + } + + if (1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetText().GetChar(rInf.GetIdx())) + { + xub_StrLen nOldIdx(rInf.GetIdx()); + xub_StrLen nOldLen(rInf.GetLen()); + String aNewText=String::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART); + rInf.SetText( aNewText ); + rInf.SetIdx( 0 ); + rInf.SetLen( aNewText.Len() ); + aTxtSize = pLastFont->GetTextSize( rInf ); + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + else if (1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetText().GetChar(rInf.GetIdx())) + { + xub_StrLen nOldIdx(rInf.GetIdx()); + xub_StrLen nOldLen(rInf.GetLen()); + String aNewText=String::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND); + rInf.SetText( aNewText ); + rInf.SetIdx( 0 ); + rInf.SetLen( aNewText.Len() ); + aTxtSize = pLastFont->GetTextSize( rInf ); + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + + return aTxtSize; +} + +/************************************************************************* + * SwSubFont::_DrawText() + *************************************************************************/ + +void SwSubFont::_DrawText( SwDrawTextInfo &rInf, const BOOL bGrey ) +{ + rInf.SetGreyWave( bGrey ); + xub_StrLen nLn = rInf.GetText().Len(); + if( !rInf.GetLen() || !nLn ) + return; + if( STRING_LEN == rInf.GetLen() ) + rInf.SetLen( nLn ); + + FontUnderline nOldUnder = UNDERLINE_NONE; + SwUnderlineFont* pUnderFnt = 0; + + if( rInf.GetUnderFnt() ) + { + nOldUnder = GetUnderline(); + SetUnderline( UNDERLINE_NONE ); + pUnderFnt = rInf.GetUnderFnt(); + } + + if( !pLastFont || pLastFont->GetOwner()!=pMagic ) + ChgFnt( rInf.GetShell(), rInf.GetOut() ); + + SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() ); + + Point aPos( rInf.GetPos() ); + const Point &rOld = rInf.GetPos(); + rInf.SetPos( aPos ); + + if( GetEscapement() ) + CalcEsc( rInf, aPos ); + + rInf.SetKern( CheckKerning() + rInf.GetSperren() / SPACING_PRECISION_FACTOR ); + + if( IsCapital() ) + DrawCapital( rInf ); + else + { + SV_STAT( nDrawText ); + if ( !IsCaseMap() ) + pLastFont->DrawText( rInf ); + else + { + const XubString &rOldStr = rInf.GetText(); + XubString aString( CalcCaseMap( rOldStr ) ); + sal_Bool bCaseMapLengthDiffers(aString.Len() != rOldStr.Len()); + + if(bCaseMapLengthDiffers && rInf.GetLen()) + { + // #108203# + // If the length of the original string and the CaseMapped one + // are different, it is necessary to handle the given text part as + // a single snippet since it�s size may differ, too. + xub_StrLen nOldIdx(rInf.GetIdx()); + xub_StrLen nOldLen(rInf.GetLen()); + const XubString aSnippet(rOldStr, nOldIdx, nOldLen); + XubString aNewText = CalcCaseMap(aSnippet); + + rInf.SetText( aNewText ); + rInf.SetIdx( 0 ); + rInf.SetLen( aNewText.Len() ); + + pLastFont->DrawText( rInf ); + + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + else + { + rInf.SetText( aString ); + pLastFont->DrawText( rInf ); + } + + rInf.SetText( rOldStr ); + } + } + + if( pUnderFnt && nOldUnder != UNDERLINE_NONE ) + { +static sal_Char __READONLY_DATA sDoubleSpace[] = " "; + Size aFontSize = _GetTxtSize( rInf ); + const XubString &rOldStr = rInf.GetText(); + XubString aStr( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ); + + xub_StrLen nOldIdx = rInf.GetIdx(); + xub_StrLen nOldLen = rInf.GetLen(); + long nSpace = 0; + if( rInf.GetSpace() ) + { + xub_StrLen nTmpEnd = nOldIdx + nOldLen; + if( nTmpEnd > rOldStr.Len() ) + nTmpEnd = rOldStr.Len(); + + const SwScriptInfo* pSI = rInf.GetScriptInfo(); + + const sal_Bool bAsianFont = + ( rInf.GetFont() && SW_CJK == rInf.GetFont()->GetActual() ); + for( xub_StrLen nTmp = nOldIdx; nTmp < nTmpEnd; ++nTmp ) + { + if( CH_BLANK == rOldStr.GetChar( nTmp ) || bAsianFont || + ( nTmp + 1 < rOldStr.Len() && pSI && + i18n::ScriptType::ASIAN == pSI->ScriptType( nTmp + 1 ) ) ) + ++nSpace; + } + + // if next portion if a hole portion we do not consider any + // extra space added because the last character was ASIAN + if ( nSpace && rInf.IsSpaceStop() && bAsianFont ) + --nSpace; + + nSpace *= rInf.GetSpace() / SPACING_PRECISION_FACTOR; + } + + rInf.SetWidth( USHORT(aFontSize.Width() + nSpace) ); + rInf.SetText( aStr ); + rInf.SetIdx( 0 ); + rInf.SetLen( 2 ); + SetUnderline( nOldUnder ); + rInf.SetUnderFnt( 0 ); + + // set position for underline font + rInf.SetPos( pUnderFnt->GetPos() ); + + pUnderFnt->GetFont()._DrawStretchText( rInf ); + + rInf.SetUnderFnt( pUnderFnt ); + rInf.SetText( rOldStr ); + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + + rInf.SetPos( rOld ); +} + +void SwSubFont::_DrawStretchText( SwDrawTextInfo &rInf ) +{ + if( !rInf.GetLen() || !rInf.GetText().Len() ) + return; + + FontUnderline nOldUnder = UNDERLINE_NONE; + SwUnderlineFont* pUnderFnt = 0; + + if( rInf.GetUnderFnt() ) + { + nOldUnder = GetUnderline(); + SetUnderline( UNDERLINE_NONE ); + pUnderFnt = rInf.GetUnderFnt(); + } + + if ( !pLastFont || pLastFont->GetOwner() != pMagic ) + ChgFnt( rInf.GetShell(), rInf.GetOut() ); + + SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() ); + + rInf.ApplyAutoColor(); + + Point aPos( rInf.GetPos() ); + + if( GetEscapement() ) + CalcEsc( rInf, aPos ); + + rInf.SetKern( CheckKerning() + rInf.GetSperren() / SPACING_PRECISION_FACTOR ); + const Point &rOld = rInf.GetPos(); + rInf.SetPos( aPos ); + + if( IsCapital() ) + DrawStretchCapital( rInf ); + else + { + SV_STAT( nDrawStretchText ); + + if ( rInf.GetFrm() ) + { + if ( rInf.GetFrm()->IsRightToLeft() ) + rInf.GetFrm()->SwitchLTRtoRTL( aPos ); + + if ( rInf.GetFrm()->IsVertical() ) + rInf.GetFrm()->SwitchHorizontalToVertical( aPos ); + } + + if ( !IsCaseMap() ) + rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(), + rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ); + else + rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(), CalcCaseMap( + rInf.GetText() ), rInf.GetIdx(), rInf.GetLen() ); + } + + if( pUnderFnt && nOldUnder != UNDERLINE_NONE ) + { +static sal_Char __READONLY_DATA sDoubleSpace[] = " "; + const XubString &rOldStr = rInf.GetText(); + XubString aStr( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ); + xub_StrLen nOldIdx = rInf.GetIdx(); + xub_StrLen nOldLen = rInf.GetLen(); + rInf.SetText( aStr ); + rInf.SetIdx( 0 ); + rInf.SetLen( 2 ); + SetUnderline( nOldUnder ); + rInf.SetUnderFnt( 0 ); + + // set position for underline font + rInf.SetPos( pUnderFnt->GetPos() ); + + pUnderFnt->GetFont()._DrawStretchText( rInf ); + + rInf.SetUnderFnt( pUnderFnt ); + rInf.SetText( rOldStr ); + rInf.SetIdx( nOldIdx ); + rInf.SetLen( nOldLen ); + } + + rInf.SetPos( rOld ); +} + +/************************************************************************* + * SwSubFont::_GetCrsrOfst() + *************************************************************************/ + +xub_StrLen SwSubFont::_GetCrsrOfst( SwDrawTextInfo& rInf ) +{ + if ( !pLastFont || pLastFont->GetOwner()!=pMagic ) + ChgFnt( rInf.GetShell(), rInf.GetOut() ); + + SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() ); + + xub_StrLen nLn = rInf.GetLen() == STRING_LEN ? rInf.GetText().Len() + : rInf.GetLen(); + rInf.SetLen( nLn ); + xub_StrLen nCrsr = 0; + if( IsCapital() && nLn ) + nCrsr = GetCapitalCrsrOfst( rInf ); + else + { + const XubString &rOldTxt = rInf.GetText(); + long nOldKern = rInf.GetKern(); + rInf.SetKern( CheckKerning() ); + SV_STAT( nGetTextSize ); + if ( !IsCaseMap() ) + nCrsr = pLastFont->GetCrsrOfst( rInf ); + else + { + String aTmp = CalcCaseMap( rInf.GetText() ); + rInf.SetText( aTmp ); + nCrsr = pLastFont->GetCrsrOfst( rInf ); + } + rInf.SetKern( nOldKern ); + rInf.SetText( rOldTxt ); + } + return nCrsr; +} + +/************************************************************************* + * SwSubFont::CalcEsc() + *************************************************************************/ + +void SwSubFont::CalcEsc( SwDrawTextInfo& rInf, Point& rPos ) +{ + long nOfst; + + USHORT nDir = UnMapDirection( + GetOrientation(), rInf.GetFrm() && rInf.GetFrm()->IsVertical() ); + + switch ( GetEscapement() ) + { + case DFLT_ESC_AUTO_SUB : + nOfst = nOrgHeight - nOrgAscent - + pLastFont->GetFontHeight( rInf.GetShell(), rInf.GetOut() ) + + pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() ); + + switch ( nDir ) + { + case 0 : + rPos.Y() += nOfst; + break; + case 900 : + rPos.X() += nOfst; + break; + case 2700 : + rPos.X() -= nOfst; + break; + } + + break; + case DFLT_ESC_AUTO_SUPER : + nOfst = pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() ) - + nOrgAscent; + + + switch ( nDir ) + { + case 0 : + rPos.Y() += nOfst; + break; + case 900 : + rPos.X() += nOfst; + break; + case 2700 : + rPos.X() -= nOfst; + break; + } + + break; + default : + nOfst = ((long)nOrgHeight * GetEscapement()) / 100L; + + switch ( nDir ) + { + case 0 : + rPos.Y() -= nOfst; + break; + case 900 : + rPos.X() -= nOfst; + break; + case 2700 : + rPos.X() += nOfst; + break; + } + } +} + +// used during painting of small capitals +void SwDrawTextInfo::Shift( USHORT nDir ) +{ + ASSERT( bPos, "DrawTextInfo: Undefined Position" ); + ASSERT( bSize, "DrawTextInfo: Undefined Width" ); + + const BOOL bBidiPor = ( GetFrm() && GetFrm()->IsRightToLeft() ) != + ( 0 != ( TEXT_LAYOUT_BIDI_RTL & GetpOut()->GetLayoutMode() ) ); + + nDir = bBidiPor ? + 1800 : + UnMapDirection( nDir, GetFrm() && GetFrm()->IsVertical() ); + + switch ( nDir ) + { + case 0 : + ((Point*)pPos)->X() += GetSize().Width(); + break; + case 900 : + ASSERT( ((Point*)pPos)->Y() >= GetSize().Width(), "Going underground" ); + ((Point*)pPos)->Y() -= GetSize().Width(); + break; + case 1800 : + ((Point*)pPos)->X() -= GetSize().Width(); + break; + case 2700 : + ((Point*)pPos)->Y() += GetSize().Width(); + break; + } +} + +/************************************************************************* + * SwUnderlineFont::~SwUnderlineFont + * + * Used for the "continuous underline" feature. + *************************************************************************/ + +SwUnderlineFont::SwUnderlineFont( SwFont& rFnt, const Point& rPoint ) + : aPos( rPoint ), pFnt( &rFnt ) +{ +}; + +SwUnderlineFont::~SwUnderlineFont() +{ + delete pFnt; +} + +//Helper for filters to find true lineheight of a font +long AttrSetToLineHeight( const IDocumentSettingAccess& rIDocumentSettingAccess, + const SwAttrSet &rSet, + const OutputDevice &rOut, sal_Int16 nScript) +{ + SwFont aFont(&rSet, &rIDocumentSettingAccess); + BYTE nActual; + switch (nScript) + { + default: + case i18n::ScriptType::LATIN: + nActual = SW_LATIN; + break; + case i18n::ScriptType::ASIAN: + nActual = SW_CJK; + break; + case i18n::ScriptType::COMPLEX: + nActual = SW_CTL; + break; + } + aFont.SetActual(nActual); + + OutputDevice &rMutableOut = const_cast<OutputDevice &>(rOut); + const Font aOldFont(rMutableOut.GetFont()); + + rMutableOut.SetFont(aFont.GetActualFont()); + long nHeight = rMutableOut.GetTextHeight(); + + rMutableOut.SetFont(aOldFont); + return nHeight; +} diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx new file mode 100644 index 000000000000..a2e326777383 --- /dev/null +++ b/sw/source/core/txtnode/thints.cxx @@ -0,0 +1,2898 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <hintids.hxx> +#include <sot/factory.hxx> +#include <editeng/xmlcnitm.hxx> +#include <svl/whiter.hxx> +#include <svl/itemiter.hxx> +#include <svl/stylepool.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/langitem.hxx> +#include <editeng/emphitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <editeng/charrotateitem.hxx> +// --> OD 2008-01-16 #newlistlevelattrs# +#include <editeng/lrspitem.hxx> +// <-- +#include <txtinet.hxx> +#include <txtflcnt.hxx> +#include <fmtfld.hxx> +#include <fmtanchr.hxx> +#include <fmtinfmt.hxx> +#include <txtatr.hxx> +#include <fchrfmt.hxx> +#include <fmtautofmt.hxx> +#include <fmtflcnt.hxx> +#include <fmtftn.hxx> +#include <txttxmrk.hxx> +#include <txtrfmrk.hxx> +#include <txtftn.hxx> +#include <txtfld.hxx> +#include <charatr.hxx> +#include <charfmt.hxx> +#include <frmfmt.hxx> +#include <ftnidx.hxx> +#include <fmtruby.hxx> +#include <fmtmeta.hxx> +#include <breakit.hxx> +#include <doc.hxx> +#include <errhdl.hxx> +#include <fldbas.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <txtfrm.hxx> +#include <rolbck.hxx> // fuer SwRegHistory +#include <ddefld.hxx> +#include <docufld.hxx> +#include <expfld.hxx> +#include <usrfld.hxx> +#include <poolfmt.hxx> +#include <swfont.hxx> +#include <istyleaccess.hxx> +// OD 26.06.2003 #108784# +#include <dcontact.hxx> +#include <docsh.hxx> +#include <svl/smplhint.hxx> +#include <algorithm> +#include <map> + +#ifdef DBG_UTIL +#define CHECK Check(); +#else +#define CHECK +#endif + +using namespace ::com::sun::star::i18n; + + +SwpHints::SwpHints() + : m_pHistory(0) + , m_bFontChange(true) + , m_bInSplitNode(false) + , m_bCalcHiddenParaField(false) + , m_bHasHiddenParaField(false) + , m_bFootnote(false) + , m_bDDEFields(false) +{ +} + +struct TxtAttrDeleter +{ + SwAttrPool & m_rPool; + TxtAttrDeleter( SwDoc & rDoc ) : m_rPool( rDoc.GetAttrPool() ) { } + void operator() (SwTxtAttr * const pAttr) + { + if (RES_TXTATR_META == pAttr->Which() || + RES_TXTATR_METAFIELD == pAttr->Which()) + { + static_cast<SwTxtMeta *>(pAttr)->ChgTxtNode(0); // prevents ASSERT + } + SwTxtAttr::Destroy( pAttr, m_rPool ); + } +}; + +struct TxtAttrContains +{ + xub_StrLen m_nPos; + TxtAttrContains( const xub_StrLen nPos ) : m_nPos( nPos ) { } + bool operator() (SwTxtAttrEnd * const pAttr) + { + return (*pAttr->GetStart() < m_nPos) && (m_nPos < *pAttr->GetEnd()); + } +}; + +// a: |-----| +// b: +// |---| => valid: b before a +// |-----| => valid: start == end; b before a +// |---------| => invalid: overlap (1) +// |-----------| => valid: same end; b around a +// |-----------------| => valid: b around a +// |---| => valid; same start; b within a +// |-----| => valid; same start and end; b around or within a? +// |-----------| => valid: same start: b around a +// |-| => valid: b within a +// |---| => valid: same end; b within a +// |---------| => invalid: overlap (2) +// |-----| => valid: end == start; b after a +// |---| => valid: b after a +// ===> 2 invalid overlap cases +static +bool isOverlap(const xub_StrLen nStart1, const xub_StrLen nEnd1, + const xub_StrLen nStart2, const xub_StrLen nEnd2) +{ + return + ((nStart1 > nStart2) && (nStart1 < nEnd2) && (nEnd1 > nEnd2)) // (1) + || ((nStart1 < nStart2) && (nStart2 < nEnd1) && (nEnd1 < nEnd2)); // (2) +} + +/// #i106930#: now asymmetric: empty hint1 is _not_ nested, but empty hint2 is +static +bool isNestedAny(const xub_StrLen nStart1, const xub_StrLen nEnd1, + const xub_StrLen nStart2, const xub_StrLen nEnd2) +{ + return ((nStart1 == nStart2) || (nEnd1 == nEnd2)) + // same start/end: nested except if hint1 empty and hint2 not empty + ? (nStart1 != nEnd1) || (nStart2 == nEnd2) + : ((nStart1 < nStart2) ? (nEnd1 >= nEnd2) : (nEnd1 <= nEnd2)); +} + +static +bool isSelfNestable(const USHORT nWhich) +{ + if ((RES_TXTATR_INETFMT == nWhich) || + (RES_TXTATR_CJK_RUBY == nWhich)) + return false; + ASSERT((RES_TXTATR_META == nWhich) || + (RES_TXTATR_METAFIELD == nWhich), "???"); + return true; +} + +static +bool isSplittable(const USHORT nWhich) +{ + if ((RES_TXTATR_INETFMT == nWhich) || + (RES_TXTATR_CJK_RUBY == nWhich)) + return true; + ASSERT((RES_TXTATR_META == nWhich) || + (RES_TXTATR_METAFIELD == nWhich), "???"); + return false; +} + +enum Split_t { FAIL, SPLIT_NEW, SPLIT_OTHER }; +/** + Calculate splitting policy for overlapping hints, based on what kind of + hint is inserted, and what kind of existing hint overlaps. + */ +static Split_t +splitPolicy(const USHORT nWhichNew, const USHORT nWhichOther) +{ + if (!isSplittable(nWhichOther)) + { + if (!isSplittable(nWhichNew)) + return FAIL; + else + return SPLIT_NEW; + } + else + { + if ((RES_TXTATR_INETFMT == nWhichNew) && + (RES_TXTATR_CJK_RUBY == nWhichOther)) + return SPLIT_NEW; + else + return SPLIT_OTHER; + } +} + +static void +lcl_InitINetFmt(SwTxtNode & rNode, SwTxtINetFmt * pNew) +{ + pNew->ChgTxtNode(&rNode); + SwCharFmt * const pFmt( + rNode.GetDoc()->GetCharFmtFromPool(RES_POOLCHR_INET_NORMAL) ); + pFmt->Add( pNew ); +} + +static void +lcl_InitRuby(SwTxtNode & rNode, SwTxtRuby * pNew) +{ + pNew->ChgTxtNode(&rNode); + SwCharFmt * const pFmt( + rNode.GetDoc()->GetCharFmtFromPool(RES_POOLCHR_RUBYTEXT) ); + pFmt->Add( pNew ); +} + +/** + Create a new nesting text hint. + */ +static SwTxtAttrNesting * +MakeTxtAttrNesting(SwTxtNode & rNode, SwTxtAttrNesting & rNesting, + const xub_StrLen nStart, const xub_StrLen nEnd) +{ + SwTxtAttr * const pNew( MakeTxtAttr( + *rNode.GetDoc(), rNesting.GetAttr(), nStart, nEnd ) ); + switch (pNew->Which()) + { + case RES_TXTATR_INETFMT: + { + lcl_InitINetFmt(rNode, static_cast<SwTxtINetFmt*>(pNew)); + break; + } + case RES_TXTATR_CJK_RUBY: + { + lcl_InitRuby(rNode, static_cast<SwTxtRuby*>(pNew)); + break; + } + default: + ASSERT(false, "MakeTxtAttrNesting: what the hell is that?"); + break; + } + return static_cast<SwTxtAttrNesting*>(pNew); +} + +typedef ::std::vector<SwTxtAttrNesting *> NestList_t; + +static void +lcl_DoSplitNew(NestList_t & rSplits, SwTxtNode & rNode, + const xub_StrLen nNewStart, + const xub_StrLen nOtherStart, const xub_StrLen nOtherEnd, bool bOtherDummy) +{ + const bool bSplitAtStart(nNewStart < nOtherStart); + const xub_StrLen nSplitPos( (bSplitAtStart) ? nOtherStart : nOtherEnd ); + // first find the portion that is split (not necessarily the last one!) + NestList_t::iterator const iter( + ::std::find_if( rSplits.begin(), rSplits.end(), + TxtAttrContains(nSplitPos) ) ); + if (iter != rSplits.end()) // already split here? + { + const xub_StrLen nStartPos( // skip other's dummy character! + (bSplitAtStart && bOtherDummy) ? nSplitPos + 1 : nSplitPos ); + SwTxtAttrNesting * const pNew( MakeTxtAttrNesting( + rNode, **iter, nStartPos, *(*iter)->GetEnd() ) ); + *(*iter)->GetEnd() = nSplitPos; + rSplits.insert(iter + 1, pNew); + } +} + +/** + Insert nesting hint into the hints array. Also calls NoteInHistory. + @param rNewHint the hint to be inserted (must not overlap existing!) + */ +void SwpHints::InsertNesting(SwTxtAttrNesting & rNewHint) +{ + SwpHintsArray::Insert(& rNewHint); + NoteInHistory( & rNewHint, true ); +} + +/** + +The following hints correspond to well-formed XML elements in ODF: +RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD + +The writer core must ensure that these do not overlap; if they did, +the document would not be storable as ODF. + +Also, a Hyperlink must not be nested within another Hyperlink, +and a Ruby must not be nested within another Ruby. + +The ODF export in xmloff will only put a hyperlink into a ruby, never a ruby +into a hyperlink. + +Unfortunately the UNO API for Hyperlink and Ruby consists of the properties +Hyperlink* and Ruby* of the css.text.CharacterProperties service. In other +words, they are treated as formatting attributes, not as content entites. +Furthermore, for API users it is not possible to easily test whether a certain +range would be overlapping with other nested attributes, and most importantly, +<em>which ones</em>, so we can hardly refuse to insert these in cases of +overlap. + +It is possible to split Hyperlink and Ruby into multiple portions, such that +the result is properly nested. + +meta and meta-field must not be split, because they have xml:id. + +These constraints result in the following design: + +RES_TXTATR_INETFMT: + always succeeds + inserts n attributes split at RES_TXTATR_CJK_RUBY, RES_TXTATR_META, + RES_TXTATR_METAFIELD + may replace existing RES_TXTATR_INETFMT at overlap +RES_TXTATR_CJK_RUBY: + always succeeds + inserts n attributes split at RES_TXTATR_META, RES_TXTATR_METAFIELD + may replace existing RES_TXTATR_CJK_RUBY at overlap + may split existing overlapping RES_TXTATR_INETFMT +RES_TXTATR_META: + may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD + may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY + inserts 1 attribute +RES_TXTATR_METAFIELD: + may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD + may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY + inserts 1 attribute + +The nesting is expressed by the position of the hints. +RES_TXTATR_META and RES_TXTATR_METAFIELD have a CH_TXTATR, and there can +only be one such hint starting and ending at a given position. +Only RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY lack a CH_TXTATR. +The interpretation given is that RES_TXTATR_CJK_RUBY is always around +a RES_TXTATR_INETFMT at the same start and end position (which corresponds +with the UNO API). +Both of these are always around a nesting hint with CH_TXTATR at the same +start and end position (if they should be inside, then the start should be +after the CH_TXTATR). +It would probably be a bad idea to add another nesting hint without +CH_TXTATR; on the other hand, it would be difficult adding a CH_TXTATR to +RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY, due to the overwriting and +splitting of exising hints that is necessary for backward compatibility. + + @param rNode the text node + @param rHint the hint to be inserted + @returns true iff hint was successfully inserted +*/ +bool +SwpHints::TryInsertNesting( SwTxtNode & rNode, SwTxtAttrNesting & rNewHint ) +{ +// INVARIANT: the nestable hints in the array are properly nested + const USHORT nNewWhich( rNewHint.Which() ); + const xub_StrLen nNewStart( *rNewHint.GetStart() ); + const xub_StrLen nNewEnd ( *rNewHint.GetEnd() ); +//??? const bool bNoLengthAttribute( nNewStart == nNewEnd ); + const bool bNewSelfNestable( isSelfNestable(nNewWhich) ); + + ASSERT( (RES_TXTATR_INETFMT == nNewWhich) || + (RES_TXTATR_CJK_RUBY == nNewWhich) || + (RES_TXTATR_META == nNewWhich) || + (RES_TXTATR_METAFIELD == nNewWhich), + "TryInsertNesting: Expecting INETFMT or RUBY or META or METAFIELD" ); + + NestList_t OverlappingExisting; // existing hints to be split + NestList_t OverwrittenExisting; // existing hints to be replaced + NestList_t SplitNew; // new hints to be inserted + + SplitNew.push_back(& rNewHint); + + // pass 1: split the inserted hint into fragments if necessary + for ( USHORT i = 0; i < GetEndCount(); ++i ) + { + SwTxtAttr * const pOther = GetEnd(i); + + if (pOther->IsNesting()) + { + const USHORT nOtherWhich( pOther->Which() ); + const xub_StrLen nOtherStart( *(pOther)->GetStart() ); + const xub_StrLen nOtherEnd ( *(pOther)->GetEnd() ); + if (isOverlap(nNewStart, nNewEnd, nOtherStart, nOtherEnd )) + { + switch (splitPolicy(nNewWhich, nOtherWhich)) + { + case FAIL: + OSL_TRACE("cannot insert hint: overlap detected"); + ::std::for_each(SplitNew.begin(), SplitNew.end(), + TxtAttrDeleter(*rNode.GetDoc())); + return false; + case SPLIT_NEW: + lcl_DoSplitNew(SplitNew, rNode, nNewStart, + nOtherStart, nOtherEnd, pOther->HasDummyChar()); + break; + case SPLIT_OTHER: + OverlappingExisting.push_back( + static_cast<SwTxtAttrNesting*>(pOther)); + break; + default: + ASSERT(false, "bad code monkey"); + break; + } + } + else if (isNestedAny(nNewStart, nNewEnd, nOtherStart, nOtherEnd)) + { + if (!bNewSelfNestable && (nNewWhich == nOtherWhich)) + { + // ruby and hyperlink: if there is nesting, _overwrite_ + OverwrittenExisting.push_back( + static_cast<SwTxtAttrNesting*>(pOther)); + } + else if ((nNewStart == nOtherStart) && pOther->HasDummyChar()) + { + if (rNewHint.HasDummyChar()) + { + ASSERT(false, + "ERROR: inserting duplicate CH_TXTATR hint"); + return false; + } else if (nNewEnd < nOtherEnd) { + // other has dummy char, new is inside other, but + // new contains the other's dummy char? + // should be corrected because it may lead to problems + // in SwXMeta::createEnumeration + // SplitNew is sorted, so this is the first split + xub_StrLen *const pStart(SplitNew.front()->GetStart()); + ASSERT(*pStart == nNewStart, "how did that happen?"); + *pStart = nNewStart + 1; + } + } + } + } + } + + ASSERT (isSplittable(nNewWhich) || SplitNew.size() == 1, + "splitting the unsplittable ???"); + + // pass 2: split existing hints that overlap/nest with new hint + // do not iterate over hints array, but over remembered set of overlapping + // hints, to keep things simple w.r.t. insertion/removal + // N.B: if there is a hint that splits the inserted hint, then + // that hint would also have already split any hint in OverlappingExisting + // so any hint in OverlappingExisting can be split at most by one hint + // in SplitNew, or even not at all (this is not true for existing hints + // that go _around_ new hint, which is the raison d'^etre for pass 4) + for (NestList_t::iterator itOther = OverlappingExisting.begin(); + itOther != OverlappingExisting.end(); ++itOther) + { + const xub_StrLen nOtherStart( *(*itOther)->GetStart() ); + const xub_StrLen nOtherEnd ( *(*itOther)->GetEnd() ); + + for (NestList_t::iterator itNew = SplitNew.begin(); + itNew != SplitNew.end(); ++itNew) + { + const xub_StrLen nSplitNewStart( *(*itNew)->GetStart() ); + const xub_StrLen nSplitNewEnd ( *(*itNew)->GetEnd() ); + // 4 cases: within, around, overlap l, overlap r, (OTHER: no action) + const bool bRemoveOverlap( + !bNewSelfNestable && (nNewWhich == (*itOther)->Which()) ); + + switch (ComparePosition(nSplitNewStart, nSplitNewEnd, + nOtherStart, nOtherEnd)) + { + case POS_INSIDE: + { + ASSERT(!bRemoveOverlap, + "this one should be in OverwrittenExisting?"); + } + break; + case POS_OUTSIDE: + case POS_EQUAL: + { + ASSERT(false, "existing hint inside new hint: why?"); + } + break; + case POS_OVERLAP_BEFORE: + { + Delete( *itOther ); // this also does NoteInHistory! + *(*itOther)->GetStart() = nSplitNewEnd; + InsertNesting( **itOther ); + if (!bRemoveOverlap) + { + if ( USHRT_MAX == Count() ) + { + ASSERT(false, "hints array full :-("); + return false; + } + SwTxtAttrNesting * const pOtherLeft( + MakeTxtAttrNesting( rNode, **itOther, + nOtherStart, nSplitNewEnd ) ); + InsertNesting( *pOtherLeft ); + } + } + break; + case POS_OVERLAP_BEHIND: + { + Delete( *itOther ); // this also does NoteInHistory! + *(*itOther)->GetEnd() = nSplitNewStart; + InsertNesting( **itOther ); + if (!bRemoveOverlap) + { + if ( USHRT_MAX == Count() ) + { + ASSERT(false, "hints array full :-("); + return false; + } + SwTxtAttrNesting * const pOtherRight( + MakeTxtAttrNesting( rNode, **itOther, + nSplitNewStart, nOtherEnd ) ); + InsertNesting( *pOtherRight ); + } + } + break; + default: + break; // overlap resolved by splitting new: nothing to do + } + } + } + + if ( USHRT_MAX - SplitNew.size() <= Count() ) + { + ASSERT(false, "hints array full :-("); + return false; + } + + // pass 3: insert new hints + for (NestList_t::iterator iter = SplitNew.begin(); + iter != SplitNew.end(); ++iter) + { + InsertNesting(**iter); + } + + // pass 4: handle overwritten hints + // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes + // of the same kind. + for (NestList_t::iterator itOther = OverwrittenExisting.begin(); + itOther != OverwrittenExisting.end(); ++itOther) + { + const xub_StrLen nOtherStart( *(*itOther)->GetStart() ); + const xub_StrLen nOtherEnd ( *(*itOther)->GetEnd() ); + + // overwritten portion is given by start/end of inserted hint + if ((nNewStart <= nOtherStart) && (nOtherEnd <= nNewEnd)) + { + Delete(*itOther); + rNode.DestroyAttr( *itOther ); + } + else + { + ASSERT((nOtherStart < nNewStart) && (nNewEnd < nOtherEnd), "huh?"); + // scenario: there is a RUBY, and contained within that a META; + // now a RUBY is inserted within the META => the exising RUBY is split: + // here it is not possible to simply insert the left/right fragment + // of the existing RUBY because they <em>overlap</em> with the META! + Delete( *itOther ); // this also does NoteInHistory! + *(*itOther)->GetEnd() = nNewStart; + bool bSuccess( TryInsertNesting(rNode, **itOther) ); + ASSERT(bSuccess, "recursive call 1 failed?"); + SwTxtAttrNesting * const pOtherRight( + MakeTxtAttrNesting( + rNode, **itOther, nNewEnd, nOtherEnd ) ); + bSuccess = TryInsertNesting(rNode, *pOtherRight); + ASSERT(bSuccess, "recursive call 2 failed?"); + } + + } + + return true; +} + + +// This function takes care for the following text attribute: +// RES_TXTATR_CHARFMT, RES_TXTATR_AUTOFMT +// These attributes have to be handled in a special way (Portion building). +// +// The new attribute will be split by any existing RES_TXTATR_AUTOFMT or +// RES_TXTATR_CHARFMT. The new attribute itself will +// split any existing RES_TXTATR_AUTOFMT or RES_TXTATR_CHARFMT. + +void SwpHints::BuildPortions( SwTxtNode& rNode, SwTxtAttr& rNewHint, + const SetAttrMode nMode ) +{ + const USHORT nWhich = rNewHint.Which(); + + const xub_StrLen nThisStart = *rNewHint.GetStart(); + const xub_StrLen nThisEnd = *rNewHint.GetEnd(); + const bool bNoLengthAttribute = nThisStart == nThisEnd; + + std::vector<SwTxtAttr*> aInsDelHints; + std::vector<SwTxtAttr*>::iterator aIter; + + ASSERT( RES_TXTATR_CHARFMT == rNewHint.Which() || + RES_TXTATR_AUTOFMT == rNewHint.Which(), + "Expecting CHARFMT or AUTOFMT" ); + + // + // 2. Find the hints which cover the start and end position + // of the new hint. These hints have to be split into two portions: + // + if ( !bNoLengthAttribute ) // nothing to do for no length attributes + { + for ( USHORT i = 0; i < Count(); ++i ) + { + SwTxtAttr* pOther = GetTextHint(i); + + if ( RES_TXTATR_CHARFMT != pOther->Which() && + RES_TXTATR_AUTOFMT != pOther->Which() ) + continue; + + xub_StrLen nOtherStart = *pOther->GetStart(); + const xub_StrLen nOtherEnd = *pOther->GetEnd(); + + // Check if start of new attribute overlaps with pOther: + // Split pOther if necessary: + if ( nOtherStart < nThisStart && nThisStart < nOtherEnd ) + { + SwTxtAttr* pNewAttr = MakeTxtAttr( *rNode.GetDoc(), + pOther->GetAttr(), nOtherStart, nThisStart ); + if ( RES_TXTATR_CHARFMT == pOther->Which() ) + static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( static_cast<SwTxtCharFmt*>(pOther)->GetSortNumber() ); + aInsDelHints.push_back( pNewAttr ); + + NoteInHistory( pOther ); + *pOther->GetStart() = nThisStart; + NoteInHistory( pOther, true ); + + nOtherStart = nThisStart; + } + + // Check if end of new attribute overlaps with pOther: + // Split pOther if necessary: + if ( nOtherStart < nThisEnd && nThisEnd < nOtherEnd ) + { + SwTxtAttr* pNewAttr = MakeTxtAttr( *rNode.GetDoc(), + pOther->GetAttr(), nOtherStart, nThisEnd ); + if ( RES_TXTATR_CHARFMT == pOther->Which() ) + static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( static_cast<SwTxtCharFmt*>(pOther)->GetSortNumber() ); + aInsDelHints.push_back( pNewAttr ); + + NoteInHistory( pOther ); + *pOther->GetStart() = nThisEnd; + NoteInHistory( pOther, true ); + } + } + + // Insert the newly created attributes: + for ( aIter = aInsDelHints.begin(); aIter != aInsDelHints.end(); ++aIter ) + { + SwpHintsArray::Insert( *aIter ); + NoteInHistory( *aIter, true ); + } + } + +#ifdef DBG_UTIL + if( !rNode.GetDoc()->IsInReading() ) + CHECK; +#endif + + // + // 4. Split rNewHint into 1 ... n new hints: + // + std::set<xub_StrLen> aBounds; + aBounds.insert( nThisStart ); + aBounds.insert( nThisEnd ); + + if ( !bNoLengthAttribute ) // nothing to do for no length attributes + { + for ( USHORT i = 0; i < Count(); ++i ) + { + const SwTxtAttr* pOther = GetTextHint(i); + + if ( RES_TXTATR_CHARFMT != pOther->Which() && + RES_TXTATR_AUTOFMT != pOther->Which() ) + continue; + + const xub_StrLen nOtherStart = *pOther->GetStart(); + const xub_StrLen nOtherEnd = *pOther->GetEnd(); + + aBounds.insert( nOtherStart ); + aBounds.insert( nOtherEnd ); + } + } + + std::set<xub_StrLen>::iterator aStartIter = aBounds.lower_bound( nThisStart ); + std::set<xub_StrLen>::iterator aEndIter = aBounds.upper_bound( nThisEnd ); + xub_StrLen nPorStart = *aStartIter; + ++aStartIter; + bool bDestroyHint = true; + + // + // Insert the 1...n new parts of the new attribute: + // + while ( aStartIter != aEndIter || bNoLengthAttribute ) + { + ASSERT( bNoLengthAttribute || nPorStart < *aStartIter, "AUTOSTYLES: BuildPortion trouble" ) + + const xub_StrLen nPorEnd = bNoLengthAttribute ? nPorStart : *aStartIter; + aInsDelHints.clear(); + + // Get all hints that are in [nPorStart, nPorEnd[: + for ( USHORT i = 0; i < Count(); ++i ) + { + SwTxtAttr *pOther = GetTextHint(i); + + if ( RES_TXTATR_CHARFMT != pOther->Which() && + RES_TXTATR_AUTOFMT != pOther->Which() ) + continue; + + const xub_StrLen nOtherStart = *pOther->GetStart(); + + if ( nOtherStart > nPorStart ) + break; + + if ( pOther->GetEnd() && *pOther->GetEnd() == nPorEnd && nOtherStart == nPorStart ) + { + ASSERT( *pOther->GetEnd() == nPorEnd, "AUTOSTYLES: BuildPortion trouble" ) + aInsDelHints.push_back( pOther ); + } + } + + SwTxtAttr* pNewAttr = 0; + if ( RES_TXTATR_CHARFMT == nWhich ) + { + // pNewHint can be inserted after calculating the sort value. + // This should ensure, that pNewHint comes behind the already present + // character style + USHORT nCharStyleCount = 0; + aIter = aInsDelHints.begin(); + while ( aIter != aInsDelHints.end() ) + { + if ( RES_TXTATR_CHARFMT == (*aIter)->Which() ) + { + // --> FME 2007-02-16 #i74589# + const SwFmtCharFmt& rOtherCharFmt = (*aIter)->GetCharFmt(); + const SwFmtCharFmt& rThisCharFmt = rNewHint.GetCharFmt(); + const bool bSameCharFmt = rOtherCharFmt.GetCharFmt() == rThisCharFmt.GetCharFmt(); + // <-- + + // --> OD 2009-03-24 #i90311# + // Do not remove existing character format hint during XML import + if ( !rNode.GetDoc()->IsInXMLImport() && + ( !( nsSetAttrMode::SETATTR_DONTREPLACE & nMode ) || + bNoLengthAttribute || + bSameCharFmt ) ) + // <-- + { + // Remove old hint + Delete( *aIter ); + rNode.DestroyAttr( *aIter ); + } + else + ++nCharStyleCount; + } + else + { + // remove all attributes from auto styles, which are explicitely set in + // the new character format: + ASSERT( RES_TXTATR_AUTOFMT == (*aIter)->Which(), "AUTOSTYLES - Misc trouble" ) + SwTxtAttr* pOther = *aIter; + boost::shared_ptr<SfxItemSet> pOldStyle = static_cast<const SwFmtAutoFmt&>(pOther->GetAttr()).GetStyleHandle(); + + // For each attribute in the automatic style check if it + // is also set the the new character style: + SfxItemSet aNewSet( *pOldStyle->GetPool(), RES_CHRATR_BEGIN, RES_CHRATR_END ); + SfxItemIter aItemIter( *pOldStyle ); + const SfxPoolItem* pItem = aItemIter.GetCurItem(); + while( TRUE ) + { + if ( !CharFmt::IsItemIncluded( pItem->Which(), &rNewHint ) ) + { + aNewSet.Put( *pItem ); + } + + if( aItemIter.IsAtEnd() ) + break; + + pItem = aItemIter.NextItem(); + } + + // Remove old hint + Delete( pOther ); + rNode.DestroyAttr( pOther ); + + // Create new AutoStyle + if ( aNewSet.Count() ) + { + pNewAttr = MakeTxtAttr( *rNode.GetDoc(), + aNewSet, nPorStart, nPorEnd ); + SwpHintsArray::Insert( pNewAttr ); + NoteInHistory( pNewAttr, true ); + } + } + ++aIter; + } + + // If there is no current hint and start and end of rNewHint + // is ok, we do not need to create a new txtattr. + if ( nPorStart == nThisStart && + nPorEnd == nThisEnd && + !nCharStyleCount ) + { + pNewAttr = &rNewHint; + bDestroyHint = false; + } + else + { + pNewAttr = MakeTxtAttr( *rNode.GetDoc(), rNewHint.GetAttr(), + nPorStart, nPorEnd ); + static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( nCharStyleCount ); + } + } + else + { + // Find the current autostyle. Mix attributes if necessary. + SwTxtAttr* pCurrentAutoStyle = 0; + SwTxtAttr* pCurrentCharFmt = 0; + aIter = aInsDelHints.begin(); + while ( aIter != aInsDelHints.end() ) + { + if ( RES_TXTATR_AUTOFMT == (*aIter)->Which() ) + pCurrentAutoStyle = *aIter; + else if ( RES_TXTATR_CHARFMT == (*aIter)->Which() ) + pCurrentCharFmt = *aIter; + ++aIter; + } + + boost::shared_ptr<SfxItemSet> pNewStyle = static_cast<const SwFmtAutoFmt&>(rNewHint.GetAttr()).GetStyleHandle(); + if ( pCurrentAutoStyle ) + { + boost::shared_ptr<SfxItemSet> pCurrentStyle = static_cast<const SwFmtAutoFmt&>(pCurrentAutoStyle->GetAttr()).GetStyleHandle(); + + // Merge attributes + SfxItemSet aNewSet( *pCurrentStyle ); + aNewSet.Put( *pNewStyle ); + + // --> FME 2007-4-11 #i75750# Remove attributes already set at whole paragraph + // --> FME 2007-09-24 #i81764# This should not be applied for no length attributes!!! <-- + if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && aNewSet.Count() ) + { + SfxItemIter aIter2( aNewSet ); + const SfxPoolItem* pItem = aIter2.GetCurItem(); + const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet(); + + do + { + const SfxPoolItem* pTmpItem = 0; + if ( SFX_ITEM_SET == rWholeParaAttrSet.GetItemState( pItem->Which(), FALSE, &pTmpItem ) && + pTmpItem == pItem ) + { + // Do not clear item if the attribute is set in a character format: + if ( !pCurrentCharFmt || 0 == CharFmt::GetItem( *pCurrentCharFmt, pItem->Which() ) ) + aNewSet.ClearItem( pItem->Which() ); + } + } + while (!aIter2.IsAtEnd() && 0 != (pItem = aIter2.NextItem())); + } + // <-- + + // Remove old hint + Delete( pCurrentAutoStyle ); + rNode.DestroyAttr( pCurrentAutoStyle ); + + // Create new AutoStyle + if ( aNewSet.Count() ) + pNewAttr = MakeTxtAttr( *rNode.GetDoc(), aNewSet, + nPorStart, nPorEnd ); + } + else + { + // Remove any attributes which are already set at the whole paragraph: + bool bOptimizeAllowed = true; + + SfxItemSet* pNewSet = 0; + // --> FME 2007-4-11 #i75750# Remove attributes already set at whole paragraph + // --> FME 2007-09-24 #i81764# This should not be applied for no length attributes!!! <-- + if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && pNewStyle->Count() ) + { + SfxItemIter aIter2( *pNewStyle ); + const SfxPoolItem* pItem = aIter2.GetCurItem(); + const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet(); + + do + { + const SfxPoolItem* pTmpItem = 0; + if ( SFX_ITEM_SET == rWholeParaAttrSet.GetItemState( pItem->Which(), FALSE, &pTmpItem ) && + pTmpItem == pItem ) + { + // Do not clear item if the attribute is set in a character format: + if ( !pCurrentCharFmt || 0 == CharFmt::GetItem( *pCurrentCharFmt, pItem->Which() ) ) + { + if ( !pNewSet ) + pNewSet = pNewStyle->Clone( TRUE ); + pNewSet->ClearItem( pItem->Which() ); + } + } + } + while (!aIter2.IsAtEnd() && 0 != (pItem = aIter2.NextItem())); + + if ( pNewSet ) + { + bOptimizeAllowed = false; + if ( pNewSet->Count() ) + pNewStyle = rNode.getIDocumentStyleAccess().getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR ); + else + pNewStyle.reset(); + + delete pNewSet; + } + } + // <-- + + // Create new AutoStyle + // If there is no current hint and start and end of rNewHint + // is ok, we do not need to create a new txtattr. + if ( bOptimizeAllowed && + nPorStart == nThisStart && + nPorEnd == nThisEnd ) + { + pNewAttr = &rNewHint; + bDestroyHint = false; + } + else if ( pNewStyle.get() ) + { + pNewAttr = MakeTxtAttr( *rNode.GetDoc(), *pNewStyle, + nPorStart, nPorEnd ); + } + } + } + + if ( pNewAttr ) + { + SwpHintsArray::Insert( pNewAttr ); +// if ( bDestroyHint ) + NoteInHistory( pNewAttr, true ); + } + + if ( !bNoLengthAttribute ) + { + nPorStart = *aStartIter; + ++aStartIter; + } + else + break; + } + + if ( bDestroyHint ) + rNode.DestroyAttr( &rNewHint ); +} + +/************************************************************************* + * SwTxtNode::MakeTxtAttr() + *************************************************************************/ + +SwTxtAttr* MakeRedlineTxtAttr( SwDoc & rDoc, SfxPoolItem & rAttr ) +{ + // this is intended _only_ for special-purpose redline attributes! + switch (rAttr.Which()) + { + case RES_CHRATR_COLOR: + case RES_CHRATR_WEIGHT: + case RES_CHRATR_CJK_WEIGHT: + case RES_CHRATR_CTL_WEIGHT: + case RES_CHRATR_POSTURE: + case RES_CHRATR_CJK_POSTURE: + case RES_CHRATR_CTL_POSTURE: + case RES_CHRATR_UNDERLINE: + case RES_CHRATR_CROSSEDOUT: + case RES_CHRATR_CASEMAP: + case RES_CHRATR_BACKGROUND: + break; + default: + ASSERT(false, "unsupported redline attribute"); + break; + } + + // Put new attribute into pool + // FIXME: this const_cast is evil! + SfxPoolItem& rNew = + const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) ); + return new SwTxtAttrEnd( rNew, 0, 0 ); +} + +// create new text attribute +SwTxtAttr* MakeTxtAttr( SwDoc & rDoc, SfxPoolItem& rAttr, + xub_StrLen nStt, xub_StrLen nEnd ) +{ + if ( isCHRATR(rAttr.Which()) ) + { + // Somebody wants to build a SwTxtAttr for a character attribute. + // Sorry, this is not allowed any longer. + // You'll get a brand new autostyle attribute: + SfxItemSet aItemSet( rDoc.GetAttrPool(), + RES_CHRATR_BEGIN, RES_CHRATR_END ); + aItemSet.Put( rAttr ); + return MakeTxtAttr( rDoc, aItemSet, nStt, nEnd ); + } + else if ( RES_TXTATR_AUTOFMT == rAttr.Which() && + static_cast<const SwFmtAutoFmt&>(rAttr).GetStyleHandle()-> + GetPool() != &rDoc.GetAttrPool() ) + { + // If the attribute is an auto-style which refers to a pool that is + // different from rDoc's pool, we have to correct this: + const StylePool::SfxItemSet_Pointer_t pAutoStyle = static_cast<const SwFmtAutoFmt&>(rAttr).GetStyleHandle(); + ::std::auto_ptr<const SfxItemSet> pNewSet( + pAutoStyle->SfxItemSet::Clone( TRUE, &rDoc.GetAttrPool() )); + SwTxtAttr* pNew = MakeTxtAttr( rDoc, *pNewSet, nStt, nEnd ); + return pNew; + } + + // Put new attribute into pool + // FIXME: this const_cast is evil! + SfxPoolItem& rNew = + const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) ); + + SwTxtAttr* pNew = 0; + switch( rNew.Which() ) + { + case RES_TXTATR_CHARFMT: + { + SwFmtCharFmt &rFmtCharFmt = (SwFmtCharFmt&) rNew; + if( !rFmtCharFmt.GetCharFmt() ) + { + rFmtCharFmt.SetCharFmt( rDoc.GetDfltCharFmt() ); + } + + pNew = new SwTxtCharFmt( rFmtCharFmt, nStt, nEnd ); + } + break; + case RES_TXTATR_INETFMT: + pNew = new SwTxtINetFmt( (SwFmtINetFmt&)rNew, nStt, nEnd ); + break; + case RES_TXTATR_FIELD: + pNew = new SwTxtFld( (SwFmtFld&)rNew, nStt ); + break; + case RES_TXTATR_FLYCNT: + { + // erst hier wird das Frame-Format kopiert (mit Inhalt) !! + pNew = new SwTxtFlyCnt( (SwFmtFlyCnt&)rNew, nStt ); + // Kopie von einem Text-Attribut + if ( static_cast<const SwFmtFlyCnt &>(rAttr).GetTxtFlyCnt() ) + { + // then the format must be copied + static_cast<SwTxtFlyCnt *>(pNew)->CopyFlyFmt( &rDoc ); + } + } + break; + case RES_TXTATR_FTN: + pNew = new SwTxtFtn( (SwFmtFtn&)rNew, nStt ); + // ggfs. SeqNo kopieren + if( ((SwFmtFtn&)rAttr).GetTxtFtn() ) + ((SwTxtFtn*)pNew)->SetSeqNo( ((SwFmtFtn&)rAttr).GetTxtFtn()->GetSeqRefNo() ); + break; + case RES_TXTATR_REFMARK: + pNew = nStt == nEnd + ? new SwTxtRefMark( (SwFmtRefMark&)rNew, nStt ) + : new SwTxtRefMark( (SwFmtRefMark&)rNew, nStt, &nEnd ); + break; + case RES_TXTATR_TOXMARK: + pNew = new SwTxtTOXMark( (SwTOXMark&)rNew, nStt, &nEnd ); + break; + case RES_TXTATR_CJK_RUBY: + pNew = new SwTxtRuby( (SwFmtRuby&)rNew, nStt, nEnd ); + break; + case RES_TXTATR_META: + case RES_TXTATR_METAFIELD: + pNew = new SwTxtMeta( static_cast<SwFmtMeta&>(rNew), nStt, nEnd ); + break; + default: + ASSERT(RES_TXTATR_AUTOFMT == rNew.Which(), "unknown attribute"); + pNew = new SwTxtAttrEnd( rNew, nStt, nEnd ); + break; + } + + return pNew; +} + +SwTxtAttr* MakeTxtAttr( SwDoc & rDoc, const SfxItemSet& rSet, + xub_StrLen nStt, xub_StrLen nEnd ) +{ + IStyleAccess& rStyleAccess = rDoc.GetIStyleAccess(); + const StylePool::SfxItemSet_Pointer_t pAutoStyle = rStyleAccess.getAutomaticStyle( rSet, IStyleAccess::AUTO_STYLE_CHAR ); + SwFmtAutoFmt aNewAutoFmt; + aNewAutoFmt.SetStyleHandle( pAutoStyle ); + SwTxtAttr* pNew = MakeTxtAttr( rDoc, aNewAutoFmt, nStt, nEnd ); + return pNew; +} + + +// loesche das Text-Attribut (muss beim Pool abgemeldet werden!) +void SwTxtNode::DestroyAttr( SwTxtAttr* pAttr ) +{ + if( pAttr ) + { + // einige Sachen muessen vorm Loeschen der "Format-Attribute" erfolgen + SwDoc* pDoc = GetDoc(); + USHORT nDelMsg = 0; + switch( pAttr->Which() ) + { + case RES_TXTATR_FLYCNT: + { + // siehe auch die Anmerkung "Loeschen von Formaten + // zeichengebundener Frames" in fesh.cxx, SwFEShell::DelFmt() + SwFrmFmt* pFmt = pAttr->GetFlyCnt().GetFrmFmt(); + if( pFmt ) // vom Undo auf 0 gesetzt ?? + pDoc->DelLayoutFmt( (SwFlyFrmFmt*)pFmt ); + } + break; + + case RES_CHRATR_HIDDEN: + SetCalcHiddenCharFlags(); + break; + + case RES_TXTATR_FTN: + ((SwTxtFtn*)pAttr)->SetStartNode( 0 ); + nDelMsg = RES_FOOTNOTE_DELETED; + break; + + case RES_TXTATR_FIELD: + if( !pDoc->IsInDtor() ) + { + // Wenn wir ein HiddenParaField sind, dann muessen wir + // ggf. fuer eine Neuberechnung des Visible-Flags sorgen. + const SwField* pFld = pAttr->GetFld().GetFld(); + + //JP 06-08-95: DDE-Felder bilden eine Ausnahme + ASSERT( RES_DDEFLD == pFld->GetTyp()->Which() || + this == ((SwTxtFld*)pAttr)->GetpTxtNode(), + "Wo steht denn dieses Feld?" ) + + // bestimmte Felder mussen am Doc das Calculations-Flag updaten + switch( pFld->GetTyp()->Which() ) + { + case RES_HIDDENPARAFLD: + SetCalcHiddenParaField(); + // kein break ! + case RES_DBSETNUMBERFLD: + case RES_GETEXPFLD: + case RES_DBFLD: + case RES_SETEXPFLD: + case RES_HIDDENTXTFLD: + case RES_DBNUMSETFLD: + case RES_DBNEXTSETFLD: + if( !pDoc->IsNewFldLst() && GetNodes().IsDocNodes() ) + pDoc->InsDelFldInFldLst( FALSE, *(SwTxtFld*)pAttr ); + break; + case RES_DDEFLD: + if( GetNodes().IsDocNodes() && + ((SwTxtFld*)pAttr)->GetpTxtNode() ) + ((SwDDEFieldType*)pFld->GetTyp())->DecRefCnt(); + break; + case RES_POSTITFLD: + { + const_cast<SwFmtFld&>(pAttr->GetFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pAttr)->GetFld(), SWFMTFLD_REMOVED ) ); + break; + } + } + } + nDelMsg = RES_FIELD_DELETED; + break; + + case RES_TXTATR_TOXMARK: + static_cast<SwTOXMark&>(pAttr->GetAttr()).InvalidateTOXMark(); + break; + + case RES_TXTATR_REFMARK: + nDelMsg = RES_REFMARK_DELETED; + break; + + case RES_TXTATR_META: + case RES_TXTATR_METAFIELD: + static_cast<SwTxtMeta*>(pAttr)->ChgTxtNode(0); + break; + + default: + break; + } + + if( nDelMsg && !pDoc->IsInDtor() && GetNodes().IsDocNodes() ) + { + SwPtrMsgPoolItem aMsgHint( nDelMsg, (void*)&pAttr->GetAttr() ); + pDoc->GetUnoCallBack()->Modify( &aMsgHint, &aMsgHint ); + } + + SwTxtAttr::Destroy( pAttr, pDoc->GetAttrPool() ); + } +} + +/************************************************************************* + * SwTxtNode::Insert() + *************************************************************************/ + +SwTxtAttr* +SwTxtNode::InsertItem( SfxPoolItem& rAttr, + const xub_StrLen nStart, const xub_StrLen nEnd, const SetAttrMode nMode ) +{ + // character attributes will be inserted as automatic styles: + ASSERT( !isCHRATR(rAttr.Which()), "AUTOSTYLES - " + "SwTxtNode::InsertItem should not be called with character attributes"); + + SwTxtAttr* const pNew = MakeTxtAttr( *GetDoc(), rAttr, nStart, nEnd ); + + if ( pNew ) + { + const bool bSuccess( InsertHint( pNew, nMode ) ); + // N.B.: also check that the hint is actually in the hints array, + // because hints of certain types may be merged after succesful + // insertion, and thus destroyed! + if (!bSuccess || ( USHRT_MAX == m_pSwpHints->GetPos( pNew ) )) + { + return 0; + } + } + + return pNew; +} + +// take ownership of pAttr; if insertion fails, delete pAttr +bool SwTxtNode::InsertHint( SwTxtAttr * const pAttr, const SetAttrMode nMode ) +{ + BOOL bHiddenPara = FALSE; + + ASSERT( pAttr && *pAttr->GetStart() <= Len(), "StartIdx out of bounds!" ); + ASSERT( !pAttr->GetEnd() || (*pAttr->GetEnd() <= Len()), + "EndIdx out of bounds!" ); + + // translate from SetAttrMode to InsertMode (for hints with CH_TXTATR) + const enum IDocumentContentOperations::InsertFlags nInsertFlags = + (nMode & nsSetAttrMode::SETATTR_FORCEHINTEXPAND) + ? static_cast<IDocumentContentOperations::InsertFlags>( + IDocumentContentOperations::INS_FORCEHINTEXPAND | + IDocumentContentOperations::INS_EMPTYEXPAND) + : IDocumentContentOperations::INS_EMPTYEXPAND; + + // need this after TryInsertHint, when pAttr may be deleted + const xub_StrLen nStart( *pAttr->GetStart() ); + const bool bDummyChar( pAttr->HasDummyChar() ); + if (bDummyChar) + { + USHORT nInsMode = nMode; + switch( pAttr->Which() ) + { + case RES_TXTATR_FLYCNT: + { + SwTxtFlyCnt *pFly = (SwTxtFlyCnt *)pAttr; + SwFrmFmt* pFmt = pAttr->GetFlyCnt().GetFrmFmt(); + if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) ) + { + // Wir muessen zuerst einfuegen, da in SetAnchor() + // dem FlyFrm GetStart() uebermittelt wird. + //JP 11.05.98: falls das Anker-Attribut schon richtig + // gesetzt ist, dann korrigiere dieses nach dem Einfuegen + // des Zeichens. Sonst muesste das immer ausserhalb + // erfolgen (Fehleranfaellig !) + const SwFmtAnchor* pAnchor = 0; + pFmt->GetItemState( RES_ANCHOR, FALSE, + (const SfxPoolItem**)&pAnchor ); + + SwIndex aIdx( this, *pAttr->GetStart() ); + const sal_Unicode c = GetCharOfTxtAttr(*pAttr); + InsertText( c, aIdx, nInsertFlags ); + nInsMode |= nsSetAttrMode::SETATTR_NOTXTATRCHR; + + if (pAnchor && + (FLY_AS_CHAR == pAnchor->GetAnchorId()) && + pAnchor->GetCntntAnchor() && + pAnchor->GetCntntAnchor()->nNode == *this && + pAnchor->GetCntntAnchor()->nContent == aIdx ) + { + const_cast<SwIndex&>( + pAnchor->GetCntntAnchor()->nContent)--; + } + } + pFly->SetAnchor( this ); + + // Format-Pointer kann sich im SetAnchor geaendert haben! + // (Kopieren in andere Docs!) + pFmt = pAttr->GetFlyCnt().GetFrmFmt(); + SwDoc *pDoc = pFmt->GetDoc(); + + // OD 26.06.2003 #108784# - allow drawing objects in header/footer. + // But don't allow control objects in header/footer + if( RES_DRAWFRMFMT == pFmt->Which() && + pDoc->IsInHeaderFooter( pFmt->GetAnchor().GetCntntAnchor()->nNode ) ) + { + SwDrawContact* pDrawContact = + static_cast<SwDrawContact*>(pFmt->FindContactObj()); + if ( pDrawContact && + pDrawContact->GetMaster() && + ::CheckControlLayer( pDrawContact->GetMaster() ) ) + { + // das soll nicht meoglich sein; hier verhindern + // Der Dtor des TxtHints loescht nicht das Zeichen. + // Wenn ein CH_TXTATR_.. vorliegt, dann muss man + // dieses explizit loeschen + if( nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode ) + { + // loesche das Zeichen aus dem String ! + ASSERT( ( CH_TXTATR_BREAKWORD == + m_Text.GetChar(*pAttr->GetStart() ) || + CH_TXTATR_INWORD == + m_Text.GetChar(*pAttr->GetStart())), + "where is my attribute character?" ); + m_Text.Erase( *pAttr->GetStart(), 1 ); + // Indizies Updaten + SwIndex aTmpIdx( this, *pAttr->GetStart() ); + Update( aTmpIdx, 1, TRUE ); + } + // Format loeschen nicht ins Undo aufnehmen!! + BOOL bUndo = pDoc->DoesUndo(); + pDoc->DoUndo( FALSE ); + DestroyAttr( pAttr ); + pDoc->DoUndo( bUndo ); + return false; + } + } + break; + } + + case RES_TXTATR_FTN : + { + // Fussnoten, man kommt an alles irgendwie heran. + // CntntNode erzeugen und in die Inserts-Section stellen + SwDoc *pDoc = GetDoc(); + SwNodes &rNodes = pDoc->GetNodes(); + + // FussNote in nicht Content-/Redline-Bereich einfuegen ?? + if( StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex() ) + { + // das soll nicht meoglich sein; hier verhindern + // Der Dtor des TxtHints loescht nicht das Zeichen. + // Wenn ein CH_TXTATR_.. vorliegt, dann muss man + // dieses explizit loeschen + if( nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode ) + { + // loesche das Zeichen aus dem String ! + ASSERT( ( CH_TXTATR_BREAKWORD == + m_Text.GetChar(*pAttr->GetStart() ) || + CH_TXTATR_INWORD == + m_Text.GetChar(*pAttr->GetStart())), + "where is my attribute character?" ); + m_Text.Erase( *pAttr->GetStart(), 1 ); + // Indizies Updaten + SwIndex aTmpIdx( this, *pAttr->GetStart() ); + Update( aTmpIdx, 1, TRUE ); + } + DestroyAttr( pAttr ); + return false; + } + + // wird eine neue Fussnote eingefuegt ?? + BOOL bNewFtn = 0 == ((SwTxtFtn*)pAttr)->GetStartNode(); + if( bNewFtn ) + { + ((SwTxtFtn*)pAttr)->MakeNewTextSection( GetNodes() ); + SwRegHistory* pHist = GetpSwpHints() + ? GetpSwpHints()->GetHistory() : 0; + if( pHist ) + pHist->ChangeNodeIndex( GetIndex() ); + } + else if ( !GetpSwpHints() || !GetpSwpHints()->IsInSplitNode() ) + { + // loesche alle Frames der Section, auf die der StartNode zeigt + ULONG nSttIdx = + ((SwTxtFtn*)pAttr)->GetStartNode()->GetIndex(); + ULONG nEndIdx = rNodes[ nSttIdx++ ]->EndOfSectionIndex(); + SwCntntNode* pCNd; + for( ; nSttIdx < nEndIdx; ++nSttIdx ) + if( 0 != ( pCNd = rNodes[ nSttIdx ]->GetCntntNode() )) + pCNd->DelFrms(); + } + + if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) ) + { + // Wir muessen zuerst einfuegen, da sonst gleiche Indizes + // entstehen koennen und das Attribut im _SortArr_ am + // Dokument nicht eingetrage wird. + SwIndex aNdIdx( this, *pAttr->GetStart() ); + const sal_Unicode c = GetCharOfTxtAttr(*pAttr); + InsertText( c, aNdIdx, nInsertFlags ); + nInsMode |= nsSetAttrMode::SETATTR_NOTXTATRCHR; + } + + // Wir tragen uns am FtnIdx-Array des Docs ein ... + SwTxtFtn* pTxtFtn = 0; + if( !bNewFtn ) + { + // eine alte Ftn wird umgehaengt (z.B. SplitNode) + for( USHORT n = 0; n < pDoc->GetFtnIdxs().Count(); ++n ) + if( pAttr == pDoc->GetFtnIdxs()[n] ) + { + // neuen Index zuweisen, dafuer aus dem SortArray + // loeschen und neu eintragen + pTxtFtn = pDoc->GetFtnIdxs()[n]; + pDoc->GetFtnIdxs().Remove( n ); + break; + } + // wenn ueber Undo der StartNode gesetzt wurde, kann + // der Index noch gar nicht in der Verwaltung stehen !! + } + if( !pTxtFtn ) + pTxtFtn = (SwTxtFtn*)pAttr; + + // fuers Update der Nummern und zum Sortieren + // muss der Node gesetzt sein. + ((SwTxtFtn*)pAttr)->ChgTxtNode( this ); + + // FussNote im Redline-Bereich NICHT ins FtnArray einfuegen! + if( StartOfSectionIndex() > rNodes.GetEndOfRedlines().GetIndex() ) + { +#ifdef DBG_UTIL + const BOOL bSuccess = +#endif + pDoc->GetFtnIdxs().Insert( pTxtFtn ); +#ifdef DBG_UTIL + ASSERT( bSuccess, "FtnIdx nicht eingetragen." ); +#endif + } + SwNodeIndex aTmpIndex( *this ); + pDoc->GetFtnIdxs().UpdateFtn( aTmpIndex); + ((SwTxtFtn*)pAttr)->SetSeqRefNo(); + } + break; + + case RES_TXTATR_FIELD: + { + // fuer HiddenParaFields Benachrichtigungsmechanismus + // anwerfen + if( RES_HIDDENPARAFLD == + pAttr->GetFld().GetFld()->GetTyp()->Which() ) + bHiddenPara = TRUE; + } + break; + + } + // Fuer SwTxtHints ohne Endindex werden CH_TXTATR_.. + // eingefuegt, aStart muss danach um einen zurueckgesetzt werden. + // Wenn wir im SwTxtNode::Copy stehen, so wurde das Zeichen bereits + // mitkopiert. In solchem Fall ist SETATTR_NOTXTATRCHR angegeben worden. + if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) ) + { + SwIndex aIdx( this, *pAttr->GetStart() ); + InsertText( GetCharOfTxtAttr(*pAttr), aIdx, nInsertFlags ); + + // adjust end of hint to account for inserted CH_TXTATR + xub_StrLen * const pEnd(pAttr->GetEnd()); + if (pEnd) + { + *pEnd = *pEnd + 1; + } + } + } + + GetOrCreateSwpHints(); + + // 4263: AttrInsert durch TextInsert => kein Adjust + const bool bRet = m_pSwpHints->TryInsertHint( pAttr, *this, nMode ); + + if (!bRet && bDummyChar) + { + // undo insertion of dummy character + // N.B. cannot insert the dummy character after inserting the hint, + // because if the hint has no extent it will be moved in InsertText, + // resulting in infinite recursion + if ( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) ) + { + ASSERT( ( CH_TXTATR_BREAKWORD == m_Text.GetChar(nStart) || + CH_TXTATR_INWORD == m_Text.GetChar(nStart) ), + "where is my attribute character?" ); + SwIndex aIdx( this, nStart ); + EraseText( aIdx, 1 ); + } + } + + if ( bHiddenPara ) + { + SetCalcHiddenParaField(); + } + + return bRet; +} + + +/************************************************************************* + * SwTxtNode::DeleteAttribute() + *************************************************************************/ + +void SwTxtNode::DeleteAttribute( SwTxtAttr * const pAttr ) +{ + if ( !HasHints() ) + { + ASSERT(false, "DeleteAttribute called, but text node without hints?"); + return; + } + + if ( pAttr->HasDummyChar() ) + { + // Unbedingt Copy-konstruieren! + const SwIndex aIdx( this, *pAttr->GetStart() ); + // erase the CH_TXTATR, which will also delete pAttr + EraseText( aIdx, 1 ); + } + else + { + // create MsgHint before start/end become invalid + SwUpdateAttr aHint( + *pAttr->GetStart(), *pAttr->GetEnd(), pAttr->Which() ); + m_pSwpHints->Delete( pAttr ); + SwTxtAttr::Destroy( pAttr, GetDoc()->GetAttrPool() ); + SwModify::Modify( 0, &aHint ); // notify Frames + + TryDeleteSwpHints(); + } +} + +/************************************************************************* + * SwTxtNode::DeleteAttributes() + *************************************************************************/ + +//FIXME: this does NOT respect SORT NUMBER (for CHARFMT)! +void SwTxtNode::DeleteAttributes( const USHORT nWhich, + const xub_StrLen nStart, const xub_StrLen nEnd ) +{ + if ( !HasHints() ) + return; + + for ( USHORT nPos = 0; m_pSwpHints && nPos < m_pSwpHints->Count(); nPos++ ) + { + SwTxtAttr * const pTxtHt = m_pSwpHints->GetTextHint( nPos ); + const xub_StrLen nHintStart = *(pTxtHt->GetStart()); + if (nStart < nHintStart) + { + break; // sorted by start + } + else if ( (nStart == nHintStart) && (nWhich == pTxtHt->Which()) ) + { + if ( nWhich == RES_CHRATR_HIDDEN ) + { + ASSERT(false, "hey, that's a CHRATR! how did that get in?"); + SetCalcHiddenCharFlags(); + } + else if ( nWhich == RES_TXTATR_CHARFMT ) + { + // Check if character format contains hidden attribute: + const SwCharFmt* pFmt = pTxtHt->GetCharFmt().GetCharFmt(); + const SfxPoolItem* pItem; + if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, TRUE, &pItem ) ) + SetCalcHiddenCharFlags(); + } + // --> FME 2007-03-16 #i75430# Recalc hidden flags if necessary + else if ( nWhich == RES_TXTATR_AUTOFMT ) + { + // Check if auto style contains hidden attribute: + const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pTxtHt, RES_CHRATR_HIDDEN ); + if ( pHiddenItem ) + SetCalcHiddenCharFlags(); + } + // <-- + + xub_StrLen const * const pEndIdx = pTxtHt->GetEnd(); + + if ( pTxtHt->HasDummyChar() ) + { + // Unbedingt Copy-konstruieren! + const SwIndex aIdx( this, nStart ); + // erase the CH_TXTATR, which will also delete pTxtHt + EraseText( aIdx, 1 ); + } + else if( *pEndIdx == nEnd ) + { + // den MsgHint jetzt fuettern, weil gleich sind + // Start und End weg. + // Das CalcVisibleFlag bei HiddenParaFields entfaellt, + // da dies das Feld im Dtor selbst erledigt. + SwUpdateAttr aHint( nStart, *pEndIdx, nWhich ); + m_pSwpHints->DeleteAtPos( nPos ); // gefunden, loeschen, + SwTxtAttr::Destroy( pTxtHt, GetDoc()->GetAttrPool() ); + SwModify::Modify( 0, &aHint ); // die Frames benachrichtigen + } + } + } + TryDeleteSwpHints(); +} + +/************************************************************************* + * SwTxtNode::DelSoftHyph() + *************************************************************************/ + +void SwTxtNode::DelSoftHyph( const xub_StrLen nStt, const xub_StrLen nEnd ) +{ + xub_StrLen nFndPos = nStt, nEndPos = nEnd; + while( STRING_NOTFOUND != + ( nFndPos = m_Text.Search( CHAR_SOFTHYPHEN, nFndPos )) && + nFndPos < nEndPos ) + { + const SwIndex aIdx( this, nFndPos ); + EraseText( aIdx, 1 ); + --nEndPos; + } +} + +// setze diese Attribute am TextNode. Wird der gesamte Bereich umspannt, +// dann setze sie nur im AutoAttrSet (SwCntntNode:: SetAttr) +BOOL SwTxtNode::SetAttr( const SfxItemSet& rSet, xub_StrLen nStt, + xub_StrLen nEnd, const SetAttrMode nMode ) +{ + if( !rSet.Count() ) + return FALSE; + + // teil die Sets auf (fuer Selektion in Nodes) + const SfxItemSet* pSet = &rSet; + SfxItemSet aTxtSet( *rSet.GetPool(), RES_TXTATR_BEGIN, RES_TXTATR_END-1 ); + + // gesamter Bereich + if ( !nStt && (nEnd == m_Text.Len()) && + !(nMode & nsSetAttrMode::SETATTR_NOFORMATATTR ) ) + { + // sind am Node schon Zeichenvorlagen gesetzt, muss man diese Attribute + // (rSet) immer als TextAttribute setzen, damit sie angezeigt werden. + int bHasCharFmts = FALSE; + if ( HasHints() ) + { + for ( USHORT n = 0; n < m_pSwpHints->Count(); ++n ) + { + if ( (*m_pSwpHints)[ n ]->IsCharFmtAttr() ) + { + bHasCharFmts = TRUE; + break; + } + } + } + + if( !bHasCharFmts ) + { + aTxtSet.Put( rSet ); + // If there are any character attributes in rSet, + // we want to set them at the paragraph: + if( aTxtSet.Count() != rSet.Count() ) + { + BOOL bRet = SetAttr( rSet ); + if( !aTxtSet.Count() ) + return bRet; + } + + // check for auto style: + const SfxPoolItem* pItem; + const bool bAutoStyle = SFX_ITEM_SET == aTxtSet.GetItemState( RES_TXTATR_AUTOFMT, FALSE, &pItem ); + if ( bAutoStyle ) + { + boost::shared_ptr<SfxItemSet> pAutoStyleSet = static_cast<const SwFmtAutoFmt*>(pItem)->GetStyleHandle(); + BOOL bRet = SetAttr( *pAutoStyleSet ); + if( 1 == aTxtSet.Count() ) + return bRet; + } + + // Continue with the text attributes: + pSet = &aTxtSet; + } + } + + GetOrCreateSwpHints(); + + SfxItemSet aCharSet( *rSet.GetPool(), aCharAutoFmtSetRange ); + + USHORT nCount = 0; + SfxItemIter aIter( *pSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + + do + { + if ( pItem && (reinterpret_cast<SfxPoolItem*>(-1) != pItem)) + { + const USHORT nWhich = pItem->Which(); + ASSERT( isCHRATR(nWhich) || isTXTATR(nWhich), + "SwTxtNode::SetAttr(): unknown attribute" ); + if ( isCHRATR(nWhich) || isTXTATR(nWhich) ) + { + if ((RES_TXTATR_CHARFMT == nWhich) && + (GetDoc()->GetDfltCharFmt() == + static_cast<const SwFmtCharFmt*>(pItem)->GetCharFmt())) + { + SwIndex aIndex( this, nStt ); + RstAttr( aIndex, nEnd - nStt, RES_TXTATR_CHARFMT, 0 ); + DontExpandFmt( aIndex ); + } + else + { + if (isCHRATR(nWhich) || + (RES_TXTATR_UNKNOWN_CONTAINER == nWhich)) + { + aCharSet.Put( *pItem ); + } + else + { + + SwTxtAttr *const pNew = MakeTxtAttr( *GetDoc(), + const_cast<SfxPoolItem&>(*pItem), nStt, nEnd ); + if ( pNew ) + { + if ( nEnd != nStt && !pNew->GetEnd() ) + { + ASSERT(false, + "Attribut without end, but area marked"); + DestroyAttr( pNew ); // do not insert + } + else if ( InsertHint( pNew, nMode ) ) + { + ++nCount; + } + } + } + } + } + } + if ( aIter.IsAtEnd() ) + break; + pItem = aIter.NextItem(); + } while( true ); + + if ( aCharSet.Count() ) + { + SwTxtAttr* pTmpNew = MakeTxtAttr( *GetDoc(), aCharSet, nStt, nEnd ); + if ( InsertHint( pTmpNew, nMode ) ) + { + ++nCount; + } + } + + TryDeleteSwpHints(); + + return nCount ? TRUE : FALSE; +} + +void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr ) +{ + if ( RES_TXTATR_AUTOFMT == rAttr.Which() ) + { + const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr ); + if ( !pCFSet ) + return; + SfxWhichIter aIter( *pCFSet ); + USHORT nWhich = aIter.FirstWhich(); + while( nWhich ) + { + if( ( nWhich < RES_CHRATR_END || + RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) && + ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, TRUE ) ) ) + rSet.Put( pCFSet->Get( nWhich ) ); + nWhich = aIter.NextWhich(); + } + } + else + rSet.Put( rAttr ); +} + +void lcl_MergeAttr_ExpandChrFmt( SfxItemSet& rSet, const SfxPoolItem& rAttr ) +{ + if( RES_TXTATR_CHARFMT == rAttr.Which() || + RES_TXTATR_INETFMT == rAttr.Which() || + RES_TXTATR_AUTOFMT == rAttr.Which() ) + { + const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr ); + + if ( pCFSet ) + { + SfxWhichIter aIter( *pCFSet ); + USHORT nWhich = aIter.FirstWhich(); + while( nWhich ) + { + if( ( nWhich < RES_CHRATR_END || + ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) && + ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, TRUE ) ) ) + rSet.Put( pCFSet->Get( nWhich ) ); + nWhich = aIter.NextWhich(); + } + } + } + + // aufnehmen als MergeWert (falls noch nicht gesetzt neu setzen!) + +/* wenn mehrere Attribute ueberlappen gewinnt der letze !! + z.B + 1234567890123456789 + |------------| Font1 + |------| Font2 + ^ ^ + |--| Abfragebereich: -> Gueltig ist Font2 +*/ + rSet.Put( rAttr ); +} + +struct SwPoolItemEndPair +{ +public: + const SfxPoolItem* mpItem; + xub_StrLen mnEndPos; + + SwPoolItemEndPair() : mpItem( 0 ), mnEndPos( 0 ) {}; +}; + +// --> OD 2008-01-16 #newlistlevelattrs# +void lcl_MergeListLevelIndentAsLRSpaceItem( const SwTxtNode& rTxtNode, + SfxItemSet& rSet ) +{ + if ( rTxtNode.AreListLevelIndentsApplicable() ) + { + const SwNumRule* pRule = rTxtNode.GetNumRule(); + if ( pRule && rTxtNode.GetActualListLevel() >= 0 ) + { + const SwNumFmt& rFmt = pRule->Get(static_cast<USHORT>(rTxtNode.GetActualListLevel())); + if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + SvxLRSpaceItem aLR( RES_LR_SPACE ); + aLR.SetTxtLeft( rFmt.GetIndentAt() ); + aLR.SetTxtFirstLineOfst( static_cast<short>(rFmt.GetFirstLineIndent()) ); + rSet.Put( aLR ); + } + } + } +} + +// erfrage die Attribute vom TextNode ueber den Bereich +// --> OD 2008-01-16 #newlistlevelattrs# +BOOL SwTxtNode::GetAttr( SfxItemSet& rSet, xub_StrLen nStt, xub_StrLen nEnd, + BOOL bOnlyTxtAttr, BOOL bGetFromChrFmt, + const bool bMergeIndentValuesOfNumRule ) const +{ + if( HasHints() ) + { + /* stelle erstmal fest, welche Text-Attribut in dem Bereich gueltig + * sind. Dabei gibt es folgende Faelle: + * UnEindeutig wenn: (wenn != Format-Attribut) + * - das Attribut liegt vollstaendig im Bereich + * - das Attributende liegt im Bereich + * - der Attributanfang liegt im Bereich: + * Eindeutig (im Set mergen): + * - das Attrib umfasst den Bereich + * nichts tun: + * das Attribut liegt ausserhalb des Bereiches + */ + + void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& ) + = bGetFromChrFmt ? &lcl_MergeAttr_ExpandChrFmt + : &lcl_MergeAttr; + + // dann besorge mal die Auto-(Fmt)Attribute + SfxItemSet aFmtSet( *rSet.GetPool(), rSet.GetRanges() ); + if( !bOnlyTxtAttr ) + { + SwCntntNode::GetAttr( aFmtSet ); + // --> OD 2008-01-16 #newlistlevelattrs# + if ( bMergeIndentValuesOfNumRule ) + { + lcl_MergeListLevelIndentAsLRSpaceItem( *this, aFmtSet ); + } + // <-- + } + + const USHORT nSize = m_pSwpHints->Count(); + + if( nStt == nEnd ) // kein Bereich: + { + for (USHORT n = 0; n < nSize; ++n) + { + const SwTxtAttr* pHt = (*m_pSwpHints)[n]; + const xub_StrLen nAttrStart = *pHt->GetStart(); + if( nAttrStart > nEnd ) // ueber den Bereich hinaus + break; + + const xub_StrLen* pAttrEnd = pHt->GetEnd(); + if ( ! pAttrEnd ) // no attributes without end + continue; + + if( ( nAttrStart < nStt && + ( pHt->DontExpand() ? nStt < *pAttrEnd + : nStt <= *pAttrEnd )) || + ( nStt == nAttrStart && + ( nAttrStart == *pAttrEnd || !nStt ))) + (*fnMergeAttr)( rSet, pHt->GetAttr() ); + } + } + else // es ist ein Bereich definiert + { + // --> FME 2007-03-13 #i75299# + ::std::auto_ptr< std::vector< SwPoolItemEndPair > > pAttrArr; + // <-- + + const USHORT coArrSz = static_cast<USHORT>(RES_TXTATR_WITHEND_END) - + static_cast<USHORT>(RES_CHRATR_BEGIN); + + for (USHORT n = 0; n < nSize; ++n) + { + const SwTxtAttr* pHt = (*m_pSwpHints)[n]; + const xub_StrLen nAttrStart = *pHt->GetStart(); + if( nAttrStart > nEnd ) // ueber den Bereich hinaus + break; + + const xub_StrLen* pAttrEnd = pHt->GetEnd(); + if ( ! pAttrEnd ) // no attributes without end + continue; + + BOOL bChkInvalid = FALSE; + if( nAttrStart <= nStt ) // vor oder genau Start + { + if( *pAttrEnd <= nStt ) // liegt davor + continue; + + if( nEnd <= *pAttrEnd ) // hinter oder genau Ende + (*fnMergeAttr)( aFmtSet, pHt->GetAttr() ); + else +// else if( pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) ) + // uneindeutig + bChkInvalid = TRUE; + } + else if( nAttrStart < nEnd // reicht in den Bereich +)// && pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) ) + bChkInvalid = TRUE; + + if( bChkInvalid ) + { + // uneindeutig ? + ::std::auto_ptr< SfxItemIter > pItemIter; + const SfxPoolItem* pItem = 0; + + if ( RES_TXTATR_AUTOFMT == pHt->Which() ) + { + const SfxItemSet* pAutoSet = CharFmt::GetItemSet( pHt->GetAttr() ); + if ( pAutoSet ) + { + pItemIter.reset( new SfxItemIter( *pAutoSet ) ); + pItem = pItemIter->GetCurItem(); + } + } + else + pItem = &pHt->GetAttr(); + + const USHORT nHintEnd = *pAttrEnd; + + while ( pItem ) + { + const USHORT nHintWhich = pItem->Which(); + ASSERT(!isUNKNOWNATR(nHintWhich), + "SwTxtNode::GetAttr(): unkonwn attribute?"); + + if ( !pAttrArr.get() ) + { + pAttrArr.reset( + new std::vector< SwPoolItemEndPair >(coArrSz)); + } + + std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin(); + if (isCHRATR(nHintWhich) || + isTXTATR_WITHEND(nHintWhich)) + { + pPrev += nHintWhich - RES_CHRATR_BEGIN; + } + else + { + pPrev = pAttrArr->end(); + } + +#if OSL_DEBUG_LEVEL > 1 + SwPoolItemEndPair aTmp = *pPrev; +#endif + + if( pPrev != pAttrArr->end() ) + { + if( !pPrev->mpItem ) + { + if ( bOnlyTxtAttr || *pItem != aFmtSet.Get( nHintWhich ) ) + { + if( nAttrStart > nStt ) + { + rSet.InvalidateItem( nHintWhich ); + pPrev->mpItem = (SfxPoolItem*)-1; + } + else + { + pPrev->mpItem = pItem; + pPrev->mnEndPos = nHintEnd; + } + } + } + else if( (SfxPoolItem*)-1 != pPrev->mpItem ) + { + if( pPrev->mnEndPos == nAttrStart && + *pPrev->mpItem == *pItem ) + { + pPrev->mpItem = pItem; + pPrev->mnEndPos = nHintEnd; + } + else + { + rSet.InvalidateItem( nHintWhich ); + pPrev->mpItem = (SfxPoolItem*)-1; + } + } + } + + pItem = ( pItemIter.get() && !pItemIter->IsAtEnd() ) + ? pItemIter->NextItem() : 0; + } // end while + } + } + + if ( pAttrArr.get() ) + { + for (USHORT n = 0; n < coArrSz; ++n) + { + const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ]; + if( (0 != rItemPair.mpItem) && ((SfxPoolItem*)-1 != rItemPair.mpItem) ) + { + const USHORT nWh = + static_cast<USHORT>(n + RES_CHRATR_BEGIN); + + if( nEnd <= rItemPair.mnEndPos ) // hinter oder genau Ende + { + if( *rItemPair.mpItem != aFmtSet.Get( nWh ) ) + (*fnMergeAttr)( rSet, *rItemPair.mpItem ); + } + else + // uneindeutig + rSet.InvalidateItem( nWh ); + } + } + } + } + if( aFmtSet.Count() ) + { + // aus dem Format-Set alle entfernen, die im TextSet auch gesetzt sind + aFmtSet.Differentiate( rSet ); + // jetzt alle zusammen "mergen" + rSet.Put( aFmtSet ); + } + } + else if( !bOnlyTxtAttr ) + { + // dann besorge mal die Auto-(Fmt)Attribute + SwCntntNode::GetAttr( rSet ); + // --> OD 2008-01-16 #newlistlevelattrs# + if ( bMergeIndentValuesOfNumRule ) + { + lcl_MergeListLevelIndentAsLRSpaceItem( *this, rSet ); + } + // <-- + } + + return rSet.Count() ? TRUE : FALSE; +} + +int lcl_IsNewAttrInSet( const SwpHints& rHints, const SfxPoolItem& rItem, + const xub_StrLen nEnd ) +{ + int bIns = TRUE; + for( USHORT i = 0; i < rHints.Count(); ++i ) + { + const SwTxtAttr *pOther = rHints[ i ]; + if( *pOther->GetStart() ) + break; + + if( pOther->GetEnd() && + *pOther->GetEnd() == nEnd && + ( pOther->IsCharFmtAttr() || pOther->Which() == rItem.Which() ) ) + { + bIns = FALSE; + break; + } + } + return bIns; +} + +void SwTxtNode::FmtToTxtAttr( SwTxtNode* pNd ) +{ + SfxItemSet aThisSet( GetDoc()->GetAttrPool(), aCharFmtSetRange ); + if( HasSwAttrSet() && GetpSwAttrSet()->Count() ) + aThisSet.Put( *GetpSwAttrSet() ); + + GetOrCreateSwpHints(); + + if( pNd == this ) + { + if( aThisSet.Count() ) + { + SfxItemIter aIter( aThisSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + std::vector<USHORT> aClearWhichIds; + + while ( true ) + { + if (lcl_IsNewAttrInSet( *m_pSwpHints, *pItem, GetTxt().Len() )) + { + m_pSwpHints->SwpHintsArray::Insert( + MakeTxtAttr( *GetDoc(), + const_cast<SfxPoolItem&>(*pItem), + 0, GetTxt().Len() ) ); + aClearWhichIds.push_back( pItem->Which() ); + } + + if( aIter.IsAtEnd() ) + break; + pItem = aIter.NextItem(); + } + + ClearItemsFromAttrSet( aClearWhichIds ); + } + } + else + { + SfxItemSet aNdSet( pNd->GetDoc()->GetAttrPool(), aCharFmtSetRange ); + if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() ) + aNdSet.Put( *pNd->GetpSwAttrSet() ); + + pNd->GetOrCreateSwpHints(); + + if( aThisSet.Count() ) + { + SfxItemIter aIter( aThisSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem; + std::vector<USHORT> aClearWhichIds; + + while( TRUE ) + { + if( ( SFX_ITEM_SET != aNdSet.GetItemState( pItem->Which(), FALSE, + &pNdItem ) || *pItem != *pNdItem ) && + lcl_IsNewAttrInSet( *m_pSwpHints, *pItem, GetTxt().Len() ) ) + { + m_pSwpHints->SwpHintsArray::Insert( MakeTxtAttr( *GetDoc(), + const_cast<SfxPoolItem&>(*pItem), + 0, GetTxt().Len() ) ); + aClearWhichIds.push_back( pItem->Which() ); + } + aNdSet.ClearItem( pItem->Which() ); + + if( aIter.IsAtEnd() ) + break; + pItem = aIter.NextItem(); + } + ClearItemsFromAttrSet( aClearWhichIds ); + } + + if( aNdSet.Count() ) + { + SfxItemIter aIter( aNdSet ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + std::vector<USHORT> aClearWhichIds; + + while ( true ) + { + if ( lcl_IsNewAttrInSet( *pNd->m_pSwpHints, *pItem, + pNd->GetTxt().Len() ) ) + { + pNd->m_pSwpHints->SwpHintsArray::Insert( + MakeTxtAttr( *pNd->GetDoc(), + const_cast<SfxPoolItem&>(*pItem), + 0, pNd->GetTxt().Len() ) ); + } + aClearWhichIds.push_back( pItem->Which() ); + + if( aIter.IsAtEnd() ) + break; + pItem = aIter.NextItem(); + } + + pNd->ClearItemsFromAttrSet( aClearWhichIds ); + SwFmtChg aTmp1( pNd->GetFmtColl() ); + pNd->SwModify::Modify( &aTmp1, &aTmp1 ); + } + } + + SetCalcHiddenCharFlags(); + + pNd->TryDeleteSwpHints(); +} + +/************************************************************************* + * SwpHints::CalcFlags() + *************************************************************************/ + +void SwpHints::CalcFlags() +{ + m_bDDEFields = m_bFootnote = false; + const USHORT nSize = Count(); + const SwTxtAttr* pAttr; + for( USHORT nPos = 0; nPos < nSize; ++nPos ) + { + switch( ( pAttr = (*this)[ nPos ])->Which() ) + { + case RES_TXTATR_FTN: + m_bFootnote = true; + if ( m_bDDEFields ) + return; + break; + case RES_TXTATR_FIELD: + { + const SwField* pFld = pAttr->GetFld().GetFld(); + if( RES_DDEFLD == pFld->GetTyp()->Which() ) + { + m_bDDEFields = true; + if ( m_bFootnote ) + return; + } + } + break; + } + } +} + +/************************************************************************* + * SwpHints::CalcVisibleFlag() + *************************************************************************/ + +bool SwpHints::CalcHiddenParaField() +{ + m_bCalcHiddenParaField = false; + bool bOldHasHiddenParaField = m_bHasHiddenParaField; + bool bNewHasHiddenParaField = false; + const USHORT nSize = Count(); + const SwTxtAttr *pTxtHt; + + for( USHORT nPos = 0; nPos < nSize; ++nPos ) + { + pTxtHt = (*this)[ nPos ]; + const USHORT nWhich = pTxtHt->Which(); + + if( RES_TXTATR_FIELD == nWhich ) + { + const SwFmtFld& rFld = pTxtHt->GetFld(); + if( RES_HIDDENPARAFLD == rFld.GetFld()->GetTyp()->Which() ) + { + if( !((SwHiddenParaField*)rFld.GetFld())->IsHidden() ) + { + SetHiddenParaField(false); + return bOldHasHiddenParaField != bNewHasHiddenParaField; + } + else + { + bNewHasHiddenParaField = true; + } + } + } + } + SetHiddenParaField( bNewHasHiddenParaField ); + return bOldHasHiddenParaField != bNewHasHiddenParaField; +} + + +/************************************************************************* + * SwpHints::NoteInHistory() + *************************************************************************/ + +void SwpHints::NoteInHistory( SwTxtAttr *pAttr, const bool bNew ) +{ + if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); } +} + +/************************************************************************* + * SwpHints::MergePortions( ) + *************************************************************************/ + +bool SwpHints::MergePortions( SwTxtNode& rNode ) +{ + if ( !Count() ) + return false; + + // sort before merging + SwpHintsArray::Resort(); + + bool bRet = false; + typedef std::multimap< int, SwTxtAttr* > PortionMap; + PortionMap aPortionMap; + xub_StrLen nLastPorStart = STRING_LEN; + USHORT i = 0; + int nKey = 0; + + // get portions by start position: + for ( i = 0; i < Count(); ++i ) + { + SwTxtAttr *pHt = GetTextHint( i ); + if ( RES_TXTATR_CHARFMT != pHt->Which() && + RES_TXTATR_AUTOFMT != pHt->Which() ) + //&& + //RES_TXTATR_INETFMT != pHt->Which() ) + continue; + + const xub_StrLen nPorStart = *pHt->GetStart(); + if ( nPorStart != nLastPorStart && nLastPorStart != STRING_LEN ) + ++nKey; + nLastPorStart = nPorStart; + aPortionMap.insert( std::pair< const int, SwTxtAttr* >( nKey, pHt ) ); + } + + // check if portion i can be merged with portion i+1: + i = 0; + int j = i + 1; + while ( i <= nKey ) + { + std::pair< PortionMap::iterator, PortionMap::iterator > aRange1 = aPortionMap.equal_range( i ); + std::pair< PortionMap::iterator, PortionMap::iterator > aRange2 = aPortionMap.equal_range( j ); + PortionMap::iterator aIter1 = aRange1.first; + PortionMap::iterator aIter2 = aRange2.first; + + bool bMerge = true; + const USHORT nAttributesInPor1 = static_cast<USHORT>(std::distance( aRange1.first, aRange1.second )); + const USHORT nAttributesInPor2 = static_cast<USHORT>(std::distance( aRange2.first, aRange2.second )); + + if ( nAttributesInPor1 == nAttributesInPor2 && nAttributesInPor1 != 0 ) + { + while ( aIter1 != aRange1.second ) + { + const SwTxtAttr* p1 = (*aIter1).second; + const SwTxtAttr* p2 = (*aIter2).second; + if ( *p1->GetEnd() < *p2->GetStart() || p1->Which() != p2->Which() || !(*p1 == *p2) ) + { + bMerge = false; + break; + } + ++aIter1; + ++aIter2; + } + } + else + { + bMerge = false; + } + + if ( bMerge ) + { + // erase all elements with key i + 1 + xub_StrLen nNewPortionEnd = 0; + for ( aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 ) + { + SwTxtAttr* p2 = (*aIter2).second; + nNewPortionEnd = *p2->GetEnd(); + + const USHORT nCountBeforeDelete = Count(); + Delete( p2 ); + + // robust: check if deletion actually took place before destroying attribute: + if ( Count() < nCountBeforeDelete ) + rNode.DestroyAttr( p2 ); + } + aPortionMap.erase( aRange2.first, aRange2.second ); + ++j; + + // change all attributes with key i + aRange1 = aPortionMap.equal_range( i ); + for ( aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 ) + { + SwTxtAttr* p1 = (*aIter1).second; + NoteInHistory( p1 ); + *p1->GetEnd() = nNewPortionEnd; + NoteInHistory( p1, true ); + bRet = true; + } + } + else + { + ++i; + j = i + 1; + } + } + + if ( bRet ) + { + SwpHintsArray::Resort(); + } + + return bRet; +} + +// check if there is already a character format and adjust the sort numbers +void lcl_CheckSortNumber( const SwpHints& rHints, SwTxtCharFmt& rNewCharFmt ) +{ + const xub_StrLen nHtStart = *rNewCharFmt.GetStart(); + const xub_StrLen nHtEnd = *rNewCharFmt.GetEnd(); + USHORT nSortNumber = 0; + + for ( USHORT i = 0; i < rHints.Count(); ++i ) + { + const SwTxtAttr* pOtherHt = rHints[i]; + + const xub_StrLen nOtherStart = *pOtherHt->GetStart(); + + if ( nOtherStart > nHtStart ) + break; + + if ( RES_TXTATR_CHARFMT == pOtherHt->Which() ) + { + const xub_StrLen nOtherEnd = *pOtherHt->GetEnd(); + + if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd ) + { + const USHORT nOtherSortNum = static_cast<const SwTxtCharFmt*>(pOtherHt)->GetSortNumber(); + nSortNumber = nOtherSortNum + 1; + } + } + } + + if ( nSortNumber > 0 ) + rNewCharFmt.SetSortNumber( nSortNumber ); +} + +/************************************************************************* + * SwpHints::Insert() + *************************************************************************/ + +/* + * Try to insert the new hint. + * Depending on the type of the hint, this either always succeeds, or may fail. + * Depending on the type of the hint, other hints may be deleted or + * overwritten. + * The return value indicates successful insertion. + */ +bool SwpHints::TryInsertHint( SwTxtAttr* const pHint, SwTxtNode &rNode, + const SetAttrMode nMode ) +{ + if ( USHRT_MAX == Count() ) // we're sorry, this flight is overbooked... + { + ASSERT(false, "hints array full :-("); + return false; + } + + // Felder bilden eine Ausnahme: + // 1) Sie koennen nie ueberlappen + // 2) Wenn zwei Felder genau aneinander liegen, + // sollen sie nicht zu einem verschmolzen werden. + // Wir koennen also auf die while-Schleife verzichten + + xub_StrLen *pHtEnd = pHint->GetEnd(); + USHORT nWhich = pHint->Which(); + + switch( nWhich ) + { + case RES_TXTATR_CHARFMT: + { + // Check if character format contains hidden attribute: + const SwCharFmt* pFmt = pHint->GetCharFmt().GetCharFmt(); + const SfxPoolItem* pItem; + if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, TRUE, &pItem ) ) + rNode.SetCalcHiddenCharFlags(); + + ((SwTxtCharFmt*)pHint)->ChgTxtNode( &rNode ); + break; + } + // --> FME 2007-03-16 #i75430# Recalc hidden flags if necessary + case RES_TXTATR_AUTOFMT: + { + // Check if auto style contains hidden attribute: + const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pHint, RES_CHRATR_HIDDEN ); + if ( pHiddenItem ) + rNode.SetCalcHiddenCharFlags(); + break; + } + // <-- + case RES_TXTATR_INETFMT: + lcl_InitINetFmt(rNode, static_cast<SwTxtINetFmt*>(pHint)); + break; + case RES_TXTATR_FIELD: + { + BOOL bDelFirst = 0 != ((SwTxtFld*)pHint)->GetpTxtNode(); + ((SwTxtFld*)pHint)->ChgTxtNode( &rNode ); + SwDoc* pDoc = rNode.GetDoc(); + const SwField* pFld = ((SwTxtFld*)pHint)->GetFld().GetFld(); + + if( !pDoc->IsNewFldLst() ) + { + // was fuer ein Feld ist es denn ?? + // bestimmte Felder mussen am Doc das Calculations-Flag updaten + switch( pFld->GetTyp()->Which() ) + { + case RES_DBFLD: + case RES_SETEXPFLD: + case RES_HIDDENPARAFLD: + case RES_HIDDENTXTFLD: + case RES_DBNUMSETFLD: + case RES_DBNEXTSETFLD: + { + if( bDelFirst ) + pDoc->InsDelFldInFldLst( FALSE, *(SwTxtFld*)pHint ); + if( rNode.GetNodes().IsDocNodes() ) + pDoc->InsDelFldInFldLst( TRUE, *(SwTxtFld*)pHint ); + } + break; + case RES_DDEFLD: + if( rNode.GetNodes().IsDocNodes() ) + ((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt(); + break; + } + } + + // gehts ins normale Nodes-Array? + if( rNode.GetNodes().IsDocNodes() ) + { + BOOL bInsFldType = FALSE; + switch( pFld->GetTyp()->Which() ) + { + case RES_SETEXPFLD: + bInsFldType = ((SwSetExpFieldType*)pFld->GetTyp())->IsDeleted(); + if( nsSwGetSetExpType::GSE_SEQ & ((SwSetExpFieldType*)pFld->GetTyp())->GetType() ) + { + // bevor die ReferenzNummer gesetzt wird, sollte + // das Feld am richtigen FeldTypen haengen! + SwSetExpFieldType* pFldType = (SwSetExpFieldType*) + pDoc->InsertFldType( *pFld->GetTyp() ); + if( pFldType != pFld->GetTyp() ) + { + SwFmtFld* pFmtFld = (SwFmtFld*)&((SwTxtFld*)pHint) + ->GetFld(); + pFldType->Add( pFmtFld ); // ummelden + pFmtFld->GetFld()->ChgTyp( pFldType ); + } + pFldType->SetSeqRefNo( *(SwSetExpField*)pFld ); + } + break; + case RES_USERFLD: + bInsFldType = ((SwUserFieldType*)pFld->GetTyp())->IsDeleted(); + break; + + case RES_DDEFLD: + if( pDoc->IsNewFldLst() ) + ((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt(); + bInsFldType = ((SwDDEFieldType*)pFld->GetTyp())->IsDeleted(); + break; + + case RES_POSTITFLD: + if ( pDoc->GetDocShell() ) + pDoc->GetDocShell()->Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFld(), SWFMTFLD_INSERTED ) ); + break; + } + if( bInsFldType ) + pDoc->InsDeletedFldType( *pFld->GetTyp() ); + } + } + break; + case RES_TXTATR_FTN : + ((SwTxtFtn*)pHint)->ChgTxtNode( &rNode ); + break; + case RES_TXTATR_REFMARK: + ((SwTxtRefMark*)pHint)->ChgTxtNode( &rNode ); + if( rNode.GetNodes().IsDocNodes() ) + { + // search for a reference with the same name + SwTxtAttr* pTmpHt; + xub_StrLen *pTmpHtEnd, *pTmpHintEnd; + for( USHORT n = 0, nEnd = Count(); n < nEnd; ++n ) + { + if (RES_TXTATR_REFMARK == (pTmpHt = GetTextHint(n))->Which() && + pHint->GetAttr() == pTmpHt->GetAttr() && + 0 != ( pTmpHtEnd = pTmpHt->GetEnd() ) && + 0 != ( pTmpHintEnd = pHint->GetEnd() ) ) + { + SwComparePosition eCmp = ::ComparePosition( + *pTmpHt->GetStart(), *pTmpHtEnd, + *pHint->GetStart(), *pTmpHintEnd ); + BOOL bDelOld = TRUE, bChgStart = FALSE, bChgEnd = FALSE; + switch( eCmp ) + { + case POS_BEFORE: + case POS_BEHIND: bDelOld = FALSE; break; + + case POS_OUTSIDE: bChgStart = bChgEnd = TRUE; break; + + case POS_COLLIDE_END: + case POS_OVERLAP_BEFORE: bChgStart = TRUE; break; + case POS_COLLIDE_START: + case POS_OVERLAP_BEHIND: bChgEnd = TRUE; break; + default: break; + } + + if( bChgStart ) + *pHint->GetStart() = *pTmpHt->GetStart(); + if( bChgEnd ) + *pTmpHintEnd = *pTmpHtEnd; + + if( bDelOld ) + { + NoteInHistory( pTmpHt ); + rNode.DestroyAttr( Cut( n-- ) ); + --nEnd; + } + } + } + } + break; + case RES_TXTATR_TOXMARK: + ((SwTxtTOXMark*)pHint)->ChgTxtNode( &rNode ); + break; + + case RES_TXTATR_CJK_RUBY: + lcl_InitRuby(rNode, static_cast<SwTxtRuby*>(pHint)); + break; + + case RES_TXTATR_META: + case RES_TXTATR_METAFIELD: + static_cast<SwTxtMeta *>(pHint)->ChgTxtNode( &rNode ); + break; + + case RES_CHRATR_HIDDEN: + rNode.SetCalcHiddenCharFlags(); + break; + } + + if( nsSetAttrMode::SETATTR_DONTEXPAND & nMode ) + pHint->SetDontExpand( TRUE ); + + // SwTxtAttrs ohne Ende werden sonderbehandelt: + // Sie werden natuerlich in das Array insertet, aber sie werden nicht + // in die pPrev/Next/On/Off-Verkettung aufgenommen. + // Der Formatierer erkennt diese TxtHints an dem CH_TXTATR_.. im Text ! + xub_StrLen nHtStart = *pHint->GetStart(); + if( !pHtEnd ) + { + SwpHintsArray::Insert( pHint ); + CalcFlags(); +#ifdef DBG_UTIL + if( !rNode.GetDoc()->IsInReading() ) + CHECK; +#endif + // ... und die Abhaengigen benachrichtigen + if ( rNode.GetDepends() ) + { + SwUpdateAttr aHint( nHtStart, nHtStart, nWhich ); + rNode.Modify( 0, &aHint ); + } + return true; + } + + // ---------------------------------------------------------------- + // Ab hier gibt es nur noch pHint mit einem EndIdx !!! + + if( *pHtEnd < nHtStart ) + { + ASSERT( *pHtEnd >= nHtStart, + "+SwpHints::Insert: invalid hint, end < start" ); + + // Wir drehen den Quatsch einfach um: + *pHint->GetStart() = *pHtEnd; + *pHtEnd = nHtStart; + nHtStart = *pHint->GetStart(); + } + + // I need this value later on for notification but the pointer may become invalid + const xub_StrLen nHintEnd = *pHtEnd; + const bool bNoHintAdjustMode = (nsSetAttrMode::SETATTR_NOHINTADJUST & nMode); + + // handle nesting attributes: inserting may fail due to overlap! + if (pHint->IsNesting()) + { + const bool bRet( + TryInsertNesting(rNode, *static_cast<SwTxtAttrNesting*>(pHint))); + if (!bRet) return false; + } + // Currently REFMARK and TOXMARK have OverlapAllowed set to true. + // These attributes may be inserted directly. + // Also attributes without length may be inserted directly. + // SETATTR_NOHINTADJUST is set e.g., during undo. + // Portion building in not necessary during XML import. + else + if ( !bNoHintAdjustMode && + !pHint->IsOverlapAllowedAttr() && + !rNode.GetDoc()->IsInXMLImport() && + ( RES_TXTATR_AUTOFMT == nWhich || + RES_TXTATR_CHARFMT == nWhich ) ) + { + ASSERT( nWhich != RES_TXTATR_AUTOFMT || + static_cast<const SwFmtAutoFmt&>(pHint->GetAttr()).GetStyleHandle()->GetPool() == + &rNode.GetDoc()->GetAttrPool(), + "AUTOSTYLES - Pool mismatch" ) + + BuildPortions( rNode, *pHint, nMode ); + + if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes + MergePortions( rNode ); + } + else + { + // There may be more than one character style at the current position. + // Take care of the sort number. + // Special case ruby portion: During import, the ruby attribute is set + // multiple times + // Special case hyperlink: During import, the ruby attribute is set + // multiple times + // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert + // character attributes directly + if ( ( RES_TXTATR_CHARFMT == nWhich && !bNoHintAdjustMode ) ) + { + BuildPortions( rNode, *pHint, nMode ); + } + else + { + // --> FME 2007-11-08 #i82989# Check sort numbers in NoHintAdjustMode + if ( RES_TXTATR_CHARFMT == nWhich ) + lcl_CheckSortNumber( *this, *static_cast<SwTxtCharFmt*>(pHint) ); + // <-- + + SwpHintsArray::Insert( pHint ); + NoteInHistory( pHint, true ); + } + } + + // ... und die Abhaengigen benachrichtigen + if ( rNode.GetDepends() ) + { + SwUpdateAttr aHint( nHtStart, nHtStart == nHintEnd ? nHintEnd + 1 : nHintEnd, nWhich ); + rNode.Modify( 0, &aHint ); + } + +#ifdef DBG_UTIL + if( !bNoHintAdjustMode && !rNode.GetDoc()->IsInReading() ) + CHECK; +#endif + + return true; +} + +/************************************************************************* + * SwpHints::DeleteAtPos() + *************************************************************************/ + +void SwpHints::DeleteAtPos( const USHORT nPos ) +{ + SwTxtAttr *pHint = GetTextHint(nPos); + // ChainDelete( pHint ); + NoteInHistory( pHint ); + SwpHintsArray::DeleteAtPos( nPos ); + + if( RES_TXTATR_FIELD == pHint->Which() ) + { + SwFieldType* pFldTyp = ((SwTxtFld*)pHint)->GetFld().GetFld()->GetTyp(); + if( RES_DDEFLD == pFldTyp->Which() ) + { + const SwTxtNode* pNd = ((SwTxtFld*)pHint)->GetpTxtNode(); + if( pNd && pNd->GetNodes().IsDocNodes() ) + ((SwDDEFieldType*)pFldTyp)->DecRefCnt(); + ((SwTxtFld*)pHint)->ChgTxtNode( 0 ); + } + else if( RES_POSTITFLD == pFldTyp->Which() ) + { + const_cast<SwFmtFld&>(((SwTxtFld*)pHint)->GetFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFld(), SWFMTFLD_REMOVED ) ); + } + else if ( m_bHasHiddenParaField && + RES_HIDDENPARAFLD == pFldTyp->Which() ) + { + m_bCalcHiddenParaField = true; + } + } + + CalcFlags(); + CHECK; +} + +// Ist der Hint schon bekannt, dann suche die Position und loesche ihn. +// Ist er nicht im Array, so gibt es ein ASSERT !! + +void SwpHints::Delete( SwTxtAttr* pTxtHt ) +{ + // Attr 2.0: SwpHintsArr::Delete( pTxtHt ); + const USHORT nPos = GetStartOf( pTxtHt ); + ASSERT( USHRT_MAX != nPos, "Attribut nicht im Attribut-Array!" ); + if( USHRT_MAX != nPos ) + DeleteAtPos( nPos ); +} + +void SwTxtNode::ClearSwpHintsArr( bool bDelFields ) +{ + if ( HasHints() ) + { + USHORT nPos = 0; + while ( nPos < m_pSwpHints->Count() ) + { + SwTxtAttr* pDel = m_pSwpHints->GetTextHint( nPos ); + bool bDel = false; + + switch( pDel->Which() ) + { + case RES_TXTATR_FLYCNT: + case RES_TXTATR_FTN: + break; + + case RES_TXTATR_FIELD: + if( bDelFields ) + bDel = true; + break; + default: + bDel = true; break; + } + + if( bDel ) + { + m_pSwpHints->SwpHintsArray::DeleteAtPos( nPos ); + DestroyAttr( pDel ); + } + else + ++nPos; + } + } +} + +USHORT SwTxtNode::GetLang( const xub_StrLen nBegin, const xub_StrLen nLen, + USHORT nScript ) const +{ + USHORT nRet = LANGUAGE_DONTKNOW; + + if ( ! nScript ) + { + nScript = pBreakIt->GetRealScriptOfText( m_Text, nBegin ); + } + + // --> FME 2008-09-29 #i91465# hennerdrewes: Consider nScript if pSwpHints == 0 + const USHORT nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript ); + // <-- + + if ( HasHints() ) + { + const xub_StrLen nEnd = nBegin + nLen; + for ( USHORT i = 0, nSize = m_pSwpHints->Count(); i < nSize; ++i ) + { + // ist der Attribut-Anfang schon groesser als der Idx ? + const SwTxtAttr *pHt = m_pSwpHints->operator[](i); + const xub_StrLen nAttrStart = *pHt->GetStart(); + if( nEnd < nAttrStart ) + break; + + const USHORT nWhich = pHt->Which(); + + if( nWhichId == nWhich || + ( ( pHt->IsCharFmtAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFmt::IsItemIncluded( nWhichId, pHt ) ) ) + { + const xub_StrLen *pEndIdx = pHt->GetEnd(); + // Ueberlappt das Attribut den Bereich? + + if( pEndIdx && + nLen ? ( nAttrStart < nEnd && nBegin < *pEndIdx ) + : (( nAttrStart < nBegin && + ( pHt->DontExpand() ? nBegin < *pEndIdx + : nBegin <= *pEndIdx )) || + ( nBegin == nAttrStart && + ( nAttrStart == *pEndIdx || !nBegin ))) ) + { + const SfxPoolItem* pItem = CharFmt::GetItem( *pHt, nWhichId ); + USHORT nLng = ((SvxLanguageItem*)pItem)->GetLanguage(); + + // Umfasst das Attribut den Bereich komplett? + if( nAttrStart <= nBegin && nEnd <= *pEndIdx ) + nRet = nLng; + else if( LANGUAGE_DONTKNOW == nRet ) + nRet = nLng; // partielle Ueberlappung, der 1. gewinnt + } + } + } + } + if( LANGUAGE_DONTKNOW == nRet ) + { + nRet = ((SvxLanguageItem&)GetSwAttrSet().Get( nWhichId )).GetLanguage(); + if( LANGUAGE_DONTKNOW == nRet ) + nRet = static_cast<USHORT>(GetAppLanguage()); + } + return nRet; +} + + +sal_Unicode GetCharOfTxtAttr( const SwTxtAttr& rAttr ) +{ + sal_Unicode cRet = CH_TXTATR_BREAKWORD; + switch ( rAttr.Which() ) + { + case RES_TXTATR_FTN: + case RES_TXTATR_REFMARK: + case RES_TXTATR_TOXMARK: + case RES_TXTATR_META: + case RES_TXTATR_METAFIELD: + cRet = CH_TXTATR_INWORD; + break; + + case RES_TXTATR_FIELD: + case RES_TXTATR_FLYCNT: + cRet = CH_TXTATR_BREAKWORD; + break; + + default: + ASSERT(false, "GetCharOfTxtAttr: unknown attr"); + break; + } + return cRet; +} + + diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx new file mode 100644 index 000000000000..f79db265d4fd --- /dev/null +++ b/sw/source/core/txtnode/txatbase.cxx @@ -0,0 +1,84 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <svl/itempool.hxx> +#include <txatbase.hxx> +#include <fmtfld.hxx> +#include <docufld.hxx> + +SwTxtAttr::SwTxtAttr( SfxPoolItem& rAttr, xub_StrLen nStart ) + : m_pAttr( &rAttr ) + , m_nStart( nStart ) + , m_bDontExpand( false ) + , m_bLockExpandFlag( false ) + , m_bDontMoveAttr( false ) + , m_bCharFmtAttr( false ) + , m_bOverlapAllowedAttr( false ) + , m_bPriorityAttr( false ) + , m_bDontExpandStart( false ) + , m_bNesting( false ) + , m_bHasDummyChar( false ) +{ +} + +SwTxtAttr::~SwTxtAttr( ) +{ +} + +xub_StrLen* SwTxtAttr::GetEnd() +{ + return 0; +} + +void SwTxtAttr::Destroy( SwTxtAttr * pToDestroy, SfxItemPool& rPool ) +{ + if (!pToDestroy) return; + SfxPoolItem * const pAttr = pToDestroy->m_pAttr; + delete pToDestroy; + rPool.Remove( *pAttr ); +} + +int SwTxtAttr::operator==( const SwTxtAttr& rAttr ) const +{ + return GetAttr() == rAttr.GetAttr(); +} + +SwTxtAttrEnd::SwTxtAttrEnd( SfxPoolItem& rAttr, + xub_StrLen nStart, xub_StrLen nEnd ) : + SwTxtAttr( rAttr, nStart ), m_nEnd( nEnd ) +{ +} + +xub_StrLen* SwTxtAttrEnd::GetEnd() +{ + return & m_nEnd; +} + diff --git a/sw/source/core/txtnode/txatritr.cxx b/sw/source/core/txtnode/txatritr.cxx new file mode 100644 index 000000000000..48ce2462cf2b --- /dev/null +++ b/sw/source/core/txtnode/txatritr.cxx @@ -0,0 +1,246 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <hintids.hxx> + +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <tools/string.hxx> +#include <editeng/langitem.hxx> +#include <txatritr.hxx> +#include <fchrfmt.hxx> +#include <charfmt.hxx> +#include <breakit.hxx> +#include <ndtxt.hxx> +#include <txatbase.hxx> + +using namespace ::com::sun::star::i18n; + + +SwScriptIterator::SwScriptIterator( const String& rStr, xub_StrLen nStt, sal_Bool bFrwrd ) + : rText( rStr ), + nChgPos( rStr.Len() ), + nCurScript( ScriptType::WEAK ), + bForward( bFrwrd ) +{ + if( pBreakIt->GetBreakIter().is() ) + { + if ( ! bFrwrd && nStt ) + --nStt; + + xub_StrLen nPos = nStt; + nCurScript = pBreakIt->GetBreakIter()->getScriptType( rText, nPos ); + if( ScriptType::WEAK == nCurScript ) + { + if( nPos ) + { + nPos = (xub_StrLen)pBreakIt->GetBreakIter()->beginOfScript( + rText, nPos, nCurScript ); + if( nPos && nPos < rText.Len() ) + { + nStt = --nPos; + nCurScript = pBreakIt->GetBreakIter()->getScriptType( rText,nPos); + } + } + } + + nChgPos = bForward ? + (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rText, nStt, nCurScript ) : + (xub_StrLen)pBreakIt->GetBreakIter()->beginOfScript( rText, nStt, nCurScript ); + } +} + +sal_Bool SwScriptIterator::Next() +{ + sal_Bool bRet = sal_False; + if( pBreakIt->GetBreakIter().is() ) + { + if ( bForward && nChgPos < rText.Len() ) + { + nCurScript = pBreakIt->GetBreakIter()->getScriptType( rText, nChgPos ); + nChgPos = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( + rText, nChgPos, nCurScript ); + bRet = sal_True; + } + else if ( ! bForward && nChgPos ) + { + --nChgPos; + nCurScript = pBreakIt->GetBreakIter()->getScriptType( rText, nChgPos ); + nChgPos = (xub_StrLen)pBreakIt->GetBreakIter()->beginOfScript( + rText, nChgPos, nCurScript ); + bRet = sal_True; + } + } + else + nChgPos = rText.Len(); + return bRet; +} + +// -------------------------------------------------------------------- + +SwTxtAttrIterator::SwTxtAttrIterator( const SwTxtNode& rTNd, USHORT nWhchId, + xub_StrLen nStt, + sal_Bool bUseGetWhichOfScript ) + : aSIter( rTNd.GetTxt(), nStt ), rTxtNd( rTNd ), + pParaItem( 0 ), nChgPos( nStt ), nAttrPos( 0 ), nWhichId( nWhchId ), + bIsUseGetWhichOfScript( bUseGetWhichOfScript ) +{ + SearchNextChg(); +} + +sal_Bool SwTxtAttrIterator::Next() +{ + sal_Bool bRet = sal_False; + if( nChgPos < aSIter.GetText().Len() ) + { + bRet = sal_True; + if( aStack.Count() ) + { + do { + const SwTxtAttr* pHt = (SwTxtAttr*)aStack[ 0 ]; + USHORT nEndPos = *pHt->GetEnd(); + if( nChgPos >= nEndPos ) + aStack.Remove( 0 ); + else + break; + } while( aStack.Count() ); + } + + if( aStack.Count() ) + { + sal_uInt16 nSavePos = nAttrPos; + SearchNextChg(); + if( aStack.Count() ) + { + const SwTxtAttr* pHt = (SwTxtAttr*)aStack[ 0 ]; + USHORT nEndPos = *pHt->GetEnd(); + if( nChgPos >= nEndPos ) + { + nChgPos = nEndPos; + nAttrPos = nSavePos; + + if( RES_TXTATR_CHARFMT == pHt->Which() ) + { + sal_uInt16 nWId = bIsUseGetWhichOfScript ? + GetWhichOfScript( nWhichId, + aSIter.GetCurrScript() ) : nWhichId; + pCurItem = &pHt->GetCharFmt().GetCharFmt()->GetFmtAttr(nWId); + } + else + pCurItem = &pHt->GetAttr(); + + aStack.Remove( 0 ); + } + } + } + else + SearchNextChg(); + } + return bRet; +} + +void SwTxtAttrIterator::AddToStack( const SwTxtAttr& rAttr ) +{ + void* pAdd = (void*)&rAttr; + USHORT nIns = 0, nEndPos = *rAttr.GetEnd(); + for( ; nIns < aStack.Count(); ++nIns ) + if( *((SwTxtAttr*)aStack[ nIns ] )->GetEnd() > nEndPos ) + break; + + aStack.Insert( pAdd, nIns ); +} + +void SwTxtAttrIterator::SearchNextChg() +{ + USHORT nWh = 0; + if( nChgPos == aSIter.GetScriptChgPos() ) + { + aSIter.Next(); + pParaItem = 0; + nAttrPos = 0; // must be restart at the beginning, because + // some attributes can start before or inside + // the current scripttype! + aStack.Remove( 0, aStack.Count() ); + } + if( !pParaItem ) + { + nWh = bIsUseGetWhichOfScript ? + GetWhichOfScript( nWhichId, + aSIter.GetCurrScript() ) : nWhichId; + pParaItem = &rTxtNd.GetSwAttrSet().Get( nWh ); + } + + xub_StrLen nStt = nChgPos; + nChgPos = aSIter.GetScriptChgPos(); + pCurItem = pParaItem; + + const SwpHints* pHts = rTxtNd.GetpSwpHints(); + if( pHts ) + { + if( !nWh ) + { + nWh = bIsUseGetWhichOfScript ? + GetWhichOfScript( nWhichId, + aSIter.GetCurrScript() ) : nWhichId; + } + + const SfxPoolItem* pItem = 0; + for( ; nAttrPos < pHts->Count(); ++nAttrPos ) + { + const SwTxtAttr* pHt = (*pHts)[ nAttrPos ]; + const USHORT* pEnd = pHt->GetEnd(); + const USHORT nHtStt = *pHt->GetStart(); + if( nHtStt < nStt && ( !pEnd || *pEnd <= nStt )) + continue; + + if( nHtStt >= nChgPos ) + break; + + pItem = CharFmt::GetItem( *pHt, nWh ); + if ( pItem ) + { + if( nHtStt > nStt ) + { + if( nChgPos > nHtStt ) + nChgPos = nHtStt; + break; + } + AddToStack( *pHt ); + pCurItem = pItem; + if( *pEnd < nChgPos ) + nChgPos = *pEnd; + } + } + } +} + + diff --git a/sw/source/core/txtnode/txtatr2.cxx b/sw/source/core/txtnode/txtatr2.cxx new file mode 100644 index 000000000000..5499eeab4439 --- /dev/null +++ b/sw/source/core/txtnode/txtatr2.cxx @@ -0,0 +1,338 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +#include <hintids.hxx> +#include <hints.hxx> +#include <sfx2/objsh.hxx> +#include <editeng/xmlcnitm.hxx> +#include <editeng/twolinesitem.hxx> +#include <txtinet.hxx> +#include <txtatr.hxx> +#include <fchrfmt.hxx> +#include <fmtinfmt.hxx> +#include <charfmt.hxx> +#include <ndtxt.hxx> // SwCharFmt, SwTxtNode +#include <poolfmt.hxx> // RES_POOLCHR_INET_... +#include <doc.hxx> // SwDoc +#include <fmtruby.hxx> +#include <fmtmeta.hxx> + + +TYPEINIT1(SwTxtINetFmt,SwClient); +TYPEINIT1(SwTxtRuby,SwClient); + + +/************************************************************************* + * class SwTxtCharFmt + *************************************************************************/ + +SwTxtCharFmt::SwTxtCharFmt( SwFmtCharFmt& rAttr, + xub_StrLen nStt, xub_StrLen nEnde ) + : SwTxtAttrEnd( rAttr, nStt, nEnde ) + , m_pTxtNode( 0 ) + , m_nSortNumber( 0 ) +{ + rAttr.pTxtAttr = this; + SetCharFmtAttr( TRUE ); +} + +SwTxtCharFmt::~SwTxtCharFmt( ) +{ +} + +void SwTxtCharFmt::Modify( SfxPoolItem* pOld, SfxPoolItem* pNew ) +{ + USHORT nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + ASSERT( isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich) + || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich), + "SwTxtCharFmt::Modify(): unknown Modify"); + + if ( m_pTxtNode ) + { + SwUpdateAttr aUpdateAttr( *GetStart(), *GetEnd(), nWhich ); + m_pTxtNode->Modify( &aUpdateAttr, &aUpdateAttr ); + } +} + + // erfrage vom Modify Informationen +BOOL SwTxtCharFmt::GetInfo( SfxPoolItem& rInfo ) const +{ + if ( RES_AUTOFMT_DOCNODE != rInfo.Which() || !m_pTxtNode || + &m_pTxtNode->GetNodes() != static_cast<SwAutoFmtGetDocNode&>(rInfo).pNodes ) + { + return TRUE; + } + + static_cast<SwAutoFmtGetDocNode&>(rInfo).pCntntNode = m_pTxtNode; + return FALSE; +} + + +/************************************************************************* + * class SwTxtAttrNesting + *************************************************************************/ + +SwTxtAttrNesting::SwTxtAttrNesting( SfxPoolItem & i_rAttr, + const xub_StrLen i_nStart, const xub_StrLen i_nEnd ) + : SwTxtAttrEnd( i_rAttr, i_nStart, i_nEnd ) +{ + SetDontExpand( true ); // never expand this attribute + // lock the expand flag: simple guarantee that nesting will not be + // invalidated by expand operations + SetLockExpandFlag( true ); + SetDontExpandStartAttr( true ); + SetNesting( true ); +} + +SwTxtAttrNesting::~SwTxtAttrNesting() +{ +} + + +/************************************************************************* + * class SwTxtINetFmt + *************************************************************************/ + +SwTxtINetFmt::SwTxtINetFmt( SwFmtINetFmt& rAttr, + xub_StrLen nStart, xub_StrLen nEnd ) + : SwTxtAttrNesting( rAttr, nStart, nEnd ) + , SwClient( 0 ) + , m_pTxtNode( 0 ) + , m_bVisited( false ) + , m_bVisitedValid( false ) +{ + rAttr.pTxtAttr = this; + SetCharFmtAttr( true ); +} + +SwTxtINetFmt::~SwTxtINetFmt( ) +{ +} + +SwCharFmt* SwTxtINetFmt::GetCharFmt() +{ + const SwFmtINetFmt& rFmt = SwTxtAttrEnd::GetINetFmt(); + SwCharFmt* pRet = NULL; + + if( rFmt.GetValue().Len() ) + { + const SwDoc* pDoc = GetTxtNode().GetDoc(); + if( !IsVisitedValid() ) + { + SetVisited( pDoc->IsVisitedURL( rFmt.GetValue() ) ); + SetVisitedValid( true ); + } + USHORT nId; + const String& rStr = IsVisited() ? rFmt.GetVisitedFmt() + : rFmt.GetINetFmt(); + if( rStr.Len() ) + nId = IsVisited() ? rFmt.GetVisitedFmtId() : rFmt.GetINetFmtId(); + else + nId = static_cast<USHORT>(IsVisited() ? RES_POOLCHR_INET_VISIT : RES_POOLCHR_INET_NORMAL); + + // JP 10.02.2000, Bug 72806: dont modify the doc for getting the + // correct charstyle. + BOOL bResetMod = !pDoc->IsModified(); + Link aOle2Lnk; + if( bResetMod ) + { + aOle2Lnk = pDoc->GetOle2Link(); + ((SwDoc*)pDoc)->SetOle2Link( Link() ); + } + + pRet = IsPoolUserFmt( nId ) + ? ((SwDoc*)pDoc)->FindCharFmtByName( rStr ) + : ((SwDoc*)pDoc)->GetCharFmtFromPool( nId ); + + if( bResetMod ) + { + ((SwDoc*)pDoc)->ResetModified(); + ((SwDoc*)pDoc)->SetOle2Link( aOle2Lnk ); + } + } + + if( pRet ) + pRet->Add( this ); + else if( GetRegisteredIn() ) + pRegisteredIn->Remove( this ); + + return pRet; +} + +void SwTxtINetFmt::Modify( SfxPoolItem* pOld, SfxPoolItem* pNew ) +{ + USHORT nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + ASSERT( isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich) + || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich), + "SwTxtINetFmt::Modify(): unknown Modify"); + + if ( m_pTxtNode ) + { + SwUpdateAttr aUpdateAttr( *GetStart(), *GetEnd(), nWhich ); + m_pTxtNode->Modify( &aUpdateAttr, &aUpdateAttr ); + } +} + + // erfrage vom Modify Informationen +BOOL SwTxtINetFmt::GetInfo( SfxPoolItem& rInfo ) const +{ + if ( RES_AUTOFMT_DOCNODE != rInfo.Which() || !m_pTxtNode || + &m_pTxtNode->GetNodes() != static_cast<SwAutoFmtGetDocNode&>(rInfo).pNodes ) + { + return TRUE; + } + + static_cast<SwAutoFmtGetDocNode&>(rInfo).pCntntNode = m_pTxtNode; + return FALSE; +} + +BOOL SwTxtINetFmt::IsProtect( ) const +{ + return m_pTxtNode && m_pTxtNode->IsProtect(); +} + +/************************************************************************* + * class SwTxtRuby + *************************************************************************/ + +SwTxtRuby::SwTxtRuby( SwFmtRuby& rAttr, + xub_StrLen nStart, xub_StrLen nEnd ) + : SwTxtAttrNesting( rAttr, nStart, nEnd ) + , SwClient( 0 ) + , m_pTxtNode( 0 ) +{ + rAttr.pTxtAttr = this; +} + +SwTxtRuby::~SwTxtRuby() +{ +} + +void SwTxtRuby::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew ) +{ + USHORT nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + ASSERT( isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich) + || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich), + "SwTxtRuby::Modify(): unknown Modify"); + + if ( m_pTxtNode ) + { + SwUpdateAttr aUpdateAttr( *GetStart(), *GetEnd(), nWhich ); + m_pTxtNode->Modify( &aUpdateAttr, &aUpdateAttr ); + } +} + +BOOL SwTxtRuby::GetInfo( SfxPoolItem& rInfo ) const +{ + if( RES_AUTOFMT_DOCNODE != rInfo.Which() || !m_pTxtNode || + &m_pTxtNode->GetNodes() != static_cast<SwAutoFmtGetDocNode&>(rInfo).pNodes ) + { + return TRUE; + } + + static_cast<SwAutoFmtGetDocNode&>(rInfo).pCntntNode = m_pTxtNode; + return FALSE; +} + +SwCharFmt* SwTxtRuby::GetCharFmt() +{ + const SwFmtRuby& rFmt = SwTxtAttrEnd::GetRuby(); + SwCharFmt* pRet = 0; + + if( rFmt.GetText().Len() ) + { + const SwDoc* pDoc = GetTxtNode().GetDoc(); + const String& rStr = rFmt.GetCharFmtName(); + USHORT nId = RES_POOLCHR_RUBYTEXT; + if ( rStr.Len() ) + nId = rFmt.GetCharFmtId(); + + // JP 10.02.2000, Bug 72806: dont modify the doc for getting the + // correct charstyle. + BOOL bResetMod = !pDoc->IsModified(); + Link aOle2Lnk; + if( bResetMod ) + { + aOle2Lnk = pDoc->GetOle2Link(); + ((SwDoc*)pDoc)->SetOle2Link( Link() ); + } + + pRet = IsPoolUserFmt( nId ) + ? ((SwDoc*)pDoc)->FindCharFmtByName( rStr ) + : ((SwDoc*)pDoc)->GetCharFmtFromPool( nId ); + + if( bResetMod ) + { + ((SwDoc*)pDoc)->ResetModified(); + ((SwDoc*)pDoc)->SetOle2Link( aOle2Lnk ); + } + } + + if( pRet ) + pRet->Add( this ); + else if( GetRegisteredIn() ) + pRegisteredIn->Remove( this ); + + return pRet; +} + + +/************************************************************************* + * class SwTxtMeta + *************************************************************************/ + +SwTxtMeta::SwTxtMeta( SwFmtMeta & i_rAttr, + const xub_StrLen i_nStart, const xub_StrLen i_nEnd ) + : SwTxtAttrNesting( i_rAttr, i_nStart, i_nEnd ) + , m_pTxtNode( 0 ) +{ + i_rAttr.SetTxtAttr( this ); + SetHasDummyChar(true); +} + +SwTxtMeta::~SwTxtMeta() +{ + SwFmtMeta & rFmtMeta( static_cast<SwFmtMeta &>(GetAttr()) ); + if (rFmtMeta.GetTxtAttr() == this) + { + rFmtMeta.SetTxtAttr(0); + } +} + +void SwTxtMeta::ChgTxtNode(SwTxtNode * const pNode) +{ + m_pTxtNode = pNode; // before Notify! + SwFmtMeta & rFmtMeta( static_cast<SwFmtMeta &>(GetAttr()) ); + if (rFmtMeta.GetTxtAttr() == this) + { + rFmtMeta.NotifyChangeTxtNode(pNode); + } +} + diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx new file mode 100644 index 000000000000..944eb70b2c3d --- /dev/null +++ b/sw/source/core/txtnode/txtedt.cxx @@ -0,0 +1,1965 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + +// So kann man die Linguistik-Statistik ( (Tmp-Path)\swlingu.stk ) aktivieren: +//#define LINGU_STATISTIK +#ifdef LINGU_STATISTIK + #include <stdio.h> // in SwLinguStatistik::DTOR + #include <stdlib.h> // getenv() + #include <time.h> // clock() + #include <tools/stream.hxx> +#endif +#include <hintids.hxx> +#include <vcl/svapp.hxx> +#include <svl/itemiter.hxx> +#include <editeng/splwrap.hxx> +#include <editeng/langitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/scripttypeitem.hxx> +#include <editeng/hangulhanja.hxx> +#include <SwSmartTagMgr.hxx> +#include <linguistic/lngprops.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/i18n/WordType.hdl> +#include <com/sun/star/i18n/ScriptType.hdl> +#include <unotools/transliterationwrapper.hxx> +#include <unotools/charclass.hxx> +#include <dlelstnr.hxx> +#include <swmodule.hxx> +#include <splargs.hxx> +#include <viewopt.hxx> +#include <acmplwrd.hxx> +#include <doc.hxx> // GetDoc() +#include <docsh.hxx> +#include <txtfld.hxx> +#include <fmtfld.hxx> +#include <txatbase.hxx> +#include <charatr.hxx> +#include <fldbas.hxx> +#include <pam.hxx> +#include <hints.hxx> +#include <ndtxt.hxx> +#include <txtfrm.hxx> +#include <SwGrammarMarkUp.hxx> + +#include <txttypes.hxx> +#include <breakit.hxx> +#include <crstate.hxx> +#include <undobj.hxx> +#include <txatritr.hxx> +#include <redline.hxx> // SwRedline +#include <docary.hxx> // SwRedlineTbl +#include <scriptinfo.hxx> +#include <docstat.hxx> +#include <editsh.hxx> +#include <unotextmarkup.hxx> +#include <txtatr.hxx> +#include <fmtautofmt.hxx> +#include <istyleaccess.hxx> + +#include <unomid.h> + +using rtl::OUString; +using namespace ::com::sun::star; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::smarttags; + +// Wir ersparen uns in Hyphenate ein GetFrm() +// Achtung: in edlingu.cxx stehen die Variablen! +extern const SwTxtNode *pLinguNode; +extern SwTxtFrm *pLinguFrm; + +bool lcl_IsSkippableWhiteSpace( xub_Unicode cCh ) +{ + return 0x3000 == cCh || + ' ' == cCh || + '\t' == cCh || + 0x0a == cCh; +} + +/* + * This has basically the same function as SwScriptInfo::MaskHiddenRanges, + * only for deleted redlines + */ + +USHORT lcl_MaskRedlines( const SwTxtNode& rNode, XubString& rText, + const xub_StrLen nStt, const xub_StrLen nEnd, + const xub_Unicode cChar ) +{ + USHORT nNumOfMaskedRedlines = 0; + + const SwDoc& rDoc = *rNode.GetDoc(); + USHORT nAct = rDoc.GetRedlinePos( rNode, USHRT_MAX ); + + for ( ; nAct < rDoc.GetRedlineTbl().Count(); nAct++ ) + { + const SwRedline* pRed = rDoc.GetRedlineTbl()[ nAct ]; + + if ( pRed->Start()->nNode > rNode.GetIndex() ) + break; + + if( nsRedlineType_t::REDLINE_DELETE == pRed->GetType() ) + { + xub_StrLen nRedlineEnd; + xub_StrLen nRedlineStart; + + pRed->CalcStartEnd( rNode.GetIndex(), nRedlineStart, nRedlineEnd ); + + if ( nRedlineEnd < nStt || nRedlineStart > nEnd ) + continue; + + while ( nRedlineStart < nRedlineEnd && nRedlineStart < nEnd ) + { + if ( nRedlineStart >= nStt && nRedlineStart < nEnd ) + { + rText.SetChar( nRedlineStart, cChar ); + ++nNumOfMaskedRedlines; + } + ++nRedlineStart; + } + } + } + + return nNumOfMaskedRedlines; +} + +/* + * Used for spell checking. Deleted redlines and hidden characters are masked + */ + +USHORT lcl_MaskRedlinesAndHiddenText( const SwTxtNode& rNode, XubString& rText, + const xub_StrLen nStt, const xub_StrLen nEnd, + const xub_Unicode cChar = CH_TXTATR_INWORD, + bool bCheckShowHiddenChar = true ) +{ + USHORT nRedlinesMasked = 0; + USHORT nHiddenCharsMasked = 0; + + const SwDoc& rDoc = *rNode.GetDoc(); + const bool bShowChg = 0 != IDocumentRedlineAccess::IsShowChanges( rDoc.GetRedlineMode() ); + + // If called from word count or from spell checking, deleted redlines + // should be masked: + if ( bShowChg ) + { + nRedlinesMasked = lcl_MaskRedlines( rNode, rText, nStt, nEnd, cChar ); + } + + const bool bHideHidden = !SW_MOD()->GetViewOption(rDoc.get(IDocumentSettingAccess::HTML_MODE))->IsShowHiddenChar(); + + // If called from word count, we want to mask the hidden ranges even + // if they are visible: + if ( !bCheckShowHiddenChar || bHideHidden ) + { + nHiddenCharsMasked = + SwScriptInfo::MaskHiddenRanges( rNode, rText, nStt, nEnd, cChar ); + } + + return nRedlinesMasked + nHiddenCharsMasked; +} + +/* + * Used for spell checking. Calculates a rectangle for repaint. + */ + +static SwRect lcl_CalculateRepaintRect( SwTxtFrm& rTxtFrm, xub_StrLen nChgStart, xub_StrLen nChgEnd ) +{ + SwRect aRect; + + SwTxtNode *pNode = rTxtFrm.GetTxtNode(); + + SwNodeIndex aNdIdx( *pNode ); + SwPosition aPos( aNdIdx, SwIndex( pNode, nChgEnd ) ); + SwCrsrMoveState aTmpState( MV_NONE ); + aTmpState.b2Lines = sal_True; + rTxtFrm.GetCharRect( aRect, aPos, &aTmpState ); + // information about end of repaint area + Sw2LinesPos* pEnd2Pos = aTmpState.p2Lines; + + const SwTxtFrm *pEndFrm = &rTxtFrm; + + while( pEndFrm->HasFollow() && + nChgEnd >= pEndFrm->GetFollow()->GetOfst() ) + pEndFrm = pEndFrm->GetFollow(); + + if ( pEnd2Pos ) + { + // we are inside a special portion, take left border + SWRECTFN( pEndFrm ) + (aRect.*fnRect->fnSetTop)( (pEnd2Pos->aLine.*fnRect->fnGetTop)() ); + if ( pEndFrm->IsRightToLeft() ) + (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetLeft)() ); + else + (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetRight)() ); + (aRect.*fnRect->fnSetWidth)( 1 ); + (aRect.*fnRect->fnSetHeight)( (pEnd2Pos->aLine.*fnRect->fnGetHeight)() ); + delete pEnd2Pos; + } + + aTmpState.p2Lines = NULL; + SwRect aTmp; + aPos = SwPosition( aNdIdx, SwIndex( pNode, nChgStart ) ); + rTxtFrm.GetCharRect( aTmp, aPos, &aTmpState ); + + // i63141: GetCharRect(..) could cause a formatting, + // during the formatting SwTxtFrms could be joined, deleted, created... + // => we have to reinit pStartFrm and pEndFrm after the formatting + const SwTxtFrm* pStartFrm = &rTxtFrm; + while( pStartFrm->HasFollow() && + nChgStart >= pStartFrm->GetFollow()->GetOfst() ) + pStartFrm = pStartFrm->GetFollow(); + pEndFrm = pStartFrm; + while( pEndFrm->HasFollow() && + nChgEnd >= pEndFrm->GetFollow()->GetOfst() ) + pEndFrm = pEndFrm->GetFollow(); + + // information about start of repaint area + Sw2LinesPos* pSt2Pos = aTmpState.p2Lines; + if ( pSt2Pos ) + { + // we are inside a special portion, take right border + SWRECTFN( pStartFrm ) + (aTmp.*fnRect->fnSetTop)( (pSt2Pos->aLine.*fnRect->fnGetTop)() ); + if ( pStartFrm->IsRightToLeft() ) + (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetRight)() ); + else + (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetLeft)() ); + (aTmp.*fnRect->fnSetWidth)( 1 ); + (aTmp.*fnRect->fnSetHeight)( (pSt2Pos->aLine.*fnRect->fnGetHeight)() ); + delete pSt2Pos; + } + + BOOL bSameFrame = TRUE; + + if( rTxtFrm.HasFollow() ) + { + if( pEndFrm != pStartFrm ) + { + bSameFrame = FALSE; + SwRect aStFrm( pStartFrm->PaintArea() ); + { + SWRECTFN( pStartFrm ) + (aTmp.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); + (aTmp.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); + (aTmp.*fnRect->fnSetBottom)( (aStFrm.*fnRect->fnGetBottom)() ); + } + aStFrm = pEndFrm->PaintArea(); + { + SWRECTFN( pEndFrm ) + (aRect.*fnRect->fnSetTop)( (aStFrm.*fnRect->fnGetTop)() ); + (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); + (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); + } + aRect.Union( aTmp ); + while( TRUE ) + { + pStartFrm = pStartFrm->GetFollow(); + if( pStartFrm == pEndFrm ) + break; + aRect.Union( pStartFrm->PaintArea() ); + } + } + } + if( bSameFrame ) + { + SWRECTFN( pStartFrm ) + if( (aTmp.*fnRect->fnGetTop)() == (aRect.*fnRect->fnGetTop)() ) + (aRect.*fnRect->fnSetLeft)( (aTmp.*fnRect->fnGetLeft)() ); + else + { + SwRect aStFrm( pStartFrm->PaintArea() ); + (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); + (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); + (aRect.*fnRect->fnSetTop)( (aTmp.*fnRect->fnGetTop)() ); + } + + if( aTmp.Height() > aRect.Height() ) + aRect.Height( aTmp.Height() ); + } + + return aRect; +} + +/* + * Used for automatic styles. Used during RstAttr. + */ + +static bool lcl_HaveCommonAttributes( IStyleAccess& rStyleAccess, + const SfxItemSet* pSet1, + USHORT nWhichId, + const SfxItemSet& rSet2, + boost::shared_ptr<SfxItemSet>& pStyleHandle ) +{ + bool bRet = false; + + SfxItemSet* pNewSet = 0; + + if ( !pSet1 ) + { + ASSERT( nWhichId, "lcl_HaveCommonAttributes not used correctly" ) + if ( SFX_ITEM_SET == rSet2.GetItemState( nWhichId, FALSE ) ) + { + pNewSet = rSet2.Clone( TRUE ); + pNewSet->ClearItem( nWhichId ); + } + } + else if ( pSet1->Count() ) + { + SfxItemIter aIter( *pSet1 ); + const SfxPoolItem* pItem = aIter.GetCurItem(); + while( TRUE ) + { + if ( SFX_ITEM_SET == rSet2.GetItemState( pItem->Which(), FALSE ) ) + { + if ( !pNewSet ) + pNewSet = rSet2.Clone( TRUE ); + pNewSet->ClearItem( pItem->Which() ); + } + + if( aIter.IsAtEnd() ) + break; + + pItem = aIter.NextItem(); + } + } + + if ( pNewSet ) + { + if ( pNewSet->Count() ) + pStyleHandle = rStyleAccess.getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR ); + delete pNewSet; + bRet = true; + } + + return bRet; +} + +inline BOOL InRange(xub_StrLen nIdx, xub_StrLen nStart, xub_StrLen nEnd) { + return ((nIdx >=nStart) && (nIdx <= nEnd)); +} + +/* + * void SwTxtNode::RstAttr(const SwIndex &rIdx, USHORT nLen) + * + * Deletes all attributes, starting at position rIdx, for length nLen. + */ + +/* 5 cases: + * 1) The attribute is completely in the deletion range: + * -> delete it + * 2) The end of the attribute is in the deletion range: + * -> delete it, then re-insert it with new end + * 3) The start of the attribute is in the deletion range: + * -> delete it, then re-insert it with new start + * 4) The attribute contains the deletion range: + * Split, i.e., + * -> Delete, re-insert from old start to start of deletion range + * -> insert new attribute from end of deletion range to old end + * 5) The attribute is outside the deletion range + * -> nothing to do + */ + +void SwTxtNode::RstAttr(const SwIndex &rIdx, xub_StrLen nLen, USHORT nWhich, + const SfxItemSet* pSet, BOOL bInclRefToxMark ) +{ + // Attribute? + if ( !GetpSwpHints() ) + return; + + USHORT i = 0; + xub_StrLen nStt = rIdx.GetIndex(); + xub_StrLen nEnd = nStt + nLen; + xub_StrLen nAttrStart; + SwTxtAttr *pHt; + + BOOL bChanged = FALSE; + + // nMin and nMax initialized to maximum / minimum (inverse) + xub_StrLen nMin = m_Text.Len(); + xub_StrLen nMax = nStt; + + const BOOL bNoLen = !nMin; + + // We have to remember the "new" attributes, which have + // been introduced by splitting surrounding attributes (case 4). + // They may not be forgotten inside the "Forget" function + //std::vector< const SwTxtAttr* > aNewAttributes; + + // iterate over attribute array until start of attribute is behind + // deletion range + while ((i < m_pSwpHints->Count()) && + ((( nAttrStart = *(*m_pSwpHints)[i]->GetStart()) < nEnd ) || nLen==0) ) + { + pHt = m_pSwpHints->GetTextHint(i); + + // attributes without end stay in! + xub_StrLen * const pAttrEnd = pHt->GetEnd(); + if ( !pAttrEnd /*|| pHt->HasDummyChar()*/ ) // see bInclRefToxMark + { + i++; + continue; + } + + // Default behavior is to process all attributes: + bool bSkipAttr = false;; + boost::shared_ptr<SfxItemSet> pStyleHandle; + + // 1. case: We want to reset only the attributes listed in pSet: + if ( pSet ) + { + bSkipAttr = SFX_ITEM_SET != pSet->GetItemState( pHt->Which(), FALSE ); + if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() ) + { + // if the current attribute is an autostyle, we have to check if the autostyle + // and pSet have any attributes in common. If so, pStyleHandle will contain + // a handle to AutoStyle / pSet: + bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet, 0, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle ); + } + } + else if ( nWhich ) + { + // 2. case: We want to reset only the attributes with WhichId nWhich: + bSkipAttr = nWhich != pHt->Which(); + if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() ) + { + bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), 0, nWhich, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle ); + } + } + else if ( !bInclRefToxMark ) + { + // 3. case: Reset all attributes except from ref/toxmarks: + // skip hints with CH_TXTATR here + // (deleting those is ONLY allowed for UNDO!) + bSkipAttr = RES_TXTATR_REFMARK == pHt->Which() + || RES_TXTATR_TOXMARK == pHt->Which() + || RES_TXTATR_META == pHt->Which() + || RES_TXTATR_METAFIELD == pHt->Which(); + } + + if ( bSkipAttr ) + { + i++; + continue; + } + + + if( nStt <= nAttrStart ) // Faelle: 1,3,5 + { + if( nEnd > nAttrStart + || ( nEnd == *pAttrEnd && nEnd==nAttrStart ) ) + { + // Faelle: 1,3 + if ( nMin > nAttrStart ) + nMin = nAttrStart; + if ( nMax < *pAttrEnd ) + nMax = *pAttrEnd; + // Falls wir nur ein nichtaufgespanntes Attribut entfernen, + // tun wir mal so, als ob sich nichts geaendert hat. + bChanged = bChanged || nEnd > nAttrStart || bNoLen; + if( *pAttrEnd <= nEnd ) // Fall: 1 + { + const xub_StrLen nAttrEnd = *pAttrEnd; + + m_pSwpHints->DeleteAtPos(i); + DestroyAttr( pHt ); + + if ( pStyleHandle.get() ) + { + SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), + *pStyleHandle, nAttrStart, nAttrEnd ); + InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + + // if the last attribute is a Field, the HintsArray is + // deleted! + if ( !m_pSwpHints ) + break; + + //JP 26.11.96: + // beim DeleteAtPos wird ein Resort ausgefuehrt!! + // darum muessen wir wieder bei 0 anfangen!!! + // ueber den Fall 3 koennen Attribute nach hinten + // verschoben worden sein; damit stimmt jetzt das i + // nicht mehr!!! + i = 0; + + continue; + } + else // Fall: 3 + { + m_pSwpHints->NoteInHistory( pHt ); + *pHt->GetStart() = nEnd; + m_pSwpHints->NoteInHistory( pHt, TRUE ); + + if ( pStyleHandle.get() && nAttrStart < nEnd ) + { + SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), + *pStyleHandle, nAttrStart, nEnd ); + InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + + bChanged = TRUE; + } + } + } + else // Faelle: 2,4,5 + if( *pAttrEnd > nStt ) // Faelle: 2,4 + { + if( *pAttrEnd < nEnd ) // Fall: 2 + { + if ( nMin > nAttrStart ) + nMin = nAttrStart; + if ( nMax < *pAttrEnd ) + nMax = *pAttrEnd; + bChanged = TRUE; + + const xub_StrLen nAttrEnd = *pAttrEnd; + + m_pSwpHints->NoteInHistory( pHt ); + *pAttrEnd = nStt; + m_pSwpHints->NoteInHistory( pHt, TRUE ); + + if ( pStyleHandle.get() ) + { + SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), + *pStyleHandle, nStt, nAttrEnd ); + InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + } + else if( nLen ) // Fall: 4 + { // bei Lange 0 werden beide Hints vom Insert(Ht) + // wieder zu einem zusammengezogen !!!! + if ( nMin > nAttrStart ) + nMin = nAttrStart; + if ( nMax < *pAttrEnd ) + nMax = *pAttrEnd; + bChanged = TRUE; + xub_StrLen nTmpEnd = *pAttrEnd; + m_pSwpHints->NoteInHistory( pHt ); + *pAttrEnd = nStt; + m_pSwpHints->NoteInHistory( pHt, TRUE ); + + if ( pStyleHandle.get() && nStt < nEnd ) + { + SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), + *pStyleHandle, nStt, nEnd ); + InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + + if( nEnd < nTmpEnd ) + { + SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), + pHt->GetAttr(), nEnd, nTmpEnd ); + if ( pNew ) + { + SwTxtCharFmt* pCharFmt = dynamic_cast<SwTxtCharFmt*>(pHt); + if ( pCharFmt ) + static_cast<SwTxtCharFmt*>(pNew)->SetSortNumber( pCharFmt->GetSortNumber() ); + + InsertHint( pNew, + nsSetAttrMode::SETATTR_NOHINTADJUST ); + } + + + // jetzt kein i+1, weil das eingefuegte Attribut + // ein anderes auf die Position geschoben hat ! + continue; + } + } + } + ++i; + } + + TryDeleteSwpHints(); + if (bChanged) + { + if ( HasHints() ) + { + m_pSwpHints->Resort(); + } + //TxtFrm's reagieren auf aHint, andere auf aNew + SwUpdateAttr aHint( nMin, nMax, 0 ); + SwModify::Modify( 0, &aHint ); + SwFmtChg aNew( GetFmtColl() ); + SwModify::Modify( 0, &aNew ); + } +} + + + +/************************************************************************* + * SwTxtNode::GetCurWord() + * + * Aktuelles Wort zurueckliefern: + * Wir suchen immer von links nach rechts, es wird also das Wort + * vor nPos gesucht. Es sei denn, wir befinden uns am Anfang des + * Absatzes, dann wird das erste Wort zurueckgeliefert. + * Wenn dieses erste Wort nur aus Whitespaces besteht, returnen wir + * einen leeren String. + *************************************************************************/ + +XubString SwTxtNode::GetCurWord( xub_StrLen nPos ) const +{ + ASSERT( nPos <= m_Text.Len(), "SwTxtNode::GetCurWord: invalid index." ); + + if (!m_Text.Len()) + return m_Text; + + Boundary aBndry; + const uno::Reference< XBreakIterator > &rxBreak = pBreakIt->GetBreakIter(); + if (rxBreak.is()) + { + sal_Int16 nWordType = WordType::DICTIONARY_WORD; + lang::Locale aLocale( pBreakIt->GetLocale( GetLang( nPos ) ) ); +#ifdef DEBUG + BOOL bBegin = rxBreak->isBeginWord( m_Text, nPos, aLocale, nWordType ); + BOOL bEnd = rxBreak->isEndWord ( m_Text, nPos, aLocale, nWordType ); + (void)bBegin; + (void)bEnd; +#endif + aBndry = + rxBreak->getWordBoundary( m_Text, nPos, aLocale, nWordType, TRUE ); + + // if no word was found use previous word (if any) + if (aBndry.startPos == aBndry.endPos) + { + aBndry = rxBreak->previousWord( m_Text, nPos, aLocale, nWordType ); + } + } + + // check if word was found and if it uses a symbol font, if so + // enforce returning an empty string + if (aBndry.endPos != aBndry.startPos && IsSymbol( (xub_StrLen)aBndry.startPos )) + aBndry.endPos = aBndry.startPos; + + return m_Text.Copy( static_cast<xub_StrLen>(aBndry.startPos), + static_cast<xub_StrLen>(aBndry.endPos - aBndry.startPos) ); +} + +SwScanner::SwScanner( const SwTxtNode& rNd, const String& rTxt, const LanguageType* pLang, + const ModelToViewHelper::ConversionMap* pConvMap, + USHORT nType, xub_StrLen nStart, xub_StrLen nEnde, BOOL bClp ) + : rNode( rNd ), rText( rTxt), pLanguage( pLang ), pConversionMap( pConvMap ), nLen( 0 ), nWordType( nType ), bClip( bClp ) +{ + ASSERT( rText.Len(), "SwScanner: EmptyString" ); + nStartPos = nBegin = nStart; + nEndPos = nEnde; + + if ( pLanguage ) + { + aCurrLang = *pLanguage; + } + else + { + ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin ); + const xub_StrLen nModelBeginPos = (xub_StrLen)aModelBeginPos.mnPos; + aCurrLang = rNd.GetLang( nModelBeginPos ); + } +} + +BOOL SwScanner::NextWord() +{ + nBegin = nBegin + nLen; + Boundary aBound; + + CharClass& rCC = GetAppCharClass(); + lang::Locale aOldLocale = rCC.getLocale(); + + while ( true ) + { + // skip non-letter characters: + while ( nBegin < rText.Len() ) + { + if ( !lcl_IsSkippableWhiteSpace( rText.GetChar( nBegin ) ) ) + { + if ( !pLanguage ) + { + const USHORT nNextScriptType = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin ); + ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin ); + const xub_StrLen nBeginModelPos = (xub_StrLen)aModelBeginPos.mnPos; + aCurrLang = rNode.GetLang( nBeginModelPos, 1, nNextScriptType ); + } + + if ( nWordType != i18n::WordType::WORD_COUNT ) + { + rCC.setLocale( pBreakIt->GetLocale( aCurrLang ) ); + if ( rCC.isLetterNumeric( rText.GetChar( nBegin ) ) ) + break; + } + else + break; + } + ++nBegin; + } + + if ( nBegin >= rText.Len() || nBegin >= nEndPos ) + return FALSE; + + // get the word boundaries + aBound = pBreakIt->GetBreakIter()->getWordBoundary( rText, nBegin, + pBreakIt->GetLocale( aCurrLang ), nWordType, sal_True ); + + //no word boundaries could be found + if(aBound.endPos == aBound.startPos) + return FALSE; + + //if a word before is found it has to be searched for the next + if(aBound.endPos == nBegin) + ++nBegin; + else + break; + } // end while( true ) + + rCC.setLocale( aOldLocale ); + + // we have to differenciate between these cases: + if ( aBound.startPos <= nBegin ) + { + ASSERT( aBound.endPos >= nBegin, "Unexpected aBound result" ) + + // restrict boundaries to script boundaries and nEndPos + const USHORT nCurrScript = + pBreakIt->GetBreakIter()->getScriptType( rText, nBegin ); + + XubString aTmpWord = rText.Copy( nBegin, static_cast<xub_StrLen>(aBound.endPos - nBegin) ); + const sal_Int32 nScriptEnd = nBegin + + pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript ); + const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd ); + + // restrict word start to last script change position + sal_Int32 nScriptBegin = 0; + if ( aBound.startPos < nBegin ) + { + // search from nBegin backwards until the next script change + aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos), + static_cast<xub_StrLen>(nBegin - aBound.startPos + 1) ); + nScriptBegin = aBound.startPos + + pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, nBegin - aBound.startPos, + nCurrScript ); + } + + nBegin = (xub_StrLen)Max( aBound.startPos, nScriptBegin ); + nLen = (xub_StrLen)(nEnd - nBegin); + } + else + { + const USHORT nCurrScript = + pBreakIt->GetBreakIter()->getScriptType( rText, aBound.startPos ); + XubString aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos), + static_cast<xub_StrLen>(aBound.endPos - aBound.startPos) ); + const sal_Int32 nScriptEnd = aBound.startPos + + pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript ); + const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd ); + nBegin = (xub_StrLen)aBound.startPos; + nLen = (xub_StrLen)(nEnd - nBegin); + } + + // optionally clip the result of getWordBoundaries: + if ( bClip ) + { + aBound.startPos = Max( (xub_StrLen)aBound.startPos, nStartPos ); + aBound.endPos = Min( (xub_StrLen)aBound.endPos, nEndPos ); + nBegin = (xub_StrLen)aBound.startPos; + nLen = (xub_StrLen)(aBound.endPos - nBegin); + } + + if( ! nLen ) + return FALSE; + + aWord = rText.Copy( nBegin, nLen ); + + return TRUE; +} + + +USHORT SwTxtNode::Spell(SwSpellArgs* pArgs) +{ + // Die Aehnlichkeiten zu SwTxtFrm::_AutoSpell sind beabsichtigt ... + // ACHTUNG: Ev. Bugs in beiden Routinen fixen! + + uno::Reference<beans::XPropertySet> xProp( GetLinguPropertySet() ); + + xub_StrLen nBegin, nEnd; + + // modify string according to redline information and hidden text + const XubString aOldTxt( m_Text ); + const bool bRestoreString = + lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0; + + if ( pArgs->pStartNode != this ) + nBegin = 0; + else + nBegin = pArgs->pStartIdx->GetIndex(); + + nEnd = ( pArgs->pEndNode != this ) + ? m_Text.Len() + : pArgs->pEndIdx->GetIndex(); + + pArgs->xSpellAlt = NULL; + + // 4 cases: + // + // 1. IsWrongDirty = 0 and GetWrong = 0 + // Everything is checked and correct + // 2. IsWrongDirty = 0 and GetWrong = 1 + // Everything is checked and errors are identified in the wrong list + // 3. IsWrongDirty = 1 and GetWrong = 0 + // Nothing has been checked + // 4. IsWrongDirty = 1 and GetWrong = 1 + // Text has been checked but there is an invalid range in the wrong list + // + // Nothing has to be done for case 1. + if ( ( IsWrongDirty() || GetWrong() ) && m_Text.Len() ) + { + if ( nBegin > m_Text.Len() ) + { + nBegin = m_Text.Len(); + } + if ( nEnd > m_Text.Len() ) + { + nEnd = m_Text.Len(); + } + // + if(!IsWrongDirty()) + { + xub_StrLen nTemp = GetWrong()->NextWrong( nBegin ); + if(nTemp > nEnd) + { + // reset original text + if ( bRestoreString ) + { + m_Text = aOldTxt; + } + return 0; + } + if(nTemp > nBegin) + nBegin = nTemp; + + } + + // In case 2. we pass the wrong list to the scanned, because only + // the words in the wrong list have to be checked + SwScanner aScanner( *this, m_Text, 0, 0, + WordType::DICTIONARY_WORD, + nBegin, nEnd ); + while( !pArgs->xSpellAlt.is() && aScanner.NextWord() ) + { + const XubString& rWord = aScanner.GetWord(); + + // get next language for next word, consider language attributes + // within the word + LanguageType eActLang = aScanner.GetCurrentLanguage(); + + if( rWord.Len() > 0 && LANGUAGE_NONE != eActLang ) + { + if (pArgs->xSpeller.is()) + { + SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang ); + pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, eActLang, + Sequence< PropertyValue >() ); + } + if( (pArgs->xSpellAlt).is() ) + { + if( IsSymbol( aScanner.GetBegin() ) ) + { + pArgs->xSpellAlt = NULL; + } + else + { + // make sure the selection build later from the + // data below does not include footnotes and other + // "in word" character to the left and right in order + // to preserve those. Therefore count those "in words" + // in order to modify the selection accordingly. + const sal_Unicode* pChar = rWord.GetBuffer(); + xub_StrLen nLeft = 0; + while (pChar && *pChar++ == CH_TXTATR_INWORD) + ++nLeft; + pChar = rWord.Len() ? rWord.GetBuffer() + rWord.Len() - 1 : 0; + xub_StrLen nRight = 0; + while (pChar && *pChar-- == CH_TXTATR_INWORD) + ++nRight; + + pArgs->pStartNode = this; + pArgs->pEndNode = this; + pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight ); + pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft ); + } + } + } + } + } + + // reset original text + if ( bRestoreString ) + { + m_Text = aOldTxt; + } + + return pArgs->xSpellAlt.is() ? 1 : 0; +} + + +void SwTxtNode::SetLanguageAndFont( const SwPaM &rPaM, + LanguageType nLang, USHORT nLangWhichId, + const Font *pFont, USHORT nFontWhichId ) +{ + sal_uInt16 aRanges[] = { + nLangWhichId, nLangWhichId, + nFontWhichId, nFontWhichId, + 0, 0, 0 }; + if (!pFont) + aRanges[2] = aRanges[3] = 0; // clear entries with font WhichId + + SwEditShell *pEditShell = GetDoc()->GetEditShell(); + SfxItemSet aSet( pEditShell->GetAttrPool(), aRanges ); + aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) ); + + DBG_ASSERT( pFont, "target font missing?" ); + if (pFont) + { + SvxFontItem aFontItem = (SvxFontItem&) aSet.Get( nFontWhichId ); + aFontItem.GetFamilyName() = pFont->GetName(); + aFontItem.GetFamily() = pFont->GetFamily(); + aFontItem.GetStyleName() = pFont->GetStyleName(); + aFontItem.GetPitch() = pFont->GetPitch(); + aFontItem.GetCharSet() = pFont->GetCharSet(); + aSet.Put( aFontItem ); + } + + GetDoc()->InsertItemSet( rPaM, aSet, 0 ); + // SetAttr( aSet ); <- Does not set language attribute of empty paragraphs correctly, + // <- because since there is no selection the flag to garbage + // <- collect all attributes is set, and therefore attributes spanned + // <- over empty selection are removed. + +} + + +USHORT SwTxtNode::Convert( SwConversionArgs &rArgs ) +{ + // get range of text within node to be converted + // (either all the text or the the text within the selection + // when the conversion was started) + xub_StrLen nTextBegin, nTextEnd; + // + if ( rArgs.pStartNode != this ) + { + nTextBegin = 0; + } + else + nTextBegin = rArgs.pStartIdx->GetIndex(); + if (nTextBegin > m_Text.Len()) + { + nTextBegin = m_Text.Len(); + } + + nTextEnd = ( rArgs.pEndNode != this ) + ? m_Text.Len() + : ::std::min( rArgs.pEndIdx->GetIndex(), m_Text.Len() ); + + rArgs.aConvText = rtl::OUString(); + + // modify string according to redline information and hidden text + const XubString aOldTxt( m_Text ); + const bool bRestoreString = + lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0; + + sal_Bool bFound = sal_False; + xub_StrLen nBegin = nTextBegin; + xub_StrLen nLen = 0; + LanguageType nLangFound = LANGUAGE_NONE; + if (!m_Text.Len()) + { + if (rArgs.bAllowImplicitChangesForNotConvertibleText) + { + // create SwPaM with mark & point spanning empty paragraph + //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different + SwPaM aCurPaM( *this, 0 ); + + SetLanguageAndFont( aCurPaM, + rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, + rArgs.pTargetFont, RES_CHRATR_CJK_FONT ); + } + } + else + { + SwLanguageIterator aIter( *this, nBegin ); + + // find non zero length text portion of appropriate language + do { + nLangFound = aIter.GetLanguage(); + sal_Bool bLangOk = (nLangFound == rArgs.nConvSrcLang) || + (editeng::HangulHanjaConversion::IsChinese( nLangFound ) && + editeng::HangulHanjaConversion::IsChinese( rArgs.nConvSrcLang )); + + xub_StrLen nChPos = aIter.GetChgPos(); + // the position at the end of the paragraph returns -1 + // which becomes 65535 when converted to xub_StrLen, + // and thus must be cut to the end of the actual string. + if (nChPos == (xub_StrLen) -1) + { + nChPos = m_Text.Len(); + } + + nLen = nChPos - nBegin; + bFound = bLangOk && nLen > 0; + if (!bFound) + { + // create SwPaM with mark & point spanning the attributed text + //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different + SwPaM aCurPaM( *this, nBegin ); + aCurPaM.SetMark(); + aCurPaM.GetPoint()->nContent = nBegin + nLen; + + // check script type of selected text + SwEditShell *pEditShell = GetDoc()->GetEditShell(); + pEditShell->Push(); // save current cursor on stack + pEditShell->SetSelection( aCurPaM ); + sal_Bool bIsAsianScript = (SCRIPTTYPE_ASIAN == pEditShell->GetScriptType()); + pEditShell->Pop( sal_False ); // restore cursor from stack + + if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText) + { + SetLanguageAndFont( aCurPaM, + rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, + rArgs.pTargetFont, RES_CHRATR_CJK_FONT ); + } + nBegin = nChPos; // start of next language portion + } + } while (!bFound && aIter.Next()); /* loop while nothing was found and still sth is left to be searched */ + } + + // keep resulting text within selection / range of text to be converted + if (nBegin < nTextBegin) + nBegin = nTextBegin; + if (nBegin + nLen > nTextEnd) + nLen = nTextEnd - nBegin; + sal_Bool bInSelection = nBegin < nTextEnd; + + if (bFound && bInSelection) // convertible text found within selection/range? + { + const XubString aTxtPortion = m_Text.Copy( nBegin, nLen ); + DBG_ASSERT( m_Text.Len() > 0, "convertible text portion missing!" ); + rArgs.aConvText = m_Text.Copy( nBegin, nLen ); + rArgs.nConvTextLang = nLangFound; + + // position where to start looking in next iteration (after current ends) + rArgs.pStartNode = this; + rArgs.pStartIdx->Assign(this, nBegin + nLen ); + // end position (when we have travelled over the whole document) + rArgs.pEndNode = this; + rArgs.pEndIdx->Assign(this, nBegin ); + } + + // restore original text + if ( bRestoreString ) + { + m_Text = aOldTxt; + } + + return rArgs.aConvText.getLength() ? 1 : 0; +} + +// Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ... +// ACHTUNG: Ev. Bugs in beiden Routinen fixen! +SwRect SwTxtFrm::_AutoSpell( const SwCntntNode* pActNode, const SwViewOption& rViewOpt, xub_StrLen nActPos ) +{ + SwRect aRect; +#if OSL_DEBUG_LEVEL > 1 + static BOOL bStop = FALSE; + if ( bStop ) + return aRect; +#endif + // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ... + // ACHTUNG: Ev. Bugs in beiden Routinen fixen! + SwTxtNode *pNode = GetTxtNode(); + if( pNode != pActNode || !nActPos ) + nActPos = STRING_LEN; + + SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); + + // modify string according to redline information and hidden text + const XubString aOldTxt( pNode->GetTxt() ); + const bool bRestoreString = + lcl_MaskRedlinesAndHiddenText( *pNode, pNode->m_Text, + 0, pNode->GetTxt().Len() ) > 0; + + // a change of data indicates that at least one word has been modified + const sal_Bool bRedlineChg = + ( pNode->GetTxt().GetBuffer() != aOldTxt.GetBuffer() ); + + xub_StrLen nBegin = 0; + xub_StrLen nEnd = pNode->GetTxt().Len(); + xub_StrLen nInsertPos = 0; + xub_StrLen nChgStart = STRING_LEN; + xub_StrLen nChgEnd = 0; + xub_StrLen nInvStart = STRING_LEN; + xub_StrLen nInvEnd = 0; + + const sal_Bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() && + rViewOpt.IsAutoCompleteWords(); + + if( pNode->GetWrong() ) + { + nBegin = pNode->GetWrong()->GetBeginInv(); + if( STRING_LEN != nBegin ) + { + nEnd = pNode->GetWrong()->GetEndInv(); + if ( nEnd > pNode->GetTxt().Len() ) + { + nEnd = pNode->GetTxt().Len(); + } + } + + // get word around nBegin, we start at nBegin - 1 + if ( STRING_LEN != nBegin ) + { + if ( nBegin ) + --nBegin; + + LanguageType eActLang = pNode->GetLang( nBegin ); + Boundary aBound = + pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetTxt(), nBegin, + pBreakIt->GetLocale( eActLang ), + WordType::DICTIONARY_WORD, TRUE ); + nBegin = xub_StrLen(aBound.startPos); + } + + // get the position in the wrong list + nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin ); + + // sometimes we have to skip one entry + if( nInsertPos < pNode->GetWrong()->Count() && + nBegin == pNode->GetWrong()->Pos( nInsertPos ) + + pNode->GetWrong()->Len( nInsertPos ) ) + nInsertPos++; + } + + BOOL bFresh = nBegin < nEnd; + + if( nBegin < nEnd ) + { + //! register listener to LinguServiceEvents now in order to get + //! notified about relevant changes in the future + SwModule *pModule = SW_MOD(); + if (!pModule->GetLngSvcEvtListener().is()) + pModule->CreateLngSvcEvtListener(); + + uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() ); + SwDoc* pDoc = pNode->GetDoc(); + + SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0, + WordType::DICTIONARY_WORD, nBegin, nEnd); + + while( aScanner.NextWord() ) + { + const XubString& rWord = aScanner.GetWord(); + nBegin = aScanner.GetBegin(); + xub_StrLen nLen = aScanner.GetLen(); + + // get next language for next word, consider language attributes + // within the word + LanguageType eActLang = aScanner.GetCurrentLanguage(); + + BOOL bSpell = TRUE; + bSpell = xSpell.is() ? xSpell->hasLanguage( eActLang ) : FALSE; + if( bSpell && rWord.Len() > 0 ) + { + // check for: bAlter => xHyphWord.is() + DBG_ASSERT(!bSpell || xSpell.is(), "NULL pointer"); + + if( !xSpell->isValid( rWord, eActLang, Sequence< PropertyValue >() ) ) + { + xub_StrLen nSmartTagStt = nBegin; + xub_StrLen nDummy = 1; + if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) ) + { + if( !pNode->GetWrong() ) + { + pNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) ); + pNode->GetWrong()->SetInvalid( 0, nEnd ); + } + if( pNode->GetWrong()->Fresh( nChgStart, nChgEnd, + nBegin, nLen, nInsertPos, nActPos ) ) + pNode->GetWrong()->Insert( rtl::OUString(), 0, nBegin, nLen, nInsertPos++ ); + else + { + nInvStart = nBegin; + nInvEnd = nBegin + nLen; + } + } + } + else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.Len() ) + { + if ( bRedlineChg ) + { + XubString rNewWord( rWord ); + rACW.InsertWord( rNewWord, *pDoc ); + } + else + rACW.InsertWord( rWord, *pDoc ); + } + } + } + } + + // reset original text + // i63141 before calling GetCharRect(..) with formatting! + if ( bRestoreString ) + { + pNode->m_Text = aOldTxt; + } + if( pNode->GetWrong() ) + { + if( bFresh ) + pNode->GetWrong()->Fresh( nChgStart, nChgEnd, + nEnd, 0, nInsertPos, nActPos ); + + // + // Calculate repaint area: + // + if( nChgStart < nChgEnd ) + { + aRect = lcl_CalculateRepaintRect( *this, nChgStart, nChgEnd ); + } + + pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd ); + pNode->SetWrongDirty( STRING_LEN != pNode->GetWrong()->GetBeginInv() ); + if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() ) + pNode->SetWrong( NULL ); + } + else + pNode->SetWrongDirty( false ); + + if( bAddAutoCmpl ) + pNode->SetAutoCompleteWordDirty( false ); + + return aRect; +} + +/** Function: SmartTagScan + + Function scans words in current text and checks them in the + smarttag libraries. If the check returns true to bounds of the + recognized words are stored into a list which is used later for drawing + the underline. + + @param SwCntntNode* pActNode + + @param xub_StrLen nActPos + + @return SwRect: Repaint area +*/ +SwRect SwTxtFrm::SmartTagScan( SwCntntNode* /*pActNode*/, xub_StrLen /*nActPos*/ ) +{ + SwRect aRet; + SwTxtNode *pNode = GetTxtNode(); + const rtl::OUString& rText = pNode->GetTxt(); + + // Iterate over language portions + SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get(); + + SwWrongList* pSmartTagList = pNode->GetSmartTags(); + + xub_StrLen nBegin = 0; + xub_StrLen nEnd = static_cast< xub_StrLen >(rText.getLength()); + + if ( pSmartTagList ) + { + if ( pSmartTagList->GetBeginInv() != STRING_LEN ) + { + nBegin = pSmartTagList->GetBeginInv(); + nEnd = Min( pSmartTagList->GetEndInv(), (xub_StrLen)rText.getLength() ); + + if ( nBegin < nEnd ) + { + const LanguageType aCurrLang = pNode->GetLang( nBegin ); + const com::sun::star::lang::Locale aCurrLocale = pBreakIt->GetLocale( aCurrLang ); + nBegin = static_cast< xub_StrLen >(pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale )); + nEnd = static_cast< xub_StrLen >(Min( rText.getLength(), pBreakIt->GetBreakIter()->endOfSentence( rText, nEnd, aCurrLocale ) )); + } + } + } + + const USHORT nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0; + USHORT nNumberOfRemovedEntries = 0; + USHORT nNumberOfInsertedEntries = 0; + + // clear smart tag list between nBegin and nEnd: + if ( 0 != nNumberOfEntries ) + { + xub_StrLen nChgStart = STRING_LEN; + xub_StrLen nChgEnd = 0; + const USHORT nCurrentIndex = pSmartTagList->GetWrongPos( nBegin ); + pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, STRING_LEN ); + nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count(); + } + + if ( nBegin < nEnd ) + { + // Expand the string: + rtl::OUString aExpandText; + const ModelToViewHelper::ConversionMap* pConversionMap = + pNode->BuildConversionMap( aExpandText ); + + // Ownership ov ConversionMap is passed to SwXTextMarkup object! + Reference< com::sun::star::text::XTextMarkup > xTextMarkup = + new SwXTextMarkup( *pNode, pConversionMap ); + + Reference< ::com::sun::star::frame::XController > xController = pNode->GetDoc()->GetDocShell()->GetController(); + + xub_StrLen nLangBegin = nBegin; + xub_StrLen nLangEnd = nEnd; + + // smart tag recognization has to be done for each language portion: + SwLanguageIterator aIter( *pNode, nLangBegin ); + + do + { + const LanguageType nLang = aIter.GetLanguage(); + const com::sun::star::lang::Locale aLocale = pBreakIt->GetLocale( nLang ); + nLangEnd = Min( nEnd, aIter.GetChgPos() ); + + const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangBegin ); + const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangEnd ); + + rSmartTagMgr.Recognize( aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin ); + + nLangBegin = nLangEnd; + } + while ( aIter.Next() && nLangEnd < nEnd ); + + pSmartTagList = pNode->GetSmartTags(); + + const USHORT nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0; + nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries ); + } + + if( pSmartTagList ) + { + // + // Update WrongList stuff + // + pSmartTagList->SetInvalid( STRING_LEN, 0 ); + pNode->SetSmartTagDirty( STRING_LEN != pSmartTagList->GetBeginInv() ); + + if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() ) + pNode->SetSmartTags( NULL ); + + // + // Calculate repaint area: + // +#if OSL_DEBUG_LEVEL > 1 + const USHORT nNumberOfEntriesAfterRecognize2 = pSmartTagList->Count(); + (void) nNumberOfEntriesAfterRecognize2; +#endif + if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries || + 0 != nNumberOfInsertedEntries ) ) + { + aRet = lcl_CalculateRepaintRect( *this, nBegin, nEnd ); + } + } + else + pNode->SetSmartTagDirty( false ); + + return aRet; +} + + +// Wird vom CollectAutoCmplWords gerufen +void SwTxtFrm::CollectAutoCmplWrds( SwCntntNode* pActNode, xub_StrLen nActPos ) +{ + SwTxtNode *pNode = GetTxtNode(); + if( pNode != pActNode || !nActPos ) + nActPos = STRING_LEN; + + SwDoc* pDoc = pNode->GetDoc(); + SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); + + xub_StrLen nBegin = 0; + xub_StrLen nEnd = pNode->GetTxt().Len(); + xub_StrLen nLen; + BOOL bACWDirty = FALSE, bAnyWrd = FALSE; + + + if( nBegin < nEnd ) + { + USHORT nCnt = 200; + SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0, + WordType::DICTIONARY_WORD, nBegin, nEnd ); + while( aScanner.NextWord() ) + { + nBegin = aScanner.GetBegin(); + nLen = aScanner.GetLen(); + if( rACW.GetMinWordLen() <= nLen ) + { + const XubString& rWord = aScanner.GetWord(); + + if( nActPos < nBegin || ( nBegin + nLen ) < nActPos ) + { + if( rACW.GetMinWordLen() <= rWord.Len() ) + rACW.InsertWord( rWord, *pDoc ); + bAnyWrd = TRUE; + } + else + bACWDirty = TRUE; + } + if( !--nCnt ) + { + if ( Application::AnyInput( INPUT_ANY ) ) + return; + nCnt = 100; + } + } + } + + if( bAnyWrd && !bACWDirty ) + pNode->SetAutoCompleteWordDirty( FALSE ); +} + + +/************************************************************************* + * SwTxtNode::Hyphenate + *************************************************************************/ +// Findet den TxtFrm und sucht dessen CalcHyph + +BOOL SwTxtNode::Hyphenate( SwInterHyphInfo &rHyphInf ) +{ + // Abkuerzung: am Absatz ist keine Sprache eingestellt: + if ( LANGUAGE_NONE == USHORT( GetSwAttrSet().GetLanguage().GetLanguage() ) + && USHRT_MAX == GetLang( 0, m_Text.Len() ) ) + { + if( !rHyphInf.IsCheck() ) + rHyphInf.SetNoLang( TRUE ); + return FALSE; + } + + if( pLinguNode != this ) + { + pLinguNode = this; + pLinguFrm = (SwTxtFrm*)GetFrm( (Point*)(rHyphInf.GetCrsrPos()) ); + } + SwTxtFrm *pFrm = pLinguFrm; + if( pFrm ) + pFrm = &(pFrm->GetFrmAtOfst( rHyphInf.nStart )); + else + { + // 4935: Seit der Trennung ueber Sonderbereiche sind Faelle + // moeglich, in denen kein Frame zum Node vorliegt. + // Also kein ASSERT! +#if OSL_DEBUG_LEVEL > 1 + ASSERT( pFrm, "!SwTxtNode::Hyphenate: can't find any frame" ); +#endif + return FALSE; + } + + while( pFrm ) + { + if( pFrm->Hyphenate( rHyphInf ) ) + { + // Das Layout ist nicht robust gegen "Direktformatierung" + // (7821, 7662, 7408); vgl. layact.cxx, + // SwLayAction::_TurboAction(), if( !pCnt->IsValid() ... + pFrm->SetCompletePaint(); + return TRUE; + } + pFrm = (SwTxtFrm*)(pFrm->GetFollow()); + if( pFrm ) + { + rHyphInf.nLen = rHyphInf.nLen - (pFrm->GetOfst() - rHyphInf.nStart); + rHyphInf.nStart = pFrm->GetOfst(); + } + } + return FALSE; +} + +#ifdef LINGU_STATISTIK + +// globale Variable +SwLinguStatistik aSwLinguStat; + + +void SwLinguStatistik::Flush() +{ + if ( !nWords ) + return ; + + static char *pLogName = 0; + const BOOL bFirstOpen = pLogName ? FALSE : TRUE; + if( bFirstOpen ) + { + char *pPath = getenv( "TEMP" ); + char *pName = "swlingu.stk"; + if( !pPath ) + pLogName = pName; + else + { + const int nLen = strlen(pPath); + // fuer dieses new wird es kein delete geben. + pLogName = new char[nLen + strlen(pName) + 3]; + if(nLen && (pPath[nLen-1] == '\\') || (pPath[nLen-1] == '/')) + snprintf( pLogName, sizeof(pLogName), "%s%s", pPath, pName ); + else + snprintf( pLogName, sizeof(pLogName), "%s/%s", pPath, pName ); + } + } + SvFileStream aStream( String::CreateFromAscii(pLogName), (bFirstOpen + ? STREAM_WRITE | STREAM_TRUNC + : STREAM_WRITE )); + + if( !aStream.GetError() ) + { + if ( bFirstOpen ) + aStream << "\nLinguistik-Statistik\n"; + aStream << endl << ++nFlushCnt << ". Messung\n"; + aStream << "Rechtschreibung\n"; + aStream << "gepruefte Worte: \t" << nWords << endl; + aStream << "als fehlerhaft erkannt:\t" << nWrong << endl; + aStream << "Alternativvorschlaege:\t" << nAlter << endl; + if ( nWrong ) + aStream << "Durchschnitt:\t\t" << nAlter*1.0 / nWrong << endl; + aStream << "Dauer (msec):\t\t" << nSpellTime << endl; + aStream << "\nThesaurus\n"; + aStream << "Synonyme gesamt:\t" << nSynonym << endl; + if ( nSynonym ) + aStream << "Synonym-Durchschnitt:\t" << + nSynonym*1.0 / ( nWords - nNoSynonym ) << endl; + aStream << "ohne Synonyme:\t\t" << nNoSynonym << endl; + aStream << "Bedeutungen gesamt:\t" << nSynonym << endl; + aStream << "keine Bedeutungen:\t"<< nNoSynonym << endl; + aStream << "Dauer (msec):\t\t" << nTheTime << endl; + aStream << "\nHyphenator\n"; + aStream << "Trennstellen gesamt:\t" << nHyphens << endl; + if ( nHyphens ) + aStream << "Hyphen-Durchschnitt:\t" << + nHyphens*1.0 / ( nWords - nNoHyph - nHyphErr ) << endl; + aStream << "keine Trennstellen:\t" << nNoHyph << endl; + aStream << "Trennung verweigert:\t" << nHyphErr << endl; + aStream << "Dauer (msec):\t\t" << nHyphTime << endl; + aStream << "---------------------------------------------\n"; + } + nWords = nWrong = nAlter = nSynonym = nNoSynonym = + nHyphens = nNoHyph = nHyphErr = nSpellTime = nTheTime = + nHyphTime = 0; + //pThes = NULL; +} + +#endif + +// change text to Upper/Lower/Hiragana/Katagana/... +void SwTxtNode::TransliterateText( utl::TransliterationWrapper& rTrans, + xub_StrLen nStt, xub_StrLen nEnd, SwUndoTransliterate* pUndo ) +{ + if( nStt < nEnd ) + { + SwLanguageIterator* pIter; + if( rTrans.needLanguageForTheMode() ) + pIter = new SwLanguageIterator( *this, nStt ); + else + pIter = 0; + + xub_StrLen nEndPos; + sal_uInt16 nLang; + do { + if( pIter ) + { + nLang = pIter->GetLanguage(); + nEndPos = pIter->GetChgPos(); + if( nEndPos > nEnd ) + nEndPos = nEnd; + } + else + { + nLang = LANGUAGE_SYSTEM; + nEndPos = nEnd; + } + xub_StrLen nLen = nEndPos - nStt; + + Sequence <sal_Int32> aOffsets; + String sChgd( rTrans.transliterate( m_Text, nLang, nStt, nLen, + &aOffsets )); + if( !m_Text.Equals( sChgd, nStt, nLen ) ) + { + if ( pUndo ) + { + pUndo->AddChanges( *this, nStt, nLen, aOffsets ); + } + ReplaceTextOnly( nStt, nLen, sChgd, aOffsets ); + } + nStt = nEndPos; + } while( nEndPos < nEnd && pIter && pIter->Next() ); + delete pIter; + } +} + + +void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos, xub_StrLen nLen, + const XubString& rText, + const Sequence<sal_Int32>& rOffsets ) +{ + m_Text.Replace( nPos, nLen, rText ); + + xub_StrLen nTLen = rText.Len(); + const sal_Int32* pOffsets = rOffsets.getConstArray(); + // now look for no 1-1 mapping -> move the indizies! + xub_StrLen nI, nMyOff; + for( nI = 0, nMyOff = nPos; nI < nTLen; ++nI, ++nMyOff ) + { + xub_StrLen nOff = (xub_StrLen)pOffsets[ nI ]; + if( nOff < nMyOff ) + { + // something is inserted + xub_StrLen nCnt = 1; + while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] ) + ++nCnt; + + Update( SwIndex( this, nMyOff ), nCnt, FALSE ); + nMyOff = nOff; + //nMyOff -= nCnt; + nI += nCnt - 1; + } + else if( nOff > nMyOff ) + { + // something is deleted + Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, TRUE ); + nMyOff = nOff; + } + } + if( nMyOff < nLen ) + // something is deleted at the end + Update( SwIndex( this, nMyOff ), nLen - nMyOff, TRUE ); + + // notify the layout! + SwDelTxt aDelHint( nPos, nTLen ); + SwModify::Modify( 0, &aDelHint ); + + SwInsTxt aHint( nPos, nTLen ); + SwModify::Modify( 0, &aHint ); +} + +void SwTxtNode::CountWords( SwDocStat& rStat, + xub_StrLen nStt, xub_StrLen nEnd ) const +{ + ++rStat.nAllPara; // #i93174#: count _all_ paragraphs + if( nStt < nEnd ) + { + if ( !IsHidden() ) + { + ++rStat.nPara; + ULONG nTmpWords = 0; + ULONG nTmpChars = 0; + + // Shortcut: Whole paragraph should be considered and cached values + // are valid: + if ( 0 == nStt && GetTxt().Len() == nEnd && !IsWordCountDirty() ) + { + nTmpWords = GetParaNumberOfWords(); + nTmpChars = GetParaNumberOfChars(); + } + else + { + String aOldStr( m_Text ); + String& rCastStr = const_cast<String&>(m_Text); + + // fills the deleted redlines and hidden ranges with cChar: + const xub_Unicode cChar(' '); + const USHORT nNumOfMaskedChars = + lcl_MaskRedlinesAndHiddenText( *this, rCastStr, nStt, nEnd, cChar, false ); + + // expand fields + rtl::OUString aExpandText; + const ModelToViewHelper::ConversionMap* pConversionMap = + BuildConversionMap( aExpandText ); + + const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nStt ); + const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nEnd ); + + const bool bCount = aExpandText.getLength() > 0; + + // count words in 'regular' text: + if( bCount && pBreakIt->GetBreakIter().is() ) + { + const String aScannerText( aExpandText ); + SwScanner aScanner( *this, aScannerText, 0, pConversionMap, + i18n::WordType::WORD_COUNT, + (xub_StrLen)nExpandBegin, (xub_StrLen)nExpandEnd ); + + const rtl::OUString aBreakWord( CH_TXTATR_BREAKWORD ); + + while ( aScanner.NextWord() ) + { + if ( aScanner.GetLen() > 1 || + CH_TXTATR_BREAKWORD != aExpandText.match(aBreakWord, aScanner.GetBegin() ) ) + ++nTmpWords; + } + } + + ASSERT( aExpandText.getLength() >= nNumOfMaskedChars, + "More characters hidden that characters in string!" ) + nTmpChars = nExpandEnd - nExpandBegin - nNumOfMaskedChars; + + // count words in numbering string: + if ( nStt == 0 && bCount ) + { + // add numbering label + const String aNumString = GetNumString(); + const xub_StrLen nNumStringLen = aNumString.Len(); + if ( nNumStringLen > 0 ) + { + LanguageType aLanguage = GetLang( 0 ); + + SwScanner aScanner( *this, aNumString, &aLanguage, 0, + i18n::WordType::WORD_COUNT, + 0, nNumStringLen ); + + while ( aScanner.NextWord() ) + ++nTmpWords; + + nTmpChars += nNumStringLen; + } + else if ( HasBullet() ) + { + ++nTmpWords; + ++nTmpChars; + } + } + + delete pConversionMap; + + rCastStr = aOldStr; + + // If the whole paragraph has been calculated, update cached + // values: + if ( 0 == nStt && GetTxt().Len() == nEnd ) + { + SetParaNumberOfWords( nTmpWords ); + SetParaNumberOfChars( nTmpChars ); + SetWordCountDirty( false ); + } + } + + rStat.nWord += nTmpWords; + rStat.nChar += nTmpChars; + } + } +} + +// +// Paragraph statistics start +// +struct SwParaIdleData_Impl +{ + SwWrongList* pWrong; // for spell checking + SwGrammarMarkUp* pGrammarCheck; // for grammar checking / proof reading + SwWrongList* pSmartTags; + ULONG nNumberOfWords; + ULONG nNumberOfChars; + bool bWordCountDirty : 1; + bool bWrongDirty : 1; // Ist das Wrong-Feld auf invalid? + bool bGrammarCheckDirty : 1; + bool bSmartTagDirty : 1; + bool bAutoComplDirty : 1; // die ACompl-Liste muss angepasst werden + + SwParaIdleData_Impl() : + pWrong ( 0 ), + pGrammarCheck ( 0 ), + pSmartTags ( 0 ), + nNumberOfWords ( 0 ), + nNumberOfChars ( 0 ), + bWordCountDirty ( true ), + bWrongDirty ( true ), + bGrammarCheckDirty ( true ), + bSmartTagDirty ( true ), + bAutoComplDirty ( true ) {}; +}; + +void SwTxtNode::InitSwParaStatistics( bool bNew ) +{ + if ( bNew ) + { + m_pParaIdleData_Impl = new SwParaIdleData_Impl; + } + else if ( m_pParaIdleData_Impl ) + { + delete m_pParaIdleData_Impl->pWrong; + delete m_pParaIdleData_Impl->pGrammarCheck; + delete m_pParaIdleData_Impl->pSmartTags; + delete m_pParaIdleData_Impl; + m_pParaIdleData_Impl = 0; + } +} + +void SwTxtNode::SetWrong( SwWrongList* pNew, bool bDelete ) +{ + if ( m_pParaIdleData_Impl ) + { + if ( bDelete ) + { + delete m_pParaIdleData_Impl->pWrong; + } + m_pParaIdleData_Impl->pWrong = pNew; + } +} + +SwWrongList* SwTxtNode::GetWrong() +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; +} + +// --> OD 2008-05-27 #i71360# +const SwWrongList* SwTxtNode::GetWrong() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; +} +// <-- + + +void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete ) +{ + if ( m_pParaIdleData_Impl ) + { + if ( bDelete ) + { + delete m_pParaIdleData_Impl->pGrammarCheck; + } + m_pParaIdleData_Impl->pGrammarCheck = pNew; + } +} + +SwGrammarMarkUp* SwTxtNode::GetGrammarCheck() +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0; +} + +void SwTxtNode::SetSmartTags( SwWrongList* pNew, bool bDelete ) +{ + ASSERT( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(), + "Weird - we have a smart tag list without any recognizers?" ) + + if ( m_pParaIdleData_Impl ) + { + if ( bDelete ) + { + delete m_pParaIdleData_Impl->pSmartTags; + } + m_pParaIdleData_Impl->pSmartTags = pNew; + } +} + +SwWrongList* SwTxtNode::GetSmartTags() +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0; +} + +void SwTxtNode::SetParaNumberOfWords( ULONG nNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->nNumberOfWords = nNew; + } +} +ULONG SwTxtNode::GetParaNumberOfWords() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0; +} +void SwTxtNode::SetParaNumberOfChars( ULONG nNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->nNumberOfChars = nNew; + } +} +ULONG SwTxtNode::GetParaNumberOfChars() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0; +} +void SwTxtNode::SetWordCountDirty( bool bNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->bWordCountDirty = bNew; + } +} +bool SwTxtNode::IsWordCountDirty() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWordCountDirty : 0; +} +void SwTxtNode::SetWrongDirty( bool bNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->bWrongDirty = bNew; + } +} +bool SwTxtNode::IsWrongDirty() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWrongDirty : 0; +} +void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->bGrammarCheckDirty = bNew; + } +} +bool SwTxtNode::IsGrammarCheckDirty() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bGrammarCheckDirty : 0; +} +void SwTxtNode::SetSmartTagDirty( bool bNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->bSmartTagDirty = bNew; + } +} +bool SwTxtNode::IsSmartTagDirty() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bSmartTagDirty : 0; +} +void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const +{ + if ( m_pParaIdleData_Impl ) + { + m_pParaIdleData_Impl->bAutoComplDirty = bNew; + } +} +bool SwTxtNode::IsAutoCompleteWordDirty() const +{ + return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bAutoComplDirty : 0; +} +// +// Paragraph statistics end +// |