diff options
Diffstat (limited to 'sw/source/core/text')
72 files changed, 46685 insertions, 0 deletions
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx new file mode 100644 index 000000000000..1784ad946e41 --- /dev/null +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -0,0 +1,2221 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: EnhancedPDFExportHelper.cxx,v $ + * $Revision: 1.26.138.1 $ + * + * 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 <com/sun/star/embed/XEmbeddedObject.hpp> +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <EnhancedPDFExportHelper.hxx> +#include <hintids.hxx> + +#ifndef _OUTDEV_HXX +#include <vcl/outdev.hxx> +#endif +#include <tools/multisel.hxx> +#include <svx/adjitem.hxx> +#include <svx/lrspitem.hxx> +#include <svx/langitem.hxx> +#include <svx/scripttypeitem.hxx> +#include <tools/urlobj.hxx> +#include <svtools/zforlist.hxx> +#include <swatrset.hxx> +#include <frmatr.hxx> +#include <paratr.hxx> +#include <ndtxt.hxx> +#include <ndole.hxx> +#include <section.hxx> +#include <tox.hxx> +#include <fmtfld.hxx> +#include <txtinet.hxx> +#include <fmtinfmt.hxx> +#include <fchrfmt.hxx> +#include <charfmt.hxx> +#include <fmtanchr.hxx> +#include <fmturl.hxx> +#include <editsh.hxx> +#include <viscrs.hxx> +#include <txtfld.hxx> +#include <reffld.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <crsskip.hxx> +#include <mdiexp.hxx> +#include <docufld.hxx> +#include <ftnidx.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <txtfrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <sectfrm.hxx> +#include <flyfrm.hxx> +#include <notxtfrm.hxx> +#include <porfld.hxx> +#include <SwStyleNameMapper.hxx> +#include <itrpaint.hxx> +#include "i18npool/mslangid.hxx" +#include <IMark.hxx> +#include <SwNodeNum.hxx> + +#include <stack> + +#include <tools/globname.hxx> + +using namespace ::com::sun::star; + +// +// Some static data structures +// +TableColumnsMap SwEnhancedPDFExportHelper::aTableColumnsMap; +LinkIdMap SwEnhancedPDFExportHelper::aLinkIdMap; +NumListIdMap SwEnhancedPDFExportHelper::aNumListIdMap; +NumListBodyIdMap SwEnhancedPDFExportHelper::aNumListBodyIdMap; +FrmTagIdMap SwEnhancedPDFExportHelper::aFrmTagIdMap; + +LanguageType SwEnhancedPDFExportHelper::eLanguageDefault = 0; + +#ifndef PRODUCT + +static std::vector< USHORT > aStructStack; + +void lcl_DBGCheckStack() +{ + /* NonStructElement = 0 Document = 1 Part = 2 + * Article = 3 Section = 4 Division = 5 + * BlockQuote = 6 Caption = 7 TOC = 8 + * TOCI = 9 Index = 10 Paragraph = 11 + * Heading = 12 H1-6 = 13 - 18 List = 19 + * ListItem = 20 LILabel = 21 LIBody = 22 + * Table = 23 TableRow = 24 TableHeader = 25 + * TableData = 26 Span = 27 Quote = 28 + * Note = 29 Reference = 30 BibEntry = 31 + * Code = 32 Link = 33 Figure = 34 + * Formula = 35 Form = 36 Continued frame = 99 + */ + + USHORT nElement; + std::vector< USHORT >::iterator aIter; + for ( aIter = aStructStack.begin(); aIter != aStructStack.end(); ++aIter ) + { + nElement = *aIter; + } +} + +#endif + +namespace +{ +// ODF Style Names: +const String aTableHeadingName = String::CreateFromAscii("Table Heading"); +const String aQuotations = String::CreateFromAscii("Quotations"); +const String aCaption = String::CreateFromAscii("Caption"); +const String aHeading = String::CreateFromAscii("Heading"); +const String aQuotation = String::CreateFromAscii("Quotation"); +const String aSourceText = String::CreateFromAscii("Source Text"); + +// PDF Tag Names: +const String aDocumentString = String::CreateFromAscii("Document"); +const String aDivString = String::CreateFromAscii("Div"); +const String aSectString = String::CreateFromAscii("Sect"); +const String aHString = String::CreateFromAscii("H"); +const String aH1String = String::CreateFromAscii("H1"); +const String aH2String = String::CreateFromAscii("H2"); +const String aH3String = String::CreateFromAscii("H3"); +const String aH4String = String::CreateFromAscii("H4"); +const String aH5String = String::CreateFromAscii("H5"); +const String aH6String = String::CreateFromAscii("H6"); +const String aListString = String::CreateFromAscii("L"); +const String aListItemString = String::CreateFromAscii("LI"); +const String aListBodyString = String::CreateFromAscii("LBody"); +const String aBlockQuoteString = String::CreateFromAscii("BlockQuote"); +const String aCaptionString = String::CreateFromAscii("Caption"); +const String aIndexString = String::CreateFromAscii("Index"); +const String aTOCString = String::CreateFromAscii("TOC"); +const String aTOCIString = String::CreateFromAscii("TOCI"); +const String aTableString = String::CreateFromAscii("Table"); +const String aTRString = String::CreateFromAscii("TR"); +const String aTDString = String::CreateFromAscii("TD"); +const String aTHString = String::CreateFromAscii("TH"); +const String aBibEntryString = String::CreateFromAscii("BibEntry"); +const String aQuoteString = String::CreateFromAscii("Quote"); +const String aSpanString = String::CreateFromAscii("Span"); +const String aCodeString = String::CreateFromAscii("Code"); +const String aFigureString = String::CreateFromAscii("Figure"); +const String aFormulaString = String::CreateFromAscii("Formula"); +const String aLinkString = String::CreateFromAscii("Link"); +const String aNoteString = String::CreateFromAscii("Note"); +const String aEmptyString = String::CreateFromAscii(""); + +// returns true if first paragraph in cell frame has 'table heading' style +bool lcl_IsHeadlineCell( const SwCellFrm& rCellFrm ) +{ + bool bRet = false; + + const SwCntntFrm *pCnt = rCellFrm.ContainsCntnt(); + if ( pCnt && pCnt->IsTxtFrm() ) + { + const SwTxtNode* pTxtNode = static_cast<const SwTxtFrm*>(pCnt)->GetTxtNode(); + const SwFmt* pTxtFmt = pTxtNode->GetFmtColl(); + + String sStyleName; + SwStyleNameMapper::FillProgName( pTxtFmt->GetName(), sStyleName, nsSwGetPoolIdFromName::GET_POOLID_TXTCOLL, sal_True ); + bRet = sStyleName == aTableHeadingName; + } + + return bRet; +} + +// List all frames for which the NonStructElement tag is set: +bool lcl_IsInNonStructEnv( const SwFrm& rFrm ) +{ + bool bRet = false; + + if ( 0 != rFrm.FindFooterOrHeader() && + !rFrm.IsHeaderFrm() && !rFrm.IsFooterFrm() ) + { + bRet = true; + } + else if ( rFrm.IsInTab() && !rFrm.IsTabFrm() ) + { + const SwTabFrm* pTabFrm = rFrm.FindTabFrm(); + if ( rFrm.GetUpper() != pTabFrm && + pTabFrm->IsFollow() && pTabFrm->IsInHeadline( rFrm ) ) + bRet = true; + } + + return bRet; +} + +// Generate key from frame for reopening tags: +void* lcl_GetKeyFromFrame( const SwFrm& rFrm ) +{ + void* pKey = 0; + + if ( rFrm.IsPageFrm() ) + pKey = (void*)static_cast<const SwPageFrm&>(rFrm).GetFmt()->getIDocumentSettingAccess(); + else if ( rFrm.IsTxtFrm() ) + pKey = (void*)static_cast<const SwTxtFrm&>(rFrm).GetTxtNode(); + else if ( rFrm.IsSctFrm() ) + pKey = (void*)static_cast<const SwSectionFrm&>(rFrm).GetSection(); + else if ( rFrm.IsTabFrm() ) + pKey = (void*)static_cast<const SwTabFrm&>(rFrm).GetTable(); + else if ( rFrm.IsRowFrm() ) + pKey = (void*)static_cast<const SwRowFrm&>(rFrm).GetTabLine(); + else if ( rFrm.IsCellFrm() ) + { + const SwTabFrm* pTabFrm = rFrm.FindTabFrm(); + const SwTable* pTable = pTabFrm->GetTable(); + pKey = (void*) & static_cast<const SwCellFrm&>(rFrm).GetTabBox()->FindStartOfRowSpan( *pTable ); + } + + return pKey; +} + +bool lcl_HasPreviousParaSameNumRule( const SwTxtNode& rNode ) +{ + bool bRet = false; + SwNodeIndex aIdx( rNode ); + const SwDoc* pDoc = rNode.GetDoc(); + const SwNodes& rNodes = pDoc->GetNodes(); + const SwNode* pNode = &rNode; + const SwNumRule* pNumRule = rNode.GetNumRule(); + + while (! (pNode == rNodes.DocumentSectionStartNode((SwNode*)&rNode) ) ) + { + --aIdx; + + if (aIdx.GetNode().IsTxtNode()) + { + const SwTxtNode* pPrevTxtNd = aIdx.GetNode().GetTxtNode(); + const SwNumRule * pPrevNumRule = pPrevTxtNd->GetNumRule(); + + // We find the previous text node. Now check, if the previous text node + // has the same numrule like rNode: + if ( (pPrevNumRule == pNumRule) && + (!pPrevTxtNd->IsOutline() == !rNode.IsOutline())) + bRet = true; + + break; + } + + pNode = &aIdx.GetNode(); + } + return bRet; +} + +} // end namespace + +/* + * SwTaggedPDFHelper::SwTaggedPDFHelper() + */ +SwTaggedPDFHelper::SwTaggedPDFHelper( const Num_Info* pNumInfo, + const Frm_Info* pFrmInfo, + const Por_Info* pPorInfo, + OutputDevice& rOut ) + : nEndStructureElement( 0 ), + nRestoreCurrentTag( -1 ), + mpNumInfo( pNumInfo ), + mpFrmInfo( pFrmInfo ), + mpPorInfo( pPorInfo ) +{ + mpPDFExtOutDevData = + PTR_CAST( vcl::PDFExtOutDevData, rOut.GetExtOutDevData() ); + + if ( mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportTaggedPDF() ) + { +#ifndef PRODUCT + sal_Int32 nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement(); + lcl_DBGCheckStack(); +#endif + if ( mpNumInfo ) + BeginNumberedListStructureElements(); + else if ( mpFrmInfo ) + BeginBlockStructureElements(); + else if ( mpPorInfo ) + BeginInlineStructureElements(); + else + BeginTag( vcl::PDFWriter::NonStructElement, aEmptyString ); + +#ifndef PRODUCT + nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement(); + lcl_DBGCheckStack(); +#endif + } +} + + +/* + * SwTaggedPDFHelper::~SwTaggedPDFHelper() + */ +SwTaggedPDFHelper::~SwTaggedPDFHelper() +{ + if ( mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportTaggedPDF() ) + { +#ifndef PRODUCT + sal_Int32 nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement(); + lcl_DBGCheckStack(); +#endif + EndStructureElements(); + +#ifndef PRODUCT + nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement(); + lcl_DBGCheckStack(); +#endif + + } +} + +/* + * SwTaggedPDFHelper::CheckReopenTag() + */ +bool SwTaggedPDFHelper::CheckReopenTag() +{ + bool bRet = false; + sal_Int32 nReopenTag = -1; + bool bContinue = false; // in some cases we just have to reopen a tag without early returning + + if ( mpFrmInfo ) + { + const SwFrm& rFrm = mpFrmInfo->mrFrm; + const SwFrm* pKeyFrm = 0; + void* pKey = 0; + + // Reopen an existing structure element if + // - rFrm is not the first page frame (reopen Document tag) + // - rFrm is a follow frame (reopen Master tag) + // - rFrm is a fly frame anchored at content (reopen Anchor paragraph tag) + // - rFrm is a fly frame anchord at page (reopen Document tag) + // - rFrm is a follow flow row (reopen TableRow tag) + // - rFrm is a cell frame in a follow flow row (reopen TableData tag) + if ( ( rFrm.IsPageFrm() && static_cast<const SwPageFrm&>(rFrm).GetPrev() ) || + ( rFrm.IsFlowFrm() && SwFlowFrm::CastFlowFrm(&rFrm)->IsFollow() ) || + ( rFrm.IsRowFrm() && rFrm.IsInFollowFlowRow() ) || + ( rFrm.IsCellFrm() && const_cast<SwFrm&>(rFrm).GetPrevCellLeaf( MAKEPAGE_NONE ) ) ) + { + pKeyFrm = &rFrm; + } + else if ( rFrm.IsFlyFrm() ) + { + const SwFmtAnchor& rAnchor = + static_cast<const SwFlyFrm*>(&rFrm)->GetFmt()->GetAnchor(); + if ( FLY_AT_CNTNT == rAnchor.GetAnchorId() || + FLY_AUTO_CNTNT == rAnchor.GetAnchorId() || + FLY_PAGE == rAnchor.GetAnchorId() ) + { + pKeyFrm = static_cast<const SwFlyFrm&>(rFrm).GetAnchorFrm(); + bContinue = true; + } + } + + if ( pKeyFrm ) + { + pKey = lcl_GetKeyFromFrame( *pKeyFrm ); + + if ( pKey ) + { + FrmTagIdMap& rFrmTagIdMap = SwEnhancedPDFExportHelper::GetFrmTagIdMap(); + const FrmTagIdMap::const_iterator aIter = rFrmTagIdMap.find( pKey ); + nReopenTag = (*aIter).second; + } + } + } + + if ( -1 != nReopenTag ) + { + nRestoreCurrentTag = mpPDFExtOutDevData->GetCurrentStructureElement(); + const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( nReopenTag ); + ASSERT( bSuccess, "Failed to reopen tag" ) + +#ifndef PRODUCT + aStructStack.push_back( 99 ); +#endif + + bRet = bSuccess; + } + + return bRet && !bContinue; +} + + +/* + * SwTaggedPDFHelper::CheckRestoreTag() + */ +bool SwTaggedPDFHelper::CheckRestoreTag() const +{ + bool bRet = false; + if ( nRestoreCurrentTag != -1 ) + { + const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( nRestoreCurrentTag ); + (void)bSuccess; + ASSERT( bSuccess, "Failed to restore reopened tag" ) + +#ifndef PRODUCT + aStructStack.pop_back(); +#endif + + bRet = true; + } + + return bRet; +} + + +/* + * SwTaggedPDFHelper::BeginTag() + */ +void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const String& rString ) +{ + // write new tag + const sal_Int32 nId = mpPDFExtOutDevData->BeginStructureElement( eType, rtl::OUString( rString ) ); + ++nEndStructureElement; + +#ifndef PRODUCT + aStructStack.push_back( static_cast<USHORT>(eType) ); +#endif + + // Store the id of the current structure element if + // - it is a list structure element + // - it is a list body element with children + // - rFrm is the first page frame + // - rFrm is a master frame + // - rFrm has objects anchored to it + // - rFrm is a row frame or cell frame in a split table row + + if ( mpNumInfo ) + { + const SwTxtFrm& rTxtFrm = static_cast<const SwTxtFrm&>(mpNumInfo->mrFrm); + const SwTxtNode* pTxtNd = rTxtFrm.GetTxtNode(); + const SwNodeNum* pNodeNum = pTxtNd->GetNum(); + + if ( vcl::PDFWriter::List == eType ) + { + NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap(); + rNumListIdMap[ pNodeNum ] = nId; + } + else if ( vcl::PDFWriter::LIBody == eType ) + { + NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap(); + rNumListBodyIdMap[ pNodeNum ] = nId; + } + } + else if ( mpFrmInfo ) + { + const SwFrm& rFrm = mpFrmInfo->mrFrm; + + if ( ( rFrm.IsPageFrm() && !static_cast<const SwPageFrm&>(rFrm).GetPrev() ) || + ( rFrm.IsFlowFrm() && !SwFlowFrm::CastFlowFrm(&rFrm)->IsFollow() && SwFlowFrm::CastFlowFrm(&rFrm)->HasFollow() ) || + ( rFrm.IsTxtFrm() && rFrm.GetDrawObjs() ) || + ( rFrm.IsRowFrm() && rFrm.IsInSplitTableRow() ) || + ( rFrm.IsCellFrm() && const_cast<SwFrm&>(rFrm).GetNextCellLeaf( MAKEPAGE_NONE ) ) ) + { + const void* pKey = lcl_GetKeyFromFrame( rFrm ); + + if ( pKey ) + { + FrmTagIdMap& rFrmTagIdMap = SwEnhancedPDFExportHelper::GetFrmTagIdMap(); + rFrmTagIdMap[ pKey ] = nId; + } + } + } + + SetAttributes( eType ); +} + + +/* + * SwTaggedPDFHelper::EndTag() + */ +void SwTaggedPDFHelper::EndTag() +{ + mpPDFExtOutDevData->EndStructureElement(); + +#ifndef PRODUCT + aStructStack.pop_back(); +#endif +} + + +/* + * SwTaggedPDFHelper::SetAttributes() + * + * Sets the attributes according to the structure type. + */ +void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType ) +{ + vcl::PDFWriter::StructAttributeValue eVal; + sal_Int32 nVal; + + /* + * ATTRIBUTES FOR BLSE + */ + if ( mpFrmInfo ) + { + const SwFrm* pFrm = &mpFrmInfo->mrFrm; + SWRECTFN( pFrm ) + + bool bPlacement = false; + bool bWritingMode = false; + bool bSpaceBefore = false; + bool bSpaceAfter = false; + bool bStartIndent = false; + bool bEndIndent = false; + bool bTextIndent = false; + bool bTextAlign = false; + bool bAlternateText = false; + bool bWidth = false; + bool bHeight = false; + bool bBox = false; + bool bRowSpan = false; + + // + // Check which attributes to set: + // + switch ( eType ) + { + case vcl::PDFWriter::Document : + bWritingMode = true; + break; + + case vcl::PDFWriter::Table : + bPlacement = + bWritingMode = + bSpaceBefore = + bSpaceAfter = + bStartIndent = + bEndIndent = + bWidth = + bHeight = + bBox = true; + break; + + case vcl::PDFWriter::TableRow : + bPlacement = + bWritingMode = true; + break; + + case vcl::PDFWriter::TableHeader : + case vcl::PDFWriter::TableData : + bPlacement = + bWritingMode = + bWidth = + bHeight = + bRowSpan = true; + break; + + case vcl::PDFWriter::H1 : + case vcl::PDFWriter::H2 : + case vcl::PDFWriter::H3 : + case vcl::PDFWriter::H4 : + case vcl::PDFWriter::H5 : + case vcl::PDFWriter::H6 : + case vcl::PDFWriter::Paragraph : + case vcl::PDFWriter::Heading : + case vcl::PDFWriter::Caption : + case vcl::PDFWriter::BlockQuote : + + bPlacement = + bWritingMode = + bSpaceBefore = + bSpaceAfter = + bStartIndent = + bEndIndent = + bTextIndent = + bTextAlign = true; + break; + + case vcl::PDFWriter::Formula : + case vcl::PDFWriter::Figure : + bPlacement = + bAlternateText = + bWidth = + bHeight = + bBox = true; + break; + default : + break; + } + + // + // Set the attributes: + // + if ( bPlacement ) + { + eVal = vcl::PDFWriter::TableHeader == eType || + vcl::PDFWriter::TableData == eType ? + vcl::PDFWriter::Inline : + vcl::PDFWriter::Block; + + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::Placement, eVal ); + } + + if ( bWritingMode ) + { + eVal = pFrm->IsVertical() ? + vcl::PDFWriter::TbRl : + pFrm->IsRightToLeft() ? + vcl::PDFWriter::RlTb : + vcl::PDFWriter::LrTb; + + if ( vcl::PDFWriter::LrTb != eVal ) + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::WritingMode, eVal ); + } + + if ( bSpaceBefore ) + { + nVal = (pFrm->*fnRect->fnGetTopMargin)(); + if ( 0 != nVal ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceBefore, nVal ); + } + + if ( bSpaceAfter ) + { + nVal = (pFrm->*fnRect->fnGetBottomMargin)(); + if ( 0 != nVal ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceAfter, nVal ); + } + + if ( bStartIndent ) + { + nVal = (pFrm->*fnRect->fnGetLeftMargin)(); + if ( 0 != nVal ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::StartIndent, nVal ); + } + + if ( bEndIndent ) + { + nVal = (pFrm->*fnRect->fnGetRightMargin)(); + if ( 0 != nVal ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::EndIndent, nVal ); + } + + if ( bTextIndent ) + { + ASSERT( pFrm->IsTxtFrm(), "Frame type <-> tag attribute mismatch" ) + const SvxLRSpaceItem &rSpace = + static_cast<const SwTxtFrm*>(pFrm)->GetTxtNode()->GetSwAttrSet().GetLRSpace(); + nVal = rSpace.GetTxtFirstLineOfst(); + if ( 0 != nVal ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::TextIndent, nVal ); + } + + if ( bTextAlign ) + { + ASSERT( pFrm->IsTxtFrm(), "Frame type <-> tag attribute mismatch" ) + const SwAttrSet& aSet = static_cast<const SwTxtFrm*>(pFrm)->GetTxtNode()->GetSwAttrSet(); + const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust(); + if ( SVX_ADJUST_BLOCK == nAdjust || SVX_ADJUST_CENTER == nAdjust || + ( (pFrm->IsRightToLeft() && SVX_ADJUST_LEFT == nAdjust) || + (!pFrm->IsRightToLeft() && SVX_ADJUST_RIGHT == nAdjust) ) ) + { + eVal = SVX_ADJUST_BLOCK == nAdjust ? + vcl::PDFWriter::Justify : + SVX_ADJUST_CENTER == nAdjust ? + vcl::PDFWriter::Center : + vcl::PDFWriter::End; + + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextAlign, eVal ); + } + } + + if ( bAlternateText ) + { + ASSERT( pFrm->IsFlyFrm(), "Frame type <-> tag attribute mismatch" ) + const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pFrm); + if ( pFly->Lower() && pFly->Lower()->IsNoTxtFrm() ) + { + const SwNoTxtFrm* pNoTxtFrm = static_cast<const SwNoTxtFrm*>(pFly->Lower()); + const SwNoTxtNode* pNoTxtNode = static_cast<const SwNoTxtNode*>(pNoTxtFrm->GetNode()); + + const String aAlternateTxt( pNoTxtNode->GetTitle() ); + mpPDFExtOutDevData->SetAlternateText( aAlternateTxt ); + } + } + + if ( bWidth ) + { + nVal = (pFrm->Frm().*fnRect->fnGetWidth)(); + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Width, nVal ); + } + + if ( bHeight ) + { + nVal = (pFrm->Frm().*fnRect->fnGetHeight)(); + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Height, nVal ); + } + + if ( bBox ) + { + // BBox only for non-split tables: + if ( vcl::PDFWriter::Table != eType || + ( pFrm->IsTabFrm() && + !static_cast<const SwTabFrm*>(pFrm)->IsFollow() && + !static_cast<const SwTabFrm*>(pFrm)->HasFollow() ) ) + mpPDFExtOutDevData->SetStructureBoundingBox( pFrm->Frm().SVRect() ); + } + + if ( bRowSpan ) + { + const SwCellFrm* pThisCell = dynamic_cast<const SwCellFrm*>(pFrm); + if ( pThisCell ) + { + nVal = pThisCell->GetTabBox()->getRowSpan(); + if ( nVal > 1 ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::RowSpan, nVal ); + + // calculate colspan: + const SwTabFrm* pTabFrm = pThisCell->FindTabFrm(); + const SwTable* pTable = pTabFrm->GetTable(); + + SWRECTFNX( pTabFrm ) + + const TableColumnsMapEntry& rCols = SwEnhancedPDFExportHelper::GetTableColumnsMap()[ pTable ]; + + const long nLeft = (pThisCell->Frm().*fnRectX->fnGetLeft)(); + const long nRight = (pThisCell->Frm().*fnRectX->fnGetRight)(); + const TableColumnsMapEntry::const_iterator aLeftIter = rCols.find( nLeft ); + const TableColumnsMapEntry::const_iterator aRightIter = rCols.find( nRight ); + + ASSERT( aLeftIter != rCols.end() && aRightIter != rCols.end(), "Colspan trouble" ) + if ( aLeftIter != rCols.end() && aRightIter != rCols.end() ) + { + nVal = std::distance( aLeftIter, aRightIter ); + if ( nVal > 1 ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::ColSpan, nVal ); + } + } + } + } + + /* + * ATTRIBUTES FOR ILSE + */ + else if ( mpPorInfo ) + { + const SwLinePortion* pPor = &mpPorInfo->mrPor; + const SwTxtPaintInfo& rInf = mpPorInfo->mrTxtPainter.GetInfo(); + + bool bActualText = false; + bool bBaselineShift = false; + bool bTextDecorationType = false; + bool bLinkAttribute = false; + bool bLanguage = false; + + // + // Check which attributes to set: + // + switch ( eType ) + { + case vcl::PDFWriter::Span : + case vcl::PDFWriter::Quote : + case vcl::PDFWriter::Code : + if( POR_HYPHSTR == pPor->GetWhichPor() || POR_SOFTHYPHSTR == pPor->GetWhichPor() ) + bActualText = true; + else + { + bBaselineShift = + bTextDecorationType = + bLanguage = true; + } + break; + + case vcl::PDFWriter::Link : + bTextDecorationType = + bBaselineShift = + bLinkAttribute = + bLanguage = true; + break; + + default: + break; + } + + if ( bActualText ) + { + const String aActualTxt( rInf.GetTxt(), rInf.GetIdx(), pPor->GetLen() ); + mpPDFExtOutDevData->SetActualText( aActualTxt ); + } + + if ( bBaselineShift ) + { + // TODO: Calculate correct values! + nVal = rInf.GetFont()->GetEscapement(); + if ( nVal > 0 ) nVal = 33; + else if ( nVal < 0 ) nVal = -33; + + if ( 0 != nVal ) + { + nVal = nVal * pPor->Height() / 100; + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::BaselineShift, nVal ); + } + } + + if ( bTextDecorationType ) + { + if ( UNDERLINE_NONE != rInf.GetFont()->GetUnderline() ) + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::Underline ); + if ( UNDERLINE_NONE != rInf.GetFont()->GetOverline() ) + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::Overline ); + if ( STRIKEOUT_NONE != rInf.GetFont()->GetStrikeout() ) + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::LineThrough ); + if ( EMPHASISMARK_NONE != rInf.GetFont()->GetEmphasisMark() ) + mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::Overline ); + } + + if ( bLanguage ) + { + + const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage(); + const LanguageType nDefaultLang = SwEnhancedPDFExportHelper::GetDefaultLanguage(); + + if ( nDefaultLang != nCurrentLanguage ) + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Language, nCurrentLanguage ); + } + + if ( bLinkAttribute ) + { + const LinkIdMap& rLinkIdMap = SwEnhancedPDFExportHelper::GetLinkIdMap(); + SwRect aPorRect; + rInf.CalcRect( *pPor, &aPorRect ); + const Point aPorCenter = aPorRect.Center(); + LinkIdMap::const_iterator aIter; + for ( aIter = rLinkIdMap.begin(); aIter != rLinkIdMap.end(); ++aIter ) + { + const SwRect& rLinkRect = (*aIter).first; + if ( rLinkRect.IsInside( aPorCenter ) ) + { + sal_Int32 nLinkId = (*aIter).second; + mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::LinkAnnotation, nLinkId ); + break; + } + } + } + } +} + +/* + * SwTaggedPDFHelper::BeginNumberedListStructureElements() + */ +void SwTaggedPDFHelper::BeginNumberedListStructureElements() +{ + ASSERT( mpNumInfo, "List without mpNumInfo?" ) + if ( !mpNumInfo ) + return; + + const SwFrm& rFrm = mpNumInfo->mrFrm; + ASSERT( rFrm.IsTxtFrm(), "numbered only for text frames" ) + const SwTxtFrm& rTxtFrm = static_cast<const SwTxtFrm&>(rFrm); + + // + // Lowers of NonStructureElements should not be considered: + // + if ( lcl_IsInNonStructEnv( rTxtFrm ) || rTxtFrm.IsFollow() ) + return; + + const SwTxtNode* pTxtNd = rTxtFrm.GetTxtNode(); + const SwNumRule* pNumRule = pTxtNd->GetNumRule(); + const SwNodeNum* pNodeNum = pTxtNd->GetNum(); + + const bool bNumbered = !pTxtNd->IsOutline() && pNodeNum && pNodeNum->GetParent() && pNumRule; + + // Check, if we have to reopen a list or a list body: + // First condition: + // Paragraph is numbered/bulleted + if ( !bNumbered ) + return; + + const SwNumberTreeNode* pParent = pNodeNum->GetParent(); + const bool bSameNumbering = lcl_HasPreviousParaSameNumRule(*pTxtNd); + + // Second condition: current numbering is not 'interrupted' + if ( bSameNumbering ) + { + sal_Int32 nReopenTag = -1; + + // Two cases: + // 1. We have to reopen an existing list body tag: + // - If the current node is either the first child of its parent + // and its level > 1 or + // - Numbering should restart at the current node and its level > 1 + // - The current item has no label + const bool bNewSubListStart = pParent->GetParent() && (pParent->IsFirst( pNodeNum ) || pTxtNd->IsListRestart() ); + const bool bNoLabel = !pTxtNd->IsCountedInList() && !pTxtNd->IsListRestart(); + if ( bNewSubListStart || bNoLabel ) + { + // Fine, we try to reopen the appropriate list body + NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap(); + + if ( bNewSubListStart ) + { + // The list body tag associated with the parent has to be reopened + // to start a new list inside the list body + NumListBodyIdMap::const_iterator aIter; + + do + aIter = rNumListBodyIdMap.find( pParent ); + while ( aIter == rNumListBodyIdMap.end() && 0 != ( pParent = pParent->GetParent() ) ); + + if ( aIter != rNumListBodyIdMap.end() ) + nReopenTag = (*aIter).second; + } + else // if(bNoLabel) + { + // The list body tag of a 'counted' predecessor has to be reopened + const SwNumberTreeNode* pPrevious = pNodeNum->GetPred(true); + while ( pPrevious ) + { + if ( pPrevious->IsCounted()) + { + // get id of list body tag + const NumListBodyIdMap::const_iterator aIter = rNumListBodyIdMap.find( pPrevious ); + if ( aIter != rNumListBodyIdMap.end() ) + { + nReopenTag = (*aIter).second; + break; + } + } + pPrevious = pPrevious->GetPred(true); + } + } + } + // 2. We have to reopen an existing list tag: + else if ( !pParent->IsFirst( pNodeNum ) && !pTxtNd->IsListRestart() ) + { + // any other than the first node in a list level has to reopen the current + // list. The current list is associated in a map with the first child of the list: + NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap(); + + // Search backwards and check if any of the previous nodes has a list associated with it: + const SwNumberTreeNode* pPrevious = pNodeNum->GetPred(true); + while ( pPrevious ) + { + // get id of list tag + const NumListIdMap::const_iterator aIter = rNumListIdMap.find( pPrevious ); + if ( aIter != rNumListIdMap.end() ) + { + nReopenTag = (*aIter).second; + break; + } + + pPrevious = pPrevious->GetPred(true); + } + } + + if ( -1 != nReopenTag ) + { + nRestoreCurrentTag = mpPDFExtOutDevData->GetCurrentStructureElement(); + mpPDFExtOutDevData->SetCurrentStructureElement( nReopenTag ); + +#ifndef PRODUCT + aStructStack.push_back( 99 ); +#endif + } + } + else + { + // clear list maps in case a list has been interrupted + NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap(); + rNumListIdMap.clear(); + NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap(); + rNumListBodyIdMap.clear(); + } + + // New tags: + const bool bNewListTag = (pNodeNum->GetParent()->IsFirst( pNodeNum ) || pTxtNd->IsListRestart() || !bSameNumbering); + const bool bNewItemTag = bNewListTag || pTxtNd->IsCountedInList(); // If the text node is not counted, we do not start a new list item: + + if ( bNewListTag ) + BeginTag( vcl::PDFWriter::List, aListString ); + + if ( bNewItemTag ) + { + BeginTag( vcl::PDFWriter::ListItem, aListItemString ); + BeginTag( vcl::PDFWriter::LIBody, aListBodyString ); + } +} + +/* + * SwTaggedPDFHelper::BeginBlockStructureElements() + */ +void SwTaggedPDFHelper::BeginBlockStructureElements() +{ + const SwFrm* pFrm = &mpFrmInfo->mrFrm; + + // + // Lowers of NonStructureElements should not be considered: + // + if ( lcl_IsInNonStructEnv( *pFrm ) ) + return; + + // Check if we have to reopen an existing structure element. + // This has to be done e.g., if pFrm is a follow frame. + if ( CheckReopenTag() ) + return; + + USHORT nPDFType = USHRT_MAX; + String aPDFType; + + switch ( pFrm->GetType() ) + { + /* + * GROUPING ELEMENTS + */ + + case FRM_PAGE : + // + // Document: Document + // + nPDFType = vcl::PDFWriter::Document; + aPDFType = aDocumentString; + break; + + case FRM_HEADER : + case FRM_FOOTER : + // + // Header, Footer: NonStructElement + // + nPDFType = vcl::PDFWriter::NonStructElement; + break; + + case FRM_FTNCONT : + // + // Footnote container: Division + // + nPDFType = vcl::PDFWriter::Division; + aPDFType = aDivString; + break; + + case FRM_FTN : + // + // Footnote frame: Note + // + // Note: vcl::PDFWriter::Note is actually a ILSE. Nevertheless + // we treat it like a grouping element! + nPDFType = vcl::PDFWriter::Note; + aPDFType = aNoteString; + break; + + case FRM_SECTION : + // + // Section: TOX, Index, or Sect + // + { + const SwSection* pSection = + static_cast<const SwSectionFrm*>(pFrm)->GetSection(); + if ( TOX_CONTENT_SECTION == pSection->GetType() ) + { + const SwTOXBase* pTOXBase = pSection->GetTOXBase(); + if ( pTOXBase ) + { + if ( TOX_INDEX == pTOXBase->GetType() ) + { + nPDFType = vcl::PDFWriter::Index; + aPDFType = aIndexString; + } + else + { + nPDFType = vcl::PDFWriter::TOC; + aPDFType = aTOCString; + } + } + } + else if ( CONTENT_SECTION == pSection->GetType() ) + { + nPDFType = vcl::PDFWriter::Section; + aPDFType = aSectString; + } + } + break; + + /* + * BLOCK-LEVEL STRUCTURE ELEMENTS + */ + + case FRM_TXT : + { + const SwTxtNode* pTxtNd = + static_cast<const SwTxtFrm*>(pFrm)->GetTxtNode(); + + const SwFmt* pTxtFmt = pTxtNd->GetFmtColl(); + const SwFmt* pParentTxtFmt = pTxtFmt->DerivedFrom(); + + String sStyleName; + String sParentStyleName; + + if ( pTxtFmt) + SwStyleNameMapper::FillProgName( pTxtFmt->GetName(), sStyleName, nsSwGetPoolIdFromName::GET_POOLID_TXTCOLL, sal_True ); + if ( pParentTxtFmt) + SwStyleNameMapper::FillProgName( pParentTxtFmt->GetName(), sParentStyleName, nsSwGetPoolIdFromName::GET_POOLID_TXTCOLL, sal_True ); + + // This is the default. If the paragraph could not be mapped to + // any of the standard pdf tags, we write a user defined tag + // <stylename> with role = P + nPDFType = static_cast<USHORT>(vcl::PDFWriter::Paragraph); + aPDFType = sStyleName; + + // + // Quotations: BlockQuote + // + if ( sStyleName == aQuotations ) + { + nPDFType = static_cast<USHORT>(vcl::PDFWriter::BlockQuote); + aPDFType = aBlockQuoteString; + } + + // + // Caption: Caption + // + else if ( sStyleName == aCaption) + { + nPDFType = static_cast<USHORT>(vcl::PDFWriter::Caption); + aPDFType = aCaptionString; + } + + // + // Caption: Caption + // + else if ( sParentStyleName == aCaption) + { + nPDFType = static_cast<USHORT>(vcl::PDFWriter::Caption); + aPDFType = sStyleName.Append(aCaptionString); + } + + // + // Heading: H + // + else if ( sStyleName == aHeading ) + { + nPDFType = static_cast<USHORT>(vcl::PDFWriter::Heading); + aPDFType = aHString; + } + + // + // Heading: H1 - H6 + // + if ( pTxtNd->IsOutline() ) + { + //int nRealLevel = pTxtNd->GetOutlineLevel(); //#outline level,zhaojianwei + int nRealLevel = pTxtNd->GetAttrOutlineLevel()-1; //<-end,zhaojianwei + nRealLevel = nRealLevel > 5 ? 5 : nRealLevel; + + nPDFType = static_cast<USHORT>(vcl::PDFWriter::H1 + nRealLevel); + switch(nRealLevel) + { + case 0 : + aPDFType = aH1String; + break; + case 1 : + aPDFType = aH2String; + break; + case 2 : + aPDFType = aH3String; + break; + case 3 : + aPDFType = aH4String; + break; + case 4 : + aPDFType = aH5String; + break; + default: + aPDFType = aH6String; + break; + } + } + + // + // Section: TOCI + // + else if ( pFrm->IsInSct() ) + { + const SwSectionFrm* pSctFrm = pFrm->FindSctFrm(); + const SwSection* pSection = + static_cast<const SwSectionFrm*>(pSctFrm)->GetSection(); + + if ( TOX_CONTENT_SECTION == pSection->GetType() ) + { + const SwTOXBase* pTOXBase = pSection->GetTOXBase(); + if ( pTOXBase && TOX_INDEX != pTOXBase->GetType() ) + { + // Special case: Open additional TOCI tag: + BeginTag( vcl::PDFWriter::TOCI, aTOCIString ); + } + } + } + } + break; + + case FRM_TAB : + // + // TabFrm: Table + // + nPDFType = vcl::PDFWriter::Table; + aPDFType = aTableString; + + { + // set up table column data: + const SwTabFrm* pTabFrm = static_cast<const SwTabFrm*>(pFrm); + const SwTable* pTable = pTabFrm->GetTable(); + + TableColumnsMap& rTableColumnsMap = SwEnhancedPDFExportHelper::GetTableColumnsMap(); + const TableColumnsMap::const_iterator aIter = rTableColumnsMap.find( pTable ); + + if ( aIter == rTableColumnsMap.end() ) + { + SWRECTFN( pTabFrm ) + TableColumnsMapEntry& rCols = rTableColumnsMap[ pTable ]; + + const SwTabFrm* pMasterFrm = pTabFrm->IsFollow() ? pTabFrm->FindMaster( true ) : pTabFrm; + + while ( pMasterFrm ) + { + const SwRowFrm* pRowFrm = static_cast<const SwRowFrm*>(pMasterFrm->GetLower()); + + while ( pRowFrm ) + { + const SwFrm* pCellFrm = pRowFrm->GetLower(); + + const long nLeft = (pCellFrm->Frm().*fnRect->fnGetLeft)(); + rCols.insert( nLeft ); + + while ( pCellFrm ) + { + const long nRight = (pCellFrm->Frm().*fnRect->fnGetRight)(); + rCols.insert( nRight ); + pCellFrm = pCellFrm->GetNext(); + } + pRowFrm = static_cast<const SwRowFrm*>(pRowFrm->GetNext()); + } + pMasterFrm = static_cast<const SwTabFrm*>(pMasterFrm->GetFollow()); + } + } + } + + break; + + /* + * TABLE ELEMENTS + */ + + case FRM_ROW : + // + // RowFrm: TR + // + if ( !static_cast<const SwRowFrm*>(pFrm)->IsRepeatedHeadline() ) + { + nPDFType = vcl::PDFWriter::TableRow; + aPDFType = aTRString; + } + else + { + nPDFType = vcl::PDFWriter::NonStructElement; + } + break; + + case FRM_CELL : + // + // CellFrm: TH, TD + // + { + const SwTabFrm* pTable = static_cast<const SwCellFrm*>(pFrm)->FindTabFrm(); + if ( pTable->IsInHeadline( *pFrm ) || lcl_IsHeadlineCell( *static_cast<const SwCellFrm*>(pFrm) ) ) + { + nPDFType = vcl::PDFWriter::TableHeader; + aPDFType = aTHString; + } + else + { + nPDFType = vcl::PDFWriter::TableData; + aPDFType = aTDString; + } + } + break; + + /* + * ILLUSTRATION + */ + + case FRM_FLY : + // + // FlyFrm: Figure, Formula, Control + // fly in content or fly at page + { + bool bFormula = false; + const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pFrm); + if ( pFly->Lower() && pFly->Lower()->IsNoTxtFrm() ) + { + const SwNoTxtFrm* pNoTxtFrm = static_cast<const SwNoTxtFrm*>(pFly->Lower()); + SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTxtFrm->GetNode()->GetOLENode()); + if ( pOLENd ) + { + SwOLEObj& aOLEObj = pOLENd->GetOLEObj(); + uno::Reference< embed::XEmbeddedObject > aRef = aOLEObj.GetOleRef(); + if ( aRef.is() ) + { + bFormula = 0 != SotExchange::IsMath( SvGlobalName( aRef->getClassID() ) ); + } + } + if ( bFormula ) + { + nPDFType = vcl::PDFWriter::Formula; + aPDFType = aFormulaString; + } + else + { + nPDFType = vcl::PDFWriter::Figure; + aPDFType = aFigureString; + } + } + else + { + nPDFType = vcl::PDFWriter::Division; + aPDFType = aDivString; + } + } + break; + } + + if ( USHRT_MAX != nPDFType ) + { + BeginTag( static_cast<vcl::PDFWriter::StructElement>(nPDFType), aPDFType ); + } +} + + +/* + * SwTaggedPDFHelper::EndStructureElements() + */ +void SwTaggedPDFHelper::EndStructureElements() +{ + while ( nEndStructureElement > 0 ) + { + EndTag(); + --nEndStructureElement; + } + + CheckRestoreTag(); +} + + +/* + * SwTaggedPDFHelper::BeginInlineStructureElements() + */ +void SwTaggedPDFHelper::BeginInlineStructureElements() +{ + const SwLinePortion* pPor = &mpPorInfo->mrPor; + const SwTxtPaintInfo& rInf = mpPorInfo->mrTxtPainter.GetInfo(); + const SwTxtFrm* pFrm = rInf.GetTxtFrm(); + + // + // Lowers of NonStructureElements should not be considered: + // + if ( lcl_IsInNonStructEnv( *pFrm ) ) + return; + + USHORT nPDFType = USHRT_MAX; + String aPDFType; + + switch ( pPor->GetWhichPor() ) + { + // Check for alternative spelling: + case POR_HYPHSTR : + case POR_SOFTHYPHSTR : + nPDFType = vcl::PDFWriter::Span; + aPDFType = aSpanString; + break; + + case POR_LAY : + case POR_TXT : + case POR_PARA : + { + SwTxtNode* pNd = (SwTxtNode*)pFrm->GetTxtNode(); + SwIndex aIndex( pNd, rInf.GetIdx() ); + const SwTxtAttr* pInetFmtAttr = pNd->GetTxtAttr( aIndex, RES_TXTATR_INETFMT ); + + String sStyleName; + if ( !pInetFmtAttr ) + { + const SwTxtAttr* pCharFmtAttr = pNd->GetTxtAttr( aIndex, RES_TXTATR_CHARFMT ); + const SwCharFmt* pCharFmt = pCharFmtAttr ? pCharFmtAttr->GetCharFmt().GetCharFmt() : 0; + if ( pCharFmt ) + SwStyleNameMapper::FillProgName( pCharFmt->GetName(), sStyleName, nsSwGetPoolIdFromName::GET_POOLID_TXTCOLL, sal_True ); + } + + // Check for Link: + if( pInetFmtAttr ) + { + nPDFType = vcl::PDFWriter::Link; + aPDFType = aLinkString; + } + // Check for Quote/Code character style: + else if ( sStyleName == aQuotation ) + { + nPDFType = vcl::PDFWriter::Quote; + aPDFType = aQuoteString; + } + else if ( sStyleName == aSourceText ) + { + nPDFType = vcl::PDFWriter::Code; + aPDFType = aCodeString; + } + else + { + const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage(); + const USHORT nFont = rInf.GetFont()->GetActual(); + const LanguageType nDefaultLang = SwEnhancedPDFExportHelper::GetDefaultLanguage(); + + if ( UNDERLINE_NONE != rInf.GetFont()->GetUnderline() || + UNDERLINE_NONE != rInf.GetFont()->GetOverline() || + STRIKEOUT_NONE != rInf.GetFont()->GetStrikeout() || + EMPHASISMARK_NONE != rInf.GetFont()->GetEmphasisMark() || + 0 != rInf.GetFont()->GetEscapement() || + SW_LATIN != nFont || + nCurrentLanguage != nDefaultLang || + sStyleName.Len() > 0 ) + { + nPDFType = vcl::PDFWriter::Span; + if ( sStyleName.Len() > 0 ) + aPDFType = sStyleName; + else + aPDFType = aSpanString; + } + } + } + break; + + case POR_FTN : + nPDFType = vcl::PDFWriter::Link; + aPDFType = aLinkString; + break; + + case POR_FLD : + { + // check field type: + const xub_StrLen nIdx = static_cast<const SwFldPortion*>(pPor)->IsFollow() ? + rInf.GetIdx() - 1 : + rInf.GetIdx(); + const SwTxtAttr* pHint = mpPorInfo->mrTxtPainter.GetAttr( nIdx ); + const SwField* pFld = 0; + if ( pHint && RES_TXTATR_FIELD == pHint->Which() ) + { + pFld = (SwField*)pHint->GetFld().GetFld(); + if ( RES_GETREFFLD == pFld->Which() ) + { + nPDFType = vcl::PDFWriter::Link; + aPDFType = aLinkString; + } + else if ( RES_AUTHORITY == pFld->Which() ) + { + nPDFType = vcl::PDFWriter::BibEntry; + aPDFType = aBibEntryString; + } + } + } + break; + + case POR_TAB : + case POR_TABRIGHT : + case POR_TABCENTER : + case POR_TABDECIMAL : + nPDFType = vcl::PDFWriter::NonStructElement; + break; + } + + if ( USHRT_MAX != nPDFType ) + { + BeginTag( static_cast<vcl::PDFWriter::StructElement>(nPDFType), aPDFType ); + } +} + +/* + * static SwTaggedPDFHelper::IsExportTaggedPDF + */ + bool SwTaggedPDFHelper::IsExportTaggedPDF( const OutputDevice& rOut ) + { + vcl::PDFExtOutDevData* pPDFExtOutDevData = PTR_CAST( vcl::PDFExtOutDevData, rOut.GetExtOutDevData() ); + return pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportTaggedPDF(); + } + +/* + * SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper() + */ +SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( SwEditShell& rSh, + OutputDevice& rOut, + const rtl::OUString& rPageRange, + bool bSkipEmptyPages, + bool bEditEngineOnly ) + : mrSh( rSh ), + mrOut( rOut ), + pPageRange( 0 ), + mbSkipEmptyPages( bSkipEmptyPages ), + mbEditEngineOnly( bEditEngineOnly ) +{ + if ( rPageRange.getLength() ) + pPageRange = new MultiSelection( rPageRange ); + + aTableColumnsMap.clear(); + aLinkIdMap.clear(); + aNumListIdMap.clear(); + aNumListBodyIdMap.clear(); + aFrmTagIdMap.clear(); + +#ifndef PRODUCT + aStructStack.clear(); +#endif + + const BYTE nScript = (BYTE)GetI18NScriptTypeOfLanguage( (USHORT)GetAppLanguage() ); + USHORT nLangRes = RES_CHRATR_LANGUAGE; + + if ( i18n::ScriptType::ASIAN == nScript ) + nLangRes = RES_CHRATR_CJK_LANGUAGE; + else if ( i18n::ScriptType::COMPLEX == nScript ) + nLangRes = RES_CHRATR_CTL_LANGUAGE; + + eLanguageDefault = static_cast<const SvxLanguageItem*>(&mrSh.GetDoc()->GetDefault( nLangRes ))->GetLanguage(); + + EnhancedPDFExport(); +} + +SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper() +{ + delete pPageRange; +} + +/* + * SwEnhancedPDFExportHelper::EnhancedPDFExport() + */ +void SwEnhancedPDFExportHelper::EnhancedPDFExport() +{ + vcl::PDFExtOutDevData* pPDFExtOutDevData = + PTR_CAST( vcl::PDFExtOutDevData, mrOut.GetExtOutDevData() ); + + if ( !pPDFExtOutDevData ) + return; + + // + // set the document locale + // + com::sun::star::lang::Locale aDocLocale = MsLangId::convertLanguageToLocale( SwEnhancedPDFExportHelper::GetDefaultLanguage() ); + pPDFExtOutDevData->SetDocumentLocale( aDocLocale ); + + // + // Prepare the output device: + // + mrOut.Push( PUSH_MAPMODE ); + MapMode aMapMode( mrOut.GetMapMode() ); + aMapMode.SetMapUnit( MAP_TWIP ); + mrOut.SetMapMode( aMapMode ); + + // + // Create new cursor and lock the view: + // + SwDoc* pDoc = mrSh.GetDoc(); + mrSh.SwCrsrShell::Push(); + mrSh.SwCrsrShell::ClearMark(); + const BOOL bOldLockView = mrSh.IsViewLocked(); + mrSh.LockView( TRUE ); + + if ( !mbEditEngineOnly ) + { + // + // POSTITS + // + if ( pPDFExtOutDevData->GetIsExportNotes() ) + { + SwFieldType* pType = mrSh.GetFldType( RES_POSTITFLD, aEmptyStr ); + SwClientIter aIter( *pType ); + const SwClient * pFirst = aIter.GoStart(); + while( pFirst ) + { + if( ((SwFmtFld*)pFirst)->GetTxtFld() && + ((SwFmtFld*)pFirst)->IsFldInDoc()) + { + const SwTxtNode* pTNd = + (SwTxtNode*)((SwFmtFld*)pFirst)->GetTxtFld()->GetpTxtNode(); + ASSERT( 0 != pTNd, "Enhanced pdf export - text node is missing" ) + + // 1. Check if the whole paragraph is hidden + // 2. Move to the field + // 3. Check for hidden text attribute + if ( !pTNd->IsHidden() && + mrSh.GotoFld( *(SwFmtFld*)pFirst ) && + !mrSh.SelectHiddenRange() ) + { + // Link Rectangle + const SwRect& rNoteRect = mrSh.GetCharRect(); + + // Link PageNum + const sal_Int32 nNotePageNum = CalcOutputPageNum( rNoteRect ); + if ( -1 != nNotePageNum ) + { + // Link Note + vcl::PDFNote aNote; + + // Use the NumberFormatter to get the date string: + const SwPostItField* pField = (SwPostItField*)((SwFmtFld*)pFirst)->GetFld(); + SvNumberFormatter* pNumFormatter = pDoc->GetNumberFormatter(); + const Date aDateDiff( pField->GetDate() - + *pNumFormatter->GetNullDate() ); + const ULONG nFormat = + pNumFormatter->GetStandardFormat( NUMBERFORMAT_DATE, pField->GetLanguage() ); + String sDate; + Color* pColor; + pNumFormatter->GetOutputString( aDateDiff.GetDate(), nFormat, sDate, &pColor ); + + // The title should consist of the author and the date: + String sTitle( pField->GetPar1() ); + sTitle.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ", " ) ); + sTitle += sDate; + aNote.Title = sTitle; + // Guess what the contents contains... + aNote.Contents = pField->GetTxt(); + + // Link Export + pPDFExtOutDevData->CreateNote( rNoteRect.SVRect(), aNote, nNotePageNum ); + } + } + } + pFirst = aIter++; + mrSh.SwCrsrShell::ClearMark(); + } + } + + // + // HYPERLINKS + // + SwGetINetAttrs aArr; + const sal_uInt16 nHyperLinkCount = mrSh.GetINetAttrs( aArr ); + for( sal_uInt16 n = 0; n < nHyperLinkCount; ++n ) + { + SwGetINetAttr* p = aArr[ n ]; + ASSERT( 0 != p, "Enhanced pdf export - SwGetINetAttr is missing" ) + + const SwTxtNode* pTNd = p->rINetAttr.GetpTxtNode(); + ASSERT( 0 != pTNd, "Enhanced pdf export - text node is missing" ) + + // 1. Check if the whole paragraph is hidden + // 2. Move to the hyperlink + // 3. Check for hidden text attribute + if ( !pTNd->IsHidden() && + mrSh.GotoINetAttr( p->rINetAttr ) && + !mrSh.SelectHiddenRange() ) + { + // Select the hyperlink: + mrSh.SwCrsrShell::Right( 1, CRSR_SKIP_CHARS ); + if ( mrSh.SwCrsrShell::SelectTxtAttr( RES_TXTATR_INETFMT, sal_True ) ) + { + // First, we create the destination, because there may be more + // than one link to this destination: + String aURL( INetURLObject::decode( + p->rINetAttr.GetINetFmt().GetValue(), + INET_HEX_ESCAPE, + INetURLObject::DECODE_UNAMBIGUOUS, + RTL_TEXTENCODING_UTF8 ) ); + + // We have to distinguish between intern and real URLs + const bool bIntern = '#' == aURL.GetChar( 0 ); + + // _GetCrsr() is a SwShellCrsr, which is derived from + // SwSelPaintRects, therefore the rectangles of the current + // selection can be easily obtained: + // Note: We make a copy of the rectangles, because they may + // be deleted again in JumpToSwMark. + SwRects aTmp; + aTmp.Insert( mrSh.SwCrsrShell::_GetCrsr(), 0 ); + ASSERT( aTmp.Count() > 0, "Enhanced pdf export - rectangles are missing" ) + + // Create the destination for internal links: + sal_Int32 nDestId = -1; + if ( bIntern ) + { + aURL.Erase( 0, 1 ); + mrSh.SwCrsrShell::ClearMark(); + JumpToSwMark( &mrSh, aURL ); + + // Destination Rectangle + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + // Destination Export + if ( -1 != nDestPageNum ) + nDestId = pPDFExtOutDevData->CreateDest( rDestRect.SVRect(), nDestPageNum ); + } + + if ( !bIntern || -1 != nDestId ) + { + // --> FME 2005-05-09 #i44368# Links in Header/Footer + const SwPosition aPos( *pTNd ); + const bool bHeaderFooter = pDoc->IsInHeaderFooter( aPos.nNode ); + // <-- + + // Create links for all selected rectangles: + const USHORT nNumOfRects = aTmp.Count(); + for ( USHORT i = 0; i < nNumOfRects; ++i ) + { + // Link Rectangle + const SwRect& rLinkRect( aTmp[ i ] ); + + // Link PageNum + const sal_Int32 nLinkPageNum = CalcOutputPageNum( rLinkRect ); + + if ( -1 != nLinkPageNum ) + { + // Link Export + const sal_Int32 nLinkId = + pPDFExtOutDevData->CreateLink( rLinkRect.SVRect(), nLinkPageNum ); + + // Store link info for tagged pdf output: + const IdMapEntry aLinkEntry( rLinkRect, nLinkId ); + aLinkIdMap.push_back( aLinkEntry ); + + // Connect Link and Destination: + if ( bIntern ) + pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId ); + else + pPDFExtOutDevData->SetLinkURL( nLinkId, aURL ); + + // --> FME 2005-05-09 #i44368# Links in Header/Footer + if ( bHeaderFooter ) + MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, aURL, bIntern ); + // <-- + } + } + } + } + } + mrSh.SwCrsrShell::ClearMark(); + } + + // + // HYPERLINKS (Graphics, Frames, OLEs ) + // + const SwSpzFrmFmts* pTbl = pDoc->GetSpzFrmFmts(); + const sal_uInt16 nSpzFrmFmtsCount = pTbl->Count(); + for( sal_uInt16 n = 0; n < nSpzFrmFmtsCount; ++n ) + { + const SwFrmFmt* pFrmFmt = (*pTbl)[n]; + const SfxPoolItem* pItem; + if ( RES_DRAWFRMFMT != pFrmFmt->Which() && + SFX_ITEM_SET == pFrmFmt->GetAttrSet().GetItemState( RES_URL, TRUE, &pItem ) ) + { + String aURL( static_cast<const SwFmtURL*>(pItem)->GetURL() ); + const bool bIntern = '#' == aURL.GetChar( 0 ); + + // Create the destination for internal links: + sal_Int32 nDestId = -1; + if ( bIntern ) + { + aURL.Erase( 0, 1 ); + mrSh.SwCrsrShell::ClearMark(); + JumpToSwMark( &mrSh, aURL ); + + // Destination Rectangle + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + // Destination Export + if ( -1 != nDestPageNum ) + nDestId = pPDFExtOutDevData->CreateDest( rDestRect.SVRect(), nDestPageNum ); + } + + if ( !bIntern || -1 != nDestId ) + { + Point aNullPt; + const SwRect aLinkRect = pFrmFmt->FindLayoutRect( sal_False, &aNullPt ); + + // Link PageNum + const sal_Int32 nLinkPageNum = CalcOutputPageNum( aLinkRect ); + + // Link Export + if ( -1 != nLinkPageNum ) + { + const sal_Int32 nLinkId = + pPDFExtOutDevData->CreateLink( aLinkRect.SVRect(), nLinkPageNum ); + + // Connect Link and Destination: + if ( bIntern ) + pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId ); + else + pPDFExtOutDevData->SetLinkURL( nLinkId, aURL ); + + // --> FME 2005-05-09 #i44368# Links in Header/Footer + const SwFmtAnchor &rAnch = pFrmFmt->GetAnchor(); + if ( FLY_PAGE != rAnch.GetAnchorId() ) + { + const SwPosition* pPosition = rAnch.GetCntntAnchor(); + if ( pPosition && pDoc->IsInHeaderFooter( pPosition->nNode ) ) + { + const SwTxtNode* pTNd = pPosition->nNode.GetNode().GetTxtNode(); + if ( pTNd ) + MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, aLinkRect, nDestId, aURL, bIntern ); + } + } + // <-- + } + } + } + mrSh.SwCrsrShell::ClearMark(); + } + + // + // REFERENCES + // + SwFieldType* pType = mrSh.GetFldType( RES_GETREFFLD, aEmptyStr ); + SwClientIter aIter( *pType ); + const SwClient * pFirst = aIter.GoStart(); + while( pFirst ) + { + if( ((SwFmtFld*)pFirst)->GetTxtFld() && + ((SwFmtFld*)pFirst)->IsFldInDoc()) + { + const SwTxtNode* pTNd = + (SwTxtNode*)((SwFmtFld*)pFirst)->GetTxtFld()->GetpTxtNode(); + ASSERT( 0 != pTNd, "Enhanced pdf export - text node is missing" ) + + // 1. Check if the whole paragraph is hidden + // 2. Move to the field + // 3. Check for hidden text attribute + if ( !pTNd->IsHidden() && + mrSh.GotoFld( *(SwFmtFld*)pFirst ) && + !mrSh.SelectHiddenRange() ) + { + // Select the field: + mrSh.SwCrsrShell::SetMark(); + mrSh.SwCrsrShell::Right( 1, CRSR_SKIP_CHARS ); + + // Link Rectangles + SwRects aTmp; + aTmp.Insert( mrSh.SwCrsrShell::_GetCrsr(), 0 ); + ASSERT( aTmp.Count() > 0, "Enhanced pdf export - rectangles are missing" ) + + mrSh.SwCrsrShell::ClearMark(); + + // Destination Rectangle + const SwGetRefField* pField = + (SwGetRefField*)((SwFmtFld*)pFirst)->GetFld(); + const String& rRefName = pField->GetSetRefName(); + mrSh.GotoRefMark( rRefName, pField->GetSubType(), pField->GetSeqNo() ); + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + if ( -1 != nDestPageNum ) + { + // Destination Export + const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest( rDestRect.SVRect(), nDestPageNum ); + + // --> FME 2005-05-09 #i44368# Links in Header/Footer + const SwPosition aPos( *pTNd ); + const bool bHeaderFooter = pDoc->IsInHeaderFooter( aPos.nNode ); + // <-- + + // Create links for all selected rectangles: + const USHORT nNumOfRects = aTmp.Count(); + for ( USHORT i = 0; i < nNumOfRects; ++i ) + { + // Link rectangle + const SwRect& rLinkRect( aTmp[ i ] ); + + // Link PageNum + const sal_Int32 nLinkPageNum = CalcOutputPageNum( rLinkRect ); + + if ( -1 != nLinkPageNum ) + { + // Link Export + const sal_Int32 nLinkId = + pPDFExtOutDevData->CreateLink( rLinkRect.SVRect(), nLinkPageNum ); + + // Store link info for tagged pdf output: + const IdMapEntry aLinkEntry( rLinkRect, nLinkId ); + aLinkIdMap.push_back( aLinkEntry ); + + // Connect Link and Destination: + pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId ); + + // --> FME 2005-05-09 #i44368# Links in Header/Footer + if ( bHeaderFooter ) + { + const String aDummy; + MakeHeaderFooterLinks( *pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, aDummy, true ); + } + // <-- + } + } + } + } + } + pFirst = aIter++; + mrSh.SwCrsrShell::ClearMark(); + } + + // + // FOOTNOTES + // + const USHORT nFtnCount = pDoc->GetFtnIdxs().Count(); + for ( USHORT nIdx = 0; nIdx < nFtnCount; ++nIdx ) + { + // Set cursor to text node that contains the footnote: + const SwTxtFtn* pTxtFtn = pDoc->GetFtnIdxs()[ nIdx ]; + SwTxtNode& rTNd = const_cast<SwTxtNode&>(pTxtFtn->GetTxtNode()); + + mrSh._GetCrsr()->GetPoint()->nNode = rTNd; + mrSh._GetCrsr()->GetPoint()->nContent.Assign( &rTNd, *pTxtFtn->GetStart() ); + + // 1. Check if the whole paragraph is hidden + // 2. Check for hidden text attribute + if ( static_cast<const SwTxtNode&>(rTNd).IsHidden() || + mrSh.SelectHiddenRange() ) + continue; + + SwCrsrSaveState aSaveState( *mrSh._GetCrsr() ); + + // Select the footnote: + mrSh.SwCrsrShell::SetMark(); + mrSh.SwCrsrShell::Right( 1, CRSR_SKIP_CHARS ); + + // Link Rectangle + SwRects aTmp; + aTmp.Insert( mrSh.SwCrsrShell::_GetCrsr(), 0 ); + ASSERT( aTmp.Count() > 0, "Enhanced pdf export - rectangles are missing" ) + const SwRect aLinkRect( aTmp[ 0 ] ); + + mrSh._GetCrsr()->RestoreSavePos(); + mrSh.SwCrsrShell::ClearMark(); + + // Goto footnote text: + if ( mrSh.GotoFtnTxt() ) + { + // Link PageNum + const sal_Int32 nLinkPageNum = CalcOutputPageNum( aLinkRect ); + + if ( -1 != nLinkPageNum ) + { + // Link Export + const sal_Int32 nLinkId = + pPDFExtOutDevData->CreateLink( aLinkRect.SVRect(), nLinkPageNum ); + + // Store link info for tagged pdf output: + const IdMapEntry aLinkEntry( aLinkRect, nLinkId ); + aLinkIdMap.push_back( aLinkEntry ); + + // Destination Rectangle + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + if ( -1 != nDestPageNum ) + { + // Destination Export + const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest( rDestRect.SVRect(), nDestPageNum ); + + // Connect Link and Destination: + pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId ); + } + } + } + } + + // + // OUTLINE + // + if( pPDFExtOutDevData->GetIsExportBookmarks() ) + { + typedef std::pair< sal_Int8, sal_Int32 > StackEntry; + std::stack< StackEntry > aOutlineStack; + aOutlineStack.push( StackEntry( -1, -1 ) ); // push default value + + const sal_uInt16 nOutlineCount = + static_cast<sal_uInt16>(mrSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount()); + for ( sal_uInt16 i = 0; i < nOutlineCount; ++i ) + { + // Check if outline is hidden + const SwTxtNode* pTNd = mrSh.GetNodes().GetOutLineNds()[ i ]->GetTxtNode(); + ASSERT( 0 != pTNd, "Enhanced pdf export - text node is missing" ) + + if ( pTNd->IsHidden() || + // --> FME 2005-01-10 #i40292# Skip empty outlines: + 0 == pTNd->GetTxt().Len() ) + // <-- + continue; + + // Get parent id from stack: + const sal_Int8 nLevel = (sal_Int8)mrSh.getIDocumentOutlineNodesAccess()->getOutlineLevel( i ); + sal_Int8 nLevelOnTopOfStack = aOutlineStack.top().first; + while ( nLevelOnTopOfStack >= nLevel && + nLevelOnTopOfStack != -1 ) + { + aOutlineStack.pop(); + nLevelOnTopOfStack = aOutlineStack.top().first; + } + const sal_Int32 nParent = aOutlineStack.top().second; + + // Destination rectangle + mrSh.GotoOutline(i); + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + if ( -1 != nDestPageNum ) + { + // Destination Export + const sal_Int32 nDestId = + pPDFExtOutDevData->CreateDest( rDestRect.SVRect(), nDestPageNum ); + + // Outline entry text + const String& rEntry = mrSh.getIDocumentOutlineNodesAccess()->getOutlineText( i ); + + // Create a new outline item: + const sal_Int32 nOutlineId = + pPDFExtOutDevData->CreateOutlineItem( nParent, rEntry, nDestId ); + + // Push current level and nOutlineId on stack: + aOutlineStack.push( StackEntry( nLevel, nOutlineId ) ); + } + } + } + + if( pPDFExtOutDevData->GetIsExportNamedDestinations() ) + { + //---> i56629 the iteration to convert the OOo bookmark (#bookmark) + // into PDF named destination, see section 8.2.1 in PDF 1.4 spec + // We need: + // 1. a name for the destination, formed from the standard OOo bookmark name + // 2. the destination, obtained from where the bookmark destination lies + IDocumentMarkAccess* const pMarkAccess = mrSh.GetDoc()->getIDocumentMarkAccess(); + for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getBookmarksBegin(); + ppMark != pMarkAccess->getBookmarksEnd(); + ppMark++) + { + //get the name + const ::sw::mark::IMark* pBkmk = ppMark->get(); + mrSh.SwCrsrShell::ClearMark(); + rtl::OUString sBkName = pBkmk->GetName(); + + //jump to it + JumpToSwMark( &mrSh, sBkName ); + + // Destination Rectangle + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + // Destination Export + if ( -1 != nDestPageNum ) + pPDFExtOutDevData->CreateNamedDest( sBkName, rDestRect.SVRect(), nDestPageNum ); + } + mrSh.SwCrsrShell::ClearMark(); + //<--- i56629 + } + } + else + { + // + // LINKS FROM EDITENGINE + // + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks(); + std::vector< vcl::PDFExtOutDevBookmarkEntry >::const_iterator aIBeg = rBookmarks.begin(); + const std::vector< vcl::PDFExtOutDevBookmarkEntry >::const_iterator aIEnd = rBookmarks.end(); + while ( aIBeg != aIEnd ) + { + String aBookmarkName( aIBeg->aBookmark ); + const bool bIntern = '#' == aBookmarkName.GetChar( 0 ); + if ( bIntern ) + { + aBookmarkName.Erase( 0, 1 ); + JumpToSwMark( &mrSh, aBookmarkName ); + + // Destination Rectangle + const SwRect& rDestRect = mrSh.GetCharRect(); + + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect ); + + if ( -1 != nDestPageNum ) + { + // Destination Export + const sal_Int32 nDestId = + pPDFExtOutDevData->CreateDest( rDestRect.SVRect(), nDestPageNum ); + + // Connect Link and Destination: + pPDFExtOutDevData->SetLinkDest( aIBeg->nLinkId, nDestId ); + } + } + else + pPDFExtOutDevData->SetLinkURL( aIBeg->nLinkId, aBookmarkName ); + + aIBeg++; + } + rBookmarks.clear(); + } + + // Restore view, cursor, and outdev: + mrSh.LockView( bOldLockView ); + mrSh.SwCrsrShell::Pop( FALSE ); + mrOut.Pop(); +} + +/* + * SwEnhancedPDFExportHelper::CalcOutputPageNum() + */ +sal_Int32 SwEnhancedPDFExportHelper::CalcOutputPageNum( const SwRect& rRect ) const +{ + // Document page numbers are 0, 1, 2, ... + const sal_Int32 nPageNumOfRect = mrSh.GetPageNumAndSetOffsetForPDF( mrOut, rRect ); + + // Shortcut: + if ( -1 == nPageNumOfRect || ( !pPageRange && !mbSkipEmptyPages ) ) + return nPageNumOfRect; + + // pPageRange page numbers are 1, 2, 3, ... + if ( pPageRange && !pPageRange->IsSelected( nPageNumOfRect + 1 ) ) + return -1; + + // What will be the page number of page nPageNumOfRect in the output doc? + sal_Int32 nOutputPageNum = -1; + const SwRootFrm* pRootFrm = mrSh.GetLayout(); + const SwPageFrm* pCurrPage = static_cast<const SwPageFrm*>(pRootFrm->Lower()); + + for ( sal_Int32 nPageIndex = 0; + nPageIndex <= nPageNumOfRect && pCurrPage; + ++nPageIndex ) + { + if ( ( !pPageRange || pPageRange->IsSelected( nPageIndex + 1 ) ) && + ( !mbSkipEmptyPages || !pCurrPage->IsEmptyPage() ) ) + ++nOutputPageNum; + + pCurrPage = static_cast<const SwPageFrm*>(pCurrPage->GetNext()); + } + + // pdf export page numbers are 0, 1, 2, ... + return nOutputPageNum; +} + +void SwEnhancedPDFExportHelper::MakeHeaderFooterLinks( vcl::PDFExtOutDevData& rPDFExtOutDevData, + const SwTxtNode& rTNd, + const SwRect& rLinkRect, + sal_Int32 nDestId, + const String& rURL, + bool bIntern ) const +{ + // We assume, that the primary link has just been exported. Therefore + // the offset of the link rectangle calculates as follows: + const Point aOffset = rLinkRect.Pos() + mrOut.GetMapMode().GetOrigin(); + + SwClientIter aClientIter( const_cast<SwTxtNode&>(rTNd) ); + SwClient* pLast = aClientIter.GoStart(); + + while( pLast ) + { + if ( pLast->ISA( SwTxtFrm ) ) + { + // Add offset to current page: + SwTxtFrm* pTmpFrm = static_cast<SwTxtFrm*>(pLast); + const SwPageFrm* pPageFrm = pTmpFrm->FindPageFrm(); + SwRect aHFLinkRect( rLinkRect ); + aHFLinkRect.Pos() = pPageFrm->Frm().Pos() + aOffset; + + // #i97135# the gcc_x64 optimizer gets aHFLinkRect != rLinkRect wrong + // fool it by comparing the position only (the width and height are the + // same anyway) + if ( aHFLinkRect.Pos() != rLinkRect.Pos() ) + { + // Link PageNum + const sal_Int32 nHFLinkPageNum = CalcOutputPageNum( aHFLinkRect ); + + if ( -1 != nHFLinkPageNum ) + { + // Link Export + const sal_Int32 nHFLinkId = + rPDFExtOutDevData.CreateLink( aHFLinkRect.SVRect(), nHFLinkPageNum ); + + // Connect Link and Destination: + if ( bIntern ) + rPDFExtOutDevData.SetLinkDest( nHFLinkId, nDestId ); + else + rPDFExtOutDevData.SetLinkURL( nHFLinkId, rURL ); + } + } + } + + pLast = ++aClientIter; + } +} + diff --git a/sw/source/core/text/SwGrammarMarkUp.cxx b/sw/source/core/text/SwGrammarMarkUp.cxx new file mode 100644 index 000000000000..74e6dbf27b30 --- /dev/null +++ b/sw/source/core/text/SwGrammarMarkUp.cxx @@ -0,0 +1,184 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SwGrammarMarkUp.cxx,v $ + * $Revision: 1.2 $ + * + * 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 "SwGrammarMarkUp.hxx" + +SwGrammarMarkUp::~SwGrammarMarkUp() +{ +} + +SwWrongList* SwGrammarMarkUp::Clone() +{ + SwWrongList* pClone = new SwGrammarMarkUp(); + pClone->CopyFrom( *this ); + return pClone; +} + +void SwGrammarMarkUp::CopyFrom( const SwWrongList& rCopy ) +{ + maSentence = ((const SwGrammarMarkUp&)rCopy).maSentence; + SwWrongList::CopyFrom( rCopy ); +} + + +void SwGrammarMarkUp::MoveGrammar( xub_StrLen nPos, long nDiff ) +{ + Move( nPos, nDiff ); + if( !maSentence.size() ) + return; + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + while( pIter != maSentence.end() && *pIter < nPos ) + ++pIter; + xub_StrLen nEnd = nDiff < 0 ? xub_StrLen(nPos - nDiff) : nPos; + while( pIter != maSentence.end() ) + { + if( *pIter >= nEnd ) + *pIter = xub_StrLen( *pIter + nDiff ); + else + *pIter = nPos; + ++pIter; + } +} + +SwGrammarMarkUp* SwGrammarMarkUp::SplitGrammarList( xub_StrLen nSplitPos ) +{ + SwGrammarMarkUp* pNew = (SwGrammarMarkUp*)SplitList( nSplitPos ); + if( !maSentence.size() ) + return pNew; + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + while( pIter != maSentence.end() && *pIter < nSplitPos ) + ++pIter; + if( pIter != maSentence.begin() ) + { + if( !pNew ) { + pNew = new SwGrammarMarkUp(); + pNew->SetInvalid( 0, STRING_LEN ); + } + pNew->maSentence.insert( pNew->maSentence.begin(), maSentence.begin(), pIter ); + maSentence.erase( maSentence.begin(), pIter ); + } + return pNew; +} + +void SwGrammarMarkUp::JoinGrammarList( SwGrammarMarkUp* pNext, xub_StrLen nInsertPos ) +{ + JoinList( pNext, nInsertPos ); + if (pNext) + { + if( !pNext->maSentence.size() ) + return; + std::vector< xub_StrLen >::iterator pIter = pNext->maSentence.begin(); + while( pIter != pNext->maSentence.end() ) + { + *pIter = *pIter + nInsertPos; + ++pIter; + } + maSentence.insert( maSentence.end(), pNext->maSentence.begin(), pNext->maSentence.end() ); + } +} + +void SwGrammarMarkUp::ClearGrammarList( xub_StrLen nSentenceEnd ) +{ + if( STRING_LEN == nSentenceEnd ) { + ClearList(); + maSentence.clear(); + Validate(); + } else if( GetBeginInv() <= nSentenceEnd ) { + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + xub_StrLen nStart = 0; + while( pIter != maSentence.end() && *pIter < GetBeginInv() ) + { + nStart = *pIter; + ++pIter; + } + std::vector< xub_StrLen >::iterator pLast = pIter; + while( pLast != maSentence.end() && *pLast <= nSentenceEnd ) + ++pLast; + maSentence.erase( pIter, pLast ); + RemoveEntry( nStart, nSentenceEnd ); + SetInvalid( nSentenceEnd + 1, STRING_LEN ); + } +} + +void SwGrammarMarkUp::setSentence( xub_StrLen nStart ) +{ + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + while( pIter != maSentence.end() && *pIter < nStart ) + ++pIter; + if( pIter == maSentence.end() || *pIter > nStart ) + maSentence.insert( pIter, nStart ); +} + +void SwGrammarMarkUp::removeSentence(xub_StrLen nStart, xub_StrLen nLength ) +{ + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + while( pIter != maSentence.end() && *pIter < nStart ) + ++pIter; + if( nLength == STRING_LEN ) + nStart = STRING_LEN; + else + nStart += nLength; + std::vector< xub_StrLen >::iterator pLast = pIter; + while( pLast != maSentence.end() && *pLast < nStart ) + ++pLast; + maSentence.erase( pIter, pLast ); +} + +xub_StrLen SwGrammarMarkUp::getSentenceStart( xub_StrLen nPos ) +{ + if( !maSentence.size() ) + return 0; + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + while( pIter != maSentence.end() && *pIter < nPos ) + ++pIter; + if( pIter != maSentence.begin() ) + --pIter; + xub_StrLen nRet = 0; + if( pIter != maSentence.end() && *pIter < nPos ) + nRet = *pIter; + return nRet; +} + +xub_StrLen SwGrammarMarkUp::getSentenceEnd( xub_StrLen nPos ) +{ + if( !maSentence.size() ) + return STRING_LEN; + std::vector< xub_StrLen >::iterator pIter = maSentence.begin(); + while( pIter != maSentence.end() && *pIter <= nPos ) + ++pIter; + xub_StrLen nRet = STRING_LEN; + if( pIter != maSentence.end() ) + nRet = *pIter; + return nRet; +} + diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx new file mode 100644 index 000000000000..3e24ba0a49e1 --- /dev/null +++ b/sw/source/core/text/atrhndl.hxx @@ -0,0 +1,182 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: atrhndl.hxx,v $ + * $Revision: 1.5.210.1 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _ATRHNDL_HXX +#define _ATRHNDL_HXX + +#define INITIAL_NUM_ATTR 3 +#define NUM_ATTRIBUTE_STACKS 40 + +#include <txatbase.hxx> +#include <swfntcch.hxx> + +class SwAttrSet; +class IDocumentSettingAccess; +class ViewShell; +class SfxPoolItem; +extern const BYTE StackPos[]; + +/************************************************************************* + * class SwAttrHandler + * + * Used by Attribute Iterators to organize attributes on stacks to + * find the valid attribute in each category + *************************************************************************/ + +class SwAttrHandler +{ +private: + + /************************************************************************* + * class SwAttrStack + * + * Container for SwTxtAttr Objects + *************************************************************************/ + + class SwAttrStack + { + private: + SwTxtAttr* pInitialArray[ INITIAL_NUM_ATTR ]; + SwTxtAttr** pArray; + USHORT nCount; // number of elements on stack + USHORT nSize; // number of positions in Array + + public: + // Ctor, Dtor + inline SwAttrStack(); + inline ~SwAttrStack() { + if ( nSize > INITIAL_NUM_ATTR ) delete [] pArray; } + + // reset stack + inline void Reset() { nCount = 0; }; + + // insert on top + inline void Push( const SwTxtAttr& rAttr ) { Insert( rAttr, nCount ); }; + // insert at specified position, take care for not inserting behind + // the value returned by Count() + void Insert( const SwTxtAttr& rAttr, const USHORT nPos ); + + // remove specified attribute + void Remove( const SwTxtAttr& rAttr ); + + // get attribute from top if exists, otherwise 0 + const SwTxtAttr* Top() const; + + // number of elements on stack + inline USHORT Count() const { return nCount; }; + + // returns position of rAttr on Stack if found, otherwise USHRT_MAX + // can be used for Remove of an attribute + USHORT Pos( const SwTxtAttr& rAttr ) const; + }; + + SwAttrStack aAttrStack[ NUM_ATTRIBUTE_STACKS ]; // stack collection + const SfxPoolItem* pDefaultArray[ NUM_DEFAULT_VALUES ]; + const IDocumentSettingAccess* mpIDocumentSettingAccess; + const ViewShell* mpShell; + + // This is the base font for the paragraph. It is stored in order to have + // a template, if we have to restart the attribute evaluation + SwFont* pFnt; + + sal_Bool bVertLayout; + + // change font according to pool item + void FontChg(const SfxPoolItem& rItem, SwFont& rFnt, sal_Bool bPush ); + + // push attribute to specified stack, returns true, if attribute has + // been pushed on top of stack (important for stacks containing different + // attributes with different priority and redlining) + sal_Bool Push( const SwTxtAttr& rAttr, const SfxPoolItem& rItem ); + + // apply top attribute on stack to font + void ActivateTop( SwFont& rFnt, USHORT nStackPos ); + +public: + // Ctor + SwAttrHandler(); + ~SwAttrHandler(); + + // set default attributes to values in rAttrSet or from cache + void Init( const SwAttrSet& rAttrSet, + const IDocumentSettingAccess& rIDocumentSettingAccess, + const ViewShell* pShell ); + void Init( const SfxPoolItem** pPoolItem, const SwAttrSet* pAttrSet, + const IDocumentSettingAccess& rIDocumentSettingAccess, + const ViewShell* pShell, SwFont& rFnt, + sal_Bool bVertLayout ); + + // remove everything from internal stacks, keep default data + void Reset( ); + + // insert specified attribute and change font + void PushAndChg( const SwTxtAttr& rAttr, SwFont& rFnt ); + + // remove specified attribute and reset font + void PopAndChg( const SwTxtAttr& rAttr, SwFont& rFnt ); + void Pop( const SwTxtAttr& rAttr ); + + // apply script dependent attributes +// void ChangeScript( SwFont& rFnt, const BYTE nScr ); + + // returns the default value for stack nStack + inline const SfxPoolItem& GetDefault( const USHORT nAttribID ) const; + // do not call these if you only used the small init function + inline void ResetFont( SwFont& rFnt ) const; + inline const SwFont* GetFont() const; + + void GetDefaultAscentAndHeight(ViewShell* pShell, + OutputDevice& rOut, + USHORT& nAscent, + USHORT& nHeight) const; +}; + +inline const SfxPoolItem& SwAttrHandler::GetDefault( const USHORT nAttribID ) const +{ + ASSERT( nAttribID < RES_TXTATR_END, + "this attrib does not ex." + ); + ASSERT( pDefaultArray[ StackPos[ nAttribID ] ], "array not initialized" ); + return *pDefaultArray[ StackPos[ nAttribID ] ]; +} + +inline void SwAttrHandler::ResetFont( SwFont& rFnt ) const +{ + ASSERT( pFnt, "ResetFont without a font" ); + if ( pFnt ) + rFnt = *pFnt; +}; + +inline const SwFont* SwAttrHandler::GetFont() const +{ + return pFnt; +}; + +#endif diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx new file mode 100644 index 000000000000..7db8ec2e2506 --- /dev/null +++ b/sw/source/core/text/atrstck.cxx @@ -0,0 +1,959 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: atrstck.cxx,v $ + * $Revision: 1.30.210.1 $ + * + * 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 <errhdl.hxx> // ASSERT +#include <atrhndl.hxx> +#include <svtools/itemiter.hxx> +#include <vcl/outdev.hxx> +#include <svx/cmapitem.hxx> +#include <svx/colritem.hxx> +#include <svx/cntritem.hxx> +#include <svx/crsditem.hxx> +#include <svx/escpitem.hxx> +#include <svx/fontitem.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/kernitem.hxx> +#include <svx/charreliefitem.hxx> +#include <svx/langitem.hxx> +#include <svx/postitem.hxx> +#include <svx/shdditem.hxx> +#include <svx/udlnitem.hxx> +#include <svx/wghtitem.hxx> +#include <svx/wrlmitem.hxx> +#include <svx/akrnitem.hxx> +#include <svx/blnkitem.hxx> +#include <svx/charrotateitem.hxx> +#include <svx/emphitem.hxx> +#include <svx/charscaleitem.hxx> +#include <svx/twolinesitem.hxx> +#include <svx/charhiddenitem.hxx> +#include <viewopt.hxx> +#include <charfmt.hxx> +#include <fchrfmt.hxx> +#include <fmtautofmt.hxx> +#include <svx/brshitem.hxx> +#include <fmtinfmt.hxx> +#include <txtinet.hxx> +#include <IDocumentSettingAccess.hxx> +#include <viewsh.hxx> // ViewShell +#include <viewopt.hxx> // SwViewOptions + +#define STACK_INCREMENT 4 + +/************************************************************************* + * Attribute to Stack Mapping + * + * Attributes applied to a text are pushed on different stacks. For each + * stack, the top most attribute on the stack is valid. Because some + * kinds of attributes have to be pushed to the same stacks we map their + * ids to stack ids + * Attention: The first NUM_DEFAULT_VALUES ( defined in swfntcch.hxx == 35 ) + * are stored in the defaultitem-cache, if you add one, you have to increase + * NUM_DEFAULT_VALUES. + * Also adjust NUM_ATTRIBUTE_STACKS in atrhndl.hxx. + *************************************************************************/ + +const BYTE StackPos[ static_cast<USHORT>(RES_TXTATR_WITHEND_END) - + static_cast<USHORT>(RES_CHRATR_BEGIN) + 1 ] = +{ + 0, // // 0 + 1, // RES_CHRATR_CASEMAP = RES_CHRATR_BEGIN // 1 + 0, // RES_CHRATR_CHARSETCOLOR, // 2 + 2, // RES_CHRATR_COLOR, // 3 + 3, // RES_CHRATR_CONTOUR, // 4 + 4, // RES_CHRATR_CROSSEDOUT, // 5 + 5, // RES_CHRATR_ESCAPEMENT, // 6 + 6, // RES_CHRATR_FONT, // 7 + 7, // RES_CHRATR_FONTSIZE, // 8 + 8, // RES_CHRATR_KERNING, // 9 + 9, // RES_CHRATR_LANGUAGE, // 10 + 10, // RES_CHRATR_POSTURE, // 11 + 0, // RES_CHRATR_PROPORTIONALFONTSIZE, // 12 + 11, // RES_CHRATR_SHADOWED, // 13 + 12, // RES_CHRATR_UNDERLINE, // 14 + 13, // RES_CHRATR_WEIGHT, // 15 + 14, // RES_CHRATR_WORDLINEMODE, // 16 + 15, // RES_CHRATR_AUTOKERN, // 17 + 16, // RES_CHRATR_BLINK, // 18 + 17, // RES_CHRATR_NOHYPHEN, // 19 + 0, // RES_CHRATR_NOLINEBREAK, // 20 + 18, // RES_CHRATR_BACKGROUND, // 21 + 19, // RES_CHRATR_CJK_FONT, // 22 + 20, // RES_CHRATR_CJK_FONTSIZE, // 23 + 21, // RES_CHRATR_CJK_LANGUAGE, // 24 + 22, // RES_CHRATR_CJK_POSTURE, // 25 + 23, // RES_CHRATR_CJK_WEIGHT, // 26 + 24, // RES_CHRATR_CTL_FONT, // 27 + 25, // RES_CHRATR_CTL_FONTSIZE, // 28 + 26, // RES_CHRATR_CTL_LANGUAGE, // 29 + 27, // RES_CHRATR_CTL_POSTURE, // 30 + 28, // RES_CHRATR_CTL_WEIGHT, // 31 + 29, // RES_CHRATR_ROTATE, // 32 + 30, // RES_CHRATR_EMPHASIS_MARK, // 33 + 31, // RES_CHRATR_TWO_LINES, // 34 + 32, // RES_CHRATR_SCALEW, // 35 + 33, // RES_CHRATR_RELIEF, // 36 + 34, // RES_CHRATR_HIDDEN, // 37 + 35, // RES_CHRATR_OVERLINE, // 38 + 0, // RES_CHRATR_DUMMY1, // 39 + 0, // RES_CHRATR_DUMMY2, // 40 + 36, // RES_TXTATR_REFMARK, // 41 + 37, // RES_TXTATR_TOXMARK, // 42 + 38, // RES_TXTATR_META, // 43 + 38, // RES_TXTATR_METAFIELD, // 44 + 0, // RES_TXTATR_AUTOFMT, // 45 + 0, // RES_TXTATR_INETFMT // 46 + 0, // RES_TXTATR_CHARFMT, // 47 + 39, // RES_TXTATR_CJK_RUBY, // 48 + 0, // RES_TXTATR_UNKNOWN_CONTAINER, // 49 + 0, // RES_TXTATR_DUMMY5 // 50 +}; + +/************************************************************************* + * CharFmt::GetItem + * returns the item set associated with an character/inet/auto style + *************************************************************************/ + +namespace CharFmt +{ + +const SfxItemSet* GetItemSet( const SfxPoolItem& rAttr ) +{ + const SfxItemSet* pSet = 0; + + if ( RES_TXTATR_AUTOFMT == rAttr.Which() ) + { + pSet = static_cast<const SwFmtAutoFmt&>(rAttr).GetStyleHandle().get(); + } + else + { + // aus der Vorlage die Attribute holen: + SwCharFmt* pFmt = RES_TXTATR_INETFMT == rAttr.Which() ? + ((SwFmtINetFmt&)rAttr).GetTxtINetFmt()->GetCharFmt() : + ((SwFmtCharFmt&)rAttr).GetCharFmt(); + if( pFmt ) + { + pSet = &pFmt->GetAttrSet(); + } + } + + return pSet; +} + +/************************************************************************* + * CharFmt::GetItem + * extracts pool item of type nWhich from rAttr + *************************************************************************/ + +const SfxPoolItem* GetItem( const SwTxtAttr& rAttr, USHORT nWhich ) +{ + if ( RES_TXTATR_INETFMT == rAttr.Which() || + RES_TXTATR_CHARFMT == rAttr.Which() || + RES_TXTATR_AUTOFMT == rAttr.Which() ) + { + const SfxItemSet* pSet = CharFmt::GetItemSet( rAttr.GetAttr() ); + if ( !pSet ) return 0; + + bool bInParent = RES_TXTATR_AUTOFMT != rAttr.Which(); + const SfxPoolItem* pItem; + BOOL bRet = SFX_ITEM_SET == pSet->GetItemState( nWhich, bInParent, &pItem ); + + return bRet ? pItem : 0; + } + + return ( nWhich == rAttr.Which() ) ? &rAttr.GetAttr() : 0; +} + +/************************************************************************* + * CharFmt::IsItemIncluded + * checks if item is included in character/inet/auto style + *************************************************************************/ + +BOOL IsItemIncluded( const USHORT nWhich, const SwTxtAttr *pAttr ) +{ + BOOL bRet = FALSE; + + const SfxItemSet* pItemSet = CharFmt::GetItemSet( pAttr->GetAttr() ); + if ( pItemSet ) + bRet = SFX_ITEM_SET == pItemSet->GetItemState( nWhich, TRUE ); + + return bRet; +} + +} + +/************************************************************************* + * lcl_ChgHyperLinkColor + * The color of hyperlinks is taken from the associated character attribute, + * depending on its 'visited' state. There are actually two cases, which + * should override the colors from the character attribute: + * 1. We never take the 'visited' color during printing/pdf export/preview + * 2. The user has choosen to override these colors in the view options + *************************************************************************/ + +bool lcl_ChgHyperLinkColor( const SwTxtAttr& rAttr, + const SfxPoolItem& rItem, + const ViewShell* pShell, + Color* pColor ) +{ + if ( !pShell || + RES_TXTATR_INETFMT != rAttr.Which() || + RES_CHRATR_COLOR != rItem.Which() ) + return false; + + // --> FME 2004-09-13 #i15455# + // 1. case: + // We do not want to show visited links: + // (printing, pdf export, page preview) + // + if ( pShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER || + pShell->GetViewOptions()->IsPDFExport() || + pShell->GetViewOptions()->IsPagePreview() ) + { + if ( ((SwTxtINetFmt&)rAttr).IsVisited() ) + { + if ( pColor ) + { + // take color from character format 'unvisited link' + SwTxtINetFmt& rInetAttr( const_cast<SwTxtINetFmt&>( + static_cast<const SwTxtINetFmt&>(rAttr)) ); + rInetAttr.SetVisited( false ); + const SwCharFmt* pTmpFmt = ((SwTxtINetFmt&)rAttr).GetCharFmt(); + const SfxPoolItem* pItem; + pTmpFmt->GetItemState( RES_CHRATR_COLOR, TRUE, &pItem ); + *pColor = ((SvxColorItem*)pItem)->GetValue(); + rInetAttr.SetVisited( true ); + } + return true; + } + + return false; + } + // <-- + + // + // 2. case: + // We do not want to apply the color set in the hyperlink + // attribute, instead we take the colors from the view options: + // + if ( pShell->GetWin() && + ( + (((SwTxtINetFmt&)rAttr).IsVisited() && SwViewOption::IsVisitedLinks()) || + (!((SwTxtINetFmt&)rAttr).IsVisited() && SwViewOption::IsLinks()) + ) + ) + { + if ( pColor ) + { + if ( ((SwTxtINetFmt&)rAttr).IsVisited() ) + { + // take color from view option 'visited link color' + *pColor = SwViewOption::GetVisitedLinksColor(); + } + else + { + // take color from view option 'unvisited link color' + *pColor = SwViewOption::GetLinksColor(); + } + } + return true; + } + + return false; +} + +/************************************************************************* + * SwAttrHandler::SwAttrStack::SwAttrStack() + *************************************************************************/ + +inline SwAttrHandler::SwAttrStack::SwAttrStack() + : nCount( 0 ), nSize( INITIAL_NUM_ATTR ) +{ + pArray = pInitialArray; +} + +/************************************************************************* + * SwAttrHandler::SwAttrStack::Insert() + *************************************************************************/ + +void SwAttrHandler::SwAttrStack::Insert( const SwTxtAttr& rAttr, const USHORT nPos ) +{ + // do we still have enough space? + if ( nCount >= nSize ) + { + // we are still in our initial array + if ( INITIAL_NUM_ATTR == nSize ) + { + nSize += STACK_INCREMENT; + pArray = new SwTxtAttr*[ nSize ]; + // copy from pInitArray to new Array + memcpy( pArray, pInitialArray, + INITIAL_NUM_ATTR * sizeof(SwTxtAttr*) + ); + } + // we are in new memory + else + { + nSize += STACK_INCREMENT; + SwTxtAttr** pTmpArray = new SwTxtAttr*[ nSize ]; + // copy from pArray to new Array + memcpy( pTmpArray, pArray, nCount * sizeof(SwTxtAttr*) ); + // free old array + delete [] pArray; + pArray = pTmpArray; + } + } + + ASSERT( nPos <= nCount, "wrong position for insert operation"); + + if ( nPos < nCount ) + memmove( pArray + nPos + 1, pArray + nPos, + ( nCount - nPos ) * sizeof(SwTxtAttr*) + ); + pArray[ nPos ] = (SwTxtAttr*)&rAttr; + + nCount++; +} + +/************************************************************************* + * SwAttrHandler::SwAttrStack::Remove() + *************************************************************************/ + +void SwAttrHandler::SwAttrStack::Remove( const SwTxtAttr& rAttr ) +{ + USHORT nPos = Pos( rAttr ); + if ( nPos < nCount ) + { + memmove( pArray + nPos, pArray + nPos + 1, + ( nCount - 1 - nPos ) * sizeof(SwTxtAttr*) + ); + nCount--; + } +} + +/************************************************************************* + * SwAttrHandler::SwAttrStack::Top() + *************************************************************************/ + +const SwTxtAttr* SwAttrHandler::SwAttrStack::Top() const +{ + return nCount ? pArray[ nCount - 1 ] : 0; +} + +/************************************************************************* + * SwAttrHandler::SwAttrStack::Pos() + *************************************************************************/ + +USHORT SwAttrHandler::SwAttrStack::Pos( const SwTxtAttr& rAttr ) const +{ + if ( ! nCount ) + // empty stack + return USHRT_MAX; + + for ( USHORT nIdx = nCount; nIdx > 0; ) + { + if ( &rAttr == pArray[ --nIdx ] ) + return nIdx; + } + + // element not found + return USHRT_MAX; +} + +/************************************************************************* + * SwAttrHandler::SwAttrHandler() + *************************************************************************/ + +SwAttrHandler::SwAttrHandler() : mpShell( 0 ), pFnt( 0 ), bVertLayout( sal_False ) + +{ + memset( pDefaultArray, 0, NUM_DEFAULT_VALUES * sizeof(SfxPoolItem*) ); +} + +SwAttrHandler::~SwAttrHandler() +{ + delete pFnt; +} + +/************************************************************************* + * SwAttrHandler::Init() + *************************************************************************/ + +void SwAttrHandler::Init( const SwAttrSet& rAttrSet, + const IDocumentSettingAccess& rIDocumentSettingAcces, + const ViewShell* pSh ) +{ + mpIDocumentSettingAccess = &rIDocumentSettingAcces; + mpShell = pSh; + + for ( USHORT i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++ ) + pDefaultArray[ StackPos[ i ] ] = &rAttrSet.Get( i, TRUE ); +} + +void SwAttrHandler::Init( const SfxPoolItem** pPoolItem, const SwAttrSet* pAS, + const IDocumentSettingAccess& rIDocumentSettingAcces, + const ViewShell* pSh, + SwFont& rFnt, sal_Bool bVL ) +{ + // initialize default array + memcpy( pDefaultArray, pPoolItem, + NUM_DEFAULT_VALUES * sizeof(SfxPoolItem*) ); + + mpIDocumentSettingAccess = &rIDocumentSettingAcces; + mpShell = pSh; + + // do we have to apply additional paragraph attributes? + bVertLayout = bVL; + + if ( pAS && pAS->Count() ) + { + SfxItemIter aIter( *pAS ); + USHORT nWhich; + const SfxPoolItem* pItem = aIter.GetCurItem(); + while( TRUE ) + { + nWhich = pItem->Which(); + if (isCHRATR(nWhich)) + { + pDefaultArray[ StackPos[ nWhich ] ] = pItem; + FontChg( *pItem, rFnt, sal_True ); + } + + if( aIter.IsAtEnd() ) + break; + + pItem = aIter.NextItem(); + } + } + + // It is possible, that Init is called more than once, e.g., in a + // SwTxtFrm::FormatOnceMore situation. + delete pFnt; + pFnt = new SwFont( rFnt ); +} + +void SwAttrHandler::Reset( ) +{ + for ( USHORT i = 0; i < NUM_ATTRIBUTE_STACKS; i++ ) + aAttrStack[ i ].Reset(); +} + +/************************************************************************* + * SwAttrHandler::PushAndChg() + *************************************************************************/ + +void SwAttrHandler::PushAndChg( const SwTxtAttr& rAttr, SwFont& rFnt ) +{ + // these special attributes in fact represent a collection of attributes + // they have to be pushed to each stack they belong to + if ( RES_TXTATR_INETFMT == rAttr.Which() || + RES_TXTATR_CHARFMT == rAttr.Which() || + RES_TXTATR_AUTOFMT == rAttr.Which() ) + { + const SfxItemSet* pSet = CharFmt::GetItemSet( rAttr.GetAttr() ); + if ( !pSet ) return; + + for ( USHORT i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++) + { + const SfxPoolItem* pItem; + BOOL bRet = SFX_ITEM_SET == pSet->GetItemState( i, rAttr.Which() != RES_TXTATR_AUTOFMT, &pItem ); + + if ( bRet ) + { + // we push rAttr onto the appropriate stack + if ( Push( rAttr, *pItem ) ) + { + // we let pItem change rFnt + Color aColor; + if ( lcl_ChgHyperLinkColor( rAttr, *pItem, mpShell, &aColor ) ) + { + SvxColorItem aItemNext( aColor, RES_CHRATR_COLOR ); + FontChg( aItemNext, rFnt, sal_True ); + } + else + FontChg( *pItem, rFnt, sal_True ); + } + } + } + } + // this is the usual case, we have a basic attribute, push it onto the + // stack and change the font + else + { + if ( Push( rAttr, rAttr.GetAttr() ) ) + // we let pItem change rFnt + FontChg( rAttr.GetAttr(), rFnt, sal_True ); + } +} + +/************************************************************************* + * SwAttrHandler::Push() + *************************************************************************/ + +sal_Bool SwAttrHandler::Push( const SwTxtAttr& rAttr, const SfxPoolItem& rItem ) +{ + ASSERT( rItem.Which() < RES_TXTATR_WITHEND_END, + "I do not want this attribute, nWhich >= RES_TXTATR_WITHEND_END" ); + + // robust + if ( RES_TXTATR_WITHEND_END <= rItem.Which() ) + return sal_False; + + USHORT nStack = StackPos[ rItem.Which() ]; + + // attributes originating from redlining have highest priority + // second priority are hyperlink attributes, which have a color replacement + const SwTxtAttr* pTopAttr = aAttrStack[ nStack ].Top(); + if ( !pTopAttr || rAttr.IsPriorityAttr() || + ( !pTopAttr->IsPriorityAttr() && + !lcl_ChgHyperLinkColor( *pTopAttr, rItem, mpShell, 0 ) ) ) + { + aAttrStack[ nStack ].Push( rAttr ); + return sal_True; + } + + USHORT nPos = aAttrStack[ nStack ].Count(); + ASSERT( nPos, "empty stack?" ); + aAttrStack[ nStack ].Insert( rAttr, nPos - 1 ); + return sal_False; +} + +/************************************************************************* + * SwAttrHandler::PopAndChg() + *************************************************************************/ + +void SwAttrHandler::PopAndChg( const SwTxtAttr& rAttr, SwFont& rFnt ) +{ + if ( RES_TXTATR_WITHEND_END <= rAttr.Which() ) + return; // robust + + // these special attributes in fact represent a collection of attributes + // they have to be removed from each stack they belong to + if ( RES_TXTATR_INETFMT == rAttr.Which() || + RES_TXTATR_CHARFMT == rAttr.Which() || + RES_TXTATR_AUTOFMT == rAttr.Which() ) + { + const SfxItemSet* pSet = CharFmt::GetItemSet( rAttr.GetAttr() ); + if ( !pSet ) return; + + for ( USHORT i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++) + { + const SfxPoolItem* pItem; + BOOL bRet = SFX_ITEM_SET == pSet->GetItemState( i, RES_TXTATR_AUTOFMT != rAttr.Which(), &pItem ); + if ( bRet ) + { + // we remove rAttr from the appropriate stack + USHORT nStackPos = StackPos[ i ]; + aAttrStack[ nStackPos ].Remove( rAttr ); + // reset font according to attribute on top of stack + // or default value + ActivateTop( rFnt, i ); + } + } + } + // this is the usual case, we have a basic attribute, remove it from the + // stack and reset the font + else + { + aAttrStack[ StackPos[ rAttr.Which() ] ].Remove( rAttr ); + // reset font according to attribute on top of stack + // or default value + ActivateTop( rFnt, rAttr.Which() ); + } +} + +/************************************************************************* + * SwAttrHandler::Pop() + * + * only used during redlining + *************************************************************************/ + +void SwAttrHandler::Pop( const SwTxtAttr& rAttr ) +{ + ASSERT( rAttr.Which() < RES_TXTATR_WITHEND_END, + "I do not have this attribute, nWhich >= RES_TXTATR_WITHEND_END" ); + + if ( rAttr.Which() < RES_TXTATR_WITHEND_END ) + { + aAttrStack[ StackPos[ rAttr.Which() ] ].Remove( rAttr ); + } +} + +/************************************************************************* + * SwAttrHandler::ActivateTop() + *************************************************************************/ +void SwAttrHandler::ActivateTop( SwFont& rFnt, const USHORT nAttr ) +{ + ASSERT( nAttr < RES_TXTATR_WITHEND_END, + "I cannot activate this attribute, nWhich >= RES_TXTATR_WITHEND_END" ); + + const USHORT nStackPos = StackPos[ nAttr ]; + const SwTxtAttr* pTopAt = aAttrStack[ nStackPos ].Top(); + if ( pTopAt ) + { + // check if top attribute is collection of attributes + if ( RES_TXTATR_INETFMT == pTopAt->Which() || + RES_TXTATR_CHARFMT == pTopAt->Which() || + RES_TXTATR_AUTOFMT == pTopAt->Which() ) + { + const SfxItemSet* pSet = CharFmt::GetItemSet( pTopAt->GetAttr() ); + const SfxPoolItem* pItemNext; + pSet->GetItemState( nAttr, RES_TXTATR_AUTOFMT != pTopAt->Which(), &pItemNext ); + + Color aColor; + if ( lcl_ChgHyperLinkColor( *pTopAt, *pItemNext, mpShell, &aColor ) ) + { + SvxColorItem aItemNext( aColor, RES_CHRATR_COLOR ); + FontChg( aItemNext, rFnt, sal_False ); + } + else + FontChg( *pItemNext, rFnt, sal_False ); + } + else + FontChg( pTopAt->GetAttr(), rFnt, sal_False ); + } + + // default value has to be set, we only have default values for char attribs + else if ( nStackPos < NUM_DEFAULT_VALUES ) + FontChg( *pDefaultArray[ nStackPos ], rFnt, sal_False ); + else if ( RES_TXTATR_REFMARK == nAttr ) + rFnt.GetRef()--; + else if ( RES_TXTATR_TOXMARK == nAttr ) + rFnt.GetTox()--; + else if ( (RES_TXTATR_META == nAttr) || (RES_TXTATR_METAFIELD == nAttr) ) + { + rFnt.GetMeta()--; + } + else if ( RES_TXTATR_CJK_RUBY == nAttr ) + { + // ruby stack has no more attributes + // check, if an rotation attribute has to be applied + USHORT nTwoLineStack = StackPos[ RES_CHRATR_TWO_LINES ]; + sal_Bool bTwoLineAct = sal_False; + const SfxPoolItem* pTwoLineItem = 0; + const SwTxtAttr* pTwoLineAttr = aAttrStack[ nTwoLineStack ].Top(); + + if ( pTwoLineAttr ) + { + pTwoLineItem = CharFmt::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES ); + bTwoLineAct = ((SvxTwoLinesItem*)pTwoLineItem)->GetValue(); + } + else + bTwoLineAct = + ((SvxTwoLinesItem*)pDefaultArray[ nTwoLineStack ])->GetValue(); + + if ( bTwoLineAct ) + return; + + // eventually, an rotate attribute has to be activated + USHORT nRotateStack = StackPos[ RES_CHRATR_ROTATE ]; + const SfxPoolItem* pRotateItem = 0; + const SwTxtAttr* pRotateAttr = aAttrStack[ nRotateStack ].Top(); + + if ( pRotateAttr ) + { + pRotateItem = CharFmt::GetItem( *pRotateAttr, RES_CHRATR_ROTATE ); + rFnt.SetVertical( ((SvxCharRotateItem*)pRotateItem)->GetValue(), + bVertLayout ); + } + else + rFnt.SetVertical( + ((SvxCharRotateItem*)pDefaultArray[ nRotateStack ])->GetValue(), + bVertLayout + ); + } +} + +/************************************************************************* + * Font Changing Function + * + * When popping an attribute from the stack, the top mose remaining + * attribute in the stack becomes valid. The following function change + * a font depending on the stack id. + *************************************************************************/ + +void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, sal_Bool bPush ) +{ + switch ( rItem.Which() ) + { + case RES_CHRATR_CASEMAP : + rFnt.SetCaseMap( ((SvxCaseMapItem&)rItem).GetCaseMap() ); + break; + case RES_CHRATR_COLOR : + rFnt.SetColor( ((SvxColorItem&)rItem).GetValue() ); + break; + case RES_CHRATR_CONTOUR : + rFnt.SetOutline( ((SvxContourItem&)rItem).GetValue() ); + break; + case RES_CHRATR_CROSSEDOUT : + rFnt.SetStrikeout( ((SvxCrossedOutItem&)rItem).GetStrikeout() ); + break; + case RES_CHRATR_ESCAPEMENT : + rFnt.SetEscapement( ((SvxEscapementItem&)rItem).GetEsc() ); + rFnt.SetProportion( ((SvxEscapementItem&)rItem).GetProp() ); + break; + case RES_CHRATR_FONT : + rFnt.SetName( ((SvxFontItem&)rItem).GetFamilyName(), SW_LATIN ); + rFnt.SetStyleName( ((SvxFontItem&)rItem).GetStyleName(), SW_LATIN ); + rFnt.SetFamily( ((SvxFontItem&)rItem).GetFamily(), SW_LATIN ); + rFnt.SetPitch( ((SvxFontItem&)rItem).GetPitch(), SW_LATIN ); + rFnt.SetCharSet( ((SvxFontItem&)rItem).GetCharSet(), SW_LATIN ); + break; + case RES_CHRATR_FONTSIZE : + rFnt.SetSize(Size(0,((SvxFontHeightItem&)rItem).GetHeight() ), SW_LATIN ); + break; + case RES_CHRATR_KERNING : + rFnt.SetFixKerning( ((SvxKerningItem&)rItem).GetValue() ); + break; + case RES_CHRATR_LANGUAGE : + rFnt.SetLanguage( ((SvxLanguageItem&)rItem).GetLanguage(), SW_LATIN ); + break; + case RES_CHRATR_POSTURE : + rFnt.SetItalic( ((SvxPostureItem&)rItem).GetPosture(), SW_LATIN ); + break; + case RES_CHRATR_SHADOWED : + rFnt.SetShadow( ((SvxShadowedItem&)rItem).GetValue() ); + break; + case RES_CHRATR_UNDERLINE : + { + const USHORT nStackPos = StackPos[ RES_CHRATR_HIDDEN ]; + const SwTxtAttr* pTopAt = aAttrStack[ nStackPos ].Top(); + + const SfxPoolItem* pTmpItem = pTopAt ? + CharFmt::GetItem( *pTopAt, RES_CHRATR_HIDDEN ) : + pDefaultArray[ nStackPos ]; + + if( (mpShell && !mpShell->GetWin()) || + (pTmpItem && !static_cast<const SvxCharHiddenItem*>(pTmpItem)->GetValue()) ) + { + rFnt.SetUnderline( ((SvxUnderlineItem&)rItem).GetLineStyle() ); + rFnt.SetUnderColor( ((SvxUnderlineItem&)rItem).GetColor() ); + } + break; + } + case RES_CHRATR_OVERLINE : + rFnt.SetOverline( ((SvxOverlineItem&)rItem).GetLineStyle() ); + rFnt.SetOverColor( ((SvxOverlineItem&)rItem).GetColor() ); + break; + case RES_CHRATR_WEIGHT : + rFnt.SetWeight( ((SvxWeightItem&)rItem).GetWeight(), SW_LATIN ); + break; + case RES_CHRATR_WORDLINEMODE : + rFnt.SetWordLineMode( ((SvxWordLineModeItem&)rItem).GetValue() ); + break; + case RES_CHRATR_AUTOKERN : + if( ((SvxAutoKernItem&)rItem).GetValue() ) + { + rFnt.SetAutoKern( ( !mpIDocumentSettingAccess || + !mpIDocumentSettingAccess->get(IDocumentSettingAccess::KERN_ASIAN_PUNCTUATION) ) ? + KERNING_FONTSPECIFIC : + KERNING_ASIAN ); + } + else + rFnt.SetAutoKern( 0 ); + break; + case RES_CHRATR_BLINK : + rFnt.SetBlink( ((SvxBlinkItem&)rItem).GetValue() ); + break; + case RES_CHRATR_BACKGROUND : + rFnt.SetBackColor(new Color( ((SvxBrushItem&)rItem).GetColor() ) ); + break; + case RES_CHRATR_CJK_FONT : + rFnt.SetName( ((SvxFontItem&)rItem).GetFamilyName(), SW_CJK ); + rFnt.SetStyleName( ((SvxFontItem&)rItem).GetStyleName(), SW_CJK ); + rFnt.SetFamily( ((SvxFontItem&)rItem).GetFamily(), SW_CJK ); + rFnt.SetPitch( ((SvxFontItem&)rItem).GetPitch(), SW_CJK ); + rFnt.SetCharSet( ((SvxFontItem&)rItem).GetCharSet(), SW_CJK ); + break; + case RES_CHRATR_CJK_FONTSIZE : + rFnt.SetSize(Size( 0, ((SvxFontHeightItem&)rItem).GetHeight()), SW_CJK); + break; + case RES_CHRATR_CJK_LANGUAGE : + rFnt.SetLanguage( ((SvxLanguageItem&)rItem).GetLanguage(), SW_CJK ); + break; + case RES_CHRATR_CJK_POSTURE : + rFnt.SetItalic( ((SvxPostureItem&)rItem).GetPosture(), SW_CJK ); + break; + case RES_CHRATR_CJK_WEIGHT : + rFnt.SetWeight( ((SvxWeightItem&)rItem).GetWeight(), SW_CJK ); + break; + case RES_CHRATR_CTL_FONT : + rFnt.SetName( ((SvxFontItem&)rItem).GetFamilyName(), SW_CTL ); + rFnt.SetStyleName( ((SvxFontItem&)rItem).GetStyleName(), SW_CTL ); + rFnt.SetFamily( ((SvxFontItem&)rItem).GetFamily(), SW_CTL ); + rFnt.SetPitch( ((SvxFontItem&)rItem).GetPitch(), SW_CTL ); + rFnt.SetCharSet( ((SvxFontItem&)rItem).GetCharSet(), SW_CTL ); + break; + case RES_CHRATR_CTL_FONTSIZE : + rFnt.SetSize(Size(0, ((SvxFontHeightItem&)rItem).GetHeight() ), SW_CTL); + break; + case RES_CHRATR_CTL_LANGUAGE : + rFnt.SetLanguage( ((SvxLanguageItem&)rItem).GetLanguage(), SW_CTL ); + break; + case RES_CHRATR_CTL_POSTURE : + rFnt.SetItalic( ((SvxPostureItem&)rItem).GetPosture(), SW_CTL ); + break; + case RES_CHRATR_CTL_WEIGHT : + rFnt.SetWeight( ((SvxWeightItem&)rItem).GetWeight(), SW_CTL ); + break; + case RES_CHRATR_EMPHASIS_MARK : + rFnt.SetEmphasisMark( + ((SvxEmphasisMarkItem&)rItem).GetEmphasisMark() + ); + break; + case RES_CHRATR_SCALEW : + rFnt.SetPropWidth( ((SvxCharScaleWidthItem&)rItem).GetValue() ); + break; + case RES_CHRATR_RELIEF : + rFnt.SetRelief( (FontRelief)((SvxCharReliefItem&)rItem).GetValue() ); + break; + case RES_CHRATR_HIDDEN : + if( mpShell && mpShell->GetWin()) + { + if ( ((SvxCharHiddenItem&)rItem).GetValue() ) + rFnt.SetUnderline( UNDERLINE_DOTTED ); + else + ActivateTop( rFnt, RES_CHRATR_UNDERLINE ); + } + break; + case RES_CHRATR_ROTATE : + { + // rotate attribute is applied, when: + // 1. ruby stack is empty and + // 2. top of two line stack ( or default attribute )is an + // deactivated two line attribute + const bool bRuby = + 0 != aAttrStack[ StackPos[ RES_TXTATR_CJK_RUBY ] ].Count(); + + if ( bRuby ) + break; + + USHORT nTwoLineStack = StackPos[ RES_CHRATR_TWO_LINES ]; + sal_Bool bTwoLineAct = sal_False; + const SfxPoolItem* pTwoLineItem = 0; + const SwTxtAttr* pTwoLineAttr = aAttrStack[ nTwoLineStack ].Top(); + + if ( pTwoLineAttr ) + { + pTwoLineItem = CharFmt::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES ); + bTwoLineAct = ((SvxTwoLinesItem*)pTwoLineItem)->GetValue(); + } + else + bTwoLineAct = + ((SvxTwoLinesItem*)pDefaultArray[ nTwoLineStack ])->GetValue(); + + if ( !bTwoLineAct ) + rFnt.SetVertical( ((SvxCharRotateItem&)rItem).GetValue(), + bVertLayout ); + + break; + } + case RES_CHRATR_TWO_LINES : + { + sal_Bool bRuby = 0 != + aAttrStack[ StackPos[ RES_TXTATR_CJK_RUBY ] ].Count(); + sal_Bool bTwoLineAct = sal_False; + + // two line is activated, if + // 1. no ruby attribute is set and + // 2. attribute is active + bTwoLineAct = ((SvxTwoLinesItem&)rItem).GetValue(); + + if ( !bRuby && bTwoLineAct ) + { + rFnt.SetVertical( 0, bVertLayout ); + break; + } + + // a deactivating two line attribute is on top of stack, + // check if rotate attribute has to be enabled + if ( bRuby ) + break; + + USHORT nRotateStack = StackPos[ RES_CHRATR_ROTATE ]; + const SfxPoolItem* pRotateItem = 0; + const SwTxtAttr* pRotateAttr = aAttrStack[ nRotateStack ].Top(); + + if ( pRotateAttr ) + { + pRotateItem = CharFmt::GetItem( *pRotateAttr, RES_CHRATR_ROTATE ); + rFnt.SetVertical( ((SvxCharRotateItem*)pRotateItem)->GetValue(), + bVertLayout ); + } + else + rFnt.SetVertical( + ((SvxCharRotateItem*)pDefaultArray[ nRotateStack ])->GetValue(), + bVertLayout + ); + break; + } + case RES_TXTATR_CJK_RUBY : + rFnt.SetVertical( 0, bVertLayout ); + break; + case RES_TXTATR_REFMARK : + if ( bPush ) + rFnt.GetRef()++; + else + rFnt.GetRef()--; + break; + case RES_TXTATR_TOXMARK : + if ( bPush ) + rFnt.GetTox()++; + else + rFnt.GetTox()--; + break; + case RES_TXTATR_META: + case RES_TXTATR_METAFIELD: + if ( bPush ) + rFnt.GetMeta()++; + else + rFnt.GetMeta()--; + break; + } +} + +// Takes the default font and calculated the ascent and height +void SwAttrHandler::GetDefaultAscentAndHeight( ViewShell* pShell, OutputDevice& rOut, + USHORT& nAscent, USHORT& nHeight ) const +{ + ASSERT( pFnt, "No font available for GetDefaultAscentAndHeight" ) + + if ( pFnt ) + { + SwFont aFont( *pFnt ); + nHeight = aFont.GetHeight( pShell, rOut ); + nAscent = aFont.GetAscent( pShell, rOut ); + } +} + diff --git a/sw/source/core/text/blink.cxx b/sw/source/core/text/blink.cxx new file mode 100644 index 000000000000..269954bb05b2 --- /dev/null +++ b/sw/source/core/text/blink.cxx @@ -0,0 +1,201 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: blink.cxx,v $ + * $Revision: 1.9 $ + * + * 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 <tools/debug.hxx> +#include "viewsh.hxx" +#include "rootfrm.hxx" // GetOleShell() +#include "txtfrm.hxx" // FindRootFrm() +#include "blink.hxx" +#include "porlin.hxx" +#include "porlay.hxx" // SwLineLayout + +// Sichtbare Zeit: +#define BLINK_ON_TIME 2400L +// Nihct sichtbare Zeit: +#define BLINK_OFF_TIME 800L + +/************************************************************************* + * pBlink zeigt auf die Instanz, bei der sich blinkende Portions anmelden + * muessen, ggf. muss pBlink erst per new SwBlink angelegt werden. + * Diese werden dann rhythmisch zum Repaint angeregt und koennen abfragen, + * ob sie zur Zeit sichtbar oder unsichtbar sein sollen ( IsVisible() ). + *************************************************************************/ +SwBlink *pBlink = NULL; + + +// Liste von blinkenden Portions +SV_IMPL_OP_PTRARR_SORT( SwBlinkList, SwBlinkPortionPtr ) + +SwBlink::SwBlink() +{ + bVisible = sal_True; + // Den Timer vorbereiten + aTimer.SetTimeout( BLINK_ON_TIME ); + aTimer.SetTimeoutHdl( LINK(this, SwBlink, Blinker) ); +} + +SwBlink::~SwBlink( ) +{ + aTimer.Stop(); +} + +/************************************************************************* + * SwBlink::Blinker (Timerablauf): + * Sichtbar/unsichtbar-Flag toggeln. + * Repaint-Rechtecke der Blinkportions ermitteln und an ihren OleShells + * invalidieren. + *************************************************************************/ + +IMPL_LINK( SwBlink, Blinker, Timer *, EMPTYARG ) +{ + bVisible = !bVisible; + if( bVisible ) + aTimer.SetTimeout( BLINK_ON_TIME ); + else + aTimer.SetTimeout( BLINK_OFF_TIME ); + if( aList.Count() ) + { + + for( MSHORT nPos = 0; nPos < aList.Count(); ) + { + const SwBlinkPortion* pTmp = aList[ nPos ]; + if( pTmp->GetRootFrm() && + ((SwRootFrm*)pTmp->GetRootFrm())->GetCurrShell() ) + { + ++nPos; + + Point aPos = pTmp->GetPos(); + long nWidth, nHeight; + switch ( pTmp->GetDirection() ) + { + case 900: + aPos.X() -= pTmp->GetPortion()->GetAscent(); + aPos.Y() -= pTmp->GetPortion()->Width(); + nWidth = pTmp->GetPortion()->SvLSize().Height(); + nHeight = pTmp->GetPortion()->SvLSize().Width(); + break; + case 1800: + aPos.Y() -= pTmp->GetPortion()->Height() - + pTmp->GetPortion()->GetAscent(); + aPos.X() -= pTmp->GetPortion()->Width(); + nWidth = pTmp->GetPortion()->SvLSize().Width(); + nHeight = pTmp->GetPortion()->SvLSize().Height(); + break; + case 2700: + aPos.X() -= pTmp->GetPortion()->Height() - + pTmp->GetPortion()->GetAscent(); + nWidth = pTmp->GetPortion()->SvLSize().Height(); + nHeight = pTmp->GetPortion()->SvLSize().Width(); + break; + default: + aPos.Y() -= pTmp->GetPortion()->GetAscent(); + nWidth = pTmp->GetPortion()->SvLSize().Width(); + nHeight = pTmp->GetPortion()->SvLSize().Height(); + } + + Rectangle aRefresh( aPos, Size( nWidth, nHeight ) ); + aRefresh.Right() += ( aRefresh.Bottom()- aRefresh.Top() ) / 8; + ((SwRootFrm*)pTmp->GetRootFrm()) + ->GetCurrShell()->InvalidateWindows( aRefresh ); + } + else // Portions ohne Shell koennen aus der Liste entfernt werden. + aList.Remove( nPos ); + } + } + else // Wenn die Liste leer ist, kann der Timer gestoppt werden. + aTimer.Stop(); + return sal_True; +} + +void SwBlink::Insert( const Point& rPoint, const SwLinePortion* pPor, + const SwTxtFrm *pTxtFrm, USHORT nDir ) +{ + SwBlinkPortion *pBlinkPor = new SwBlinkPortion( pPor, nDir ); + + MSHORT nPos; + if( aList.Seek_Entry( pBlinkPor, &nPos ) ) + { + aList[ nPos ]->SetPos( rPoint ); + delete pBlinkPor; + } + else + { + pBlinkPor->SetPos( rPoint ); + pBlinkPor->SetRootFrm( pTxtFrm->FindRootFrm() ); + aList.Insert( pBlinkPor ); + pTxtFrm->SetBlinkPor(); + if( pPor->IsLayPortion() || pPor->IsParaPortion() ) + ((SwLineLayout*)pPor)->SetBlinking( sal_True ); + + if( !aTimer.IsActive() ) + aTimer.Start(); + } +} + +void SwBlink::Replace( const SwLinePortion* pOld, const SwLinePortion* pNew ) +{ + // setting direction to 0 because direction does not matter + // for this operation + SwBlinkPortion aBlink( pOld, 0 ); + MSHORT nPos; + if( aList.Seek_Entry( &aBlink, &nPos ) ) + { + SwBlinkPortion* pTmp = new SwBlinkPortion( aList[ nPos ], pNew ); + aList.Remove( nPos ); + aList.Insert( pTmp ); + } +} + +void SwBlink::Delete( const SwLinePortion* pPor ) +{ + // setting direction to 0 because direction does not matter + // for this operation + SwBlinkPortion aBlink( pPor, 0 ); + MSHORT nPos; + if( aList.Seek_Entry( &aBlink, &nPos ) ) + aList.Remove( nPos ); +} + +void SwBlink::FrmDelete( const SwRootFrm* pRoot ) +{ + for( MSHORT nPos = 0; nPos < aList.Count(); ) + { + if( pRoot == aList[ nPos ]->GetRootFrm() ) + aList.Remove( nPos ); + else + ++nPos; + } +} + + diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx new file mode 100644 index 000000000000..8e5bf1bfc25c --- /dev/null +++ b/sw/source/core/text/frmcrsr.cxx @@ -0,0 +1,1747 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: frmcrsr.cxx,v $ + * $Revision: 1.44.212.1 $ + * + * 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 "ndtxt.hxx" // GetNode() +#include "pam.hxx" // SwPosition +#include "frmtool.hxx" +#include "viewopt.hxx" +#include "paratr.hxx" +#include "pagefrm.hxx" +#include "colfrm.hxx" +#include "txttypes.hxx" +#include <sfx2/printer.hxx> +#include <svx/lrspitem.hxx> +#include <svx/tstpitem.hxx> +#include <svx/ulspitem.hxx> +#include <svx/lspcitem.hxx> +#include <pormulti.hxx> // SwMultiPortion +#include <doc.hxx> +#include <sortedobjs.hxx> + +#include <unicode/ubidi.h> + +#include "txtcfg.hxx" +#include "txtfrm.hxx" // SwTxtFrm +#include "inftxt.hxx" // SwTxtSizeInfo +#include "itrtxt.hxx" // SwTxtCursor +#include "crstate.hxx" // SwTxtCursor +#include "viewsh.hxx" // InvalidateWindows +#include "swfntcch.hxx" // SwFontAccess +#include "flyfrm.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include "txtpaint.hxx" +#endif + +#define MIN_OFFSET_STEP 10 + +using namespace ::com::sun::star; + + +/* + * 1170-SurvivalKit: Wie gelangt man hinter das letzte Zeichen der Zeile. + * - RightMargin verzichtet auf den Positionsausgleich mit -1 + * - GetCharRect liefert bei MV_RIGHTMARGIN ein GetEndCharRect + * - GetEndCharRect setzt bRightMargin auf sal_True + * - SwTxtCursor::bRightMargin wird per CharCrsrToLine auf sal_False gesetzt + */ + +/************************************************************************* + * GetAdjFrmAtPos() + *************************************************************************/ + +SwTxtFrm *GetAdjFrmAtPos( SwTxtFrm *pFrm, const SwPosition &rPos, + const sal_Bool bRightMargin, const sal_Bool bNoScroll = TRUE ) +{ + // 8810: vgl. 1170, RightMargin in der letzten Masterzeile... + const xub_StrLen nOffset = rPos.nContent.GetIndex(); + SwTxtFrm *pFrmAtPos = pFrm; + if( !bNoScroll || pFrm->GetFollow() ) + { + pFrmAtPos = pFrm->GetFrmAtPos( rPos ); + if( nOffset < pFrmAtPos->GetOfst() && + !pFrmAtPos->IsFollow() ) + { + xub_StrLen nNew = nOffset; + if( nNew < MIN_OFFSET_STEP ) + nNew = 0; + else + nNew -= MIN_OFFSET_STEP; + lcl_ChangeOffset( pFrmAtPos, nNew ); + } + } + while( pFrm != pFrmAtPos ) + { + pFrm = pFrmAtPos; + pFrm->GetFormatted(); + pFrmAtPos = (SwTxtFrm*)pFrm->GetFrmAtPos( rPos ); + } + + if( nOffset && bRightMargin ) + { + while( pFrmAtPos && pFrmAtPos->GetOfst() == nOffset && + pFrmAtPos->IsFollow() ) + { + pFrmAtPos->GetFormatted(); + pFrmAtPos = pFrmAtPos->FindMaster(); + } + ASSERT( pFrmAtPos, "+GetCharRect: no frame with my rightmargin" ); + } + return pFrmAtPos ? pFrmAtPos : pFrm; +} + +sal_Bool lcl_ChangeOffset( SwTxtFrm* pFrm, xub_StrLen nNew ) +{ + // In Bereichen und ausserhalb von Flies wird nicht mehr gescrollt. + ASSERT( !pFrm->IsFollow(), "Illegal Scrolling by Follow!" ); + if( pFrm->GetOfst() != nNew && !pFrm->IsInSct() ) + { + SwFlyFrm *pFly = pFrm->FindFlyFrm(); + // Vorsicht, wenn z.B. bei einem spaltigen Rahmen die Groesse noch invalide ist, + // duerfen wir nicht mal eben herumscrollen + if ( ( pFly && pFly->IsValid() && + !pFly->GetNextLink() && !pFly->GetPrevLink() ) || + ( !pFly && pFrm->IsInTab() ) ) + { + ViewShell* pVsh = pFrm->GetShell(); + if( pVsh ) + { + if( pVsh->GetNext() != pVsh || + ( pFrm->GetDrawObjs() && pFrm->GetDrawObjs()->Count() ) ) + { + if( !pFrm->GetOfst() ) + return sal_False; + nNew = 0; + } + pFrm->SetOfst( nNew ); + pFrm->SetPara( 0 ); + pFrm->GetFormatted(); + if( pFrm->Frm().HasArea() ) + pFrm->GetShell()->InvalidateWindows( pFrm->Frm() ); + return sal_True; + } + } + } + return sal_False; +} + +/************************************************************************* + * GetFrmAtOfst(), GetFrmAtPos() + *************************************************************************/ + +// OD 07.10.2003 #110978# +SwTxtFrm& SwTxtFrm::GetFrmAtOfst( const xub_StrLen nWhere ) +{ + SwTxtFrm* pRet = this; + while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOfst() ) + pRet = pRet->GetFollow(); + return *pRet; +} + +SwTxtFrm *SwTxtFrm::GetFrmAtPos( const SwPosition &rPos ) +{ + SwTxtFrm *pFoll = (SwTxtFrm*)this; + while( pFoll->GetFollow() ) + { + if( rPos.nContent.GetIndex() > pFoll->GetFollow()->GetOfst() ) + pFoll = pFoll->GetFollow(); + else + { + if( rPos.nContent.GetIndex() == pFoll->GetFollow()->GetOfst() + && !SwTxtCursor::IsRightMargin() ) + pFoll = pFoll->GetFollow(); + else + break; + } + } + return pFoll; +} + +/************************************************************************* + * SwTxtFrm::GetCharRect() + *************************************************************************/ + +/* + * GetCharRect() findet die Characterzelle des Characters, dass + * durch aPos beschrieben wird. GetCrsrOfst() findet den + * umgekehrten Weg: Von einer Dokumentkoordinate zu einem Pam. + * Beide sind virtuell in der Framebasisklasse und werden deshalb + * immer angezogen. + */ + +sal_Bool SwTxtFrm::GetCharRect( SwRect& rOrig, const SwPosition &rPos, + SwCrsrMoveState *pCMS ) const +{ + ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::GetCharRect with swapped frame" ); + + if( IsLocked() || IsHiddenNow() ) + return sal_False; + + //Erstmal den richtigen Frm finden, dabei muss beachtet werden, dass: + //- die gecachten Informationen verworfen sein koennen (GetPara() == 0) + //- das ein Follow gemeint sein kann + //- das die Kette der Follows dynamisch waechst; der in den wir + // schliesslich gelangen muss aber Formatiert sein. + + // opt: reading ahead erspart uns ein GetAdjFrmAtPos + const sal_Bool bRightMargin = pCMS && ( MV_RIGHTMARGIN == pCMS->eState ); + const sal_Bool bNoScroll = pCMS && pCMS->bNoScroll; + SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, rPos, bRightMargin, + bNoScroll ); + pFrm->GetFormatted(); + const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper(); + + SWRECTFN ( pFrm ) + const SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)(); + const SwTwips nFrmMaxY = (pFrm->*fnRect->fnGetPrtBottom)(); + + // nMaxY is an absolute value + SwTwips nMaxY = bVert ? + Max( nFrmMaxY, nUpperMaxY ) : + Min( nFrmMaxY, nUpperMaxY ); + + sal_Bool bRet = sal_False; + + if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() ) + { + Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos(); + SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); + short nFirstOffset; + pTxtNd->GetFirstLineOfsWithNum( nFirstOffset ); + + Point aPnt2; + if ( bVert ) + { + if( nFirstOffset > 0 ) + aPnt1.Y() += nFirstOffset; + + if ( aPnt1.X() < nMaxY ) + aPnt1.X() = nMaxY; + aPnt2.X() = aPnt1.X() + pFrm->Prt().Width(); + aPnt2.Y() = aPnt1.Y(); + if( aPnt2.X() < nMaxY ) + aPnt2.X() = nMaxY; + } + else + { + if( nFirstOffset > 0 ) + aPnt1.X() += nFirstOffset; + + if( aPnt1.Y() > nMaxY ) + aPnt1.Y() = nMaxY; + aPnt2.X() = aPnt1.X(); + aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height(); + if( aPnt2.Y() > nMaxY ) + aPnt2.Y() = nMaxY; + } + + rOrig = SwRect( aPnt1, aPnt2 ); + + if ( pCMS ) + { + pCMS->aRealHeight.X() = 0; + pCMS->aRealHeight.Y() = bVert ? -rOrig.Width() : rOrig.Height(); + } + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( rOrig ); + + bRet = sal_True; + } + else + { + if( !pFrm->HasPara() ) + return sal_False; + + SwFrmSwapper aSwapper( pFrm, sal_True ); + if ( bVert ) + nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY ); + + sal_Bool bGoOn = sal_True; + xub_StrLen nOffset = rPos.nContent.GetIndex(); + xub_StrLen nNextOfst; + + do + { + { + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + nNextOfst = aLine.GetEnd(); + // Siehe Kommentar in AdjustFrm + // 1170: das letzte Zeichen der Zeile mitnehmen? + bRet = bRightMargin ? aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY ) + : aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY ); + } + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( rOrig ); + + if ( bVert ) + pFrm->SwitchHorizontalToVertical( rOrig ); + + if( pFrm->IsUndersized() && pCMS && !pFrm->GetNext() && + (rOrig.*fnRect->fnGetBottom)() == nUpperMaxY && + pFrm->GetOfst() < nOffset && + !pFrm->IsFollow() && !bNoScroll && + pFrm->GetTxtNode()->GetTxt().Len() != nNextOfst ) + bGoOn = lcl_ChangeOffset( pFrm, nNextOfst ); + else + bGoOn = sal_False; + } while ( bGoOn ); + + if ( pCMS ) + { + if ( pFrm->IsRightToLeft() ) + { + if( pCMS->b2Lines && pCMS->p2Lines) + { + pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aLine ); + pFrm->SwitchLTRtoRTL( pCMS->p2Lines->aPortion ); + } + } + + if ( bVert ) + { + if ( pCMS->bRealHeight ) + { + pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y(); + if ( pCMS->aRealHeight.Y() < 0 ) + { + // writing direction is from top to bottom + pCMS->aRealHeight.X() = ( rOrig.Width() - + pCMS->aRealHeight.X() + + pCMS->aRealHeight.Y() ); + } + } + if( pCMS->b2Lines && pCMS->p2Lines) + { + pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aLine ); + pFrm->SwitchHorizontalToVertical( pCMS->p2Lines->aPortion ); + } + } + + } + } + if( bRet ) + { + SwPageFrm *pPage = pFrm->FindPageFrm(); + ASSERT( pPage, "Text esaped from page?" ); + const SwTwips nOrigTop = (rOrig.*fnRect->fnGetTop)(); + const SwTwips nPageTop = (pPage->Frm().*fnRect->fnGetTop)(); + const SwTwips nPageBott = (pPage->Frm().*fnRect->fnGetBottom)(); + + // Following situation: if the frame is in an invalid sectionframe, + // it's possible that the frame is outside the page. If we restrict + // the cursor position to the page area, we enforce the formatting + // of the page, of the section frame and the frame himself. + if( (*fnRect->fnYDiff)( nPageTop, nOrigTop ) > 0 ) + (rOrig.*fnRect->fnSetTop)( nPageTop ); + + if ( (*fnRect->fnYDiff)( nOrigTop, nPageBott ) > 0 ) + (rOrig.*fnRect->fnSetTop)( nPageBott ); + } + + return bRet; +} + +/************************************************************************* + * SwTxtFrm::GetAutoPos() + *************************************************************************/ + +/* + * GetAutoPos() findet die Characterzelle des Characters, dass + * durch aPos beschrieben wird und wird von autopositionierten Rahmen genutzt. + */ + +sal_Bool SwTxtFrm::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const +{ + if( IsHiddenNow() ) + return sal_False; + + xub_StrLen nOffset = rPos.nContent.GetIndex(); + SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset )); + + pFrm->GetFormatted(); + const SwFrm* pTmpFrm = (SwFrm*)pFrm->GetUpper(); + + SWRECTFN( pTmpFrm ) + SwTwips nUpperMaxY = (pTmpFrm->*fnRect->fnGetPrtBottom)(); + + // nMaxY is in absolute value + SwTwips nMaxY = bVert ? + Max( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ) : + Min( (pFrm->*fnRect->fnGetPrtBottom)(), nUpperMaxY ); + + if ( pFrm->IsEmpty() || ! (pFrm->Prt().*fnRect->fnGetHeight)() ) + { + Point aPnt1 = pFrm->Frm().Pos() + pFrm->Prt().Pos(); + Point aPnt2; + if ( bVert ) + { + if ( aPnt1.X() < nMaxY ) + aPnt1.X() = nMaxY; + aPnt2.X() = aPnt1.X() + pFrm->Prt().Width(); + aPnt2.Y() = aPnt1.Y(); + if( aPnt2.X() < nMaxY ) + aPnt2.X() = nMaxY; + } + else + { + if( aPnt1.Y() > nMaxY ) + aPnt1.Y() = nMaxY; + aPnt2.X() = aPnt1.X(); + aPnt2.Y() = aPnt1.Y() + pFrm->Prt().Height(); + if( aPnt2.Y() > nMaxY ) + aPnt2.Y() = nMaxY; + } + rOrig = SwRect( aPnt1, aPnt2 ); + return sal_True; + } + else + { + if( !pFrm->HasPara() ) + return sal_False; + + SwFrmSwapper aSwapper( pFrm, sal_True ); + if ( bVert ) + nMaxY = pFrm->SwitchVerticalToHorizontal( nMaxY ); + + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + SwCrsrMoveState aTmpState( MV_SETONLYTEXT ); + aTmpState.bRealHeight = TRUE; + if( aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY ) ) + { + if( aTmpState.aRealHeight.X() >= 0 ) + { + rOrig.Pos().Y() += aTmpState.aRealHeight.X(); + rOrig.Height( aTmpState.aRealHeight.Y() ); + } + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( rOrig ); + + if ( bVert ) + pFrm->SwitchHorizontalToVertical( rOrig ); + + return sal_True; + } + return sal_False; + } +} + +/** determine top of line for given position in the text frame + + OD 11.11.2003 #i22341# + OD 2004-03-18 #114789# - corrections: + - Top of first paragraph line is the top of the printing area of the text frame + - If a proportional line spacing is applied use top of anchor character as + top of the line. + + @author OD +*/ +bool SwTxtFrm::GetTopOfLine( SwTwips& _onTopOfLine, + const SwPosition& _rPos ) const +{ + bool bRet = true; + + // get position offset + xub_StrLen nOffset = _rPos.nContent.GetIndex(); + + if ( GetTxt().Len() < nOffset ) + { + bRet = false; + } + else + { + SWRECTFN( this ) + if ( IsEmpty() || !(Prt().*fnRect->fnGetHeight)() ) + { + // OD 2004-03-18 #i11860# - consider upper space amount considered + // for previous frame and the page grid. + _onTopOfLine = (this->*fnRect->fnGetPrtTop)(); + } + else + { + // determine formatted text frame that contains the requested position + SwTxtFrm* pFrm = &(const_cast<SwTxtFrm*>(this)->GetFrmAtOfst( nOffset )); + pFrm->GetFormatted(); + SWREFRESHFN( pFrm ) + // OD 2004-03-18 #114789# - If proportional line spacing is applied + // to the text frame, the top of the anchor character is also the + // top of the line. + // Otherwise the line layout determines the top of the line + const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing(); + if ( rSpace.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) + { + SwRect aCharRect; + if ( GetAutoPos( aCharRect, _rPos ) ) + { + _onTopOfLine = (aCharRect.*fnRect->fnGetTop)(); + } + else + { + bRet = false; + } + } + else + { + // assure that text frame is in a horizontal layout + SwFrmSwapper aSwapper( pFrm, sal_True ); + // determine text line that contains the requested position + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + aLine.CharCrsrToLine( nOffset ); + // determine top of line + _onTopOfLine = aLine.Y(); + if ( bVert ) + { + _onTopOfLine = pFrm->SwitchHorizontalToVertical( _onTopOfLine ); + } + } + } + } + + return bRet; +} + +/************************************************************************* + * SwTxtFrm::_GetCrsrOfst() + *************************************************************************/ + +// Minimaler Abstand von nichtleeren Zeilen etwas weniger als 2 cm +#define FILL_MIN_DIST 1100 + +struct SwFillData +{ + SwRect aFrm; + const SwCrsrMoveState *pCMS; + SwPosition* pPos; + const Point& rPoint; + SwTwips nLineWidth; + sal_Bool bFirstLine : 1; + sal_Bool bInner : 1; + sal_Bool bColumn : 1; + sal_Bool bEmpty : 1; + SwFillData( const SwCrsrMoveState *pC, SwPosition* pP, const SwRect& rR, + const Point& rPt ) : aFrm( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ), + nLineWidth( 0 ), bFirstLine( sal_True ), bInner( sal_False ), bColumn( sal_False ), + bEmpty( sal_True ){} + SwFillMode Mode() const { return pCMS->pFill->eMode; } + long X() const { return rPoint.X(); } + long Y() const { return rPoint.Y(); } + long Left() const { return aFrm.Left(); } + long Right() const { return aFrm.Right(); } + long Bottom() const { return aFrm.Bottom(); } + SwRect& Frm() { return aFrm; } + SwFillCrsrPos &Fill() const { return *pCMS->pFill; } + void SetTab( MSHORT nNew ) { pCMS->pFill->nTabCnt = nNew; } + void SetSpace( MSHORT nNew ) { pCMS->pFill->nSpaceCnt = nNew; } + void SetOrient( const sal_Int16 eNew ){ pCMS->pFill->eOrient = eNew; } +}; + +sal_Bool SwTxtFrm::_GetCrsrOfst(SwPosition* pPos, const Point& rPoint, + const sal_Bool bChgFrm, SwCrsrMoveState* pCMS ) const +{ + // 8804: _GetCrsrOfst wird vom GetCrsrOfst und GetKeyCrsrOfst gerufen. + // In keinem Fall nur ein return sal_False. + + if( IsLocked() || IsHiddenNow() ) + return sal_False; + + ((SwTxtFrm*)this)->GetFormatted(); + + Point aOldPoint( rPoint ); + + if ( IsVertical() ) + { + SwitchVerticalToHorizontal( (Point&)rPoint ); + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + } + + if ( IsRightToLeft() ) + SwitchRTLtoLTR( (Point&)rPoint ); + + SwFillData *pFillData = ( pCMS && pCMS->pFill ) ? + new SwFillData( pCMS, pPos, Frm(), rPoint ) : NULL; + + if ( IsEmpty() ) + { + SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); + pPos->nNode = *pTxtNd; + pPos->nContent.Assign( pTxtNd, 0 ); + if( pCMS && pCMS->bFieldInfo ) + { + SwTwips nDiff = rPoint.X() - Frm().Left() - Prt().Left(); + if( nDiff > 50 || nDiff < 0 ) + ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True; + } + } + else + { + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + + // Siehe Kommentar in AdjustFrm() + SwTwips nMaxY = Frm().Top() + Prt().Top() + Prt().Height(); + aLine.TwipsToLine( rPoint.Y() ); + while( aLine.Y() + aLine.GetLineHeight() > nMaxY ) + { + DBG_LOOP; + if( !aLine.Prev() ) + break; + } + + if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr() + && rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() ) + while( aLine.GetLineNr() > 1 ) + aLine.Prev(); + + xub_StrLen nOffset = aLine.GetCrsrOfst( pPos, rPoint, bChgFrm, pCMS ); + + if( pCMS && pCMS->eState == MV_NONE && aLine.GetEnd() == nOffset ) + ((SwCrsrMoveState*)pCMS)->eState = MV_RIGHTMARGIN; + + // 6776: pPos ist ein reiner IN-Parameter, der nicht ausgewertet werden darf. + // Das pIter->GetCrsrOfst returnt aus einer Verschachtelung mit STRING_LEN. + // Wenn SwTxtIter::GetCrsrOfst von sich aus weitere GetCrsrOfst + // ruft, so aendert sich nNode der Position. In solchen Faellen + // darf pPos nicht berechnet werden. + if( STRING_LEN != nOffset ) + { + SwTxtNode* pTxtNd = ((SwTxtFrm*)this)->GetTxtNode(); + pPos->nNode = *pTxtNd; + pPos->nContent.Assign( pTxtNd, nOffset ); + if( pFillData ) + { + if( pTxtNd->GetTxt().Len() > nOffset || + rPoint.Y() < Frm().Top() ) + pFillData->bInner = sal_True; + pFillData->bFirstLine = aLine.GetLineNr() < 2; + if( pTxtNd->GetTxt().Len() ) + { + pFillData->bEmpty = sal_False; + pFillData->nLineWidth = aLine.GetCurr()->Width(); + } + } + } + } + sal_Bool bChgFillData = sal_False; + if( pFillData && FindPageFrm()->Frm().IsInside( aOldPoint ) ) + { + FillCrsrPos( *pFillData ); + bChgFillData = sal_True; + } + + if ( IsVertical() ) + { + if ( bChgFillData ) + SwitchHorizontalToVertical( pFillData->Fill().aCrsr.Pos() ); + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + } + + if ( IsRightToLeft() && bChgFillData ) + { + SwitchLTRtoRTL( pFillData->Fill().aCrsr.Pos() ); + const sal_Int16 eOrient = pFillData->pCMS->pFill->eOrient; + + if ( text::HoriOrientation::LEFT == eOrient ) + pFillData->SetOrient( text::HoriOrientation::RIGHT ); + else if ( text::HoriOrientation::RIGHT == eOrient ) + pFillData->SetOrient( text::HoriOrientation::LEFT ); + } + + (Point&)rPoint = aOldPoint; + delete pFillData; + + return sal_True; +} + +/************************************************************************* + * virtual SwTxtFrm::GetCrsrOfst() + *************************************************************************/ + +sal_Bool SwTxtFrm::GetCrsrOfst(SwPosition* pPos, Point& rPoint, + SwCrsrMoveState* pCMS ) const +{ + MSHORT nChgFrm = 2; + if( pCMS ) + { + if( MV_UPDOWN == pCMS->eState ) + nChgFrm = 0; + else if( MV_SETONLYTEXT == pCMS->eState || + MV_TBLSEL == pCMS->eState ) + nChgFrm = 1; + } + return _GetCrsrOfst( pPos, rPoint, nChgFrm != 0, pCMS ); +} + +/************************************************************************* + * SwTxtFrm::LeftMargin() + *************************************************************************/ + +/* + * Layout-orientierte Cursorbewegungen + */ + +/* + * an den Zeilenanfang + */ + +sal_Bool SwTxtFrm::LeftMargin(SwPaM *pPam) const +{ + if( ((const SwNode*)pPam->GetNode()) != GetNode() ) + pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode(); + + SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(), + SwTxtCursor::IsRightMargin() ); + pFrm->GetFormatted(); + xub_StrLen nIndx; + if ( pFrm->IsEmpty() ) + nIndx = 0; + else + { + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + + aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex()); + nIndx = aLine.GetStart(); + if( pFrm->GetOfst() && !pFrm->IsFollow() && !aLine.GetPrev() ) + { + lcl_ChangeOffset( pFrm, 0 ); + nIndx = 0; + } + } + pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nIndx ); + SwTxtCursor::SetRightMargin( sal_False ); + return sal_True; +} + +/************************************************************************* + * SwTxtFrm::RightMargin() + *************************************************************************/ + +/* + * An das Zeilenende:Das ist die Position vor dem letzten + * Character in der Zeile. Ausnahme: In der letzten Zeile soll + * der Cursor auch hinter dem letzten Character stehen koennen, + * um Text anhaengen zu koennen. + * + */ + +sal_Bool SwTxtFrm::RightMargin(SwPaM *pPam, sal_Bool bAPI) const +{ + if( ((const SwNode*)pPam->GetNode()) != GetNode() ) + pPam->GetPoint()->nNode = *((SwTxtFrm*)this)->GetTxtNode(); + + SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *pPam->GetPoint(), + SwTxtCursor::IsRightMargin() ); + pFrm->GetFormatted(); + xub_StrLen nRightMargin; + if ( IsEmpty() ) + nRightMargin = 0; + else + { + SwTxtSizeInfo aInf( pFrm ); + SwTxtCursor aLine( pFrm, &aInf ); + + aLine.CharCrsrToLine(pPam->GetPoint()->nContent.GetIndex()); + nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen(); + + // Harte Zeilenumbrueche lassen wir hinter uns. + if( aLine.GetCurr()->GetLen() && + CH_BREAK == aInf.GetTxt().GetChar( nRightMargin - 1 ) ) + --nRightMargin; + if( !bAPI && (aLine.GetNext() || pFrm->GetFollow()) ) + { + while( nRightMargin > aLine.GetStart() && + ' ' == aInf.GetTxt().GetChar( nRightMargin - 1 ) ) + --nRightMargin; + } + } + pPam->GetPoint()->nContent = SwIndex( pFrm->GetTxtNode(), nRightMargin ); + SwTxtCursor::SetRightMargin( !bAPI ); + return sal_True; +} + +/************************************************************************* + * SwTxtFrm::_UnitUp() + *************************************************************************/ + +//Die beiden folgenden Methoden versuchen zunaechst den Crsr in die +//nachste/folgende Zeile zu setzen. Gibt es im Frame keine vorhergehende/ +//folgende Zeile, so wird der Aufruf an die Basisklasse weitergeleitet. +//Die Horizontale Ausrichtung des Crsr wird hinterher von der CrsrShell +//vorgenommen. + +class SwSetToRightMargin +{ + sal_Bool bRight; +public: + inline SwSetToRightMargin() : bRight( sal_False ) { } + inline ~SwSetToRightMargin() { SwTxtCursor::SetRightMargin( bRight ); } + inline void SetRight( const sal_Bool bNew ) { bRight = bNew; } +}; + +sal_Bool SwTxtFrm::_UnitUp( SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + // 8626: Im Notfall den RightMargin setzen. + SwSetToRightMargin aSet; + + if( IsInTab() && + pPam->GetNode( sal_True )->StartOfSectionNode() != + pPam->GetNode( sal_False )->StartOfSectionNode() ) + { + //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um + //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet. + return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); + } + + ((SwTxtFrm*)this)->GetFormatted(); + const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex(); + SwRect aCharBox; + + if( !IsEmpty() && !IsHiddenNow() ) + { + xub_StrLen nFormat = STRING_LEN; + do + { + if( nFormat != STRING_LEN && !IsFollow() ) + lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ); + + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + + // 8116: Flys ohne Umlauf und IsDummy(); hier wegoptimiert + if( nPos ) + aLine.CharCrsrToLine( nPos ); + else + aLine.Top(); + + const SwLineLayout *pPrevLine = aLine.GetPrevLine(); + const xub_StrLen nStart = aLine.GetStart(); + aLine.GetCharRect( &aCharBox, nPos ); + + sal_Bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() ); + sal_Bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() ); + + if( !pPrevLine && !bSecondOfDouble && GetOfst() && !IsFollow() ) + { + nFormat = GetOfst(); + xub_StrLen nDiff = aLine.GetLength(); + if( !nDiff ) + nDiff = MIN_OFFSET_STEP; + if( nFormat > nDiff ) + nFormat = nFormat - nDiff; + else + nFormat = 0; + continue; + } + + // we select the target line for the cursor, in case we are in a + // double line portion, prev line = curr line + if( bPrevLine && !bSecondOfDouble ) + { + aLine.PrevLine(); + while ( aLine.GetStart() == nStart && + 0 != ( pPrevLine = aLine.GetPrevLine() ) && + pPrevLine != aLine.GetCurr() ) + aLine.PrevLine(); + } + + if ( bPrevLine || bSecondOfDouble ) + { + aCharBox.SSize().Width() /= 2; + aCharBox.Pos().X() = aCharBox.Pos().X() - 150; + + // siehe Kommentar in SwTxtFrm::GetCrsrOfst() +#ifndef PRODUCT + const ULONG nOldNode = pPam->GetPoint()->nNode.GetIndex(); +#endif + // Der Node soll nicht gewechselt werden + xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(), + aCharBox.Pos(), sal_False ); + ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(), + "SwTxtFrm::UnitUp: illegal node change" ) + + // 7684: Wir stellen sicher, dass wir uns nach oben bewegen. + if( nTmpOfst >= nStart && nStart && !bSecondOfDouble ) + { + nTmpOfst = nStart; + aSet.SetRight( sal_True ); + } + pPam->GetPoint()->nContent = + SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst ); + return sal_True; + } + + if ( IsFollow() ) + { + aLine.GetCharRect( &aCharBox, nPos ); + aCharBox.SSize().Width() /= 2; + } + break; + } while ( sal_True ); + } + /* Wenn this ein Follow ist und ein Prev miszlang, so + * muessen wir in die letzte Zeile des Master ... und der sind wir. + * Oder wir sind ein Follow mit Follow, dann muessen wir uns den + * Master extra besorgen... + */ + if ( IsFollow() ) + { + const SwTxtFrm *pTmpPrev = FindMaster(); + xub_StrLen nOffs = GetOfst(); + if( pTmpPrev ) + { + ViewShell *pSh = GetShell(); + sal_Bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea(); + const SwTxtFrm *pPrevPrev = pTmpPrev; + // Hier werden geschuetzte Frames und Frame ohne Inhalt ausgelassen + while( pPrevPrev && ( pPrevPrev->GetOfst() == nOffs || + ( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) ) + { + pTmpPrev = pPrevPrev; + nOffs = pTmpPrev->GetOfst(); + if ( pPrevPrev->IsFollow() ) + pPrevPrev = pTmpPrev->FindMaster(); + else + pPrevPrev = NULL; + } + if ( !pPrevPrev ) + return pTmpPrev->SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); + aCharBox.Pos().Y() = pPrevPrev->Frm().Bottom() - 1; + return pPrevPrev->GetKeyCrsrOfst( pPam->GetPoint(), aCharBox.Pos() ); + } + } + return SwCntntFrm::UnitUp( pPam, nOffset, bSetInReadOnly ); +} + +// +// Used for Bidi. nPos is the logical position in the string, bLeft indicates +// if left arrow or right arrow was pressed. The return values are: +// nPos: the new visual position +// bLeft: whether the break iterator has to add or subtract from the +// current position +void lcl_VisualMoveRecursion( const SwLineLayout& rCurrLine, xub_StrLen nIdx, + xub_StrLen& nPos, sal_Bool& bRight, + BYTE& nCrsrLevel, BYTE nDefaultDir ) +{ + const SwLinePortion* pPor = rCurrLine.GetFirstPortion(); + const SwLinePortion* pLast = 0; + + // what's the current portion + while ( pPor && nIdx + pPor->GetLen() <= nPos ) + { + nIdx = nIdx + pPor->GetLen(); + pLast = pPor; + pPor = pPor->GetPortion(); + } + + if ( bRight ) + { + sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->IsBidi(); + + // 1. special case: at beginning of bidi portion + if ( bRecurse && nIdx == nPos ) + { + nPos = nPos + pPor->GetLen(); + + // leave bidi portion + if ( nCrsrLevel != nDefaultDir ) + { + bRecurse = sal_False; + } + else + // special case: + // buffer: abcXYZ123 in LTR paragraph + // view: abc123ZYX + // cursor is between c and X in the buffer and cursor level = 0 + nCrsrLevel++; + } + + // 2. special case: at beginning of portion after bidi portion + else if ( pLast && pLast->IsMultiPortion() && + ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos ) + { + // enter bidi portion + if ( nCrsrLevel != nDefaultDir ) + { + bRecurse = sal_True; + nIdx = nIdx - pLast->GetLen(); + pPor = pLast; + } + } + + // Recursion + if ( bRecurse ) + { + const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot(); + xub_StrLen nTmpPos = nPos - nIdx; + sal_Bool bTmpForward = ! bRight; + BYTE nTmpCrsrLevel = nCrsrLevel; + lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward, + nTmpCrsrLevel, nDefaultDir + 1 ); + + nPos = nTmpPos + nIdx; + bRight = bTmpForward; + nCrsrLevel = nTmpCrsrLevel; + } + + // go forward + else + { + bRight = sal_True; + nCrsrLevel = nDefaultDir; + } + + } + else + { + sal_Bool bRecurse = pPor && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi(); + + // 1. special case: at beginning of bidi portion + if ( bRecurse && nIdx == nPos ) + { + // leave bidi portion + if ( nCrsrLevel == nDefaultDir ) + { + bRecurse = sal_False; + } + } + + // 2. special case: at beginning of portion after bidi portion + else if ( pLast && pLast->IsMultiPortion() && + ((SwMultiPortion*)pLast)->IsBidi() && nIdx == nPos ) + { + nPos = nPos - pLast->GetLen(); + + // enter bidi portion + if ( nCrsrLevel % 2 == nDefaultDir % 2 ) + { + bRecurse = sal_True; + nIdx = nIdx - pLast->GetLen(); + pPor = pLast; + + // special case: + // buffer: abcXYZ123 in LTR paragraph + // view: abc123ZYX + // cursor is behind 3 in the buffer and cursor level = 2 + if ( nDefaultDir + 2 == nCrsrLevel ) + nPos = nPos + pLast->GetLen(); + } + } + + // go forward + if ( bRecurse ) + { + const SwLineLayout& rLine = ((SwMultiPortion*)pPor)->GetRoot(); + xub_StrLen nTmpPos = nPos - nIdx; + sal_Bool bTmpForward = ! bRight; + BYTE nTmpCrsrLevel = nCrsrLevel; + lcl_VisualMoveRecursion( rLine, 0, nTmpPos, bTmpForward, + nTmpCrsrLevel, nDefaultDir + 1 ); + + // special case: + // buffer: abcXYZ123 in LTR paragraph + // view: abc123ZYX + // cursor is between Z and 1 in the buffer and cursor level = 2 + if ( nTmpPos == pPor->GetLen() && nTmpCrsrLevel == nDefaultDir + 1 ) + { + nTmpPos = nTmpPos - pPor->GetLen(); + nTmpCrsrLevel = nDefaultDir; + bTmpForward = ! bTmpForward; + } + + nPos = nTmpPos + nIdx; + bRight = bTmpForward; + nCrsrLevel = nTmpCrsrLevel; + } + + // go backward + else + { + bRight = sal_False; + nCrsrLevel = nDefaultDir; + } + } +} + +void SwTxtFrm::PrepareVisualMove( xub_StrLen& nPos, BYTE& nCrsrLevel, + sal_Bool& bForward, sal_Bool bInsertCrsr ) +{ + if( IsEmpty() || IsHiddenNow() ) + return; + + ((SwTxtFrm*)this)->GetFormatted(); + + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + + if( nPos ) + aLine.CharCrsrToLine( nPos ); + else + aLine.Top(); + + const SwLineLayout* pLine = aLine.GetCurr(); + const xub_StrLen nStt = aLine.GetStart(); + const xub_StrLen nLen = pLine->GetLen(); + + // We have to distinguish between an insert and overwrite cursor: + // The insert cursor position depends on the cursor level: + // buffer: abcXYZdef in LTR paragraph + // display: abcZYXdef + // If cursor is between c and X in the buffer and cursor level is 0, + // the cursor blinks between c and Z and -> sets the cursor between Z and Y. + // If the cursor level is 1, the cursor blinks between X and d and + // -> sets the cursor between d and e. + // The overwrite cursor simply travels to the next visual character. + if ( bInsertCrsr ) + { + lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward, + nCrsrLevel, IsRightToLeft() ? 1 : 0 ); + return; + } + + const BYTE nDefaultDir = static_cast<BYTE>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR); + const sal_Bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) || + ( nDefaultDir == UBIDI_RTL && ! bForward ); + + // + // Bidi functions from icu 2.0 + // + const sal_Unicode* pLineString = GetTxtNode()->GetTxt().GetBuffer(); + pLine += nStt; + + UErrorCode nError = U_ZERO_ERROR; + UBiDi* pBidi = ubidi_openSized( nLen, 0, &nError ); + ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), nLen, nDefaultDir, NULL, &nError ); // UChar != sal_Unicode in MinGW + + xub_StrLen nTmpPos; + sal_Bool bOutOfBounds = sal_False; + + if ( nPos < nStt + nLen ) + { + nTmpPos = (xub_StrLen)ubidi_getVisualIndex( pBidi, nPos, &nError ); + + // visual indices are always LTR aligned + if ( bVisualRight ) + { + if ( nTmpPos + 1 < nStt + nLen ) + ++nTmpPos; + else + { + nPos = nDefaultDir == UBIDI_RTL ? 0 : nStt + nLen; + bOutOfBounds = sal_True; + } + } + else + { + if ( nTmpPos ) + --nTmpPos; + else + { + nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : 0; + bOutOfBounds = sal_True; + } + } + } + else + { + nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - 1 : 0; + } + + if ( ! bOutOfBounds ) + { + nPos = (xub_StrLen)ubidi_getLogicalIndex( pBidi, nTmpPos, &nError ); + + if ( bForward ) + { + if ( nPos ) + --nPos; + else + { + ++nPos; + bForward = ! bForward; + } + } + else + ++nPos; + } + + ubidi_close( pBidi ); +} + +/************************************************************************* + * SwTxtFrm::_UnitDown() + *************************************************************************/ + +sal_Bool SwTxtFrm::_UnitDown(SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + + if ( IsInTab() && + pPam->GetNode( sal_True )->StartOfSectionNode() != + pPam->GetNode( sal_False )->StartOfSectionNode() ) + { + //Wenn der PaM in unterschiedlichen Boxen sitzt, so handelt es sich um + //eine Tabellenselektion; diese wird von der Basisklasse abgearbeitet. + return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); + } + ((SwTxtFrm*)this)->GetFormatted(); + const xub_StrLen nPos = pPam->GetPoint()->nContent.GetIndex(); + SwRect aCharBox; + const SwCntntFrm *pTmpFollow = 0; + + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + + if ( !IsEmpty() && !IsHiddenNow() ) + { + xub_StrLen nFormat = STRING_LEN; + do + { + if( nFormat != STRING_LEN && !IsFollow() && + !lcl_ChangeOffset( ((SwTxtFrm*)this), nFormat ) ) + break; + + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtCursor aLine( ((SwTxtFrm*)this), &aInf ); + nFormat = aLine.GetEnd(); + + aLine.CharCrsrToLine( nPos ); + + const SwLineLayout* pNextLine = aLine.GetNextLine(); + const xub_StrLen nStart = aLine.GetStart(); + aLine.GetCharRect( &aCharBox, nPos ); + + sal_Bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() ); + + if( pNextLine || bFirstOfDouble ) + { + aCharBox.SSize().Width() /= 2; +#ifndef PRODUCT + // siehe Kommentar in SwTxtFrm::GetCrsrOfst() + const ULONG nOldNode = pPam->GetPoint()->nNode.GetIndex(); +#endif + if ( pNextLine && ! bFirstOfDouble ) + aLine.NextLine(); + + xub_StrLen nTmpOfst = aLine.GetCrsrOfst( pPam->GetPoint(), + aCharBox.Pos(), sal_False ); + ASSERT( nOldNode == pPam->GetPoint()->nNode.GetIndex(), + "SwTxtFrm::UnitDown: illegal node change" ) + + // 7684: Wir stellen sicher, dass wir uns nach unten bewegen. + if( nTmpOfst <= nStart && ! bFirstOfDouble ) + nTmpOfst = nStart + 1; + pPam->GetPoint()->nContent = + SwIndex( ((SwTxtFrm*)this)->GetTxtNode(), nTmpOfst ); + + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + + return sal_True; + } + if( 0 != ( pTmpFollow = GetFollow() ) ) + { // geschuetzte Follows auslassen + const SwCntntFrm* pTmp = pTmpFollow; + ViewShell *pSh = GetShell(); + if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() ) + { + while( pTmpFollow && pTmpFollow->IsProtected() ) + { + pTmp = pTmpFollow; + pTmpFollow = pTmpFollow->GetFollow(); + } + } + if( !pTmpFollow ) // nur noch geschuetzte + { + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + return pTmp->SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); + } + + aLine.GetCharRect( &aCharBox, nPos ); + aCharBox.SSize().Width() /= 2; + } + else if( !IsFollow() ) + { + xub_StrLen nTmpLen = aInf.GetTxt().Len(); + if( aLine.GetEnd() < nTmpLen ) + { + if( nFormat <= GetOfst() ) + { + nFormat = Min( xub_StrLen( GetOfst() + MIN_OFFSET_STEP ), + nTmpLen ); + if( nFormat <= GetOfst() ) + break; + } + continue; + } + } + break; + } while( sal_True ); + } + else + pTmpFollow = GetFollow(); + + if ( IsVertical() ) + ((SwTxtFrm*)this)->SwapWidthAndHeight(); + + // Bei Follows schlagen wir eine Abkuerzung + if( pTmpFollow ) + { + aCharBox.Pos().Y() = pTmpFollow->Frm().Top() + 1; + return ((SwTxtFrm*)pTmpFollow)->GetKeyCrsrOfst( pPam->GetPoint(), + aCharBox.Pos() ); + } + return SwCntntFrm::UnitDown( pPam, nOffset, bSetInReadOnly ); +} + +/************************************************************************* + * virtual SwTxtFrm::UnitUp() + *************************************************************************/ + +sal_Bool SwTxtFrm::UnitUp(SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + /* Im CrsrSh::Up() wird CntntNode::GetFrm() gerufen. + * Dies liefert _immer_ den Master zurueck. + * Um das Cursortravelling nicht zu belasten, korrigieren wir + * hier im SwTxtFrm. + * Wir ermittelt UnitUp fuer pFrm, pFrm ist entweder ein Master (=this) + * oder ein Follow (!=this) + */ + const SwTxtFrm *pFrm = GetAdjFrmAtPos( (SwTxtFrm*)this, *(pPam->GetPoint()), + SwTxtCursor::IsRightMargin() ); + const sal_Bool bRet = pFrm->_UnitUp( pPam, nOffset, bSetInReadOnly ); + + // 8626: kein SwTxtCursor::SetRightMargin( sal_False ); + // statt dessen steht ein SwSetToRightMargin im _UnitUp + return bRet; +} + +/************************************************************************* + * virtual SwTxtFrm::UnitDown() + *************************************************************************/ + +sal_Bool SwTxtFrm::UnitDown(SwPaM *pPam, const SwTwips nOffset, + sal_Bool bSetInReadOnly ) const +{ + const SwTxtFrm *pFrm = GetAdjFrmAtPos((SwTxtFrm*)this, *(pPam->GetPoint()), + SwTxtCursor::IsRightMargin() ); + const sal_Bool bRet = pFrm->_UnitDown( pPam, nOffset, bSetInReadOnly ); + SwTxtCursor::SetRightMargin( sal_False ); + return bRet; +} + +void SwTxtFrm::FillCrsrPos( SwFillData& rFill ) const +{ + if( !rFill.bColumn && GetUpper()->IsColBodyFrm() ) // ColumnFrms jetzt mit BodyFrm + { + const SwColumnFrm* pTmp = + (SwColumnFrm*)GetUpper()->GetUpper()->GetUpper()->Lower(); // die 1. Spalte + // der erste SwFrm im BodyFrm der ersten Spalte + const SwFrm* pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower(); + MSHORT nNextCol = 0; + // In welcher Spalte landen wir? + while( rFill.X() > pTmp->Frm().Right() && pTmp->GetNext() ) + { + pTmp = (SwColumnFrm*)pTmp->GetNext(); + if( ((SwLayoutFrm*)pTmp->Lower())->Lower() ) // ColumnFrms jetzt mit BodyFrm + { + pFrm = ((SwLayoutFrm*)pTmp->Lower())->Lower(); + nNextCol = 0; + } + else + ++nNextCol; // leere Spalten erfordern Spaltenumbrueche + } + if( pTmp != GetUpper()->GetUpper() ) // Sind wir in einer anderen Spalte gelandet? + { + if( !pFrm ) + return; + if( nNextCol ) + { + while( pFrm->GetNext() ) + pFrm = pFrm->GetNext(); + } + else + { + while( pFrm->GetNext() && pFrm->Frm().Bottom() < rFill.Y() ) + pFrm = pFrm->GetNext(); + } + // Kein Fuellen, wenn als letzter Frame in der anvisierten + // Spalte kein Absatz, sondern z.B. eine Tabelle steht + if( pFrm->IsTxtFrm() ) + { + rFill.Fill().nColumnCnt = nNextCol; + rFill.bColumn = sal_True; + if( rFill.pPos ) + { + SwTxtNode* pTxtNd = ((SwTxtFrm*)pFrm)->GetTxtNode(); + rFill.pPos->nNode = *pTxtNd; + rFill.pPos->nContent.Assign( pTxtNd, pTxtNd->GetTxt().Len() ); + } + if( nNextCol ) + { + rFill.aFrm = pTmp->Prt(); + rFill.aFrm += pTmp->Frm().Pos(); + } + else + rFill.aFrm = pFrm->Frm(); + ((SwTxtFrm*)pFrm)->FillCrsrPos( rFill ); + } + return; + } + } + sal_Bool bFill = sal_True; + SwFont *pFnt; + SwTxtFmtColl* pColl = GetTxtNode()->GetTxtColl(); + MSHORT nFirst = GetTxtNode()->GetSwAttrSet().GetULSpace().GetLower(); + SwTwips nDiff = rFill.Y() - Frm().Bottom(); + if( nDiff < nFirst ) + nDiff = -1; + else + pColl = &pColl->GetNextTxtFmtColl(); + SwAttrSet aSet( ((SwDoc*)GetTxtNode()->GetDoc())->GetAttrPool(), aTxtFmtCollSetRange ); + const SwAttrSet* pSet = &pColl->GetAttrSet(); + ViewShell *pSh = GetShell(); + if( GetTxtNode()->HasSwAttrSet() ) + { + aSet.Put( *GetTxtNode()->GetpSwAttrSet() ); + aSet.SetParent( pSet ); + pSet = &aSet; + pFnt = new SwFont( pSet, GetNode()->getIDocumentSettingAccess() ); + } + else + { + SwFontAccess aFontAccess( pColl, pSh ); + pFnt = new SwFont( *aFontAccess.Get()->GetFont() ); + pFnt->ChkMagic( pSh, pFnt->GetActual() ); + } + OutputDevice* pOut = pSh->GetOut(); + if ( !GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE) || + ( pSh->GetViewOptions()->IsPrtFormat() ) ) + pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true ); + + pFnt->SetFntChg( sal_True ); + pFnt->ChgPhysFnt( pSh, *pOut ); + + SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut ); + + if( nLineHeight ) + { + const SvxULSpaceItem &rUL = pSet->GetULSpace(); + SwTwips nDist = Max( rUL.GetLower(), rUL.GetUpper() ); + if( rFill.Fill().nColumnCnt ) + { + rFill.aFrm.Height( nLineHeight ); + nDiff = rFill.Y() - rFill.Bottom(); + nFirst = 0; + } + else if( nDist < nFirst ) + nFirst = nFirst - (USHORT)nDist; + else + nFirst = 0; + nDist = Max( nDist, long( GetLineSpace() ) ); + nDist += nLineHeight; + nDiff -= nFirst; + + if( nDiff > 0 ) + { + nDiff /= nDist; + rFill.Fill().nParaCnt = static_cast<USHORT>(nDiff + 1); + rFill.nLineWidth = 0; + rFill.bInner = sal_False; + rFill.bEmpty = sal_True; + rFill.SetOrient( text::HoriOrientation::LEFT ); + } + else + nDiff = -1; + if( rFill.bInner ) + bFill = sal_False; + else + { + const SvxTabStopItem &rRuler = pSet->GetTabStops(); + const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace(); + + SwRect &rRect = rFill.Fill().aCrsr; + rRect.Top( rFill.Bottom() + (nDiff+1) * nDist - nLineHeight ); + if( nFirst && nDiff > -1 ) + rRect.Top( rRect.Top() + nFirst ); + rRect.Height( nLineHeight ); + SwTwips nLeft = rFill.Left() + rLRSpace.GetLeft() + + GetTxtNode()->GetLeftMarginWithNum( sal_False ); + SwTwips nRight = rFill.Right() - rLRSpace.GetRight(); + SwTwips nCenter = ( nLeft + nRight ) / 2; + rRect.Left( nLeft ); + if( FILL_MARGIN == rFill.Mode() ) + { + if( rFill.bEmpty ) + { + rFill.SetOrient( text::HoriOrientation::LEFT ); + if( rFill.X() < nCenter ) + { + if( rFill.X() > ( nLeft + 2 * nCenter ) / 3 ) + { + rFill.SetOrient( text::HoriOrientation::CENTER ); + rRect.Left( nCenter ); + } + } + else if( rFill.X() > ( nRight + 2 * nCenter ) / 3 ) + { + rFill.SetOrient( text::HoriOrientation::RIGHT ); + rRect.Left( nRight ); + } + else + { + rFill.SetOrient( text::HoriOrientation::CENTER ); + rRect.Left( nCenter ); + } + } + else + bFill = sal_False; + } + else + { + SwTwips nSpace = 0; + if( FILL_TAB != rFill.Mode() ) + { +static sal_Char __READONLY_DATA sDoubleSpace[] = " "; + const XubString aTmp( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ); + + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTmp, 0, 2 ); + nSpace = pFnt->_GetTxtSize( aDrawInf ).Width()/2; + } + if( rFill.X() >= nRight ) + { + if( FILL_INDENT != rFill.Mode() && ( rFill.bEmpty || + rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) ) + { + rFill.SetOrient( text::HoriOrientation::RIGHT ); + rRect.Left( nRight ); + } + else + bFill = sal_False; + } + else if( FILL_INDENT == rFill.Mode() ) + { + SwTwips nIndent = rFill.X(); + if( !rFill.bEmpty || nIndent > nRight ) + bFill = sal_False; + else + { + nIndent -= rFill.Left(); + if( nIndent >= 0 && nSpace ) + { + nIndent /= nSpace; + nIndent *= nSpace; + rFill.SetTab( MSHORT( nIndent ) ); + rRect.Left( nIndent + rFill.Left() ); + } + else + bFill = sal_False; + } + } + else if( rFill.X() > nLeft ) + { + SwTwips nTxtLeft = rFill.Left() + rLRSpace.GetTxtLeft() + + GetTxtNode()->GetLeftMarginWithNum( sal_True ); + rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTxtLeft; + SwTwips nLeftTab = nLeft; + SwTwips nRightTab = nLeft; + MSHORT nSpaceCnt = 0; + MSHORT nTabCnt = 0; + MSHORT nIdx = 0; + do + { + nLeftTab = nRightTab; + if( nIdx < rRuler.Count() ) + { + const SvxTabStop &rTabStop = rRuler.operator[](nIdx); + nRightTab = nTxtLeft + rTabStop.GetTabPos(); + if( nLeftTab < nTxtLeft && nRightTab > nTxtLeft ) + nRightTab = nTxtLeft; + else + ++nIdx; + if( nRightTab > rFill.nLineWidth ) + ++nTabCnt; + } + else + { + const SvxTabStopItem& rTab = + (const SvxTabStopItem &)pSet-> + GetPool()->GetDefaultItem( RES_PARATR_TABSTOP ); + MSHORT nDefTabDist = (MSHORT)rTab.GetStart()->GetTabPos(); + nRightTab = nLeftTab - nTxtLeft; + nRightTab /= nDefTabDist; + nRightTab = nRightTab * nDefTabDist + nTxtLeft; + while ( nRightTab <= nLeftTab ) + nRightTab += nDefTabDist; + if( nRightTab > rFill.nLineWidth ) + ++nTabCnt; + while ( nRightTab < rFill.X() ) + { + nRightTab += nDefTabDist; + if( nRightTab > rFill.nLineWidth ) + ++nTabCnt; + } + if( nLeftTab < nRightTab - nDefTabDist ) + nLeftTab = nRightTab - nDefTabDist; + } + if( nRightTab > nRight ) + nRightTab = nRight; + } + while( rFill.X() > nRightTab ); + --nTabCnt; + if( FILL_TAB != rFill.Mode() ) + { + if( nSpace > 0 ) + { + if( !nTabCnt ) + nLeftTab = rFill.nLineWidth; + while( nLeftTab < rFill.X() ) + { + nLeftTab += nSpace; + ++nSpaceCnt; + } + if( nSpaceCnt ) + { + nLeftTab -= nSpace; + --nSpaceCnt; + } + if( rFill.X() - nLeftTab > nRightTab - rFill.X() ) + { + nSpaceCnt = 0; + ++nTabCnt; + rRect.Left( nRightTab ); + } + else + { + if( rFill.X() - nLeftTab > nSpace/2 ) + { + ++nSpaceCnt; + rRect.Left( nLeftTab + nSpace ); + } + else + rRect.Left( nLeftTab ); + } + } + else if( rFill.X() - nLeftTab < nRightTab - rFill.X() ) + rRect.Left( nLeftTab ); + else + { + if( nRightTab >= nRight ) + { + rFill.SetOrient( text::HoriOrientation::RIGHT ); + rRect.Left( nRight ); + nTabCnt = 0; + nSpaceCnt = 0; + } + else + { + rRect.Left( nRightTab ); + ++nTabCnt; + } + } + } + else + { + if( rFill.X() - nLeftTab < nRightTab - rFill.X() ) + rRect.Left( nLeftTab ); + else + { + if( nRightTab >= nRight ) + { + rFill.SetOrient( text::HoriOrientation::RIGHT ); + rRect.Left( nRight ); + nTabCnt = 0; + nSpaceCnt = 0; + } + else + { + rRect.Left( nRightTab ); + ++nTabCnt; + } + } + } + rFill.SetTab( nTabCnt ); + rFill.SetSpace( nSpaceCnt ); + if( bFill ) + { + if( Abs( rFill.X() - nCenter ) <= + Abs( rFill.X() - rRect.Left() ) ) + { + rFill.SetOrient( text::HoriOrientation::CENTER ); + rFill.SetTab( 0 ); + rFill.SetSpace( 0 ); + rRect.Left( nCenter ); + } + if( !rFill.bEmpty ) + rFill.nLineWidth += FILL_MIN_DIST; + if( rRect.Left() < rFill.nLineWidth ) + bFill = sal_False; + } + } + } + // Gehen wir ueber die Unterkante der Seite/Spalte etc. hinaus? + const SwFrm* pUp = GetUpper(); + if( pUp->IsInSct() ) + { + if( pUp->IsSctFrm() ) + pUp = pUp->GetUpper(); + else if( pUp->IsColBodyFrm() && + pUp->GetUpper()->GetUpper()->IsSctFrm() ) + pUp = pUp->GetUpper()->GetUpper()->GetUpper(); + } + SWRECTFN( this ) + SwTwips nLimit = (pUp->*fnRect->fnGetPrtBottom)(); + SwTwips nRectBottom = rRect.Bottom(); + if ( bVert ) + nRectBottom = SwitchHorizontalToVertical( nRectBottom ); + + if( (*fnRect->fnYDiff)( nLimit, nRectBottom ) < 0 ) + bFill = sal_False; + else + rRect.Width( 1 ); + } + } + else + bFill = sal_False; + ((SwCrsrMoveState*)rFill.pCMS)->bFillRet = bFill; + delete pFnt; +} diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx new file mode 100644 index 000000000000..b5a3dff475ae --- /dev/null +++ b/sw/source/core/text/frmform.cxx @@ -0,0 +1,2176 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: frmform.cxx,v $ + * $Revision: 1.71 $ + * + * 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 <svx/keepitem.hxx> +#include <svx/hyznitem.hxx> +#include <pagefrm.hxx> // ChangeFtnRef +#include <ndtxt.hxx> // MakeFrm() +#include <dcontact.hxx> // SwDrawContact +#include <dflyobj.hxx> // SwVirtFlyDrawObj +#include <flyfrm.hxx> +#include <ftnfrm.hxx> // SwFtnFrm +#include <txtftn.hxx> +#include <fmtftn.hxx> +#include <paratr.hxx> +#include <viewopt.hxx> // SwViewOptions +#include <viewsh.hxx> // ViewShell +#include <frmatr.hxx> +#include <pam.hxx> +#include <flyfrms.hxx> +#include <fmtanchr.hxx> +#include <txtcfg.hxx> +#include <itrform2.hxx> // SwTxtFormatter +#include <widorp.hxx> // Widows and Orphans +#include <txtcache.hxx> +#include <porrst.hxx> // SwEmptyPortion +#include <blink.hxx> // pBlink +#include <porfld.hxx> // SwFldPortion +#include <sectfrm.hxx> // SwSectionFrm +#include <pormulti.hxx> // SwMultiPortion + +#include <rootfrm.hxx> +#include <frmfmt.hxx> // SwFrmFmt +// OD 2004-05-24 #i28701# +#include <sortedobjs.hxx> + +class FormatLevel +{ + static MSHORT nLevel; +public: + inline FormatLevel() { ++nLevel; } + inline ~FormatLevel() { --nLevel; } + inline MSHORT GetLevel() const { return nLevel; } + static sal_Bool LastLevel() { return 10 < nLevel; } +}; +MSHORT FormatLevel::nLevel = 0; + +/************************************************************************* + * ValidateTxt/Frm() + *************************************************************************/ + +void ValidateTxt( SwFrm *pFrm ) // Freund vom Frame +{ + if ( ( ! pFrm->IsVertical() && + pFrm->Frm().Width() == pFrm->GetUpper()->Prt().Width() ) || + pFrm->IsVertical() && + pFrm->Frm().Height() == pFrm->GetUpper()->Prt().Height() ) + pFrm->bValidSize = sal_True; +/* + pFrm->bValidPrtArea = sal_True; + //Die Position validieren um nicht unnoetige (Test-)Moves zu provozieren. + //Dabei darf allerdings nicht eine tatsaechlich falsche Coordinate + //validiert werden. + if ( !pFrm->bValidPos ) + { + //Leider muessen wir dazu die korrekte Position berechnen. + Point aOld( pFrm->Frm().Pos() ); + pFrm->MakePos(); + if ( aOld != pFrm->Pos() ) + { + pFrm->Frm().Pos( aOld ); + pFrm->bValidPos = sal_False; + } + } +*/ +} + +void SwTxtFrm::ValidateFrm() +{ + // Umgebung validieren, um Oszillationen zu verhindern. + SWAP_IF_SWAPPED( this ) + + if ( !IsInFly() && !IsInTab() ) + { //Innerhalb eines Flys nur this validieren, der Rest sollte eigentlich + //nur fuer Fussnoten notwendig sein und die gibt es innerhalb von + //Flys nicht. Fix fuer 5544 + SwSectionFrm* pSct = FindSctFrm(); + if( pSct ) + { + if( !pSct->IsColLocked() ) + pSct->ColLock(); + else + pSct = NULL; + } + + SwFrm *pUp = GetUpper(); + pUp->Calc(); + if( pSct ) + pSct->ColUnlock(); + } + ValidateTxt( this ); + + //MA: mindestens das MustFit-Flag muessen wir retten! + ASSERT( HasPara(), "ResetPreps(), missing ParaPortion." ); + SwParaPortion *pPara = GetPara(); + const sal_Bool bMustFit = pPara->IsPrepMustFit(); + ResetPreps(); + pPara->SetPrepMustFit( bMustFit ); + + UNDO_SWAP( this ) +} + +/************************************************************************* + * ValidateBodyFrm() + *************************************************************************/ + +// nach einem RemoveFtn muss der BodyFrm und alle innenliegenden kalkuliert +// werden, damit die DeadLine richtig sitzt. +// Erst wird nach aussen hin gesucht, beim Rueckweg werden alle kalkuliert. + +void _ValidateBodyFrm( SwFrm *pFrm ) +{ + if( pFrm && !pFrm->IsCellFrm() ) + { + if( !pFrm->IsBodyFrm() && pFrm->GetUpper() ) + _ValidateBodyFrm( pFrm->GetUpper() ); + if( !pFrm->IsSctFrm() ) + pFrm->Calc(); + else + { + sal_Bool bOld = ((SwSectionFrm*)pFrm)->IsCntntLocked(); + ((SwSectionFrm*)pFrm)->SetCntntLock( sal_True ); + pFrm->Calc(); + if( !bOld ) + ((SwSectionFrm*)pFrm)->SetCntntLock( sal_False ); + } + } +} + +void SwTxtFrm::ValidateBodyFrm() +{ + SWAP_IF_SWAPPED( this ) + + //siehe Kommtar in ValidateFrm() + if ( !IsInFly() && !IsInTab() && + !( IsInSct() && FindSctFrm()->Lower()->IsColumnFrm() ) ) + _ValidateBodyFrm( GetUpper() ); + + UNDO_SWAP( this ) +} + +/************************************************************************* + * SwTxtFrm::FindBodyFrm() + *************************************************************************/ + +sal_Bool SwTxtFrm::_GetDropRect( SwRect &rRect ) const +{ + SWAP_IF_NOT_SWAPPED( this ) + + ASSERT( HasPara(), "SwTxtFrm::_GetDropRect: try again next year." ); + SwTxtSizeInfo aInf( (SwTxtFrm*)this ); + SwTxtMargin aLine( (SwTxtFrm*)this, &aInf ); + if( aLine.GetDropLines() ) + { + rRect.Top( aLine.Y() ); + rRect.Left( aLine.GetLineStart() ); + rRect.Height( aLine.GetDropHeight() ); + rRect.Width( aLine.GetDropLeft() ); + + if ( IsRightToLeft() ) + SwitchLTRtoRTL( rRect ); + + if ( IsVertical() ) + SwitchHorizontalToVertical( rRect ); + UNDO_SWAP( this ) + return sal_True; + } + + UNDO_SWAP( this ) + + return sal_False; +} + +/************************************************************************* + * SwTxtFrm::FindBodyFrm() + *************************************************************************/ + +const SwBodyFrm *SwTxtFrm::FindBodyFrm() const +{ + if ( IsInDocBody() ) + { + const SwFrm *pFrm = GetUpper(); + while( pFrm && !pFrm->IsBodyFrm() ) + pFrm = pFrm->GetUpper(); + return (const SwBodyFrm*)pFrm; + } + return 0; +} + +/************************************************************************* + * SwTxtFrm::CalcFollow() + *************************************************************************/ + +sal_Bool SwTxtFrm::CalcFollow( const xub_StrLen nTxtOfst ) +{ + SWAP_IF_SWAPPED( this ) + + ASSERT( HasFollow(), "CalcFollow: missing Follow." ); + + SwTxtFrm* pMyFollow = GetFollow(); + + SwParaPortion *pPara = GetPara(); + sal_Bool bFollowFld = pPara ? pPara->IsFollowField() : sal_False; + + if( !pMyFollow->GetOfst() || pMyFollow->GetOfst() != nTxtOfst || + bFollowFld || pMyFollow->IsFieldFollow() || + ( pMyFollow->IsVertical() && !pMyFollow->Prt().Width() ) || + ( ! pMyFollow->IsVertical() && !pMyFollow->Prt().Height() ) ) + { +#ifndef PRODUCT + const SwFrm *pOldUp = GetUpper(); +#endif + + SWRECTFN ( this ) + SwTwips nOldBottom = (GetUpper()->Frm().*fnRect->fnGetBottom)(); + SwTwips nMyPos = (Frm().*fnRect->fnGetTop)(); + + const SwPageFrm *pPage = 0; + sal_Bool bOldInvaCntnt = sal_True; + if ( !IsInFly() && GetNext() ) + { + pPage = FindPageFrm(); + //Minimieren - sprich ggf. zuruecksetzen - der Invalidierungen s.u. + bOldInvaCntnt = pPage->IsInvalidCntnt(); + } + + pMyFollow->_SetOfst( nTxtOfst ); + pMyFollow->SetFieldFollow( bFollowFld ); + if( HasFtn() || pMyFollow->HasFtn() ) + { + ValidateFrm(); + ValidateBodyFrm(); + if( pPara ) + { + *(pPara->GetReformat()) = SwCharRange(); + *(pPara->GetDelta()) = 0; + } + } + + //Der Fussnotenbereich darf sich keinesfalls vergrossern. + SwSaveFtnHeight aSave( FindFtnBossFrm( sal_True ), LONG_MAX ); + + pMyFollow->CalcFtnFlag(); + if ( !pMyFollow->GetNext() && !pMyFollow->HasFtn() ) + nOldBottom = bVert ? 0 : LONG_MAX; + + while( sal_True ) + { + if( !FormatLevel::LastLevel() ) + { + // Weenn der Follow in einem spaltigen Bereich oder einem + // spaltigen Rahmen steckt, muss zunaechst dieser kalkuliert + // werden, da das FormatWidthCols() nicht funktioniert, wenn + // es aus dem MakeAll des _gelockten_ Follows heraus gerufen + // wird. + SwSectionFrm* pSct = pMyFollow->FindSctFrm(); + if( pSct && !pSct->IsAnLower( this ) ) + { + if( pSct->GetFollow() ) + pSct->SimpleFormat(); + else if( ( pSct->IsVertical() && !pSct->Frm().Width() ) || + ( ! pSct->IsVertical() && !pSct->Frm().Height() ) ) + break; + } + // OD 14.03.2003 #i11760# - intrinsic format of follow is controlled. + if ( FollowFormatAllowed() ) + { + // OD 14.03.2003 #i11760# - no nested format of follows, if + // text frame is contained in a column frame. + // Thus, forbid intrinsic format of follow. + { + bool bIsFollowInColumn = false; + SwFrm* pFollowUpper = pMyFollow->GetUpper(); + while ( pFollowUpper ) + { + if ( pFollowUpper->IsColumnFrm() ) + { + bIsFollowInColumn = true; + break; + } + if ( pFollowUpper->IsPageFrm() || + pFollowUpper->IsFlyFrm() ) + { + break; + } + pFollowUpper = pFollowUpper->GetUpper(); + } + if ( bIsFollowInColumn ) + { + pMyFollow->ForbidFollowFormat(); + } + } + + pMyFollow->Calc(); + // Der Follow merkt anhand seiner Frm().Height(), dass was schief + // gelaufen ist. + ASSERT( !pMyFollow->GetPrev(), "SwTxtFrm::CalcFollow: cheesy follow" ); + if( pMyFollow->GetPrev() ) + { + pMyFollow->Prepare( PREP_CLEAR ); + pMyFollow->Calc(); + ASSERT( !pMyFollow->GetPrev(), "SwTxtFrm::CalcFollow: very cheesy follow" ); + } + + // OD 14.03.2003 #i11760# - reset control flag for follow format. + pMyFollow->AllowFollowFormat(); + } + + //Sicherstellen, dass der Follow gepaintet wird. + pMyFollow->SetCompletePaint(); + } + + pPara = GetPara(); + //Solange der Follow wg. Orphans Zeilen angefordert, bekommt er + //diese und wird erneut formatiert, falls moeglich. + if( pPara && pPara->IsPrepWidows() ) + CalcPreps(); + else + break; + } + + if( HasFtn() || pMyFollow->HasFtn() ) + { + ValidateBodyFrm(); + ValidateFrm(); + if( pPara ) + { + *(pPara->GetReformat()) = SwCharRange(); + *(pPara->GetDelta()) = 0; + } + } + + if ( pPage ) + { + if ( !bOldInvaCntnt ) + pPage->ValidateCntnt(); + } + +#ifndef PRODUCT + ASSERT( pOldUp == GetUpper(), "SwTxtFrm::CalcFollow: heavy follow" ); +#endif + + const long nRemaining = + - (GetUpper()->Frm().*fnRect->fnBottomDist)( nOldBottom ); + if ( nRemaining > 0 && !GetUpper()->IsSctFrm() && + nRemaining != ( bVert ? + nMyPos - Frm().Right() : + Frm().Top() - nMyPos ) ) + { + UNDO_SWAP( this ) + return sal_True; + } + } + + UNDO_SWAP( this ) + + return sal_False; +} + +/************************************************************************* + * SwTxtFrm::AdjustFrm() + *************************************************************************/ + +void SwTxtFrm::AdjustFrm( const SwTwips nChgHght, sal_Bool bHasToFit ) +{ + if( IsUndersized() ) + { + if( GetOfst() && !IsFollow() ) // ein gescrollter Absatz (undersized) + return; + SetUndersized( nChgHght == 0 || bHasToFit ); + } + + // AdjustFrm is called with a swapped frame during + // formatting but the frame is not swapped during FormatEmpty + SWAP_IF_SWAPPED( this ) + SWRECTFN ( this ) + + // Die Size-Variable des Frames wird durch Grow inkrementiert + // oder durch Shrink dekrementiert. Wenn die Groesse + // unveraendert ist, soll nichts passieren! + if( nChgHght >= 0) + { + SwTwips nChgHeight = nChgHght; + if( nChgHght && !bHasToFit ) + { + if( IsInFtn() && !IsInSct() ) + { + SwTwips nReal = Grow( nChgHght, sal_True ); + if( nReal < nChgHght ) + { + SwTwips nBot = (*fnRect->fnYInc)( (Frm().*fnRect->fnGetBottom)(), + nChgHght - nReal ); + SwFrm* pCont = FindFtnFrm()->GetUpper(); + + if( (pCont->Frm().*fnRect->fnBottomDist)( nBot ) > 0 ) + { + (Frm().*fnRect->fnAddBottom)( nChgHght ); + if( bVert ) + Prt().SSize().Width() += nChgHght; + else + Prt().SSize().Height() += nChgHght; + UNDO_SWAP( this ) + return; + } + } + } + + Grow( nChgHght ); + + if ( IsInFly() ) + { + //MA 06. May. 93: Wenn einer der Upper ein Fly ist, so ist es + //sehr wahrscheinlich, dass dieser Fly durch das Grow seine + //Position veraendert - also muss auch meine Position korrigiert + //werden (sonst ist die Pruefung s.u. nicht aussagekraeftig). + //Die Vorgaenger muessen berechnet werden, damit die Position + //korrekt berechnet werden kann. + if ( GetPrev() ) + { + SwFrm *pPre = GetUpper()->Lower(); + do + { pPre->Calc(); + pPre = pPre->GetNext(); + } while ( pPre && pPre != this ); + } + const Point aOldPos( Frm().Pos() ); + MakePos(); + if ( aOldPos != Frm().Pos() ) + { + // OD 2004-07-01 #i28701# - use new method <SwFrm::InvalidateObjs(..)> + // No format is performed for the floating screen objects. + InvalidateObjs( true ); + } + } + nChgHeight = 0; + } + // Ein Grow() wird von der Layout-Seite immer akzeptiert, + // also auch, wenn die FixSize des umgebenden Layoutframes + // dies nicht zulassen sollte. Wir ueberpruefen diesen + // Fall und korrigieren die Werte. + // MA 06. May. 93: Der Frm darf allerdings auch im Notfall nicht + // weiter geschrumpft werden als es seine Groesse zulaesst. + SwTwips nRstHeight; + if ( IsVertical() ) + { + ASSERT( ! IsSwapped(),"Swapped frame while calculating nRstHeight" ); + nRstHeight = Frm().Left() + Frm().Width() - + ( GetUpper()->Frm().Left() + GetUpper()->Prt().Left() ); + } + else + nRstHeight = GetUpper()->Frm().Top() + + GetUpper()->Prt().Top() + + GetUpper()->Prt().Height() + - Frm().Top(); + + //In Tabellenzellen kann ich mir evtl. noch ein wenig dazuholen, weil + //durch eine vertikale Ausrichtung auch oben noch Raum sein kann. + // --> OD 2004-11-25 #115759# - assure, that first lower in upper + // is the current one or is valid. + if ( IsInTab() && + ( GetUpper()->Lower() == this || + GetUpper()->Lower()->IsValid() ) ) + // <-- + { + long nAdd = (*fnRect->fnYDiff)( (GetUpper()->Lower()->Frm().*fnRect->fnGetTop)(), + (GetUpper()->*fnRect->fnGetPrtTop)() ); + ASSERT( nAdd >= 0, "Ey" ); + nRstHeight += nAdd; + } + +/* ------------------------------------ + * #50964#: nRstHeight < 0 bedeutet, dass der TxtFrm komplett ausserhalb seines + * Upper liegt. Dies kann passieren, wenn er innerhalb eines FlyAtCntFrm liegt, der + * durch das Grow() die Seite gewechselt hat. In so einem Fall ist es falsch, der + * folgenden Grow-Versuch durchzufuehren. Im Bugfall fuehrte dies sogar zur + * Endlosschleife. + * -----------------------------------*/ + SwTwips nFrmHeight = (Frm().*fnRect->fnGetHeight)(); + SwTwips nPrtHeight = (Prt().*fnRect->fnGetHeight)(); + + if( nRstHeight < nFrmHeight ) + { + //Kann sein, dass ich die richtige Grosse habe, der Upper aber zu + //klein ist und der Upper noch Platz schaffen kann. + if( ( nRstHeight >= 0 || ( IsInFtn() && IsInSct() ) ) && !bHasToFit ) + nRstHeight += GetUpper()->Grow( nFrmHeight - nRstHeight ); + // In spaltigen Bereichen wollen wir moeglichst nicht zu gross werden, damit + // nicht ueber GetNextSctLeaf weitere Bereiche angelegt werden. Stattdessen + // schrumpfen wir und notieren bUndersized, damit FormatWidthCols die richtige + // Spaltengroesse ermitteln kann. + if ( nRstHeight < nFrmHeight ) + { + if( bHasToFit || !IsMoveable() || + ( IsInSct() && !FindSctFrm()->MoveAllowed(this) ) ) + { + SetUndersized( sal_True ); + Shrink( Min( ( nFrmHeight - nRstHeight), nPrtHeight ) ); + } + else + SetUndersized( sal_False ); + } + } + else if( nChgHeight ) + { + if( nRstHeight - nFrmHeight < nChgHeight ) + nChgHeight = nRstHeight - nFrmHeight; + if( nChgHeight ) + Grow( nChgHeight ); + } + } + else + Shrink( -nChgHght ); + + UNDO_SWAP( this ) +} + +/************************************************************************* + * SwTxtFrm::AdjustFollow() + *************************************************************************/ + +/* AdjustFollow erwartet folgende Situation: + * Der SwTxtIter steht am unteren Ende des Masters, der Offset wird + * im Follow eingestellt. + * nOffset haelt den Offset im Textstring, ab dem der Master abschliesst + * und der Follow beginnt. Wenn er 0 ist, wird der FolgeFrame geloescht. + */ + +void SwTxtFrm::_AdjustFollow( SwTxtFormatter &rLine, + const xub_StrLen nOffset, const xub_StrLen nEnd, + const sal_uInt8 nMode ) +{ + SwFrmSwapper aSwapper( this, sal_False ); + + // Wir haben den Rest der Textmasse: alle Follows loeschen + // Sonderfall sind DummyPortions() + // - special cases are controlled by parameter <nMode>. + if( HasFollow() && !(nMode & 1) && nOffset == nEnd ) + { + while( GetFollow() ) + { + if( ((SwTxtFrm*)GetFollow())->IsLocked() ) + { + ASSERT( sal_False, "+SwTxtFrm::JoinFrm: Follow ist locked." ); + return; + } + JoinFrm(); + } + + return; + } + + // Tanz auf dem Vulkan: Wir formatieren eben schnell noch einmal + // die letzte Zeile fuer das QuoVadis-Geraffel. Selbstverstaendlich + // kann sich dadurch auch der Offset verschieben: + const xub_StrLen nNewOfst = ( IsInFtn() && ( !GetIndNext() || HasFollow() ) ) ? + rLine.FormatQuoVadis(nOffset) : nOffset; + + if( !(nMode & 1) ) + { + // Wir klauen unseren Follows Textmasse, dabei kann es passieren, + // dass wir einige Follows Joinen muessen. + while( GetFollow() && GetFollow()->GetFollow() && + nNewOfst >= GetFollow()->GetFollow()->GetOfst() ) + { + DBG_LOOP; + JoinFrm(); + } + } + + // Der Ofst hat sich verschoben. + if( GetFollow() ) + { +#if OSL_DEBUG_LEVEL > 1 + static sal_Bool bTest = sal_False; + if( !bTest || ( nMode & 1 ) ) +#endif + if ( nMode ) + GetFollow()->ManipOfst( 0 ); + + if ( CalcFollow( nNewOfst ) ) // CalcFollow erst zum Schluss, dort erfolgt ein SetOfst + rLine.SetOnceMore( sal_True ); + } +} + +/************************************************************************* + * SwTxtFrm::JoinFrm() + *************************************************************************/ + +SwCntntFrm *SwTxtFrm::JoinFrm() +{ + ASSERT( GetFollow(), "+SwTxtFrm::JoinFrm: no follow" ); + SwTxtFrm *pFoll = GetFollow(); + + SwTxtFrm *pNxt = pFoll->GetFollow(); + + // Alle Fussnoten des zu zerstoerenden Follows werden auf uns + // umgehaengt. + xub_StrLen nStart = pFoll->GetOfst(); + if ( pFoll->HasFtn() ) + { + const SwpHints *pHints = pFoll->GetTxtNode()->GetpSwpHints(); + if( pHints ) + { + SwFtnBossFrm *pFtnBoss = 0; + SwFtnBossFrm *pEndBoss = 0; + for ( USHORT i = 0; i < pHints->Count(); ++i ) + { + const SwTxtAttr *pHt = (*pHints)[i]; + if( RES_TXTATR_FTN==pHt->Which() && *pHt->GetStart()>=nStart ) + { + if( pHt->GetFtn().IsEndNote() ) + { + if( !pEndBoss ) + pEndBoss = pFoll->FindFtnBossFrm(); + pEndBoss->ChangeFtnRef( pFoll, (SwTxtFtn*)pHt, this ); + } + else + { + if( !pFtnBoss ) + pFtnBoss = pFoll->FindFtnBossFrm( sal_True ); + pFtnBoss->ChangeFtnRef( pFoll, (SwTxtFtn*)pHt, this ); + } + SetFtn( sal_True ); + } + } + } + } + +#ifndef PRODUCT + else if ( pFoll->GetValidPrtAreaFlag() || + pFoll->GetValidSizeFlag() ) + { + pFoll->CalcFtnFlag(); + ASSERT( !pFoll->HasFtn(), "Missing FtnFlag." ); + } +#endif + + pFoll->MoveFlyInCnt( this, nStart, STRING_LEN ); + pFoll->SetFtn( FALSE ); + // --> OD 2005-12-01 #i27138# + // notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation. + // Relation CONTENT_FLOWS_FROM for current next paragraph will change + // and relation CONTENT_FLOWS_TO for current previous paragraph, which + // is <this>, will change. + { + ViewShell* pViewShell( pFoll->GetShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTxtFrm*>(pFoll->FindNextCnt( true )), + this ); + } + } + // <-- + pFoll->Cut(); + delete pFoll; + pFollow = pNxt; + return pNxt; +} + +/************************************************************************* + * SwTxtFrm::SplitFrm() + *************************************************************************/ + +SwCntntFrm *SwTxtFrm::SplitFrm( const xub_StrLen nTxtPos ) +{ + SWAP_IF_SWAPPED( this ) + + // Durch das Paste wird ein Modify() an mich verschickt. + // Damit meine Daten nicht verschwinden, locke ich mich. + SwTxtFrmLocker aLock( this ); + SwTxtFrm *pNew = (SwTxtFrm *)(GetTxtNode()->MakeFrm()); + pNew->bIsFollow = sal_True; + + pNew->SetFollow( GetFollow() ); + SetFollow( pNew ); + + pNew->Paste( GetUpper(), GetNext() ); + // --> OD 2005-12-01 #i27138# + // notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation. + // Relation CONTENT_FLOWS_FROM for current next paragraph will change + // and relation CONTENT_FLOWS_TO for current previous paragraph, which + // is <this>, will change. + { + ViewShell* pViewShell( pNew->GetShell() ); + if ( pViewShell && pViewShell->GetLayout() && + pViewShell->GetLayout()->IsAnyShellAccessible() ) + { + pViewShell->InvalidateAccessibleParaFlowRelation( + dynamic_cast<SwTxtFrm*>(pNew->FindNextCnt( true )), + this ); + } + } + // <-- + + // Wenn durch unsere Aktionen Fussnoten in pNew landen, + // so muessen sie umgemeldet werden. + if ( HasFtn() ) + { + const SwpHints *pHints = GetTxtNode()->GetpSwpHints(); + if( pHints ) + { + SwFtnBossFrm *pFtnBoss = 0; + SwFtnBossFrm *pEndBoss = 0; + for ( USHORT i = 0; i < pHints->Count(); ++i ) + { + const SwTxtAttr *pHt = (*pHints)[i]; + if( RES_TXTATR_FTN==pHt->Which() && *pHt->GetStart()>=nTxtPos ) + { + if( pHt->GetFtn().IsEndNote() ) + { + if( !pEndBoss ) + pEndBoss = FindFtnBossFrm(); + pEndBoss->ChangeFtnRef( this, (SwTxtFtn*)pHt, pNew ); + } + else + { + if( !pFtnBoss ) + pFtnBoss = FindFtnBossFrm( sal_True ); + pFtnBoss->ChangeFtnRef( this, (SwTxtFtn*)pHt, pNew ); + } + pNew->SetFtn( sal_True ); + } + } + } + } + +#ifndef PRODUCT + else + { + CalcFtnFlag( nTxtPos-1 ); + ASSERT( !HasFtn(), "Missing FtnFlag." ); + } +#endif + + MoveFlyInCnt( pNew, nTxtPos, STRING_LEN ); + + // Kein SetOfst oder CalcFollow, weil gleich ohnehin ein AdjustFollow folgt. + + pNew->ManipOfst( nTxtPos ); + + UNDO_SWAP( this ) + return pNew; +} + + +/************************************************************************* + * virtual SwTxtFrm::SetOfst() + *************************************************************************/ + +void SwTxtFrm::_SetOfst( const xub_StrLen nNewOfst ) +{ +#ifdef DBGTXT + // Es gibt tatsaechlich einen Sonderfall, in dem ein SetOfst(0) + // zulaessig ist: bug 3496 + ASSERT( nNewOfst, "!SwTxtFrm::SetOfst: missing JoinFrm()." ); +#endif + + // Die Invalidierung unseres Follows ist nicht noetig. + // Wir sind ein Follow, werden gleich formatiert und + // rufen von dort aus das SetOfst() ! + nOfst = nNewOfst; + SwParaPortion *pPara = GetPara(); + if( pPara ) + { + SwCharRange &rReformat = *(pPara->GetReformat()); + rReformat.Start() = 0; + rReformat.Len() = GetTxt().Len(); + *(pPara->GetDelta()) = rReformat.Len(); + } + InvalidateSize(); +} + +/************************************************************************* + * SwTxtFrm::CalcPreps + *************************************************************************/ + +sal_Bool SwTxtFrm::CalcPreps() +{ + ASSERT( ! IsVertical() || ! IsSwapped(), "SwTxtFrm::CalcPreps with swapped frame" ); + SWRECTFN( this ); + + SwParaPortion *pPara = GetPara(); + if ( !pPara ) + return sal_False; + sal_Bool bPrep = pPara->IsPrep(); + sal_Bool bPrepWidows = pPara->IsPrepWidows(); + sal_Bool bPrepAdjust = pPara->IsPrepAdjust(); + sal_Bool bPrepMustFit = pPara->IsPrepMustFit(); + ResetPreps(); + + sal_Bool bRet = sal_False; + if( bPrep && !pPara->GetReformat()->Len() ) + { + // PREP_WIDOWS bedeutet, dass im Follow die Orphans-Regel + // zuschlug. + // Es kann in unguenstigen Faellen vorkommen, dass auch ein + // PrepAdjust vorliegt (3680)! + if( bPrepWidows ) + { + if( !GetFollow() ) + { + ASSERT( GetFollow(), "+SwTxtFrm::CalcPreps: no credits" ); + return sal_False; + } + + // Wir muessen uns auf zwei Faelle einstellen: + // Wir konnten dem Follow noch ein paar Zeilen abgeben, + // -> dann muessen wir schrumpfen + // oder wir muessen auf die naechste Seite + // -> dann lassen wir unseren Frame zu gross werden. + + SwTwips nChgHeight = GetParHeight(); + if( nChgHeight >= (Prt().*fnRect->fnGetHeight)() ) + { + if( bPrepMustFit ) + { + GetFollow()->SetJustWidow( sal_True ); + GetFollow()->Prepare( PREP_CLEAR ); + } + else if ( bVert ) + { + Frm().Width( Frm().Width() + Frm().Left() ); + Prt().Width( Prt().Width() + Frm().Left() ); + Frm().Left( 0 ); + SetWidow( sal_True ); + } + else + { + SwTwips nTmp = LONG_MAX - (Frm().Top()+10000); + SwTwips nDiff = nTmp - Frm().Height(); + Frm().Height( nTmp ); + Prt().Height( Prt().Height() + nDiff ); + SetWidow( sal_True ); + } + } + else + { + ASSERT( nChgHeight < (Prt().*fnRect->fnGetHeight)(), + "+SwTxtFrm::CalcPrep: wanna shrink" ); + + nChgHeight = (Prt().*fnRect->fnGetHeight)() - nChgHeight; + + GetFollow()->SetJustWidow( sal_True ); + GetFollow()->Prepare( PREP_CLEAR ); + Shrink( nChgHeight ); + SwRect &rRepaint = *(pPara->GetRepaint()); + + if ( bVert ) + { + SwRect aRepaint( Frm().Pos() + Prt().Pos(), Prt().SSize() ); + SwitchVerticalToHorizontal( aRepaint ); + rRepaint.Chg( aRepaint.Pos(), aRepaint.SSize() ); + } + else + rRepaint.Chg( Frm().Pos() + Prt().Pos(), Prt().SSize() ); + + // 6792: Rrand < LRand und Repaint + if( 0 >= rRepaint.Width() ) + rRepaint.Width(1); + } + bRet = sal_True; + } + + else if ( bPrepAdjust ) + { + if ( HasFtn() ) + { + if( !CalcPrepFtnAdjust() ) + { + if( bPrepMustFit ) + { + SwTxtLineAccess aAccess( this ); + aAccess.GetPara()->SetPrepMustFit( sal_True ); + } + return sal_False; + } + } + + SWAP_IF_NOT_SWAPPED( this ) + + SwTxtFormatInfo aInf( this ); + SwTxtFormatter aLine( this, &aInf ); + + WidowsAndOrphans aFrmBreak( this ); + // Egal was die Attribute meinen, bei MustFit wird + // der Absatz im Notfall trotzdem gesplittet... + if( bPrepMustFit ) + { + aFrmBreak.SetKeep( sal_False ); + aFrmBreak.ClrOrphLines(); + } + // Bevor wir FormatAdjust aufrufen muessen wir dafuer + // sorgen, dass die Zeilen, die unten raushaengen + // auch tatsaechlich abgeschnitten werden. + // OD 2004-02-25 #i16128# - method renamed + sal_Bool bBreak = aFrmBreak.IsBreakNowWidAndOrp( aLine ); + bRet = sal_True; + while( !bBreak && aLine.Next() ) + { + // OD 2004-02-25 #i16128# - method renamed + bBreak = aFrmBreak.IsBreakNowWidAndOrp( aLine ); + } + if( bBreak ) + { + // Es gibt Komplikationen: wenn TruncLines gerufen wird, + // veraendern sich ploetzlich die Bedingungen in + // IsInside, so dass IsBreakNow andere Ergebnisse + // liefern kann. Aus diesem Grund wird rFrmBreak bekannt + // gegeben, dass da wo rLine steht, das Ende erreicht + // ist. Mal sehen, ob's klappt ... + aLine.TruncLines(); + aFrmBreak.SetRstHeight( aLine ); + FormatAdjust( aLine, aFrmBreak, aInf.GetTxt().Len(), aInf.IsStop() ); + } + else + { + if( !GetFollow() ) + { + FormatAdjust( aLine, aFrmBreak, + aInf.GetTxt().Len(), aInf.IsStop() ); + } + else if ( !aFrmBreak.IsKeepAlways() ) + { + // Siehe Bug: 2320 + // Vor dem Master wird eine Zeile geloescht, der Follow + // koennte eine Zeile abgeben. + const SwCharRange aFollowRg( GetFollow()->GetOfst(), 1 ); + *(pPara->GetReformat()) += aFollowRg; + // Es soll weitergehen! + bRet = sal_False; + } + } + + UNDO_SWAP( this ) + // Eine letzte Ueberpruefung, falls das FormatAdjust() nichts + // brachte, muessen wir amputieren. + if( bPrepMustFit ) + { + const SwTwips nMust = (GetUpper()->*fnRect->fnGetPrtBottom)(); + const SwTwips nIs = (Frm().*fnRect->fnGetBottom)(); + + if( bVert && nIs < nMust ) + { + Shrink( nMust - nIs ); + if( Prt().Width() < 0 ) + Prt().Width( 0 ); + SetUndersized( sal_True ); + } + else if ( ! bVert && nIs > nMust ) + { + Shrink( nIs - nMust ); + if( Prt().Height() < 0 ) + Prt().Height( 0 ); + SetUndersized( sal_True ); + } + } + } + } + pPara->SetPrepMustFit( bPrepMustFit ); + return bRet; +} + + +/************************************************************************* + * SwTxtFrm::FormatAdjust() + *************************************************************************/ + +// Hier werden die Fussnoten und "als Zeichen"-gebundenen Objekte umgehaengt +#define CHG_OFFSET( pFrm, nNew )\ + {\ + if( pFrm->GetOfst() < nNew )\ + pFrm->MoveFlyInCnt( this, 0, nNew );\ + else if( pFrm->GetOfst() > nNew )\ + MoveFlyInCnt( pFrm, nNew, STRING_LEN );\ + } + +void SwTxtFrm::FormatAdjust( SwTxtFormatter &rLine, + WidowsAndOrphans &rFrmBreak, + const xub_StrLen nStrLen, + const sal_Bool bDummy ) +{ + SWAP_IF_NOT_SWAPPED( this ) + + SwParaPortion *pPara = rLine.GetInfo().GetParaPortion(); + + xub_StrLen nEnd = rLine.GetStart(); + + sal_Bool bHasToFit = pPara->IsPrepMustFit(); + + // Das StopFlag wird durch Fussnoten gesetzt, + // die auf die naechste Seite wollen. + // OD, FME 2004-03-03 - call base class method <SwTxtFrmBreak::IsBreakNow(..)> + // instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break, + // even if due to widow rule no enough lines exists. + sal_uInt8 nNew = ( !GetFollow() && + nEnd < nStrLen && + ( rLine.IsStop() || + ( bHasToFit + ? ( rLine.GetLineNr() > 1 && + !rFrmBreak.IsInside( rLine ) ) + : rFrmBreak.IsBreakNow( rLine ) ) ) ) + ? 1 : 0; + if ( nNew ) + SplitFrm( nEnd ); + + const SwFrm *pBodyFrm = (const SwFrm*)(FindBodyFrm()); + + const long nBodyHeight = pBodyFrm ? ( IsVertical() ? + pBodyFrm->Frm().Width() : + pBodyFrm->Frm().Height() ) : 0; + + // Wenn die aktuellen Werte berechnet wurden, anzeigen, dass + // sie jetzt gueltig sind. + *(pPara->GetReformat()) = SwCharRange(); + sal_Bool bDelta = *pPara->GetDelta() != 0; + *(pPara->GetDelta()) = 0; + + if( rLine.IsStop() ) + { + rLine.TruncLines( sal_True ); + nNew = 1; + } + + // FindBreak schneidet die letzte Zeile ab. + if( !rFrmBreak.FindBreak( this, rLine, bHasToFit ) ) + { + // Wenn wir bis zum Ende durchformatiert haben, wird nEnd auf das Ende + // gesetzt. In AdjustFollow wird dadurch ggf. JoinFrm() ausgefuehrt. + // Ansonsten ist nEnd das Ende der letzten Zeile im Master. + xub_StrLen nOld = nEnd; + nEnd = rLine.GetEnd(); + if( GetFollow() ) + { + if( nNew && nOld < nEnd ) + RemoveFtn( nOld, nEnd - nOld ); + CHG_OFFSET( GetFollow(), nEnd ) + if( !bDelta ) + GetFollow()->ManipOfst( nEnd ); + } + } + else + { // Wenn wir Zeilen abgeben, darf kein Join auf den Folows gerufen werden, + // im Gegenteil, es muss ggf. sogar ein Follow erzeugt werden. + // Dies muss auch geschehen, wenn die Textmasse komplett im Master + // bleibt, denn es k???nnte ja ein harter Zeilenumbruch noch eine weitere + // Zeile (ohne Textmassse) notwendig machen! + nEnd = rLine.GetEnd(); + if( GetFollow() ) + { + // OD 21.03.2003 #108121# - Another case for not joining the follow: + // Text frame has no content, but a numbering. Then, do *not* join. + // Example of this case: When an empty, but numbered paragraph + // at the end of page is completely displaced by a fly frame. + // Thus, the text frame introduced a follow by a + // <SwTxtFrm::SplitFrm(..)> - see below. The follow then shows + // the numbering and must stay. + if ( GetFollow()->GetOfst() != nEnd || + GetFollow()->IsFieldFollow() || + ( nStrLen == 0 && GetTxtNode()->GetNumRule()) + ) + { + nNew |= 3; + } + CHG_OFFSET( GetFollow(), nEnd ) + GetFollow()->ManipOfst( nEnd ); + } + else + { + // OD 21.03.2003 #108121# - Only split frame, if the frame contains + // content or contains no content, but has a numbering. + if ( nStrLen > 0 || + ( nStrLen == 0 && GetTxtNode()->GetNumRule()) + ) + { + SplitFrm( nEnd ); + nNew |= 3; + } + } + // Wenn sich die Resthoehe geaendert hat, z.B. durch RemoveFtn() + // dann muessen wir auffuellen, um Oszillationen zu vermeiden! + if( bDummy && pBodyFrm && + nBodyHeight < ( IsVertical() ? + pBodyFrm->Frm().Width() : + pBodyFrm->Frm().Height() ) ) + rLine.MakeDummyLine(); + } + + // In AdjustFrm() stellen wir uns selbst per Grow/Shrink ein, + // in AdjustFollow() stellen wir unseren FolgeFrame ein. + + const SwTwips nDocPrtTop = Frm().Top() + Prt().Top(); + const SwTwips nOldHeight = Prt().SSize().Height(); + const SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight; + + // Vertical Formatting: + // The (rotated) repaint rectangle's x coordinate referes to the frame. + // If the frame grows (or shirks) the repaint rectangle cannot simply + // be rotated back after formatting, because we use the upper left point + // of the frame for rotation. This point changes when growing/shrinking. + if ( IsVertical() && nChg ) + { + SwRect &rRepaint = *(pPara->GetRepaint()); + rRepaint.Left( rRepaint.Left() - nChg ); + rRepaint.Width( rRepaint.Width() - nChg ); + } + + AdjustFrm( nChg, bHasToFit ); + +/* + // FME 16.07.2003 #i16930# - removed this code because it did not + // work correctly. In SwCntntFrm::MakeAll, the frame did not move to the + // next page, instead the print area was recalculated and + // Prepare( PREP_POS_CHGD, (const void*)&bFormatted, FALSE ) invalidated + // the other flags => loop + + // OD 04.04.2003 #108446# - handle special case: + // If text frame contains no content and just has split, because of a + // line stop, it has to move forward. To force this forward move without + // unnecessary formatting of its footnotes and its follow, especially in + // columned sections, adjust frame height to zero (0) and do not perform + // the intrinsic format of the follow. + // The formating method <SwCntntFrm::MakeAll()> will initiate the move forward. + sal_Bool bForcedNoIntrinsicFollowCalc = sal_False; + if ( nEnd == 0 && + rLine.IsStop() && HasFollow() && nNew == 1 + ) + { + AdjustFrm( -Frm().SSize().Height(), bHasToFit ); + Prt().Pos().Y() = 0; + Prt().Height( Frm().Height() ); + if ( FollowFormatAllowed() ) + { + bForcedNoIntrinsicFollowCalc = sal_True; + ForbidFollowFormat(); + } + } + else + { + AdjustFrm( nChg, bHasToFit ); + } + */ + + if( HasFollow() || IsInFtn() ) + _AdjustFollow( rLine, nEnd, nStrLen, nNew ); + + // FME 16.07.2003 #i16930# - removed this code because it did not work + // correctly + // OD 04.04.2003 #108446# - allow intrinsic format of follow, if above + // special case has forbit it. +/* if ( bForcedNoIntrinsicFollowCalc ) + { + AllowFollowFormat(); + } + */ + + pPara->SetPrepMustFit( sal_False ); + + UNDO_SWAP( this ) +} + +/************************************************************************* + * SwTxtFrm::FormatLine() + *************************************************************************/ + +// bPrev zeigt an, ob Reformat.Start() wegen Prev() vorgezogen wurde. +// Man weiss sonst nicht, ob man Repaint weiter einschraenken kann oder nicht. + + +sal_Bool SwTxtFrm::FormatLine( SwTxtFormatter &rLine, const sal_Bool bPrev ) +{ + ASSERT( ! IsVertical() || IsSwapped(), + "SwTxtFrm::FormatLine( rLine, bPrev) with unswapped frame" ); + SwParaPortion *pPara = rLine.GetInfo().GetParaPortion(); + // Nach rLine.FormatLine() haelt nStart den neuen Wert, + // waehrend in pOldStart der alte Offset gepflegt wird. + // Ueber diesen Weg soll das nDelta ersetzt werden. + // *pOldStart += rLine.GetCurr()->GetLen(); + const SwLineLayout *pOldCur = rLine.GetCurr(); + const xub_StrLen nOldLen = pOldCur->GetLen(); + const KSHORT nOldAscent = pOldCur->GetAscent(); + const KSHORT nOldHeight = pOldCur->Height(); + const SwTwips nOldWidth = pOldCur->Width() + pOldCur->GetHangingMargin(); + const sal_Bool bOldHyph = pOldCur->IsEndHyph(); + SwTwips nOldTop = 0; + SwTwips nOldBottom = 0; + if( rLine.GetCurr()->IsClipping() ) + rLine.CalcUnclipped( nOldTop, nOldBottom ); + + const xub_StrLen nNewStart = rLine.FormatLine( rLine.GetStart() ); + + ASSERT( Frm().Pos().Y() + Prt().Pos().Y() == rLine.GetFirstPos(), + "SwTxtFrm::FormatLine: frame leaves orbit." ); + ASSERT( rLine.GetCurr()->Height(), + "SwTxtFrm::FormatLine: line height is zero" ); + + // Das aktuelle Zeilenumbruchobjekt. + const SwLineLayout *pNew = rLine.GetCurr(); + + sal_Bool bUnChg = nOldLen == pNew->GetLen() && + bOldHyph == pNew->IsEndHyph(); + if ( bUnChg && !bPrev ) + { + // 6672: Toleranz von SLOPPY_TWIPS (5 Twips); vgl. 6922 + const long nWidthDiff = nOldWidth > pNew->Width() + ? nOldWidth - pNew->Width() + : pNew->Width() - nOldWidth; + + // we only declare a line as unchanged, if its main values have not + // changed and it is not the last line (!paragraph end symbol!) + bUnChg = nOldHeight == pNew->Height() && + nOldAscent == pNew->GetAscent() && + nWidthDiff <= SLOPPY_TWIPS && + pOldCur->GetNext(); + } + + // rRepaint wird berechnet: + const SwTwips nBottom = rLine.Y() + rLine.GetLineHeight(); + SwRepaint &rRepaint = *(pPara->GetRepaint()); + if( bUnChg && rRepaint.Top() == rLine.Y() + && (bPrev || nNewStart <= pPara->GetReformat()->Start()) + && ( nNewStart < GetTxtNode()->GetTxt().Len() ) ) + { + rRepaint.Top( nBottom ); + rRepaint.Height( 0 ); + } + else + { + if( nOldTop ) + { + if( nOldTop < rRepaint.Top() ) + rRepaint.Top( nOldTop ); + if( !rLine.IsUnclipped() || nOldBottom > rRepaint.Bottom() ) + { + rRepaint.Bottom( nOldBottom - 1 ); + rLine.SetUnclipped( sal_True ); + } + } + if( rLine.GetCurr()->IsClipping() && rLine.IsFlyInCntBase() ) + { + SwTwips nTmpTop, nTmpBottom; + rLine.CalcUnclipped( nTmpTop, nTmpBottom ); + if( nTmpTop < rRepaint.Top() ) + rRepaint.Top( nTmpTop ); + if( !rLine.IsUnclipped() || nTmpBottom > rRepaint.Bottom() ) + { + rRepaint.Bottom( nTmpBottom - 1 ); + rLine.SetUnclipped( sal_True ); + } + } + else + { + if( !rLine.IsUnclipped() || nBottom > rRepaint.Bottom() ) + { + rRepaint.Bottom( nBottom - 1 ); + rLine.SetUnclipped( sal_False ); + } + } + SwTwips nRght = Max( nOldWidth, pNew->Width() + + pNew->GetHangingMargin() ); + ViewShell *pSh = GetShell(); + const SwViewOption *pOpt = pSh ? pSh->GetViewOptions() : 0; + if( pOpt && (pOpt->IsParagraph() || pOpt->IsLineBreak()) ) + nRght += ( Max( nOldAscent, pNew->GetAscent() ) ); + else + nRght += ( Max( nOldAscent, pNew->GetAscent() ) / 4); + nRght += rLine.GetLeftMargin(); + if( rRepaint.GetOfst() || rRepaint.GetRightOfst() < nRght ) + rRepaint.SetRightOfst( nRght ); + + // Finally we enlarge the repaint rectangle if we found an underscore + // within our line. 40 Twips should be enough + const sal_Bool bHasUnderscore = + ( rLine.GetInfo().GetUnderScorePos() < nNewStart ); + if ( bHasUnderscore || rLine.GetCurr()->HasUnderscore() ) + rRepaint.Bottom( rRepaint.Bottom() + 40 ); + + ((SwLineLayout*)rLine.GetCurr())->SetUnderscore( bHasUnderscore ); + } + if( !bUnChg ) + rLine.SetChanges(); + + // Die gute, alte nDelta-Berechnung: + *(pPara->GetDelta()) -= long(pNew->GetLen()) - long(nOldLen); + + // Stop! + if( rLine.IsStop() ) + return sal_False; + + // Unbedingt noch eine Zeile + if( rLine.IsNewLine() ) + return sal_True; + + // bis zum Ende des Strings ? + if( nNewStart >= GetTxtNode()->GetTxt().Len() ) + return sal_False; + + if( rLine.GetInfo().IsShift() ) + return sal_True; + + // Ende des Reformats erreicht ? + const xub_StrLen nEnd = pPara->GetReformat()->Start() + + pPara->GetReformat()->Len(); + + if( nNewStart <= nEnd ) + return sal_True; + + return 0 != *(pPara->GetDelta()); +} + +/************************************************************************* + * SwTxtFrm::_Format() + *************************************************************************/ + +void SwTxtFrm::_Format( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf, + const sal_Bool bAdjust ) +{ + ASSERT( ! IsVertical() || IsSwapped(),"SwTxtFrm::_Format with unswapped frame" ); + + SwParaPortion *pPara = rLine.GetInfo().GetParaPortion(); + rLine.SetUnclipped( sal_False ); + + // Das war dem C30 zu kompliziert: aString( GetTxt() ); + const XubString &rString = GetTxtNode()->GetTxt(); + const xub_StrLen nStrLen = rString.Len(); + + SwCharRange &rReformat = *(pPara->GetReformat()); + SwRepaint &rRepaint = *(pPara->GetRepaint()); + SwRepaint *pFreeze = NULL; + + // Aus Performancegruenden wird in Init() rReformat auf STRING_LEN gesetzt. + // Fuer diesen Fall wird rReformat angepasst. + if( rReformat.Len() > nStrLen ) + rReformat.Len() = nStrLen; + + // Optimiert: + xub_StrLen nEnd = rReformat.Start() + rReformat.Len(); + if( nEnd > nStrLen ) + { + rReformat.Len() = nStrLen - rReformat.Start(); + nEnd = nStrLen; + } + + SwTwips nOldBottom; + if( GetOfst() && !IsFollow() ) + { + rLine.Bottom(); + nOldBottom = rLine.Y(); + rLine.Top(); + } + else + nOldBottom = 0; + rLine.CharToLine( rReformat.Start() ); + + // Worte koennen durch Fortfall oder Einfuegen eines Space + // auf die Zeile vor der editierten hinausgezogen werden, + // deshalb muss diese ebenfalls formatiert werden. + // Optimierung: Wenn rReformat erst hinter dem ersten Wort der + // Zeile beginnt, so kann diese Zeile die vorige nicht mehr beeinflussen. + // AMA: Leider doch, Textgroessenaenderungen + FlyFrames, die Rueckwirkung + // kann im Extremfall mehrere Zeilen (Frames!!!) betreffen! + + // --> FME 2005-04-18 #i46560# + // FME: Yes, consider this case: (word ) has to go to the next line + // because ) is a forbidden character at the beginning of a line although + // (word would still fit on the previous line. Adding text right in front + // of ) would not trigger a reformatting of the previous line. Adding 1 + // to the result of FindBrk() does not solve the problem in all cases, + // nevertheless it should be sufficient. + // <-- + sal_Bool bPrev = rLine.GetPrev() && + ( FindBrk( rString, rLine.GetStart(), rReformat.Start() + 1 ) + // --> FME 2005-04-18 #i46560# + + 1 + // <-- + >= rReformat.Start() || + rLine.GetCurr()->IsRest() ); + if( bPrev ) + { + while( rLine.Prev() ) + if( rLine.GetCurr()->GetLen() && !rLine.GetCurr()->IsRest() ) + { + if( !rLine.GetStart() ) + rLine.Top(); // damit NumDone nicht durcheinander kommt + break; + } + xub_StrLen nNew = rLine.GetStart() + rLine.GetLength(); + if( nNew ) + { + --nNew; + if( CH_BREAK == rString.GetChar( nNew ) ) + { + ++nNew; + rLine.Next(); + bPrev = sal_False; + } + } + rReformat.Len() += rReformat.Start() - nNew; + rReformat.Start() = nNew; + } + + rRepaint.SetOfst( 0 ); + rRepaint.SetRightOfst( 0 ); + rRepaint.Chg( Frm().Pos() + Prt().Pos(), Prt().SSize() ); + if( pPara->IsMargin() ) + rRepaint.Width( rRepaint.Width() + pPara->GetHangingMargin() ); + rRepaint.Top( rLine.Y() ); + // 6792: Rrand < LRand und Repaint + if( 0 >= rRepaint.Width() ) + rRepaint.Width(1); + WidowsAndOrphans aFrmBreak( this, rInf.IsTest() ? 1 : 0 ); + + // rLine steht jetzt auf der ersten Zeile, die formatiert werden + // muss. Das Flag bFirst sorgt dafuer, dass nicht Next() gerufen wird. + // Das ganze sieht verdreht aus, aber es muss sichergestellt werden, + // dass bei IsBreakNow rLine auf der Zeile zum stehen kommt, die + // nicht mehr passt. + sal_Bool bFirst = sal_True; + sal_Bool bFormat = sal_True; + + // 5383: Das CharToLine() kann uns auch in den roten Bereich fuehren. + // In diesem Fall muessen wir zurueckwandern, bis die Zeile, die + // nicht mehr passt in rLine eingestellt ist. Ansonsten geht Textmasse + // verloren, weil der Ofst im Follow falsch eingestellt wird. + + // OD 2004-02-25 #i16128# - method renamed + sal_Bool bBreak = ( !pPara->IsPrepMustFit() || rLine.GetLineNr() > 1 ) + && aFrmBreak.IsBreakNowWidAndOrp( rLine ); + if( bBreak ) + { + sal_Bool bPrevDone = 0 != rLine.Prev(); + // OD 2004-02-25 #i16128# - method renamed + while( bPrevDone && aFrmBreak.IsBreakNowWidAndOrp(rLine) ) + bPrevDone = 0 != rLine.Prev(); + if( bPrevDone ) + { + aFrmBreak.SetKeep( sal_False ); + rLine.Next(); + } + rLine.TruncLines(); + + // auf Nummer sicher: + // OD 2004-02-25 #i16128# - method renamed + bBreak = aFrmBreak.IsBreakNowWidAndOrp(rLine) && + ( !pPara->IsPrepMustFit() || rLine.GetLineNr() > 1 ); + } + + /* Bedeutung der folgenden Flags: + Ist das Watch(End/Mid)Hyph-Flag gesetzt, so muss formatiert werden, wenn + eine Trennung am (Zeilenende/Fly) vorliegt, sofern MaxHyph erreicht ist. + Das Jump(End/Mid)Flag bedeutet, dass die naechste Zeile, bei der keine + Trennung (Zeilenende/Fly) vorliegt, formatiert werden muss, da jetzt + umgebrochen werden koennte, was vorher moeglicherweise durch MaxHyph + verboten war. + Watch(End/Mid)Hyph wird gesetzt, wenn die letzte formatierte Zeile eine + Trennstelle erhalten hat, vorher aber keine hatte, + Jump(End/Mid)Hyph, wenn eine Trennstelle verschwindet. + */ + sal_Bool bJumpEndHyph = sal_False, + bWatchEndHyph = sal_False, + bJumpMidHyph = sal_False, + bWatchMidHyph = sal_False; + + const SwAttrSet& rAttrSet = GetTxtNode()->GetSwAttrSet(); + sal_Bool bMaxHyph = ( 0 != + ( rInf.MaxHyph() = rAttrSet.GetHyphenZone().GetMaxHyphens() ) ); + if ( bMaxHyph ) + rLine.InitCntHyph(); + + if( IsFollow() && IsFieldFollow() && rLine.GetStart() == GetOfst() ) + { + const SwLineLayout* pLine; + { + SwTxtFrm *pMaster = FindMaster(); + ASSERT( pMaster, "SwTxtFrm::Format: homeless follow" ); + if( !pMaster->HasPara() ) + pMaster->GetFormatted(); + SwTxtSizeInfo aInf( pMaster ); + SwTxtIter aMasterLine( pMaster, &aInf ); + aMasterLine.Bottom(); + pLine = aMasterLine.GetCurr(); + } + SwLinePortion* pRest = + rLine.MakeRestPortion( pLine, GetOfst() ); + if( pRest ) + rInf.SetRest( pRest ); + else + SetFieldFollow( sal_False ); + } + + /* Zum Abbruchkriterium: + * Um zu erkennen, dass eine Zeile nicht mehr auf die Seite passt, + * muss sie formatiert werden. Dieser Ueberhang wird z.B. in AdjustFollow + * wieder entfernt. + * Eine weitere Komplikation: wenn wir der Master sind, so muessen + * wir die Zeilen durchgehen, da es ja sein kann, dass eine Zeile + * vom Follow in den Master rutschen kann. + */ + do + { + DBG_LOOP; + if( bFirst ) + bFirst = sal_False; + else + { + if ( bMaxHyph ) + { + if ( rLine.GetCurr()->IsEndHyph() ) + rLine.CntEndHyph()++; + else + rLine.CntEndHyph() = 0; + if ( rLine.GetCurr()->IsMidHyph() ) + rLine.CntMidHyph()++; + else + rLine.CntMidHyph() = 0; + } + if( !rLine.Next() ) + { + if( !bFormat ) + { + SwLinePortion* pRest = + rLine.MakeRestPortion( rLine.GetCurr(), rLine.GetEnd() ); + if( pRest ) + rInf.SetRest( pRest ); + } + rLine.Insert( new SwLineLayout() ); + rLine.Next(); + bFormat = sal_True; + } + } + if ( !bFormat && bMaxHyph && + (bWatchEndHyph || bJumpEndHyph || bWatchMidHyph || bJumpMidHyph) ) + { + if ( rLine.GetCurr()->IsEndHyph() ) + { + if ( bWatchEndHyph ) + bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() ); + } + else + { + bFormat = bJumpEndHyph; + bWatchEndHyph = sal_False; + bJumpEndHyph = sal_False; + } + if ( rLine.GetCurr()->IsMidHyph() ) + { + if ( bWatchMidHyph && !bFormat ) + bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() ); + } + else + { + bFormat = bFormat || bJumpMidHyph; + bWatchMidHyph = sal_False; + bJumpMidHyph = sal_False; + } + } + if( bFormat ) + { + sal_Bool bOldEndHyph = rLine.GetCurr()->IsEndHyph(); + sal_Bool bOldMidHyph = rLine.GetCurr()->IsMidHyph(); + bFormat = FormatLine( rLine, bPrev ); + //9334: Es kann nur ein bPrev geben... (???) + bPrev = sal_False; + if ( bMaxHyph ) + { + if ( rLine.GetCurr()->IsEndHyph() != bOldEndHyph ) + { + bWatchEndHyph = !bOldEndHyph; + bJumpEndHyph = bOldEndHyph; + } + if ( rLine.GetCurr()->IsMidHyph() != bOldMidHyph ) + { + bWatchMidHyph = !bOldMidHyph; + bJumpMidHyph = bOldMidHyph; + } + } + } + + if( !rInf.IsNewLine() ) + { + if( !bFormat ) + bFormat = 0 != rInf.GetRest(); + if( rInf.IsStop() || rInf.GetIdx() >= nStrLen ) + break; + if( !bFormat && ( !bMaxHyph || ( !bWatchEndHyph && + !bJumpEndHyph && !bWatchMidHyph && !bJumpMidHyph ) ) ) + { + if( GetFollow() ) + { + while( rLine.Next() ) + ; //Nothing + pFreeze = new SwRepaint( rRepaint ); // to minimize painting + } + else + break; + } + } + // OD 2004-02-25 #i16128# - method renamed + bBreak = aFrmBreak.IsBreakNowWidAndOrp(rLine); + }while( !bBreak ); + + if( pFreeze ) + { + rRepaint = *pFreeze; + delete pFreeze; + } + + if( !rLine.IsStop() ) + { + // Wurde aller Text formatiert und gibt es noch weitere + // Zeilenobjekte, dann sind diese jetzt ueberfluessig, + // weil der Text kuerzer geworden ist. + if( rLine.GetStart() + rLine.GetLength() >= nStrLen && + rLine.GetCurr()->GetNext() ) + { + rLine.TruncLines(); + rLine.SetTruncLines( sal_True ); + } + } + + if( !rInf.IsTest() ) + { + // Bei OnceMore lohnt sich kein FormatAdjust + if( bAdjust || !rLine.GetDropFmt() || !rLine.CalcOnceMore() ) + { + FormatAdjust( rLine, aFrmBreak, nStrLen, rInf.IsStop() ); + } + if( rRepaint.HasArea() ) + SetRepaint(); + rLine.SetTruncLines( sal_False ); + if( nOldBottom ) // Bei "gescollten" Absaetzen wird + { // noch ueberprueft, ob durch Schrumpfen + rLine.Bottom(); // das Scrolling ueberfluessig wurde. + SwTwips nNewBottom = rLine.Y(); + if( nNewBottom < nOldBottom ) + _SetOfst( 0 ); + } + } +} + +/************************************************************************* + * SwTxtFrm::Format() + *************************************************************************/ + +void SwTxtFrm::FormatOnceMore( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf ) +{ + ASSERT( ! IsVertical() || IsSwapped(), + "A frame is not swapped in SwTxtFrm::FormatOnceMore" ); + + SwParaPortion *pPara = rLine.GetInfo().GetParaPortion(); + if( !pPara ) + return; + + // ggf gegen pPara + KSHORT nOld = ((const SwTxtMargin&)rLine).GetDropHeight(); + sal_Bool bShrink = sal_False, + bGrow = sal_False, + bGoOn = rLine.IsOnceMore(); + sal_uInt8 nGo = 0; + while( bGoOn ) + { +#ifdef DBGTXT + aDbstream << "OnceMore!" << endl; +#endif + ++nGo; + rInf.Init(); + rLine.Top(); + if( !rLine.GetDropFmt() ) + rLine.SetOnceMore( sal_False ); + SwCharRange aRange( 0, rInf.GetTxt().Len() ); + *(pPara->GetReformat()) = aRange; + _Format( rLine, rInf ); + + bGoOn = rLine.IsOnceMore(); + if( bGoOn ) + { + const KSHORT nNew = ((const SwTxtMargin&)rLine).GetDropHeight(); + if( nOld == nNew ) + bGoOn = sal_False; + else + { + if( nOld > nNew ) + bShrink = sal_True; + else + bGrow = sal_True; + + if( bShrink == bGrow || 5 < nGo ) + bGoOn = sal_False; + + nOld = nNew; + } + + // 6107: Wenn was schief ging, muss noch einmal formatiert werden. + if( !bGoOn ) + { + rInf.CtorInitTxtFormatInfo( this ); + rLine.CtorInitTxtFormatter( this, &rInf ); + rLine.SetDropLines( 1 ); + rLine.CalcDropHeight( 1 ); + SwCharRange aTmpRange( 0, rInf.GetTxt().Len() ); + *(pPara->GetReformat()) = aTmpRange; + _Format( rLine, rInf, sal_True ); + // 8047: Wir painten alles... + SetCompletePaint(); + } + } + } +} + +/************************************************************************* + * SwTxtFrm::_Format() + *************************************************************************/ + + +void SwTxtFrm::_Format( SwParaPortion *pPara ) +{ + const xub_StrLen nStrLen = GetTxt().Len(); + + // AMA: Wozu soll das gut sein? Scheint mir zuoft zu einem kompletten + // Formatieren und Repainten zu fuehren??? +// if ( !(*pPara->GetDelta()) ) +// *(pPara->GetDelta()) = nStrLen; +// else + if ( !nStrLen ) + { + // Leere Zeilen werden nicht lange gequaelt: + // pPara wird blank geputzt + // entspricht *pPara = SwParaPortion; + sal_Bool bMustFit = pPara->IsPrepMustFit(); + pPara->Truncate(); + pPara->FormatReset(); + if( pBlink && pPara->IsBlinking() ) + pBlink->Delete( pPara ); + + // delete pSpaceAdd und pKanaComp + pPara->FinishSpaceAdd(); + pPara->FinishKanaComp(); + pPara->ResetFlags(); + pPara->SetPrepMustFit( bMustFit ); + } + + ASSERT( ! IsSwapped(), "A frame is swapped before _Format" ); + + if ( IsVertical() ) + SwapWidthAndHeight(); + + SwTxtFormatInfo aInf( this ); + SwTxtFormatter aLine( this, &aInf ); + + // OD 2004-01-15 #110582# + HideAndShowObjects(); + + _Format( aLine, aInf ); + + if( aLine.IsOnceMore() ) + FormatOnceMore( aLine, aInf ); + + if ( IsVertical() ) + SwapWidthAndHeight(); + + ASSERT( ! IsSwapped(), "A frame is swapped after _Format" ); + + if( 1 < aLine.GetDropLines() ) + { + if( SVX_ADJUST_LEFT != aLine.GetAdjust() && + SVX_ADJUST_BLOCK != aLine.GetAdjust() ) + { + aLine.CalcDropAdjust(); + aLine.SetPaintDrop( sal_True ); + } + + if( aLine.IsPaintDrop() ) + { + aLine.CalcDropRepaint(); + aLine.SetPaintDrop( sal_False ); + } + } +} + +/************************************************************************* + * SwTxtFrm::Format() + *************************************************************************/ + +/* + * Format berechnet die Groesse des Textframes und ruft, wenn + * diese feststeht, Shrink() oder Grow(), um die Framegroesse dem + * evtl. veraenderten Platzbedarf anzupassen. + */ + +void SwTxtFrm::Format( const SwBorderAttrs * ) +{ + DBG_LOOP; +#if OSL_DEBUG_LEVEL > 1 + const XubString aXXX = GetTxtNode()->GetTxt(); + const SwTwips nDbgY = Frm().Top(); + (void)nDbgY; + const SwPageFrm *pDbgPage = FindPageFrm(); + const MSHORT nDbgPageNr = pDbgPage->GetPhyPageNum(); + (void)nDbgPageNr; + // Um zu gucken, ob es einen Ftn-Bereich gibt. + const SwFrm *pDbgFtnCont = (const SwFrm*)(FindPageFrm()->FindFtnCont()); + (void)pDbgFtnCont; + +#ifndef PRODUCT + // nStopAt laesst sich vom CV bearbeiten. + static MSHORT nStopAt = 0; + if( nStopAt == GetFrmId() ) + { + int i = GetFrmId(); + (void)i; + } +#endif +#endif + +#ifdef DEBUG_FTN + //Fussnote darf nicht auf einer Seite vor ihrer Referenz stehen. + if( IsInFtn() ) + { + const SwFtnFrm *pFtn = (SwFtnFrm*)GetUpper(); + const SwPageFrm *pFtnPage = pFtn->GetRef()->FindPageFrm(); + const MSHORT nFtnPageNr = pFtnPage->GetPhyPageNum(); + if( !IsLocked() ) + { + if( nFtnPageNr > nDbgPageNr ) + { + SwTxtFrmLocker aLock(this); + ASSERT( nFtnPageNr <= nDbgPageNr, "!Ftn steht vor der Referenz." ); + MSHORT i = 0; + } + } + } +#endif + + SWRECTFN( this ) + + // --> OD 2008-01-31 #newlistlevelattrs# + CalcAdditionalFirstLineOffset(); + // <-- + + // Vom Berichtsautopiloten oder ueber die BASIC-Schnittstelle kommen + // gelegentlich TxtFrms mit einer Breite <=0. + if( (Prt().*fnRect->fnGetWidth)() <= 0 ) + { + // Wenn MustFit gesetzt ist, schrumpfen wir ggf. auf die Unterkante + // des Uppers, ansonsten nehmen wir einfach eine Standardgroesse + // von 12 Pt. ein (240 Twip). + SwTxtLineAccess aAccess( this ); + long nFrmHeight = (Frm().*fnRect->fnGetHeight)(); + if( aAccess.GetPara()->IsPrepMustFit() ) + { + const SwTwips nLimit = (GetUpper()->*fnRect->fnGetPrtBottom)(); + const SwTwips nDiff = - (Frm().*fnRect->fnBottomDist)( nLimit ); + if( nDiff > 0 ) + Shrink( nDiff ); + } + else if( 240 < nFrmHeight ) + Shrink( nFrmHeight - 240 ); + else if( 240 > nFrmHeight ) + Grow( 240 - nFrmHeight ); + nFrmHeight = (Frm().*fnRect->fnGetHeight)(); + + long nTop = (this->*fnRect->fnGetTopMargin)(); + if( nTop > nFrmHeight ) + (this->*fnRect->fnSetYMargins)( nFrmHeight, 0 ); + else if( (Prt().*fnRect->fnGetHeight)() < 0 ) + (Prt().*fnRect->fnSetHeight)( 0 ); + return; + } + + const xub_StrLen nStrLen = GetTxtNode()->GetTxt().Len(); + if ( nStrLen || !FormatEmpty() ) + { + + SetEmpty( sal_False ); + // Um nicht durch verschachtelte Formats irritiert zu werden. + FormatLevel aLevel; + if( 12 == aLevel.GetLevel() ) + return; + + // Die Formatinformationen duerfen u.U. nicht veraendert werden. + if( IsLocked() ) + return; + + // 8708: Vorsicht, das Format() kann auch durch GetFormatted() + // angestossen werden. + if( IsHiddenNow() ) + { + long nPrtHeight = (Prt().*fnRect->fnGetHeight)(); + if( nPrtHeight ) + { + HideHidden(); + Shrink( nPrtHeight ); + } + else + { + // OD 2004-01-20 #110582# - assure that objects anchored + // at paragraph resp. at/as character inside paragraph + // are hidden. + HideAndShowObjects(); + } + ChgThisLines(); + return; + } + + // Waehrend wir formatieren, wollen wir nicht gestoert werden. + SwTxtFrmLocker aLock(this); + SwTxtLineAccess aAccess( this ); + const sal_Bool bNew = !aAccess.SwTxtLineAccess::IsAvailable(); + const sal_Bool bSetOfst = ( GetOfst() && GetOfst() > GetTxtNode()->GetTxt().Len() ); + + if( CalcPreps() ) + ; // nothing + // Wir returnen, wenn schon formatiert wurde, nicht aber, wenn + // der TxtFrm gerade erzeugt wurde und ueberhaupt keine Format- + // informationen vorliegen. + else if( !bNew && !aAccess.GetPara()->GetReformat()->Len() ) + { + if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue() ) + { + aAccess.GetPara()->SetPrepAdjust( sal_True ); + aAccess.GetPara()->SetPrep( sal_True ); + CalcPreps(); + } + SetWidow( sal_False ); + } + else if( bSetOfst && IsFollow() ) + { + SwTxtFrm *pMaster = FindMaster(); + ASSERT( pMaster, "SwTxtFrm::Format: homeless follow" ); + if( pMaster ) + pMaster->Prepare( PREP_FOLLOW_FOLLOWS ); + SwTwips nMaxY = (GetUpper()->*fnRect->fnGetPrtBottom)(); + if( (Frm().*fnRect->fnOverStep)( nMaxY ) ) + (this->*fnRect->fnSetLimit)( nMaxY ); + else if( (Frm().*fnRect->fnBottomDist)( nMaxY ) < 0 ) + (Frm().*fnRect->fnAddBottom)( -(Frm().*fnRect->fnGetHeight)() ); + } + else + { + // bSetOfst here means that we have the "red arrow situation" + if ( bSetOfst ) + _SetOfst( 0 ); + + const sal_Bool bOrphan = IsWidow(); + const SwFtnBossFrm* pFtnBoss = HasFtn() ? FindFtnBossFrm() : 0; + SwTwips nFtnHeight = 0; + if( pFtnBoss ) + { + const SwFtnContFrm* pCont = pFtnBoss->FindFtnCont(); + nFtnHeight = pCont ? (pCont->Frm().*fnRect->fnGetHeight)() : 0; + } + do + { + _Format( aAccess.GetPara() ); + if( pFtnBoss && nFtnHeight ) + { + const SwFtnContFrm* pCont = pFtnBoss->FindFtnCont(); + SwTwips nNewHeight = pCont ? (pCont->Frm().*fnRect->fnGetHeight)() : 0; + // If we lost some footnotes, we may have more space + // for our main text, so we have to format again ... + if( nNewHeight < nFtnHeight ) + nFtnHeight = nNewHeight; + else + break; + } + else + break; + } while ( pFtnBoss ); + if( bOrphan ) + { + ValidateFrm(); + SetWidow( sal_False ); + } + } + if( IsEmptyMaster() ) + { + SwFrm* pPre = GetPrev(); + if( pPre && + // --> FME 2004-07-22 #i10826# It's the first, it cannot keep! + pPre->GetIndPrev() && + // <-- + pPre->GetAttrSet()->GetKeep().GetValue() ) + { + pPre->InvalidatePos(); + } + } + } + + ChgThisLines(); + + // the PrepMustFit should not survive a Format operation + SwParaPortion *pPara = GetPara(); + if ( pPara ) + pPara->SetPrepMustFit( sal_False ); + +#if OSL_DEBUG_LEVEL > 1 + // Hier ein Instrumentarium, um ungewoehnlichen Master/Follow-Kombinationen, + // insbesondere bei Fussnoten, auf die Schliche zu kommen + if( IsFollow() || GetFollow() ) + { + SwTxtFrm *pTmpFrm = IsFollow() ? FindMaster() : this; + const SwPageFrm *pTmpPage = pTmpFrm->FindPageFrm(); + MSHORT nPgNr = pTmpPage->GetPhyPageNum(); + MSHORT nLast; + MSHORT nDummy = 0; // nur zum Breakpoint setzen + while( pTmpFrm->GetFollow() ) + { + pTmpFrm = pTmpFrm->GetFollow(); + nLast = nPgNr; + pTmpPage = pTmpFrm->FindPageFrm(); + nPgNr = pTmpPage->GetPhyPageNum(); + if( nLast > nPgNr ) + ++nDummy; // schon fast eine Assertion wert + else if( nLast == nPgNr ) + ++nDummy; // bei Spalten voellig normal, aber sonst!? + else if( nLast < nPgNr - 1 ) + ++nDummy; // kann schon mal temporaer vorkommen + } + } +#endif + + CalcBaseOfstForFly(); + // OD 2004-03-17 #i11860# + _CalcHeightOfLastLine(); +} + +/************************************************************************* + * SwTxtFrm::FormatQuick() + * + * bForceQuickFormat is set if GetFormatted() has been called during the + * painting process. Actually I cannot imagine a situation which requires + * a full formatting of the paragraph during painting, on the other hand + * a full formatting can cause the invalidation of other layout frames, + * e.g., if there are footnotes in this paragraph, and invalid layout + * frames will not calculated during the painting. So I actually want to + * avoid a formatting during painting, but since I'm a coward, I'll only + * force the quick formatting in the situation of issue i29062. + *************************************************************************/ + +sal_Bool SwTxtFrm::FormatQuick( bool bForceQuickFormat ) +{ + ASSERT( ! IsVertical() || ! IsSwapped(), + "SwTxtFrm::FormatQuick with swapped frame" ); + + DBG_LOOP; +#if OSL_DEBUG_LEVEL > 1 + const XubString aXXX = GetTxtNode()->GetTxt(); + const SwTwips nDbgY = Frm().Top(); + (void)nDbgY; +#ifndef PRODUCT + // nStopAt laesst sich vom CV bearbeiten. + static MSHORT nStopAt = 0; + if( nStopAt == GetFrmId() ) + { + int i = GetFrmId(); + (void)i; + } +#endif +#endif + + if( IsEmpty() && FormatEmpty() ) + return sal_True; + + // Wir sind sehr waehlerisch: + if( HasPara() || IsWidow() || IsLocked() + || !GetValidSizeFlag() || + ( ( IsVertical() ? Prt().Width() : Prt().Height() ) && IsHiddenNow() ) ) + return sal_False; + + SwTxtLineAccess aAccess( this ); + SwParaPortion *pPara = aAccess.GetPara(); + if( !pPara ) + return sal_False; + + SwFrmSwapper aSwapper( this, sal_True ); + + SwTxtFrmLocker aLock(this); + SwTxtFormatInfo aInf( this, sal_False, sal_True ); + if( 0 != aInf.MaxHyph() ) // 27483: MaxHyphen beachten! + return sal_False; + + SwTxtFormatter aLine( this, &aInf ); + + // DropCaps sind zu kompliziert... + if( aLine.GetDropFmt() ) + return sal_False; + + xub_StrLen nStart = GetOfst(); + const xub_StrLen nEnd = GetFollow() + ? GetFollow()->GetOfst() : aInf.GetTxt().Len(); + do + { + //DBG_LOOP; shadows declaration above. + //resolved into: +#if OSL_DEBUG_LEVEL > 1 +#ifndef PRODUCT + DbgLoop aDbgLoop2( (const void*) this ); +#endif +#endif + nStart = aLine.FormatLine( nStart ); + if( aInf.IsNewLine() || (!aInf.IsStop() && nStart < nEnd) ) + aLine.Insert( new SwLineLayout() ); + } while( aLine.Next() ); + + // Last exit: die Hoehen muessen uebereinstimmen. + Point aTopLeft( Frm().Pos() ); + aTopLeft += Prt().Pos(); + const SwTwips nNewHeight = aLine.Y() + aLine.GetLineHeight(); + const SwTwips nOldHeight = aTopLeft.Y() + Prt().Height(); + + if( !bForceQuickFormat && nNewHeight != nOldHeight && !IsUndersized() ) + { + // Achtung: Durch FormatLevel==12 kann diese Situation auftreten, don't panic! + // ASSERT( nNewHeight == nOldHeight, "!FormatQuick: rosebud" ); + const xub_StrLen nStrt = GetOfst(); + _InvalidateRange( SwCharRange( nStrt, nEnd - nStrt) ); + return sal_False; + } + + if( pFollow && nStart != ((SwTxtFrm*)pFollow)->GetOfst() ) + return sal_False; // kann z.B. durch Orphans auftreten (35083,35081) + + // Geschafft, wir sind durch ... + + // Repaint setzen + pPara->GetRepaint()->Pos( aTopLeft ); + pPara->GetRepaint()->SSize( Prt().SSize() ); + + // Reformat loeschen + *(pPara->GetReformat()) = SwCharRange(); + *(pPara->GetDelta()) = 0; + + return sal_True; +} diff --git a/sw/source/core/text/frminf.cxx b/sw/source/core/text/frminf.cxx new file mode 100644 index 000000000000..e043b4994bcd --- /dev/null +++ b/sw/source/core/text/frminf.cxx @@ -0,0 +1,380 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: frminf.cxx,v $ + * $Revision: 1.10 $ + * + * 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 <pam.hxx> // GetSpaces +#include <txtcfg.hxx> +#include <frminf.hxx> // SwTxtFrminfo +#include <itrtxt.hxx> // SwTxtMargin + +/************************************************************************* + * SwTxtMargin::GetTxtStart() + *************************************************************************/ + +xub_StrLen SwTxtMargin::GetTxtStart() const +{ + const XubString &rTxt = GetInfo().GetTxt(); + const xub_StrLen nTmpPos = nStart; + const xub_StrLen nEnd = nTmpPos + pCurr->GetLen(); + xub_StrLen i; + + for( i = nTmpPos; i < nEnd; ++i ) + { + const xub_Unicode aChar = rTxt.GetChar( i ); + if( CH_TAB != aChar && ' ' != aChar ) + return i; + } + return i; +} + +/************************************************************************* + * SwTxtMargin::GetTxtEnd() + *************************************************************************/ + +xub_StrLen SwTxtMargin::GetTxtEnd() const +{ + const XubString &rTxt = GetInfo().GetTxt(); + const xub_StrLen nTmpPos = nStart; + const xub_StrLen nEnd = nTmpPos + pCurr->GetLen(); + long i; + for( i = nEnd - 1; i >= nTmpPos; --i ) + { + xub_Unicode aChar = rTxt.GetChar( static_cast<xub_StrLen>(i) ); + if( CH_TAB != aChar && CH_BREAK != aChar && ' ' != aChar ) + return static_cast<xub_StrLen>(i + 1); + } + return static_cast<xub_StrLen>(i + 1); +} + +/************************************************************************* + * SwTxtFrmInfo::IsOneLine() + *************************************************************************/ + +// Passt der Absatz in eine Zeile? +sal_Bool SwTxtFrmInfo::IsOneLine() const +{ + const SwLineLayout *pLay = pFrm->GetPara(); + if( !pLay ) + return sal_False; + else + { + // 6575: bei Follows natuerlich sal_False + if( pFrm->GetFollow() ) + return sal_False; + pLay = pLay->GetNext(); + while( pLay ) + { + if( pLay->GetLen() ) + return sal_False; + pLay = pLay->GetNext(); + } + return sal_True; + } +} + +/************************************************************************* + * SwTxtFrmInfo::IsFilled() + *************************************************************************/ + +// Ist die Zeile zu X% gefuellt? +sal_Bool SwTxtFrmInfo::IsFilled( const sal_uInt8 nPercent ) const +{ + const SwLineLayout *pLay = pFrm->GetPara(); + if( !pLay ) + return sal_False; + else + { + long nWidth = pFrm->Prt().Width(); + nWidth *= nPercent; + nWidth /= 100; + return KSHORT(nWidth) <= pLay->Width(); + } +} + +/************************************************************************* + * SwTxtFrmInfo::GetLineStart() + *************************************************************************/ + +// Wo beginnt der Text (ohne whitespaces)? ( Dokument global ) +SwTwips SwTxtFrmInfo::GetLineStart( const SwTxtCursor &rLine ) const +{ + xub_StrLen nTxtStart = rLine.GetTxtStart(); + SwTwips nStart; + if( rLine.GetStart() == nTxtStart ) + nStart = rLine.GetLineStart(); + else + { + SwRect aRect; + if( ((SwTxtCursor&)rLine).GetCharRect( &aRect, nTxtStart ) ) + nStart = aRect.Left(); + else + nStart = rLine.GetLineStart(); + } + return nStart; +} + + +/************************************************************************* + * SwTxtFrmInfo::GetLineStart() + *************************************************************************/ + +// Wo beginnt der Text (ohne whitespaces)? (rel. im Frame) +SwTwips SwTxtFrmInfo::GetLineStart() const +{ + SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm ); + SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf ); + return GetLineStart( aLine ) - pFrm->Frm().Left() - pFrm->Prt().Left(); +} + +// errechne die Position des Zeichens und gebe die Mittelposition zurueck +SwTwips SwTxtFrmInfo::GetCharPos( xub_StrLen nChar, sal_Bool bCenter ) const +{ + SWRECTFN( pFrm ) + SwFrmSwapper aSwapper( pFrm, sal_True ); + + SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm ); + SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf ); + + SwTwips nStt, nNext; + SwRect aRect; + if( ((SwTxtCursor&)aLine).GetCharRect( &aRect, nChar ) ) + { + if ( bVert ) + pFrm->SwitchHorizontalToVertical( aRect ); + + nStt = (aRect.*fnRect->fnGetLeft)(); + } + else + nStt = aLine.GetLineStart(); + + if( !bCenter ) + return nStt - (pFrm->Frm().*fnRect->fnGetLeft)(); + + if( ((SwTxtCursor&)aLine).GetCharRect( &aRect, nChar+1 ) ) + { + if ( bVert ) + pFrm->SwitchHorizontalToVertical( aRect ); + + nNext = (aRect.*fnRect->fnGetLeft)(); + } + else + nNext = aLine.GetLineStart(); + + return (( nNext + nStt ) / 2 ) - (pFrm->Frm().*fnRect->fnGetLeft)(); +} + +/************************************************************************* + * SwTxtFrmInfo::GetSpaces() + *************************************************************************/ + +SwPaM *AddPam( SwPaM *pPam, const SwTxtFrm* pTxtFrm, + const xub_StrLen nPos, const xub_StrLen nLen ) +{ + if( nLen ) + { + // Es koennte auch der erste sein. + if( pPam->HasMark() ) + { + // liegt die neue Position genau hinter der aktuellen, dann + // erweiter den Pam einfach + if( nPos == pPam->GetPoint()->nContent.GetIndex() ) + { + pPam->GetPoint()->nContent += nLen; + return pPam; + } + pPam = new SwPaM( *pPam ); + } + + SwIndex &rContent = pPam->GetPoint()->nContent; + rContent.Assign( (SwTxtNode*)pTxtFrm->GetTxtNode(), nPos ); + pPam->SetMark(); + rContent += nLen; + } + return pPam; +} + +// Sammelt die whitespaces am Zeilenbeginn und -ende im Pam +void SwTxtFrmInfo::GetSpaces( SwPaM &rPam, sal_Bool bWithLineBreak ) const +{ + SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm ); + SwTxtMargin aLine( (SwTxtFrm*)pFrm, &aInf ); + SwPaM *pPam = &rPam; + sal_Bool bFirstLine = sal_True; + do { + + if( aLine.GetCurr()->GetLen() ) + { + xub_StrLen nPos = aLine.GetTxtStart(); + // Bug 49649: von der ersten Line die Blanks/Tabs NICHT + // mit selektieren + if( !bFirstLine && nPos > aLine.GetStart() ) + pPam = AddPam( pPam, pFrm, aLine.GetStart(), + nPos - aLine.GetStart() ); + + // Bug 49649: von der letzten Line die Blanks/Tabs NICHT + // mit selektieren + if( aLine.GetNext() ) + { + nPos = aLine.GetTxtEnd(); + + if( nPos < aLine.GetEnd() ) + { + MSHORT nOff = !bWithLineBreak && CH_BREAK == + aLine.GetInfo().GetChar( aLine.GetEnd() - 1 ) + ? 1 : 0; + pPam = AddPam( pPam, pFrm, nPos, aLine.GetEnd() - nPos - nOff ); + } + } + } + bFirstLine = sal_False; + } + while( aLine.Next() ); +} + +/************************************************************************* + * SwTxtFrmInfo::IsBullet() + *************************************************************************/ + +// Ist an der Textposition ein Bullet/Symbol etc? +// Fonts: CharSet, SYMBOL und DONTKNOW +sal_Bool SwTxtFrmInfo::IsBullet( xub_StrLen nTxtStart ) const +{ + SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm ); + SwTxtMargin aLine( (SwTxtFrm*)pFrm, &aInf ); + aInf.SetIdx( nTxtStart ); + return aLine.IsSymbol( nTxtStart ); +} + +/************************************************************************* + * SwTxtFrmInfo::GetFirstIndent() + *************************************************************************/ + +// Ermittelt Erstzeileneinzug +// Voraussetzung fuer pos. oder neg. EZE ist, dass alle +// Zeilen ausser der ersten Zeile den selben linken Rand haben. +// Wir wollen nicht so knauserig sein und arbeiten mit einer Toleranz +// von TOLERANCE Twips. + +#define TOLERANCE 20 + +SwTwips SwTxtFrmInfo::GetFirstIndent() const +{ + SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm ); + SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf ); + const SwTwips nFirst = GetLineStart( aLine ); + if( !aLine.Next() ) + return 0; + + SwTwips nLeft = GetLineStart( aLine ); + while( aLine.Next() ) + { + if( aLine.GetCurr()->GetLen() ) + { + const SwTwips nCurrLeft = GetLineStart( aLine ); + if( nLeft + TOLERANCE < nCurrLeft || + nLeft - TOLERANCE > nCurrLeft ) + return 0; + } + } + + // Vorerst wird nur +1, -1 und 0 returnt. + if( nLeft == nFirst ) + return 0; + else + if( nLeft > nFirst ) + return -1; + else + return +1; +} + +/************************************************************************* + * SwTxtFrmInfo::GetBigIndent() + *************************************************************************/ + +KSHORT SwTxtFrmInfo::GetBigIndent( xub_StrLen& rFndPos, + const SwTxtFrm *pNextFrm ) const +{ + SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm ); + SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf ); + SwTwips nNextIndent = 0; + + if( pNextFrm ) + { + // ich bin einzeilig + SwTxtSizeInfo aNxtInf( (SwTxtFrm*)pNextFrm ); + SwTxtCursor aNxtLine( (SwTxtFrm*)pNextFrm, &aNxtInf ); + nNextIndent = GetLineStart( aNxtLine ); + } + else + { + // ich bin mehrzeilig + if( aLine.Next() ) + { + nNextIndent = GetLineStart( aLine ); + aLine.Prev(); + } + } + + if( nNextIndent <= GetLineStart( aLine ) ) + return 0; + + const Point aPoint( nNextIndent, aLine.Y() ); + rFndPos = aLine.GetCrsrOfst( 0, aPoint, sal_False ); + if( 1 >= rFndPos ) + return 0; + + // steht vor einem "nicht Space" + const XubString& rTxt = aInf.GetTxt(); + xub_Unicode aChar = rTxt.GetChar( rFndPos ); + if( CH_TAB == aChar || CH_BREAK == aChar || ' ' == aChar || + (( CH_TXTATR_BREAKWORD == aChar || CH_TXTATR_INWORD == aChar ) && + aInf.HasHint( rFndPos ) ) ) + return 0; + + // und hinter einem "Space" + aChar = rTxt.GetChar( rFndPos - 1 ); + if( CH_TAB != aChar && CH_BREAK != aChar && + ( ( CH_TXTATR_BREAKWORD != aChar && CH_TXTATR_INWORD != aChar ) || + !aInf.HasHint( rFndPos - 1 ) ) && + // mehr als 2 Blanks !! + ( ' ' != aChar || ' ' != rTxt.GetChar( rFndPos - 2 ) ) ) + return 0; + + SwRect aRect; + return aLine.GetCharRect( &aRect, rFndPos ) + ? KSHORT( aRect.Left() - pFrm->Frm().Left() - pFrm->Prt().Left()) + : 0; +} + + + diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx new file mode 100644 index 000000000000..5a88599c6e35 --- /dev/null +++ b/sw/source/core/text/frmpaint.cxx @@ -0,0 +1,820 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: frmpaint.cxx,v $ + * $Revision: 1.60.68.1 $ + * + * 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 <com/sun/star/text/HoriOrientation.hpp> +#include <hintids.hxx> +#include <vcl/sound.hxx> +#include <tools/shl.hxx> // SW_MOD +#include <svx/pgrditem.hxx> +#include <svx/lrspitem.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <paratr.hxx> + +#ifndef _FMTLINE_HXX +#include <fmtline.hxx> +#endif +#ifndef _LINEINFO_HXX +#include <lineinfo.hxx> +#endif +#include <charfmt.hxx> +#include <pagefrm.hxx> +#include <viewsh.hxx> // ViewShell +#include <viewimp.hxx> // SwViewImp +#include <viewopt.hxx> // SwViewOption +#include <frmtool.hxx> // DrawGraphic +#include <txtcfg.hxx> +#include <txtfrm.hxx> // SwTxtFrm +#include <itrpaint.hxx> // SwTxtPainter +#include <txtpaint.hxx> // SwSaveClip +#include <txtcache.hxx> // SwTxtLineAccess +#include <flyfrm.hxx> // SwFlyFrm +#include <redlnitr.hxx> // SwRedlineItr +#include <swmodule.hxx> // SW_MOD +#include <tabfrm.hxx> // SwTabFrm (Redlining) +#include <scrrect.hxx> +#include <SwGrammarMarkUp.hxx> + +// --> FME 2004-06-08 #i12836# enhanced pdf export +#include <EnhancedPDFExportHelper.hxx> +// <-- + +#include <IDocumentStylePoolAccess.hxx> +#include <IDocumentLineNumberAccess.hxx> + +// --> OD 2006-06-27 #b6440955# +// variable moved to class <numfunc:GetDefBulletConfig> +//extern const sal_Char __FAR_DATA sBulletFntName[]; +namespace numfunc +{ + extern const String& GetDefBulletFontname(); + extern bool IsDefBulletFontUserDefined(); +} +// <-- + + +#define REDLINE_DISTANCE 567/4 +#define REDLINE_MINDIST 567/10 + +using namespace ::com::sun::star; + +//////////////////////////////////////////////////////////// + +sal_Bool bInitFont = sal_True; + +class SwExtraPainter +{ + SwSaveClip aClip; + SwRect aRect; + const SwTxtFrm* pTxtFrm; + ViewShell *pSh; + SwFont* pFnt; + const SwLineNumberInfo &rLineInf; + SwTwips nX; + SwTwips nRedX; + ULONG nLineNr; + MSHORT nDivider; + sal_Bool bGoLeft; + sal_Bool bLineNum; + inline sal_Bool IsClipChg() { return aClip.IsChg(); } +public: + SwExtraPainter( const SwTxtFrm *pFrm, ViewShell *pVwSh, + const SwLineNumberInfo &rLnInf, const SwRect &rRct, + sal_Int16 eHor, sal_Bool bLnNm ); + ~SwExtraPainter() { delete pFnt; } + inline SwFont* GetFont() const { return pFnt; } + inline void IncLineNr() { ++nLineNr; } + inline sal_Bool HasNumber() { return !( nLineNr % rLineInf.GetCountBy() ); } + inline sal_Bool HasDivider() { if( !nDivider ) return sal_False; + return !(nLineNr % rLineInf.GetDividerCountBy()); } + + void PaintExtra( SwTwips nY, long nAsc, long nMax, sal_Bool bRed ); + void PaintRedline( SwTwips nY, long nMax ); +}; + + +SwExtraPainter::SwExtraPainter( const SwTxtFrm *pFrm, ViewShell *pVwSh, + const SwLineNumberInfo &rLnInf, const SwRect &rRct, + sal_Int16 eHor, sal_Bool bLnNm ) + : aClip( pVwSh->GetWin() || pFrm->IsUndersized() ? pVwSh->GetOut() : 0 ), + aRect( rRct ), pTxtFrm( pFrm ), pSh( pVwSh ), pFnt( 0 ), rLineInf( rLnInf ), + nLineNr( 1L ), bLineNum( bLnNm ) +{ + if( pFrm->IsUndersized() ) + { + SwTwips nBottom = pFrm->Frm().Bottom(); + if( aRect.Bottom() > nBottom ) + aRect.Bottom( nBottom ); + } + MSHORT nVirtPageNum = 0; + if( bLineNum ) + { /* initialisiert die Member, die bei Zeilennumerierung notwendig sind: + + nDivider, wie oft ist ein Teilerstring gewuenscht, 0 == nie; + nX, X-Position der Zeilennummern; + pFnt, der Font der Zeilennummern; + nLineNr, die erste Zeilennummer; + bLineNum wird ggf.wieder auf sal_False gesetzt, wenn die Numerierung sich + komplett ausserhalb des Paint-Rechtecks aufhaelt. */ + nDivider = rLineInf.GetDivider().Len() ? rLineInf.GetDividerCountBy() : 0; + nX = pFrm->Frm().Left(); + SwCharFmt* pFmt = rLineInf.GetCharFmt( const_cast<IDocumentStylePoolAccess&>(*pFrm->GetNode()->getIDocumentStylePoolAccess()) ); + ASSERT( pFmt, "PaintExtraData without CharFmt" ); + pFnt = new SwFont( &pFmt->GetAttrSet(), pFrm->GetTxtNode()->getIDocumentSettingAccess() ); + pFnt->Invalidate(); + pFnt->ChgPhysFnt( pSh, *pSh->GetOut() ); + pFnt->SetVertical( 0, pFrm->IsVertical() ); + nLineNr += pFrm->GetAllLines() - pFrm->GetThisLines(); + LineNumberPosition ePos = rLineInf.GetPos(); + if( ePos != LINENUMBER_POS_LEFT && ePos != LINENUMBER_POS_RIGHT ) + { + if( pFrm->FindPageFrm()->OnRightPage() ) + { + nVirtPageNum = 1; + ePos = ePos == LINENUMBER_POS_INSIDE ? + LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT; + } + else + { + nVirtPageNum = 2; + ePos = ePos == LINENUMBER_POS_OUTSIDE ? + LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT; + } + } + if( LINENUMBER_POS_LEFT == ePos ) + { + bGoLeft = sal_True; + nX -= rLineInf.GetPosFromLeft(); + if( nX < aRect.Left() ) + bLineNum = sal_False; + } + else + { + bGoLeft = sal_False; + nX += pFrm->Frm().Width() + rLineInf.GetPosFromLeft(); + if( nX > aRect.Right() ) + bLineNum = sal_False; + } + } + if( eHor != text::HoriOrientation::NONE ) + { + if( text::HoriOrientation::INSIDE == eHor || text::HoriOrientation::OUTSIDE == eHor ) + { + if( !nVirtPageNum ) + nVirtPageNum = pFrm->FindPageFrm()->OnRightPage() ? 1 : 2; + if( nVirtPageNum % 2 ) + eHor = eHor == text::HoriOrientation::INSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT; + else + eHor = eHor == text::HoriOrientation::OUTSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT; + } + const SwFrm* pTmpFrm = pFrm->FindTabFrm(); + if( !pTmpFrm ) + pTmpFrm = pFrm; + nRedX = text::HoriOrientation::LEFT == eHor ? pTmpFrm->Frm().Left() - REDLINE_DISTANCE : + pTmpFrm->Frm().Right() + REDLINE_DISTANCE; + } +} + +/************************************************************************* + * SwExtraPainter::PaintExtra() + **************************************************************************/ + +void SwExtraPainter::PaintExtra( SwTwips nY, long nAsc, long nMax, sal_Bool bRed ) +{ + //Zeilennummer ist staerker als der Teiler + const XubString aTmp( HasNumber() ? rLineInf.GetNumType().GetNumStr( nLineNr ) + : rLineInf.GetDivider() ); + + // get script type of line numbering: + pFnt->SetActual( SwScriptInfo::WhichFont( 0, &aTmp, 0 ) ); + + SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), 0, aTmp, 0, aTmp.Len() ); + aDrawInf.SetSpace( 0 ); + aDrawInf.SetWrong( NULL ); + aDrawInf.SetGrammarCheck( NULL ); + aDrawInf.SetSmartTags( NULL ); // SMARTTAGS + aDrawInf.SetLeft( 0 ); + aDrawInf.SetRight( LONG_MAX ); + aDrawInf.SetFrm( pTxtFrm ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( sal_False ); + aDrawInf.SetIgnoreFrmRTL( sal_True ); + + sal_Bool bTooBig = pFnt->GetSize( pFnt->GetActual() ).Height() > nMax && + pFnt->GetHeight( pSh, *pSh->GetOut() ) > nMax; + SwFont* pTmpFnt; + if( bTooBig ) + { + pTmpFnt = new SwFont( *GetFont() ); + if( nMax >= 20 ) + { + nMax *= 17; + nMax /= 20; + } + pTmpFnt->SetSize( Size( 0, nMax ), pTmpFnt->GetActual() ); + } + else + pTmpFnt = GetFont(); + Point aTmpPos( nX, nY ); + aTmpPos.Y() += nAsc; + sal_Bool bPaint = sal_True; + if( !IsClipChg() ) + { + Size aSize = pTmpFnt->_GetTxtSize( aDrawInf ); + if( bGoLeft ) + aTmpPos.X() -= aSize.Width(); + // calculate rectangle containing the line number + SwRect aRct( Point( aTmpPos.X(), + aTmpPos.Y() - pTmpFnt->GetAscent( pSh, *pSh->GetOut() ) + ), aSize ); + if( !aRect.IsInside( aRct ) ) + { + if( aRct.Intersection( aRect ).IsEmpty() ) + bPaint = sal_False; + else + aClip.ChgClip( aRect, pTxtFrm ); + } + } + else if( bGoLeft ) + aTmpPos.X() -= pTmpFnt->_GetTxtSize( aDrawInf ).Width(); + aDrawInf.SetPos( aTmpPos ); + if( bPaint ) + pTmpFnt->_DrawText( aDrawInf ); + + if( bTooBig ) + delete pTmpFnt; + if( bRed ) + { + long nDiff = bGoLeft ? nRedX - nX : nX - nRedX; + if( nDiff > REDLINE_MINDIST ) + PaintRedline( nY, nMax ); + } +} + +void SwExtraPainter::PaintRedline( SwTwips nY, long nMax ) +{ + Point aStart( nRedX, nY ); + Point aEnd( nRedX, nY + nMax ); + + if( !IsClipChg() ) + { + SwRect aRct( aStart, aEnd ); + if( !aRect.IsInside( aRct ) ) + { + if( aRct.Intersection( aRect ).IsEmpty() ) + return; + aClip.ChgClip( aRect, pTxtFrm ); + } + } + const Color aOldCol( pSh->GetOut()->GetLineColor() ); + pSh->GetOut()->SetLineColor( SW_MOD()->GetRedlineMarkColor() ); + + if ( pTxtFrm->IsVertical() ) + { + pTxtFrm->SwitchHorizontalToVertical( aStart ); + pTxtFrm->SwitchHorizontalToVertical( aEnd ); + } + + pSh->GetOut()->DrawLine( aStart, aEnd ); + pSh->GetOut()->SetLineColor( aOldCol ); +} + +void SwTxtFrm::PaintExtraData( const SwRect &rRect ) const +{ + if( Frm().Top() > rRect.Bottom() || Frm().Bottom() < rRect.Top() ) + return; + + const SwTxtNode& rTxtNode = *GetTxtNode(); + const IDocumentRedlineAccess* pIDRA = rTxtNode.getIDocumentRedlineAccess(); + const SwLineNumberInfo &rLineInf = rTxtNode.getIDocumentLineNumberAccess()->GetLineNumberInfo(); + const SwFmtLineNumber &rLineNum = GetAttrSet()->GetLineNumber(); + sal_Bool bLineNum = !IsInTab() && rLineInf.IsPaintLineNumbers() && + ( !IsInFly() || rLineInf.IsCountInFlys() ) && rLineNum.IsCount(); + sal_Int16 eHor = (sal_Int16)SW_MOD()->GetRedlineMarkPos(); + if( eHor != text::HoriOrientation::NONE && !IDocumentRedlineAccess::IsShowChanges( pIDRA->GetRedlineMode() ) ) + eHor = text::HoriOrientation::NONE; + sal_Bool bRedLine = eHor != text::HoriOrientation::NONE; + if ( bLineNum || bRedLine ) + { + if( IsLocked() || IsHiddenNow() || !Prt().Height() ) + return; + ViewShell *pSh = GetShell(); + + SWAP_IF_NOT_SWAPPED( this ) + SwRect rOldRect( rRect ); + + if ( IsVertical() ) + SwitchVerticalToHorizontal( (SwRect&)rRect ); + + SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() ); + aLayoutModeModifier.Modify( sal_False ); + + // --> FME 2004-06-24 #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( 0, 0, 0, *pSh->GetOut() ); + // <-- + + SwExtraPainter aExtra( this, pSh, rLineInf, rRect, eHor, bLineNum ); + + if( HasPara() ) + { + SwTxtFrmLocker aLock((SwTxtFrm*)this); + + SwTxtLineAccess aAccess( (SwTxtFrm*)this ); + aAccess.GetPara(); + + SwTxtPaintInfo aInf( (SwTxtFrm*)this, rRect ); + + aLayoutModeModifier.Modify( sal_False ); + + SwTxtPainter aLine( (SwTxtFrm*)this, &aInf ); + sal_Bool bNoDummy = !aLine.GetNext(); // Nur eine Leerzeile! + + while( aLine.Y() + aLine.GetLineHeight() <= rRect.Top() ) + { + if( !aLine.GetCurr()->IsDummy() && + ( rLineInf.IsCountBlankLines() || + aLine.GetCurr()->HasCntnt() ) ) + aExtra.IncLineNr(); + if( !aLine.Next() ) + { + (SwRect&)rRect = rOldRect; + UNDO_SWAP( this ) + return; + } + } + + long nBottom = rRect.Bottom(); + + sal_Bool bNoPrtLine = 0 == GetMinPrtLine(); + if( !bNoPrtLine ) + { + while ( aLine.Y() < GetMinPrtLine() ) + { + if( ( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasCntnt() ) + && !aLine.GetCurr()->IsDummy() ) + aExtra.IncLineNr(); + if( !aLine.Next() ) + break; + } + bNoPrtLine = aLine.Y() >= GetMinPrtLine(); + } + if( bNoPrtLine ) + { + do + { + if( bNoDummy || !aLine.GetCurr()->IsDummy() ) + { + sal_Bool bRed = bRedLine && aLine.GetCurr()->HasRedline(); + if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasCntnt() ) + { + if( bLineNum && + ( aExtra.HasNumber() || aExtra.HasDivider() ) ) + { + KSHORT nTmpHeight, nTmpAscent; + aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + aExtra.PaintExtra( aLine.Y(), nTmpAscent, + nTmpHeight, bRed ); + bRed = sal_False; + } + aExtra.IncLineNr(); + } + if( bRed ) + aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight() ); + } + } while( aLine.Next() && aLine.Y() <= nBottom ); + } + } + else + { + bRedLine &= ( MSHRT_MAX!= pIDRA->GetRedlinePos(rTxtNode, USHRT_MAX) ); + + if( bLineNum && rLineInf.IsCountBlankLines() && + ( aExtra.HasNumber() || aExtra.HasDivider() ) ) + { + aExtra.PaintExtra( Frm().Top()+Prt().Top(), aExtra.GetFont() + ->GetAscent( pSh, *pSh->GetOut() ), Prt().Height(), bRedLine ); + } + else if( bRedLine ) + aExtra.PaintRedline( Frm().Top()+Prt().Top(), Prt().Height() ); + } + + (SwRect&)rRect = rOldRect; + UNDO_SWAP( this ) + } +} + +/************************************************************************* + * SwTxtFrm::Paint() + *************************************************************************/ + +SwRect SwTxtFrm::Paint() +{ +#if OSL_DEBUG_LEVEL > 1 + const SwTwips nDbgY = Frm().Top(); + (void)nDbgY; +#endif + + // finger layout + ASSERT( GetValidPosFlag(), "+SwTxtFrm::Paint: no Calc()" ); + + SwRect aRet( Prt() ); + if ( IsEmpty() || !HasPara() ) + aRet += Frm().Pos(); + else + { + // AMA: Wir liefern jetzt mal das richtige Repaintrechteck zurueck, + // d.h. als linken Rand den berechneten PaintOfst! + SwRepaint *pRepaint = GetPara()->GetRepaint(); + long l; + if( pRepaint->GetOfst() ) + pRepaint->Left( pRepaint->GetOfst() ); + + l = pRepaint->GetRightOfst(); + if( l && ( pRepaint->GetOfst() || l > pRepaint->Right() ) ) + pRepaint->Right( l ); + pRepaint->SetOfst( 0 ); + aRet = *pRepaint; + + if ( IsRightToLeft() ) + SwitchLTRtoRTL( aRet ); + + if ( IsVertical() ) + SwitchHorizontalToVertical( aRet ); + } + ResetRepaint(); + + return aRet; +} + +/************************************************************************* + * SwTxtFrm::Paint() + *************************************************************************/ + +sal_Bool SwTxtFrm::PaintEmpty( const SwRect &rRect, sal_Bool bCheck ) const +{ + ViewShell *pSh = GetShell(); + if( pSh && ( pSh->GetViewOptions()->IsParagraph() || bInitFont ) ) + { + bInitFont = sal_False; + SwTxtFly aTxtFly( this ); + aTxtFly.SetTopRule(); + SwRect aRect; + if( bCheck && aTxtFly.IsOn() && aTxtFly.IsAnyObj( aRect ) ) + return sal_False; + else if( pSh->GetWin() ) + { + SwFont *pFnt; + const SwTxtNode& rTxtNode = *GetTxtNode(); + if ( rTxtNode.HasSwAttrSet() ) + { + const SwAttrSet *pAttrSet = &( rTxtNode.GetSwAttrSet() ); + pFnt = new SwFont( pAttrSet, rTxtNode.getIDocumentSettingAccess() ); + } + else + { + SwFontAccess aFontAccess( &rTxtNode.GetAnyFmtColl(), pSh ); + pFnt = new SwFont( *aFontAccess.Get()->GetFont() ); + } + + const IDocumentRedlineAccess* pIDRA = rTxtNode.getIDocumentRedlineAccess(); + if( IDocumentRedlineAccess::IsShowChanges( pIDRA->GetRedlineMode() ) ) + { + MSHORT nRedlPos = pIDRA->GetRedlinePos( rTxtNode, USHRT_MAX ); + if( MSHRT_MAX != nRedlPos ) + { + SwAttrHandler aAttrHandler; + aAttrHandler.Init( rTxtNode.GetSwAttrSet(), + *rTxtNode.getIDocumentSettingAccess(), NULL ); + SwRedlineItr aRedln( rTxtNode, *pFnt, aAttrHandler, nRedlPos, sal_True ); + } + } + + if( pSh->GetViewOptions()->IsParagraph() && Prt().Height() ) + { + if( RTL_TEXTENCODING_SYMBOL == pFnt->GetCharSet( SW_LATIN ) && + pFnt->GetName( SW_LATIN ) != numfunc::GetDefBulletFontname() ) + { + pFnt->SetFamily( FAMILY_DONTKNOW, SW_LATIN ); + pFnt->SetName( numfunc::GetDefBulletFontname(), SW_LATIN ); + pFnt->SetStyleName( aEmptyStr, SW_LATIN ); + pFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, SW_LATIN ); + } + pFnt->SetVertical( 0, IsVertical() ); + SwFrmSwapper aSwapper( this, sal_True ); + SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() ); + aLayoutModeModifier.Modify( IsRightToLeft() ); + + pFnt->Invalidate(); + pFnt->ChgPhysFnt( pSh, *pSh->GetOut() ); + Point aPos = Frm().Pos() + Prt().Pos(); + + const SvxLRSpaceItem &rSpace = + GetTxtNode()->GetSwAttrSet().GetLRSpace(); + + if ( rSpace.GetTxtFirstLineOfst() > 0 ) + aPos.X() += rSpace.GetTxtFirstLineOfst(); + + SwSaveClip *pClip; + if( IsUndersized() ) + { + pClip = new SwSaveClip( pSh->GetOut() ); + pClip->ChgClip( rRect ); + } + else + pClip = NULL; + + aPos.Y() += pFnt->GetAscent( pSh, *pSh->GetOut() ); + + if ( GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() && + IsInDocBody() ) + { + GETGRID( FindPageFrm() ) + if ( pGrid ) + { + // center character in grid line + aPos.Y() += ( pGrid->GetBaseHeight() - + pFnt->GetHeight( pSh, *pSh->GetOut() ) ) / 2; + + if ( ! pGrid->GetRubyTextBelow() ) + aPos.Y() += pGrid->GetRubyHeight(); + } + } + + const XubString aTmp( CH_PAR ); + SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), 0, aTmp, 0, 1 ); + aDrawInf.SetLeft( rRect.Left() ); + aDrawInf.SetRight( rRect.Right() ); + aDrawInf.SetPos( aPos ); + aDrawInf.SetSpace( 0 ); + aDrawInf.SetKanaComp( 0 ); + aDrawInf.SetWrong( NULL ); + aDrawInf.SetGrammarCheck( NULL ); + aDrawInf.SetSmartTags( NULL ); // SMARTTAGS + aDrawInf.SetFrm( this ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( sal_False ); + + pFnt->_DrawText( aDrawInf ); + delete pClip; + } + delete pFnt; + return sal_True; + } + } + else + return sal_True; + return sal_False; +} + +/************************************************************************* + * SwTxtFrm::Paint() + *************************************************************************/ + +void SwTxtFrm::Paint( const SwRect &rRect ) const +{ + ResetRepaint(); + + // --> FME 2004-06-24 #i16816# tagged pdf support + ViewShell *pSh = GetShell(); + + Num_Info aNumInfo( *this ); + SwTaggedPDFHelper aTaggedPDFHelperNumbering( &aNumInfo, 0, 0, *pSh->GetOut() ); + + Frm_Info aFrmInfo( *this ); + SwTaggedPDFHelper aTaggedPDFHelperParagraph( 0, &aFrmInfo, 0, *pSh->GetOut() ); + // <-- + + DBG_LOOP_RESET; + if( !IsEmpty() || !PaintEmpty( rRect, sal_True ) ) + { +#if OSL_DEBUG_LEVEL > 1 + const SwTwips nDbgY = Frm().Top(); + (void)nDbgY; +#endif + +#ifdef DBGTXT + if( IsDbg( this ) ) + DBTXTFRM << "Paint()" << endl; +#endif + if( IsLocked() || IsHiddenNow() || ! Prt().HasArea() ) + return; + + //Kann gut sein, dass mir der IdleCollector mir die gecachten + //Informationen entzogen hat. + if( !HasPara() ) + { + ASSERT( GetValidPosFlag(), "+SwTxtFrm::Paint: no Calc()" ); + + // --> FME 2004-10-29 #i29062# pass info that we are currently + // painting. + ((SwTxtFrm*)this)->GetFormatted( true ); + // <-- + if( IsEmpty() ) + { + PaintEmpty( rRect, sal_False ); + return; + } + if( !HasPara() ) + { + ASSERT( !this, "+SwTxtFrm::Paint: missing format information" ); + return; + } + } + + // Waehrend wir painten, wollen wir nicht gestoert werden. + // Aber erst hinter dem Format() ! + SwTxtFrmLocker aLock((SwTxtFrm*)this); + + //Hier wird ggf. nur der Teil des TxtFrm ausgegeben, der sich veraendert + //hat und der in dem Bereich liegt, dessen Ausgabe angefordert wurde. + //Man kann jetzt auf die Idee kommen, dass der Bereich rRect ausgegeben + //werden _muss_ obwohl rRepaint gesetzt ist; in der Tat kann dieses + //Problem nicht formal vermieden werden. Gluecklicherweise koennen + //wir davon ausgehen, dass rRepaint immer dann leer ist, wenn der Frm + //komplett gepainted werden muss. + SwTxtLineAccess aAccess( (SwTxtFrm*)this ); + SwParaPortion *pPara = aAccess.GetPara(); + + SwRepaint &rRepaint = *(pPara->GetRepaint()); + + // Das Recycling muss abgeschaltet werden, wenn wir uns im + // FlyCntFrm befinden, weil ein DrawRect fuer die Retusche der + // Zeile aufgerufen wird. + if( rRepaint.GetOfst() ) + { + const SwFlyFrm *pFly = FindFlyFrm(); + if( pFly && pFly->IsFlyInCntFrm() ) + rRepaint.SetOfst( 0 ); + } + + // Hier holen wir uns den String fuer die Ausgabe, besonders + // die Laenge ist immer wieder interessant. + + // Rectangle + ASSERT( ! IsSwapped(), "A frame is swapped before Paint" ); + SwRect aOldRect( rRect ); + + SWAP_IF_NOT_SWAPPED( this ) + + if ( IsVertical() ) + SwitchVerticalToHorizontal( (SwRect&)rRect ); + + if ( IsRightToLeft() ) + SwitchRTLtoLTR( (SwRect&)rRect ); + + SwTxtPaintInfo aInf( (SwTxtFrm*)this, rRect ); + aInf.SetWrongList( ( (SwTxtNode*)GetTxtNode() )->GetWrong() ); + aInf.SetGrammarCheckList( ( (SwTxtNode*)GetTxtNode() )->GetGrammarCheck() ); + aInf.SetSmartTags( ( (SwTxtNode*)GetTxtNode() )->GetSmartTags() ); // SMARTTAGS + aInf.GetTxtFly()->SetTopRule(); + + SwTxtPainter aLine( (SwTxtFrm*)this, &aInf ); + // Eine Optimierung, die sich lohnt: wenn kein freifliegender Frame + // in unsere Zeile ragt, schaltet sich der SwTxtFly einfach ab: + aInf.GetTxtFly()->Relax(); + + OutputDevice* pOut = aInf.GetOut(); + const sal_Bool bOnWin = pSh->GetWin() != 0; + + SwSaveClip aClip( bOnWin || IsUndersized() ? pOut : 0 ); + + // Ausgabeschleife: Fuer jede Zeile ... (die noch zu sehen ist) ... + // rRect muss angepasst werden (Top+1, Bottom-1), weil der Iterator + // die Zeilen nahtlos aneinanderfuegt. + aLine.TwipsToLine( rRect.Top() + 1 ); + long nBottom = rRect.Bottom(); + + sal_Bool bNoPrtLine = 0 == GetMinPrtLine(); + if( !bNoPrtLine ) + { + while ( aLine.Y() < GetMinPrtLine() && aLine.Next() ) + ; + bNoPrtLine = aLine.Y() >= GetMinPrtLine(); + } + if( bNoPrtLine ) + { + do + { + //DBG_LOOP; shadows declaration above. + //resolved into: +#if OSL_DEBUG_LEVEL > 1 +#ifndef PRODUCT + DbgLoop aDbgLoop2( (const void*) this ); +#endif +#endif + aLine.DrawTextLine( rRect, aClip, IsUndersized() ); + + } while( aLine.Next() && aLine.Y() <= nBottom ); + } + + // Einmal reicht: + if( aLine.IsPaintDrop() ) + aLine.PaintDropPortion(); + + if( rRepaint.HasArea() ) + rRepaint.Clear(); + + UNDO_SWAP( this ) + (SwRect&)rRect = aOldRect; + + ASSERT( ! IsSwapped(), "A frame is swapped after Paint" ); + } +} + +void SwTxtFrm::CriticalLines( const OutputDevice& rOut, SwStripes &rStripes, + long nOffs) +{ + ASSERT( ! IsVertical() || ! IsSwapped(), + "SwTxtFrm::CriticalLines with swapped frame" ); + SWRECTFN( this ) + long nFrmHeight; + + GetFormatted(); + if( HasPara() ) + { + const long nTopMargin = (this->*fnRect->fnGetTopMargin)(); + SwStripe aStripe( (Frm().*fnRect->fnGetTop)(), nTopMargin ); + if ( nTopMargin ) + { + rStripes.Insert( aStripe, rStripes.Count() ); + // OD 06.11.2002 #104171#,#103931# - consider vertical layout + if ( bVert ) + aStripe.Y() -= nTopMargin; + else + // OD 06.11.2002 #104171#,#103931# - *add* top margin to Y. + aStripe.Y() += nTopMargin; + } + SwLineLayout* pLay = GetPara(); + do + { + SwTwips nBase = aStripe.GetY() + + ( bVert ? -pLay->GetAscent() : pLay->GetAscent() ); + + long nLogToPixBase, nLogToPixSum, nLogToPixOffs; + + if ( bVert ) + { + nLogToPixBase = rOut.LogicToPixel( Point( nBase, 0 ) ).X(); + nLogToPixSum = rOut.LogicToPixel( Point( nBase + nOffs, 0 ) ).X(); + nLogToPixOffs = -rOut.LogicToPixel( Size( nOffs, 0 ) ).Width(); + } + else + { + nLogToPixBase = rOut.LogicToPixel( Point( 0, nBase ) ).Y(); + nLogToPixSum = rOut.LogicToPixel( Point( 0, nBase - nOffs ) ).Y(); + nLogToPixOffs = rOut.LogicToPixel( Size( 0, nOffs ) ).Height(); + } + + if( nLogToPixBase != nLogToPixSum + nLogToPixOffs ) + { + aStripe.Height() = pLay->GetRealHeight(); + rStripes.Insert( aStripe, rStripes.Count() ); + } + aStripe.Y() += ( bVert ? -pLay->GetRealHeight() : + pLay->GetRealHeight() ); + pLay = pLay->GetNext(); + } while( pLay ); + + const long nBottomMargin = (this->*fnRect->fnGetBottomMargin)(); + if( nBottomMargin ) + { + + aStripe.Height() = nBottomMargin; + rStripes.Insert( aStripe, rStripes.Count() ); + } + } + else if( 0 != (nFrmHeight = (Frm().*fnRect->fnGetHeight)() )) + rStripes.Insert( SwStripe( (Frm().*fnRect->fnGetTop)(), nFrmHeight ), + rStripes.Count() ); +} + diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx new file mode 100644 index 000000000000..a5eba7301052 --- /dev/null +++ b/sw/source/core/text/guess.cxx @@ -0,0 +1,557 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: guess.cxx,v $ + * $Revision: 1.50 $ + * + * 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 <ctype.h> +#include <svx/unolingu.hxx> +#include <tools/shl.hxx> // needed for SW_MOD() macro +#include <errhdl.hxx> // ASSERTs +#include <dlelstnr.hxx> +#include <swmodule.hxx> +#include <IDocumentSettingAccess.hxx> +#include <txtcfg.hxx> +#include <guess.hxx> +#include <inftxt.hxx> +#include <pagefrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <com/sun/star/i18n/BreakType.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <unotools/charclass.hxx> +#include <porfld.hxx> + +using ::rtl::OUString; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::linguistic2; + +#define CH_FULL_BLANK 0x3000 + +/************************************************************************* + * SwTxtGuess::Guess + * + * provides information for line break calculation + * returns true if no line break has to be performed + * otherwise possible break or hyphenation position is determined + *************************************************************************/ + +sal_Bool SwTxtGuess::Guess( const SwTxtPortion& rPor, SwTxtFormatInfo &rInf, + const KSHORT nPorHeight ) +{ + nCutPos = rInf.GetIdx(); + + // Leere Strings sind immer 0 + if( !rInf.GetLen() || !rInf.GetTxt().Len() ) + return sal_False; + + ASSERT( rInf.GetIdx() < rInf.GetTxt().Len(), + "+SwTxtGuess::Guess: invalid SwTxtFormatInfo" ); + + ASSERT( nPorHeight, "+SwTxtGuess::Guess: no height" ); + + USHORT nMinSize; + USHORT nMaxSizeDiff; + + const SwScriptInfo& rSI = + ((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo(); + + USHORT nMaxComp = ( SW_CJK == rInf.GetFont()->GetActual() ) && + rSI.CountCompChg() && + ! rInf.IsMulti() && + ! rPor.InFldGrp() && + ! rPor.IsDropPortion() ? + 10000 : + 0 ; + + SwTwips nLineWidth = rInf.Width() - rInf.X(); + xub_StrLen nMaxLen = rInf.GetTxt().Len() - rInf.GetIdx(); + + if ( rInf.GetLen() < nMaxLen ) + nMaxLen = rInf.GetLen(); + + if( !nMaxLen ) + return sal_False; + + KSHORT nItalic = 0; + if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() ) + { + sal_Bool bAddItalic = sal_True; + + // do not add extra italic value if we have an active character grid + if ( rInf.SnapToGrid() ) + { + GETGRID( rInf.GetTxtFrm()->FindPageFrm() ) + bAddItalic = !pGrid || GRID_LINES_CHARS != pGrid->GetGridType(); + } + + // do not add extra italic value for an isolated blank: + if ( 1 == rInf.GetLen() && + CH_BLANK == rInf.GetTxt().GetChar( rInf.GetIdx() ) ) + bAddItalic = sal_False; + + nItalic = bAddItalic ? nPorHeight / 12 : 0; + + nLineWidth -= nItalic; + + // --> FME 2005-05-13 #i46524# LineBreak bug with italics + if ( nLineWidth < 0 ) nLineWidth = 0; + // <-- + } + + // first check if everything fits to line + if ( long ( nLineWidth ) * 2 > long ( nMaxLen ) * nPorHeight ) + { + // call GetTxtSize with maximum compression (for kanas) + rInf.GetTxtSize( &rSI, rInf.GetIdx(), nMaxLen, + nMaxComp, nMinSize, nMaxSizeDiff ); + + nBreakWidth = nMinSize; + + if ( nBreakWidth <= nLineWidth ) + { + // portion fits to line + nCutPos = rInf.GetIdx() + nMaxLen; + if( nItalic && + ( nCutPos >= rInf.GetTxt().Len() || + // --> FME 2005-05-13 #i48035# Needed for CalcFitToContent + // if first line ends with a manual line break + rInf.GetTxt().GetChar( nCutPos ) == CH_BREAK ) ) + // <-- + nBreakWidth = nBreakWidth + nItalic; + + // save maximum width for later use + if ( nMaxSizeDiff ) + rInf.SetMaxWidthDiff( (ULONG)&rPor, nMaxSizeDiff ); + + return sal_True; + } + } + + sal_Bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud(); + xub_StrLen nHyphPos = 0; + + // nCutPos is the first character not fitting to the current line + // nHyphPos is the first character not fitting to the current line, + // considering an additional "-" for hyphenation + if( bHyph ) + { + nCutPos = rInf.GetTxtBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos ); + + if ( !nHyphPos && rInf.GetIdx() ) + nHyphPos = rInf.GetIdx() - 1; + } + else + { + nCutPos = rInf.GetTxtBreak( nLineWidth, nMaxLen, nMaxComp ); + +#ifndef PRODUCT + if ( STRING_LEN != nCutPos ) + { + rInf.GetTxtSize( &rSI, rInf.GetIdx(), nCutPos - rInf.GetIdx(), + nMaxComp, nMinSize, nMaxSizeDiff ); + ASSERT( nMinSize <= nLineWidth, "What a Guess!!!" ); + } +#endif + } + + if( nCutPos > rInf.GetIdx() + nMaxLen ) + { + // second check if everything fits to line + nCutPos = nBreakPos = rInf.GetIdx() + nMaxLen - 1; + rInf.GetTxtSize( &rSI, rInf.GetIdx(), nMaxLen, nMaxComp, + nMinSize, nMaxSizeDiff ); + + nBreakWidth = nMinSize; + + // Der folgende Vergleich sollte eigenlich immer sal_True ergeben, sonst + // hat es wohl bei GetTxtBreak einen Pixel-Rundungsfehler gegeben... + if ( nBreakWidth <= nLineWidth ) + { + if( nItalic && ( nBreakPos + 1 ) >= rInf.GetTxt().Len() ) + nBreakWidth = nBreakWidth + nItalic; + + // save maximum width for later use + if ( nMaxSizeDiff ) + rInf.SetMaxWidthDiff( (ULONG)&rPor, nMaxSizeDiff ); + + return sal_True; + } + } + + // we have to trigger an underflow for a footnote portion + // which does not fit to the current line + if ( rPor.IsFtnPortion() ) + { + nBreakPos = rInf.GetIdx(); + nCutPos = rInf.GetLen(); + return sal_False; + } + + xub_StrLen nPorLen = 0; + // do not call the break iterator nCutPos is a blank + xub_Unicode cCutChar = rInf.GetTxt().GetChar( nCutPos ); + if( CH_BLANK == cCutChar || CH_FULL_BLANK == cCutChar ) + { + nBreakPos = nCutPos; + xub_StrLen nX = nBreakPos; + + // we step back until a non blank character has been found + // or there is only one more character left + while( nX && nBreakPos > rInf.GetLineStart() + 1 && + ( CH_BLANK == ( cCutChar = rInf.GetChar( --nX ) ) || + CH_FULL_BLANK == cCutChar ) ) + --nBreakPos; + + if( nBreakPos > rInf.GetIdx() ) + nPorLen = nBreakPos - rInf.GetIdx(); + while( ++nCutPos < rInf.GetTxt().Len() && + ( CH_BLANK == ( cCutChar = rInf.GetChar( nCutPos ) ) || + CH_FULL_BLANK == cCutChar ) ) + ; // nothing + + nBreakStart = nCutPos; + } + else if( pBreakIt->GetBreakIter().is() ) + { + // New: We should have a look into the last portion, if it was a + // field portion. For this, we expand the text of the field portion + // into our string. If the line break position is inside of before + // the field portion, we trigger an underflow. + + xub_StrLen nOldIdx = rInf.GetIdx(); + xub_Unicode cFldChr = 0; + +#if OSL_DEBUG_LEVEL > 1 + XubString aDebugString; +#endif + + // be careful: a field portion can be both: 0x01 (common field) + // or 0x02 (the follow of a footnode) + if ( rInf.GetLast() && rInf.GetLast()->InFldGrp() && + ! rInf.GetLast()->IsFtnPortion() && + rInf.GetIdx() > rInf.GetLineStart() && + CH_TXTATR_BREAKWORD == + ( cFldChr = rInf.GetTxt().GetChar( rInf.GetIdx() - 1 ) ) ) + { + SwFldPortion* pFld = (SwFldPortion*)rInf.GetLast(); + XubString aTxt; + pFld->GetExpTxt( rInf, aTxt ); + + if ( aTxt.Len() ) + { + nFieldDiff = aTxt.Len() - 1; + nCutPos = nCutPos + nFieldDiff; + nHyphPos = nHyphPos + nFieldDiff; + +#if OSL_DEBUG_LEVEL > 1 + aDebugString = rInf.GetTxt(); +#endif + + XubString& rOldTxt = (XubString&)rInf.GetTxt(); + rOldTxt.Erase( rInf.GetIdx() - 1, 1 ); + rOldTxt.Insert( aTxt, rInf.GetIdx() - 1 ); + rInf.SetIdx( rInf.GetIdx() + nFieldDiff ); + } + else + cFldChr = 0; + } + + LineBreakHyphenationOptions aHyphOpt; + Reference< XHyphenator > xHyph; + if( bHyph ) + { + xHyph = ::GetHyphenator(); + aHyphOpt = LineBreakHyphenationOptions( xHyph, + rInf.GetHyphValues(), nHyphPos ); + } + + // Get Language for break iterator. + // We have to switch the current language if we have a script + // change at nCutPos. Otherwise LATIN punctuation would never + // be allowed to be hanging punctuation. + // NEVER call GetLang if the string has been modified!!! + LanguageType aLang = rInf.GetFont()->GetLanguage(); + + // If we are inside a field portion, we use a temporar string which + // differs from the string at the textnode. Therefore we are not allowed + // to call the GetLang function. + if ( nCutPos && ! rPor.InFldGrp() ) + { + const CharClass& rCC = GetAppCharClass(); + + // step back until a non-punctuation character is reached + xub_StrLen nLangIndex = nCutPos; + + // If a field has been expanded right in front of us we do not + // step further than the beginning of the expanded field + // (which is the position of the field placeholder in our + // original string). + const xub_StrLen nDoNotStepOver = CH_TXTATR_BREAKWORD == cFldChr ? + rInf.GetIdx() - nFieldDiff - 1: + 0; + + while ( nLangIndex > nDoNotStepOver && + ! rCC.isLetterNumeric( rInf.GetTxt(), nLangIndex ) ) + --nLangIndex; + + // last "real" character is not inside our current portion + // we have to check the script type of the last "real" character + if ( nLangIndex < rInf.GetIdx() ) + { + USHORT nScript = pBreakIt->GetRealScriptOfText( rInf.GetTxt(), + nLangIndex ); + ASSERT( nScript, "Script is not between 1 and 4" ); + + // compare current script with script from last "real" character + if ( nScript - 1 != rInf.GetFont()->GetActual() ) + aLang = rInf.GetTxtFrm()->GetTxtNode()->GetLang( + CH_TXTATR_BREAKWORD == cFldChr ? + nDoNotStepOver : + nLangIndex, 0, nScript ); + } + } + + const ForbiddenCharacters aForbidden( + *rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->getForbiddenCharacters( aLang, true ) ); + + const sal_Bool bAllowHanging = rInf.IsHanging() && ! rInf.IsMulti() && + ! rPor.InFldGrp(); + + LineBreakUserOptions aUserOpt( + aForbidden.beginLine, aForbidden.endLine, + rInf.HasForbiddenChars(), bAllowHanging, sal_False ); + + //! 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(); + + // !!! We must have a local copy of the locale, because inside + // getLineBreak the LinguEventListener can trigger a new formatting, + // which can corrupt the locale pointer inside pBreakIt. + const lang::Locale aLocale = pBreakIt->GetLocale( aLang ); + + // determines first possible line break from nRightPos to + // start index of current line + LineBreakResults aResult = pBreakIt->GetBreakIter()->getLineBreak( + rInf.GetTxt(), nCutPos, aLocale, + rInf.GetLineStart(), aHyphOpt, aUserOpt ); + + nBreakPos = (xub_StrLen)aResult.breakIndex; + + // if we are formatting multi portions we want to allow line breaks + // at the border between single line and multi line portion + // we have to be carefull with footnote portions, they always come in + // with an index 0 + if ( nBreakPos < rInf.GetLineStart() && rInf.IsFirstMulti() && + ! rInf.IsFtnInside() ) + nBreakPos = rInf.GetLineStart(); + + nBreakStart = nBreakPos; + + bHyph = BreakType::HYPHENATION == aResult.breakType; + + if ( bHyph && nBreakPos != STRING_LEN) + { + // found hyphenation position within line + // nBreakPos is set to the hyphenation position + xHyphWord = aResult.rHyphenatedWord; + nBreakPos += xHyphWord->getHyphenationPos() + 1; + +#if OSL_DEBUG_LEVEL > 1 + // e.g., Schif-fahrt, referes to our string + const String aWord = xHyphWord->getWord(); + // e.g., Schiff-fahrt, referes to the word after hyphenation + const String aHyphenatedWord = xHyphWord->getHyphenatedWord(); + // e.g., Schif-fahrt: 5, referes to our string + const USHORT nHyphenationPos = xHyphWord->getHyphenationPos(); + (void)nHyphenationPos; + // e.g., Schiff-fahrt: 6, referes to the word after hyphenation + const USHORT nHyphenPos = xHyphWord->getHyphenPos(); + (void)nHyphenPos; +#endif + + // if not in interactive mode, we have to break behind a soft hyphen + if ( ! rInf.IsInterHyph() && rInf.GetIdx() ) + { + const long nSoftHyphPos = + xHyphWord->getWord().indexOf( CHAR_SOFTHYPHEN ); + + if ( nSoftHyphPos >= 0 && + nBreakStart + nSoftHyphPos <= nBreakPos && + nBreakPos > rInf.GetLineStart() ) + nBreakPos = rInf.GetIdx() - 1; + } + + if( nBreakPos >= rInf.GetIdx() ) + { + nPorLen = nBreakPos - rInf.GetIdx(); + if( '-' == rInf.GetTxt().GetChar( nBreakPos - 1 ) ) + xHyphWord = NULL; + } + } + else if ( !bHyph && nBreakPos >= rInf.GetLineStart() ) + { + ASSERT( nBreakPos != STRING_LEN, "we should have found a break pos" ); + + // found break position within line + xHyphWord = NULL; + + // check, if break position is soft hyphen and an underflow + // has to be triggered + if( nBreakPos > rInf.GetLineStart() && rInf.GetIdx() && + CHAR_SOFTHYPHEN == rInf.GetTxt().GetChar( nBreakPos - 1 ) ) + nBreakPos = rInf.GetIdx() - 1; + + // Delete any blanks at the end of a line, but be careful: + // If a field has been expanded, we do not want to delete any + // blanks inside the field portion. This would cause an unwanted + // underflow + xub_StrLen nX = nBreakPos; + while( nX > rInf.GetLineStart() && + ( CH_TXTATR_BREAKWORD != cFldChr || nX > rInf.GetIdx() ) && + ( CH_BLANK == rInf.GetChar( --nX ) || + CH_FULL_BLANK == rInf.GetChar( nX ) ) ) + nBreakPos = nX; + if( nBreakPos > rInf.GetIdx() ) + nPorLen = nBreakPos - rInf.GetIdx(); + } + else + { + // no line break found, setting nBreakPos to STRING_LEN + // causes a break cut + nBreakPos = STRING_LEN; + ASSERT( nCutPos >= rInf.GetIdx(), "Deep cut" ); + nPorLen = nCutPos - rInf.GetIdx(); + } + + if( nBreakPos > nCutPos && nBreakPos != STRING_LEN ) + { + const xub_StrLen nHangingLen = nBreakPos - nCutPos; + SwPosSize aTmpSize = rInf.GetTxtSize( &rSI, nCutPos, + nHangingLen, 0 ); + ASSERT( !pHanging, "A hanging portion is hanging around" ); + pHanging = new SwHangingPortion( aTmpSize ); + pHanging->SetLen( nHangingLen ); + nPorLen = nCutPos - rInf.GetIdx(); + } + + // If we expanded a field, we must repair the original string. + // In case we do not trigger an underflow, we correct the nBreakPos + // value, but we cannot correct the nBreakStart value: + // If we have found a hyphenation position, nBreakStart can lie before + // the field. + if ( CH_TXTATR_BREAKWORD == cFldChr ) + { + if ( nBreakPos < rInf.GetIdx() ) + nBreakPos = nOldIdx - 1; + else if ( STRING_LEN != nBreakPos ) + { + ASSERT( nBreakPos >= nFieldDiff, "I've got field trouble!" ); + nBreakPos = nBreakPos - nFieldDiff; + } + + ASSERT( nCutPos >= rInf.GetIdx() && nCutPos >= nFieldDiff, + "I've got field trouble, part2!" ); + nCutPos = nCutPos - nFieldDiff; + + XubString& rOldTxt = (XubString&)rInf.GetTxt(); + rOldTxt.Erase( nOldIdx - 1, nFieldDiff + 1 ); + rOldTxt.Insert( cFldChr, nOldIdx - 1 ); + rInf.SetIdx( nOldIdx ); + +#if OSL_DEBUG_LEVEL > 1 + ASSERT( aDebugString == rInf.GetTxt(), + "Somebody, somebody, somebody put something in my string" ); +#endif + } + } + + if( nPorLen ) + { + rInf.GetTxtSize( &rSI, rInf.GetIdx(), nPorLen, + nMaxComp, nMinSize, nMaxSizeDiff ); + + // save maximum width for later use + if ( nMaxSizeDiff ) + rInf.SetMaxWidthDiff( (ULONG)&rPor, nMaxSizeDiff ); + + nBreakWidth = nItalic + nMinSize; + } + else + nBreakWidth = 0; + + if( pHanging ) + nBreakPos = nCutPos; + + return sal_False; +} + +/************************************************************************* + * SwTxtGuess::AlternativeSpelling + *************************************************************************/ + +// returns true if word at position nPos has a diffenrent spelling +// if hyphenated at this position (old german spelling) + +sal_Bool SwTxtGuess::AlternativeSpelling( const SwTxtFormatInfo &rInf, + const xub_StrLen nPos ) +{ + // get word boundaries + xub_StrLen nWordLen; + + Boundary aBound = + pBreakIt->GetBreakIter()->getWordBoundary( rInf.GetTxt(), nPos, + pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ), + WordType::DICTIONARY_WORD, sal_True ); + nBreakStart = (xub_StrLen)aBound.startPos; + nWordLen = static_cast<xub_StrLen>(aBound.endPos - nBreakStart); + + // if everything else fails, we want to cut at nPos + nCutPos = nPos; + + XubString aTxt( rInf.GetTxt().Copy( nBreakStart, nWordLen ) ); + + // check, if word has alternative spelling + Reference< XHyphenator > xHyph( ::GetHyphenator() ); + ASSERT( xHyph.is(), "Hyphenator is missing"); + //! subtract 1 since the UNO-interface is 0 based + xHyphWord = xHyph->queryAlternativeSpelling( OUString(aTxt), + pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ), + nPos - nBreakStart, rInf.GetHyphValues() ); + return xHyphWord.is() && xHyphWord->isAlternativeSpelling(); +} + diff --git a/sw/source/core/text/guess.hxx b/sw/source/core/text/guess.hxx new file mode 100644 index 000000000000..6e3fd9293ad6 --- /dev/null +++ b/sw/source/core/text/guess.hxx @@ -0,0 +1,76 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: guess.hxx,v $ + * $Revision: 1.12 $ + * + * 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. + * + ************************************************************************/ +#ifndef _GUESS_HXX +#define _GUESS_HXX +#include <com/sun/star/linguistic2/XHyphenatedWord.hpp> + +#include "txttypes.hxx" +#include "breakit.hxx" +#include "porrst.hxx" // SwHangingPortion + +class SwTxtFormatInfo; + +/************************************************************************* + * class SwTxtGuess + *************************************************************************/ + +class SwTxtGuess +{ + ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XHyphenatedWord > xHyphWord; + SwHangingPortion *pHanging; // for hanging punctuation + xub_StrLen nCutPos; // this character doesn't fit + xub_StrLen nBreakStart; // start index of word containing line break + xub_StrLen nBreakPos; // start index of break position + xub_StrLen nFieldDiff; // absolut positions can be wrong if we + // a field in the text has been expanded + KSHORT nBreakWidth; // width of the broken portion +public: + inline SwTxtGuess(): pHanging( NULL ), nCutPos(0), nBreakStart(0), + nBreakPos(0), nFieldDiff(0), nBreakWidth(0) + { } + ~SwTxtGuess() { delete pHanging; } + + // true, if current portion still fits to current line + sal_Bool Guess( const SwTxtPortion& rPor, SwTxtFormatInfo &rInf, + const KSHORT nHeight ); + sal_Bool AlternativeSpelling( const SwTxtFormatInfo &rInf, const xub_StrLen nPos ); + + inline SwHangingPortion* GetHangingPortion() const { return pHanging; } + inline void ClearHangingPortion() { pHanging = NULL; } + inline KSHORT BreakWidth() const { return nBreakWidth; } + inline xub_StrLen CutPos() const { return nCutPos; } + inline xub_StrLen BreakStart() const { return nBreakStart; } + inline xub_StrLen BreakPos() const {return nBreakPos; } + inline xub_StrLen FieldDiff() const {return nFieldDiff; } + inline ::com::sun::star::uno::Reference< ::com::sun::star::linguistic2::XHyphenatedWord > HyphWord() const + { return xHyphWord; } +}; + +#endif diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx new file mode 100644 index 000000000000..fea63bf2dca0 --- /dev/null +++ b/sw/source/core/text/inftxt.cxx @@ -0,0 +1,1967 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: inftxt.cxx,v $ + * $Revision: 1.123.20.1 $ + * + * 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 <com/sun/star/uno/Sequence.h> +#include <svtools/linguprops.hxx> +#include <svtools/lingucfg.hxx> +#include <hintids.hxx> +#include <sfx2/printer.hxx> +#include <svx/hyznitem.hxx> +#include <svx/escpitem.hxx> +#include <svx/hngpnctitem.hxx> +#include <svx/scriptspaceitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/splwrap.hxx> +#include <svx/pgrditem.hxx> +// --> OD 2008-01-17 #newlistlevelattrs# +#ifndef _SVX_TSTPITEM_HXX +#include <svx/tstpitem.hxx> +#endif +// <-- + +#include <SwSmartTagMgr.hxx> +#include <linguistic/lngprops.hxx> +#include <svx/unolingu.hxx> +#include <breakit.hxx> +#include <svx/forbiddenruleitem.hxx> +#include <txatbase.hxx> +#include <fmtinfmt.hxx> +#include <swmodule.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <viewsh.hxx> // ViewShell +#include <viewopt.hxx> // SwViewOptions +#include <frmtool.hxx> // DrawGraphic +#include <IDocumentSettingAccess.hxx> +#ifndef IDOCUMENTDEVICEACCESS_HXX_INCLUDED +#include <IDocumentDeviceAccess.hxx> +#endif +#include <paratr.hxx> // SwFmtDrop +#include <rootfrm.hxx> // SwRootFrm +#include <inftxt.hxx> // SwTxtInfo +#include <blink.hxx> // SwBlink +#include <noteurl.hxx> // SwNoteURL +#include <porftn.hxx> // SwFtnPortion +#include <porrst.hxx> // SwHangingPortion +#include <itratr.hxx> +#include <accessibilityoptions.hxx> +#include <wrong.hxx> +#include <doc.hxx> +#include <pam.hxx> +#include <SwGrammarMarkUp.hxx> + +// --> FME 2004-06-08 #i12836# enhanced pdf export +#include <EnhancedPDFExportHelper.hxx> +// <-- + +#include <unomid.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + + +#define CHAR_UNDERSCORE ((sal_Unicode)0x005F) +#define CHAR_LEFT_ARROW ((sal_Unicode)0x25C0) +#define CHAR_RIGHT_ARROW ((sal_Unicode)0x25B6) +#define CHAR_TAB ((sal_Unicode)0x2192) +#define CHAR_TAB_RTL ((sal_Unicode)0x2190) +#define CHAR_LINEBREAK ((sal_Unicode)0x21B5) +#define CHAR_LINEBREAK_RTL ((sal_Unicode)0x21B3) + +#define DRAW_SPECIAL_OPTIONS_CENTER 1 +#define DRAW_SPECIAL_OPTIONS_ROTATE 2 + +// --> OD 2006-06-27 #b6440955# +// variable moved to class <numfunc:GetDefBulletConfig> +//extern const sal_Char __FAR_DATA sBulletFntName[]; +namespace numfunc +{ + extern const String& GetDefBulletFontname(); + extern bool IsDefBulletFontUserDefined(); +} +// <-- + +#ifndef PRODUCT +// Test2: WYSIWYG++ +// Test4: WYSIWYG debug +static sal_Bool bDbgLow = sal_False; +#endif + +#ifndef PRODUCT + +sal_Bool SwTxtSizeInfo::IsOptCalm() const { return !GetOpt().IsTest3(); } + +sal_Bool SwTxtSizeInfo::IsOptLow() const { return bDbgLow; } + +sal_Bool SwTxtSizeInfo::IsOptDbg() const { return GetOpt().IsTest4(); } + +sal_Bool SwTxtSizeInfo::IsOptTest1() const { return GetOpt().IsTest1(); } + +sal_Bool SwTxtSizeInfo::IsOptTest2() const { return GetOpt().IsTest2(); } + +sal_Bool SwTxtSizeInfo::IsOptTest3() const { return GetOpt().IsTest3(); } + +sal_Bool SwTxtSizeInfo::IsOptTest4() const { return GetOpt().IsTest4(); } + +sal_Bool SwTxtSizeInfo::IsOptTest5() const { return GetOpt().IsTest5(); } + +sal_Bool SwTxtSizeInfo::IsOptTest6() const { return GetOpt().IsTest6(); } + +sal_Bool SwTxtSizeInfo::IsOptTest7() const { return GetOpt().IsTest7(); } + +sal_Bool SwTxtSizeInfo::IsOptTest8() const { return GetOpt().IsTest8(); } + +#endif + +/************************************************************************* + * SwLineInfo::SwLineInfo() + *************************************************************************/ + +// --> OD 2008-01-17 #newlistlevelattrs# +SwLineInfo::SwLineInfo() + : pRuler( 0 ), + pSpace( 0 ), + nVertAlign( 0 ), + nDefTabStop( 0 ), + bListTabStopIncluded( false ), + nListTabStopPosition( 0 ) +{ +} + +SwLineInfo::~SwLineInfo() +{ + delete pRuler; +} +void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet, + const SwTxtNode& rTxtNode ) +// <-- +{ + // --> OD 2008-01-17 #newlistlevelattrs# +// pRuler = &rAttrSet.GetTabStops(); + delete pRuler; + pRuler = new SvxTabStopItem( rAttrSet.GetTabStops() ); + if ( rTxtNode.GetListTabStopPosition( nListTabStopPosition ) ) + { + bListTabStopIncluded = true; + + // insert the list tab stop into SvxTabItem instance <pRuler> + const SvxTabStop aListTabStop( nListTabStopPosition, + SVX_TAB_ADJUST_LEFT ); + pRuler->Insert( aListTabStop ); + + // remove default tab stops, which are before the inserted list tab stop + for ( USHORT i = 0; i < pRuler->Count(); i++ ) + { + if ( (*pRuler)[i].GetTabPos() < nListTabStopPosition && + (*pRuler)[i].GetAdjustment() == SVX_TAB_ADJUST_DEFAULT ) + { + pRuler->Remove(i); + continue; + } + } + } + // <-- + // --> OD 2008-02-15 #newlistlevelattrs# + if ( !rTxtNode.getIDocumentSettingAccess()->get(IDocumentSettingAccess::TABS_RELATIVE_TO_INDENT) ) + { + // remove default tab stop at position 0 + for ( USHORT i = 0; i < pRuler->Count(); i++ ) + { + if ( (*pRuler)[i].GetTabPos() == 0 && + (*pRuler)[i].GetAdjustment() == SVX_TAB_ADJUST_DEFAULT ) + { + pRuler->Remove(i); + break; + } + } + } + // <-- + pSpace = &rAttrSet.GetLineSpacing(); + nVertAlign = rAttrSet.GetParaVertAlign().GetValue(); + nDefTabStop = MSHRT_MAX; +} + +/************************************************************************* + * SwTxtInfo::CtorInitTxtInfo() + *************************************************************************/ + +void SwTxtInfo::CtorInitTxtInfo( SwTxtFrm *pFrm ) +{ + pPara = pFrm->GetPara(); + nTxtStart = pFrm->GetOfst(); + if( !pPara ) + { + ASSERT( pPara, "+SwTxtInfo::CTOR: missing paragraph information" ); + pFrm->Format(); + pPara = pFrm->GetPara(); + } +} + +SwTxtInfo::SwTxtInfo( const SwTxtInfo &rInf ) + : pPara( ((SwTxtInfo&)rInf).GetParaPortion() ), + nTxtStart( rInf.GetTxtStart() ) +{ } + + +#ifndef PRODUCT +/************************************************************************* + * ChkOutDev() + *************************************************************************/ + +void ChkOutDev( const SwTxtSizeInfo &rInf ) +{ + if ( !rInf.GetVsh() ) + return; + + const OutputDevice* pOut = rInf.GetOut(); + const OutputDevice* pRef = rInf.GetRefDev(); + ASSERT( pOut && pRef, "ChkOutDev: invalid output devices" ) +} +#endif // PRODUCT + + +inline xub_StrLen GetMinLen( const SwTxtSizeInfo &rInf ) +{ + const xub_StrLen nInfLen = rInf.GetIdx() + rInf.GetLen(); + return Min( rInf.GetTxt().Len(), nInfLen ); +} + + +SwTxtSizeInfo::SwTxtSizeInfo( const SwTxtSizeInfo &rNew ) + : SwTxtInfo( rNew ), + pKanaComp(((SwTxtSizeInfo&)rNew).GetpKanaComp()), + pVsh(((SwTxtSizeInfo&)rNew).GetVsh()), + pOut(((SwTxtSizeInfo&)rNew).GetOut()), + pRef(((SwTxtSizeInfo&)rNew).GetRefDev()), + pFnt(((SwTxtSizeInfo&)rNew).GetFont()), + pUnderFnt(((SwTxtSizeInfo&)rNew).GetUnderFnt()), + pFrm(rNew.pFrm), + pOpt(&rNew.GetOpt()), + pTxt(&rNew.GetTxt()), + nIdx(rNew.GetIdx()), + nLen(rNew.GetLen()), + nKanaIdx( rNew.GetKanaIdx() ), + bOnWin( rNew.OnWin() ), + bNotEOL( rNew.NotEOL() ), + bURLNotify( rNew.URLNotify() ), + bStopUnderFlow( rNew.StopUnderFlow() ), + bFtnInside( rNew.IsFtnInside() ), + bMulti( rNew.IsMulti() ), + bFirstMulti( rNew.IsFirstMulti() ), + bRuby( rNew.IsRuby() ), + bHanging( rNew.IsHanging() ), + bScriptSpace( rNew.HasScriptSpace() ), + bForbiddenChars( rNew.HasForbiddenChars() ), + bSnapToGrid( rNew.SnapToGrid() ), + nDirection( rNew.GetDirection() ) +{ +#ifndef PRODUCT + ChkOutDev( *this ); +#endif +} + +void SwTxtSizeInfo::CtorInitTxtSizeInfo( SwTxtFrm *pFrame, SwFont *pNewFnt, + const xub_StrLen nNewIdx, const xub_StrLen nNewLen ) +{ + pKanaComp = NULL; + nKanaIdx = 0; + pFrm = pFrame; + CtorInitTxtInfo( pFrm ); + const SwTxtNode *pNd = pFrm->GetTxtNode(); + pVsh = pFrm->GetShell(); + + // Get the output and reference device + if ( pVsh ) + { + pOut = pVsh->GetOut(); + pRef = &pVsh->GetRefDev(); + bOnWin = pVsh->GetWin() || OUTDEV_WINDOW == pOut->GetOutDevType(); + } + else + { + //Zugriff ueber StarONE, es muss keine Shell existieren oder aktiv sein. + if ( pNd->getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE) ) + { + //in Ermangelung eines Besseren kann hier ja wohl nur noch das + //AppWin genommen werden? + pOut = GetpApp()->GetDefaultDevice(); + } + else + pOut = pNd->getIDocumentDeviceAccess()->getPrinter( false ); + + pRef = pOut; + } + +#ifndef PRODUCT + ChkOutDev( *this ); +#endif + + // Set default layout mode ( LTR or RTL ). + if ( pFrm->IsRightToLeft() ) + { + pOut->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_BIDI_RTL ); + pRef->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_BIDI_RTL ); + nDirection = DIR_RIGHT2LEFT; + } + else + { + pOut->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG ); + pRef->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG ); + nDirection = DIR_LEFT2RIGHT; + } + +/* LanguageType eLang; + const SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions(); + if ( SvtCTLOptions::NUMERALS_HINDI == rCTLOptions.GetCTLTextNumerals() ) + eLang = LANGUAGE_ARABIC_SAUDI_ARABIA; + else if ( SvtCTLOptions::NUMERALS_ARABIC == rCTLOptions.GetCTLTextNumerals() ) + eLang = LANGUAGE_ENGLISH; + else + eLang = (LanguageType)::GetAppLanguage(); + + pOut->SetDigitLanguage( eLang ); + pRef->SetDigitLanguage( eLang );*/ + + // + // The Options + // + pOpt = pVsh ? + pVsh->GetViewOptions() : + SW_MOD()->GetViewOption( pNd->getIDocumentSettingAccess()->get(IDocumentSettingAccess::HTML_MODE) ); //Options vom Module wg. StarONE + + // bURLNotify wird gesetzt, wenn MakeGraphic dies vorbereitet + // TODO: Aufdr?seln + bURLNotify = pNoteURL && !bOnWin; + + SetSnapToGrid( pNd->GetSwAttrSet().GetParaGrid().GetValue() && + pFrm->IsInDocBody() ); + + pFnt = pNewFnt; + pUnderFnt = 0; + pTxt = &pNd->GetTxt(); + + nIdx = nNewIdx; + nLen = nNewLen; + bNotEOL = sal_False; + bStopUnderFlow = bFtnInside = sal_False; + bMulti = bFirstMulti = bRuby = bHanging = bScriptSpace = + bForbiddenChars = sal_False; + + SetLen( GetMinLen( *this ) ); +} + +SwTxtSizeInfo::SwTxtSizeInfo( const SwTxtSizeInfo &rNew, const XubString &rTxt, + const xub_StrLen nIndex, const xub_StrLen nLength ) + : SwTxtInfo( rNew ), + pKanaComp(((SwTxtSizeInfo&)rNew).GetpKanaComp()), + pVsh(((SwTxtSizeInfo&)rNew).GetVsh()), + pOut(((SwTxtSizeInfo&)rNew).GetOut()), + pRef(((SwTxtSizeInfo&)rNew).GetRefDev()), + pFnt(((SwTxtSizeInfo&)rNew).GetFont()), + pUnderFnt(((SwTxtSizeInfo&)rNew).GetUnderFnt()), + pFrm( rNew.pFrm ), + pOpt(&rNew.GetOpt()), + pTxt(&rTxt), + nIdx(nIndex), + nLen(nLength), + nKanaIdx( rNew.GetKanaIdx() ), + bOnWin( rNew.OnWin() ), + bNotEOL( rNew.NotEOL() ), + bURLNotify( rNew.URLNotify() ), + bStopUnderFlow( rNew.StopUnderFlow() ), + bFtnInside( rNew.IsFtnInside() ), + bMulti( rNew.IsMulti() ), + bFirstMulti( rNew.IsFirstMulti() ), + bRuby( rNew.IsRuby() ), + bHanging( rNew.IsHanging() ), + bScriptSpace( rNew.HasScriptSpace() ), + bForbiddenChars( rNew.HasForbiddenChars() ), + bSnapToGrid( rNew.SnapToGrid() ), + nDirection( rNew.GetDirection() ) +{ +#ifndef PRODUCT + ChkOutDev( *this ); +#endif + SetLen( GetMinLen( *this ) ); +} + +/************************************************************************* + * SwTxtSizeInfo::SelectFont() + *************************************************************************/ + +void SwTxtSizeInfo::SelectFont() +{ + // 8731: Der Weg muss ueber ChgPhysFnt gehen, sonst geraet + // der FontMetricCache durcheinander. In diesem Fall steht pLastMet + // auf dem alten Wert. + // Falsch: GetOut()->SetFont( GetFont()->GetFnt() ); + GetFont()->Invalidate(); + GetFont()->ChgPhysFnt( pVsh, *GetOut() ); +} + +/************************************************************************* + * SwTxtSizeInfo::NoteAnimation() + *************************************************************************/ + +void SwTxtSizeInfo::NoteAnimation() const +{ + if( OnWin() ) + SwRootFrm::FlushVout(); + + ASSERT( pOut == pVsh->GetOut(), + "SwTxtSizeInfo::NoteAnimation() changed pOut" ) +} + +/************************************************************************* + * SwTxtSizeInfo::GetTxtSize() + *************************************************************************/ + +SwPosSize SwTxtSizeInfo::GetTxtSize( OutputDevice* pOutDev, + const SwScriptInfo* pSI, + const XubString& rTxt, + const xub_StrLen nIndex, + const xub_StrLen nLength, + const USHORT nComp ) const +{ + SwDrawTextInfo aDrawInf( pVsh, *pOutDev, pSI, rTxt, nIndex, nLength ); + aDrawInf.SetFrm( pFrm ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( SnapToGrid() ); + aDrawInf.SetKanaComp( nComp ); + SwPosSize aSize = pFnt->_GetTxtSize( aDrawInf ); + return aSize; +} + +/************************************************************************* + * SwTxtSizeInfo::GetTxtSize() + *************************************************************************/ + +SwPosSize SwTxtSizeInfo::GetTxtSize() const +{ + const SwScriptInfo& rSI = + ( (SwParaPortion*)GetParaPortion() )->GetScriptInfo(); + + // in some cases, compression is not allowed or surpressed for + // performance reasons + USHORT nComp =( SW_CJK == GetFont()->GetActual() && + rSI.CountCompChg() && + ! IsMulti() ) ? + GetKanaComp() : + 0 ; + + SwDrawTextInfo aDrawInf( pVsh, *pOut, &rSI, *pTxt, nIdx, nLen ); + aDrawInf.SetFrm( pFrm ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( SnapToGrid() ); + aDrawInf.SetKanaComp( nComp ); + return pFnt->_GetTxtSize( aDrawInf ); +} + +/************************************************************************* + * SwTxtSizeInfo::GetTxtSize() + *************************************************************************/ + +void SwTxtSizeInfo::GetTxtSize( const SwScriptInfo* pSI, const xub_StrLen nIndex, + const xub_StrLen nLength, const USHORT nComp, + USHORT& nMinSize, USHORT& nMaxSizeDiff ) const +{ + SwDrawTextInfo aDrawInf( pVsh, *pOut, pSI, *pTxt, nIndex, nLength ); + aDrawInf.SetFrm( pFrm ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( SnapToGrid() ); + aDrawInf.SetKanaComp( nComp ); + SwPosSize aSize = pFnt->_GetTxtSize( aDrawInf ); + nMaxSizeDiff = (USHORT)aDrawInf.GetKanaDiff(); + nMinSize = aSize.Width(); +} + +/************************************************************************* + * SwTxtSizeInfo::GetTxtBreak() + *************************************************************************/ + +xub_StrLen SwTxtSizeInfo::GetTxtBreak( const long nLineWidth, + const xub_StrLen nMaxLen, + const USHORT nComp ) const +{ + const SwScriptInfo& rScriptInfo = + ( (SwParaPortion*)GetParaPortion() )->GetScriptInfo(); + + ASSERT( pRef == pOut, "GetTxtBreak is supposed to use the RefDev" ) + SwDrawTextInfo aDrawInf( pVsh, *pOut, &rScriptInfo, + *pTxt, GetIdx(), nMaxLen ); + aDrawInf.SetFrm( pFrm ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( SnapToGrid() ); + aDrawInf.SetKanaComp( nComp ); + aDrawInf.SetHyphPos( 0 ); + + return pFnt->GetTxtBreak( aDrawInf, nLineWidth ); +} + +/************************************************************************* + * SwTxtSizeInfo::GetTxtBreak() + *************************************************************************/ + +xub_StrLen SwTxtSizeInfo::GetTxtBreak( const long nLineWidth, + const xub_StrLen nMaxLen, + const USHORT nComp, + xub_StrLen& rExtraCharPos ) const +{ + const SwScriptInfo& rScriptInfo = + ( (SwParaPortion*)GetParaPortion() )->GetScriptInfo(); + + ASSERT( pRef == pOut, "GetTxtBreak is supposed to use the RefDev" ) + SwDrawTextInfo aDrawInf( pVsh, *pOut, &rScriptInfo, + *pTxt, GetIdx(), nMaxLen ); + aDrawInf.SetFrm( pFrm ); + aDrawInf.SetFont( pFnt ); + aDrawInf.SetSnapToGrid( SnapToGrid() ); + aDrawInf.SetKanaComp( nComp ); + aDrawInf.SetHyphPos( &rExtraCharPos ); + + return pFnt->GetTxtBreak( aDrawInf, nLineWidth ); +} + +/************************************************************************* + * SwTxtPaintInfo::CtorInitTxtPaintInfo() + *************************************************************************/ + +void SwTxtPaintInfo::CtorInitTxtPaintInfo( SwTxtFrm *pFrame, const SwRect &rPaint ) +{ + CtorInitTxtSizeInfo( pFrame ); + aTxtFly.CtorInitTxtFly( pFrame ), + aPaintRect = rPaint; + nSpaceIdx = 0; + pSpaceAdd = NULL; + pWrongList = NULL; + pGrammarCheckList = NULL; + pSmartTags = NULL; // SMARTTAGS + +#ifdef PRODUCT + pBrushItem = 0; +#else + pBrushItem = ((SvxBrushItem*)-1); +#endif +} + +SwTxtPaintInfo::SwTxtPaintInfo( const SwTxtPaintInfo &rInf, const XubString &rTxt ) + : SwTxtSizeInfo( rInf, rTxt ), + pWrongList( rInf.GetpWrongList() ), + pGrammarCheckList( rInf.GetGrammarCheckList() ), + pSmartTags( rInf.GetSmartTags() ), // SMARTTAGS + pSpaceAdd( rInf.GetpSpaceAdd() ), + pBrushItem( rInf.GetBrushItem() ), + aTxtFly( *rInf.GetTxtFly() ), + aPos( rInf.GetPos() ), + aPaintRect( rInf.GetPaintRect() ), + nSpaceIdx( rInf.GetSpaceIdx() ) +{ } + +SwTxtPaintInfo::SwTxtPaintInfo( const SwTxtPaintInfo &rInf ) + : SwTxtSizeInfo( rInf ), + pWrongList( rInf.GetpWrongList() ), + pGrammarCheckList( rInf.GetGrammarCheckList() ), + pSmartTags( rInf.GetSmartTags() ), // SMARTTAGS + pSpaceAdd( rInf.GetpSpaceAdd() ), + pBrushItem( rInf.GetBrushItem() ), + aTxtFly( *rInf.GetTxtFly() ), + aPos( rInf.GetPos() ), + aPaintRect( rInf.GetPaintRect() ), + nSpaceIdx( rInf.GetSpaceIdx() ) +{ } + +extern Color aGlobalRetoucheColor; + +/************************************************************************* + * lcl_IsDarkBackground + * + * Returns if the current background color is dark. + *************************************************************************/ + +sal_Bool lcl_IsDarkBackground( const SwTxtPaintInfo& rInf ) +{ + const Color* pCol = rInf.GetFont()->GetBackColor(); + 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 background color, if there is a background brush and + /// its color is *not* "no fill"/"auto fill". + if( rInf.GetTxtFrm()->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; + } + + + if( !pCol ) + pCol = &aGlobalRetoucheColor; + + return pCol->IsDark(); +} + +/************************************************************************* + * SwTxtPaintInfo::_DrawText() + *************************************************************************/ + +void SwTxtPaintInfo::_DrawText( const XubString &rText, const SwLinePortion &rPor, + const xub_StrLen nStart, const xub_StrLen nLength, + const sal_Bool bKern, const sal_Bool bWrong, + const sal_Bool bSmartTag, + const sal_Bool bGrammarCheck ) // SMARTTAGS +{ + if( !nLength ) + return; + + if( GetFont()->IsBlink() && OnWin() && rPor.Width() ) + { + // check if accessibility options allow blinking portions: + const ViewShell* pSh = GetTxtFrm()->GetShell(); + if ( pSh && ! pSh->GetAccessibilityOptions()->IsStopAnimatedText() && + ! pSh->IsPreView() ) + { + if( !pBlink ) + pBlink = new SwBlink(); + + Point aPoint( aPos ); + + if ( GetTxtFrm()->IsRightToLeft() ) + GetTxtFrm()->SwitchLTRtoRTL( aPoint ); + + if ( TEXT_LAYOUT_BIDI_STRONG != GetOut()->GetLayoutMode() ) + aPoint.X() -= rPor.Width(); + + if ( GetTxtFrm()->IsVertical() ) + GetTxtFrm()->SwitchHorizontalToVertical( aPoint ); + + pBlink->Insert( aPoint, &rPor, GetTxtFrm(), pFnt->GetOrientation() ); + + if( !pBlink->IsVisible() ) + return; + } + else + { + delete pBlink; + pBlink = NULL; + } + } + + // The SwScriptInfo is useless if we are inside a field portion + SwScriptInfo* pSI = 0; + if ( ! rPor.InFldGrp() ) + pSI = &GetParaPortion()->GetScriptInfo(); + + // in some cases, kana compression is not allowed or surpressed for + // performance reasons + USHORT nComp = 0; + if ( ! IsMulti() ) + nComp = GetKanaComp(); + + sal_Bool bCfgIsAutoGrammar = sal_False; + SvtLinguConfig().GetProperty( C2U( UPN_IS_GRAMMAR_AUTO ) ) >>= bCfgIsAutoGrammar; + const sal_Bool bBullet = OnWin() && GetOpt().IsBlank() && IsNoSymbol(); + const sal_Bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell(); + const sal_Bool bTmpGrammarCheck = bGrammarCheck && OnWin() && bCfgIsAutoGrammar && GetOpt().IsOnlineSpell(); + const sal_Bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled(); // SMARTTAGS + + ASSERT( GetParaPortion(), "No paragraph!"); + SwDrawTextInfo aDrawInf( pFrm->GetShell(), *pOut, pSI, rText, nStart, nLength, + rPor.Width(), bBullet ); + + aDrawInf.SetLeft( GetPaintRect().Left() ); + aDrawInf.SetRight( GetPaintRect().Right() ); + aDrawInf.SetUnderFnt( pUnderFnt ); + + const long nSpaceAdd = ( rPor.IsBlankPortion() || rPor.IsDropPortion() || + rPor.InNumberGrp() ) ? 0 : GetSpaceAdd(); + if ( nSpaceAdd ) + { + xub_StrLen nCharCnt; + // --> FME 2005-04-04 #i41860# Thai justified alignemt needs some + // additional information: + aDrawInf.SetNumberOfBlanks( rPor.InTxtGrp() ? + static_cast<const SwTxtPortion&>(rPor).GetSpaceCnt( *this, nCharCnt ) : + 0 ); + // <-- + } + + aDrawInf.SetSpace( nSpaceAdd ); + aDrawInf.SetKanaComp( nComp ); + + // the font is used to identify the current script via nActual + aDrawInf.SetFont( pFnt ); + // the frame is used to identify the orientation + aDrawInf.SetFrm( GetTxtFrm() ); + // we have to know if the paragraph should snap to grid + aDrawInf.SetSnapToGrid( SnapToGrid() ); + // for underlining we must know when not to add extra space behind + // a character in justified mode + aDrawInf.SetSpaceStop( ! rPor.GetPortion() || + rPor.GetPortion()->InFixMargGrp() || + rPor.GetPortion()->IsHolePortion() ); + + if( GetTxtFly()->IsOn() ) + { + // aPos muss als TopLeft vorliegen, weil die ClipRects sonst + // nicht berechnet werden koennen. + const Point aPoint( aPos.X(), aPos.Y() - rPor.GetAscent() ); + const Size aSize( rPor.Width(), rPor.Height() ); + aDrawInf.SetPos( aPoint ); + aDrawInf.SetSize( aSize ); + aDrawInf.SetAscent( rPor.GetAscent() ); + aDrawInf.SetKern( bKern ? rPor.Width() : 0 ); + aDrawInf.SetWrong( bTmpWrong ? pWrongList : NULL ); + aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? pGrammarCheckList : NULL ); + aDrawInf.SetSmartTags( bTmpSmart ? pSmartTags : NULL ); // SMARTTAGS + GetTxtFly()->DrawTextOpaque( aDrawInf ); + } + else + { + aDrawInf.SetPos( aPos ); + if( bKern ) + pFnt->_DrawStretchText( aDrawInf ); + else + { + aDrawInf.SetWrong( bTmpWrong ? pWrongList : NULL ); + aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? pGrammarCheckList : NULL ); + aDrawInf.SetSmartTags( bTmpSmart ? pSmartTags : NULL ); // SMARTTAGS + pFnt->_DrawText( aDrawInf ); + } + } +} + +/************************************************************************* + * SwTxtPaintInfo::CalcRect() + *************************************************************************/ + +void SwTxtPaintInfo::CalcRect( const SwLinePortion& rPor, + SwRect* pRect, SwRect* pIntersect ) const +{ + Size aSize( rPor.Width(), rPor.Height() ); + if( rPor.IsHangingPortion() ) + aSize.Width() = ((SwHangingPortion&)rPor).GetInnerWidth(); + if( rPor.InSpaceGrp() && GetSpaceAdd() ) + { + SwTwips nAdd = rPor.CalcSpacing( GetSpaceAdd(), *this ); + if( rPor.InFldGrp() && GetSpaceAdd() < 0 && nAdd ) + nAdd += GetSpaceAdd() / SPACING_PRECISION_FACTOR; + aSize.Width() += nAdd; + } + + Point aPoint; + + if( IsRotated() ) + { + long nTmp = aSize.Width(); + aSize.Width() = aSize.Height(); + aSize.Height() = nTmp; + if ( 1 == GetDirection() ) + { + aPoint.A() = X() - rPor.GetAscent(); + aPoint.B() = Y() - aSize.Height(); + } + else + { + aPoint.A() = X() - rPor.Height() + rPor.GetAscent(); + aPoint.B() = Y(); + } + } + else + { + aPoint.A() = X(); + aPoint.B() = Y() - rPor.GetAscent(); + } + + // Adjust x coordinate if we are inside a bidi portion + const BOOL bFrmDir = GetTxtFrm()->IsRightToLeft(); + BOOL bCounterDir = ( ! bFrmDir && DIR_RIGHT2LEFT == GetDirection() ) || + ( bFrmDir && DIR_LEFT2RIGHT == GetDirection() ); + + if ( bCounterDir ) + aPoint.A() -= aSize.Width(); + + SwRect aRect( aPoint, aSize ); + + if ( GetTxtFrm()->IsRightToLeft() ) + GetTxtFrm()->SwitchLTRtoRTL( aRect ); + + if ( GetTxtFrm()->IsVertical() ) + GetTxtFrm()->SwitchHorizontalToVertical( aRect ); + + if ( pRect ) + *pRect = aRect; + + if( aRect.HasArea() && pIntersect ) + { + ::SwAlignRect( aRect, (ViewShell*)GetVsh() ); + + if ( GetOut()->IsClipRegion() ) + { + SwRect aClip( GetOut()->GetClipRegion().GetBoundRect() ); + aRect.Intersection( aClip ); + } + + *pIntersect = aRect; + } +} + +/************************************************************************* + * lcl_DrawSpecial + * + * Draws a special portion, e.g., line break portion, tab portion. + * rPor - The portion + * rRect - The rectangle surrounding the character + * pCol - Specify a color for the character + * bCenter - Draw the character centered, otherwise left aligned + * bRotate - Rotate the character if character rotation is set + *************************************************************************/ + +static void lcl_DrawSpecial( const SwTxtPaintInfo& rInf, const SwLinePortion& rPor, + SwRect& rRect, const Color* pCol, sal_Unicode cChar, + BYTE nOptions ) +{ + sal_Bool bCenter = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_CENTER ); + sal_Bool bRotate = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_ROTATE ); + + // rRect is given in absolute coordinates + if ( rInf.GetTxtFrm()->IsRightToLeft() ) + rInf.GetTxtFrm()->SwitchRTLtoLTR( rRect ); + if ( rInf.GetTxtFrm()->IsVertical() ) + rInf.GetTxtFrm()->SwitchVerticalToHorizontal( rRect ); + + const SwFont* pOldFnt = rInf.GetFont(); + + // Font is generated only once: + static SwFont* pFnt = 0; + if ( ! pFnt ) + { + pFnt = new SwFont( *pOldFnt ); + pFnt->SetFamily( FAMILY_DONTKNOW, pFnt->GetActual() ); + pFnt->SetName( numfunc::GetDefBulletFontname(), pFnt->GetActual() ); + pFnt->SetStyleName( aEmptyStr, pFnt->GetActual() ); + pFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, pFnt->GetActual() ); + } + + // Some of the current values are set at the font: + if ( ! bRotate ) + pFnt->SetVertical( 0, rInf.GetTxtFrm()->IsVertical() ); + else + pFnt->SetVertical( pOldFnt->GetOrientation() ); + + if ( pCol ) + pFnt->SetColor( *pCol ); + else + pFnt->SetColor( pOldFnt->GetColor() ); + + Size aFontSize( 0, SPECIAL_FONT_HEIGHT ); + pFnt->SetSize( aFontSize, pFnt->GetActual() ); + + ((SwTxtPaintInfo&)rInf).SetFont( pFnt ); + + // The maximum width depends on the current orientation + const USHORT nDir = pFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); + SwTwips nMaxWidth = 0; + switch ( nDir ) + { + case 0 : + nMaxWidth = rRect.Width(); + break; + case 900 : + case 2700 : + nMaxWidth = rRect.Height(); + break; + default: + ASSERT( sal_False, "Unknown direction set at font" ) + break; + } + + // check if char fits into rectangle + const XubString aTmp( cChar ); + aFontSize = rInf.GetTxtSize( aTmp ).SvLSize(); + while ( aFontSize.Width() > nMaxWidth ) + { + SwTwips nFactor = ( 100 * aFontSize.Width() ) / nMaxWidth; + const SwTwips nOldWidth = aFontSize.Width(); + + // new height for font + const BYTE nAct = pFnt->GetActual(); + aFontSize.Height() = ( 100 * pFnt->GetSize( nAct ).Height() ) / nFactor; + aFontSize.Width() = ( 100 * pFnt->GetSize( nAct).Width() ) / nFactor; + + if ( !aFontSize.Width() && !aFontSize.Height() ) + break; + + pFnt->SetSize( aFontSize, nAct ); + + aFontSize = rInf.GetTxtSize( aTmp ).SvLSize(); + + if ( aFontSize.Width() >= nOldWidth ) + break; + } + + const Point aOldPos( rInf.GetPos() ); + + // adjust values so that tab is vertically and horizontally centered + SwTwips nX = rRect.Left(); + SwTwips nY = rRect.Top(); + switch ( nDir ) + { + case 0 : + if ( bCenter ) + nX += ( rRect.Width() - aFontSize.Width() ) / 2; + nY += ( rRect.Height() - aFontSize.Height() ) / 2 + rInf.GetAscent(); + break; + case 900 : + if ( bCenter ) + nX += ( rRect.Width() - aFontSize.Height() ) / 2 + rInf.GetAscent(); + nY += ( rRect.Height() + aFontSize.Width() ) / 2; + break; + case 2700 : + if ( bCenter ) + nX += ( rRect.Width() + aFontSize.Height() ) / 2 - rInf.GetAscent(); + nY += ( rRect.Height() - aFontSize.Width() ) / 2; + break; + } + + Point aTmpPos( nX, nY ); + ((SwTxtPaintInfo&)rInf).SetPos( aTmpPos ); + USHORT nOldWidth = rPor.Width(); + ((SwLinePortion&)rPor).Width( (USHORT)aFontSize.Width() ); + rInf.DrawText( aTmp, rPor ); + ((SwLinePortion&)rPor).Width( nOldWidth ); + ((SwTxtPaintInfo&)rInf).SetFont( (SwFont*)pOldFnt ); + ((SwTxtPaintInfo&)rInf).SetPos( aOldPos ); +} + +/************************************************************************* + * SwTxtPaintInfo::DrawRect() + *************************************************************************/ + +void SwTxtPaintInfo::DrawRect( const SwRect &rRect, sal_Bool bNoGraphic, + sal_Bool bRetouche ) const +{ + if ( OnWin() || !bRetouche ) + { + if( aTxtFly.IsOn() ) + ((SwTxtPaintInfo*)this)->GetTxtFly()-> + DrawFlyRect( pOut, rRect, *this, bNoGraphic ); + else if ( bNoGraphic ) + pOut->DrawRect( rRect.SVRect() ); + else + { + ASSERT( ((SvxBrushItem*)-1) != pBrushItem, "DrawRect: Uninitialized BrushItem!" ); + ::DrawGraphic( pBrushItem, pOut, aItemRect, rRect ); + } + } +} + +/************************************************************************* + * SwTxtPaintInfo::DrawTab() + *************************************************************************/ + +void SwTxtPaintInfo::DrawTab( const SwLinePortion &rPor ) const +{ + if( OnWin() ) + { + SwRect aRect; + CalcRect( rPor, &aRect ); + + if ( ! aRect.HasArea() ) + return; + + const sal_Unicode cChar = GetTxtFrm()->IsRightToLeft() ? + CHAR_TAB_RTL : CHAR_TAB; + const BYTE nOptions = DRAW_SPECIAL_OPTIONS_CENTER | + DRAW_SPECIAL_OPTIONS_ROTATE; + lcl_DrawSpecial( *this, rPor, aRect, 0, cChar, nOptions ); + } +} + +/************************************************************************* + * SwTxtPaintInfo::DrawLineBreak() + *************************************************************************/ + +void SwTxtPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const +{ + if( OnWin() ) + { + KSHORT nOldWidth = rPor.Width(); + ((SwLinePortion&)rPor).Width( LINE_BREAK_WIDTH ); + + SwRect aRect; + CalcRect( rPor, &aRect ); + + if( aRect.HasArea() ) + { + const sal_Unicode cChar = GetTxtFrm()->IsRightToLeft() ? + CHAR_LINEBREAK_RTL : CHAR_LINEBREAK; + const BYTE nOptions = 0; + lcl_DrawSpecial( *this, rPor, aRect, 0, cChar, nOptions ); + } + + ((SwLinePortion&)rPor).Width( nOldWidth ); + } +} + + +/************************************************************************* + * SwTxtPaintInfo::DrawRedArrow() + *************************************************************************/ + +void SwTxtPaintInfo::DrawRedArrow( const SwLinePortion &rPor ) const +{ + Size aSize( SPECIAL_FONT_HEIGHT, SPECIAL_FONT_HEIGHT ); + SwRect aRect( ((SwArrowPortion&)rPor).GetPos(), aSize ); + sal_Unicode cChar; + if( ((SwArrowPortion&)rPor).IsLeft() ) + { + aRect.Pos().Y() += 20 - GetAscent(); + aRect.Pos().X() += 20; + if( aSize.Height() > rPor.Height() ) + aRect.Height( rPor.Height() ); + cChar = CHAR_LEFT_ARROW; + } + else + { + if( aSize.Height() > rPor.Height() ) + aRect.Height( rPor.Height() ); + aRect.Pos().Y() -= aRect.Height() + 20; + aRect.Pos().X() -= aRect.Width() + 20; + cChar = CHAR_RIGHT_ARROW; + } + + if ( GetTxtFrm()->IsVertical() ) + GetTxtFrm()->SwitchHorizontalToVertical( aRect ); + + Color aCol( COL_LIGHTRED ); + + if( aRect.HasArea() ) + { + const BYTE nOptions = 0; + lcl_DrawSpecial( *this, rPor, aRect, &aCol, cChar, nOptions ); + } +} + + +/************************************************************************* + * SwTxtPaintInfo::DrawPostIts() + *************************************************************************/ + +void SwTxtPaintInfo::DrawPostIts( const SwLinePortion&, sal_Bool bScript ) const +{ + if( OnWin() && pOpt->IsPostIts() ) + { + Size aSize; + Point aTmp; + + const USHORT nPostItsWidth = pOpt->GetPostItsWidth( GetOut() ); + const USHORT nFontHeight = pFnt->GetHeight( pVsh, *GetOut() ); + const USHORT nFontAscent = pFnt->GetAscent( pVsh, *GetOut() ); + + switch ( pFnt->GetOrientation( GetTxtFrm()->IsVertical() ) ) + { + case 0 : + aSize.Width() = nPostItsWidth; + aSize.Height() = nFontHeight; + aTmp.X() = aPos.X(); + aTmp.Y() = aPos.Y() - nFontAscent; + break; + case 900 : + aSize.Height() = nPostItsWidth; + aSize.Width() = nFontHeight; + aTmp.X() = aPos.X() - nFontAscent; + aTmp.Y() = aPos.Y(); + break; + case 2700 : + aSize.Height() = nPostItsWidth; + aSize.Width() = nFontHeight; + aTmp.X() = aPos.X() - nFontHeight + + nFontAscent; + aTmp.Y() = aPos.Y(); + break; + } + + SwRect aTmpRect( aTmp, aSize ); + + if ( GetTxtFrm()->IsRightToLeft() ) + GetTxtFrm()->SwitchLTRtoRTL( aTmpRect ); + + if ( GetTxtFrm()->IsVertical() ) + GetTxtFrm()->SwitchHorizontalToVertical( aTmpRect ); + + const Rectangle aRect( aTmpRect.SVRect() ); + pOpt->PaintPostIts( (OutputDevice*)GetOut(), aRect, bScript ); + } +} + + +void SwTxtPaintInfo::DrawCheckBox( const SwFieldFormPortion &rPor, bool checked) const +{ + SwRect aIntersect; + CalcRect( rPor, &aIntersect, 0 ); + if ( aIntersect.HasArea() ) { + if (OnWin()) { + OutputDevice* pOutDev = (OutputDevice*)GetOut(); + pOutDev->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + pOutDev->SetLineColor( Color(220, 233, 245)); + pOutDev->SetFillColor( Color(220, 233, 245)); + pOutDev->DrawRect( aIntersect.SVRect() ); + pOutDev->Pop(); + } + const int delta=10; + Rectangle r(aIntersect.Left()+delta, aIntersect.Top()+delta, aIntersect.Right()-delta, aIntersect.Bottom()-delta); + pOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + pOut->SetLineColor( Color(0, 0, 0)); + pOut->SetFillColor(); + pOut->DrawRect( r ); + if (checked) { + pOut->DrawLine(r.TopLeft(), r.BottomRight()); + pOut->DrawLine(r.TopRight(), r.BottomLeft()); + pOut->Pop(); + } + } +} + +/************************************************************************* + * SwTxtPaintInfo::DrawBackGround() + *************************************************************************/ +void SwTxtPaintInfo::DrawBackground( const SwLinePortion &rPor ) const +{ + ASSERT( OnWin(), "SwTxtPaintInfo::DrawBackground: printer polution ?" ); + + SwRect aIntersect; + CalcRect( rPor, 0, &aIntersect ); + + if ( aIntersect.HasArea() ) + { + OutputDevice* pTmpOut = (OutputDevice*)GetOut(); + pTmpOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + + // For dark background we do not want to have a filled rectangle + if ( GetVsh() && GetVsh()->GetWin() && lcl_IsDarkBackground( *this ) ) + { + pTmpOut->SetLineColor( SwViewOption::GetFontColor().GetColor() ); + } + else + { + pTmpOut->SetFillColor( SwViewOption::GetFieldShadingsColor() ); + pTmpOut->SetLineColor(); + } + + DrawRect( aIntersect, sal_True ); + pTmpOut->Pop(); + } +} + +void SwTxtPaintInfo::_DrawBackBrush( const SwLinePortion &rPor ) const +{ + { + SwRect aIntersect; + CalcRect( rPor, &aIntersect, 0 ); + if(aIntersect.HasArea()) + { + SwTxtNode *pNd = pFrm->GetTxtNode(); + const ::sw::mark::IMark* pFieldmark = NULL; + if(pNd) + { + const SwDoc *doc=pNd->GetDoc(); + if(doc) + { + SwIndex aIndex(pNd, GetIdx()); + SwPosition aPosition(*pNd, aIndex); + pFieldmark=doc->getIDocumentMarkAccess()->getFieldmarkFor(aPosition); + } + } + bool bIsStartMark=(1==GetLen() && CH_TXT_ATR_FIELDSTART==GetTxt().GetChar(GetIdx())); + if(pFieldmark) OSL_TRACE("Found Fieldmark"); + if(bIsStartMark) OSL_TRACE("Found StartMark"); + if (OnWin() && (pFieldmark!=NULL || bIsStartMark)) + { + OutputDevice* pOutDev = (OutputDevice*)GetOut(); + pOutDev->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + pOutDev->SetLineColor( Color(220, 233, 245)); + pOutDev->SetFillColor( Color(220, 233, 245)); + pOutDev->DrawRect( aIntersect.SVRect() ); + pOutDev->Pop(); + } + } + } + if( !pFnt->GetBackColor() ) return; + + ASSERT( pFnt->GetBackColor(), "DrawBackBrush: Lost Color" ); + + SwRect aIntersect; + CalcRect( rPor, 0, &aIntersect ); + + if ( aIntersect.HasArea() ) + { + OutputDevice* pTmpOut = (OutputDevice*)GetOut(); + + // --> FME 2004-06-24 #i16816# tagged pdf support + SwTaggedPDFHelper aTaggedPDFHelper( 0, 0, 0, *pTmpOut ); + // <-- + + pTmpOut->Push( PUSH_LINECOLOR | PUSH_FILLCOLOR ); + + pTmpOut->SetFillColor( *pFnt->GetBackColor() ); + pTmpOut->SetLineColor(); + + DrawRect( aIntersect, sal_True, sal_False ); + + pTmpOut->Pop(); + } +} + +/************************************************************************* + * SwTxtPaintInfo::DrawViewOpt() + *************************************************************************/ + +void SwTxtPaintInfo::DrawViewOpt( const SwLinePortion &rPor, + const MSHORT nWhich ) const +{ + if( OnWin() && !IsMulti() ) + { + sal_Bool bDraw = sal_False; + switch( nWhich ) + { + case POR_FTN: + case POR_QUOVADIS: + case POR_NUMBER: + case POR_FLD: + case POR_URL: + case POR_HIDDEN: + case POR_TOX: + case POR_REF: + case POR_META: + case POR_CONTROLCHAR: + if ( !GetOpt().IsPagePreview() && + !GetOpt().IsReadonly() && + SwViewOption::IsFieldShadings() && + (POR_NUMBER != nWhich || + pFrm->GetTxtNode()->HasMarkedLabel())) // #i27615# + bDraw = sal_True; + break; + case POR_TAB: if ( GetOpt().IsTab() ) bDraw = sal_True; break; + case POR_SOFTHYPH: if ( GetOpt().IsSoftHyph() )bDraw = sal_True; break; + case POR_BLANK: if ( GetOpt().IsHardBlank())bDraw = sal_True; break; + default: + { + ASSERT( !this, "SwTxtPaintInfo::DrawViewOpt: don't know how to draw this" ); + break; + } + } + if ( bDraw ) + DrawBackground( rPor ); + } +} + +/************************************************************************* + * SwTxtPaintInfo::_NotifyURL() + *************************************************************************/ + +void SwTxtPaintInfo::_NotifyURL( const SwLinePortion &rPor ) const +{ + ASSERT( pNoteURL, "NotifyURL: pNoteURL gone with the wind!" ); + + SwRect aIntersect; + CalcRect( rPor, 0, &aIntersect ); + + if( aIntersect.HasArea() ) + { + SwTxtNode *pNd = (SwTxtNode*)GetTxtFrm()->GetTxtNode(); + SwIndex aIndex( pNd, GetIdx() ); + SwTxtAttr *pAttr = pNd->GetTxtAttr( aIndex, RES_TXTATR_INETFMT ); + if( pAttr ) + { + const SwFmtINetFmt& rFmt = pAttr->GetINetFmt(); + pNoteURL->InsertURLNote( rFmt.GetValue(), rFmt.GetTargetFrame(), + aIntersect ); + } + } +} + +/************************************************************************* + * lcl_InitHyphValues() + *************************************************************************/ + +static void lcl_InitHyphValues( PropertyValues &rVals, + INT16 nMinLeading, INT16 nMinTrailing ) +{ + INT32 nLen = rVals.getLength(); + + if (0 == nLen) // yet to be initialized? + { + rVals.realloc( 2 ); + PropertyValue *pVal = rVals.getArray(); + + pVal[0].Name = C2U( UPN_HYPH_MIN_LEADING ); + pVal[0].Handle = UPH_HYPH_MIN_LEADING; + pVal[0].Value <<= nMinLeading; + + pVal[1].Name = C2U( UPN_HYPH_MIN_TRAILING ); + pVal[1].Handle = UPH_HYPH_MIN_TRAILING; + pVal[1].Value <<= nMinTrailing; + } + else if (2 == nLen) // already initialized once? + { + PropertyValue *pVal = rVals.getArray(); + pVal[0].Value <<= nMinLeading; + pVal[1].Value <<= nMinTrailing; + } + else { + DBG_ERROR( "unxpected size of sequence" ); + } +} + +/************************************************************************* + * SwTxtFormatInfo::GetHyphValues() + *************************************************************************/ + +const PropertyValues & SwTxtFormatInfo::GetHyphValues() const +{ + DBG_ASSERT( 2 == aHyphVals.getLength(), + "hyphenation values not yet initialized" ); + return aHyphVals; +} + +/************************************************************************* + * SwTxtFormatInfo::InitHyph() + *************************************************************************/ + +sal_Bool SwTxtFormatInfo::InitHyph( const sal_Bool bAutoHyphen ) +{ + const SwAttrSet& rAttrSet = GetTxtFrm()->GetTxtNode()->GetSwAttrSet(); + SetHanging( rAttrSet.GetHangingPunctuation().GetValue() ); + SetScriptSpace( rAttrSet.GetScriptSpace().GetValue() ); + SetForbiddenChars( rAttrSet.GetForbiddenRule().GetValue() ); + const SvxHyphenZoneItem &rAttr = rAttrSet.GetHyphenZone(); + MaxHyph() = rAttr.GetMaxHyphens(); + sal_Bool bAuto = bAutoHyphen || rAttr.IsHyphen(); + if( bAuto || bInterHyph ) + { + nHyphStart = nHyphWrdStart = STRING_LEN; + nHyphWrdLen = 0; + + const INT16 nMinimalLeading = Max(rAttr.GetMinLead(), sal_uInt8(2)); + const INT16 nMinimalTrailing = rAttr.GetMinTrail(); + lcl_InitHyphValues( aHyphVals, nMinimalLeading, nMinimalTrailing); + } + return bAuto; +} + +/************************************************************************* + * SwTxtFormatInfo::CtorInitTxtFormatInfo() + *************************************************************************/ + +void SwTxtFormatInfo::CtorInitTxtFormatInfo( SwTxtFrm *pNewFrm, const sal_Bool bNewInterHyph, + const sal_Bool bNewQuick, const sal_Bool bTst ) +{ + CtorInitTxtPaintInfo( pNewFrm, SwRect() ); + + bQuick = bNewQuick; + bInterHyph = bNewInterHyph; + + //! needs to be done in this order + nMinLeading = 2; + nMinTrailing = 2; + nMinWordLength = 0; + bAutoHyph = InitHyph(); + + bIgnoreFly = sal_False; + bFakeLineStart = sal_False; + bShift = sal_False; + bDropInit = sal_False; + bTestFormat = bTst; + nLeft = 0; + nRight = 0; + nFirst = 0; + nRealWidth = 0; + nForcedLeftMargin = 0; + pRest = 0; + nLineHeight = 0; + nLineNettoHeight = 0; + SetLineStart(0); + Init(); +} + +/************************************************************************* + * SwTxtFormatInfo::IsHyphenate() + *************************************************************************/ +// Trennen oder nicht trennen, das ist hier die Frage: +// - in keinem Fall trennen, wenn der Hyphenator ERROR zurueckliefert, +// oder wenn als Sprache NOLANGUAGE eingestellt ist. +// - ansonsten immer trennen, wenn interaktive Trennung vorliegt +// - wenn keine interakt. Trennung, dann nur trennen, wenn im ParaFmt +// automatische Trennung eingestellt ist. + +sal_Bool SwTxtFormatInfo::IsHyphenate() const +{ + if( !bInterHyph && !bAutoHyph ) + return sal_False; + + LanguageType eTmp = GetFont()->GetLanguage(); + if( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp ) + return sal_False; + + uno::Reference< XHyphenator > xHyph = ::GetHyphenator(); + if (bInterHyph && xHyph.is()) + SvxSpellWrapper::CheckHyphLang( xHyph, eTmp ); + + if( !xHyph.is() || !xHyph->hasLocale( pBreakIt->GetLocale(eTmp) ) ) + return sal_False; + return sal_True; +} + +/************************************************************************* + * SwTxtFormatInfo::GetDropFmt() + *************************************************************************/ + +// Dropcaps vom SwTxtFormatter::CTOR gerufen. +const SwFmtDrop *SwTxtFormatInfo::GetDropFmt() const +{ + const SwFmtDrop *pDrop = &GetTxtFrm()->GetTxtNode()->GetSwAttrSet().GetDrop(); + if( 1 >= pDrop->GetLines() || + ( !pDrop->GetChars() && !pDrop->GetWholeWord() ) ) + pDrop = 0; + return pDrop; +} + +/************************************************************************* + * SwTxtFormatInfo::Init() + *************************************************************************/ + +void SwTxtFormatInfo::Init() +{ + // Nicht initialisieren: pRest, nLeft, nRight, nFirst, nRealWidth + X(0); + bArrowDone = bFull = bFtnDone = bErgoDone = bNumDone = bNoEndHyph = + bNoMidHyph = bStop = bNewLine = bUnderFlow = sal_False; + + // generally we do not allow number portions in follows, except... + if ( GetTxtFrm()->IsFollow() ) + { + const SwTxtFrm* pMaster = GetTxtFrm()->FindMaster(); + const SwLinePortion* pTmpPara = pMaster->GetPara(); + + // there is a master for this follow and the master does not have + // any contents (especially it does not have a number portion) + bNumDone = ! pTmpPara || + ! ((SwParaPortion*)pTmpPara)->GetFirstPortion()->IsFlyPortion(); + } + + pRoot = 0; + pLast = 0; + pFly = 0; + pLastFld = 0; + pLastTab = 0; + pUnderFlow = 0; + cTabDecimal = 0; + nWidth = nRealWidth; + nForcedLeftMargin = 0; + nSoftHyphPos = 0; + nUnderScorePos = STRING_LEN; + cHookChar = 0; + SetIdx(0); + SetLen( GetTxt().Len() ); + SetPaintOfst(0); +} + +/*-----------------16.10.00 11:39------------------- + * There are a few differences between a copy constructor + * and the following constructor for multi-line formatting. + * The root is the first line inside the multi-portion, + * the line start is the actual position in the text, + * the line width is the rest width from the surrounding line + * and the bMulti and bFirstMulti-flag has to be set correctly. + * --------------------------------------------------*/ + +SwTxtFormatInfo::SwTxtFormatInfo( const SwTxtFormatInfo& rInf, + SwLineLayout& rLay, SwTwips nActWidth ) : SwTxtPaintInfo( rInf ) +{ + pRoot = &rLay; + pLast = &rLay; + pFly = NULL; + pLastFld = NULL; + pUnderFlow = NULL; + pRest = NULL; + pLastTab = NULL; + + nSoftHyphPos = 0; + nUnderScorePos = STRING_LEN; + nHyphStart = 0; + nHyphWrdStart = 0; + nHyphWrdLen = 0; + nLineStart = rInf.GetIdx(); + nLeft = rInf.nLeft; + nRight = rInf.nRight; + nFirst = rInf.nLeft; + nRealWidth = KSHORT(nActWidth); + nWidth = nRealWidth; + nLineHeight = 0; + nLineNettoHeight = 0; + nForcedLeftMargin = 0; + + nMinLeading = 0; + nMinTrailing = 0; + nMinWordLength = 0; + bFull = FALSE; + bFtnDone = TRUE; + bErgoDone = TRUE; + bNumDone = TRUE; + bArrowDone = TRUE; + bStop = FALSE; + bNewLine = TRUE; + bShift = FALSE; + bUnderFlow = FALSE; + bInterHyph = FALSE; + bAutoHyph = FALSE; + bDropInit = FALSE; + bQuick = rInf.bQuick; + bNoEndHyph = FALSE; + bNoMidHyph = FALSE; + bIgnoreFly = FALSE; + bFakeLineStart = FALSE; + + cTabDecimal = 0; + cHookChar = 0; + nMaxHyph = 0; + bTestFormat = rInf.bTestFormat; + SetMulti( sal_True ); + SetFirstMulti( rInf.IsFirstMulti() ); +} + +/************************************************************************* + * SwTxtFormatInfo::_CheckFtnPortion() + *************************************************************************/ + +sal_Bool SwTxtFormatInfo::_CheckFtnPortion( SwLineLayout* pCurr ) +{ + KSHORT nHeight = pCurr->GetRealHeight(); + SwLinePortion *pPor = pCurr->GetPortion(); + sal_Bool bRet = sal_False; + while( pPor ) + { + if( pPor->IsFtnPortion() && nHeight > ((SwFtnPortion*)pPor)->Orig() ) + { + bRet = sal_True; + SetLineHeight( nHeight ); + SetLineNettoHeight( pCurr->Height() ); + break; + } + pPor = pPor->GetPortion(); + } + return bRet; +} + + + + +/************************************************************************* + * SwTxtFormatInfo::ScanPortionEnd() + *************************************************************************/ +xub_StrLen SwTxtFormatInfo::ScanPortionEnd( const xub_StrLen nStart, + const xub_StrLen nEnd ) +{ + cHookChar = 0; + xub_StrLen i = nStart; + + // + // Used for decimal tab handling: + // + const xub_Unicode cTabDec = GetLastTab() ? (sal_Unicode)GetTabDecimal() : 0; + const xub_Unicode cThousandSep = ',' == cTabDec ? '.' : ','; + // --> FME 2006-01-23 #i45951# German (Switzerland) uses ' as thousand separator: + const xub_Unicode cThousandSep2 = ',' == cTabDec ? '.' : '\''; + // <-- + + bool bNumFound = false; + const bool bTabCompat = GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT); + + // Removed for i7288. bSkip used to be passed from SwFldPortion::Format + // as IsFollow(). Therefore more than one special character was not + // handled correctly at the beginning of follow fields. +// if ( bSkip && i < nEnd ) +// ++i; + + for( ; i < nEnd; ++i ) + { + const xub_Unicode cPos = GetChar( i ); + switch( cPos ) + { + case CH_TXTATR_BREAKWORD: + case CH_TXTATR_INWORD: + if( !HasHint( i )) + break; + // no break; + + case CHAR_SOFTHYPHEN: + case CHAR_HARDHYPHEN: + case CHAR_HARDBLANK: + case CH_TAB: + case CH_BREAK: + case CHAR_ZWSP : + case CHAR_ZWNBSP : +// case CHAR_RLM : +// case CHAR_LRM : + cHookChar = cPos; + return i; + + case CHAR_UNDERSCORE: + if ( STRING_LEN == nUnderScorePos ) + nUnderScorePos = i; + break; + + default: + if ( cTabDec ) + { + if( cTabDec == cPos ) + { + ASSERT( cPos, "Unexpected end of string" ); + if( cPos ) // robust + { + cHookChar = cPos; + return i; + } + } + + // + // Compatibility: First non-digit character behind a + // a digit character becomes the hook character + // + if ( bTabCompat ) + { + if ( ( 0x2F < cPos && cPos < 0x3A ) || + ( bNumFound && ( cPos == cThousandSep || cPos == cThousandSep2 ) ) ) + { + bNumFound = true; + } + else + { + if ( bNumFound ) + { + cHookChar = cPos; + SetTabDecimal( cPos ); + return i; + } + } + } + } + } + } + + // --> FME 2006-01-13 #130210# Check if character *behind* the portion has + // to become the hook: + if ( i == nEnd && i < GetTxt().Len() && bNumFound ) + { + const xub_Unicode cPos = GetChar( i ); + if ( cPos != cTabDec && cPos != cThousandSep && cPos !=cThousandSep2 && ( 0x2F >= cPos || cPos >= 0x3A ) ) + { + cHookChar = GetChar( i ); + SetTabDecimal( cHookChar ); + } + } + + return i; +} + +BOOL SwTxtFormatInfo::LastKernPortion() +{ + if( GetLast() ) + { + if( GetLast()->IsKernPortion() ) + return TRUE; + if( GetLast()->Width() || ( GetLast()->GetLen() && + !GetLast()->IsHolePortion() ) ) + return FALSE; + } + SwLinePortion* pPor = GetRoot(); + SwLinePortion *pKern = NULL; + while( pPor ) + { + if( pPor->IsKernPortion() ) + pKern = pPor; + else if( pPor->Width() || ( pPor->GetLen() && !pPor->IsHolePortion() ) ) + pKern = NULL; + pPor = pPor->GetPortion(); + } + if( pKern ) + { + SetLast( pKern ); + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * class SwTxtSlot + *************************************************************************/ + +SwTxtSlot::SwTxtSlot( const SwTxtSizeInfo *pNew, const SwLinePortion *pPor, + bool bTxtLen, bool bExgLists, const sal_Char *pCh ) + : pOldTxt( 0 ), + pOldSmartTagList( 0 ), + pOldGrammarCheckList( 0 ), + pTempList( 0 ) +{ + if( pCh ) + { + aTxt = XubString( pCh, RTL_TEXTENCODING_MS_1252 ); + bOn = sal_True; + } + else + bOn = pPor->GetExpTxt( *pNew, aTxt ); + + // Der Text wird ausgetauscht... + if( bOn ) + { + pInf = (SwTxtSizeInfo*)pNew; + nIdx = pInf->GetIdx(); + nLen = pInf->GetLen(); + pOldTxt = &(pInf->GetTxt()); + pInf->SetTxt( aTxt ); + pInf->SetIdx( 0 ); + pInf->SetLen( bTxtLen ? pInf->GetTxt().Len() : pPor->GetLen() ); + + // ST2 + if ( bExgLists ) + { + pOldSmartTagList = static_cast<SwTxtPaintInfo*>(pInf)->GetSmartTags(); + if ( pOldSmartTagList ) + { + const USHORT nPos = pOldSmartTagList->GetWrongPos(nIdx); + const xub_StrLen nListPos = pOldSmartTagList->Pos(nPos); + if( nListPos == nIdx ) + ((SwTxtPaintInfo*)pInf)->SetSmartTags( pOldSmartTagList->SubList( nPos ) ); + else if( !pTempList && nPos < pOldSmartTagList->Count() && nListPos < nIdx && aTxt.Len() ) + { + pTempList = new SwWrongList( WRONGLIST_SMARTTAG ); + pTempList->Insert( rtl::OUString(), 0, 0, aTxt.Len(), 0 ); + ((SwTxtPaintInfo*)pInf)->SetSmartTags( pTempList ); + } + else + ((SwTxtPaintInfo*)pInf)->SetSmartTags( 0); + } + pOldGrammarCheckList = static_cast<SwTxtPaintInfo*>(pInf)->GetGrammarCheckList(); + if ( pOldGrammarCheckList ) + { + const USHORT nPos = pOldGrammarCheckList->GetWrongPos(nIdx); + const xub_StrLen nListPos = pOldGrammarCheckList->Pos(nPos); + if( nListPos == nIdx ) + ((SwTxtPaintInfo*)pInf)->SetGrammarCheckList( pOldGrammarCheckList->SubList( nPos ) ); + else if( !pTempList && nPos < pOldGrammarCheckList->Count() && nListPos < nIdx && aTxt.Len() ) + { + pTempList = new SwWrongList( WRONGLIST_GRAMMAR ); + pTempList->Insert( rtl::OUString(), 0, 0, aTxt.Len(), 0 ); + ((SwTxtPaintInfo*)pInf)->SetGrammarCheckList( pTempList ); + } + else + ((SwTxtPaintInfo*)pInf)->SetGrammarCheckList( 0); + } + } + } +} + +/************************************************************************* + * SwTxtSlot::~SwTxtSlot() + *************************************************************************/ + +SwTxtSlot::~SwTxtSlot() +{ + if( bOn ) + { + pInf->SetTxt( *pOldTxt ); + pInf->SetIdx( nIdx ); + pInf->SetLen( nLen ); + + // ST2 + // Restore old smart tag list + if ( pOldSmartTagList ) + ((SwTxtPaintInfo*)pInf)->SetSmartTags( pOldSmartTagList ); + if ( pOldGrammarCheckList ) + ((SwTxtPaintInfo*)pInf)->SetGrammarCheckList( pOldGrammarCheckList ); + delete pTempList; + } +} + +/************************************************************************* + * SwFontSave::SwFontSave() + *************************************************************************/ + +SwFontSave::SwFontSave( const SwTxtSizeInfo &rInf, SwFont *pNew, + SwAttrIter* pItr ) + : pFnt( pNew ? ((SwTxtSizeInfo&)rInf).GetFont() : 0 ) +{ + if( pFnt ) + { + pInf = &((SwTxtSizeInfo&)rInf); + // In these cases we temporarily switch to the new font: + // 1. the fonts have a different magic number + // 2. they have different script types + // 3. their background colors differ (this is not covered by 1.) + if( pFnt->DifferentMagic( pNew, pFnt->GetActual() ) || + pNew->GetActual() != pFnt->GetActual() || + ( ! pNew->GetBackColor() && pFnt->GetBackColor() ) || + ( pNew->GetBackColor() && ! pFnt->GetBackColor() ) || + ( pNew->GetBackColor() && pFnt->GetBackColor() && + ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) ) ) + { + pNew->SetTransparent( sal_True ); + pNew->SetAlign( ALIGN_BASELINE ); + pInf->SetFont( pNew ); + } + else + pFnt = 0; + pNew->Invalidate(); + pNew->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() ); + if( pItr && pItr->GetFnt() == pFnt ) + { + pIter = pItr; + pIter->SetFnt( pNew ); + } + else + pIter = NULL; + } +} + +/************************************************************************* + * SwFontSave::~SwFontSave() + *************************************************************************/ + +SwFontSave::~SwFontSave() +{ + if( pFnt ) + { + // SwFont zurueckstellen + pFnt->Invalidate(); + pInf->SetFont( pFnt ); + if( pIter ) + { + pIter->SetFnt( pFnt ); + pIter->nPos = STRING_LEN; + } + } +} + +/************************************************************************* + * SwDefFontSave::SwDefFontSave() + *************************************************************************/ + +SwDefFontSave::SwDefFontSave( const SwTxtSizeInfo &rInf ) + : pFnt( ((SwTxtSizeInfo&)rInf).GetFont() ) +{ + const BOOL bTmpAlter = pFnt->GetFixKerning() || + ( RTL_TEXTENCODING_SYMBOL == pFnt->GetCharSet(pFnt->GetActual()) ) + ; + + const sal_Bool bFamily = bTmpAlter && + pFnt->GetName( pFnt->GetActual() ) != numfunc::GetDefBulletFontname(); + const sal_Bool bRotation = (sal_Bool)pFnt->GetOrientation() && + ! rInf.GetTxtFrm()->IsVertical(); + + if( bFamily || bRotation ) + { + pNewFnt = new SwFont( *pFnt ); + + if ( bFamily ) + { + pNewFnt->SetFamily( FAMILY_DONTKNOW, pFnt->GetActual() ); + pNewFnt->SetName( numfunc::GetDefBulletFontname(), pFnt->GetActual() ); + pNewFnt->SetStyleName( aEmptyStr, pFnt->GetActual() ); + pNewFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, pFnt->GetActual() ); + pNewFnt->SetFixKerning( 0 ); + } + + if ( bRotation ) + pNewFnt->SetVertical( 0, rInf.GetTxtFrm()->IsVertical() ); + + pInf = &((SwTxtSizeInfo&)rInf); + pNewFnt->Invalidate(); + pInf->SetFont( pNewFnt ); + } + else + { + pFnt = 0; + pNewFnt = 0; + } +} + +/************************************************************************* + * SwDefFontSave::~SwDefFontSave() + *************************************************************************/ + +SwDefFontSave::~SwDefFontSave() +{ + if( pFnt ) + { + delete pNewFnt; + // SwFont zurueckstellen + pFnt->Invalidate(); + pInf->SetFont( pFnt ); + } +} + +/************************************************************************* + * SwTxtFormatInfo::ChgHyph() + *************************************************************************/ + +sal_Bool SwTxtFormatInfo::ChgHyph( const sal_Bool bNew ) +{ + const sal_Bool bOld = bAutoHyph; + if( bAutoHyph != bNew ) + { + bAutoHyph = bNew; + InitHyph( bNew ); + // 5744: Sprache am Hyphenator einstellen. + if( pFnt ) + pFnt->ChgPhysFnt( pVsh, *pOut ); + } + return bOld; +} + + diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx new file mode 100644 index 000000000000..42873bf67ff2 --- /dev/null +++ b/sw/source/core/text/inftxt.hxx @@ -0,0 +1,904 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: inftxt.hxx,v $ + * $Revision: 1.60 $ + * + * 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. + * + ************************************************************************/ +#ifndef _INFTXT_HXX +#define _INFTXT_HXX +#include <com/sun/star/linguistic2/XHyphenatedWord.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> + +#ifndef _TABLE_HXX //autogen +#include <tools/table.hxx> +#endif + +#include "swtypes.hxx" +#include "txttypes.hxx" +#include "swrect.hxx" +#include "txtfly.hxx" +#include "swfont.hxx" +#include "porlay.hxx" +#include "txtfrm.hxx" +#include "ndtxt.hxx" +#include "txttypes.hxx" +#include <svx/paravertalignitem.hxx> + +class Font; +class OutputDevice; +class SvxBrushItem; +class SvxLineSpacingItem; +class SvxTabStop; +class SvxTabStopItem; +class SwAttrSet; +class SwFldPortion; +class SwFlyPortion; +class SwFmtDrop; +class SwLineLayout; +class SwLinePortion; +class SwParaPortion; +class SwTabPortion; +class SwTxtFrm; +class SwTxtSizeInfo; +class SwViewOption; +class ViewShell; +class SwTxtFtn; +class SwAttrIter; +struct SwMultiCreator; +class SwMultiPortion; +class SwWrongList; + +/* Minimum: Prozentwert fuers kernen */ +#define MINKERNPERCENT 5 +#define ARROW_WIDTH 200 +#define DIR_LEFT2RIGHT 0 +#define DIR_BOTTOM2TOP 1 +#define DIR_RIGHT2LEFT 2 +#define DIR_TOP2BOTTOM 3 + +#ifndef PRODUCT +#define OPTCALM( rInf ) (rInf).IsOptCalm() +#define OPTLOW( rInf ) (rInf).IsOptLow() +#define OPTDBG( rInf ) (rInf).IsOptDbg() +#else +#define OPTCALM( rInf ) sal_True +#define OPTLOW( rInf ) sal_False +#define OPTDBG( rInf ) sal_False +#endif + +/************************************************************************* + * class SwLineInfo + *************************************************************************/ + +// Beruecksichtigt das Attribut LineSpace bei der Hoehen/Ascentberechnung. + +class SwLineInfo +{ + friend class SwTxtIter; + + SvxTabStopItem* pRuler; + const SvxLineSpacingItem *pSpace; + USHORT nVertAlign; + KSHORT nDefTabStop; + // --> OD 2008-02-04 #newlistlevelattrs# + bool bListTabStopIncluded; + long nListTabStopPosition; + // <-- + + // --> OD 2008-01-17 #newlistlevelattrs# + void CtorInitLineInfo( const SwAttrSet& rAttrSet, + const SwTxtNode& rTxtNode ); + // <-- + + // --> OD 2008-01-17 #newlistlevelattrs# + SwLineInfo(); + ~SwLineInfo(); +public: +// const SvxTabStop *GetTabStop( const SwTwips nLinePos, +// const SwTwips nLeft, + // #i24363# tab stops relative to indent - returns the tab stop following nSearchPos or NULL + const SvxTabStop *GetTabStop( const SwTwips nSearchPos, + const SwTwips nRight ) const; + inline const SvxLineSpacingItem *GetLineSpacing() const { return pSpace; } + inline KSHORT GetDefTabStop() const { return nDefTabStop; } + inline void SetDefTabStop( KSHORT nNew ) const + { ( (SwLineInfo*)this )->nDefTabStop = nNew; } + + // vertical alignment + inline USHORT GetVertAlign() const { return nVertAlign; } + inline sal_Bool HasSpecialAlign( sal_Bool bVert ) const + { return bVert ? + ( SvxParaVertAlignItem::BASELINE != nVertAlign ) : + ( SvxParaVertAlignItem::BASELINE != nVertAlign && + SvxParaVertAlignItem::AUTOMATIC != nVertAlign ); } + + USHORT NumberOfTabStops() const; + + // --> OD 2008-02-04 #newlistlevelattrs# + inline bool IsListTabStopIncluded() const + { + return bListTabStopIncluded; + } + inline long GetListTabStopPosition() const + { + return nListTabStopPosition; + } + // <-- + + +// friend ostream &operator<<( ostream &rOS, const SwLineInfo &rInf ); + friend SvStream &operator<<( SvStream &rOS, const SwLineInfo &rInf ); +}; + +/************************************************************************* + * class SwTxtInfo + *************************************************************************/ + +class SwTxtInfo +{ + // Implementation in txthyph.cxx + friend void SetParaPortion( SwTxtInfo *pInf, SwParaPortion *pRoot ); + SwParaPortion *pPara; + xub_StrLen nTxtStart; // TxtOfst bei Follows + +protected: + inline SwTxtInfo() { } +public: + void CtorInitTxtInfo( SwTxtFrm *pFrm ); + SwTxtInfo( const SwTxtInfo &rInf ); + inline SwTxtInfo( SwTxtFrm *pFrm ) { CtorInitTxtInfo( pFrm ); } + inline SwParaPortion *GetParaPortion() { return pPara; } + inline const SwParaPortion *GetParaPortion() const { return pPara; } + inline xub_StrLen GetTxtStart() const { return nTxtStart; } + + friend SvStream &operator<<( SvStream &rOS, const SwTxtInfo &rInf ); +}; + +/************************************************************************* + * class SwTxtSizeInfo + *************************************************************************/ + +DECLARE_TABLE( SwTxtPortionTable, sal_IntPtr ) + +class SwTxtSizeInfo : public SwTxtInfo +{ +protected: + // during formatting, a small database is built, mapping portion pointers + // to their maximum size (used for kana compression) + SwTxtPortionTable aMaxWidth; + // for each line, an array of compression values is calculated + // this array is passed over to the info structure + SvUShorts* pKanaComp; + + ViewShell *pVsh; + + // pOut is the output device, pRef is the device used for formatting + OutputDevice* pOut; + OutputDevice* pRef; + + SwFont *pFnt; + SwUnderlineFont *pUnderFnt; // Font for underlining + SwTxtFrm *pFrm; + const SwViewOption *pOpt; + const XubString *pTxt; + xub_StrLen nIdx, nLen; + USHORT nKanaIdx; + sal_Bool bOnWin : 1; + sal_Bool bNotEOL : 1; + sal_Bool bURLNotify : 1; + sal_Bool bStopUnderFlow : 1;// Underflow gestoppt z.B. von einer FlyPortion + sal_Bool bFtnInside : 1; // the current line contains a footnote + sal_Bool bMulti : 1; // inside a multiportion + sal_Bool bFirstMulti : 1; // this flag is used for two purposes: + // - the multiportion is the first lineportion + // - indicates, if we are currently in second + // line of multi portion + sal_Bool bRuby : 1; // during the formatting of a phonetic line + sal_Bool bHanging : 1; // formatting of hanging punctuation allowed + sal_Bool bScriptSpace : 1; // space between different scripts (Asian/Latin) + sal_Bool bForbiddenChars : 1; // Forbidden start/endline characters + sal_Bool bSnapToGrid : 1; // paragraph snaps to grid + sal_uInt8 nDirection : 2; // writing direction: 0/90/180/270 degree + +protected: + void CtorInitTxtSizeInfo( SwTxtFrm *pFrm, SwFont *pFnt = 0, + const xub_StrLen nIdx = 0, + const xub_StrLen nLen = STRING_LEN ); + SwTxtSizeInfo() {} +public: + SwTxtSizeInfo( const SwTxtSizeInfo &rInf ); + SwTxtSizeInfo( const SwTxtSizeInfo &rInf, const XubString &rTxt, + const xub_StrLen nIdx = 0, + const xub_StrLen nLen = STRING_LEN ); + + inline SwTxtSizeInfo( SwTxtFrm *pTxtFrm, SwFont *pTxtFnt = 0, + const xub_StrLen nIndex = 0, + const xub_StrLen nLength = STRING_LEN ) + { CtorInitTxtSizeInfo( pTxtFrm, pTxtFnt, nIndex, nLength ); } + + // GetMultiAttr returns the text attribute of the multiportion, + // if rPos is inside any multi-line part. + // rPos will set to the end of the multi-line part. + SwMultiCreator* GetMultiCreator( xub_StrLen &rPos, SwMultiPortion* pM ) const; + + inline sal_Bool OnWin() const { return bOnWin; } + inline void SetOnWin( const sal_Bool bNew ) { bOnWin = bNew; } + inline sal_Bool NotEOL() const { return bNotEOL; } + inline void SetNotEOL( const sal_Bool bNew ) { bNotEOL = bNew; } + inline sal_Bool URLNotify() const { return bURLNotify; } + inline void SetURLNotify( const sal_Bool bNew ) { bURLNotify = bNew; } + inline sal_Bool StopUnderFlow() const { return bStopUnderFlow; } + inline void SetStopUnderFlow( const sal_Bool bNew ) { bStopUnderFlow = bNew; } + inline sal_Bool IsFtnInside() const { return bFtnInside; } + inline void SetFtnInside( const sal_Bool bNew ) { bFtnInside = bNew; } + inline sal_Bool IsMulti() const { return bMulti; } + inline void SetMulti( const sal_Bool bNew ) { bMulti = bNew; } + inline sal_Bool IsFirstMulti() const { return bFirstMulti; } + inline void SetFirstMulti( const sal_Bool bNew ) { bFirstMulti = bNew; } + inline sal_Bool IsRuby() const { return bRuby; } + inline void SetRuby( const sal_Bool bNew ) { bRuby = bNew; } + inline sal_Bool IsHanging() const { return bHanging; } + inline void SetHanging( const sal_Bool bNew ) { bHanging = bNew; } + inline sal_Bool HasScriptSpace() const { return bScriptSpace; } + inline void SetScriptSpace( const sal_Bool bNew ) { bScriptSpace = bNew; } + inline sal_Bool HasForbiddenChars() const { return bForbiddenChars; } + inline void SetForbiddenChars( const sal_Bool bN ) { bForbiddenChars = bN; } + inline sal_Bool SnapToGrid() const { return bSnapToGrid; } + inline void SetSnapToGrid( const sal_Bool bN ) { bSnapToGrid = bN; } + inline sal_uInt8 GetDirection() const { return nDirection; } + inline void SetDirection( const sal_uInt8 nNew ) { nDirection = nNew; } + inline sal_Bool IsRotated() const { return 0 != ( 1 & nDirection ); } + + inline ViewShell *GetVsh() { return pVsh; } + inline const ViewShell *GetVsh() const { return pVsh; } + + inline OutputDevice *GetOut() { return pOut; } + inline const OutputDevice *GetOut() const { return pOut; } + inline void SetOut( OutputDevice* pNewOut ) { pOut = pNewOut; } + + inline OutputDevice *GetRefDev() { return pRef; } + inline const OutputDevice *GetRefDev() const { return pRef; } + + inline SwFont *GetFont() { return pFnt; } + inline const SwFont *GetFont() const { return pFnt; } + inline void SetFont( SwFont *pNew ) { pFnt = pNew; } + void SelectFont(); + inline void SetUnderFnt( SwUnderlineFont* pNew ) { pUnderFnt = pNew; } + inline SwUnderlineFont* GetUnderFnt() const { return pUnderFnt; } + + inline const SwViewOption &GetOpt() const { return *pOpt; } + inline const XubString &GetTxt() const { return *pTxt; } + inline xub_Unicode GetChar( const xub_StrLen nPos ) const + { return pTxt->GetChar( nPos ); } + + inline KSHORT GetTxtHeight() const; + + // + // GetTxtSize + // + SwPosSize GetTxtSize( OutputDevice* pOut, const SwScriptInfo* pSI, + const XubString& rTxt, const xub_StrLen nIdx, + const xub_StrLen nLen, const USHORT nComp ) const; + SwPosSize GetTxtSize() const; + void GetTxtSize( const SwScriptInfo* pSI, const xub_StrLen nIdx, + const xub_StrLen nLen, const USHORT nComp, + USHORT& nMinSize, USHORT& nMaxSizeDiff ) const; + inline SwPosSize GetTxtSize( const SwScriptInfo* pSI, const xub_StrLen nIdx, + const xub_StrLen nLen, const USHORT nComp ) const; + inline SwPosSize GetTxtSize( const XubString &rTxt ) const; + + // + // GetTxtBreak + // + xub_StrLen GetTxtBreak( const long nLineWidth, + const xub_StrLen nMaxLen, + const USHORT nComp ) const; + xub_StrLen GetTxtBreak( const long nLineWidth, + const xub_StrLen nMaxLen, + const USHORT nComp, + xub_StrLen& rExtraCharPos ) const; + + inline KSHORT GetAscent() const; + + inline xub_StrLen GetIdx() const { return nIdx; } + inline void SetIdx( const xub_StrLen nNew ) { nIdx = nNew; } + inline xub_StrLen GetLen() const { return nLen; } + inline void SetLen( const xub_StrLen nNew ) { nLen = nNew; } + inline void SetTxt( const XubString &rNew ){ pTxt = &rNew; } + + friend SvStream &operator<<( SvStream &rOS, const SwTxtSizeInfo &rInf ); + +// 7780: Keine Bullets beim Symbol-Zeichensatz! + inline sal_Bool IsNoSymbol() const + { return RTL_TEXTENCODING_SYMBOL != pFnt->GetCharSet( pFnt->GetActual() ); } + + void NoteAnimation() const; + + // Home is where Your heart is... + inline SwTxtFrm *GetTxtFrm() { return pFrm; } + inline const SwTxtFrm *GetTxtFrm() const { return pFrm; } + + inline sal_Bool HasHint( xub_StrLen nPos ) const + { return _HasHint( pFrm->GetTxtNode(), nPos ); } + static sal_Bool _HasHint( const SwTxtNode* pTxtNode, xub_StrLen nPos ); + + // If Kana Compression is enabled, a minimum and maximum portion width + // is calculated. We format lines with minimal size and share remaining + // space among compressed kanas. + // During formatting, the maximum values of compressable portions are + // stored in aMaxWidth and discarded after a line has been formatted. + inline void SetMaxWidthDiff( ULONG nKey, USHORT nVal ) + { + aMaxWidth.Insert( nKey, nVal ); + }; + inline USHORT GetMaxWidthDiff( ULONG nKey ) + { + return (USHORT)aMaxWidth.Get( nKey ); + }; + inline void ResetMaxWidthDiff() + { + aMaxWidth.Clear(); + }; + inline sal_Bool CompressLine() + { + return (sal_Bool)aMaxWidth.Count(); + }; + + // + // Feature: Kana Compression + // + inline MSHORT GetKanaIdx() const { return nKanaIdx; } + inline void ResetKanaIdx(){ nKanaIdx = 0; } + inline void SetKanaIdx( MSHORT nNew ) { nKanaIdx = nNew; } + inline void IncKanaIdx() { ++nKanaIdx; } + inline void SetKanaComp( SvUShorts *pNew ){ pKanaComp = pNew; } + inline SvUShorts* GetpKanaComp() const { return pKanaComp; } + inline USHORT GetKanaComp() const + { return ( pKanaComp && nKanaIdx < pKanaComp->Count() ) + ? (*pKanaComp)[nKanaIdx] : 0; } + +#ifndef PRODUCT + sal_Bool IsOptCalm() const; + sal_Bool IsOptLow() const; + sal_Bool IsOptDbg() const; + sal_Bool IsOptTest1() const; + sal_Bool IsOptTest2() const; + sal_Bool IsOptTest3() const; + sal_Bool IsOptTest4() const; + sal_Bool IsOptTest5() const; + sal_Bool IsOptTest6() const; + sal_Bool IsOptTest7() const; + sal_Bool IsOptTest8() const; +#endif +}; + +/************************************************************************* + * class SwTxtPaintInfo + *************************************************************************/ + +class SwTxtPaintInfo : public SwTxtSizeInfo +{ + const SwWrongList *pWrongList; + const SwWrongList *pGrammarCheckList; + const SwWrongList *pSmartTags; // SMARTTAGS + std::vector<long>* pSpaceAdd; + const SvxBrushItem *pBrushItem; // Fuer den Hintergrund + SwRect aItemRect; // ebenfalls fuer den Hintergrund + SwTxtFly aTxtFly; // FlyFrm-Berechnung + Point aPos; // Ausgabeposition + SwRect aPaintRect; // Original Ausgaberechteck (aus Layout-Paint) + + MSHORT nSpaceIdx; + void _DrawText( const XubString &rText, const SwLinePortion &rPor, + const xub_StrLen nIdx, const xub_StrLen nLen, + const sal_Bool bKern, const sal_Bool bWrong = sal_False, + const sal_Bool bSmartTag = sal_False, + const sal_Bool bGrammarCheck = sal_False ); // SMARTTAGS + + SwTxtPaintInfo &operator=(const SwTxtPaintInfo&); + void _NotifyURL( const SwLinePortion &rPor ) const; + void _DrawBackBrush( const SwLinePortion &rPor ) const; + +protected: +#ifdef PRODUCT + SwTxtPaintInfo() { pFrm = 0; pWrongList = 0; pGrammarCheckList = 0; pWrongList = 0; pSmartTags = 0; pSpaceAdd = 0; pBrushItem = 0;} +#else + SwTxtPaintInfo() { pFrm = 0; pWrongList = 0; pGrammarCheckList = 0; pSmartTags = 0; pSpaceAdd = 0; + pBrushItem = ((SvxBrushItem*)-1);} +#endif +public: + SwTxtPaintInfo( const SwTxtPaintInfo &rInf ); + SwTxtPaintInfo( const SwTxtPaintInfo &rInf, const XubString &rTxt ); + + void CtorInitTxtPaintInfo( SwTxtFrm *pFrame, const SwRect &rPaint ); + + void SetBack( const SvxBrushItem *pItem, + const SwRect &rRect ) { pBrushItem = pItem; aItemRect = rRect;} + const SvxBrushItem *GetBrushItem() const { return pBrushItem; } + const SwRect &GetBrushRect() const { return aItemRect; } + + inline SwTxtPaintInfo( SwTxtFrm *pFrame, const SwRect &rPaint ) + { CtorInitTxtPaintInfo( pFrame, rPaint ); } + + inline SwTwips X() const { return aPos.X(); } + inline void X( const long nNew ) { aPos.X() = nNew; } + inline SwTwips Y() const { return aPos.Y(); } + inline void Y( const SwTwips nNew ) { aPos.Y() = nNew; } + + inline SwTxtFly *GetTxtFly() { return &aTxtFly; } + inline const SwTxtFly *GetTxtFly() const { return &aTxtFly; } + inline void DrawText( const XubString &rText, const SwLinePortion &rPor, + const xub_StrLen nIdx = 0, + const xub_StrLen nLen = STRING_LEN, + const sal_Bool bKern = sal_False) const; + inline void DrawText( const SwLinePortion &rPor, const xub_StrLen nLen, + const sal_Bool bKern = sal_False ) const; + inline void DrawMarkedText( const SwLinePortion &rPor, const xub_StrLen nLen, + const sal_Bool bKern, + const sal_Bool bWrong, + const sal_Bool bSmartTags, + const sal_Bool bGrammarCheck ) const; + + void DrawRect( const SwRect &rRect, sal_Bool bNoGraphic = sal_False, + sal_Bool bRetouche = sal_True ) const; + void DrawTab( const SwLinePortion &rPor ) const; + void DrawLineBreak( const SwLinePortion &rPor ) const; + void DrawRedArrow( const SwLinePortion &rPor ) const; + void DrawPostIts( const SwLinePortion &rPor, sal_Bool bScript ) const; + void DrawBackground( const SwLinePortion &rPor ) const; + void DrawViewOpt( const SwLinePortion &rPor, const MSHORT nWhich ) const; + inline void DrawBackBrush( const SwLinePortion &rPor ) const + { /* if( pFnt->GetBackColor() ) */ _DrawBackBrush( rPor ); } + + void DrawCheckBox( const SwFieldFormPortion &rPor, bool checked) const; + + inline void NotifyURL( const SwLinePortion &rPor ) const + { if( URLNotify() ) _NotifyURL( rPor ); } + + void CalcRect( const SwLinePortion& rPor, SwRect* pRect, SwRect* pIntersect = 0 ) const; + + inline SwTwips GetPaintOfst() const; + inline void SetPaintOfst( const SwTwips nNew ); + inline const Point &GetPos() const { return aPos; } + inline void SetPos( const Point &rNew ) { aPos = rNew; } + + inline const SwRect &GetPaintRect() const { return aPaintRect; } + inline void SetPaintRect( const SwRect &rNew ) { aPaintRect = rNew; } + + friend SvStream &operator<<( SvStream &rOS, const SwTxtPaintInfo &rInf ); + + // + // STUFF FOR JUSTIFIED ALIGNMENT + // + inline MSHORT GetSpaceIdx() const { return nSpaceIdx; } + inline void ResetSpaceIdx(){nSpaceIdx = 0; } + inline void SetSpaceIdx( MSHORT nNew ) { nSpaceIdx = nNew; } + inline void IncSpaceIdx() { ++nSpaceIdx; } + inline void RemoveFirstSpaceAdd() { pSpaceAdd->erase( pSpaceAdd->begin() ); } + inline long GetSpaceAdd() const + { return ( pSpaceAdd && nSpaceIdx < pSpaceAdd->size() ) + ? (*pSpaceAdd)[nSpaceIdx] : 0; } + + inline void SetpSpaceAdd( std::vector<long>* pNew ){ pSpaceAdd = pNew; } + inline std::vector<long>* GetpSpaceAdd() const { return pSpaceAdd; } + + + inline void SetWrongList( const SwWrongList *pNew ){ pWrongList = pNew; } + inline const SwWrongList* GetpWrongList() const { return pWrongList; } + + inline void SetGrammarCheckList( const SwWrongList *pNew ){ pGrammarCheckList = pNew; } + inline const SwWrongList* GetGrammarCheckList() const { return pGrammarCheckList; } + + // SMARTTAGS + inline void SetSmartTags( const SwWrongList *pNew ){ pSmartTags = pNew; } + inline const SwWrongList* GetSmartTags() const { return pSmartTags; } +}; + +/************************************************************************* + * class SwTxtFormatInfo + *************************************************************************/ + +class SwTxtFormatInfo : public SwTxtPaintInfo +{ + // temporary arguments for hyphenation + com::sun::star::beans::PropertyValues aHyphVals; + + SwLineLayout *pRoot; // die Root der aktuellen Zeile (pCurr) + SwLinePortion *pLast; // die letzte Portion + SwFlyPortion *pFly; // die nachfolgende FlyPortion + SwFldPortion *pLastFld; // umgebrochenes Feld + SwLinePortion *pUnderFlow; // Unterlaufsituation: letzte Portion + SwLinePortion *pRest; // Rest ist der Beginn der naechsten Zeile + + SwTabPortion *pLastTab; // die _letzte_ TabPortion + + xub_StrLen nSoftHyphPos; // SoftHyphPos fuer Hyphenate + xub_StrLen nHyphStart; // TxtPos, an der die interakt.Tr.z.Z. steht + xub_StrLen nHyphWrdStart; // gefundene Wort-Position + xub_StrLen nHyphWrdLen; // gefundene Wort-Laenge + xub_StrLen nLineStart; // aktueller Zeilenbeginn im rTxt + xub_StrLen nUnderScorePos; // enlarge repaint if underscore has been found + // --> FME 2004-11-25 #i34348# Changed type from USHORT to SwTwips + SwTwips nLeft; // linker Rand + SwTwips nRight; // rechter Rand + SwTwips nFirst; // EZE + // <-- + KSHORT nRealWidth; // "echte" Zeilenbreite + KSHORT nWidth; // "virtuelle" Zeilenbreite + KSHORT nLineHeight; // endgueltige Hoehe nach CalcLine + KSHORT nLineNettoHeight; // line height without spacing + KSHORT nForcedLeftMargin; // Verschiebung des linken Rands wg. Rahmen + + INT16 nMinLeading; // minimum number of chars before hyphenation point + INT16 nMinTrailing; // minimum number of chars after hyphenation point + INT16 nMinWordLength; // minimum length of word to be hyphenated + + sal_Bool bFull : 1; // Zeile ist voll + sal_Bool bFtnDone : 1; // Ftn bereits formatiert + sal_Bool bErgoDone : 1; // ErgoDone bereits formatiert + sal_Bool bNumDone : 1; // bNumDone bereits formatiert + sal_Bool bArrowDone : 1; // Pfeil nach links bei gescrollten Absaetzen + sal_Bool bStop : 1; // Sofort abbrechen, Zeile verwerfen. + sal_Bool bNewLine : 1; // Noch eine weitere Zeile formatieren. + sal_Bool bShift : 1; // Positionsaend.: Repaint bis auf Weiteres + sal_Bool bUnderFlow : 1; // Kontext: UnderFlow() ? + sal_Bool bInterHyph: 1; // interaktive Trennung ? + sal_Bool bAutoHyph : 1; // automatische Trennung ? + sal_Bool bDropInit : 1; // DropWidth einstellen. + sal_Bool bQuick : 1; // FormatQuick() + sal_Bool bNoEndHyph : 1; // Trennung am Zeilenende abgeschaltet wg. MaxHyphens + sal_Bool bNoMidHyph : 1; // Trennung vor Flies abgeschaltet wg. MaxHyphens + sal_Bool bIgnoreFly: 1; // FitToContent ignoriert Flies + sal_Bool bFakeLineStart: 1; // String has been replaced by field portion + // info structure only pretends that we are at + // the beginning of a line + + xub_Unicode cTabDecimal; // das _aktuelle_ Dezimalzeichen + xub_Unicode cHookChar; // fuer Tabs in Feldern etc. + sal_uInt8 nMaxHyph; // max. Zeilenanz. aufeinanderfolg. Trenn. + sal_Bool bTestFormat; // Testformatierung aus WouldFit, keine Benachrichtigungen etc. + + // Hyphenating ... + sal_Bool InitHyph( const sal_Bool bAuto = sal_False ); + sal_Bool _CheckFtnPortion( SwLineLayout* pCurr ); + +public: + void CtorInitTxtFormatInfo( SwTxtFrm *pFrm, const sal_Bool bInterHyph = sal_False, + const sal_Bool bQuick = sal_False, const sal_Bool bTst = sal_False ); + inline SwTxtFormatInfo(SwTxtFrm *pFrame,const sal_Bool bInterHyphL=sal_False, + const sal_Bool bQuickL = sal_False, const sal_Bool bTst = sal_False ) + { CtorInitTxtFormatInfo( pFrame, bInterHyphL, bQuickL, bTst ); } + + // For the formatting inside a double line in a line (multi-line portion) + // we need a modified text-format-info: + SwTxtFormatInfo( const SwTxtFormatInfo& rInf, SwLineLayout& rLay, + SwTwips nActWidth ); + + inline KSHORT Width() const { return nWidth; } + inline void Width( const KSHORT nNew ) { nWidth = nNew; } + void Init(); + + // liefert die erste veraenderte Position im Absatz zurueck + inline xub_StrLen GetReformatStart() const; + + // Raender + inline SwTwips Left() const { return nLeft; } + inline void Left( const SwTwips nNew ) { nLeft = nNew; } + inline SwTwips Right() const { return nRight; } + inline void Right( const SwTwips nNew ) { nRight = nNew; } + inline SwTwips First() const { return nFirst; } + inline void First( const SwTwips nNew ) { nFirst = nNew; } + inline SwTwips CurrLeft() const { return (nLineStart ? nLeft : nFirst); } + inline KSHORT RealWidth() const { return nRealWidth; } + inline void RealWidth( const KSHORT nNew ) { nRealWidth = nNew; } + inline KSHORT ForcedLeftMargin() const { return nForcedLeftMargin; } + inline void ForcedLeftMargin( const KSHORT nN ) { nForcedLeftMargin = nN; } + + inline sal_uInt8 &MaxHyph() { return nMaxHyph; } + inline const sal_uInt8 &MaxHyph() const { return nMaxHyph; } + + inline SwLineLayout *GetRoot() { return pRoot; } + inline const SwLineLayout *GetRoot() const { return pRoot; } + + inline void SetRoot( SwLineLayout *pNew ) { pRoot = pNew; } + inline SwLinePortion *GetLast() { return pLast; } + inline void SetLast( SwLinePortion *pNewLast ) { pLast = pNewLast; } + inline sal_Bool IsFull() const { return bFull; } + inline void SetFull( const sal_Bool bNew ) { bFull = bNew; } + inline sal_Bool IsHyphForbud() const + { return pFly ? bNoMidHyph : bNoEndHyph; } + inline void SetHyphForbud( const sal_Bool bNew ) + { if ( pFly ) bNoMidHyph = bNew; else bNoEndHyph = bNew; } + inline void ChkNoHyph( const sal_uInt8 bEnd, const sal_uInt8 bMid ) + { bNoEndHyph = (nMaxHyph && bEnd >= nMaxHyph); + bNoMidHyph = (nMaxHyph && bMid >= nMaxHyph); } + inline sal_Bool IsIgnoreFly() const { return bIgnoreFly; } + inline void SetIgnoreFly( const sal_Bool bNew ) { bIgnoreFly = bNew; } + inline sal_Bool IsFakeLineStart() const { return bFakeLineStart; } + inline void SetFakeLineStart( const sal_Bool bNew ) { bFakeLineStart = bNew; } + inline sal_Bool IsStop() const { return bStop; } + inline void SetStop( const sal_Bool bNew ) { bStop = bNew; } + inline SwLinePortion *GetRest() { return pRest; } + inline void SetRest( SwLinePortion *pNewRest ) { pRest = pNewRest; } + inline sal_Bool IsNewLine() const { return bNewLine; } + inline void SetNewLine( const sal_Bool bNew ) { bNewLine = bNew; } + inline sal_Bool IsShift() const { return bShift; } + inline void SetShift( const sal_Bool bNew ) { bShift = bNew; } + inline sal_Bool IsInterHyph() const { return bInterHyph; } + inline sal_Bool IsAutoHyph() const { return bAutoHyph; } + inline sal_Bool IsUnderFlow() const { return bUnderFlow; } + inline void ClrUnderFlow() { bUnderFlow = sal_False; } + inline sal_Bool IsDropInit() const { return bDropInit; } + inline void SetDropInit( const sal_Bool bNew ) { bDropInit = bNew; } + inline sal_Bool IsQuick() const { return bQuick; } + inline sal_Bool IsTest() const { return bTestFormat; } + + inline xub_StrLen GetLineStart() const { return nLineStart; } + inline void SetLineStart( const xub_StrLen nNew ) { nLineStart = nNew; } + + // these are used during fly calculation + inline KSHORT GetLineHeight() const { return nLineHeight; } + inline void SetLineHeight( const KSHORT nNew ) { nLineHeight = nNew; } + inline KSHORT GetLineNettoHeight() const { return nLineNettoHeight; } + inline void SetLineNettoHeight( const KSHORT nNew ) { nLineNettoHeight = nNew; } + + inline const SwLinePortion *GetUnderFlow() const { return pUnderFlow; } + inline SwLinePortion *GetUnderFlow() { return pUnderFlow; } + inline void SetUnderFlow( SwLinePortion *pNew ) + { pUnderFlow = pNew; bUnderFlow = sal_True; } + inline xub_StrLen GetSoftHyphPos() const { return nSoftHyphPos; } + inline void SetSoftHyphPos( const xub_StrLen nNew ) { nSoftHyphPos = nNew; } + + inline void SetParaFtn(); + + // FlyFrms + inline SwFlyPortion *GetFly() { return pFly; } + inline void SetFly( SwFlyPortion *pNew ) { pFly = pNew; } + + inline const SwAttrSet& GetCharAttr() const; + + // Tabs + inline SwTabPortion *GetLastTab() { return pLastTab; } + inline void SetLastTab( SwTabPortion *pNew ) { pLastTab = pNew; } + inline xub_Unicode GetTabDecimal() const { return cTabDecimal; } + inline void SetTabDecimal( const xub_Unicode cNew ) { cTabDecimal = cNew;} + + // Last* + inline SwFldPortion *GetLastFld() { return pLastFld; } + inline void SetLastFld( SwFldPortion *pNew ) { pLastFld = pNew; } + + inline void ClearHookChar() { cHookChar = 0; } + inline void SetHookChar( const xub_Unicode cNew ) { cHookChar = cNew; } + inline xub_Unicode GetHookChar() const { return cHookChar; } + + // Done-Flags + inline sal_Bool IsFtnDone() const { return bFtnDone; } + inline void SetFtnDone( const sal_Bool bNew ) { bFtnDone = bNew; } + inline sal_Bool IsErgoDone() const { return bErgoDone; } + inline void SetErgoDone( const sal_Bool bNew ) { bErgoDone = bNew; } + inline sal_Bool IsNumDone() const { return bNumDone; } + inline void SetNumDone( const sal_Bool bNew ) { bNumDone = bNew; } + inline sal_Bool IsArrowDone() const { return bArrowDone; } + inline void SetArrowDone( const sal_Bool bNew ) { bArrowDone = bNew; } + + // Fuer SwTxtPortion::Hyphenate + inline sal_Bool IsSoftHyph( const xub_StrLen nPos ) const; + sal_Bool ChgHyph( const sal_Bool bNew ); + + // Soll die Trennhilfe angeschmissen werden? + sal_Bool IsHyphenate() const; + inline void SetHyphStart( const xub_StrLen nNew ) { nHyphStart = nNew; } + inline xub_StrLen GetHyphStart() const { return nHyphStart; } + inline void SetHyphWrdStart( const xub_StrLen nNew ) { nHyphWrdStart = nNew; } + inline xub_StrLen GetHyphWrdStart() const { return nHyphWrdStart; } + inline void SetHyphWrdLen( const xub_StrLen nNew ) { nHyphWrdLen = nNew; } + inline xub_StrLen GetHyphWrdLen() const { return nHyphWrdLen; } + inline xub_StrLen GetUnderScorePos() const { return nUnderScorePos; } + inline void SetUnderScorePos( xub_StrLen nNew ) { nUnderScorePos = nNew; } + + // ruft HyphenateWord() des Hyphenators + ::com::sun::star::uno::Reference< + ::com::sun::star::linguistic2::XHyphenatedWord > + HyphWord( const String &rTxt, const USHORT nMinTrail ); + const com::sun::star::beans::PropertyValues & + GetHyphValues() const; + + sal_Bool CheckFtnPortion( SwLineLayout* pCurr ) + { return IsFtnInside() && _CheckFtnPortion( pCurr ); } + + // Dropcaps vom SwTxtFormatter::CTOR gerufen. + const SwFmtDrop *GetDropFmt() const; + + // setzt die FormatInfo wieder in den Anfangszustand + void Reset( const SwTxtFrm *pFrame); // , const sal_Bool bAll ); + + // Sets the last SwKernPortion as pLast, if it is followed by empty portions + BOOL LastKernPortion(); + + // Sucht ab nIdx bis nEnd nach Tabs, TabDec, TXTATR und BRK. + // Return: gefundene Position, setzt ggf. cHookChar + xub_StrLen ScanPortionEnd( const xub_StrLen nStart, const xub_StrLen nEnd ); + +// friend ostream &operator<<( ostream &rOS, const SwTxtFormatInfo &rInf ); + friend SvStream &operator<<( SvStream &rOS, const SwTxtFormatInfo &rInf ); +}; + +/************************************************************************* + * class SwTxtSlot + *************************************************************************/ + +// Fuer die Textersetzung und Restaurierung der SwTxtSizeInfo. +// Die Art und Weise ist etwas kriminell, rInf ist const und wird +// trotzdem veraendert. Da rInf im DTOR wieder restauriert wird, +// ist dies zulaessig, es handelt sich um ein "logisches const". + +class SwTxtSlot +{ + XubString aTxt; + const XubString *pOldTxt; + const SwWrongList* pOldSmartTagList; + const SwWrongList* pOldGrammarCheckList; + SwWrongList* pTempList; + xub_StrLen nIdx; + xub_StrLen nLen; + sal_Bool bOn; +protected: + SwTxtSizeInfo *pInf; +public: + // Der Ersetzungstring kommt wahlweise aus der Portion via GetExpText() + // oder aus dem char Pointer pCh, wenn dieser ungleich NULL ist. + SwTxtSlot( const SwTxtSizeInfo *pNew, const SwLinePortion *pPor, bool bTxtLen, + bool bExgLists, const sal_Char *pCh = NULL ); + ~SwTxtSlot(); + inline sal_Bool IsOn() const { return bOn; } +}; + +/************************************************************************* + * class SwFontSave + *************************************************************************/ + +class SwFontSave +{ + SwTxtSizeInfo *pInf; + SwFont *pFnt; + SwAttrIter *pIter; +public: + SwFontSave( const SwTxtSizeInfo &rInf, SwFont *pFnt, + SwAttrIter* pItr = NULL ); + ~SwFontSave(); +}; + +/************************************************************************* + * class SwDefFontSave + *************************************************************************/ + +class SwDefFontSave +{ + SwTxtSizeInfo *pInf; + SwFont *pFnt; + SwFont *pNewFnt; + sal_Bool bAlter; +public: + SwDefFontSave( const SwTxtSizeInfo &rInf ); + ~SwDefFontSave(); +}; + +/************************************************************************* + * Inline-Implementierungen SwTxtSizeInfo + *************************************************************************/ + +inline KSHORT SwTxtSizeInfo::GetAscent() const +{ + ASSERT( GetOut(), "SwTxtSizeInfo::GetAscent() without pOut" ) + return ((SwFont*)GetFont())->GetAscent( pVsh, *GetOut() ); +} + +inline KSHORT SwTxtSizeInfo::GetTxtHeight() const +{ + ASSERT( GetOut(), "SwTxtSizeInfo::GetTxtHeight() without pOut" ) + return ((SwFont*)GetFont())->GetHeight( pVsh, *GetOut() ); +} + +inline SwPosSize SwTxtSizeInfo::GetTxtSize( const XubString &rTxt ) const +{ + return GetTxtSize( pOut, 0, rTxt, 0, rTxt.Len(), 0 ); +} + +inline SwPosSize SwTxtSizeInfo::GetTxtSize( const SwScriptInfo* pSI, + const xub_StrLen nNewIdx, + const xub_StrLen nNewLen, + const USHORT nCompress ) const +{ + return GetTxtSize( pOut, pSI, *pTxt, nNewIdx, nNewLen, nCompress ); +} + +/************************************************************************* + * Inline-Implementierungen SwTxtPaintInfo + *************************************************************************/ + +inline SwTwips SwTxtPaintInfo::GetPaintOfst() const +{ + return GetParaPortion()->GetRepaint()->GetOfst(); +} + +inline void SwTxtPaintInfo::SetPaintOfst( const SwTwips nNew ) +{ + GetParaPortion()->GetRepaint()->SetOfst( nNew ); +} + + +inline void SwTxtPaintInfo::DrawText( const XubString &rText, + const SwLinePortion &rPor, + const xub_StrLen nStart, const xub_StrLen nLength, + const sal_Bool bKern ) const +{ + ((SwTxtPaintInfo*)this)->_DrawText( rText, rPor, nStart, nLength, bKern ); +} + +inline void SwTxtPaintInfo::DrawText( const SwLinePortion &rPor, + const xub_StrLen nLength, const sal_Bool bKern ) const +{ + ((SwTxtPaintInfo*)this)->_DrawText( *pTxt, rPor, nIdx, nLength, bKern ); +} + +inline void SwTxtPaintInfo::DrawMarkedText( const SwLinePortion &rPor, + const xub_StrLen nLength, + const sal_Bool bKern, + const sal_Bool bWrong, + const sal_Bool bSmartTags, + const sal_Bool bGrammarCheck ) const +{ + ((SwTxtPaintInfo*)this)->_DrawText( *pTxt, rPor, nIdx, nLength, bKern, bWrong, bSmartTags, bGrammarCheck ); +} + +/************************************************************************* + * Inline-Implementierungen SwTxtFormatInfo + *************************************************************************/ + +inline xub_StrLen SwTxtFormatInfo::GetReformatStart() const +{ + return GetParaPortion()->GetReformat()->Start(); +} + +inline const SwAttrSet& SwTxtFormatInfo::GetCharAttr() const +{ + return GetTxtFrm()->GetTxtNode()->GetSwAttrSet(); +} + +inline void SwTxtFormatInfo::SetParaFtn() +{ + GetTxtFrm()->SetFtn( sal_True ); +} + +inline sal_Bool SwTxtFormatInfo::IsSoftHyph( const xub_StrLen nPos ) const +{ + return CHAR_SOFTHYPHEN == GetTxtFrm()->GetTxtNode()->GetTxt().GetChar(nPos); +} + + + +#endif + diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx new file mode 100644 index 000000000000..b4b16d3f3ecb --- /dev/null +++ b/sw/source/core/text/itradj.cxx @@ -0,0 +1,922 @@ + /************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itradj.cxx,v $ + * $Revision: 1.24.112.4 $ + * + * 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 _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <vcl/outdev.hxx> +#include <IDocumentSettingAccess.hxx> + +#include "frame.hxx" // CalcFlyAdjust() +#include "paratr.hxx" +#include "txtcfg.hxx" +#include "itrtxt.hxx" +#include "porglue.hxx" +#include "porlay.hxx" +#include "porfly.hxx" // CalcFlyAdjust() +#include "pordrop.hxx" // CalcFlyAdjust() +#include "pormulti.hxx" +#include <portab.hxx> + +#define MIN_TAB_WIDTH 60 + +using namespace ::com::sun::star; + +/************************************************************************* + * SwTxtAdjuster::FormatBlock() + *************************************************************************/ + +void SwTxtAdjuster::FormatBlock( ) +{ + // In der letzten Zeile gibt's keinen Blocksatz. + // Und bei Tabulatoren aus Tradition auch nicht. + // 7701: wenn Flys im Spiel sind, geht's weiter + + const SwLinePortion *pFly = 0; + + sal_Bool bSkip = !IsLastBlock() && + nStart + pCurr->GetLen() >= GetInfo().GetTxt().Len(); + + // ????: mehrzeilige Felder sind fies: wir muessen kontrollieren, + // ob es noch andere Textportions im Absatz gibt. + if( bSkip ) + { + const SwLineLayout *pLay = pCurr->GetNext(); + while( pLay && !pLay->GetLen() ) + { + const SwLinePortion *pPor = pCurr->GetFirstPortion(); + while( pPor && bSkip ) + { + if( pPor->InTxtGrp() ) + bSkip = sal_False; + pPor = pPor->GetPortion(); + } + pLay = bSkip ? pLay->GetNext() : 0; + } + } + + if( bSkip ) + { + if( !GetInfo().GetParaPortion()->HasFly() ) + { + if( IsLastCenter() ) + CalcFlyAdjust( pCurr ); + pCurr->FinishSpaceAdd(); + return; + } + else + { + const SwLinePortion *pTmpFly = NULL; + + // 7701: beim letzten Fly soll Schluss sein + const SwLinePortion *pPos = pCurr->GetFirstPortion(); + while( pPos ) + { + // Ich suche jetzt den letzten Fly, hinter dem noch Text ist: + if( pPos->IsFlyPortion() ) + pTmpFly = pPos; // Ein Fly wurde gefunden + else if ( pTmpFly && pPos->InTxtGrp() ) + { + pFly = pTmpFly; // Ein Fly mit nachfolgendem Text! + pTmpFly = NULL; + } + pPos = pPos->GetPortion(); + } + // 8494: Wenn keiner gefunden wurde, ist sofort Schluss! + if( !pFly ) + { + if( IsLastCenter() ) + CalcFlyAdjust( pCurr ); + pCurr->FinishSpaceAdd(); + return; + } + } + } + + const xub_StrLen nOldIdx = GetInfo().GetIdx(); + GetInfo().SetIdx( nStart ); + CalcNewBlock( pCurr, pFly ); + GetInfo().SetIdx( nOldIdx ); + GetInfo().GetParaPortion()->GetRepaint()->SetOfst(0); +} + +/************************************************************************* + * lcl_CheckKashidaPositions() + *************************************************************************/ +bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, + xub_StrLen& nKashidas, xub_StrLen& nGluePortion ) +{ + // i60594 validate Kashida justification + xub_StrLen nIdx = rItr.GetStart(); + xub_StrLen nEnd = rItr.GetEnd(); + + // Note on calling KashidaJustify(): + // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean + // total number of kashida positions, or the number of kashida positions after some positions + // have been dropped. + // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before. + nKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 ); + + if (!nKashidas) // nothing to do + return true; + + // kashida positions found in SwScriptInfo are not necessarily valid in every font + // if two characters are replaced by a ligature glyph, there will be no place for a kashida + xub_StrLen* pKashidaPos = new xub_StrLen [ nKashidas ]; + xub_StrLen* pKashidaPosDropped = new xub_StrLen [ nKashidas ]; + rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos ); + xub_StrLen nKashidaIdx = 0; + while ( nKashidas && nIdx < nEnd ) + { + rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() ); + xub_StrLen nNext = rItr.GetNextAttr(); + + // is there also a script change before? + // if there is, nNext should point to the script change + xub_StrLen nNextScript = rSI.NextScriptChg( nIdx ); + if( nNextScript < nNext ) + nNext = nNextScript; + + if ( nNext == STRING_LEN || nNext > nEnd ) + nNext = nEnd; + xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx ); + if ( nKashidasInAttr ) + { + xub_StrLen nKashidasDropped = 0; + if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) ) + { + nKashidasDropped = nKashidasInAttr; + nKashidas -= nKashidasDropped; + } + else + { + ULONG nOldLayout = rInf.GetOut()->GetLayoutMode(); + rInf.GetOut()->SetLayoutMode ( nOldLayout | TEXT_LAYOUT_BIDI_RTL ); + nKashidasDropped = rInf.GetOut()->ValidateKashidas ( rInf.GetTxt(), nIdx, nNext - nIdx, + nKashidasInAttr, pKashidaPos + nKashidaIdx, + pKashidaPosDropped ); + rInf.GetOut()->SetLayoutMode ( nOldLayout ); + if ( nKashidasDropped ) + { + rSI.MarkKashidasInvalid ( nKashidasDropped, pKashidaPosDropped ); + nKashidas -= nKashidasDropped; + nGluePortion -= nKashidasDropped; + } + } + nKashidaIdx += nKashidasInAttr; + } + nIdx = nNext; + } + delete[] pKashidaPos; + delete[] pKashidaPosDropped; + + // return false if all kashidas have been eliminated + return (nKashidas > 0); +} + +/************************************************************************* + * lcl_CheckKashidaWidth() + *************************************************************************/ +bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, xub_StrLen& nKashidas, + xub_StrLen& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd ) +{ + // check kashida width + // if width is smaller than minimal kashida width allowed by fonts in the current line + // drop one kashida after the other until kashida width is OK + bool bAddSpaceChanged; + while ( nKashidas ) + { + bAddSpaceChanged = false; + xub_StrLen nIdx = rItr.GetStart(); + xub_StrLen nEnd = rItr.GetEnd(); + while ( nIdx < nEnd ) + { + rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() ); + xub_StrLen nNext = rItr.GetNextAttr(); + + // is there also a script change before? + // if there is, nNext should point to the script change + xub_StrLen nNextScript = rSI.NextScriptChg( nIdx ); + if( nNextScript < nNext ) + nNext = nNextScript; + + if ( nNext == STRING_LEN || nNext > nEnd ) + nNext = nEnd; + xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx ); + + long nFontMinKashida = rInf.GetOut()->GetMinKashida(); + if ( nFontMinKashida && nKashidasInAttr && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) ) + { + xub_StrLen nKashidasDropped = 0; + while ( nKashidas && nGluePortion && nKashidasInAttr && + nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida ) + { + --nGluePortion; + --nKashidas; + --nKashidasInAttr; + ++nKashidasDropped; + if( !nKashidas || !nGluePortion ) // nothing left, return false to + return false; // do regular blank justification + + nSpaceAdd = nGluePortionWidth / nGluePortion; + bAddSpaceChanged = true; + } + if( nKashidasDropped ) + rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx ); + } + if ( bAddSpaceChanged ) + break; // start all over again + nIdx = nNext; + } + if ( !bAddSpaceChanged ) + break; // everything was OK + } + return true; +} + +/************************************************************************* + * SwTxtAdjuster::CalcNewBlock() + * + * CalcNewBlock() darf erst nach CalcLine() gerufen werden ! + * Aufgespannt wird immer zwischen zwei RandPortions oder FixPortions + * (Tabs und Flys). Dabei werden die Glues gezaehlt und ExpandBlock gerufen. + *************************************************************************/ + +void SwTxtAdjuster::CalcNewBlock( SwLineLayout *pCurrent, + const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida ) +{ + ASSERT( GetInfo().IsMulti() || SVX_ADJUST_BLOCK == GetAdjust(), + "CalcNewBlock: Why?" ); + ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" ); + + pCurrent->InitSpaceAdd(); + xub_StrLen nGluePortion = 0; + xub_StrLen nCharCnt = 0; + MSHORT nSpaceIdx = 0; + + // i60591: hennerdrews + SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); + SwTxtSizeInfo aInf ( GetTxtFrm() ); + SwTxtIter aItr ( GetTxtFrm(), &aInf ); + + if ( rSI.CountKashida() ) + { + while (aItr.GetCurr() != pCurrent && aItr.GetNext()) + aItr.Next(); + + if( bSkipKashida ) + { + rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength()); + } + else + { + rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() ); + rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() ); + } + } + + // Nicht vergessen: + // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite ! + if (!bSkipKashida) + CalcRightMargin( pCurrent, nReal ); + + // --> FME 2005-06-08 #i49277# + const sal_Bool bDoNotJustifyLinesWithManualBreak = + GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); + // <-- + + SwLinePortion *pPos = pCurrent->GetPortion(); + + while( pPos ) + { + if ( bDoNotJustifyLinesWithManualBreak && + pPos->IsBreakPortion() && !IsLastBlock() ) + { + pCurrent->FinishSpaceAdd(); + break; + } + + if ( pPos->InTxtGrp() ) + nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt ); + else if( pPos->IsMultiPortion() ) + { + SwMultiPortion* pMulti = (SwMultiPortion*)pPos; + // a multiportion with a tabulator inside breaks the text adjustment + // a ruby portion will not be stretched by text adjustment + // a double line portion takes additional space for each blank + // in the wider line + if( pMulti->HasTabulator() ) + { + if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() ) + pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); + + nSpaceIdx++; + nGluePortion = 0; + nCharCnt = 0; + } + else if( pMulti->IsDouble() ) + nGluePortion = nGluePortion + ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt(); + else if ( pMulti->IsBidi() ) + nGluePortion = nGluePortion + ((SwBidiPortion*)pMulti)->GetSpaceCnt( GetInfo() ); // i60594 + } + + if( pPos->InGlueGrp() ) + { + if( pPos->InFixMargGrp() ) + { + if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() ) + pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); + + const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() * + SPACING_PRECISION_FACTOR; + + xub_StrLen nKashidas = 0; + if( nGluePortion && rSI.CountKashida() && !bSkipKashida ) + { + // kashida positions found in SwScriptInfo are not necessarily valid in every font + // if two characters are replaced by a ligature glyph, there will be no place for a kashida + if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion )) + { + // all kashida positions are invalid + // do regular blank justification + pCurrent->FinishSpaceAdd(); + GetInfo().SetIdx( nStart ); + CalcNewBlock( pCurrent, pStopAt, nReal, true ); + return; + } + } + + if( nGluePortion ) + { + long nSpaceAdd = nGluePortionWidth / nGluePortion; + + // i60594 + if( rSI.CountKashida() && !bSkipKashida ) + { + if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd )) + { + // no kashidas left + // do regular blank justification + pCurrent->FinishSpaceAdd(); + GetInfo().SetIdx( nStart ); + CalcNewBlock( pCurrent, pStopAt, nReal, true ); + return; + } + } + + pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx ); + pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() ); + } + else if ( IsOneBlock() && nCharCnt > 1 ) + { + const long nSpaceAdd = - nGluePortionWidth / ( nCharCnt - 1 ); + pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx ); + pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() ); + } + + nSpaceIdx++; + nGluePortion = 0; + nCharCnt = 0; + } + else + ++nGluePortion; + } + GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() ); + if ( pPos == pStopAt ) + { + pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); + break; + } + pPos = pPos->GetPortion(); + } +} + +/************************************************************************* + * SwTxtAdjuster::CalcKanaAdj() + *************************************************************************/ + +SwTwips SwTxtAdjuster::CalcKanaAdj( SwLineLayout* pCurrent ) +{ + ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" ); + ASSERT( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" ); + + SvUShorts *pNewKana = new SvUShorts; + pCurrent->SetKanaComp( pNewKana ); + + const USHORT nNull = 0; + MSHORT nKanaIdx = 0; + long nKanaDiffSum = 0; + SwTwips nRepaintOfst = 0; + SwTwips nX = 0; + sal_Bool bNoCompression = sal_False; + + // Nicht vergessen: + // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite ! + CalcRightMargin( pCurrent, 0 ); + + SwLinePortion* pPos = pCurrent->GetPortion(); + + while( pPos ) + { + if ( pPos->InTxtGrp() ) + { + // get maximum portion width from info structure, calculated + // during text formatting + USHORT nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pPos ); + + // check, if information is stored under other key + if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() ) + nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pCurrent ); + + // calculate difference between portion width and max. width + nKanaDiffSum += nMaxWidthDiff; + + // we store the beginning of the first compressable portion + // for repaint + if ( nMaxWidthDiff && !nRepaintOfst ) + nRepaintOfst = nX + GetLeftMargin(); + } + else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) + { + if ( nKanaIdx == pCurrent->GetKanaComp().Count() ) + pCurrent->GetKanaComp().Insert( nNull, nKanaIdx ); + + USHORT nRest; + + if ( pPos->InTabGrp() ) + { + nRest = ! bNoCompression && + ( pPos->Width() > MIN_TAB_WIDTH ) ? + pPos->Width() - MIN_TAB_WIDTH : + 0; + + // for simplifying the handling of left, right ... tabs, + // we do expand portions, which are lying behind + // those special tabs + bNoCompression = !pPos->IsTabLeftPortion(); + } + else + { + nRest = ! bNoCompression ? + ((SwGluePortion*)pPos)->GetPrtGlue() : + 0; + + bNoCompression = sal_False; + } + + if( nKanaDiffSum ) + { + ULONG nCompress = ( 10000 * nRest ) / nKanaDiffSum; + + if ( nCompress >= 10000 ) + // kanas can be expanded to 100%, and there is still + // some space remaining + nCompress = 0; + + else + nCompress = 10000 - nCompress; + + ( pCurrent->GetKanaComp() )[ nKanaIdx ] = (USHORT)nCompress; + nKanaDiffSum = 0; + } + + nKanaIdx++; + } + + nX += pPos->Width(); + pPos = pPos->GetPortion(); + } + + // set portion width + nKanaIdx = 0; + USHORT nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ]; + pPos = pCurrent->GetPortion(); + long nDecompress = 0; + nKanaDiffSum = 0; + + while( pPos ) + { + if ( pPos->InTxtGrp() ) + { + const USHORT nMinWidth = pPos->Width(); + + // get maximum portion width from info structure, calculated + // during text formatting + USHORT nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pPos ); + + // check, if information is stored under other key + if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() ) + nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (ULONG)pCurrent ); + nKanaDiffSum += nMaxWidthDiff; + pPos->Width( nMinWidth + + ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 ); + nDecompress += pPos->Width() - nMinWidth; + } + else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) + { + if( nCompress ) + { + nKanaDiffSum *= nCompress; + nKanaDiffSum /= 10000; + } + + pPos->Width( static_cast<USHORT>(pPos->Width() - nDecompress) ); + + if ( pPos->InTabGrp() ) + // set fix width to width + ((SwTabPortion*)pPos)->SetFixWidth( pPos->Width() ); + + const SvUShorts& rKanaComp = pCurrent->GetKanaComp(); + if ( ++nKanaIdx < rKanaComp.Count() ) + nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ]; + + nKanaDiffSum = 0; + nDecompress = 0; + } + pPos = pPos->GetPortion(); + } + + return nRepaintOfst; +} + +/************************************************************************* + * SwTxtAdjuster::CalcRightMargin() + *************************************************************************/ + +SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent, + SwTwips nReal ) +{ + long nRealWidth; + const USHORT nRealHeight = GetLineHeight(); + const USHORT nLineHeight = pCurrent->Height(); + + KSHORT nPrtWidth = pCurrent->PrtWidth(); + SwLinePortion *pLast = pCurrent->FindLastPortion(); + + if( GetInfo().IsMulti() ) + nRealWidth = nReal; + else + { + nRealWidth = GetLineWidth(); + // Fuer jeden FlyFrm, der in den rechten Rand hineinragt, + // wird eine FlyPortion angelegt. + const long nLeftMar = GetLeftMargin(); + SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight, + nRealWidth - nPrtWidth, nLineHeight ); + + SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect ); + while( pFly && long( nPrtWidth )< nRealWidth ) + { + pLast->Append( pFly ); + pLast = pFly; + if( pFly->Fix() > nPrtWidth ) + pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1); + nPrtWidth += pFly->Width() + 1; + aCurrRect.Left( nLeftMar + nPrtWidth ); + pFly = CalcFlyPortion( nRealWidth, aCurrRect ); + } + if( pFly ) + delete pFly; + } + + SwMarginPortion *pRight = new SwMarginPortion( 0 ); + pLast->Append( pRight ); + + if( long( nPrtWidth )< nRealWidth ) + pRight->PrtWidth( KSHORT( nRealWidth - nPrtWidth ) ); + + // pCurrent->Width() wird auf die reale Groesse gesetzt, + // da jetzt die MarginPortions eingehaengt sind. + // Dieser Trick hat wundersame Auswirkungen. + // Wenn pCurrent->Width() == nRealWidth ist, dann wird das gesamte + // Adjustment implizit ausgecontert. GetLeftMarginAdjust() und + // IsBlocksatz() sind der Meinung, sie haetten eine mit Zeichen + // gefuellte Zeile. + + pCurrent->PrtWidth( KSHORT( nRealWidth ) ); + return pRight; +} + +/************************************************************************* + * SwTxtAdjuster::CalcFlyAdjust() + *************************************************************************/ + +void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent ) +{ + // 1) Es wird ein linker Rand eingefuegt: + SwMarginPortion *pLeft = pCurrent->CalcLeftMargin(); + SwGluePortion *pGlue = pLeft; // die letzte GluePortion + + + // 2) Es wird ein rechter Rand angehaengt: + // CalcRightMargin berechnet auch eventuelle Ueberlappungen mit + // FlyFrms. + CalcRightMargin( pCurrent ); + + SwLinePortion *pPos = pLeft->GetPortion(); + xub_StrLen nLen = 0; + + // Wenn wir nur eine Zeile vorliegen haben und die Textportion zusammen + // haengend ist und wenn zentriert wird, dann ... + + sal_Bool bComplete = 0 == nStart; + const sal_Bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT); + sal_Bool bMultiTab = sal_False; + + while( pPos ) + { + if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() ) + bMultiTab = sal_True; + else if( pPos->InFixMargGrp() && + ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) ) + { + // in tab compat mode we do not want to change tab portions + // in non tab compat mode we do not want to change margins if we + // found a multi portion with tabs + if( SVX_ADJUST_RIGHT == GetAdjust() ) + ((SwGluePortion*)pPos)->MoveAllGlue( pGlue ); + else + { + // Eine schlaue Idee von MA: + // Fuer die erste Textportion wird rechtsbuendig eingestellt, + // fuer die letzte linksbuendig. + + // Die erste Textportion kriegt den ganzen Glue + // Aber nur, wenn wir mehr als eine Zeile besitzen. + if( bComplete && GetInfo().GetTxt().Len() == nLen ) + ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); + else + { + if ( ! bTabCompat ) + { + if( pLeft == pGlue ) + { + // Wenn es nur einen linken und rechten Rand gibt, + // dann teilen sich die Raender den Glue. + if( nLen + pPos->GetLen() >= pCurrent->GetLen() ) + ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); + else + ((SwGluePortion*)pPos)->MoveAllGlue( pGlue ); + } + else + { + // Die letzte Textportion behaelt sein Glue + if( !pPos->IsMarginPortion() ) + ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); + } + } + else + ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); + } + } + + pGlue = (SwFlyPortion*)pPos; + bComplete = sal_False; + } + nLen = nLen + pPos->GetLen(); + pPos = pPos->GetPortion(); + } + + if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() ) + // portions are moved to the right if possible + pLeft->AdjustRight( pCurrent ); +} + +/************************************************************************* + * SwTxtAdjuster::CalcAdjLine() + *************************************************************************/ + +void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent ) +{ + ASSERT( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" ); + + pCurrent->SetFormatAdj(sal_False); + + SwParaPortion* pPara = GetInfo().GetParaPortion(); + + switch( GetAdjust() ) + { + case SVX_ADJUST_RIGHT: + case SVX_ADJUST_CENTER: + { + CalcFlyAdjust( pCurrent ); + pPara->GetRepaint()->SetOfst( 0 ); + break; + } + case SVX_ADJUST_BLOCK: + { + // disabled for #i13507# + // 8311: In Zeilen mit LineBreaks gibt es keinen Blocksatz! +/* if( pCurrent->GetLen() && + CH_BREAK == GetInfo().GetChar( nStart + pCurrent->GetLen() - 1 ) && + !IsLastBlock() ) + { + if( IsLastCenter() ) + { + CalcFlyAdjust( pCurrent ); + pPara->GetRepaint()->SetOfst( 0 ); + break; + } + return; + } +*/ FormatBlock(); + break; + } + default : return; + } +} + +/************************************************************************* + * SwTxtAdjuster::CalcFlyPortion() + * + * Die Berechnung hat es in sich: nCurrWidth geibt die Breite _vor_ dem + * aufaddieren des Wortes das noch auf die Zeile passt! Aus diesem Grund + * stimmt die Breite der FlyPortion auch, wenn die Blockierungssituation + * bFirstWord && !WORDFITS eintritt. + *************************************************************************/ + +SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth, + const SwRect &rCurrRect ) +{ + SwTxtFly aTxtFly( GetTxtFrm() ); + + const KSHORT nCurrWidth = pCurr->PrtWidth(); + SwFlyPortion *pFlyPortion = 0; + + SwRect aLineVert( rCurrRect ); + if ( GetTxtFrm()->IsRightToLeft() ) + GetTxtFrm()->SwitchLTRtoRTL( aLineVert ); + if ( GetTxtFrm()->IsVertical() ) + GetTxtFrm()->SwitchHorizontalToVertical( aLineVert ); + + // aFlyRect ist dokumentglobal ! + SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) ); + + if ( GetTxtFrm()->IsRightToLeft() ) + GetTxtFrm()->SwitchRTLtoLTR( aFlyRect ); + if ( GetTxtFrm()->IsVertical() ) + GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect ); + + // Wenn ein Frame ueberlappt, wird eine Portion eroeffnet. + if( aFlyRect.HasArea() ) + { + // aLocal ist framelokal + SwRect aLocal( aFlyRect ); + aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() ); + if( nCurrWidth > aLocal.Left() ) + aLocal.Left( nCurrWidth ); + + // Wenn das Rechteck breiter als die Zeile ist, stutzen + // wir es ebenfalls zurecht. + KSHORT nLocalWidth = KSHORT( aLocal.Left() + aLocal.Width() ); + if( nRealWidth < long( nLocalWidth ) ) + aLocal.Width( nRealWidth - aLocal.Left() ); + GetInfo().GetParaPortion()->SetFly( sal_True ); + pFlyPortion = new SwFlyPortion( aLocal ); + pFlyPortion->Height( KSHORT( rCurrRect.Height() ) ); + // Die Width koennte kleiner sein als die FixWidth, daher: + pFlyPortion->AdjFixWidth(); + } + return pFlyPortion; +} + +/************************************************************************* + * SwTxtPainter::_CalcDropAdjust() + *************************************************************************/ + +// 6721: Drops und Adjustment +// CalcDropAdjust wird ggf. am Ende von Format() gerufen. + +void SwTxtAdjuster::CalcDropAdjust() +{ + ASSERT( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(), + "CalcDropAdjust: No reason for DropAdjustment." ) + + const MSHORT nLineNumber = GetLineNr(); + + // 1) Dummies ueberspringen + Top(); + + if( !pCurr->IsDummy() || NextLine() ) + { + // Erst adjustieren. + GetAdjusted(); + + SwLinePortion *pPor = pCurr->GetFirstPortion(); + + // 2) Sicherstellen, dass die DropPortion dabei ist. + // 3) pLeft: Die GluePor vor der DropPor + if( pPor->InGlueGrp() && pPor->GetPortion() + && pPor->GetPortion()->IsDropPortion() ) + { + const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion(); + SwGluePortion *pLeft = (SwGluePortion*) pPor; + + // 4) pRight: Die GluePor hinter der DropPor suchen + pPor = pPor->GetPortion(); + while( pPor && !pPor->InFixMargGrp() ) + pPor = pPor->GetPortion(); + + SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ? + (SwGluePortion*) pPor : 0; + if( pRight && pRight != pLeft ) + { + // 5) nMinLeft berechnen. Wer steht am weitesten links? + const KSHORT nDropLineStart = + KSHORT(GetLineStart()) + pLeft->Width() + pDropPor->Width(); + KSHORT nMinLeft = nDropLineStart; + for( MSHORT i = 1; i < GetDropLines(); ++i ) + { + if( NextLine() ) + { + // Erst adjustieren. + GetAdjusted(); + + pPor = pCurr->GetFirstPortion(); + const SwMarginPortion *pMar = pPor->IsMarginPortion() ? + (SwMarginPortion*)pPor : 0; + if( !pMar ) + nMinLeft = 0; + else + { + const KSHORT nLineStart = + KSHORT(GetLineStart()) + pMar->Width(); + if( nMinLeft > nLineStart ) + nMinLeft = nLineStart; + } + } + } + + // 6) Den Glue zwischen pLeft und pRight neu verteilen. + if( nMinLeft < nDropLineStart ) + { + // Glue wird immer von pLeft nach pRight abgegeben, + // damit der Text nach links wandert. + const short nGlue = nDropLineStart - nMinLeft; + if( !nMinLeft ) + pLeft->MoveAllGlue( pRight ); + else + pLeft->MoveGlue( pRight, nGlue ); +#ifdef DBGTXT + aDbstream << "Drop adjusted: " << nGlue << endl; +#endif + } + } + } + } + + if( nLineNumber != GetLineNr() ) + { + Top(); + while( nLineNumber != GetLineNr() && Next() ) + ; + } +} + +/************************************************************************* + * SwTxtAdjuster::CalcDropRepaint() + *************************************************************************/ + +void SwTxtAdjuster::CalcDropRepaint() +{ + Top(); + SwRepaint &rRepaint = *GetInfo().GetParaPortion()->GetRepaint(); + if( rRepaint.Top() > Y() ) + rRepaint.Top( Y() ); + for( MSHORT i = 1; i < GetDropLines(); ++i ) + NextLine(); + const SwTwips nBottom = Y() + GetLineHeight() - 1; + if( rRepaint.Bottom() < nBottom ) + rRepaint.Bottom( nBottom ); +} + + diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx new file mode 100644 index 000000000000..bd51317f77fe --- /dev/null +++ b/sw/source/core/text/itratr.cxx @@ -0,0 +1,1083 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itratr.cxx,v $ + * $Revision: 1.40 $ + * + * 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 <svx/charscaleitem.hxx> +#include <txtatr.hxx> +#include <sfx2/printer.hxx> +#include <svx/lrspitem.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <fmtflcnt.hxx> +#include <fmtcntnt.hxx> +#include <fmtftn.hxx> +#include <frmatr.hxx> +#include <frmfmt.hxx> +#include <fmtfld.hxx> +#include <doc.hxx> +#include <viewsh.hxx> // ViewShell +#include <rootfrm.hxx> +#include <docary.hxx> +#include <ndtxt.hxx> +#include <dcontact.hxx> +#include <fldbas.hxx> // SwField +#include <pam.hxx> // SwPosition (lcl_MinMaxNode) +#include <itratr.hxx> +#include <htmltbl.hxx> +#include <swtable.hxx> +#include <redlnitr.hxx> +#include <fmtsrnd.hxx> +#include <itrtxt.hxx> +#include <breakit.hxx> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/i18n/ScriptType.hdl> + +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star; + +/************************************************************************* + * SwAttrIter::Chg() + *************************************************************************/ + +void SwAttrIter::Chg( SwTxtAttr *pHt ) +{ + ASSERT( pHt && pFnt, "No attribute of font available for change"); + if( pRedln && pRedln->IsOn() ) + pRedln->ChangeTxtAttr( pFnt, *pHt, sal_True ); + else + aAttrHandler.PushAndChg( *pHt, *pFnt ); + nChgCnt++; +} + +/************************************************************************* + * SwAttrIter::Rst() + *************************************************************************/ + +void SwAttrIter::Rst( SwTxtAttr *pHt ) +{ + ASSERT( pHt && pFnt, "No attribute of font available for reset"); + // get top from stack after removing pHt + if( pRedln && pRedln->IsOn() ) + pRedln->ChangeTxtAttr( pFnt, *pHt, sal_False ); + else + aAttrHandler.PopAndChg( *pHt, *pFnt ); + nChgCnt--; +} + +/************************************************************************* + * virtual SwAttrIter::~SwAttrIter() + *************************************************************************/ + +SwAttrIter::~SwAttrIter() +{ + delete pRedln; + delete pFnt; +} + +/************************************************************************* + * SwAttrIter::GetAttr() + * + * Liefert fuer eine Position das Attribut, wenn das Attribut genau auf + * der Position nPos liegt und kein EndIndex besitzt. + * GetAttr() wird fuer Attribute benoetigt, die die Formatierung beeinflussen + * sollen, ohne dabei den Inhalt des Strings zu veraendern. Solche "entarteten" + * Attribute sind z.B. Felder (die expandierten Text bereit halten) und + * zeilengebundene Frames. Um Mehrdeutigkeiten zwischen verschiedenen + * solcher Attribute zu vermeiden, werden beim Anlegen eines Attributs + * an der Startposition ein Sonderzeichen in den String einfuegt. + * Der Formatierer stoesst auf das Sonderzeichen und holt sich per + * GetAttr() das entartete Attribut. + *************************************************************************/ + +SwTxtAttr *SwAttrIter::GetAttr( const xub_StrLen nPosition ) const +{ + return (m_pTxtNode) ? m_pTxtNode->GetTxtAttrForCharAt(nPosition) : 0; +} + +/************************************************************************* + * SwAttrIter::SeekAndChg() + *************************************************************************/ + +sal_Bool SwAttrIter::SeekAndChgAttrIter( const xub_StrLen nNewPos, OutputDevice* pOut ) +{ + sal_Bool bChg = nStartIndex && nNewPos == nPos ? pFnt->IsFntChg() : Seek( nNewPos ); + if ( pLastOut != pOut ) + { + pLastOut = pOut; + pFnt->SetFntChg( sal_True ); + bChg = sal_True; + } + if( bChg ) + { + // wenn der Aenderungszaehler auf Null ist, kennen wir die MagicNo + // des gewuenschten Fonts ... + if ( !nChgCnt && !nPropFont ) + pFnt->SetMagic( aMagicNo[ pFnt->GetActual() ], + aFntIdx[ pFnt->GetActual() ], pFnt->GetActual() ); + pFnt->ChgPhysFnt( pShell, *pOut ); + } + return bChg; +} + +sal_Bool SwAttrIter::IsSymbol( const xub_StrLen nNewPos ) +{ + Seek( nNewPos ); + if ( !nChgCnt && !nPropFont ) + pFnt->SetMagic( aMagicNo[ pFnt->GetActual() ], + aFntIdx[ pFnt->GetActual() ], pFnt->GetActual() ); + return pFnt->IsSymbol( pShell ); +} + +/************************************************************************* + * SwAttrIter::SeekStartAndChg() + *************************************************************************/ + +sal_Bool SwAttrIter::SeekStartAndChgAttrIter( OutputDevice* pOut, const sal_Bool bParaFont ) +{ + if ( pRedln && pRedln->ExtOn() ) + pRedln->LeaveExtend( *pFnt, 0 ); + + // reset font to its original state + aAttrHandler.Reset(); + aAttrHandler.ResetFont( *pFnt ); + + nStartIndex = nEndIndex = nPos = nChgCnt = 0; + if( nPropFont ) + pFnt->SetProportion( nPropFont ); + if( pRedln ) + { + pRedln->Clear( pFnt ); + if( !bParaFont ) + nChgCnt = nChgCnt + pRedln->Seek( *pFnt, 0, STRING_LEN ); + else + pRedln->Reset(); + } + + if ( pHints && !bParaFont ) + { + SwTxtAttr *pTxtAttr; + // Solange wir noch nicht am Ende des StartArrays angekommen sind && + // das TextAttribut an Position 0 beginnt ... + while ( ( nStartIndex < pHints->GetStartCount() ) && + !(*(pTxtAttr=pHints->GetStart(nStartIndex))->GetStart()) ) + { + // oeffne die TextAttribute + Chg( pTxtAttr ); + nStartIndex++; + } + } + + sal_Bool bChg = pFnt->IsFntChg(); + if ( pLastOut != pOut ) + { + pLastOut = pOut; + pFnt->SetFntChg( sal_True ); + bChg = sal_True; + } + if( bChg ) + { + // wenn der Aenderungszaehler auf Null ist, kennen wir die MagicNo + // des gewuenschten Fonts ... + if ( !nChgCnt && !nPropFont ) + pFnt->SetMagic( aMagicNo[ pFnt->GetActual() ], + aFntIdx[ pFnt->GetActual() ], pFnt->GetActual() ); + pFnt->ChgPhysFnt( pShell, *pOut ); + } + return bChg; +} + +/************************************************************************* + * SwAttrIter::SeekFwd() + *************************************************************************/ + +// AMA: Neuer AttrIter Nov 94 + +void SwAttrIter::SeekFwd( const xub_StrLen nNewPos ) +{ + SwTxtAttr *pTxtAttr; + + if ( nStartIndex ) // wenn ueberhaupt schon Attribute geoeffnet wurden... + { + // Schliesse Attr, die z. Z. geoeffnet sind, vor nNewPos+1 aber enden. + + // Solange wir noch nicht am Ende des EndArrays angekommen sind && + // das TextAttribut vor oder an der neuen Position endet ... + while ( ( nEndIndex < pHints->GetEndCount() ) && + (*(pTxtAttr=pHints->GetEnd(nEndIndex))->GetAnyEnd()<=nNewPos)) + { + // schliesse die TextAttribute, deren StartPos vor + // oder an der alten nPos lag, die z.Z. geoeffnet sind. + if (*pTxtAttr->GetStart() <= nPos) Rst( pTxtAttr ); + nEndIndex++; + } + } + else // ueberlies die nicht geoeffneten Enden + { + while ( ( nEndIndex < pHints->GetEndCount() ) && + (*(pTxtAttr=pHints->GetEnd(nEndIndex))->GetAnyEnd()<=nNewPos)) + { + nEndIndex++; + } + } + // Solange wir noch nicht am Ende des StartArrays angekommen sind && + // das TextAttribut vor oder an der neuen Position beginnt ... + while ( ( nStartIndex < pHints->GetStartCount() ) && + (*(pTxtAttr=pHints->GetStart(nStartIndex))->GetStart()<=nNewPos)) + { + // oeffne die TextAttribute, deren Ende hinter der neuen Position liegt + if ( *pTxtAttr->GetAnyEnd() > nNewPos ) Chg( pTxtAttr ); + nStartIndex++; + } + +} + +/************************************************************************* + * SwAttrIter::Seek() + *************************************************************************/ + +sal_Bool SwAttrIter::Seek( const xub_StrLen nNewPos ) +{ + if ( pRedln && pRedln->ExtOn() ) + pRedln->LeaveExtend( *pFnt, nNewPos ); + + if( pHints ) + { + if( !nNewPos || nNewPos < nPos ) + { + if( pRedln ) + pRedln->Clear( NULL ); + + // reset font to its original state + aAttrHandler.Reset(); + aAttrHandler.ResetFont( *pFnt ); + + if( nPropFont ) + pFnt->SetProportion( nPropFont ); + nStartIndex = nEndIndex = nPos = 0; + nChgCnt = 0; + + // Achtung! + // resetting the font here makes it necessary to apply any + // changes for extended input directly to the font + if ( pRedln && pRedln->ExtOn() ) + { + pRedln->UpdateExtFont( *pFnt ); + ++nChgCnt; + } + } + SeekFwd( nNewPos ); + } + + pFnt->SetActual( SwScriptInfo::WhichFont( nNewPos, 0, pScriptInfo ) ); + + if( pRedln ) + nChgCnt = nChgCnt + pRedln->Seek( *pFnt, nNewPos, nPos ); + nPos = nNewPos; + + if( nPropFont ) + pFnt->SetProportion( nPropFont ); + + return pFnt->IsFntChg(); +} + +/************************************************************************* + * SwAttrIter::GetNextAttr() + *************************************************************************/ + +xub_StrLen SwAttrIter::GetNextAttr( ) const +{ + xub_StrLen nNext = STRING_LEN; + if( pHints ) + { + if (pHints->GetStartCount() > nStartIndex) // Gibt es noch Starts? + nNext = (*pHints->GetStart(nStartIndex)->GetStart()); + if (pHints->GetEndCount() > nEndIndex) // Gibt es noch Enden? + { + xub_StrLen nNextEnd = (*pHints->GetEnd(nEndIndex)->GetAnyEnd()); + if ( nNextEnd<nNext ) nNext = nNextEnd; // Wer ist naeher? + } + } + if (m_pTxtNode!=NULL) { + //TODO maybe use hints like FieldHints for this instead of looking at the text... + int l=(nNext<m_pTxtNode->Len()?nNext:m_pTxtNode->Len()); + USHORT p=nPos; + const sal_Unicode *txt=m_pTxtNode->GetTxt().GetBuffer(); + while(p<l && txt[p]!=CH_TXT_ATR_FIELDSTART && txt[p]!=CH_TXT_ATR_FIELDEND && txt[p]!=CH_TXT_ATR_FORMELEMENT) p++; + if ((p<l && p>nPos) || nNext<=p) + nNext=p; + else + nNext=p+1; + } + if( pRedln ) + return pRedln->GetNextRedln( nNext ); + return nNext; +} + +#if OSL_DEBUG_LEVEL > 1 +/************************************************************************* + * SwAttrIter::Dump() + *************************************************************************/ + +void SwAttrIter::Dump( SvStream &/*rOS*/ ) const +{ +// Noch nicht an den neuen Attributiterator angepasst ... +} + +#endif + +class SwMinMaxArgs +{ +public: + OutputDevice* pOut; + ViewShell* pSh; + ULONG &rMin; + ULONG &rMax; + ULONG &rAbsMin; + long nRowWidth; + long nWordWidth; + long nWordAdd; + xub_StrLen nNoLineBreak; + SwMinMaxArgs( OutputDevice* pOutI, ViewShell* pShI, ULONG& rMinI, ULONG &rMaxI, ULONG &rAbsI ) + : pOut( pOutI ), pSh( pShI ), rMin( rMinI ), rMax( rMaxI ), rAbsMin( rAbsI ) + { nRowWidth = nWordWidth = nWordAdd = 0; nNoLineBreak = STRING_LEN; } + void Minimum( long nNew ) { if( (long)rMin < nNew ) rMin = nNew; } + void NewWord() { nWordAdd = nWordWidth = 0; } +}; + +sal_Bool lcl_MinMaxString( SwMinMaxArgs& rArg, SwFont* pFnt, const XubString &rTxt, + xub_StrLen nIdx, xub_StrLen nEnd ) +{ + sal_Bool bRet = sal_False; + while( nIdx < nEnd ) + { + xub_StrLen nStop = nIdx; + sal_Bool bClear; + LanguageType eLang = pFnt->GetLanguage(); + if( pBreakIt->GetBreakIter().is() ) + { + bClear = CH_BLANK == rTxt.GetChar( nStop ); + Boundary aBndry( pBreakIt->GetBreakIter()->getWordBoundary( rTxt, nIdx, + pBreakIt->GetLocale( eLang ), + WordType::DICTIONARY_WORD, TRUE ) ); + nStop = (xub_StrLen)aBndry.endPos; + if( nIdx <= aBndry.startPos && nIdx && nIdx-1 != rArg.nNoLineBreak ) + rArg.NewWord(); + if( nStop == nIdx ) + ++nStop; + if( nStop > nEnd ) + nStop = nEnd; + } + else + { + while( nStop < nEnd && CH_BLANK != rTxt.GetChar( nStop ) ) + ++nStop; + bClear = nStop == nIdx; + if ( bClear ) + { + rArg.NewWord(); + while( nStop < nEnd && CH_BLANK == rTxt.GetChar( nStop ) ) + ++nStop; + } + } + + SwDrawTextInfo aDrawInf( rArg.pSh, *rArg.pOut, 0, rTxt, nIdx, nStop - nIdx ); + long nAktWidth = pFnt->_GetTxtSize( aDrawInf ).Width(); + rArg.nRowWidth += nAktWidth; + if( bClear ) + rArg.NewWord(); + else + { + rArg.nWordWidth += nAktWidth; + if( (long)rArg.rAbsMin < rArg.nWordWidth ) + rArg.rAbsMin = rArg.nWordWidth; + rArg.Minimum( rArg.nWordWidth + rArg.nWordAdd ); + bRet = sal_True; + } + nIdx = nStop; + } + return bRet; +} + +sal_Bool SwTxtNode::IsSymbol( const xub_StrLen nBegin ) const +{ + SwScriptInfo aScriptInfo; + SwAttrIter aIter( *(SwTxtNode*)this, aScriptInfo ); + aIter.Seek( nBegin ); + const SwRootFrm* pTmpRootFrm = getIDocumentLayoutAccess()->GetRootFrm(); + return aIter.GetFnt()->IsSymbol( pTmpRootFrm ? + pTmpRootFrm->GetCurrShell() : + 0 ); +} + +class SwMinMaxNodeArgs +{ +public: + ULONG nMaxWidth; // Summe aller Rahmenbreite + long nMinWidth; // Breitester Rahmen + long nLeftRest; // noch nicht von Rahmen ueberdeckter Platz im l. Rand + long nRightRest; // noch nicht von Rahmen ueberdeckter Platz im r. Rand + long nLeftDiff; // Min/Max-Differenz des Rahmens im linken Rand + long nRightDiff; // Min/Max-Differenz des Rahmens im rechten Rand + ULONG nIndx; // Indexnummer des Nodes + void Minimum( long nNew ) { if( nNew > nMinWidth ) nMinWidth = nNew; } +}; + +sal_Bool lcl_MinMaxNode( const SwFrmFmtPtr& rpNd, void* pArgs ) +{ + const SwFmtAnchor& rFmtA = ((SwFrmFmt*)rpNd)->GetAnchor(); + + bool bCalculate = false; + if ( + (FLY_AT_CNTNT == rFmtA.GetAnchorId()) || + (FLY_AUTO_CNTNT == rFmtA.GetAnchorId()) + ) + { + bCalculate = true; + } + + if (bCalculate) + { + const SwMinMaxNodeArgs *pIn = (const SwMinMaxNodeArgs*)pArgs; + const SwPosition *pPos = rFmtA.GetCntntAnchor(); + ASSERT(pPos && pIn, "Unexpected NULL arguments"); + if (!pPos || !pIn || pIn->nIndx != pPos->nNode.GetIndex()) + bCalculate = false; + } + + if (bCalculate) + { + long nMin, nMax; + SwHTMLTableLayout *pLayout = 0; + MSHORT nWhich = ((SwFrmFmt*)rpNd)->Which(); + if( RES_DRAWFRMFMT != nWhich ) + { + // Enthaelt der Rahmen zu Beginn oder am Ende eine Tabelle? + const SwNodes& rNodes = static_cast<SwFrmFmt*>(rpNd)->GetDoc()->GetNodes(); + const SwFmtCntnt& rFlyCntnt = ((SwFrmFmt*)rpNd)->GetCntnt(); + ULONG nStt = rFlyCntnt.GetCntntIdx()->GetIndex(); + SwTableNode* pTblNd = rNodes[nStt+1]->GetTableNode(); + if( !pTblNd ) + { + SwNode *pNd = rNodes[nStt]; + pNd = rNodes[pNd->EndOfSectionIndex()-1]; + if( pNd->IsEndNode() ) + pTblNd = pNd->StartOfSectionNode()->GetTableNode(); + } + + if( pTblNd ) + pLayout = pTblNd->GetTable().GetHTMLTableLayout(); + } + + const SwFmtHoriOrient& rOrient = ((SwFrmFmt*)rpNd)->GetHoriOrient(); + sal_Int16 eHoriOri = rOrient.GetHoriOrient(); + + long nDiff; + if( pLayout ) + { + nMin = pLayout->GetMin(); + nMax = pLayout->GetMax(); + nDiff = nMax - nMin; + } + else + { + if( RES_DRAWFRMFMT == nWhich ) + { + const SdrObject* pSObj = rpNd->FindSdrObject(); + if( pSObj ) + nMin = pSObj->GetCurrentBoundRect().GetWidth(); + else + nMin = 0; + + } + else + { + const SwFmtFrmSize &rSz = ( (SwFrmFmt*)rpNd )->GetFrmSize(); + nMin = rSz.GetWidth(); + } + nMax = nMin; + nDiff = 0; + } + + const SvxLRSpaceItem &rLR = ( (SwFrmFmt*)rpNd )->GetLRSpace(); + nMin += rLR.GetLeft(); + nMin += rLR.GetRight(); + nMax += rLR.GetLeft(); + nMax += rLR.GetRight(); + + if( SURROUND_THROUGHT == ((SwFrmFmt*)rpNd)->GetSurround().GetSurround() ) + { + ( (SwMinMaxNodeArgs*)pArgs )->Minimum( nMin ); + return sal_True; + } + + // Rahmen, die recht bzw. links ausgerichtet sind, gehen nur + // teilweise in die Max-Berechnung ein, da der Rand schon berueck- + // sichtigt wird. Nur wenn die Rahmen in den Textkoerper ragen, + // wird dieser Teil hinzuaddiert. + switch( eHoriOri ) + { + case text::HoriOrientation::RIGHT: + { + if( nDiff ) + { + ((SwMinMaxNodeArgs*)pArgs)->nRightRest -= + ((SwMinMaxNodeArgs*)pArgs)->nRightDiff; + ((SwMinMaxNodeArgs*)pArgs)->nRightDiff = nDiff; + } + if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() ) + { + if( ((SwMinMaxNodeArgs*)pArgs)->nRightRest > 0 ) + ((SwMinMaxNodeArgs*)pArgs)->nRightRest = 0; + } + ((SwMinMaxNodeArgs*)pArgs)->nRightRest -= nMin; + break; + } + case text::HoriOrientation::LEFT: + { + if( nDiff ) + { + ((SwMinMaxNodeArgs*)pArgs)->nLeftRest -= + ((SwMinMaxNodeArgs*)pArgs)->nLeftDiff; + ((SwMinMaxNodeArgs*)pArgs)->nLeftDiff = nDiff; + } + if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() && + ((SwMinMaxNodeArgs*)pArgs)->nLeftRest < 0 ) + ((SwMinMaxNodeArgs*)pArgs)->nLeftRest = 0; + ((SwMinMaxNodeArgs*)pArgs)->nLeftRest -= nMin; + break; + } + default: + { + ( (SwMinMaxNodeArgs*)pArgs )->nMaxWidth += nMax; + ( (SwMinMaxNodeArgs*)pArgs )->Minimum( nMin ); + } + } + } + return sal_True; +} + +#define FLYINCNT_MIN_WIDTH 284 + +// changing this method very likely requires changing of +// "GetScalingOfSelectedText" +void SwTxtNode::GetMinMaxSize( ULONG nIndex, ULONG& rMin, ULONG &rMax, + ULONG& rAbsMin, OutputDevice* pOut ) const +{ + ViewShell* pSh = 0; + GetDoc()->GetEditShell( &pSh ); + if( !pOut ) + { + if( pSh ) + pOut = pSh->GetWin(); + if( !pOut ) + pOut = GetpApp()->GetDefaultDevice(); + } + + MapMode aOldMap( pOut->GetMapMode() ); + pOut->SetMapMode( MapMode( MAP_TWIP ) ); + + rMin = 0; + rMax = 0; + rAbsMin = 0; + + const SvxLRSpaceItem &rSpace = GetSwAttrSet().GetLRSpace(); + long nLROffset = rSpace.GetTxtLeft() + GetLeftMarginWithNum( sal_True ); + short nFLOffs; + // Bei Numerierung ist ein neg. Erstzeileneinzug vermutlich + // bereits gefuellt... + if( !GetFirstLineOfsWithNum( nFLOffs ) || nFLOffs > nLROffset ) + nLROffset = nFLOffs; + + SwMinMaxNodeArgs aNodeArgs; + aNodeArgs.nMinWidth = 0; + aNodeArgs.nMaxWidth = 0; + aNodeArgs.nLeftRest = nLROffset; + aNodeArgs.nRightRest = rSpace.GetRight(); + aNodeArgs.nLeftDiff = 0; + aNodeArgs.nRightDiff = 0; + if( nIndex ) + { + SwSpzFrmFmts* pTmp = (SwSpzFrmFmts*)GetDoc()->GetSpzFrmFmts(); + if( pTmp ) + { + aNodeArgs.nIndx = nIndex; + pTmp->ForEach( &lcl_MinMaxNode, &aNodeArgs ); + } + } + if( aNodeArgs.nLeftRest < 0 ) + aNodeArgs.Minimum( nLROffset - aNodeArgs.nLeftRest ); + aNodeArgs.nLeftRest -= aNodeArgs.nLeftDiff; + if( aNodeArgs.nLeftRest < 0 ) + aNodeArgs.nMaxWidth -= aNodeArgs.nLeftRest; + + if( aNodeArgs.nRightRest < 0 ) + aNodeArgs.Minimum( rSpace.GetRight() - aNodeArgs.nRightRest ); + aNodeArgs.nRightRest -= aNodeArgs.nRightDiff; + if( aNodeArgs.nRightRest < 0 ) + aNodeArgs.nMaxWidth -= aNodeArgs.nRightRest; + + SwScriptInfo aScriptInfo; + SwAttrIter aIter( *(SwTxtNode*)this, aScriptInfo ); + xub_StrLen nIdx = 0; + aIter.SeekAndChgAttrIter( nIdx, pOut ); + xub_StrLen nLen = m_Text.Len(); + long nAktWidth = 0; + MSHORT nAdd = 0; + SwMinMaxArgs aArg( pOut, pSh, rMin, rMax, rAbsMin ); + while( nIdx < nLen ) + { + xub_StrLen nNextChg = aIter.GetNextAttr(); + xub_StrLen nStop = aScriptInfo.NextScriptChg( nIdx ); + if( nNextChg > nStop ) + nNextChg = nStop; + SwTxtAttr *pHint = NULL; + xub_Unicode cChar = CH_BLANK; + nStop = nIdx; + while( nStop < nLen && nStop < nNextChg && + CH_TAB != ( cChar = m_Text.GetChar( nStop ) ) && + CH_BREAK != cChar && CHAR_HARDBLANK != cChar && + CHAR_HARDHYPHEN != cChar && CHAR_SOFTHYPHEN != cChar && + !pHint ) + { + if( ( CH_TXTATR_BREAKWORD != cChar && CH_TXTATR_INWORD != cChar ) + || ( 0 == ( pHint = aIter.GetAttr( nStop ) ) ) ) + ++nStop; + } + if ( lcl_MinMaxString( aArg, aIter.GetFnt(), m_Text, nIdx, nStop ) ) + { + nAdd = 20; + } + nIdx = nStop; + aIter.SeekAndChgAttrIter( nIdx, pOut ); + switch( cChar ) + { + case CH_BREAK : + { + if( (long)rMax < aArg.nRowWidth ) + rMax = aArg.nRowWidth; + aArg.nRowWidth = 0; + aArg.NewWord(); + aIter.SeekAndChgAttrIter( ++nIdx, pOut ); + } + break; + case CH_TAB : + { + aArg.NewWord(); + aIter.SeekAndChgAttrIter( ++nIdx, pOut ); + } + break; + case CHAR_SOFTHYPHEN: + ++nIdx; + break; + case CHAR_HARDBLANK: + case CHAR_HARDHYPHEN: + { + XubString sTmp( cChar ); + const SwRootFrm* pTmpRootFrm = getIDocumentLayoutAccess()->GetRootFrm(); + SwDrawTextInfo aDrawInf( pTmpRootFrm ? + pTmpRootFrm->GetCurrShell() : + 0, *pOut, 0, sTmp, 0, 1, 0, sal_False ); + nAktWidth = aIter.GetFnt()->_GetTxtSize( aDrawInf ).Width(); + aArg.nWordWidth += nAktWidth; + aArg.nRowWidth += nAktWidth; + if( (long)rAbsMin < aArg.nWordWidth ) + rAbsMin = aArg.nWordWidth; + aArg.Minimum( aArg.nWordWidth + aArg.nWordAdd ); + aArg.nNoLineBreak = nIdx++; + } + break; + case CH_TXTATR_BREAKWORD: + case CH_TXTATR_INWORD: + { + if( !pHint ) + break; + long nOldWidth = aArg.nWordWidth; + long nOldAdd = aArg.nWordAdd; + aArg.NewWord(); + + switch( pHint->Which() ) + { + case RES_TXTATR_FLYCNT : + { + SwFrmFmt *pFrmFmt = pHint->GetFlyCnt().GetFrmFmt(); + const SvxLRSpaceItem &rLR = pFrmFmt->GetLRSpace(); + if( RES_DRAWFRMFMT == pFrmFmt->Which() ) + { + const SdrObject* pSObj = pFrmFmt->FindSdrObject(); + if( pSObj ) + nAktWidth = pSObj->GetCurrentBoundRect().GetWidth(); + else + nAktWidth = 0; + } + else + { + const SwFmtFrmSize& rTmpSize = pFrmFmt->GetFrmSize(); + if( RES_FLYFRMFMT == pFrmFmt->Which() + && rTmpSize.GetWidthPercent() ) + { +/*-----------------24.01.97 14:09---------------------------------------------- + * Hier ein HACK fuer folgende Situation: In dem Absatz befindet sich + * ein Textrahmen mit relativer Groesse. Dann nehmen wir mal als minimale + * Breite 0,5 cm und als maximale KSHRT_MAX. + * Sauberer und vielleicht spaeter notwendig waere es, ueber den Inhalt + * des Textrahmens zu iterieren und GetMinMaxSize rekursiv zu rufen. + * --------------------------------------------------------------------------*/ + nAktWidth = FLYINCNT_MIN_WIDTH; // 0,5 cm + if( (long)rMax < KSHRT_MAX ) + rMax = KSHRT_MAX; + } + else + nAktWidth = pFrmFmt->GetFrmSize().GetWidth(); + } + nAktWidth += rLR.GetLeft(); + nAktWidth += rLR.GetRight(); + aArg.nWordAdd = nOldWidth + nOldAdd; + aArg.nWordWidth = nAktWidth; + aArg.nRowWidth += nAktWidth; + if( (long)rAbsMin < aArg.nWordWidth ) + rAbsMin = aArg.nWordWidth; + aArg.Minimum( aArg.nWordWidth + aArg.nWordAdd ); + break; + } + case RES_TXTATR_FTN : + { + const XubString aTxt = pHint->GetFtn().GetNumStr(); + if( lcl_MinMaxString( aArg, aIter.GetFnt(), aTxt, 0, + aTxt.Len() ) ) + nAdd = 20; + break; + } + case RES_TXTATR_FIELD : + { + SwField *pFld = (SwField*)pHint->GetFld().GetFld(); + const String aTxt = pFld->GetCntnt( FALSE ); + if( lcl_MinMaxString( aArg, aIter.GetFnt(), aTxt, 0, + aTxt.Len() ) ) + nAdd = 20; + break; + } + default: aArg.nWordWidth = nOldWidth; + aArg.nWordAdd = nOldAdd; + + } + aIter.SeekAndChgAttrIter( ++nIdx, pOut ); + } + break; + } + } + if( (long)rMax < aArg.nRowWidth ) + rMax = aArg.nRowWidth; + + nLROffset += rSpace.GetRight(); + + rAbsMin += nLROffset; + rAbsMin += nAdd; + rMin += nLROffset; + rMin += nAdd; + if( (long)rMin < aNodeArgs.nMinWidth ) + rMin = aNodeArgs.nMinWidth; + if( (long)rAbsMin < aNodeArgs.nMinWidth ) + rAbsMin = aNodeArgs.nMinWidth; + rMax += aNodeArgs.nMaxWidth; + rMax += nLROffset; + rMax += nAdd; + if( rMax < rMin ) // z.B. Rahmen mit Durchlauf gehen zunaechst nur + rMax = rMin; // in das Minimum ein + pOut->SetMapMode( aOldMap ); +} + +/************************************************************************* + * SwTxtNode::GetScalingOfSelectedText() + * + * Calculates the width of the text part specified by nStt and nEnd, + * the height of the line containing nStt is devided by this width, + * indicating the scaling factor, if the text part is rotated. + * Having CH_BREAKs in the text part, this method returns the scaling + * factor for the longest of the text parts separated by the CH_BREAKs. + * + * changing this method very likely requires changing of "GetMinMaxSize" + *************************************************************************/ + +USHORT SwTxtNode::GetScalingOfSelectedText( xub_StrLen nStt, xub_StrLen nEnd ) + const +{ + ViewShell* pSh = NULL; + OutputDevice* pOut = NULL; + GetDoc()->GetEditShell( &pSh ); + + if ( pSh ) + pOut = &pSh->GetRefDev(); + else + { + //Zugriff ueber StarONE, es muss keine Shell existieren oder aktiv sein. + if ( getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE) ) + pOut = GetpApp()->GetDefaultDevice(); + else + pOut = getIDocumentDeviceAccess()->getReferenceDevice( true ); + } + + ASSERT( pOut, "GetScalingOfSelectedText without outdev" ) + + MapMode aOldMap( pOut->GetMapMode() ); + pOut->SetMapMode( MapMode( MAP_TWIP ) ); + + if ( nStt == nEnd ) + { + if ( !pBreakIt->GetBreakIter().is() ) + return 100; + + SwScriptInfo aScriptInfo; + SwAttrIter aIter( *(SwTxtNode*)this, aScriptInfo ); + aIter.SeekAndChgAttrIter( nStt, pOut ); + + Boundary aBound = + pBreakIt->GetBreakIter()->getWordBoundary( GetTxt(), nStt, + pBreakIt->GetLocale( aIter.GetFnt()->GetLanguage() ), + WordType::DICTIONARY_WORD, sal_True ); + + if ( nStt == aBound.startPos ) + { + // cursor is at left or right border of word + pOut->SetMapMode( aOldMap ); + return 100; + } + + nStt = (xub_StrLen)aBound.startPos; + nEnd = (xub_StrLen)aBound.endPos; + + if ( nStt == nEnd ) + { + pOut->SetMapMode( aOldMap ); + return 100; + } + } + + SwScriptInfo aScriptInfo; + SwAttrIter aIter( *(SwTxtNode*)this, aScriptInfo ); + + // We do not want scaling attributes to be considered during this + // calculation. For this, we push a temporary scaling attribute with + // scaling value 100 and priority flag on top of the scaling stack + SwAttrHandler& rAH = aIter.GetAttrHandler(); + SvxCharScaleWidthItem aItem(100, RES_CHRATR_SCALEW); + SwTxtAttrEnd aAttr( aItem, nStt, nEnd ); + aAttr.SetPriorityAttr( sal_True ); + rAH.PushAndChg( aAttr, *(aIter.GetFnt()) ); + + xub_StrLen nIdx = nStt; + + ULONG nWidth = 0; + ULONG nProWidth = 0; + + while( nIdx < nEnd ) + { + aIter.SeekAndChgAttrIter( nIdx, pOut ); + + // scan for end of portion + xub_StrLen nNextChg = aIter.GetNextAttr(); + xub_StrLen nStop = aScriptInfo.NextScriptChg( nIdx ); + if( nNextChg > nStop ) + nNextChg = nStop; + + nStop = nIdx; + xub_Unicode cChar = CH_BLANK; + SwTxtAttr* pHint = NULL; + + // stop at special characters in [ nIdx, nNextChg ] + while( nStop < nEnd && nStop < nNextChg ) + { + cChar = m_Text.GetChar( nStop ); + if( CH_TAB == cChar || CH_BREAK == cChar || + CHAR_HARDBLANK == cChar || CHAR_HARDHYPHEN == cChar || + CHAR_SOFTHYPHEN == cChar || + ( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar ) && + ( 0 == ( pHint = aIter.GetAttr( nStop ) ) ) ) + break; + else + ++nStop; + } + + // calculate text widths up to cChar + if ( nStop > nIdx ) + { + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, GetTxt(), nIdx, nStop - nIdx ); + nProWidth += aIter.GetFnt()->_GetTxtSize( aDrawInf ).Width(); + } + + nIdx = nStop; + aIter.SeekAndChgAttrIter( nIdx, pOut ); + + if ( cChar == CH_BREAK ) + { + nWidth = Max( nWidth, nProWidth ); + nProWidth = 0; + nIdx++; + } + else if ( cChar == CH_TAB ) + { + // tab receives width of one space + XubString sTmp( CH_BLANK ); + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, sTmp, 0, 1 ); + nProWidth += aIter.GetFnt()->_GetTxtSize( aDrawInf ).Width(); + nIdx++; + } + else if ( cChar == CHAR_SOFTHYPHEN ) + ++nIdx; + else if ( cChar == CHAR_HARDBLANK || cChar == CHAR_HARDHYPHEN ) + { + XubString sTmp( cChar ); + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, sTmp, 0, 1 ); + nProWidth += aIter.GetFnt()->_GetTxtSize( aDrawInf ).Width(); + nIdx++; + } + else if ( pHint && ( cChar == CH_TXTATR_BREAKWORD || CH_TXTATR_INWORD ) ) + { + switch( pHint->Which() ) + { + case RES_TXTATR_FTN : + { + const XubString aTxt = pHint->GetFtn().GetNumStr(); + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTxt, 0, aTxt.Len() ); + + nProWidth += aIter.GetFnt()->_GetTxtSize( aDrawInf ).Width(); + break; + } + case RES_TXTATR_FIELD : + { + SwField *pFld = (SwField*)pHint->GetFld().GetFld(); + const String aTxt = pFld->GetCntnt( FALSE ); + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, aTxt, 0, aTxt.Len() ); + + nProWidth += aIter.GetFnt()->_GetTxtSize( aDrawInf ).Width(); + break; + } + default: + { + // any suggestions for a default action? + } + } // end of switch + nIdx++; + } // end of while + } + + nWidth = Max( nWidth, nProWidth ); + + // search for a text frame this node belongs to + SwClientIter aClientIter( *(SwTxtNode*)this ); + SwClient* pLastFrm = aClientIter.GoStart(); + SwTxtFrm* pFrm = 0; + + while( pLastFrm ) + { + if ( pLastFrm->ISA( SwTxtFrm ) ) + { + SwTxtFrm* pTmpFrm = ( SwTxtFrm* )pLastFrm; + if ( pTmpFrm->GetOfst() <= nStt && + ( !pTmpFrm->GetFollow() || + pTmpFrm->GetFollow()->GetOfst() > nStt ) ) + { + pFrm = pTmpFrm; + break; + } + } + pLastFrm = ++aClientIter; + } + + // search for the line containing nStt + if ( pFrm && pFrm->HasPara() ) + { + SwTxtInfo aInf( pFrm ); + SwTxtIter aLine( pFrm, &aInf ); + aLine.CharToLine( nStt ); + pOut->SetMapMode( aOldMap ); + return (USHORT)( nWidth ? + ( ( 100 * aLine.GetCurr()->Height() ) / nWidth ) : 0 ); + } + // no frame or no paragraph, we take the height of the character + // at nStt as line height + + aIter.SeekAndChgAttrIter( nStt, pOut ); + pOut->SetMapMode( aOldMap ); + + SwDrawTextInfo aDrawInf( pSh, *pOut, 0, GetTxt(), nStt, 1 ); + return (USHORT) + ( nWidth ? ((100 * aIter.GetFnt()->_GetTxtSize( aDrawInf ).Height()) / nWidth ) : 0 ); +} + +USHORT SwTxtNode::GetWidthOfLeadingTabs() const +{ + USHORT nRet = 0; + + xub_StrLen nIdx = 0; + sal_Unicode cCh; + + while ( nIdx < GetTxt().Len() && + ( '\t' == ( cCh = GetTxt().GetChar( nIdx ) ) || + ' ' == cCh ) ) + ++nIdx; + + if ( nIdx > 0 ) + { + SwPosition aPos( *this ); + aPos.nContent += nIdx; + + // Find the non-follow text frame: + SwClientIter aClientIter( (SwTxtNode&)*this ); + SwClient* pLastFrm = aClientIter.GoStart(); + + while( pLastFrm ) + { + // Only consider master frames: + if ( pLastFrm->ISA(SwTxtFrm) && + !static_cast<SwTxtFrm*>(pLastFrm)->IsFollow() ) + { + const SwTxtFrm* pFrm = static_cast<SwTxtFrm*>(pLastFrm); + SWRECTFN( pFrm ) + SwRect aRect; + pFrm->GetCharRect( aRect, aPos ); + nRet = (USHORT) + ( pFrm->IsRightToLeft() ? + (pFrm->*fnRect->fnGetPrtRight)() - (aRect.*fnRect->fnGetRight)() : + (aRect.*fnRect->fnGetLeft)() - (pFrm->*fnRect->fnGetPrtLeft)() ); + break; + } + pLastFrm = ++aClientIter; + } + } + + return nRet; +} diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx new file mode 100644 index 000000000000..18b2fec04609 --- /dev/null +++ b/sw/source/core/text/itratr.hxx @@ -0,0 +1,134 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itratr.hxx,v $ + * $Revision: 1.21 $ + * + * 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. + * + ************************************************************************/ +#ifndef _ITRATR_HXX +#define _ITRATR_HXX +#include <atrhndl.hxx> + +#include "txttypes.hxx" +#include "swfont.hxx" +#include "porlay.hxx" + +#define _SVSTDARR_XUB_STRLEN +#define _SVSTDARR_USHORTS +#include <svtools/svstdarr.hxx> + +class OutputDevice; +class SwFont; +class SwpHints; +class SwTxtAttr; +class SwAttrSet; +class SwTxtNode; +class SwRedlineItr; +class ViewShell; +class SwTxtFrm; + +/************************************************************************* + * class SwAttrIter + *************************************************************************/ + +class SwAttrIter +{ + friend class SwFontSave; +protected: + + SwAttrHandler aAttrHandler; + ViewShell *pShell; + SwFont *pFnt; + SwpHints *pHints; + const SwAttrSet* pAttrSet; // das Char-Attribut-Set + SwScriptInfo* pScriptInfo; + +private: + OutputDevice *pLastOut; + MSHORT nChgCnt; + SwRedlineItr *pRedln; + xub_StrLen nStartIndex, nEndIndex, nPos; + BYTE nPropFont; + void SeekFwd( const xub_StrLen nPos ); + inline void SetFnt( SwFont* pNew ) { pFnt = pNew; } + const void* aMagicNo[ SW_SCRIPTS ]; + MSHORT aFntIdx[ SW_SCRIPTS ]; + const SwTxtNode* m_pTxtNode; + +protected: + void Chg( SwTxtAttr *pHt ); + void Rst( SwTxtAttr *pHt ); + void CtorInitAttrIter( SwTxtNode& rTxtNode, SwScriptInfo& rScrInf, SwTxtFrm* pFrm = 0 ); + inline SwAttrIter(SwTxtNode* pTxtNode) + : pShell(0), pFnt(0), pLastOut(0), nChgCnt(0), pRedln(0), nPropFont(0), m_pTxtNode(pTxtNode) {} + +public: + // Konstruktor, Destruktor + inline SwAttrIter( SwTxtNode& rTxtNode, SwScriptInfo& rScrInf ) + : pShell(0), pFnt(0), pHints(0), pScriptInfo(0), pLastOut(0), nChgCnt(0), pRedln(0),nPropFont(0), m_pTxtNode(&rTxtNode) + { CtorInitAttrIter( rTxtNode, rScrInf ); } + + virtual ~SwAttrIter(); + + inline SwRedlineItr *GetRedln() { return pRedln; } + // Liefert im Parameter die Position des naechsten Wechsels vor oder an + // der uebergebenen Characterposition zurueck. Liefert sal_False, wenn vor + // oder an dieser Position kein Wechsel mehr erfolgt, sal_True sonst. + xub_StrLen GetNextAttr( ) const; + // Macht die an der Characterposition i gueltigen Attribute im + // logischen Font wirksam. + sal_Bool Seek( const xub_StrLen nPos ); + // Bastelt den Font an der gew. Position via Seek und fragt ihn, + // ob er ein Symbolfont ist. + sal_Bool IsSymbol( const xub_StrLen nPos ); + + // Fuehrt ChgPhysFnt aus, wenn Seek() sal_True zurueckliefert. + sal_Bool SeekAndChgAttrIter( const xub_StrLen nPos, OutputDevice* pOut ); + sal_Bool SeekStartAndChgAttrIter( OutputDevice* pOut, const sal_Bool bParaFont = sal_False ); + + // Gibt es ueberhaupt Attributwechsel ? + inline sal_Bool HasHints() const { return 0 != pHints; } + + // liefert fuer eine Position das Attribut + SwTxtAttr *GetAttr( const xub_StrLen nPos ) const; + + inline const SwAttrSet* GetAttrSet() const { return pAttrSet; } + + inline const SwpHints *GetHints() const { return pHints; } + + inline SwFont *GetFnt() { return pFnt; } + inline const SwFont *GetFnt() const { return pFnt; } + + inline BYTE GetPropFont() const { return nPropFont; } + inline void SetPropFont( const BYTE nNew ) { nPropFont = nNew; } + + inline SwAttrHandler& GetAttrHandler() { return aAttrHandler; } + +#if OSL_DEBUG_LEVEL > 1 + void Dump( SvStream &rOS ) const; +#endif +}; + +#endif diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx new file mode 100644 index 000000000000..c42ba1c25df0 --- /dev/null +++ b/sw/source/core/text/itrcrsr.cxx @@ -0,0 +1,1859 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrcrsr.cxx,v $ + * $Revision: 1.81.40.1 $ + * + * 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 "errhdl.hxx" +#include "ndtxt.hxx" +#include "frmfmt.hxx" +#include "paratr.hxx" +#include "flyfrm.hxx" +#include "pam.hxx" +#include "swselectionlist.hxx" +#include <sortedobjs.hxx> +#include <svx/protitem.hxx> +#include <svx/adjitem.hxx> +#include <svx/lspcitem.hxx> +#include <svx/lrspitem.hxx> +#include <frmatr.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <IDocumentSettingAccess.hxx> +#include <pagefrm.hxx> + +#include "txtcfg.hxx" +#include "itrtxt.hxx" +#include "txtfrm.hxx" +#include "flyfrms.hxx" +#include "porglue.hxx" // SwFlyCnt +#include "porfld.hxx" // SwFldPortion::IsFollow() +#include "porfly.hxx" // GetFlyCrsrOfst() +#include "pordrop.hxx" +#include "crstate.hxx" // SwCrsrMoveState +#include <pormulti.hxx> // SwMultiPortion + +// Nicht reentrant !!! +// wird in GetCharRect gesetzt und im UnitUp/Down ausgewertet. +sal_Bool SwTxtCursor::bRightMargin = sal_False; + + +/************************************************************************* + * lcl_GetCharRectInsideField + * + * After calculating the position of a character during GetCharRect + * this function allows to find the coordinates of a position (defined + * in pCMS->pSpecialPos) inside a special portion (e.g., a field) + *************************************************************************/ +void lcl_GetCharRectInsideField( SwTxtSizeInfo& rInf, SwRect& rOrig, + const SwCrsrMoveState& rCMS, + const SwLinePortion& rPor ) +{ + ASSERT( rCMS.pSpecialPos, "Information about special pos missing" ) + + if ( rPor.InFldGrp() && ((SwFldPortion&)rPor).GetExp().Len() ) + { + const USHORT nCharOfst = rCMS.pSpecialPos->nCharOfst; + USHORT nFldIdx = 0; + USHORT nFldLen = 0; + + const XubString* pString = 0; + const SwLinePortion* pPor = &rPor; + do + { + if ( pPor->InFldGrp() ) + { + pString = &((SwFldPortion*)pPor)->GetExp(); + nFldLen = pString->Len(); + } + else + { + pString = 0; + nFldLen = 0; + } + + if ( ! pPor->GetPortion() || nFldIdx + nFldLen > nCharOfst ) + break; + + nFldIdx = nFldIdx + nFldLen; + rOrig.Pos().X() += pPor->Width(); + pPor = pPor->GetPortion(); + + } while ( TRUE ); + + ASSERT( nCharOfst >= nFldIdx, "Request of position inside field failed" ) + USHORT nLen = nCharOfst - nFldIdx + 1; + + if ( pString ) + { + // get script for field portion + rInf.GetFont()->SetActual( SwScriptInfo::WhichFont( 0, pString, 0 ) ); + + xub_StrLen nOldLen = pPor->GetLen(); + ((SwLinePortion*)pPor)->SetLen( nLen - 1 ); + const SwTwips nX1 = pPor->GetLen() ? + pPor->GetTxtSize( rInf ).Width() : + 0; + + SwTwips nX2 = 0; + if ( rCMS.bRealWidth ) + { + ((SwLinePortion*)pPor)->SetLen( nLen ); + nX2 = pPor->GetTxtSize( rInf ).Width(); + } + + ((SwLinePortion*)pPor)->SetLen( nOldLen ); + + rOrig.Pos().X() += nX1; + rOrig.Width( ( nX2 > nX1 ) ? + ( nX2 - nX1 ) : + 1 ); + } + } + else + { + // special cases: no common fields, e.g., graphic number portion, + // FlyInCntPortions, Notes + rOrig.Width( rCMS.bRealWidth && rPor.Width() ? rPor.Width() : 1 ); + } +} + +/************************************************************************* + * SwTxtMargin::CtorInitTxtMargin() + *************************************************************************/ +void SwTxtMargin::CtorInitTxtMargin( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf ) +{ + CtorInitTxtIter( pNewFrm, pNewInf ); + + pInf = pNewInf; + GetInfo().SetFont( GetFnt() ); + const SwTxtNode *pNode = pFrm->GetTxtNode(); + + const SvxLRSpaceItem &rSpace = pFrm->GetTxtNode()->GetSwAttrSet().GetLRSpace(); + // --> OD 2009-09-08 #i95907#, #b6879723# + const bool bListLevelIndentsApplicable = pFrm->GetTxtNode()->AreListLevelIndentsApplicable(); + // <-- + + // + // Carefully adjust the text formatting ranges. + // + // This whole area desperately needs some rework. There are + // quite a couple of values that need to be considered: + // 1. paragraph indent + // 2. paragraph first line indent + // 3. numbering indent + // 4. numbering spacing to text + // 5. paragraph border + // Note: These values have already been used during calculation + // of the printing area of the paragraph. + const int nLMWithNum = pNode->GetLeftMarginWithNum( sal_True ); + if ( pFrm->IsRightToLeft() ) + { + // --> OD 2008-01-23 #newlistlevelattrs# + // this calculation is identical this the calculation for L2R layout - see below + nLeft = pFrm->Frm().Left() + + pFrm->Prt().Left() + + nLMWithNum - + pNode->GetLeftMarginWithNum( sal_False ) - + // --> OD 2009-09-08 #i95907#, #b6879723# +// rSpace.GetLeft() + +// rSpace.GetTxtLeft(); + ( bListLevelIndentsApplicable + ? 0 + : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) ); + // <-- + } + else + { + // --> OD 2009-09-08 #i95907#, #b6879723# +// if ( !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ) + if ( bListLevelIndentsApplicable || + !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ) + // <-- + { + // this calculation is identical this the calculation for R2L layout - see above + nLeft = pFrm->Frm().Left() + + pFrm->Prt().Left() + + nLMWithNum - + pNode->GetLeftMarginWithNum( sal_False ) - + // --> OD 2009-09-08 #i95907#, #b6879723# +// rSpace.GetLeft() + +// rSpace.GetTxtLeft(); + ( bListLevelIndentsApplicable + ? 0 + : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) ); + // <-- + } + else + { + nLeft = pFrm->Frm().Left() + + Max( long( rSpace.GetTxtLeft() + nLMWithNum ), + pFrm->Prt().Left() ); + } + } + + nRight = pFrm->Frm().Left() + pFrm->Prt().Left() + pFrm->Prt().Width(); + + if( nLeft >= nRight && + // --> FME 2005-08-10 #i53066# Omit adjustment of nLeft for numbered + // paras inside cells inside new documents: + ( pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) || + !pFrm->IsInTab() || + !nLMWithNum ) ) + // <-- + { + nLeft = pFrm->Prt().Left() + pFrm->Frm().Left(); + if( nLeft >= nRight ) // z.B. bei grossen Absatzeinzuegen in schmalen Tabellenspalten + nRight = nLeft + 1; // einen goennen wir uns immer + } + + if( pFrm->IsFollow() && pFrm->GetOfst() ) + nFirst = nLeft; + else + { + short nFLOfst = 0; + long nFirstLineOfs = 0; + if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) && + rSpace.IsAutoFirst() ) + { + nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height(); + const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing(); + if( pSpace ) + { + switch( pSpace->GetLineSpaceRule() ) + { + case SVX_LINE_SPACE_AUTO: + break; + case SVX_LINE_SPACE_MIN: + { + if( nFirstLineOfs < KSHORT( pSpace->GetLineHeight() ) ) + nFirstLineOfs = pSpace->GetLineHeight(); + break; + } + case SVX_LINE_SPACE_FIX: + nFirstLineOfs = pSpace->GetLineHeight(); + break; + default: ASSERT( sal_False, ": unknown LineSpaceRule" ); + } + switch( pSpace->GetInterLineSpaceRule() ) + { + case SVX_INTER_LINE_SPACE_OFF: + break; + case SVX_INTER_LINE_SPACE_PROP: + { + long nTmp = pSpace->GetPropLineSpace(); + // 50% ist das Minimum, bei 0% schalten wir auf + // den Defaultwert 100% um ... + if( nTmp < 50 ) + nTmp = nTmp ? 50 : 100; + + nTmp *= nFirstLineOfs; + nTmp /= 100; + if( !nTmp ) + ++nTmp; + nFirstLineOfs = (KSHORT)nTmp; + break; + } + case SVX_INTER_LINE_SPACE_FIX: + { + nFirstLineOfs += pSpace->GetInterLineSpace(); + break; + } + default: ASSERT( sal_False, ": unknown InterLineSpaceRule" ); + } + } + } + else + nFirstLineOfs = nFLOfst; + + // --> OD 2009-09-08 #i95907#, #b6879723# +// if ( pFrm->IsRightToLeft() || +// !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ) + if ( pFrm->IsRightToLeft() || + bListLevelIndentsApplicable || + !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ) + // <-- + { + nFirst = nLeft + nFirstLineOfs; + } + else + { + nFirst = pFrm->Frm().Left() + + Max( rSpace.GetTxtLeft() + nLMWithNum+ nFirstLineOfs, + pFrm->Prt().Left() ); + } + + // --> OD 2008-01-31 #newlistlevelattrs# + // Note: <SwTxtFrm::GetAdditionalFirstLineOffset()> returns a negative + // value for the new list label postion and space mode LABEL_ALIGNMENT + // and label alignment CENTER and RIGHT in L2R layout respectively + // label alignment LEFT and CENTER in R2L layout + nFirst += pFrm->GetAdditionalFirstLineOffset(); + // <-- + + if( nFirst >= nRight ) + nFirst = nRight - 1; + } + const SvxAdjustItem& rAdjust = pFrm->GetTxtNode()->GetSwAttrSet().GetAdjust(); + nAdjust = static_cast<USHORT>(rAdjust.GetAdjust()); + + // left is left and right is right + if ( pFrm->IsRightToLeft() ) + { + if ( SVX_ADJUST_LEFT == nAdjust ) + nAdjust = SVX_ADJUST_RIGHT; + else if ( SVX_ADJUST_RIGHT == nAdjust ) + nAdjust = SVX_ADJUST_LEFT; + } + + bOneBlock = rAdjust.GetOneWord() == SVX_ADJUST_BLOCK; + bLastBlock = rAdjust.GetLastBlock() == SVX_ADJUST_BLOCK; + bLastCenter = rAdjust.GetLastBlock() == SVX_ADJUST_CENTER; + + // --> OD 2008-07-01 #i91133# + mnTabLeft = pNode->GetLeftMarginForTabCalculation(); + // <-- + +#if OSL_DEBUG_LEVEL > 1 + static sal_Bool bOne = sal_False; + static sal_Bool bLast = sal_False; + static sal_Bool bCenter = sal_False; + bOneBlock |= bOne; + bLastBlock |= bLast; + bLastCenter |= bCenter; +#endif + DropInit(); +} + +/************************************************************************* + * SwTxtMargin::DropInit() + *************************************************************************/ +void SwTxtMargin::DropInit() +{ + nDropLeft = nDropLines = nDropHeight = nDropDescent = 0; + const SwParaPortion *pPara = GetInfo().GetParaPortion(); + if( pPara ) + { + const SwDropPortion *pPorDrop = pPara->FindDropPortion(); + if ( pPorDrop ) + { + nDropLeft = pPorDrop->GetDropLeft(); + nDropLines = pPorDrop->GetLines(); + nDropHeight = pPorDrop->GetDropHeight(); + nDropDescent = pPorDrop->GetDropDescent(); + } + } +} + +/************************************************************************* + * SwTxtMargin::GetLineStart() + *************************************************************************/ + +// Unter Beruecksichtigung des Erstzeileneinzuges und der angebenen Breite. +SwTwips SwTxtMargin::GetLineStart() const +{ + SwTwips nRet = GetLeftMargin(); + if( GetAdjust() != SVX_ADJUST_LEFT && + !pCurr->GetFirstPortion()->IsMarginPortion() ) + { + // Wenn die erste Portion ein Margin ist, dann wird das + // Adjustment durch die Portions ausgedrueckt. + if( GetAdjust() == SVX_ADJUST_RIGHT ) + nRet = Right() - CurrWidth(); + else if( GetAdjust() == SVX_ADJUST_CENTER ) + nRet += (GetLineWidth() - CurrWidth()) / 2; + } + return nRet; +} + +/************************************************************************* + * SwTxtCursor::CtorInitTxtCursor() + *************************************************************************/ +void SwTxtCursor::CtorInitTxtCursor( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf ) +{ + CtorInitTxtMargin( pNewFrm, pNewInf ); + // 6096: Vorsicht, die Iteratoren sind abgeleitet! + // GetInfo().SetOut( GetInfo().GetWin() ); +} + +/************************************************************************* + * SwTxtCursor::GetEndCharRect() + *************************************************************************/ + +// 1170: Antikbug: Shift-Ende vergisst das letzte Zeichen ... + +sal_Bool SwTxtCursor::GetEndCharRect( SwRect* pOrig, const xub_StrLen nOfst, + SwCrsrMoveState* pCMS, const long nMax ) +{ + // 1170: Mehrdeutigkeit von Dokumentpositionen + bRightMargin = sal_True; + CharCrsrToLine(nOfst); + + // Etwas verdreht: nOfst bezeichnet die Position hinter dem letzten + // Zeichen der letzten Zeile == Position vor dem ersten Zeichen der + // Zeile in der wir gerade stehen: + if( nOfst != GetStart() || !pCurr->GetLen() ) + { + // 8810: Masterzeile RightMargin, danach LeftMargin + const sal_Bool bRet = GetCharRect( pOrig, nOfst, pCMS, nMax ); + bRightMargin = nOfst >= GetEnd() && nOfst < GetInfo().GetTxt().Len(); + return bRet; + } + + if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() ) + return GetCharRect( pOrig, nOfst, pCMS, nMax ); + + // Adjustierung ggf. nachholen + GetAdjusted(); + + KSHORT nX = 0; + KSHORT nLast = 0; + SwLinePortion *pPor = pCurr->GetFirstPortion(); + + KSHORT nTmpHeight, nTmpAscent; + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + KSHORT nPorHeight = nTmpHeight; + KSHORT nPorAscent = nTmpAscent; + + // Die letzte Text/EndPortion der Zeile suchen + while( pPor ) + { + nX = nX + pPor->Width(); + if( pPor->InTxtGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion() + && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() ) + { + nLast = nX; + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + pPor = pPor->GetPortion(); + } + + const Size aCharSize( 1, nTmpHeight ); + pOrig->Pos( GetTopLeft() ); + pOrig->SSize( aCharSize ); + pOrig->Pos().X() += nLast; + const SwTwips nTmpRight = Right() - 1; + if( pOrig->Left() > nTmpRight ) + pOrig->Pos().X() = nTmpRight; + + if ( pCMS && pCMS->bRealHeight ) + { + if ( nTmpAscent > nPorAscent ) + pCMS->aRealHeight.X() = nTmpAscent - nPorAscent; + else + pCMS->aRealHeight.X() = 0; + ASSERT( nPorHeight, "GetCharRect: Missing Portion-Height" ); + pCMS->aRealHeight.Y() = nPorHeight; + } + + return sal_True; +} + +/************************************************************************* + * void SwTxtCursor::_GetCharRect(..) + * internal function, called by SwTxtCursor::GetCharRect() to calculate + * the relative character position in the current line. + * pOrig referes to x and y coordinates, width and height of the cursor + * pCMS is used for restricting the cursor, if there are different font + * heights in one line ( first value = offset to y of pOrig, second + * value = real height of (shortened) cursor + *************************************************************************/ + +void SwTxtCursor::_GetCharRect( SwRect* pOrig, const xub_StrLen nOfst, + SwCrsrMoveState* pCMS ) +{ + const XubString &rText = GetInfo().GetTxt(); + SwTxtSizeInfo aInf( GetInfo(), rText, nStart ); + if( GetPropFont() ) + aInf.GetFont()->SetProportion( GetPropFont() ); + KSHORT nTmpAscent, nTmpHeight; // Zeilenhoehe + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + const Size aCharSize( 1, nTmpHeight ); + const Point aCharPos; + pOrig->Pos( aCharPos ); + pOrig->SSize( aCharSize ); + + // If we are looking for a position inside a field which covers + // more than one line we may not skip any "empty portions" at the + // beginning of a line + const sal_Bool bInsideFirstField = pCMS && pCMS->pSpecialPos && + ( pCMS->pSpecialPos->nLineOfst || + SP_EXTEND_RANGE_BEFORE == + pCMS->pSpecialPos->nExtendRange ); + + sal_Bool bWidth = pCMS && pCMS->bRealWidth; + if( !pCurr->GetLen() && !pCurr->Width() ) + { + if ( pCMS && pCMS->bRealHeight ) + { + pCMS->aRealHeight.X() = 0; + pCMS->aRealHeight.Y() = nTmpHeight; + } + } + else + { + KSHORT nPorHeight = nTmpHeight; + KSHORT nPorAscent = nTmpAscent; + SwTwips nX = 0; + SwTwips nTmpFirst = 0; + SwLinePortion *pPor = pCurr->GetFirstPortion(); + SwBidiPortion* pLastBidiPor = 0; + SwTwips nLastBidiPorWidth = 0; + SvUShorts* pKanaComp = pCurr->GetpKanaComp(); + MSHORT nSpaceIdx = 0; + MSHORT nKanaIdx = 0; + long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0; + + sal_Bool bNoTxt = sal_True; + + // Zuerst werden alle Portions ohne Len am Zeilenanfang uebersprungen. + // Ausnahme bilden die fiesen Spezialportions aus WhichFirstPortion: + // Num, ErgoSum, FtnNum, FeldReste + // 8477: aber auch die einzige Textportion einer leeren Zeile mit + // Right/Center-Adjustment! Also nicht nur pPor->GetExpandPortion() ... + while( pPor && !pPor->GetLen() && ! bInsideFirstField ) + { + nX += pPor->Width(); + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nX += pPor->CalcSpacing( nSpaceAdd, aInf ); + if( bNoTxt ) + nTmpFirst = nX; + // 8670: EndPortions zaehlen hier einmal als TxtPortions. + // --> OD 2008-01-28 #newlistlevelattrs# +// if( pPor->InTxtGrp() || pPor->IsBreakPortion() ) + if( pPor->InTxtGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() ) + // <-- + { + bNoTxt = sal_False; + nTmpFirst = nX; + } + if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() ) + { + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() ) + ++nKanaIdx; + } + if( pPor->InFixMargGrp() ) + { + if( pPor->IsMarginPortion() ) + bNoTxt = sal_False; + else + { + // fix margin portion => next SpaceAdd, KanaComp value + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() ) + ++nKanaIdx; + } + } + pPor = pPor->GetPortion(); + } + + if( !pPor ) + { + // Es sind nur Spezialportions unterwegs. + nX = nTmpFirst; + } + else + { + if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() && + (!pPor->InFldGrp() || pPor->GetAscent() ) ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst || + ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) ) + { + if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() && + (!pPor->InFldGrp() || pPor->GetAscent() ) ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + + // If we are behind the portion, we add the portion width to + // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen(). + // For common portions (including BidiPortions) we want to add + // the portion width to nX. For MultiPortions, nExtra = 0, + // therefore we go to the 'else' branch and start a recursion. + const BYTE nExtra = pPor->IsMultiPortion() && + ! ((SwMultiPortion*)pPor)->IsBidi() && + ! bWidth ? 0 : 1; + if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra ) + { + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nX += pPor->PrtWidth() + + pPor->CalcSpacing( nSpaceAdd, aInf ); + else + { + if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) + { + // update to current SpaceAdd, KanaComp values + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if ( pKanaComp && + ( nKanaIdx + 1 ) < pKanaComp->Count() + ) + ++nKanaIdx; + } + if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() && + !pPor->GetPortion()->IsMarginPortion() ) ) + nX += pPor->PrtWidth(); + } + if( pPor->IsMultiPortion() ) + { + if ( ((SwMultiPortion*)pPor)->HasTabulator() ) + { + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() ) + ++nKanaIdx; + } + + // if we are right behind a BidiPortion, we have to + // hold a pointer to the BidiPortion in order to + // find the correct cursor position, depending on the + // cursor level + if ( ((SwMultiPortion*)pPor)->IsBidi() && + aInf.GetIdx() + pPor->GetLen() == nOfst ) + { + pLastBidiPor = (SwBidiPortion*)pPor; + nLastBidiPorWidth = pLastBidiPor->Width() + + pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );; + } + } + + aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() ); + pPor = pPor->GetPortion(); + } + else + { + if( pPor->IsMultiPortion() ) + { + nTmpAscent = AdjustBaseLine( *pCurr, pPor ); + GetInfo().SetMulti( sal_True ); + pOrig->Pos().Y() += nTmpAscent - nPorAscent; + + if( pCMS && pCMS->b2Lines ) + { + sal_Bool bRecursion = sal_True; + if ( ! pCMS->p2Lines ) + { + pCMS->p2Lines = new Sw2LinesPos; + pCMS->p2Lines->aLine = SwRect(aCharPos, aCharSize); + bRecursion = sal_False; + } + + if( ((SwMultiPortion*)pPor)->HasRotation() ) + { + if( ((SwMultiPortion*)pPor)->IsRevers() ) + pCMS->p2Lines->nMultiType = MT_ROT_270; + else + pCMS->p2Lines->nMultiType = MT_ROT_90; + } + else if( ((SwMultiPortion*)pPor)->IsDouble() ) + pCMS->p2Lines->nMultiType = MT_TWOLINE; + else if( ((SwMultiPortion*)pPor)->IsBidi() ) + pCMS->p2Lines->nMultiType = MT_BIDI; + else + pCMS->p2Lines->nMultiType = MT_RUBY; + + SwTwips nTmpWidth = pPor->Width(); + if( nSpaceAdd ) + nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf); + + SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ), + Size( nTmpWidth, pPor->Height() ) ); + + if ( ! bRecursion ) + pCMS->p2Lines->aPortion = aRect; + else + pCMS->p2Lines->aPortion2 = aRect; + } + + // In a multi-portion we use GetCharRect()-function + // recursively and must add the x-position + // of the multi-portion. + xub_StrLen nOldStart = nStart; + SwTwips nOldY = nY; + BYTE nOldProp = GetPropFont(); + nStart = aInf.GetIdx(); + SwLineLayout* pOldCurr = pCurr; + pCurr = &((SwMultiPortion*)pPor)->GetRoot(); + if( ((SwMultiPortion*)pPor)->IsDouble() ) + SetPropFont( 50 ); + + GETGRID( GetTxtFrm()->FindPageFrm() ) + const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid(); + const USHORT nRubyHeight = bHasGrid ? + pGrid->GetRubyHeight() : 0; + + if( nStart + pCurr->GetLen() <= nOfst && GetNext() && + ( ! ((SwMultiPortion*)pPor)->IsRuby() || + ((SwMultiPortion*)pPor)->OnTop() ) ) + { + USHORT nOffset; + // in grid mode we may only add the height of the + // ruby line if ruby line is on top + if ( bHasGrid && + ((SwMultiPortion*)pPor)->IsRuby() && + ((SwMultiPortion*)pPor)->OnTop() ) + nOffset = nRubyHeight; + else + nOffset = GetLineHeight(); + + pOrig->Pos().Y() += nOffset; + Next(); + } + + sal_Bool bSpaceChg = ((SwMultiPortion*)pPor)-> + ChgSpaceAdd( pCurr, nSpaceAdd ); + Point aOldPos = pOrig->Pos(); + + // Ok, for ruby portions in grid mode we have to + // temporarily set the inner line height to the + // outer line height because that value is needed + // for the adjustment inside the recursion + const USHORT nOldRubyHeight = pCurr->Height(); + const USHORT nOldRubyRealHeight = pCurr->GetRealHeight(); + const sal_Bool bChgHeight = + ((SwMultiPortion*)pPor)->IsRuby() && bHasGrid; + + if ( bChgHeight ) + { + pCurr->Height( pOldCurr->Height() - nRubyHeight ); + pCurr->SetRealHeight( pOldCurr->GetRealHeight() - + nRubyHeight ); + } + + SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); + if ( ((SwMultiPortion*)pPor)->IsBidi() ) + { + aLayoutModeModifier.Modify( + ((SwBidiPortion*)pPor)->GetLevel() % 2 ); + } + + _GetCharRect( pOrig, nOfst, pCMS ); + + if ( bChgHeight ) + { + pCurr->Height( nOldRubyHeight ); + pCurr->SetRealHeight( nOldRubyRealHeight ); + } + + // if we are still in the first row of + // our 2 line multiportion, we use the FirstMulti flag + // to indicate this + if ( ((SwMultiPortion*)pPor)->IsDouble() ) + { + // the recursion may have damaged our font size + SetPropFont( nOldProp ); + if ( !nOldProp ) + nOldProp = 100; + GetInfo().GetFont()->SetProportion( 100 ); + + if ( pCurr == &((SwMultiPortion*)pPor)->GetRoot() ) + { + GetInfo().SetFirstMulti( sal_True ); + + // we want to treat a double line portion like a + // single line portion, if there is no text in + // the second line + if ( !pCurr->GetNext() || + !pCurr->GetNext()->GetLen() ) + GetInfo().SetMulti( sal_False ); + } + } + // ruby portions are treated like single line portions + else if( ((SwMultiPortion*)pPor)->IsRuby() || + ((SwMultiPortion*)pPor)->IsBidi() ) + GetInfo().SetMulti( sal_False ); + + // calculate cursor values + if( ((SwMultiPortion*)pPor)->HasRotation() ) + { + GetInfo().SetMulti( sal_False ); + long nTmp = pOrig->Width(); + pOrig->Width( pOrig->Height() ); + pOrig->Height( nTmp ); + nTmp = pOrig->Left() - aOldPos.X(); + + // if we travel into our rotated portion from + // a line below, we have to take care, that the + // y coord in pOrig is less than line height: + if ( nTmp ) + nTmp--; + + pOrig->Pos().X() = nX + aOldPos.X(); + if( ((SwMultiPortion*)pPor)->IsRevers() ) + pOrig->Pos().Y() = aOldPos.Y() + nTmp; + else + pOrig->Pos().Y() = aOldPos.Y() + + pPor->Height() - nTmp - pOrig->Height(); + if ( pCMS && pCMS->bRealHeight ) + { + pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y(); + // result for rotated multi portion is not + // correct for reverse (270 degree) portions + if( ((SwMultiPortion*)pPor)->IsRevers() ) + { + if ( SvxParaVertAlignItem::AUTOMATIC == + GetLineInfo().GetVertAlign() ) + // if vertical alignment is set to auto, + // we switch from base line alignment + // to centered alignment + pCMS->aRealHeight.X() = + ( pOrig->Width() + + pCMS->aRealHeight.Y() ) / 2; + else + pCMS->aRealHeight.X() = + ( pOrig->Width() - + pCMS->aRealHeight.X() + + pCMS->aRealHeight.Y() ); + } + } + } + else + { + pOrig->Pos().Y() += aOldPos.Y(); + if ( ((SwMultiPortion*)pPor)->IsBidi() ) + { + const SwTwips nPorWidth = pPor->Width() + + pPor->CalcSpacing( nSpaceAdd, aInf ); + const SwTwips nInsideOfst = pOrig->Pos().X(); + pOrig->Pos().X() = nX + nPorWidth - + nInsideOfst - pOrig->Width(); + } + else + pOrig->Pos().X() += nX; + + if( ((SwMultiPortion*)pPor)->HasBrackets() ) + pOrig->Pos().X() += + ((SwDoubleLinePortion*)pPor)->PreWidth(); + } + + if( bSpaceChg ) + SwDoubleLinePortion::ResetSpaceAdd( pCurr ); + + pCurr = pOldCurr; + nStart = nOldStart; + nY = nOldY; + bPrev = sal_False; + + return; + } + if ( pPor->PrtWidth() ) + { + xub_StrLen nOldLen = pPor->GetLen(); + pPor->SetLen( nOfst - aInf.GetIdx() ); + aInf.SetLen( pPor->GetLen() ); + if( nX || !pPor->InNumberGrp() ) + { + SeekAndChg( aInf ); + const sal_Bool bOldOnWin = aInf.OnWin(); + aInf.SetOnWin( sal_False ); // keine BULLETs! + SwTwips nTmp = nX; + aInf.SetKanaComp( pKanaComp ); + aInf.SetKanaIdx( nKanaIdx ); + nX += pPor->GetTxtSize( aInf ).Width(); + aInf.SetOnWin( bOldOnWin ); + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nX += pPor->CalcSpacing( nSpaceAdd, aInf ); + if( bWidth ) + { + pPor->SetLen( pPor->GetLen() + 1 ); + aInf.SetLen( pPor->GetLen() ); + aInf.SetOnWin( sal_False ); // keine BULLETs! + nTmp += pPor->GetTxtSize( aInf ).Width(); + aInf.SetOnWin( bOldOnWin ); + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nTmp += pPor->CalcSpacing(nSpaceAdd, aInf); + pOrig->Width( nTmp - nX ); + } + } + pPor->SetLen( nOldLen ); + } + bWidth = sal_False; + break; + } + } + } + + if( pPor ) + { + ASSERT( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" ); + sal_Bool bEmptyFld = sal_False; + if( pPor->InFldGrp() && pPor->GetLen() ) + { + SwFldPortion *pTmp = (SwFldPortion*)pPor; + while( pTmp->HasFollow() && !pTmp->GetExp().Len() ) + { + KSHORT nAddX = pTmp->Width(); + SwLinePortion *pNext = pTmp->GetPortion(); + while( pNext && !pNext->InFldGrp() ) + { + ASSERT( !pNext->GetLen(), "Where's my field follow?" ); + nAddX = nAddX + pNext->Width(); + pNext = pNext->GetPortion(); + } + if( !pNext ) + break; + pTmp = (SwFldPortion*)pNext; + nPorHeight = pTmp->Height(); + nPorAscent = pTmp->GetAscent(); + nX += nAddX; + bEmptyFld = sal_True; + } + } + // 8513: Felder im Blocksatz, ueberspringen + while( pPor && !pPor->GetLen() && ! bInsideFirstField && + ( pPor->IsFlyPortion() || pPor->IsKernPortion() || + pPor->IsBlankPortion() || pPor->InTabGrp() || + ( !bEmptyFld && pPor->InFldGrp() ) ) ) + { + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nX += pPor->PrtWidth() + + pPor->CalcSpacing( nSpaceAdd, aInf ); + else + { + if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) + { + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() ) + ++nKanaIdx; + } + if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() && + !pPor->GetPortion()->IsMarginPortion() ) ) + nX += pPor->PrtWidth(); + } + if( pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->HasTabulator() ) + { + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() ) + ++nKanaIdx; + } + if( !pPor->IsFlyPortion() ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + pPor = pPor->GetPortion(); + } + + if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() && + pPor->GetPortion() && pPor->GetPortion()->InFixGrp() ) + { + // Alle Sonderportions muessen uebersprungen werden + // Beispiel: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right() + // Ohne den Ausgleich landen wir vor '-' mit dem + // Ausgleich vor 's'. + while( pPor && !pPor->GetLen() ) + { + DBG_LOOP; + nX += pPor->Width(); + if( !pPor->IsMarginPortion() ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + pPor = pPor->GetPortion(); + } + } + if( pPor && pCMS ) + { + if( pCMS->bFieldInfo && pPor->InFldGrp() && pPor->Width() ) + pOrig->Width( pPor->Width() ); + if( pPor->IsDropPortion() ) + { + nPorAscent = ((SwDropPortion*)pPor)->GetDropHeight(); + // The drop height is only calculated, if we have more than + // one line. Otherwise it is 0. + if ( ! nPorAscent) + nPorAscent = pPor->Height(); + nPorHeight = nPorAscent; + pOrig->Height( nPorHeight + + ((SwDropPortion*)pPor)->GetDropDescent() ); + if( nTmpHeight < pOrig->Height() ) + { + nTmpAscent = nPorAscent; + nTmpHeight = USHORT( pOrig->Height() ); + } + } + if( bWidth && pPor->PrtWidth() && pPor->GetLen() && + aInf.GetIdx() == nOfst ) + { + if( !pPor->IsFlyPortion() && pPor->Height() && + pPor->GetAscent() ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + SwTwips nTmp; + if( 2 > pPor->GetLen() ) + { + nTmp = pPor->Width(); + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nTmp += pPor->CalcSpacing( nSpaceAdd, aInf ); + } + else + { + const sal_Bool bOldOnWin = aInf.OnWin(); + xub_StrLen nOldLen = pPor->GetLen(); + pPor->SetLen( 1 ); + aInf.SetLen( pPor->GetLen() ); + SeekAndChg( aInf ); + aInf.SetOnWin( sal_False ); // keine BULLETs! + aInf.SetKanaComp( pKanaComp ); + aInf.SetKanaIdx( nKanaIdx ); + nTmp = pPor->GetTxtSize( aInf ).Width(); + aInf.SetOnWin( bOldOnWin ); + if ( pPor->InSpaceGrp() && nSpaceAdd ) + nTmp += pPor->CalcSpacing( nSpaceAdd, aInf ); + pPor->SetLen( nOldLen ); + } + pOrig->Width( nTmp ); + } + + // travel inside field portion? + if ( pCMS->pSpecialPos ) + { + // apply attributes to font + Seek( nOfst ); + lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor ); + } + } + } + + // special case: We are at the beginning of a BidiPortion or + // directly behind a BidiPortion + if ( pCMS && + ( pLastBidiPor || + ( pPor && + pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->IsBidi() ) ) ) + { + // we determine if the cursor has to blink before or behind + // the bidi portion + if ( pLastBidiPor ) + { + const BYTE nPortionLevel = pLastBidiPor->GetLevel(); + + if ( pCMS->nCursorBidiLevel >= nPortionLevel ) + { + // we came from inside the bidi portion, we want to blink + // behind the portion + pOrig->Pos().X() -= nLastBidiPorWidth; + + // Again, there is a special case: logically behind + // the portion can actually mean that the cursor is inside + // the portion. This can happen is the last portion + // inside the bidi portion is a nested bidi portion + SwLineLayout& rLineLayout = + ((SwMultiPortion*)pLastBidiPor)->GetRoot(); + + const SwLinePortion *pLast = rLineLayout.FindLastPortion(); + if ( pLast->IsMultiPortion() ) + { + ASSERT( ((SwMultiPortion*)pLast)->IsBidi(), + "Non-BidiPortion inside BidiPortion" ) + pOrig->Pos().X() += pLast->Width() + + pLast->CalcSpacing( nSpaceAdd, aInf ); + } + } + } + else + { + const BYTE nPortionLevel = ((SwBidiPortion*)pPor)->GetLevel(); + + if ( pCMS->nCursorBidiLevel >= nPortionLevel ) + { + // we came from inside the bidi portion, we want to blink + // behind the portion + pOrig->Pos().X() += pPor->Width() + + pPor->CalcSpacing( nSpaceAdd, aInf ); + } + } + } + + pOrig->Pos().X() += nX; + + if ( pCMS && pCMS->bRealHeight ) + { + nTmpAscent = AdjustBaseLine( *pCurr, 0, nPorHeight, nPorAscent ); + if ( nTmpAscent > nPorAscent ) + pCMS->aRealHeight.X() = nTmpAscent - nPorAscent; + else + pCMS->aRealHeight.X() = 0; + ASSERT( nPorHeight, "GetCharRect: Missing Portion-Height" ); + if ( nTmpHeight > nPorHeight ) + pCMS->aRealHeight.Y() = nPorHeight; + else + pCMS->aRealHeight.Y() = nTmpHeight; + } + } +} + +/************************************************************************* + * SwTxtCursor::GetCharRect() + *************************************************************************/ + +sal_Bool SwTxtCursor::GetCharRect( SwRect* pOrig, const xub_StrLen nOfst, + SwCrsrMoveState* pCMS, const long nMax ) +{ + CharCrsrToLine(nOfst); + + // Indicates that a position inside a special portion (field, number portion) + // is requested. + const sal_Bool bSpecialPos = pCMS && pCMS->pSpecialPos; + xub_StrLen nFindOfst = nOfst; + + if ( bSpecialPos ) + { + const BYTE nExtendRange = pCMS->pSpecialPos->nExtendRange; + + ASSERT( ! pCMS->pSpecialPos->nLineOfst || SP_EXTEND_RANGE_BEFORE != nExtendRange, + "LineOffset AND Number Portion?" ) + + // portions which are behind the string + if ( SP_EXTEND_RANGE_BEHIND == nExtendRange ) + ++nFindOfst; + + // skip lines for fields which cover more than one line + for ( USHORT i = 0; i < pCMS->pSpecialPos->nLineOfst; i++ ) + Next(); + } + + // Adjustierung ggf. nachholen + GetAdjusted(); + + const Point aCharPos( GetTopLeft() ); + sal_Bool bRet = sal_True; + + _GetCharRect( pOrig, nFindOfst, pCMS ); + + const SwTwips nTmpRight = Right() - 12; + + pOrig->Pos().X() += aCharPos.X(); + pOrig->Pos().Y() += aCharPos.Y(); + + if( pCMS && pCMS->b2Lines && pCMS->p2Lines ) + { + pCMS->p2Lines->aLine.Pos().X() += aCharPos.X(); + pCMS->p2Lines->aLine.Pos().Y() += aCharPos.Y(); + pCMS->p2Lines->aPortion.Pos().X() += aCharPos.X(); + pCMS->p2Lines->aPortion.Pos().Y() += aCharPos.Y(); + } + + if( pOrig->Left() > nTmpRight ) + pOrig->Pos().X() = nTmpRight; + + if( nMax ) + { + if( pOrig->Top() + pOrig->Height() > nMax ) + { + if( pOrig->Top() > nMax ) + pOrig->Top( nMax ); + pOrig->Height( nMax - pOrig->Top() ); + } + if ( pCMS && pCMS->bRealHeight && pCMS->aRealHeight.Y() >= 0 ) + { + long nTmp = pCMS->aRealHeight.X() + pOrig->Top(); + if( nTmp >= nMax ) + { + pCMS->aRealHeight.X() = nMax - pOrig->Top(); + pCMS->aRealHeight.Y() = 0; + } + else if( nTmp + pCMS->aRealHeight.Y() > nMax ) + pCMS->aRealHeight.Y() = nMax - nTmp; + } + } + long nOut = pOrig->Right() - GetTxtFrm()->Frm().Right(); + if( nOut > 0 ) + { + if( GetTxtFrm()->Frm().Width() < GetTxtFrm()->Prt().Left() + + GetTxtFrm()->Prt().Width() ) + nOut += GetTxtFrm()->Frm().Width() - GetTxtFrm()->Prt().Left() + - GetTxtFrm()->Prt().Width(); + if( nOut > 0 ) + pOrig->Pos().X() -= nOut + 10; + } + return bRet; +} + +/************************************************************************* + * SwTxtCursor::GetCrsrOfst() + * + * Return: Offset im String + *************************************************************************/ +xub_StrLen SwTxtCursor::GetCrsrOfst( SwPosition *pPos, const Point &rPoint, + const MSHORT nChgNode, SwCrsrMoveState* pCMS ) const +{ + // Adjustierung ggf. nachholen + GetAdjusted(); + + const XubString &rText = GetInfo().GetTxt(); + xub_StrLen nOffset = 0; + + // x ist der horizontale Offset innerhalb der Zeile. + SwTwips x = rPoint.X(); + CONST SwTwips nLeftMargin = GetLineStart(); + SwTwips nRightMargin = GetLineEnd(); + if( nRightMargin == nLeftMargin ) + nRightMargin += 30; + + const sal_Bool bLeftOver = x < nLeftMargin; + if( bLeftOver ) + x = nLeftMargin; + const sal_Bool bRightOver = x > nRightMargin; + if( bRightOver ) + x = nRightMargin; + + sal_Bool bRightAllowed = pCMS && ( pCMS->eState == MV_NONE ); + + // Bis hierher in Dokumentkoordinaten. + x -= nLeftMargin; + + KSHORT nX = KSHORT( x ); + + // Wenn es in der Zeile Attributwechsel gibt, den Abschnitt + // suchen, in dem nX liegt. + SwLinePortion *pPor = pCurr->GetFirstPortion(); + xub_StrLen nCurrStart = nStart; + sal_Bool bHolePortion = sal_False; + sal_Bool bLastHyph = sal_False; + + SvUShorts *pKanaComp = pCurr->GetpKanaComp(); + xub_StrLen nOldIdx = GetInfo().GetIdx(); + MSHORT nSpaceIdx = 0; + MSHORT nKanaIdx = 0; + long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0; + short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0; + + // nWidth ist die Breite der Zeile, oder die Breite des + // Abschnitts mit dem Fontwechsel, in dem nX liegt. + + KSHORT nWidth = pPor->Width(); + if ( pCurr->IsSpaceAdd() || pKanaComp ) + { + if ( pPor->InSpaceGrp() && nSpaceAdd ) + { + ((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart ); + nWidth = nWidth + USHORT( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) ); + } + if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) || + ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() ) + ) + { + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if( pKanaComp ) + { + if ( nKanaIdx + 1 < pKanaComp->Count() ) + nKanaComp = (*pKanaComp)[++nKanaIdx]; + else + nKanaComp = 0; + } + } + } + + KSHORT nWidth30; + if ( pPor->IsPostItsPortion() ) + nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2; + else + nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ? + 30 : + nWidth; + + while( pPor->GetPortion() && nWidth30 < nX && !pPor->IsBreakPortion() ) + { + nX = nX - nWidth; + nCurrStart = nCurrStart + pPor->GetLen(); + bHolePortion = pPor->IsHolePortion(); + pPor = pPor->GetPortion(); + nWidth = pPor->Width(); + if ( pCurr->IsSpaceAdd() || pKanaComp ) + { + if ( pPor->InSpaceGrp() && nSpaceAdd ) + { + ((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart ); + nWidth = nWidth + USHORT( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) ); + } + + if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) || + ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() ) + ) + { + if ( pCurr->IsSpaceAdd() ) + { + if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() ) + nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx ); + else + nSpaceAdd = 0; + } + + if ( pKanaComp ) + { + if( nKanaIdx + 1 < pKanaComp->Count() ) + nKanaComp = (*pKanaComp)[++nKanaIdx]; + else + nKanaComp = 0; + } + } + } + + if ( pPor->IsPostItsPortion() ) + nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2; + else + nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ? + 30 : + nWidth; + if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() ) + bLastHyph = pPor->InHyphGrp(); + } + + const sal_Bool bLastPortion = (0 == pPor->GetPortion()); + + if( nX==nWidth ) + { + SwLinePortion *pNextPor = pPor->GetPortion(); + while( pNextPor && pNextPor->InFldGrp() && !pNextPor->Width() ) + { + nCurrStart = nCurrStart + pPor->GetLen(); + pPor = pNextPor; + if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() ) + bLastHyph = pPor->InHyphGrp(); + pNextPor = pPor->GetPortion(); + } + } + + ((SwTxtSizeInfo&)GetInfo()).SetIdx( nOldIdx ); + + xub_StrLen nLength = pPor->GetLen(); + + sal_Bool bFieldInfo = pCMS && pCMS->bFieldInfo; + + if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver || + ( pPor->InNumberGrp() && !pPor->IsFtnNumPortion() ) || + ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) ) + ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True; + + + // #i27615# + if (pCMS) + { + if( pCMS->bInFrontOfLabel) + { + if (! (2 * nX < nWidth && pPor->InNumberGrp() && + !pPor->IsFtnNumPortion())) + pCMS->bInFrontOfLabel = sal_False; + } + } + + // 7684: Wir sind genau auf der HyphPortion angelangt und muessen dafuer + // sorgen, dass wir in dem String landen. + // 7993: Wenn die Laenge 0 ist muessen wir raus... + if( !nLength ) + { + if( pCMS ) + { + if( pPor->IsFlyPortion() && bFieldInfo ) + ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True; + + if (!bRightOver && nX) + { + if( pPor->IsFtnNumPortion()) + ((SwCrsrMoveState*)pCMS)->bFtnNoInfo = sal_True; + else if (pPor->InNumberGrp() ) // #i23726# + { + ((SwCrsrMoveState*)pCMS)->nInNumPostionOffset = nX; + ((SwCrsrMoveState*)pCMS)->bInNumPortion = sal_True; + } + } + } + if( !nCurrStart ) + return 0; + + // 7849, 7816: auf pPor->GetHyphPortion kann nicht verzichtet werden! + if( bHolePortion || ( !bRightAllowed && bLastHyph ) || + ( pPor->IsMarginPortion() && !pPor->GetPortion() && + // 46598: In der letzten Zeile eines zentrierten Absatzes wollen + // wir auch mal hinter dem letzten Zeichen landen. + nCurrStart < rText.Len() ) ) + --nCurrStart; + else if( pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow() + && nWidth > nX ) + { + if( bFieldInfo ) + --nCurrStart; + else + { + KSHORT nHeight = pPor->Height(); + if ( !nHeight || nHeight > nWidth ) + nHeight = nWidth; + if( nChgNode && nWidth - nHeight/2 > nX ) + --nCurrStart; + } + } + return nCurrStart; + } + if ( 1 == nLength ) + { + if ( nWidth ) + { + // Sonst kommen wir nicht mehr in zeichengeb. Rahmen hinein... + if( !( nChgNode && pPos && pPor->IsFlyCntPortion() ) ) + { + if ( pPor->InFldGrp() || + ( pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->IsBidi() ) ) + { + KSHORT nHeight = 0; + if( !bFieldInfo ) + { + nHeight = pPor->Height(); + if ( !nHeight || nHeight > nWidth ) + nHeight = nWidth; + } + + if( nWidth - nHeight/2 <= nX && + ( ! pPor->InFldGrp() || + !((SwFldPortion*)pPor)->HasFollow() ) ) + ++nCurrStart; + } + else if ( ( !pPor->IsFlyPortion() || ( pPor->GetPortion() && + !pPor->GetPortion()->IsMarginPortion() && + !pPor->GetPortion()->IsHolePortion() ) ) + && ( nWidth/2 < nX ) && + ( !bFieldInfo || + ( pPor->GetPortion() && + pPor->GetPortion()->IsPostItsPortion() ) ) + && ( bRightAllowed || !bLastHyph )) + ++nCurrStart; + + // if we want to get the position inside the field, we should not return + if ( !pCMS || !pCMS->pSpecialPos ) + return nCurrStart; + } + } + else + { + if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() || + pPor->InToxRefGrp() ) + return nCurrStart; + if ( pPor->InFldGrp() ) + { + if( bRightOver && !((SwFldPortion*)pPor)->HasFollow() ) + ++nCurrStart; + return nCurrStart; + } + } + } + + if( bLastPortion && (pCurr->GetNext() || pFrm->GetFollow() ) ) + --nLength; + + if( nWidth > nX || + ( nWidth == nX && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsDouble() ) ) + { + if( pPor->IsMultiPortion() ) + { + // In a multi-portion we use GetCrsrOfst()-function recursively + SwTwips nTmpY = rPoint.Y() - pCurr->GetAscent() + pPor->GetAscent(); + // if we are in the first line of a double line portion, we have + // to add a value to nTmpY for not staying in this line + // we also want to skip the first line, if we are inside ruby + if ( ( ((SwTxtSizeInfo*)pInf)->IsMulti() && + ((SwTxtSizeInfo*)pInf)->IsFirstMulti() ) || + ( ((SwMultiPortion*)pPor)->IsRuby() && + ((SwMultiPortion*)pPor)->OnTop() ) ) + nTmpY += ((SwMultiPortion*)pPor)->Height(); + + // Important for cursor traveling in ruby portions: + // We have to set nTmpY to 0 in order to stay in the first row + // if the phonetic line is the second row + if ( ((SwMultiPortion*)pPor)->IsRuby() && + ! ((SwMultiPortion*)pPor)->OnTop() ) + nTmpY = 0; + + SwTxtCursorSave aSave( (SwTxtCursor*)this, (SwMultiPortion*)pPor, + nTmpY, nX, nCurrStart, nSpaceAdd ); + + SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); + if ( ((SwMultiPortion*)pPor)->IsBidi() ) + { + const BYTE nBidiLevel = ((SwBidiPortion*)pPor)->GetLevel(); + aLayoutModeModifier.Modify( nBidiLevel % 2 ); + } + + if( ((SwMultiPortion*)pPor)->HasRotation() ) + { + nTmpY -= nY; + if( !((SwMultiPortion*)pPor)->IsRevers() ) + nTmpY = pPor->Height() - nTmpY; + if( nTmpY < 0 ) + nTmpY = 0; + nX = (KSHORT)nTmpY; + } + + if( ((SwMultiPortion*)pPor)->HasBrackets() ) + { + USHORT nPreWidth = ((SwDoubleLinePortion*)pPor)->PreWidth(); + if ( nX > nPreWidth ) + nX = nX - nPreWidth; + else + nX = 0; + } + + return GetCrsrOfst( pPos, Point( GetLineStart() + nX, rPoint.Y() ), + nChgNode, pCMS ); + } + if( pPor->InTxtGrp() ) + { + BYTE nOldProp; + if( GetPropFont() ) + { + ((SwFont*)GetFnt())->SetProportion( GetPropFont() ); + nOldProp = GetFnt()->GetPropr(); + } + else + nOldProp = 0; + { + SwTxtSizeInfo aSizeInf( GetInfo(), rText, nCurrStart ); + ((SwTxtCursor*)this)->SeekAndChg( aSizeInf ); + SwTxtSlot aDiffTxt( &aSizeInf, ((SwTxtPortion*)pPor), false, false ); + SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ? + ((SwDropPortion*)pPor)->GetFnt() : NULL ); + + SwParaPortion* pPara = (SwParaPortion*)GetInfo().GetParaPortion(); + ASSERT( pPara, "No paragraph!" ); + + SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(), + *aSizeInf.GetOut(), + &pPara->GetScriptInfo(), + aSizeInf.GetTxt(), + aSizeInf.GetIdx(), + pPor->GetLen() ); + aDrawInf.SetOfst( nX ); + + if ( nSpaceAdd ) + { + xub_StrLen nCharCnt; + // --> FME 2005-04-04 #i41860# Thai justified alignemt needs some + // additional information: + aDrawInf.SetNumberOfBlanks( pPor->InTxtGrp() ? + static_cast<const SwTxtPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) : + 0 ); + // <-- + } + + if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos ) + aDrawInf.SetLen( STRING_LEN ); // SMARTTAGS + + aDrawInf.SetSpace( nSpaceAdd ); + aDrawInf.SetFont( aSizeInf.GetFont() ); + aDrawInf.SetFrm( pFrm ); + aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() ); + aDrawInf.SetPosMatchesBounds( pCMS && pCMS->bPosMatchesBounds ); + + if ( SW_CJK == aSizeInf.GetFont()->GetActual() && + pPara->GetScriptInfo().CountCompChg() && + ! pPor->InFldGrp() ) + aDrawInf.SetKanaComp( nKanaComp ); + + nLength = aSizeInf.GetFont()->_GetCrsrOfst( aDrawInf ); + + // get position inside field portion? + if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos ) + { + pCMS->pSpecialPos->nCharOfst = nLength; + nLength = 0; // SMARTTAGS + } + + // set cursor bidi level + if ( pCMS ) + ((SwCrsrMoveState*)pCMS)->nCursorBidiLevel = + aDrawInf.GetCursorBidiLevel(); + + if( bFieldInfo && nLength == pPor->GetLen() && + ( ! pPor->GetPortion() || + ! pPor->GetPortion()->IsPostItsPortion() ) ) + --nLength; + } + if( nOldProp ) + ((SwFont*)GetFnt())->SetProportion( nOldProp ); + } + else + { + if( nChgNode && pPos && pPor->IsFlyCntPortion() + && !( (SwFlyCntPortion*)pPor )->IsDraw() ) + { + // JP 24.11.94: liegt die Pos nicht im Fly, dann + // darf nicht mit STRING_LEN returnt werden! + // (BugId: 9692 + Aenderung in feshview) + SwFlyInCntFrm *pTmp = ( (SwFlyCntPortion*)pPor )->GetFlyFrm(); + sal_Bool bChgNode = 1 < nChgNode; + if( !bChgNode ) + { + SwFrm* pLower = pTmp->GetLower(); + if( pLower && (pLower->IsTxtFrm() || pLower->IsLayoutFrm()) ) + bChgNode = sal_True; + } + Point aTmpPoint( rPoint ); + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( aTmpPoint ); + + if ( pFrm->IsVertical() ) + pFrm->SwitchHorizontalToVertical( aTmpPoint ); + + if( bChgNode && pTmp->Frm().IsInside( aTmpPoint ) && + !( pTmp->IsProtected() ) ) + { + nLength = ((SwFlyCntPortion*)pPor)-> + GetFlyCrsrOfst( nX, aTmpPoint, pPos, pCMS ); + // Sobald der Frame gewechselt wird, muessen wir aufpassen, dass + // unser Font wieder im OutputDevice steht. + // vgl. Paint und new SwFlyCntPortion ! + ((SwTxtSizeInfo*)pInf)->SelectFont(); + + // 6776: Das pIter->GetCrsrOfst returnt + // aus einer Verschachtelung mit STRING_LEN. + return STRING_LEN; + } + } + else + nLength = pPor->GetCrsrOfst( nX ); + } + } + nOffset = nCurrStart + nLength; + + // 7684: Wir sind vor der HyphPortion angelangt und muessen dafuer + // sorgen, dass wir in dem String landen. + // Bei Zeilenenden vor FlyFrms muessen ebenso behandelt werden. + + if( nOffset && pPor->GetLen() == nLength && pPor->GetPortion() && + !pPor->GetPortion()->GetLen() && pPor->GetPortion()->InHyphGrp() ) + --nOffset; + + return nOffset; +} + +/** Looks for text portions which are inside the given rectangle + + For a rectangular text selection every text portions which is inside the given + rectangle has to be put into the SwSelectionList as SwPaM + From these SwPaM the SwCursors will be created. + + @param rSelList + The container for the overlapped text portions + + @param rRect + A rectangle in document coordinates, text inside this rectangle has to be + selected. + + @return [ true, false ] + true if any overlapping text portion has been found and put into list + false if no portion overlaps, the list has been unchanged +*/ +bool SwTxtFrm::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const +{ + bool bRet = false; + // PaintArea() instead Frm() for negative indents + SwRect aTmpFrm( PaintArea() ); + if( !rRect.IsOver( aTmpFrm ) ) + return false; + if( rSelList.checkContext( this ) ) + { + SwRect aRect( aTmpFrm ); + aRect.Intersection( rRect ); + // rNode without const to create SwPaMs + SwCntntNode &rNode = const_cast<SwCntntNode&>( *GetNode() ); + SwNodeIndex aIdx( rNode ); + SwPosition aPosL( aIdx, SwIndex( &rNode, 0 ) ); + if( IsEmpty() ) + { + SwPaM *pPam = new SwPaM( aPosL, aPosL ); + rSelList.insertPaM( pPam ); + } + else if( aRect.HasArea() ) + { + xub_StrLen nOld = STRING_LEN; + SwPosition aPosR( aPosL ); + Point aPoint; + SwTxtInfo aInf( const_cast<SwTxtFrm*>(this) ); + SwTxtIter aLine( const_cast<SwTxtFrm*>(this), &aInf ); + // We have to care for top-to-bottom layout, where right becomes top etc. + SWRECTFN( this ) + SwTwips nTop = (aRect.*fnRect->fnGetTop)(); + SwTwips nBottom = (aRect.*fnRect->fnGetBottom)(); + SwTwips nLeft = (aRect.*fnRect->fnGetLeft)(); + SwTwips nRight = (aRect.*fnRect->fnGetRight)(); + SwTwips nY = aLine.Y(); // Top position of the first line + SwTwips nLastY = nY; + while( nY < nTop && aLine.Next() ) // line above rectangle + { + nLastY = nY; + nY = aLine.Y(); + } + bool bLastLine = false; + if( nY < nTop && !aLine.GetNext() ) + { + bLastLine = true; + nY += aLine.GetLineHeight(); + } + do // check the lines for overlapping + { + if( nLastY < nTop ) // if the last line was above rectangle + nLastY = nTop; + if( nY > nBottom ) // if the current line leaves the rectangle + nY = nBottom; + if( nY >= nLastY ) // gotcha: overlapping + { + nLastY += nY; + nLastY /= 2; + if( bVert ) + { + aPoint.X() = nLastY; + aPoint.Y() = nLeft; + } + else + { + aPoint.X() = nLeft; + aPoint.Y() = nLastY; + } + // Looking for the position of the left border of the rectangle + // in this text line + SwCrsrMoveState aState( MV_UPDOWN ); + if( GetCrsrOfst( &aPosL, aPoint, &aState ) ) + { + if( bVert ) + { + aPoint.X() = nLastY; + aPoint.Y() = nRight; + } + else + { + aPoint.X() = nRight; + aPoint.Y() = nLastY; + } + // If we get a right position and if the left position + // is not the same like the left position of the line before + // which cound happen e.g. for field portions or fly frames + // a SwPaM will be inserted with these positions + if( GetCrsrOfst( &aPosR, aPoint, &aState ) && + nOld != aPosL.nContent.GetIndex() ) + { + SwPaM *pPam = new SwPaM( aPosL, aPosR ); + rSelList.insertPaM( pPam ); + nOld = aPosL.nContent.GetIndex(); + } + } + } + if( aLine.Next() ) + { + nLastY = nY; + nY = aLine.Y(); + } + else if( !bLastLine ) + { + bLastLine = true; + nLastY = nY; + nY += aLine.GetLineHeight(); + } + else + break; + }while( nLastY < nBottom ); + } + } + if( GetDrawObjs() ) + { + const SwSortedObjs &rObjs = *GetDrawObjs(); + for ( USHORT i = 0; i < rObjs.Count(); ++i ) + { + const SwAnchoredObject* pAnchoredObj = rObjs[i]; + if( !pAnchoredObj->ISA(SwFlyFrm) ) + continue; + const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pAnchoredObj); + if( pFly->IsFlyInCntFrm() && pFly->FillSelection( rSelList, rRect ) ) + bRet = true; + } + } + return bRet; +} + diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx new file mode 100644 index 000000000000..cb54872d19e2 --- /dev/null +++ b/sw/source/core/text/itrform2.cxx @@ -0,0 +1,2123 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrform2.cxx,v $ + * $Revision: 1.107.20.2 $ + * + * 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 <svx/lspcitem.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> +#include <ftninfo.hxx> +#include <charfmt.hxx> +#include <svx/charrotateitem.hxx> +#include <layfrm.hxx> // GetFrmRstHeight, etc +#include <viewsh.hxx> +#include <viewopt.hxx> // SwViewOptions +#include <paratr.hxx> // SwFmtDrop +#include <txtcfg.hxx> +#include <itrform2.hxx> +#include <porrst.hxx> +#include <portab.hxx> // pLastTab-> +#include <porfly.hxx> // CalcFlyWidth +#include <portox.hxx> // WhichTxtPortion +#include <porref.hxx> // WhichTxtPortion +#include <porfld.hxx> // SwNumberPortion fuer CalcAscent() +#include <porftn.hxx> // SwFtnPortion +#include <porhyph.hxx> +#include <guess.hxx> +#include <blink.hxx> // pBlink +#include <ftnfrm.hxx> // WhichFirstPortion() -> mal Verlagern. +#include <redlnitr.hxx> // SwRedlineItr +#include <pagefrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <doc.hxx> // SwDoc +#include <pormulti.hxx> // SwMultiPortion +#define _SVSTDARR_LONGS +#include <svtools/svstdarr.hxx> +#include <unotools/charclass.hxx> + +#if OSL_DEBUG_LEVEL > 1 +#include <ndtxt.hxx> // pSwpHints, Ausgabeoperator +#endif + +using namespace ::com::sun::star; + +extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ); +bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ); + +#define MAX_TXTPORLEN 300 + +inline void ClearFly( SwTxtFormatInfo &rInf ) +{ + if( rInf.GetFly() ) + { + delete rInf.GetFly(); + rInf.SetFly(0); + } +} + +/************************************************************************* + * SwTxtFormatter::CtorInitTxtFormatter() + *************************************************************************/ + +void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf ) +{ + CtorInitTxtPainter( pNewFrm, pNewInf ); + pInf = pNewInf; + pDropFmt = GetInfo().GetDropFmt(); + pMulti = NULL; + + bOnceMore = sal_False; + bChanges = sal_False; + bTruncLines = sal_False; + nCntEndHyph = 0; + nCntMidHyph = 0; + nLeftScanIdx = STRING_LEN; + nRightScanIdx = 0; + m_nHintEndIndex = 0; + + if( nStart > GetInfo().GetTxt().Len() ) + { + ASSERT( !this, "+SwTxtFormatter::CTOR: bad offset" ); + nStart = GetInfo().GetTxt().Len(); + } + +} + +/************************************************************************* + * SwTxtFormatter::DTOR + *************************************************************************/ + +SwTxtFormatter::~SwTxtFormatter() +{ + // Auesserst unwahrscheinlich aber denkbar. + // z.B.: Feld spaltet sich auf, Widows schlagen zu + if( GetInfo().GetRest() ) + { + delete GetInfo().GetRest(); + GetInfo().SetRest(0); + } +} + +/************************************************************************* + * SwTxtFormatter::Insert() + *************************************************************************/ + +void SwTxtFormatter::Insert( SwLineLayout *pLay ) +{ + // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element. + if ( pCurr ) + { + pLay->SetNext( pCurr->GetNext() ); + pCurr->SetNext( pLay ); + } + else + pCurr = pLay; +} + +/************************************************************************* + * SwTxtFormatter::GetFrmRstHeight() + *************************************************************************/ + +KSHORT SwTxtFormatter::GetFrmRstHeight() const +{ + // 8725: Uns interessiert die Resthoehe bezogen auf die Seite. + // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht + // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn + // gerufen. + // Falsch: const SwFrm *pUpper = pFrm->GetUpper(); + const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm(); + const SwTwips nHeight = pPage->Frm().Top() + + pPage->Prt().Top() + + pPage->Prt().Height() - Y(); + if( 0 > nHeight ) + return pCurr->Height(); + else + return KSHORT( nHeight ); +} + +/************************************************************************* + * SwTxtFormatter::UnderFlow() + *************************************************************************/ + +SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf ) +{ + // Werte sichern und rInf initialisieren. + SwLinePortion *pUnderFlow = rInf.GetUnderFlow(); + if( !pUnderFlow ) + return 0; + + // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der + // naechsten Zeile durchaus noch einmal drankommen koennen. + // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt. + + const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos(); + const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos(); + + // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF + // 3983: Nicht ClearFly(rInf) ! + SwFlyPortion *pFly = rInf.GetFly(); + rInf.SetFly( 0 ); + + FeedInf( rInf ); + rInf.SetLast( pCurr ); + // pUnderFlow braucht nicht deletet werden, weil es im folgenden + // Truncate() untergehen wird. + rInf.SetUnderFlow(0); + rInf.SetSoftHyphPos( nSoftHyphPos ); + rInf.SetUnderScorePos( nUnderScorePos ); + rInf.SetPaintOfst( GetLeftMargin() ); + + // Wir suchen die Portion mit der Unterlaufposition + SwLinePortion *pPor = pCurr->GetFirstPortion(); + if( pPor != pUnderFlow ) + { + // pPrev wird die letzte Portion vor pUnderFlow, + // die noch eine echte Breite hat. + // Ausnahme: SoftHyphPortions duerfen dabei natuerlich + // nicht vergessen werden, obwohl sie keine Breite haben. + SwLinePortion *pTmpPrev = pPor; + while( pPor && pPor != pUnderFlow ) + { + DBG_LOOP; + if( !pPor->IsKernPortion() && + ( pPor->Width() || pPor->IsSoftHyphPortion() ) ) + { + while( pTmpPrev != pPor ) + { + pTmpPrev->Move( rInf ); + rInf.SetLast( pTmpPrev ); + pTmpPrev = pTmpPrev->GetPortion(); + ASSERT( pTmpPrev, "UnderFlow: Loosing control!" ); + }; + } + pPor = pPor->GetPortion(); + } + pPor = pTmpPrev; + if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen + ( pPor->IsFlyPortion() || pPor->IsDropPortion() || + pPor->IsFlyCntPortion() ) ) + { + pPor->Move( rInf ); + rInf.SetLast( pPor ); + rInf.SetStopUnderFlow( sal_True ); + pPor = pUnderFlow; + } + } + + // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ? + ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" ); + + // OD 2004-05-26 #i29529# - correction: no delete of footnotes +// if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() ) +// { +// SwLinePortion *pTmp = pPor->GetPortion(); +// while( pTmp ) +// { +// if( pTmp->IsFtnPortion() ) +// ((SwFtnPortion*)pTmp)->ClearFtn(); +// pTmp = pTmp->GetPortion(); +// } +// } + + /*-----------------14.12.94 09:45------------------- + * 9849: Schnellschuss + * --------------------------------------------------*/ + if ( pPor==rInf.GetLast() ) + { + // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich + // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber + // mehrere Zeilen geht und in der zweiten Zeile in einen Fly + // hineinlaeuft! + rInf.SetFly( pFly ); // wg. 28300 + pPor->Truncate(); + return pPor; // Reicht das? + } + /*--------------------------------------------------- + * Ende des Schnellschusses wg. 9849 + * --------------------------------------------------*/ + + // 4656: X + Width == 0 bei SoftHyph > Zeile ?! + if( !pPor || !(rInf.X() + pPor->Width()) ) + { + delete pFly; + return 0; + } + + // Vorbereitungen auf's Format() + // Wir muessen die Kette hinter pLast abknipsen, weil + // nach dem Format() ein Insert erfolgt. + SeekAndChg( rInf ); + + // line width is adjusted, so that pPor does not fit to current + // line anymore + rInf.Width( (USHORT)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) ); + rInf.SetLen( pPor->GetLen() ); + rInf.SetFull( sal_False ); + if( pFly ) + { + // Aus folgendem Grund muss die FlyPortion neu berechnet werden: + // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie + // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht, + // so hat die FlyPortion eine falsche Groesse/Fixsize. + rInf.SetFly( pFly ); + CalcFlyWidth( rInf ); + } + rInf.GetLast()->SetPortion(0); + + // Eine Ausnahme bildet das SwLineLayout, dass sich beim + // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg: + if( rInf.GetLast() == pCurr ) + { + if( pPor->InTxtGrp() && !pPor->InExpGrp() ) + { + MSHORT nOldWhich = pCurr->GetWhichPor(); + *(SwLinePortion*)pCurr = *pPor; + pCurr->SetPortion( pPor->GetPortion() ); + pCurr->SetWhichPor( nOldWhich ); + pPor->SetPortion( 0 ); + delete pPor; + pPor = pCurr; + } + } + pPor->Truncate(); + SwLinePortion *const pRest( rInf.GetRest() ); + if (pRest && pRest->InFldGrp() && + static_cast<SwFldPortion*>(pRest)->IsNoLength()) + { + // HACK: decrement again, so we pick up the suffix in next line! + --m_nHintEndIndex; + } + delete pRest; + rInf.SetRest(0); + return pPor; +} + +/************************************************************************* + * SwTxtFormatter::InsertPortion() + *************************************************************************/ + +void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf, + SwLinePortion *pPor ) const +{ + // Die neue Portion wird eingefuegt, + // bei dem LineLayout ist allerdings alles anders... + if( pPor == pCurr ) + { + if( pCurr->GetPortion() ) + pPor = pCurr->GetPortion(); + } + else + { + SwLinePortion *pLast = rInf.GetLast(); + if( pLast->GetPortion() ) + { + while( pLast->GetPortion() ) + pLast = pLast->GetPortion(); + rInf.SetLast( pLast ); + } + pLast->Insert( pPor ); + + // Maxima anpassen: + if( pCurr->Height() < pPor->Height() ) + pCurr->Height( pPor->Height() ); + if( pCurr->GetAscent() < pPor->GetAscent() ) + pCurr->SetAscent( pPor->GetAscent() ); + } + + // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate) + rInf.SetLast( pPor ); + while( pPor ) + { + DBG_LOOP; + pPor->Move( rInf ); + rInf.SetLast( pPor ); + pPor = pPor->GetPortion(); + } +} + +/************************************************************************* + * SwTxtFormatter::BuildPortion() + *************************************************************************/ + +void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf ) +{ + ASSERT( rInf.GetTxt().Len() < STRING_LEN, + "SwTxtFormatter::BuildPortions: bad text length in info" ); + + rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); + + // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet. + // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt + // wird. In CalcAscent geschieht dies automatisch. + rInf.SetLast( pCurr ); + rInf.ForcedLeftMargin( 0 ); + + ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" ); + + if( !pCurr->GetAscent() && !pCurr->Height() ) + CalcAscent( rInf, pCurr ); + + SeekAndChg( rInf ); + + // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. + ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" ); + CalcFlyWidth( rInf ); + SwFlyPortion *pFly = rInf.GetFly(); + if( pFly ) + { + if ( 0 < pFly->Fix() ) + ClearFly( rInf ); + else + rInf.SetFull(sal_True); + } + + SwLinePortion *pPor = NewPortion( rInf ); + + // Asian grid stuff + GETGRID( pFrm->FindPageFrm() ) + const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() && + GRID_LINES_CHARS == pGrid->GetGridType(); + + const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); + const USHORT nGridWidth = bHasGrid ? + GETGRIDWIDTH(pGrid,pDoc) : 0; //for textgrid refactor + + // used for grid mode only: + // the pointer is stored, because after formatting of non-asian text, + // the width of the kerning portion has to be adjusted + SwKernPortion* pGridKernPortion = 0; + + sal_Bool bFull; + SwTwips nUnderLineStart = 0; + rInf.Y( Y() ); + + while( pPor && !rInf.IsStop() ) + { + ASSERT( rInf.GetLen() < STRING_LEN && + rInf.GetIdx() <= rInf.GetTxt().Len(), + "SwTxtFormatter::BuildPortions: bad length in info" ); + DBG_LOOP; + + // We have to check the script for fields in order to set the + // correct nActual value for the font. + if( pPor->InFldGrp() ) + ((SwFldPortion*)pPor)->CheckScript( rInf ); + + if( ! bHasGrid && rInf.HasScriptSpace() && + rInf.GetLast() && rInf.GetLast()->InTxtGrp() && + rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() ) + { + BYTE nNxtActual = rInf.GetFont()->GetActual(); + BYTE nLstActual = nNxtActual; + USHORT nLstHeight = (USHORT)rInf.GetFont()->GetHeight(); + sal_Bool bAllowBefore = sal_False; + sal_Bool bAllowBehind = sal_False; + const CharClass& rCC = GetAppCharClass(); + + // are there any punctuation characters on both sides + // of the kerning portion? + if ( pPor->InFldGrp() ) + { + XubString aAltTxt; + if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) && + aAltTxt.Len() ) + { + bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 ); + + const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont(); + if ( pTmpFnt ) + nNxtActual = pTmpFnt->GetActual(); + } + } + else + bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() ); + + const SwLinePortion* pLast = rInf.GetLast(); + if ( bAllowBehind && pLast ) + { + if ( pLast->InFldGrp() ) + { + XubString aAltTxt; + if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) && + aAltTxt.Len() ) + { + bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 ); + + const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont(); + if ( pTmpFnt ) + { + nLstActual = pTmpFnt->GetActual(); + nLstHeight = (USHORT)pTmpFnt->GetHeight(); + } + } + } + else if ( rInf.GetIdx() ) + { + bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 ); + // Note: ScriptType returns values in [1,4] + if ( bAllowBefore ) + nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1; + } + + nLstHeight /= 5; + // does the kerning portion still fit into the line? + if( bAllowBefore && ( nLstActual != nNxtActual ) && + nLstHeight && rInf.X() + nLstHeight <= rInf.Width() ) + { + SwKernPortion* pKrn = + new SwKernPortion( *rInf.GetLast(), nLstHeight, + pLast->InFldGrp() && pPor->InFldGrp() ); + rInf.GetLast()->SetPortion( NULL ); + InsertPortion( rInf, pKrn ); + } + } + } + else if ( bHasGrid && ! pGridKernPortion && ! pMulti ) + { + // insert a grid kerning portion + if ( ! pGridKernPortion ) + pGridKernPortion = pPor->IsKernPortion() ? + (SwKernPortion*)pPor : + new SwKernPortion( *pCurr ); + + // if we have a new GridKernPortion, we initially calculate + // its size so that its ends on the grid + const SwPageFrm* pPageFrm = pFrm->FindPageFrm(); + const SwLayoutFrm* pBody = pPageFrm->FindBodyCont(); + SWRECTFN( pPageFrm ) + + const long nGridOrigin = pBody ? + (pBody->*fnRect->fnGetPrtLeft)() : + (pPageFrm->*fnRect->fnGetPrtLeft)(); + + SwTwips nStartX = rInf.X() + GetLeftMargin(); + if ( bVert ) + { + Point aPoint( nStartX, 0 ); + pFrm->SwitchHorizontalToVertical( aPoint ); + nStartX = aPoint.Y(); + } + + const SwTwips nOfst = nStartX - nGridOrigin; + if ( nOfst ) + { + const ULONG i = ( nOfst > 0 ) ? + ( ( nOfst - 1 ) / nGridWidth + 1 ) : + 0; + const SwTwips nKernWidth = i * nGridWidth - nOfst; + const SwTwips nRestWidth = rInf.Width() - rInf.X(); + + if ( nKernWidth <= nRestWidth ) + pGridKernPortion->Width( (USHORT)nKernWidth ); + } + + if ( pGridKernPortion != pPor ) + InsertPortion( rInf, pGridKernPortion ); + } + + // the multi-portion has it's own format function + if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) ) + bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) ); + else + bFull = pPor->Format( rInf ); + + if( rInf.IsRuby() && !rInf.GetRest() ) + bFull = sal_True; + + // if we are underlined, we store the beginning of this underlined + // segment for repaint optimization + if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart ) + nUnderLineStart = GetLeftMargin() + rInf.X(); + + if ( pPor->IsFlyPortion() ) + pCurr->SetFly( sal_True ); + // some special cases, where we have to take care for the repaint + // offset: + // 1. Underlined portions due to special underline feature + // 2. Right Tab + // 3. BidiPortions + // 4. other Multiportions + // 5. DropCaps + // 6. Grid Mode + else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) && + // 1. Underlined portions + nUnderLineStart && + // reformat is at end of an underlined portion and next portion + // is not underlined + ( ( rInf.GetReformatStart() == rInf.GetIdx() && + UNDERLINE_NONE == pFnt->GetUnderline() + ) || + // reformat is inside portion and portion is underlined + ( rInf.GetReformatStart() >= rInf.GetIdx() && + rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() && + UNDERLINE_NONE != pFnt->GetUnderline() ) ) ) + rInf.SetPaintOfst( nUnderLineStart ); + else if ( ! rInf.GetPaintOfst() && + // 2. Right Tab + ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) || + // 3. BidiPortions + ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) || + // 4. Multi Portion and 5. Drop Caps + ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) && + rInf.GetReformatStart() >= rInf.GetIdx() && + rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() ) + // 6. Grid Mode + || ( bHasGrid && SW_CJK != pFnt->GetActual() ) + ) + ) + // we store the beginning of the critical portion as our + // paint offset + rInf.SetPaintOfst( GetLeftMargin() + rInf.X() ); + + // under one of these conditions we are allowed to delete the + // start of the underline portion + if ( IsUnderlineBreak( *pPor, *pFnt ) ) + nUnderLineStart = 0; + + if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) ) + SetFlyInCntBase(); + // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim + // naechsten Softhyphen wieder umgebrochen! + if ( !bFull ) + { + rInf.ClrUnderFlow(); + if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() && + pPor->GetLen() && !pPor->InFldGrp() ) + { + // The distance between two different scripts is set + // to 20% of the fontheight. + xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen(); + if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) && + nTmp != rInf.GetTxt().Len() ) + { + USHORT nDist = (USHORT)(rInf.GetFont()->GetHeight()/5); + + if( nDist ) + { + // we do not want a kerning portion if any end + // would be a punctuation character + const CharClass& rCC = GetAppCharClass(); + if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) && + rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) ) + { + // does the kerning portion still fit into the line? + if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() ) + new SwKernPortion( *pPor, nDist ); + else + bFull = sal_True; + } + } + } + } + } + + if ( bHasGrid && pPor != pGridKernPortion && ! pMulti ) + { + xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen(); + const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width(); + + const BYTE nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() ); + const BYTE nNextScript = nTmp >= rInf.GetTxt().Len() ? + SW_CJK : + SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo ); + + // snap non-asian text to grid if next portion is ASIAN or + // there are no more portions in this line + // be careful when handling an underflow event: the gridkernportion + // could have been deleted + if ( nRestWidth > 0 && SW_CJK != nCurrScript && + ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) ) + { + ASSERT( pGridKernPortion, "No GridKernPortion available" ) + + // calculate size + SwLinePortion* pTmpPor = pGridKernPortion->GetPortion(); + USHORT nSumWidth = pPor->Width(); + while ( pTmpPor ) + { + nSumWidth = nSumWidth + pTmpPor->Width(); + pTmpPor = pTmpPor->GetPortion(); + } + + const USHORT i = nSumWidth ? + ( nSumWidth - 1 ) / nGridWidth + 1 : + 0; + const SwTwips nTmpWidth = i * nGridWidth; + const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth), + nRestWidth ); + const USHORT nKernWidth_1 = (USHORT)(nKernWidth / 2); + + ASSERT( nKernWidth <= nRestWidth, + "Not enough space left for adjusting non-asian text in grid mode" ) + + pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 ); + rInf.X( rInf.X() + nKernWidth_1 ); + + if ( ! bFull ) + new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1), + sal_False, sal_True ); + + pGridKernPortion = 0; + } + else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() || + pPor->IsFlyCntPortion() || pPor->InNumberGrp() || + pPor->InFldGrp() || nCurrScript != nNextScript ) + // next portion should snap to grid + pGridKernPortion = 0; + } + + rInf.SetFull( bFull ); + + // Restportions von mehrzeiligen Feldern haben bisher noch + // nicht den richtigen Ascent. + if ( !pPor->GetLen() && !pPor->IsFlyPortion() + && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp() + && !pPor->IsMultiPortion() ) + CalcAscent( rInf, pPor ); + + InsertPortion( rInf, pPor ); + pPor = NewPortion( rInf ); + } + + if( !rInf.IsStop() ) + { + // der letzte rechte, zentrierte, dezimale Tab + SwTabPortion *pLastTab = rInf.GetLastTab(); + if( pLastTab ) + pLastTab->FormatEOL( rInf ); + else if( rInf.GetLast() && rInf.LastKernPortion() ) + rInf.GetLast()->FormatEOL( rInf ); + } + if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp() + && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() ) + rInf.SetNumDone( sal_False ); + + // 3260, 3860: Fly auf jeden Fall loeschen! + ClearFly( rInf ); +} + +/************************************************************************* + * SwTxtFormatter::CalcAdjustLine() + *************************************************************************/ + +void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent ) +{ + if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti) + { + pCurrent->SetFormatAdj(sal_True); + if( IsFlyInCntBase() ) + { + CalcAdjLine( pCurrent ); + // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint + // auf jeden Fall umsetzen, deshalb bAllWays = sal_True + UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True ); + } + } +} + +/************************************************************************* + * SwTxtFormatter::CalcAscent() + *************************************************************************/ + +void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor ) +{ + if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() ) + { + // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten, + // dann ist ihre Groesse unabhaengig von harten Attributierungen. + SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt; + SwFontSave aSave( rInf, pFldFnt ); + ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) ); + ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) ); + } + // --> OD 2008-06-05 #i89179# + // tab portion representing the list tab of a list label gets the + // same height and ascent as the corresponding number portion + else if ( pPor->InTabGrp() && pPor->GetLen() == 0 && + rInf.GetLast() && rInf.GetLast()->InNumberGrp() && + static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() ) + { + const SwLinePortion* pLast = rInf.GetLast(); + pPor->Height( pLast->Height() ); + pPor->SetAscent( pLast->GetAscent() ); + } + // <-- + else + { + const SwLinePortion *pLast = rInf.GetLast(); + sal_Bool bChg; + + // Fallunterscheidung: in leeren Zeilen werden die Attribute + // per SeekStart angeschaltet. + const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx(); + if ( pPor->IsQuoVadisPortion() ) + bChg = SeekStartAndChg( rInf, sal_True ); + else + { + if( bFirstPor ) + { + if( rInf.GetTxt().Len() ) + { + if ( pPor->GetLen() || !rInf.GetIdx() + || ( pCurr != pLast && !pLast->IsFlyPortion() ) + || !pCurr->IsRest() ) // statt !rInf.GetRest() + bChg = SeekAndChg( rInf ); + else + bChg = SeekAndChgBefore( rInf ); + } + else if ( pMulti ) + // do not open attributes starting at 0 in empty multi + // portions (rotated numbering followed by a footnote + // can cause trouble, because the footnote attribute + // starts at 0, but if we open it, the attribute handler + // cannot handle it. + bChg = sal_False; + else + bChg = SeekStartAndChg( rInf ); + } + else + bChg = SeekAndChg( rInf ); + } + if( bChg || bFirstPor || !pPor->GetAscent() + || !rInf.GetLast()->InTxtGrp() ) + { + pPor->SetAscent( rInf.GetAscent() ); + pPor->Height( rInf.GetTxtHeight() ); + } + else + { + pPor->Height( pLast->Height() ); + pPor->SetAscent( pLast->GetAscent() ); + } + } +} + +/************************************************************************* + * class SwMetaPortion + *************************************************************************/ + +class SwMetaPortion : public SwTxtPortion +{ +public: + inline SwMetaPortion() { SetWhichPor( POR_META ); } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; +// OUTPUT_OPERATOR +}; + +//CLASSIO( SwMetaPortion ) + +/************************************************************************* + * virtual SwMetaPortion::Paint() + *************************************************************************/ + +void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if ( Width() ) + { + rInf.DrawViewOpt( *this, POR_META ); + SwTxtPortion::Paint( rInf ); + } +} + + +/************************************************************************* + * SwTxtFormatter::WhichTxtPor() + *************************************************************************/ + +SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const +{ + SwTxtPortion *pPor = 0; + if( GetFnt()->IsTox() ) + pPor = new SwToxPortion; + else + { + if( GetFnt()->IsRef() ) + pPor = new SwRefPortion; + else if (GetFnt()->IsMeta()) + { + pPor = new SwMetaPortion; + } + else + { + // Erst zum Schluss ! + // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben, + // z.B. bei nicht darstellbaren Zeichen. + if( rInf.GetLen() > 0 ) + { + if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART ) + pPor = new SwFieldMarkPortion(); + else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND ) + pPor = new SwFieldMarkPortion(); + else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT ) + pPor = new SwFieldFormPortion(); + } + if( !pPor ) + { + if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() ) + pPor = pCurr; + else + { + pPor = new SwTxtPortion; + if( GetFnt()->IsURL() ) + pPor->SetWhichPor( POR_URL ); + } + } + } + } + return pPor; +} + +/************************************************************************* + * SwTxtFormatter::NewTxtPortion() + *************************************************************************/ +// Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert: +// 1) Tabs +// 2) Linebreaks +// 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD +// 4) naechster Attributwechsel + +SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf ) +{ + // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr + // Wenn pCurr nicht von SwTxtPortion abgeleitet ist, + // muessen wir duplizieren ... + Seek( rInf.GetIdx() ); + SwTxtPortion *pPor = WhichTxtPor( rInf ); + + // until next attribute change: + const xub_StrLen nNextAttr = GetNextAttr(); + xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() ); + + // end of script type: + const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() ); + nNextChg = Min( nNextChg, nNextScript ); + + // end of direction: + const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() ); + nNextChg = Min( nNextChg, nNextDir ); + + // 7515, 7516, 3470, 6441 : Turbo-Boost + // Es wird unterstellt, dass die Buchstaben eines Fonts nicht + // groesser als doppelt so breit wie hoch sind. + // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen. + // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe + // ergibt sich erst im CalcAscent! + // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times + // New Roman besitzt einen Ascent von 182, eine Hoehe von 200 + // und eine Breite von 53! Daraus folgt, dass eine Zeile mit + // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von + // Faktor 2 auf 8 (wg. negativen Kernings). + + pPor->SetLen(1); + CalcAscent( rInf, pPor ); + + const SwFont* pTmpFnt = rInf.GetFont(); + KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ), + KSHORT( pPor->GetAscent() ) ) / 8; + if ( !nExpect ) + nExpect = 1; + nExpect = (USHORT)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect)); + if( nExpect > rInf.GetIdx() && nNextChg > nExpect ) + nNextChg = Min( nExpect, rInf.GetTxt().Len() ); + + // we keep an invariant during method calls: + // there are no portion ending characters like hard spaces + // or tabs in [ nLeftScanIdx, nRightScanIdx ] + if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx ) + { + if ( nNextChg > nRightScanIdx ) + nNextChg = nRightScanIdx = + rInf.ScanPortionEnd( nRightScanIdx, nNextChg ); + } + else + { + nLeftScanIdx = rInf.GetIdx(); + nNextChg = nRightScanIdx = + rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg ); + } + + pPor->SetLen( nNextChg - rInf.GetIdx() ); + rInf.SetLen( pPor->GetLen() ); + return pPor; +} + + +/************************************************************************* + * SwTxtFormatter::WhichFirstPortion() + *************************************************************************/ + +SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf) +{ + SwLinePortion *pPor = 0; + + if( rInf.GetRest() ) + { + // 5010: Tabs und Felder + if( '\0' != rInf.GetHookChar() ) + return 0; + + pPor = rInf.GetRest(); + if( pPor->IsErgoSumPortion() ) + rInf.SetErgoDone(sal_True); + else + if( pPor->IsFtnNumPortion() ) + rInf.SetFtnDone(sal_True); + else + if( pPor->InNumberGrp() ) + rInf.SetNumDone(sal_True); + if( pPor ) + { + rInf.SetRest(0); + pCurr->SetRest( sal_True ); + return pPor; + } + } + + // ???? und ????: im Follow duerfen wir schon stehen, + // entscheidend ist, ob pFrm->GetOfst() == 0 ist! + if( rInf.GetIdx() ) + { + // Nun koennen auch FtnPortions und ErgoSumPortions + // verlaengert werden. + + // 1) Die ErgoSumTexte + if( !rInf.IsErgoDone() ) + { + if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) + pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); + rInf.SetErgoDone( sal_True ); + } + + // 2) Arrow portions + if( !pPor && !rInf.IsArrowDone() ) + { + if( pFrm->GetOfst() && !pFrm->IsFollow() && + rInf.GetIdx() == pFrm->GetOfst() ) + pPor = new SwArrowPortion( *pCurr ); + rInf.SetArrowDone( sal_True ); + } + + // 3) Kerning portions at beginning of line in grid mode + if ( ! pPor && ! pCurr->GetPortion() ) + { + GETGRID( GetTxtFrm()->FindPageFrm() ) + if ( pGrid ) + pPor = new SwKernPortion( *pCurr ); + } + + // 4) Die Zeilenreste (mehrzeilige Felder) + if( !pPor ) + { + pPor = rInf.GetRest(); + // 6922: Nur bei pPor natuerlich. + if( pPor ) + { + pCurr->SetRest( sal_True ); + rInf.SetRest(0); + } + } + } + else + { + // 5) Die Fussnotenzahlen + if( !rInf.IsFtnDone() ) + { + ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), + "Rotated number portion trouble" ) + + sal_Bool bFtnNum = pFrm->IsFtnNumFrm(); + rInf.GetParaPortion()->SetFtnNum( bFtnNum ); + if( bFtnNum ) + pPor = (SwLinePortion*)NewFtnNumPortion( rInf ); + rInf.SetFtnDone( sal_True ); + } + + // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster, + // entscheidend ist, ob der SwFtnFrm ein Follow ist. + if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() ) + { + if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) + pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); + rInf.SetErgoDone( sal_True ); + } + + // 7) Die Numerierungen + if( !rInf.IsNumDone() && !pPor ) + { + ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), + "Rotated number portion trouble" ) + + // Wenn wir im Follow stehen, dann natuerlich nicht. + if( GetTxtFrm()->GetTxtNode()->GetNumRule() ) + pPor = (SwLinePortion*)NewNumberPortion( rInf ); + rInf.SetNumDone( sal_True ); + } + // 8) Die DropCaps + if( !pPor && GetDropFmt() && ! rInf.IsMulti() ) + pPor = (SwLinePortion*)NewDropPortion( rInf ); + + // 9) Kerning portions at beginning of line in grid mode + if ( !pPor && !pCurr->GetPortion() ) + { + GETGRID( GetTxtFrm()->FindPageFrm() ) + if ( pGrid ) + pPor = new SwKernPortion( *pCurr ); + } + } + + // 10) Decimal tab portion at the beginning of each line in table cells + if ( !pPor && !pCurr->GetPortion() && + GetTxtFrm()->IsInTab() && + GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) ) + { + pPor = NewTabPortion( rInf, true ); + } + + // 11) suffix of meta-field + if (!pPor) + { + pPor = TryNewNoLengthPortion(rInf); + } + + return pPor; +} + +sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr ) +{ + if( !pCurr->GetNext() ) + return sal_False; + const SwLinePortion *pPor = pCurr->GetNext()->GetPortion(); + sal_Bool bRet = sal_False; + while( pPor && !bRet ) + { + bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) || + (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld()); + if( !pPor->GetLen() ) + break; + pPor = pPor->GetPortion(); + } + return bRet; +} + +/************************************************************************* + * SwTxtFormatter::NewPortion() + *************************************************************************/ + +/* NewPortion stellt rInf.nLen ein. + * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr, + * attrwechsel. + * Drei Faelle koennen eintreten: + * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert + * -> return 0; + * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert + * -> Breite neu einstellen und return new FlyPortion + * 3) Es muss eine neue Portion gebaut werden. + * -> CalcFlyWidth emuliert ggf. die Breite und return Portion + */ + +SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf ) +{ + // Underflow hat Vorrang + rInf.SetStopUnderFlow( sal_False ); + if( rInf.GetUnderFlow() ) + { + ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" ); + return UnderFlow( rInf ); + } + + // Wenn die Zeile voll ist, koennten noch Flys oder + // UnderFlow-LinePortions warten ... + if( rInf.IsFull() ) + { + // ????: LineBreaks und Flys (bug05.sdw) + // 8450: IsDummy() + if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) ) + return 0; + + // Wenn der Text an den Fly gestossen ist, oder wenn + // der Fly als erstes drankommt, weil er ueber dem linken + // Rand haengt, wird GetFly() returnt. + // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's + // naturgemaesz eine 0. + if( rInf.GetFly() ) + { + if( rInf.GetLast()->IsBreakPortion() ) + { + delete rInf.GetFly(); + rInf.SetFly( 0 ); + } + + return rInf.GetFly(); + } + // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den + // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest + // bekanntgeben, damit SwTxtFrm::Format nicht abbricht + // (die Textmasse wurde ja durchformatiert). + if( rInf.GetRest() ) + rInf.SetNewLine( sal_True ); + else + { + // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt, + // jetzt aber kein Rest mehr anliegt, + // muss sie auf jeden Fall neu formatiert werden! + if( lcl_OldFieldRest( GetCurr() ) ) + rInf.SetNewLine( sal_True ); + else + { + SwLinePortion *pFirst = WhichFirstPortion( rInf ); + if( pFirst ) + { + rInf.SetNewLine( sal_True ); + if( pFirst->InNumberGrp() ) + rInf.SetNumDone( sal_False) ; + delete pFirst; + } + } + } + + return 0; + } + + SwLinePortion *pPor = WhichFirstPortion( rInf ); + + // Check for Hidden Portion: + if ( !pPor ) + { + xub_StrLen nEnd = rInf.GetIdx(); + if ( lcl_BuildHiddenPortion( rInf, nEnd ) ) + pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() ); + } + + if( !pPor ) + { + if( ( !pMulti || pMulti->IsBidi() ) && + // --> FME 2005-02-14 #i42734# + // No multi portion if there is a hook character waiting: + ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) ) + // <-- + { + // We open a multiportion part, if we enter a multi-line part + // of the paragraph. + xub_StrLen nEnd = rInf.GetIdx(); + SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti ); + if( pCreate ) + { + SwMultiPortion* pTmp = NULL; + + if ( SW_MC_BIDI == pCreate->nId ) + pTmp = new SwBidiPortion( nEnd, pCreate->nLevel ); + else if ( SW_MC_RUBY == pCreate->nId ) + { + Seek( rInf.GetIdx() ); + sal_Bool bRubyTop; + sal_Bool* pRubyPos = 0; + + if ( rInf.SnapToGrid() ) + { + GETGRID( GetTxtFrm()->FindPageFrm() ) + if ( pGrid ) + { + bRubyTop = ! pGrid->GetRubyTextBelow(); + pRubyPos = &bRubyTop; + } + } + + pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(), + *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(), + nEnd, 0, pRubyPos ); + } + else if( SW_MC_ROTATE == pCreate->nId ) + pTmp = new SwRotatedPortion( *pCreate, nEnd, + GetTxtFrm()->IsRightToLeft() ); + else + pTmp = new SwDoubleLinePortion( *pCreate, nEnd ); + + delete pCreate; + CalcFlyWidth( rInf ); + + return pTmp; + } + } + // 5010: Tabs und Felder + xub_Unicode cChar = rInf.GetHookChar(); + + if( cChar ) + { + /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das + * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile + * gewandert ist ( so geschehen hinter Rahmen ). + * Wenn allerdings eine FldPortion im Rest wartet, muessen wir + * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei + * DezimalTabs und Feldern (22615) + */ + if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() ) + cChar = rInf.GetChar( rInf.GetIdx() ); + rInf.ClearHookChar(); + } + else + { + if( rInf.GetIdx() >= rInf.GetTxt().Len() ) + { + rInf.SetFull(sal_True); + CalcFlyWidth( rInf ); + return pPor; + } + cChar = rInf.GetChar( rInf.GetIdx() ); + } + + switch( cChar ) + { + case CH_TAB: + pPor = NewTabPortion( rInf, false ); break; + + case CH_BREAK: + pPor = new SwBreakPortion( *rInf.GetLast() ); break; + + case CHAR_SOFTHYPHEN: // soft hyphen + pPor = new SwSoftHyphPortion; break; + + case CHAR_HARDBLANK: // no-break space + pPor = new SwBlankPortion( ' ' ); break; + + case CHAR_HARDHYPHEN: // non-breaking hyphen + pPor = new SwBlankPortion( '-' ); break; + + case CHAR_ZWSP: // zero width space + case CHAR_ZWNBSP : // word joiner +// case CHAR_RLM : // right to left mark +// case CHAR_LRM : // left to right mark + pPor = new SwControlCharPortion( cChar ); break; + + case CH_TXTATR_BREAKWORD: + case CH_TXTATR_INWORD: + if( rInf.HasHint( rInf.GetIdx() ) ) + { + pPor = NewExtraPortion( rInf ); + break; + } + // No break + default : + { + SwTabPortion* pLastTabPortion = rInf.GetLastTab(); + if ( pLastTabPortion && cChar == rInf.GetTabDecimal() ) + { + // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full: + // We have a decimal tab portion in the line and the next character has to be + // aligned at the tab stop position. We store the width from the beginning of + // the tab stop portion up to the portion containint the decimal separator: + if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ && + POR_TABDECIMAL == pLastTabPortion->GetWhichPor() ) + { + ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" ) + const USHORT nWidthOfPortionsUpToDecimalPosition = (USHORT)(rInf.X() - pLastTabPortion->Fix() ); + static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition ); + rInf.SetTabDecimal( 0 ); + } + // <-- + else + rInf.SetFull( rInf.GetLastTab()->Format( rInf ) ); + } + + if( rInf.GetRest() ) + { + if( rInf.IsFull() ) + { + rInf.SetNewLine(sal_True); + return 0; + } + pPor = rInf.GetRest(); + rInf.SetRest(0); + } + else + { + if( rInf.IsFull() ) + return 0; + pPor = NewTxtPortion( rInf ); + } + break; + } + } + + // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht, + // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet + // hat, weil z.B. ein Tab enthalten ist. + if( pPor && rInf.GetRest() ) + pPor->SetLen( 0 ); + + // robust: + if( !pPor || rInf.IsStop() ) + { + delete pPor; + return 0; + } + } + + // Special portions containing numbers (footnote anchor, footnote number, + // numbering) can be contained in a rotated portion, if the user + // choose a rotated character attribute. + if ( pPor && ! pMulti ) + { + if ( pPor->IsFtnPortion() ) + { + const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn(); + + if ( pTxtFtn ) + { + SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn(); + const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); + const SwEndNoteInfo* pInfo; + if( rFtn.IsEndNote() ) + pInfo = &pDoc->GetEndNoteInfo(); + else + pInfo = &pDoc->GetFtnInfo(); + const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet(); + + const SfxPoolItem* pItem; + USHORT nDir = 0; + if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE, + sal_True, &pItem )) + nDir = ((SvxCharRotateItem*)pItem)->GetValue(); + + if ( 0 != nDir ) + { + delete pPor; + pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ? + DIR_BOTTOM2TOP : + DIR_TOP2BOTTOM ); + } + } + } + else if ( pPor->InNumberGrp() ) + { + const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont(); + + if ( pNumFnt ) + { + USHORT nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); + if ( 0 != nDir ) + { + delete pPor; + pPor = new SwRotatedPortion( 0, 900 == nDir ? + DIR_BOTTOM2TOP : + DIR_TOP2BOTTOM ); + + rInf.SetNumDone( sal_False ); + rInf.SetFtnDone( sal_False ); + } + } + } + } + + // Der Font wird im Outputdevice eingestellt, + // der Ascent und die Hoehe werden berechnet. + if( !pPor->GetAscent() && !pPor->Height() ) + CalcAscent( rInf, pPor ); + rInf.SetLen( pPor->GetLen() ); + + // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. + CalcFlyWidth( rInf ); + + // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige + // Werte bereithalten muss: + if( !pCurr->Height() ) + { + ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" ); + pCurr->Height( pPor->Height() ); + pCurr->SetAscent( pPor->GetAscent() ); + } + + ASSERT( !pPor || pPor->Height(), + "SwTxtFormatter::NewPortion: something went wrong"); + if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() ) + { + delete pPor; + pPor = rInf.GetFly(); + } + return pPor; +} + +/************************************************************************* + * SwTxtFormatter::FormatLine() + *************************************************************************/ + +xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos ) +{ + ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), + "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" ); + + // For the formatting routines, we set pOut to the reference device. + SwHookOut aHook( GetInfo() ); + if( GetInfo().GetLen() < GetInfo().GetTxt().Len() ) + GetInfo().SetLen( GetInfo().GetTxt().Len() ); + + sal_Bool bBuild = sal_True; + SetFlyInCntBase( sal_False ); + GetInfo().SetLineHeight( 0 ); + GetInfo().SetLineNettoHeight( 0 ); + + // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden + // und auch bei geaendertem Ascent (Absenken der Grundlinie). + const KSHORT nOldHeight = pCurr->Height(); + const KSHORT nOldAscent = pCurr->GetAscent(); + + pCurr->SetEndHyph( sal_False ); + pCurr->SetMidHyph( sal_False ); + + // fly positioning can make it necessary format a line several times + // for this, we have to keep a copy of our rest portion + SwLinePortion* pFld = GetInfo().GetRest(); + SwFldPortion* pSaveFld = 0; + + if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() ) + pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) ); + + // for an optimal repaint rectangle, we want to compare fly portions + // before and after the BuildPortions call + const sal_Bool bOptimizeRepaint = AllowRepaintOpt(); + const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen(); + SvLongs* pFlyStart = 0; + + // these are the conditions for a fly position comparison + if ( bOptimizeRepaint && pCurr->IsFly() ) + { + pFlyStart = new SvLongs; + SwLinePortion* pPor = pCurr->GetFirstPortion(); + long nPOfst = 0; + USHORT nCnt = 0; + + while ( pPor ) + { + if ( pPor->IsFlyPortion() ) + // insert start value of fly portion + pFlyStart->Insert( nPOfst, nCnt++ ); + + nPOfst += pPor->Width(); + pPor = pPor->GetPortion(); + } + } + + // Hier folgt bald die Unterlaufpruefung. + while( bBuild ) + { + GetInfo().SetFtnInside( sal_False ); + + // These values must not be reset by FormatReset(); + sal_Bool bOldNumDone = GetInfo().IsNumDone(); + sal_Bool bOldArrowDone = GetInfo().IsArrowDone(); + sal_Bool bOldErgoDone = GetInfo().IsErgoDone(); + + // besides other things, this sets the repaint offset to 0 + FormatReset( GetInfo() ); + + GetInfo().SetNumDone( bOldNumDone ); + GetInfo().SetArrowDone( bOldArrowDone ); + GetInfo().SetErgoDone( bOldErgoDone ); + + // build new portions for this line + BuildPortions( GetInfo() ); + + if( GetInfo().IsStop() ) + { + pCurr->SetLen( 0 ); + pCurr->Height( GetFrmRstHeight() + 1 ); + pCurr->SetRealHeight( GetFrmRstHeight() + 1 ); + pCurr->Width(0); + pCurr->Truncate(); + return nStartPos; + } + else if( GetInfo().IsDropInit() ) + { + DropInit(); + GetInfo().SetDropInit( sal_False ); + } + + pCurr->CalcLine( *this, GetInfo() ); + CalcRealHeight( GetInfo().IsNewLine() ); + + if ( IsFlyInCntBase() && !IsQuick() ) + { + KSHORT nTmpAscent, nTmpHeight; + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + AlignFlyInCntBase( Y() + long( nTmpAscent ) ); + pCurr->CalcLine( *this, GetInfo() ); + CalcRealHeight(); + } + + // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird + if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() ) + { + pCurr->SetRealHeight( GetInfo().GetLineHeight() ); + bBuild = sal_False; + } + else + { + bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow( GetInfo() ) + || GetInfo().CheckFtnPortion( pCurr ) ); + if( bBuild ) + { + GetInfo().SetNumDone( bOldNumDone ); + GetInfo().ResetMaxWidthDiff(); + + // delete old rest + if ( GetInfo().GetRest() ) + { + delete GetInfo().GetRest(); + GetInfo().SetRest( 0 ); + } + + // set original rest portion + if ( pSaveFld ) + GetInfo().SetRest( new SwFldPortion( *pSaveFld ) ); + + pCurr->SetLen( 0 ); + pCurr->Width(0); + pCurr->Truncate(); + } + } + } + + // calculate optimal repaint rectangle + if ( bOptimizeRepaint ) + { + GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) ); + if ( pFlyStart ) + delete pFlyStart; + } + else + // Special case: We do not allow an optimitation of the repaint + // area, but during formatting the repaint offset is set to indicate + // a maximum value for the offset. This value has to be reset: + GetInfo().SetPaintOfst( 0 ); + + // This corrects the start of the reformat range if something has + // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt + // will give us a wrong result if we have to reformat another line + GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() ); + + // delete master copy of rest portion + if ( pSaveFld ) + delete pSaveFld; + + xub_StrLen nNewStart = nStartPos + pCurr->GetLen(); + + // adjust text if kana compression is enabled + if ( GetInfo().CompressLine() ) + { + SwTwips nRepaintOfst = CalcKanaAdj( pCurr ); + + // adjust repaint offset + if ( nRepaintOfst < GetInfo().GetPaintOfst() ) + GetInfo().SetPaintOfst( nRepaintOfst ); + } + + CalcAdjustLine( pCurr ); + + if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() ) + { + SetFlyInCntBase(); + GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling + // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind + // auch formatiert werden. + GetInfo().SetShift( sal_True ); + } + + if ( IsFlyInCntBase() && !IsQuick() ) + UpdatePos( pCurr, GetTopLeft(), GetStart() ); + + return nNewStart; +} + +/************************************************************************* + * SwTxtFormatter::RecalcRealHeight() + *************************************************************************/ + +void SwTxtFormatter::RecalcRealHeight() +{ + sal_Bool bMore = sal_True; + while(bMore) + { + DBG_LOOP; + CalcRealHeight(); + bMore = Next() != 0; + } +} + +/************************************************************************* + * SwTxtFormatter::CalcRealHeight() + *************************************************************************/ + +void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine ) +{ + KSHORT nLineHeight = pCurr->Height(); + pCurr->SetClipping( sal_False ); + + GETGRID( pFrm->FindPageFrm() ) + if ( pGrid && GetInfo().SnapToGrid() ) + { + const USHORT nGridWidth = pGrid->GetBaseHeight(); + const USHORT nRubyHeight = pGrid->GetRubyHeight(); + const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow(); + + nLineHeight = nGridWidth + nRubyHeight; + USHORT nLineDist = nLineHeight; + + while ( pCurr->Height() > nLineHeight ) + nLineHeight = nLineHeight + nLineDist; + + KSHORT nAsc = pCurr->GetAscent() + + ( bRubyTop ? + ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 : + ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 ); + + pCurr->Height( nLineHeight ); + pCurr->SetAscent( nAsc ); + pInf->GetParaPortion()->SetFixLineHeight(); + + // we ignore any line spacing options except from ... + const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing(); + if ( ! IsParaLine() && pSpace && + SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() ) + { + ULONG nTmp = pSpace->GetPropLineSpace(); + + if( nTmp < 100 ) + nTmp = 100; + + nTmp *= nLineHeight; + nLineHeight = (USHORT)(nTmp / 100); + } + + pCurr->SetRealHeight( nLineHeight ); + return; + } + + // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese + // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere + // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem + // Shift-Return), die das Register durchaus beachten soll. + if( !pCurr->IsDummy() || ( !pCurr->GetNext() && + GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) ) + { + const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing(); + if( pSpace ) + { + switch( pSpace->GetLineSpaceRule() ) + { + case SVX_LINE_SPACE_AUTO: + break; + case SVX_LINE_SPACE_MIN: + { + if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) ) + nLineHeight = pSpace->GetLineHeight(); + break; + } + case SVX_LINE_SPACE_FIX: + { + nLineHeight = pSpace->GetLineHeight(); + KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80% + if( nAsc < pCurr->GetAscent() || + nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() ) + pCurr->SetClipping( sal_True ); + pCurr->Height( nLineHeight ); + pCurr->SetAscent( nAsc ); + pInf->GetParaPortion()->SetFixLineHeight(); + } + break; + default: ASSERT( sal_False, ": unknown LineSpaceRule" ); + } + if( !IsParaLine() ) + switch( pSpace->GetInterLineSpaceRule() ) + { + case SVX_INTER_LINE_SPACE_OFF: + break; + case SVX_INTER_LINE_SPACE_PROP: + { + long nTmp = pSpace->GetPropLineSpace(); + // 50% ist das Minimum, bei 0% schalten wir auf + // den Defaultwert 100% um ... + if( nTmp < 50 ) + nTmp = nTmp ? 50 : 100; + + nTmp *= nLineHeight; + nTmp /= 100; + if( !nTmp ) + ++nTmp; + nLineHeight = (KSHORT)nTmp; + break; + } + case SVX_INTER_LINE_SPACE_FIX: + { + nLineHeight = nLineHeight + pSpace->GetInterLineSpace(); + break; + } + default: ASSERT( sal_False, ": unknown InterLineSpaceRule" ); + } + } +#if OSL_DEBUG_LEVEL > 1 + KSHORT nDummy = nLineHeight + 1; + (void)nDummy; +#endif + + if( IsRegisterOn() ) + { + SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height(); + SWRECTFN( pFrm ) + if ( bVert ) + nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY ); + nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() ); + KSHORT nDiff = KSHORT( nTmpY % RegDiff() ); + if( nDiff ) + nLineHeight += RegDiff() - nDiff; + } + } + pCurr->SetRealHeight( nLineHeight ); +} + +/************************************************************************* + * SwTxtFormatter::FeedInf() + *************************************************************************/ + +void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const +{ + // 3260, 3860: Fly auf jeden Fall loeschen! + ClearFly( rInf ); + rInf.Init(); + + rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); + rInf.SetRoot( pCurr ); + rInf.SetLineStart( nStart ); + rInf.SetIdx( nStart ); + + // Handle overflows: + // --> FME 2004-11-25 #i34348# Changed type from USHORT to SwTwips + SwTwips nTmpLeft = Left(); + SwTwips nTmpRight = Right(); + SwTwips nTmpFirst = FirstLeft(); + // <-- + + if ( nTmpLeft > USHRT_MAX || + nTmpRight > USHRT_MAX || + nTmpFirst > USHRT_MAX ) + { + SWRECTFN( rInf.GetTxtFrm() ) + nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)(); + nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)(); + nTmpFirst = nTmpLeft; + } + + rInf.Left( nTmpLeft ); + rInf.Right( nTmpRight ); + rInf.First( nTmpFirst ); + + rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) ); + rInf.Width( rInf.RealWidth() ); + if( ((SwTxtFormatter*)this)->GetRedln() ) + { + ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() ); + ((SwTxtFormatter*)this)->GetRedln()->Reset(); + } +} + +/************************************************************************* + * SwTxtFormatter::FormatReset() + *************************************************************************/ + +void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf ) +{ + pCurr->Truncate(); + pCurr->Init(); + if( pBlink && pCurr->IsBlinking() ) + pBlink->Delete( pCurr ); + + // delete pSpaceAdd und pKanaComp + pCurr->FinishSpaceAdd(); + pCurr->FinishKanaComp(); + pCurr->ResetFlags(); + FeedInf( rInf ); +} + +/************************************************************************* + * SwTxtFormatter::CalcOnceMore() + *************************************************************************/ + +sal_Bool SwTxtFormatter::CalcOnceMore() +{ + if( pDropFmt ) + { + const KSHORT nOldDrop = GetDropHeight(); + CalcDropHeight( pDropFmt->GetLines() ); + bOnceMore = nOldDrop != GetDropHeight(); + } + else + bOnceMore = sal_False; + return bOnceMore; +} + +/************************************************************************* + * SwTxtFormatter::CalcBottomLine() + *************************************************************************/ + +SwTwips SwTxtFormatter::CalcBottomLine() const +{ + SwTwips nRet = Y() + GetLineHeight(); + SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom(); + if( nMin && ++nMin > nRet ) + { + SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height() + - pFrm->Prt().Top(); + if( nRet + nDist < nMin ) + { + sal_Bool bRepaint = HasTruncLines() && + GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1; + nRet = nMin - nDist; + if( bRepaint ) + { + ((SwRepaint*)GetInfo().GetParaPortion() + ->GetRepaint())->Bottom( nRet-1 ); + ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 ); + } + } + } + return nRet; +} + +/************************************************************************* + * SwTxtFormatter::_CalcFitToContent() + * + * FME/OD: This routine does a limited text formatting. + *************************************************************************/ + +SwTwips SwTxtFormatter::_CalcFitToContent() +{ + FormatReset( GetInfo() ); + BuildPortions( GetInfo() ); + pCurr->CalcLine( *this, GetInfo() ); + return pCurr->Width(); +} + +/************************************************************************* + * SwTxtFormatter::AllowRepaintOpt() + * + * determines if the calculation of a repaint offset is allowed + * otherwise each line is painted from 0 (this is a copy of the beginning + * of the former SwTxtFormatter::Recycle() function + *************************************************************************/ +sal_Bool SwTxtFormatter::AllowRepaintOpt() const +{ + // reformat position in front of current line? Only in this case + // we want to set the repaint offset + sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() && + pCurr->GetLen(); + + // a special case is the last line of a block adjusted paragraph: + if ( bOptimizeRepaint ) + { + switch( GetAdjust() ) + { + case SVX_ADJUST_BLOCK: + { + if( IsLastBlock() || IsLastCenter() ) + bOptimizeRepaint = sal_False; + else + { + // ????: Blank in der letzten Masterzeile (blocksat.sdw) + bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow(); + if ( bOptimizeRepaint ) + { + SwLinePortion *pPos = pCurr->GetFirstPortion(); + while ( pPos && !pPos->IsFlyPortion() ) + pPos = pPos->GetPortion(); + bOptimizeRepaint = !pPos; + } + } + break; + } + case SVX_ADJUST_CENTER: + case SVX_ADJUST_RIGHT: + bOptimizeRepaint = sal_False; + break; + default: ; + } + } + + // Schon wieder ein Sonderfall: unsichtbare SoftHyphs + const xub_StrLen nReformat = GetInfo().GetReformatStart(); + if( bOptimizeRepaint && STRING_LEN != nReformat ) + { + const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat ); + bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh ) + || ! GetInfo().HasHint( nReformat ); + } + + return bOptimizeRepaint; +} + +/************************************************************************* + * SwTxtFormatter::CalcOptRepaint() + * + * calculates an optimal repaint offset for the current line + *************************************************************************/ +long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd, + const SvLongs* pFlyStart ) +{ + if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() ) + // the reformat position is behind our new line, that means + // something of our text has moved to the next line + return 0; + + xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd ); + + // in case we do not have any fly in our line, our repaint position + // is the changed position - 1 + if ( ! pFlyStart && ! pCurr->IsFly() ) + { + // this is the maximum repaint offset determined during formatting + // for example: the beginning of the first right tab stop + // if this value is 0, this means that we do not have an upper + // limit for the repaint offset + const long nFormatRepaint = GetInfo().GetPaintOfst(); + + if ( nReformat < GetInfo().GetLineStart() + 3 ) + return 0; + + // step back two positions for smoother repaint + nReformat -= 2; + +#ifndef QUARTZ +#ifndef ENABLE_GRAPHITE + // --> FME 2004-09-27 #i28795#, #i34607#, #i38388# + // step back six(!) more characters for complex scripts + // this is required e.g., for Khmer (thank you, Javier!) + const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); + xub_StrLen nMaxContext = 0; + if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) ) + nMaxContext = 6; +#else + // Some Graphite fonts need context for scripts not marked as complex + static const xub_StrLen nMaxContext = 10; +#endif +#else + // some fonts like Quartz's Zapfino need more context + // TODO: query FontInfo for maximum unicode context + static const xub_StrLen nMaxContext = 8; +#endif + if( nMaxContext > 0 ) + { + if ( nReformat > GetInfo().GetLineStart() + nMaxContext ) + nReformat = nReformat - nMaxContext; + else + nReformat = GetInfo().GetLineStart(); + } + // <-- + + // Weird situation: Our line used to end with a hole portion + // and we delete some characters at the end of our line. We have + // to take care for repainting the blanks which are not anymore + // covered by the hole portion + while ( nReformat > GetInfo().GetLineStart() && + CH_BLANK == GetInfo().GetChar( nReformat ) ) + --nReformat; + + ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" ); + SwRect aRect; + + // Note: GetChareRect is not const. It definitely changes the + // bMulti flag. We have to save and resore the old value. + sal_Bool bOldMulti = GetInfo().IsMulti(); + GetCharRect( &aRect, nReformat ); + GetInfo().SetMulti( bOldMulti ); + + return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) : + aRect.Left(); + } + else + { + // nReformat may be wrong, if something around flys has changed: + // we compare the former and the new fly positions in this line + // if anything has changed, we carefully have to adjust the right + // repaint position + long nPOfst = 0; + USHORT nCnt = 0; + USHORT nX = 0; + USHORT nIdx = GetInfo().GetLineStart(); + SwLinePortion* pPor = pCurr->GetFirstPortion(); + + while ( pPor ) + { + if ( pPor->IsFlyPortion() ) + { + // compare start of fly with former start of fly + if ( pFlyStart && + nCnt < pFlyStart->Count() && + nX == (*pFlyStart)[ nCnt ] && + nIdx < nReformat + ) + // found fix position, nothing has changed left from nX + nPOfst = nX + pPor->Width(); + else + break; + + nCnt++; + } + nX = nX + pPor->Width(); + nIdx = nIdx + pPor->GetLen(); + pPor = pPor->GetPortion(); + } + + return nPOfst + GetLeftMargin(); + } +} + +bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ) +{ + // Only if hidden text should not be shown: + if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() ) + return false; + + const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo(); + xub_StrLen nHiddenStart; + xub_StrLen nHiddenEnd; + rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd ); + if ( nHiddenEnd ) + { + rPos = nHiddenEnd; + return true; + } + + return false; +} diff --git a/sw/source/core/text/itrform2.hxx b/sw/source/core/text/itrform2.hxx new file mode 100644 index 000000000000..7eaaeb396d3d --- /dev/null +++ b/sw/source/core/text/itrform2.hxx @@ -0,0 +1,217 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrform2.hxx,v $ + * $Revision: 1.19 $ + * + * 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. + * + ************************************************************************/ +#ifndef _ITRFORM2_HXX +#define _ITRFORM2_HXX +#include "itrpaint.hxx" + +class SwFlyCntPortion; +class SwInterHyphInfo; +class SwDropPortion; +class SwFmtDrop; +class SwTxtAttr; +class SwNumberPortion; +class SwErgoSumPortion; +class SwExpandPortion; +class SwMultiPortion; +class SwFtnPortion; +class SvLongs; + +/************************************************************************* + * class SwTxtFormatter + *************************************************************************/ + +class SwTxtFormatter : public SwTxtPainter +{ + const SwFmtDrop *pDropFmt; + SwMultiPortion* pMulti; // during formatting a multi-portion + sal_uInt8 nCntEndHyph; // zaehlt aufeinanderfolgende Hyphens am Zeilenende + sal_uInt8 nCntMidHyph; // zaehlt aufeinanderfolgende Hyphens vor Flies + xub_StrLen nLeftScanIdx; // for increasing performance during + xub_StrLen nRightScanIdx; // scanning for portion ends + sal_Bool bOnceMore : 1; // noch 'ne Runde? + sal_Bool bFlyInCntBase : 1; // Base-Referenz der zeichengeb. Rahmen setzen + sal_Bool bChanges : 1; // Flag, fuer die Berechnung des Repaint-Rechtecks + sal_Bool bTruncLines : 1; // Flag, Repaint-Rechtecks ggf. erweitern + sal_Bool bUnclipped : 1; // Flag, ob Repaint groesser als feste Zeilenhoehe + USHORT m_nHintEndIndex; // HACK for TryNewNoLengthPortion + SwLinePortion *NewPortion( SwTxtFormatInfo &rInf ); + SwTxtPortion *NewTxtPortion( SwTxtFormatInfo &rInf ); + SwLinePortion *NewExtraPortion( SwTxtFormatInfo &rInf ); + SwTabPortion *NewTabPortion( SwTxtFormatInfo &rInf, bool bAuto ) const; + SwNumberPortion *NewNumberPortion( SwTxtFormatInfo &rInf ) const; + SwDropPortion *NewDropPortion( SwTxtFormatInfo &rInf ); + SwNumberPortion *NewFtnNumPortion( SwTxtFormatInfo &rInf ) const; + SwErgoSumPortion *NewErgoSumPortion( SwTxtFormatInfo &rInf ) const; + SwExpandPortion *NewFldPortion( SwTxtFormatInfo &rInf, + const SwTxtAttr *pHt ) const; + SwFtnPortion *NewFtnPortion( SwTxtFormatInfo &rInf, SwTxtAttr *pHt ); + SwFlyCntPortion *NewFlyCntPortion( SwTxtFormatInfo &rInf, + SwTxtAttr *pHt ) const; + SwLinePortion *WhichFirstPortion( SwTxtFormatInfo &rInf ); + SwTxtPortion *WhichTxtPor( SwTxtFormatInfo &rInf ) const; + SwExpandPortion * TryNewNoLengthPortion( SwTxtFormatInfo & rInfo ); + + // Das Herzstueck der Formatierung + void BuildPortions( SwTxtFormatInfo &rInf ); + BOOL BuildMultiPortion( SwTxtFormatInfo &rInf, SwMultiPortion& rMulti ); + + // Berechnung des emulierten rechten Rands + void CalcFlyWidth( SwTxtFormatInfo &rInf ); + + // wird von SwTxtFormatter wegen UpdatePos ueberladen + void CalcAdjustLine( SwLineLayout *pCurr ); + + // consideres line spacing attributes + void CalcRealHeight( sal_Bool bNewLine = sal_False ); + + // uebertraegt die Daten nach rInf + void FeedInf( SwTxtFormatInfo &rInf ) const; + + // behandelt die Unterlaufsituationen + SwLinePortion *UnderFlow( SwTxtFormatInfo &rInf ); + + // errechnet den Ascent und die Hoehe aus der Fontmetric + void CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor ); + + // determines, if a optimized repaint rectange is allowed + sal_Bool AllowRepaintOpt() const; + + // calculates and sets the optimized repaint offset + long CalcOptRepaint( xub_StrLen nOldLineEnd, const SvLongs* pFlyStart ); + + // wird von FormatLine gerufen. + void FormatReset( SwTxtFormatInfo &rInf ); + + // durch das Adjustment aendert sich die Position der Portions + void UpdatePos( SwLineLayout *pCurr, Point aStart, xub_StrLen nStartIdx, + sal_Bool bAllWays = sal_False ) const; + + // Setze alle FlyInCntFrms auf die uebergebene BaseLine + void AlignFlyInCntBase( long nBaseLine ) const; + + // Unterlaufbedingungen bei Flys + sal_Bool ChkFlyUnderflow( SwTxtFormatInfo &rInf ) const; + + // Portion einfuegen. + void InsertPortion( SwTxtFormatInfo &rInf, SwLinePortion *pPor ) const; + + // schaetzt die Hoehe fuer die DropPortion + void GuessDropHeight( const MSHORT nLines ); + +public: + // errechnet die Hoehe fuer die DropPortion + void CalcDropHeight( const MSHORT nLines ); + + // errechnet den Bottom des Absatzes, beruecksichtigt an diesem verankerte + // Objekte mit Umlauf 1. Absatz. + SwTwips CalcBottomLine() const; + + // Beruecksichtigt zeichengebundene Objekte bei der Repaintrechteck- + // berechnung in Zeilen mit fester Zeilenhoehe + void CalcUnclipped( SwTwips& rTop, SwTwips& rBottom ); + + // u.a. fuer DropCaps + sal_Bool CalcOnceMore(); + + void CtorInitTxtFormatter( SwTxtFrm *pFrm, SwTxtFormatInfo *pInf ); + inline SwTxtFormatter( SwTxtFrm *pTxtFrm, SwTxtFormatInfo *pTxtFmtInf ) : SwTxtPainter(pTxtFrm!=NULL?pTxtFrm->GetTxtNode():NULL) + { CtorInitTxtFormatter( pTxtFrm, pTxtFmtInf ); } + ~SwTxtFormatter(); + + xub_StrLen FormatLine( const xub_StrLen nStart ); + + void RecalcRealHeight(); + + // Wir formatieren eine Zeile fuer die interaktive Trennung + sal_Bool Hyphenate( SwInterHyphInfo &rInf ); + + // Spezialmethode fuer QuoVadis-Texte + // nErgo ist die Seitennummer der ErgoSum-Ftn + // Bei 0 ist es noch unklar. + xub_StrLen FormatQuoVadis( const xub_StrLen nStart ); + + // Die Notbremse: Formatierung abbrechen, Zeile verwerfen. + inline sal_Bool IsStop() const { return GetInfo().IsStop(); } + + // Das Gegenstueck: Formatierung unbedingt fortsetzen. + inline sal_Bool IsNewLine() const { return GetInfo().IsNewLine(); } + + // FormatQuick(); auffrischen von Formatinformationen + inline sal_Bool IsQuick() const { return GetInfo().IsQuick(); } + + // erzeugt ggfs. ein SwLineLayout, dass Ftn/Fly--Oszillation unterbindet. + void MakeDummyLine(); + + // SwTxtIter-Funktionalitaet + void Insert( SwLineLayout *pLine ); + + // die noch verbleibende Hoehe bis zum Seitenrand + KSHORT GetFrmRstHeight() const; + + // Wie breit waerest Du ohne rechte Begrenzungen (Flys etc.)? + SwTwips _CalcFitToContent( ); + + SwLinePortion* MakeRestPortion(const SwLineLayout* pLine, xub_StrLen nPos); + + inline const SwFmtDrop *GetDropFmt() const { return pDropFmt; } + inline void ClearDropFmt() { pDropFmt = 0; } + + inline SwMultiPortion *GetMulti() const { return pMulti; } + + inline sal_Bool IsOnceMore() const { return bOnceMore; } + inline void SetOnceMore( sal_Bool bNew ) { bOnceMore = bNew; } + + inline sal_Bool HasChanges() const { return bChanges; } + inline void SetChanges() { bChanges = sal_True; } + + inline sal_Bool HasTruncLines() const { return bTruncLines; } + inline void SetTruncLines( sal_Bool bNew ) { bTruncLines = bNew; } + + inline sal_Bool IsUnclipped() const { return bUnclipped; } + inline void SetUnclipped( sal_Bool bNew ) { bUnclipped = bNew; } + + inline sal_Bool IsFlyInCntBase() const { return bFlyInCntBase; } + inline void SetFlyInCntBase( sal_Bool bNew = sal_True ){ bFlyInCntBase = bNew; } + + inline SwTxtFormatInfo &GetInfo() + { return (SwTxtFormatInfo&)SwTxtIter::GetInfo(); } + inline const SwTxtFormatInfo &GetInfo() const + { return (const SwTxtFormatInfo&)SwTxtIter::GetInfo(); } + + inline void InitCntHyph() { CntHyphens( nCntEndHyph, nCntMidHyph ); } + inline const sal_uInt8 &CntEndHyph() const { return nCntEndHyph; } + inline const sal_uInt8 &CntMidHyph() const { return nCntMidHyph; } + inline sal_uInt8 &CntEndHyph() { return nCntEndHyph; } + inline sal_uInt8 &CntMidHyph() { return nCntMidHyph; } +}; + + + +#endif diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx new file mode 100644 index 000000000000..7cfb941545a8 --- /dev/null +++ b/sw/source/core/text/itrpaint.cxx @@ -0,0 +1,721 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrpaint.cxx,v $ + * $Revision: 1.47.210.1 $ + * + * 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 "flyfrm.hxx" // SwFlyInCntFrm +#include "viewopt.hxx" // SwViewOptions +#include "errhdl.hxx" +#include "txtatr.hxx" // SwINetFmt +#include <tools/multisel.hxx> +#include <svx/escpitem.hxx> +#include <svx/udlnitem.hxx> +#include <svx/lrspitem.hxx> +#include <txtinet.hxx> +#include <fchrfmt.hxx> +#include <frmatr.hxx> +#include <sfx2/printer.hxx> +#include <fmtftn.hxx> +#include <fmtfld.hxx> +#include <fldbas.hxx> // SwField +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> + +// --> FME 2004-06-08 #i12836# enhanced pdf export +#include <EnhancedPDFExportHelper.hxx> +// <-- + + +#include "flyfrms.hxx" +#include "viewsh.hxx" +#include "txtcfg.hxx" +#include "itrpaint.hxx" +#include "txtfrm.hxx" // pFrm +#include "txtfly.hxx" +#include "swfont.hxx" +#include "txtpaint.hxx" +#include "portab.hxx" // SwTabPortion::IsFilled +#include "porfly.hxx" // SwFlyCntPortion +#include "porfld.hxx" // SwGrfNumPortion +#include "frmfmt.hxx" // LRSpace +#include "txatbase.hxx" // SwTxtAttr +#include "charfmt.hxx" // SwFmtCharFmt +#include "redlnitr.hxx" // SwRedlineItr +#include "porrst.hxx" // SwArrowPortion +#include "pormulti.hxx" + +/************************************************************************* + * IsUnderlineBreak + * + * Returns, if we have an underline breaking situation + * Adding some more conditions here means you also have to change them + * in SwTxtPainter::CheckSpecialUnderline + *************************************************************************/ +sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ) +{ + return UNDERLINE_NONE == rFnt.GetUnderline() || + rPor.IsFlyPortion() || rPor.IsFlyCntPortion() || + rPor.IsBreakPortion() || rPor.IsMarginPortion() || + rPor.IsHolePortion() || + ( rPor.IsMultiPortion() && ! ((SwMultiPortion&)rPor).IsBidi() ) || + rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() || + SVX_CASEMAP_KAPITAELCHEN == rFnt.GetCaseMap(); +} + +/************************************************************************* + * SwTxtPainter::CtorInitTxtPainter() + *************************************************************************/ +void SwTxtPainter::CtorInitTxtPainter( SwTxtFrm *pNewFrm, SwTxtPaintInfo *pNewInf ) +{ + CtorInitTxtCursor( pNewFrm, pNewInf ); + pInf = pNewInf; + SwFont *pMyFnt = GetFnt(); + GetInfo().SetFont( pMyFnt ); +#ifndef PRODUCT + if( ALIGN_BASELINE != pMyFnt->GetAlign() ) + { + ASSERT( ALIGN_BASELINE == pMyFnt->GetAlign(), + "+SwTxtPainter::CTOR: font alignment revolution" ); + pMyFnt->SetAlign( ALIGN_BASELINE ); + } +#endif + bPaintDrop = sal_False; +} + + +/************************************************************************* + * SwTxtPainter::CalcPaintOfst() + *************************************************************************/ +SwLinePortion *SwTxtPainter::CalcPaintOfst( const SwRect &rPaint ) +{ + SwLinePortion *pPor = pCurr->GetFirstPortion(); + GetInfo().SetPaintOfst( 0 ); + SwTwips nPaintOfst = rPaint.Left(); + + // nPaintOfst wurde exakt auf das Ende eingestellt, deswegen <= + // nPaintOfst ist dokumentglobal, deswegen nLeftMar aufaddieren + // const KSHORT nLeftMar = KSHORT(GetLeftMargin()); + // 8310: painten von LineBreaks in leeren Zeilen. + if( nPaintOfst && pCurr->Width() ) + { + SwLinePortion *pLast = 0; + // 7529 und 4757: nicht <= nPaintOfst + while( pPor && GetInfo().X() + pPor->Width() + (pPor->Height()/2) + < nPaintOfst ) + { + DBG_LOOP; + if( pPor->InSpaceGrp() && GetInfo().GetSpaceAdd() ) + { + long nTmp = GetInfo().X() +pPor->Width() + + pPor->CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() ); + if( nTmp + (pPor->Height()/2) >= nPaintOfst ) + break; + GetInfo().X( nTmp ); + GetInfo().SetIdx( GetInfo().GetIdx() + pPor->GetLen() ); + } + else + pPor->Move( GetInfo() ); + pLast = pPor; + pPor = pPor->GetPortion(); + } + + // 7529: bei PostIts auch pLast returnen. + if( pLast && !pLast->Width() && pLast->IsPostItsPortion() ) + { + pPor = pLast; + GetInfo().SetIdx( GetInfo().GetIdx() - pPor->GetLen() ); + } + } + return pPor; +} + +/************************************************************************* + * SwTxtPainter::DrawTextLine() + * + * Es gibt zwei Moeglichkeiten bei transparenten Font auszugeben: + * 1) DrawRect auf die ganze Zeile und die DrawText hinterher + * (objektiv schnell, subjektiv langsam). + * 2) Fuer jede Portion ein DrawRect mit anschliessendem DrawText + * ausgefuehrt (objektiv langsam, subjektiv schnell). + * Da der User in der Regel subjektiv urteilt, wird die 2. Methode + * als Default eingestellt. + *************************************************************************/ +void SwTxtPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip, + const sal_Bool bUnderSz ) +{ +#if OSL_DEBUG_LEVEL > 1 +// USHORT nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() ); +// USHORT nFntAscent = GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), GetInfo().GetOut() ); +#endif + + // Adjustierung ggf. nachholen + GetAdjusted(); + GetInfo().SetpSpaceAdd( pCurr->GetpLLSpaceAdd() ); + GetInfo().ResetSpaceIdx(); + GetInfo().SetKanaComp( pCurr->GetpKanaComp() ); + GetInfo().ResetKanaIdx(); + // Die Groesse des Frames + GetInfo().SetIdx( GetStart() ); + GetInfo().SetPos( GetTopLeft() ); + + const sal_Bool bDrawInWindow = GetInfo().OnWin(); + + // 6882: Leerzeilen duerfen nicht wegoptimiert werden bei Paragraphzeichen. + const sal_Bool bEndPor = GetInfo().GetOpt().IsParagraph() && !GetInfo().GetTxt().Len(); + + SwLinePortion *pPor = bEndPor ? pCurr->GetFirstPortion() : CalcPaintOfst( rPaint ); + + // Optimierung! + const SwTwips nMaxRight = Min( rPaint.Right(), Right() ); + const SwTwips nTmpLeft = GetInfo().X(); + if( !bEndPor && nTmpLeft >= nMaxRight ) + return; + + // DropCaps! + // 7538: natuerlich auch auf dem Drucker + if( !bPaintDrop ) + { + // 8084: Optimierung, weniger Painten. + // AMA: Durch 8084 wurde 7538 wiederbelebt! + // bDrawInWindow entfernt, damit DropCaps auch gedruckt werden + bPaintDrop = pPor == pCurr->GetFirstPortion() + && GetDropLines() >= GetLineNr(); + } + + KSHORT nTmpHeight, nTmpAscent; + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + + // bClip entscheidet darueber, ob geclippt werden muss. + // Das Ganze muss vor der Retusche stehen + + sal_Bool bClip = ( bDrawInWindow || bUnderSz ) && !rClip.IsChg(); + if( bClip && pPor ) + { + // Wenn TopLeft oder BottomLeft der Line ausserhalb liegen, + // muss geclippt werden. Die Ueberpruefung auf Right() erfolgt + // in der folgenden Ausgabeschleife... + + if( GetInfo().GetPos().X() < rPaint.Left() || + GetInfo().GetPos().Y() < rPaint.Top() || + GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() ) + { + bClip = sal_False; + rClip.ChgClip( rPaint, pFrm, pCurr->HasUnderscore() ); + } +#if OSL_DEBUG_LEVEL > 1 + static sal_Bool bClipAlways = sal_False; + if( bClip && bClipAlways ) + { bClip = sal_False; + rClip.ChgClip( rPaint ); + } +#endif + } + + // Alignment: + sal_Bool bPlus = sal_False; + OutputDevice* pOut = GetInfo().GetOut(); + Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() ); + if ( aPnt1.X() < rPaint.Left() ) + aPnt1.X() = rPaint.Left(); + if ( aPnt1.Y() < rPaint.Top() ) + aPnt1.Y() = rPaint.Top(); + Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(), + GetInfo().GetPos().Y() + nTmpHeight ); + if ( aPnt2.X() > rPaint.Right() ) + aPnt2.X() = rPaint.Right(); + if ( aPnt2.Y() > rPaint.Bottom() ) + { + aPnt2.Y() = rPaint.Bottom(); + bPlus = sal_True; + } + + const SwRect aLineRect( aPnt1, aPnt2 ); + + if( pCurr->IsClipping() ) + { + rClip.ChgClip( aLineRect, pFrm ); + bClip = sal_False; + } + + if( !pPor && !bEndPor ) + { +#ifdef DBGTXT + aDbstream << "PAINTER: done nothing" << endl; +#endif + return; + } + + // Baseline-Ausgabe auch bei nicht-TxtPortions (vgl. TabPor mit Fill) + // if no special vertical alignment is used, + // we calculate Y value for the whole line + GETGRID( GetTxtFrm()->FindPageFrm() ) + const sal_Bool bAdjustBaseLine = + GetLineInfo().HasSpecialAlign( GetTxtFrm()->IsVertical() ) || + ( 0 != pGrid ); + const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent; + if ( ! bAdjustBaseLine ) + GetInfo().Y( nLineBaseLine ); + + // 7529: PostIts prepainten + if( GetInfo().OnWin() && pPor && !pPor->Width() ) + { + SeekAndChg( GetInfo() ); + + if( bAdjustBaseLine ) + { + const SwTwips nOldY = GetInfo().Y(); + + GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *pCurr, 0, + GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), *pOut ), + GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), *pOut ) + ) ); + + pPor->PrePaint( GetInfo(), pPor ); + GetInfo().Y( nOldY ); + } + else + pPor->PrePaint( GetInfo(), pPor ); + } + + // 7923: EndPortions geben auch Zeichen aus, deswegen den Fnt wechseln! + if( bEndPor ) + SeekStartAndChg( GetInfo() ); + + sal_Bool bRest = pCurr->IsRest(); + sal_Bool bFirst = sal_True; + + SwArrowPortion *pArrow = NULL; + // Reference portion for the paragraph end portion + SwLinePortion* pEndTempl = pCurr->GetFirstPortion(); + + while( pPor ) + { + DBG_LOOP; + sal_Bool bSeeked = sal_True; + GetInfo().SetLen( pPor->GetLen() ); + + const SwTwips nOldY = GetInfo().Y(); + + if ( bAdjustBaseLine ) + { + GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *pCurr, pPor ) ); + + // we store the last portion, because a possible paragraph + // end character has the same font as this portion + // (only in special vertical alignment case, otherwise the first + // portion of the line is used) + if ( pPor->Width() && pPor->InTxtGrp() ) + pEndTempl = pPor; + } + + // Ein Sonderfall sind GluePortions, die Blanks ausgeben. + + // 6168: Der Rest einer FldPortion zog sich die Attribute der naechsten + // Portion an, dies wird durch SeekAndChgBefore vermieden: + if( ( bRest && pPor->InFldGrp() && !pPor->GetLen() ) ) + SeekAndChgBefore( GetInfo() ); + else if ( pPor->IsQuoVadisPortion() ) + { + xub_StrLen nOffset = GetInfo().GetIdx(); + SeekStartAndChg( GetInfo(), sal_True ); + if( GetRedln() && pCurr->HasRedline() ) + GetRedln()->Seek( *pFnt, nOffset, 0 ); + } + else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() ) + SeekAndChg( GetInfo() ); + else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() ) + { + // Paragraphzeichen sollten den gleichen Font wie das Zeichen vor + // haben, es sei denn, es gibt Redlining in dem Absatz. + if( GetRedln() ) + SeekAndChg( GetInfo() ); + else + SeekAndChgBefore( GetInfo() ); + } + else + bSeeked = sal_False; + +// bRest = sal_False; + + // Wenn das Ende der Portion hinausragt, wird geclippt. + // Es wird ein Sicherheitsabstand von Height-Halbe aufaddiert, + // damit die TTF-"f" nicht im Seitenrand haengen... + if( bClip && + GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight ) + { + bClip = sal_False; + rClip.ChgClip( rPaint, pFrm, pCurr->HasUnderscore() ); + } + + // Portions, die "unter" dem Text liegen wie PostIts + SwLinePortion *pNext = pPor->GetPortion(); + if( GetInfo().OnWin() && pNext && !pNext->Width() ) + { + // Fix 11289: Felder waren hier ausgeklammert wg. Last!=Owner beim + // Laden von Brief.sdw. Jetzt sind die Felder wieder zugelassen, + // durch bSeeked wird Last!=Owner vermieden. + if ( !bSeeked ) + SeekAndChg( GetInfo() ); + pNext->PrePaint( GetInfo(), pPor ); + } + + // We calculate a separate font for underlining. + CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 ); + SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt(); + if ( pUnderLineFnt ) + { + const Point aTmpPoint( GetInfo().X(), + bAdjustBaseLine ? + pUnderLineFnt->GetPos().Y() : + nLineBaseLine ); + pUnderLineFnt->SetPos( aTmpPoint ); + } + + + // in extended input mode we do not want a common underline font. + SwUnderlineFont* pOldUnderLineFnt = 0; + if ( GetRedln() && GetRedln()->ExtOn() ) + { + pOldUnderLineFnt = GetInfo().GetUnderFnt(); + GetInfo().SetUnderFnt( 0 ); + } + + { + // --> FME 2004-06-24 #i16816# tagged pdf support + Por_Info aPorInfo( *pPor, *this ); + SwTaggedPDFHelper aTaggedPDFHelper( 0, 0, &aPorInfo, *pOut ); + // <-- + + if( pPor->IsMultiPortion() ) + PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor ); + else + pPor->Paint( GetInfo() ); + } + + // reset underline font + if ( pOldUnderLineFnt ) + GetInfo().SetUnderFnt( pOldUnderLineFnt ); + + // reset (for special vertical alignment) + GetInfo().Y( nOldY ); + + if( GetFnt()->IsURL() && pPor->InTxtGrp() ) + GetInfo().NotifyURL( *pPor ); + + bFirst &= !pPor->GetLen(); + if( pNext || !pPor->IsMarginPortion() ) + pPor->Move( GetInfo() ); + if( pPor->IsArrowPortion() && GetInfo().OnWin() && !pArrow ) + pArrow = (SwArrowPortion*)pPor; + + pPor = bDrawInWindow || GetInfo().X() <= nMaxRight || + // --> FME 2004-06-24 #i16816# tagged pdf support + ( GetInfo().GetVsh() && + GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() && + pNext && pNext->IsHolePortion() ) ? + // <-- + pNext : + 0; + } + + // delete underline font + delete GetInfo().GetUnderFnt(); + GetInfo().SetUnderFnt( 0 ); + + // paint remaining stuff + if( bDrawInWindow ) + { + // If special vertical alignment is enabled, GetInfo().Y() is the + // top of the current line. Therefore is has to be adjusted for + // the painting of the remaining stuff. We first store the old value. + const SwTwips nOldY = GetInfo().Y(); + + if( !GetNextLine() && + GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreView() && + GetInfo().GetOpt().IsParagraph() && !GetTxtFrm()->GetFollow() && + GetInfo().GetIdx() >= GetInfo().GetTxt().Len() ) + { + const SwTmpEndPortion aEnd( *pEndTempl ); + GetFnt()->ChgPhysFnt( GetInfo().GetVsh(), *pOut ); + + if ( bAdjustBaseLine ) + GetInfo().Y( GetInfo().GetPos().Y() + + AdjustBaseLine( *pCurr, &aEnd ) ); + + aEnd.Paint( GetInfo() ); + GetInfo().Y( nOldY ); + } + if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreView() ) + { + const sal_Bool bNextUndersized = + ( GetTxtFrm()->GetNext() && + 0 == GetTxtFrm()->GetNext()->Prt().Height() && + GetTxtFrm()->GetNext()->IsTxtFrm() && + ((SwTxtFrm*)GetTxtFrm()->GetNext())->IsUndersized() ) ; + + if( bUnderSz || bNextUndersized ) + { + if ( bAdjustBaseLine ) + GetInfo().Y( GetInfo().GetPos().Y() + pCurr->GetAscent() ); + + if( pArrow ) + GetInfo().DrawRedArrow( *pArrow ); + + // GetInfo().Y() must be current baseline. + SwTwips nDiff = GetInfo().Y() + nTmpHeight - nTmpAscent - GetTxtFrm()->Frm().Bottom(); + if( ( nDiff > 0 && + ( GetEnd() < GetInfo().GetTxt().Len() || + ( nDiff > nTmpHeight/2 && GetPrevLine() ) ) ) || + nDiff >= 0 && bNextUndersized ) + + { + SwArrowPortion aArrow( GetInfo() ); + GetInfo().DrawRedArrow( aArrow ); + } + + GetInfo().Y( nOldY ); + } + } + } + + if( pCurr->IsClipping() ) + rClip.ChgClip( rPaint, pFrm ); +} + +void SwTxtPainter::CheckSpecialUnderline( const SwLinePortion* pPor, + long nAdjustBaseLine ) +{ + // Check if common underline should not be continued. + if ( IsUnderlineBreak( *pPor, *pFnt ) ) + { + // delete underline font + delete GetInfo().GetUnderFnt(); + GetInfo().SetUnderFnt( 0 ); + return; + } + + // If current underline matches the common underline font, we continue + // to use the common underline font. + if ( GetInfo().GetUnderFnt() && + GetInfo().GetUnderFnt()->GetFont().GetUnderline() == + GetFnt()->GetUnderline() ) + return; + + // calculate the new common underline font + SwFont* pUnderlineFnt = 0; + Point aCommonBaseLine; + + Range aRange( 0, GetInfo().GetTxt().Len() ); + MultiSelection aUnderMulti( aRange ); + + ASSERT( GetFnt() && UNDERLINE_NONE != GetFnt()->GetUnderline(), + "CheckSpecialUnderline without underlined font" ) + const SwFont* pParaFnt = GetAttrHandler().GetFont(); + if( pParaFnt && pParaFnt->GetUnderline() == GetFnt()->GetUnderline() ) + aUnderMulti.SelectAll(); + + SwTxtAttr* pTxtAttr; + if( HasHints() ) + { + sal_Bool bUnder = sal_False; + MSHORT nTmp = 0; + + while( nTmp < pHints->GetStartCount() ) + { + pTxtAttr = pHints->GetStart( nTmp++ ); + sal_Bool bUnderSelect = sal_False; + + const SvxUnderlineItem* pItem = + static_cast<const SvxUnderlineItem*>(CharFmt::GetItem( *pTxtAttr, RES_CHRATR_UNDERLINE )); + + if ( pItem ) + { + bUnder = sal_True; + bUnderSelect = pFnt->GetUnderline() == pItem->GetLineStyle(); + } + + if( bUnder ) + { + xub_StrLen nSt = *pTxtAttr->GetStart(); + xub_StrLen nEnd = *pTxtAttr->GetEnd(); + if( nEnd > nSt ) + { + Range aTmp( nSt, nEnd - 1 ); + if( bUnder ) + aUnderMulti.Select( aTmp, bUnderSelect ); + } + bUnder = sal_False; + } + } + } + + MSHORT i; + xub_StrLen nIndx = GetInfo().GetIdx(); + long nUnderStart = 0; + long nUnderEnd = 0; + MSHORT nCnt = (MSHORT)aUnderMulti.GetRangeCount(); + + // find the underline range the current portion is contained in + for( i = 0; i < nCnt; ++i ) + { + const Range& rRange = aUnderMulti.GetRange( i ); + if( nUnderEnd == rRange.Min() ) + nUnderEnd = rRange.Max(); + else if( nIndx >= rRange.Min() ) + { + nUnderStart = rRange.Min(); + nUnderEnd = rRange.Max(); + } + else + break; + } + + // restrict start and end to current line + if ( GetStart() > nUnderStart ) + nUnderStart = GetStart(); + + if ( GetEnd() && GetEnd() <= nUnderEnd ) + nUnderEnd = GetEnd() - 1; + + + // check, if underlining is not isolated + if ( nIndx + GetInfo().GetLen() < nUnderEnd + 1 ) + { + // + // here starts the algorithm for calculating the underline font + // + SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo(); + SwAttrIter aIter( *(SwTxtNode*)GetInfo().GetTxtFrm()->GetTxtNode(), + rScriptInfo ); + + xub_StrLen nTmpIdx = nIndx; + ULONG nSumWidth = 0; + ULONG nSumHeight = 0; + ULONG nBold = 0; + USHORT nMaxBaseLineOfst = 0; + USHORT nNumberOfPortions = 0; + + while( nTmpIdx <= nUnderEnd && pPor ) + { + if ( pPor->IsFlyPortion() || pPor->IsFlyCntPortion() || + pPor->IsBreakPortion() || pPor->IsMarginPortion() || + pPor->IsHolePortion() || + ( pPor->IsMultiPortion() && ! ((SwMultiPortion*)pPor)->IsBidi() ) ) + break; + + aIter.Seek( nTmpIdx ); + + if ( aIter.GetFnt()->GetEscapement() < 0 || pFnt->IsWordLineMode() || + SVX_CASEMAP_KAPITAELCHEN == pFnt->GetCaseMap() ) + break; + + if ( !aIter.GetFnt()->GetEscapement() ) + { + nSumWidth += pPor->Width(); + const ULONG nFontHeight = aIter.GetFnt()->GetHeight(); + + // If we do not have a common baseline we take the baseline + // and the font of the lowest portion. + if ( nAdjustBaseLine ) + { + USHORT nTmpBaseLineOfst = AdjustBaseLine( *pCurr, pPor ); + if ( nMaxBaseLineOfst < nTmpBaseLineOfst ) + { + nMaxBaseLineOfst = nTmpBaseLineOfst; + nSumHeight = nFontHeight; + } + } + // in horizontal layout we build a weighted sum of the heights + else + nSumHeight += pPor->Width() * nFontHeight; + + if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() ) + nBold += pPor->Width(); + } + + ++nNumberOfPortions; + + nTmpIdx = nTmpIdx + pPor->GetLen(); + pPor = pPor->GetPortion(); + } + + // resulting height + if ( nNumberOfPortions > 1 && nSumWidth ) + { + const ULONG nNewFontHeight = nAdjustBaseLine ? + nSumHeight : + nSumHeight / nSumWidth; + + pUnderlineFnt = new SwFont( *GetInfo().GetFont() ); + + // font height + const BYTE nActual = pUnderlineFnt->GetActual(); + pUnderlineFnt->SetSize( Size( pUnderlineFnt->GetSize( nActual ).Width(), + nNewFontHeight ), nActual ); + + // font weight + if ( 2 * nBold > nSumWidth ) + pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual ); + else + pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual ); + + // common base line + aCommonBaseLine.Y() = nAdjustBaseLine + nMaxBaseLineOfst; + } + } + + // an escaped redlined portion should also have a special underlining + if( ! pUnderlineFnt && pFnt->GetEscapement() > 0 && GetRedln() && + GetRedln()->ChkSpecialUnderline() ) + pUnderlineFnt = new SwFont( *pFnt ); + + delete GetInfo().GetUnderFnt(); + + if ( pUnderlineFnt ) + { + pUnderlineFnt->SetProportion( 100 ); + pUnderlineFnt->SetEscapement( 0 ); + pUnderlineFnt->SetStrikeout( STRIKEOUT_NONE ); + pUnderlineFnt->SetOverline( UNDERLINE_NONE ); + const Color aFillColor( COL_TRANSPARENT ); + pUnderlineFnt->SetFillColor( aFillColor ); + + GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt, + aCommonBaseLine ) ); + } + else + // I'm sorry, we do not have a special underlining font for you. + GetInfo().SetUnderFnt( 0 ); +} diff --git a/sw/source/core/text/itrpaint.hxx b/sw/source/core/text/itrpaint.hxx new file mode 100644 index 000000000000..b6ed27047977 --- /dev/null +++ b/sw/source/core/text/itrpaint.hxx @@ -0,0 +1,72 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrpaint.hxx,v $ + * $Revision: 1.11 $ + * + * 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. + * + ************************************************************************/ +#ifndef _ITRPAINT_HXX +#define _ITRPAINT_HXX +#include "itrtxt.hxx" + +class SwSaveClip; // SwTxtPainter +class SwMultiPortion; + +/************************************************************************* + * class SwTxtPainter + *************************************************************************/ + +class SwTxtPainter : public SwTxtCursor +{ + sal_Bool bPaintDrop; + + SwLinePortion *CalcPaintOfst( const SwRect &rPaint ); + void CheckSpecialUnderline( const SwLinePortion* pPor, + long nAdjustBaseLine = 0 ); +protected: + void CtorInitTxtPainter( SwTxtFrm *pFrm, SwTxtPaintInfo *pInf ); + inline SwTxtPainter(SwTxtNode* pTxtNode) : SwTxtCursor(pTxtNode) { } + +public: + inline SwTxtPainter( SwTxtFrm *pTxtFrm, SwTxtPaintInfo *pTxtPaintInf ) : SwTxtCursor(pTxtFrm!=NULL?pTxtFrm->GetTxtNode():NULL) + { CtorInitTxtPainter( pTxtFrm, pTxtPaintInf ); } + void DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip, + const sal_Bool bUnderSz ); + void PaintDropPortion(); + // if PaintMultiPortion is called recursively, we have to pass the + // surrounding SwBidiPortion + void PaintMultiPortion( const SwRect &rPaint, SwMultiPortion& rMulti, + const SwMultiPortion* pEnvPor = 0 ); + inline void SetPaintDrop( const sal_Bool bNew ) { bPaintDrop = bNew; } + inline sal_Bool IsPaintDrop() const { return bPaintDrop; } + inline SwTxtPaintInfo &GetInfo() + { return (SwTxtPaintInfo&)SwTxtIter::GetInfo(); } + inline const SwTxtPaintInfo &GetInfo() const + { return (const SwTxtPaintInfo&)SwTxtIter::GetInfo(); } +}; + + + +#endif diff --git a/sw/source/core/text/itrtxt.cxx b/sw/source/core/text/itrtxt.cxx new file mode 100644 index 000000000000..dc789beb9235 --- /dev/null +++ b/sw/source/core/text/itrtxt.cxx @@ -0,0 +1,526 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrtxt.cxx,v $ + * $Revision: 1.34 $ + * + * 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 "ndtxt.hxx" +#include "flyfrm.hxx" +#include "paratr.hxx" +#include "errhdl.hxx" +#include <vcl/outdev.hxx> +#include <svx/paravertalignitem.hxx> + +#include "pormulti.hxx" +#include <pagefrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <porfld.hxx> + +#include "txtcfg.hxx" +#include "itrtxt.hxx" +#include "txtfrm.hxx" +#include "porfly.hxx" + +#if OSL_DEBUG_LEVEL > 1 +# include "txtfrm.hxx" // GetFrmID, +#endif + +/************************************************************************* + * SwTxtIter::CtorInitTxtIter() + *************************************************************************/ + +void SwTxtIter::CtorInitTxtIter( SwTxtFrm *pNewFrm, SwTxtInfo *pNewInf ) +{ +#ifdef DBGTXT + // nStopAt laesst sich vom CV bearbeiten. + static MSHORT nStopAt = 0; + if( nStopAt == pNewFrm->GetFrmId() ) + { + int i = pNewFrm->GetFrmId(); + } +#endif + + SwTxtNode *pNode = pNewFrm->GetTxtNode(); + + ASSERT( pNewFrm->GetPara(), "No paragraph" ); + + CtorInitAttrIter( *pNode, pNewFrm->GetPara()->GetScriptInfo(), pNewFrm ); + + pFrm = pNewFrm; + pInf = pNewInf; + // --> OD 2008-01-17 #newlistlevelattrs# + aLineInf.CtorInitLineInfo( pNode->GetSwAttrSet(), *pNode ); + // <-- + nFrameStart = pFrm->Frm().Pos().Y() + pFrm->Prt().Pos().Y(); + SwTxtIter::Init(); + if( pNode->GetSwAttrSet().GetRegister().GetValue() ) + bRegisterOn = pFrm->FillRegister( nRegStart, nRegDiff ); + else + bRegisterOn = sal_False; +} + +/************************************************************************* + * SwTxtIter::Init() + *************************************************************************/ + +void SwTxtIter::Init() +{ + pCurr = pInf->GetParaPortion(); + nStart = pInf->GetTxtStart(); + nY = nFrameStart; + bPrev = sal_True; + pPrev = 0; + nLineNr = 1; +} + +/************************************************************************* + * SwTxtIter::_GetHeightAndAscent() + *************************************************************************/ + +void SwTxtIter::CalcAscentAndHeight( KSHORT &rAscent, KSHORT &rHeight ) const +{ + rHeight = GetLineHeight(); + rAscent = pCurr->GetAscent() + rHeight - pCurr->Height(); +} + +/************************************************************************* + * SwTxtIter::_GetPrev() + *************************************************************************/ + +SwLineLayout *SwTxtIter::_GetPrev() +{ + pPrev = 0; + bPrev = sal_True; + SwLineLayout *pLay = pInf->GetParaPortion(); + if( pCurr == pLay ) + return 0; + while( pLay->GetNext() != pCurr ) + pLay = pLay->GetNext(); + return pPrev = pLay; +} + +/************************************************************************* + * SwTxtIter::GetPrev() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::GetPrev() +{ + if(! bPrev) + _GetPrev(); + return pPrev; +} + +/************************************************************************* + * SwTxtIter::Prev() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::Prev() +{ + if( !bPrev ) + _GetPrev(); + if( pPrev ) + { + bPrev = sal_False; + pCurr = pPrev; + nStart = nStart - pCurr->GetLen(); + nY = nY - GetLineHeight(); + if( !pCurr->IsDummy() && !(--nLineNr) ) + ++nLineNr; + return pCurr; + } + else + return 0; +} + +/************************************************************************* + * SwTxtIter::Next() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::Next() +{ + if(pCurr->GetNext()) + { + pPrev = pCurr; + bPrev = sal_True; + nStart = nStart + pCurr->GetLen(); + nY += GetLineHeight(); + if( pCurr->GetLen() || ( nLineNr>1 && !pCurr->IsDummy() ) ) + ++nLineNr; + return pCurr = pCurr->GetNext(); + } + else + return 0; +} + +/************************************************************************* + * SwTxtIter::NextLine() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::NextLine() +{ + const SwLineLayout *pNext = Next(); + while( pNext && pNext->IsDummy() && pNext->GetNext() ) + { + DBG_LOOP; + pNext = Next(); + } + return pNext; +} + +/************************************************************************* + * SwTxtIter::GetNextLine() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::GetNextLine() const +{ + const SwLineLayout *pNext = pCurr->GetNext(); + while( pNext && pNext->IsDummy() && pNext->GetNext() ) + { + DBG_LOOP; + pNext = pNext->GetNext(); + } + return (SwLineLayout*)pNext; +} + +/************************************************************************* + * SwTxtIter::GetPrevLine() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::GetPrevLine() +{ + const SwLineLayout *pRoot = pInf->GetParaPortion(); + if( pRoot == pCurr ) + return 0; + const SwLineLayout *pLay = pRoot; + + while( pLay->GetNext() != pCurr ) + pLay = pLay->GetNext(); + + if( pLay->IsDummy() ) + { + const SwLineLayout *pTmp = pRoot; + pLay = pRoot->IsDummy() ? 0 : pRoot; + while( pTmp->GetNext() != pCurr ) + { + if( !pTmp->IsDummy() ) + pLay = pTmp; + pTmp = pTmp->GetNext(); + } + } + + // Wenn sich nichts getan hat, dann gibt es nur noch Dummys + return (SwLineLayout*)pLay; +} + +/************************************************************************* + * SwTxtIter::PrevLine() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::PrevLine() +{ + const SwLineLayout *pMyPrev = Prev(); + if( !pMyPrev ) + return 0; + + const SwLineLayout *pLast = pMyPrev; + while( pMyPrev && pMyPrev->IsDummy() ) + { + DBG_LOOP; + pLast = pMyPrev; + pMyPrev = Prev(); + } + return (SwLineLayout*)(pMyPrev ? pMyPrev : pLast); +} + +/************************************************************************* + * SwTxtIter::Bottom() + *************************************************************************/ + +void SwTxtIter::Bottom() +{ + while( Next() ) + { + DBG_LOOP; + } +} + +/************************************************************************* + * SwTxtIter::CharToLine() + *************************************************************************/ + +void SwTxtIter::CharToLine(const xub_StrLen nChar) +{ + while( nStart + pCurr->GetLen() <= nChar && Next() ) + ; + while( nStart > nChar && Prev() ) + ; +} + +/************************************************************************* + * SwTxtIter::CharCrsrToLine() + *************************************************************************/ + +// 1170: beruecksichtigt Mehrdeutigkeiten: +const SwLineLayout *SwTxtCursor::CharCrsrToLine( const xub_StrLen nPosition ) +{ + CharToLine( nPosition ); + if( nPosition != nStart ) + bRightMargin = sal_False; + sal_Bool bPrevious = bRightMargin && pCurr->GetLen() && GetPrev() && + GetPrev()->GetLen(); + if( bPrevious && nPosition && CH_BREAK == GetInfo().GetChar( nPosition-1 ) ) + bPrevious = sal_False; + return bPrevious ? PrevLine() : pCurr; +} + +/************************************************************************* + * SwTxtCrsr::AdjustBaseLine() + *************************************************************************/ + +USHORT SwTxtCursor::AdjustBaseLine( const SwLineLayout& rLine, + const SwLinePortion* pPor, + USHORT nPorHeight, USHORT nPorAscent, + const sal_Bool bAutoToCentered ) const +{ + if ( pPor ) + { + nPorHeight = pPor->Height(); + nPorAscent = pPor->GetAscent(); + } + + USHORT nOfst = rLine.GetRealHeight() - rLine.Height(); + + GETGRID( pFrm->FindPageFrm() ) + const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid(); + + if ( bHasGrid ) + { + const USHORT nRubyHeight = pGrid->GetRubyHeight(); + const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow(); + + if ( GetInfo().IsMulti() ) + // we are inside the GetCharRect recursion for multi portions + // we center the portion in its surrounding line + nOfst = ( pCurr->Height() - nPorHeight ) / 2 + nPorAscent; + else + { + // We have to take care for ruby portions. + // The ruby portion is NOT centered + nOfst = nOfst + nPorAscent; + + if ( ! pPor || ! pPor->IsMultiPortion() || + ! ((SwMultiPortion*)pPor)->IsRuby() ) + { + // Portions which are bigger than on grid distance are + // centered inside the whole line. + + //for text refactor + const USHORT nLineNetto = rLine.Height() - nRubyHeight; + //const USHORT nLineNetto = ( nPorHeight > nGridWidth ) ? + // rLine.Height() - nRubyHeight : + // nGridWidth; + nOfst += ( nLineNetto - nPorHeight ) / 2; + if ( bRubyTop ) + nOfst = nOfst + nRubyHeight; + } + } + } + else + { + switch ( GetLineInfo().GetVertAlign() ) { + case SvxParaVertAlignItem::TOP : + nOfst = nOfst + nPorAscent; + break; + case SvxParaVertAlignItem::CENTER : + ASSERT( rLine.Height() >= nPorHeight, "Portion height > Line height"); + nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent; + break; + case SvxParaVertAlignItem::BOTTOM : + nOfst += rLine.Height() - nPorHeight + nPorAscent; + break; + case SvxParaVertAlignItem::AUTOMATIC : + if ( bAutoToCentered || GetInfo().GetTxtFrm()->IsVertical() ) + { + nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent; + break; + } + case SvxParaVertAlignItem::BASELINE : + // base line + nOfst = nOfst + rLine.GetAscent(); + break; + } + } + + return nOfst; +} + +/************************************************************************* + * SwTxtIter::TwipsToLine() + *************************************************************************/ + +const SwLineLayout *SwTxtIter::TwipsToLine( const SwTwips y) +{ + while( nY + GetLineHeight() <= y && Next() ) + ; + while( nY > y && Prev() ) + ; + return pCurr; +} + +// +// Local helper function to check, if pCurr needs a field rest portion: +// +sal_Bool lcl_NeedsFieldRest( const SwLineLayout* pCurr ) +{ + const SwLinePortion *pPor = pCurr->GetPortion(); + sal_Bool bRet = sal_False; + while( pPor && !bRet ) + { + bRet = pPor->InFldGrp() && ((SwFldPortion*)pPor)->HasFollow(); + if( !pPor->GetPortion() || !pPor->GetPortion()->InFldGrp() ) + break; + pPor = pPor->GetPortion(); + } + return bRet; +} + +/************************************************************************* + * SwTxtIter::TruncLines() + *************************************************************************/ + +void SwTxtIter::TruncLines( sal_Bool bNoteFollow ) +{ + SwLineLayout *pDel = pCurr->GetNext(); + const xub_StrLen nEnd = nStart + pCurr->GetLen(); + + if( pDel ) + { + pCurr->SetNext( 0 ); + if( GetHints() && bNoteFollow ) + { + GetInfo().GetParaPortion()->SetFollowField( pDel->IsRest() || + lcl_NeedsFieldRest( pCurr ) ); + + // bug 88534: wrong positioning of flys + SwTxtFrm* pFollow = GetTxtFrm()->GetFollow(); + if ( pFollow && ! pFollow->IsLocked() && + nEnd == pFollow->GetOfst() ) + { + xub_StrLen nRangeEnd = nEnd; + SwLineLayout* pLine = pDel; + + // determine range to be searched for flys anchored as characters + while ( pLine ) + { + nRangeEnd = nRangeEnd + pLine->GetLen(); + pLine = pLine->GetNext(); + } + + SwpHints* pTmpHints = GetTxtFrm()->GetTxtNode()->GetpSwpHints(); + + // examine hints in range nEnd - (nEnd + nRangeChar) + for( USHORT i = 0; i < pTmpHints->Count(); i++ ) + { + const SwTxtAttr* pHt = pTmpHints->GetTextHint( i ); + if( RES_TXTATR_FLYCNT == pHt->Which() ) + { + // check, if hint is in our range + const USHORT nTmpPos = *pHt->GetStart(); + if ( nEnd <= nTmpPos && nTmpPos < nRangeEnd ) + pFollow->_InvalidateRange( + SwCharRange( nTmpPos, nTmpPos ), 0 ); + } + } + } + } + delete pDel; + } + if( pCurr->IsDummy() && + !pCurr->GetLen() && + nStart < GetTxtFrm()->GetTxt().Len() ) + pCurr->SetRealHeight( 1 ); + if( GetHints() ) + pFrm->RemoveFtn( nEnd ); +} + +/************************************************************************* + * SwTxtIter::CntHyphens() + *************************************************************************/ + +void SwTxtIter::CntHyphens( sal_uInt8 &nEndCnt, sal_uInt8 &nMidCnt) const +{ + nEndCnt = 0; + nMidCnt = 0; + if ( bPrev && pPrev && !pPrev->IsEndHyph() && !pPrev->IsMidHyph() ) + return; + SwLineLayout *pLay = pInf->GetParaPortion(); + if( pCurr == pLay ) + return; + while( pLay != pCurr ) + { + DBG_LOOP; + if ( pLay->IsEndHyph() ) + nEndCnt++; + else + nEndCnt = 0; + if ( pLay->IsMidHyph() ) + nMidCnt++; + else + nMidCnt = 0; + pLay = pLay->GetNext(); + } +} + +/************************************************************************* + * SwHookOut + * + * Change current output device to formatting device, this has to be done before + * formatting. + *************************************************************************/ + +SwHookOut::SwHookOut( SwTxtSizeInfo& rInfo ) : + pInf( &rInfo ), + pOut( rInfo.GetOut() ), + bOnWin( rInfo.OnWin() ) +{ + ASSERT( rInfo.GetRefDev(), "No reference device for text formatting" ) + + // set new values + rInfo.SetOut( rInfo.GetRefDev() ); + rInfo.SetOnWin( sal_False ); +} + +SwHookOut::~SwHookOut() +{ + pInf->SetOut( pOut ); + pInf->SetOnWin( bOnWin ); +} diff --git a/sw/source/core/text/itrtxt.hxx b/sw/source/core/text/itrtxt.hxx new file mode 100644 index 000000000000..58e77479f51e --- /dev/null +++ b/sw/source/core/text/itrtxt.hxx @@ -0,0 +1,343 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: itrtxt.hxx,v $ + * $Revision: 1.21.40.3 $ + * + * 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. + * + ************************************************************************/ +#ifndef _ITRTXT_HXX +#define _ITRTXT_HXX +#include "swtypes.hxx" +#include "itratr.hxx" +#include "inftxt.hxx" + +class SwTxtFrm; +struct SwPosition; +struct SwCrsrMoveState; +class SwMarginPortion; +class SwFlyPortion; + +/************************************************************************* + * class SwTxtIter + *************************************************************************/ + +class SwTxtIter : public SwAttrIter +{ +protected: + SwLineInfo aLineInf; + SwTxtFrm *pFrm; + SwTxtInfo *pInf; + SwLineLayout *pCurr; + SwLineLayout *pPrev; + SwTwips nFrameStart; + SwTwips nY; + SwTwips nRegStart; // Anfangsposition (Y) des Registers + xub_StrLen nStart; // Start im Textstring, Ende = pCurr->GetLen() + KSHORT nRegDiff; // Zeilenabstand des Registers + MSHORT nLineNr; // Zeilennummer + sal_Bool bPrev : 1; + sal_Bool bRegisterOn : 1; // Registerhaltigkeit + sal_Bool bOneBlock : 1; // Blocksatz: Einzelwoerter austreiben + sal_Bool bLastBlock : 1; // Blocksatz: Auch die letzte Zeile + sal_Bool bLastCenter : 1; // Blocksatz: Letzte Zeile zentrieren + + SwLineLayout *_GetPrev(); + + // Zuruecksetzen in die erste Zeile. + void Init(); + void CtorInitTxtIter( SwTxtFrm *pFrm, SwTxtInfo *pInf ); + inline SwTxtIter(SwTxtNode* pTxtNode) : SwAttrIter(pTxtNode) { } + +public: + inline SwTxtIter( SwTxtFrm *pTxtFrm, SwTxtInfo *pTxtInf ) : SwAttrIter(pTxtFrm!=NULL?pTxtFrm->GetTxtNode():NULL) + { CtorInitTxtIter( pTxtFrm, pTxtInf ); } + inline const SwLineLayout *GetCurr() const { return pCurr; } // niemals 0! + inline const SwLineLayout *GetNext() const { return pCurr->GetNext(); } + const SwLineLayout *GetPrev(); + inline xub_StrLen GetLength() const { return pCurr->GetLen(); } + inline MSHORT GetLineNr() const { return nLineNr; } + inline xub_StrLen GetStart() const { return nStart; } + inline xub_StrLen GetEnd() const { return GetStart() + GetLength(); } + inline SwTwips Y() const { return nY; } + + inline SwTwips RegStart() const { return nRegStart; } + inline KSHORT RegDiff() const { return nRegDiff; } + inline sal_Bool IsRegisterOn() const { return bRegisterOn; } + + inline SwTxtInfo &GetInfo() { return *pInf; } + inline const SwTxtInfo &GetInfo() const { return *pInf; } + + inline void Top() { Init(); } + void Bottom(); + const SwLineLayout *Next(); + const SwLineLayout *Prev(); + + // Ueberspringt die Dummyzeilen der FlyFrms + const SwLineLayout *NextLine(); + const SwLineLayout *PrevLine(); + const SwLineLayout *GetNextLine() const; + const SwLineLayout *GetPrevLine(); + + void CharToLine( const xub_StrLen ); + const SwLineLayout *TwipsToLine(const SwTwips); + + // schneidet ab pCurr alle ab. + void TruncLines( sal_Bool bNoteFollow = sal_False ); + + inline KSHORT GetLineHeight() const { return pCurr->GetRealHeight(); } + void CalcAscentAndHeight( KSHORT &rAscent, KSHORT &rHeight ) const; + + // 5298, viel Aerger durch die Abfrage auf pCurr == pPara + inline sal_Bool IsFirstTxtLine() const + { return nStart == GetInfo().GetTxtStart() && + !( pCurr->IsDummy() && GetNextLine() ); } + + // Als Ersatz fuer das alte IsFirstLine() + inline sal_Bool IsParaLine() const + { return pCurr == pInf->GetParaPortion(); } + + const SwLineInfo &GetLineInfo() const { return aLineInf; } + inline SwTwips GetFirstPos() const { return nFrameStart; } + inline sal_Bool SeekAndChg( SwTxtSizeInfo &rInf ); + inline sal_Bool SeekAndChgBefore( SwTxtSizeInfo &rInf ); + inline sal_Bool SeekStartAndChg( SwTxtSizeInfo &rInf, const sal_Bool bPara=sal_False ); + + inline SwTxtFrm *GetTxtFrm() { return pFrm; } + inline const SwTxtFrm *GetTxtFrm() const { return pFrm; } + + // zaehlt aufeinanderfolgende Trennungen, um MaxHyphens einzuhalten + void CntHyphens( sal_uInt8 &nEndCnt, sal_uInt8 &nMidCnt) const; +}; + +/************************************************************************* + * class SwTxtMargin + *************************************************************************/ + +class SwTxtMargin : public SwTxtIter +{ +private: + SwTwips nLeft; + SwTwips nRight; + SwTwips nFirst; + KSHORT nDropLeft; + KSHORT nDropHeight; + KSHORT nDropDescent; + MSHORT nDropLines; + MSHORT nAdjust; + // --> OD 2008-06-30 #i91133# + SwTwips mnTabLeft; + // <-- + +protected: + // fuer FormatQuoVadis + inline void Right( const SwTwips nNew ) { nRight = nNew; } + // fuer CalcFlyAdjust + inline void SetDropLeft( const KSHORT nNew ) { nDropLeft = nNew; } + + void CtorInitTxtMargin( SwTxtFrm *pFrm, SwTxtSizeInfo *pInf ); + inline SwTxtMargin(SwTxtNode* pTxtNode) : SwTxtIter(pTxtNode) { } +public: + inline SwTxtMargin( SwTxtFrm *pTxtFrm, SwTxtSizeInfo *pTxtSizeInf ) : SwTxtIter(pTxtFrm!=NULL?pTxtFrm->GetTxtNode():NULL) + { CtorInitTxtMargin( pTxtFrm, pTxtSizeInf ); } + inline SwTwips GetLeftMargin() const; + inline SwTwips Left() const; + inline SwTwips Right() const { return nRight; } + inline SwTwips FirstLeft() const { return nFirst; } + inline SwTwips CurrWidth() const { return pCurr->PrtWidth(); } + SwTwips GetLineStart() const; + inline SwTwips GetLineEnd() const { return GetLineStart() + CurrWidth(); } + inline Point GetTopLeft() const { return Point( GetLineStart(), Y() ); } + inline sal_Bool IsOneBlock() const { return bOneBlock; } + inline sal_Bool IsLastBlock() const { return bLastBlock; } + inline sal_Bool IsLastCenter() const { return bLastCenter; } + inline MSHORT GetAdjust() const { return nAdjust; } + inline KSHORT GetLineWidth() const + { return KSHORT( Right() - GetLeftMargin() + 1 ); } + inline SwTwips GetLeftMin() const { return nFirst < nLeft ? nFirst : nLeft; } + inline sal_Bool HasNegFirst() const { return nFirst < nLeft; } + + // --> OD 2008-06-30 #i91133# + inline SwTwips GetTabLeft() const + { + return mnTabLeft; + } + // <-- + // DropCaps + inline MSHORT GetDropLines() const { return nDropLines; } + inline void SetDropLines( const MSHORT nNew ) { nDropLines = nNew; } + inline KSHORT GetDropLeft() const { return nDropLeft; } + inline KSHORT GetDropHeight() const { return nDropHeight; } + inline void SetDropHeight( const KSHORT nNew ) { nDropHeight = nNew; } + inline KSHORT GetDropDescent() const { return nDropDescent; } + inline void SetDropDescent( const KSHORT nNew ) { nDropDescent = nNew; } + void DropInit(); + + // liefert TxtPos fuer Start und Ende der aktuellen Zeile ohne whitespaces + // In frminf.cxx implementiert. + xub_StrLen GetTxtStart() const; + xub_StrLen GetTxtEnd() const; + + inline SwTxtSizeInfo &GetInfo() + { return (SwTxtSizeInfo&)SwTxtIter::GetInfo(); } + inline const SwTxtSizeInfo &GetInfo() const + { return (const SwTxtSizeInfo&)SwTxtIter::GetInfo(); } + +}; + + +/************************************************************************* + * class SwTxtAdjuster + *************************************************************************/ + +class SwTxtAdjuster : public SwTxtMargin +{ + // Gleicht die Portions aus, wenn Adjustment und FlyFrms vorliegen. + void CalcFlyAdjust( SwLineLayout *pCurr ); + + // ruft SplitGlues und CalcBlockAdjust + void FormatBlock( ); + + // Erstellt bei kurzen Zeilen die Glue-Kette. + SwMarginPortion* CalcRightMargin( SwLineLayout *pCurr, SwTwips nReal = 0 ); + + // Berechnung des Adjustments (FlyPortions) + SwFlyPortion *CalcFlyPortion( const long nRealWidth, + const SwRect &rCurrRect ); + +protected: + inline SwTxtAdjuster(SwTxtNode* pTxtNode) : SwTxtMargin(pTxtNode) { } + // spannt beim Blocksatz die Glues auf. + void CalcNewBlock( SwLineLayout *pCurr, const SwLinePortion *pStopAt, + SwTwips nReal = 0, bool bSkipKashida = false ); + SwTwips CalcKanaAdj( SwLineLayout *pCurr ); +public: + inline SwTxtAdjuster( SwTxtFrm *pTxtFrm, SwTxtSizeInfo *pTxtSizeInf ) : SwTxtMargin(pTxtFrm!=NULL?pTxtFrm->GetTxtNode():NULL) + { CtorInitTxtMargin( pTxtFrm, pTxtSizeInf ); } + + // wird von SwTxtFormatter wegen UpdatePos ueberladen + void CalcAdjLine( SwLineLayout *pCurr ); + + // sorgt fuer das nachtraegliche adjustieren + inline void GetAdjusted() const + { + if( pCurr->IsFormatAdj() ) + ((SwTxtAdjuster*)this)->CalcAdjLine( pCurr ); + } + + // DropCaps-Extrawurst + void CalcDropAdjust(); + void CalcDropRepaint(); +}; + +/************************************************************************* + * class SwTxtCursor + *************************************************************************/ + +class SwTxtCursor : public SwTxtAdjuster +{ + // A small helper-class to save SwTxtCursor member, manipulate them + // and to restore them + friend class SwTxtCursorSave; + + // 1170: Mehrdeutigkeiten + static sal_Bool bRightMargin; + void _GetCharRect(SwRect *, const xub_StrLen, SwCrsrMoveState* ); +protected: + void CtorInitTxtCursor( SwTxtFrm *pFrm, SwTxtSizeInfo *pInf ); + inline SwTxtCursor(SwTxtNode* pTxtNode) : SwTxtAdjuster(pTxtNode) { } +public: + inline SwTxtCursor( SwTxtFrm *pTxtFrm, SwTxtSizeInfo *pTxtSizeInf ) : SwTxtAdjuster(pTxtFrm!=NULL?pTxtFrm->GetTxtNode():NULL) + { CtorInitTxtCursor( pTxtFrm, pTxtSizeInf ); } + sal_Bool GetCharRect(SwRect *, const xub_StrLen, SwCrsrMoveState* = 0, + const long nMax = 0 ); + sal_Bool GetEndCharRect(SwRect *, const xub_StrLen, SwCrsrMoveState* = 0, + const long nMax = 0 ); + xub_StrLen GetCrsrOfst( SwPosition *pPos, const Point &rPoint, + const MSHORT nChgNode, SwCrsrMoveState* = 0 ) const; + // 1170: beruecksichtigt Mehrdeutigkeiten; Implementierung s.u. + const SwLineLayout *CharCrsrToLine( const xub_StrLen nPos ); + + // calculates baseline for portion rPor + // bAutoToCentered indicates, if AUTOMATIC mode means CENTERED or BASELINE + USHORT AdjustBaseLine( const SwLineLayout& rLine, const SwLinePortion* pPor, + USHORT nPorHeight = 0, USHORT nAscent = 0, + const sal_Bool bAutoToCentered = sal_False ) const; + + static inline void SetRightMargin( const sal_Bool bNew ){ bRightMargin = bNew; } + static inline sal_Bool IsRightMargin() { return bRightMargin; } +}; + +/************************************************************************* + * SwHookOut + * + * Change current output device to printer, this has to be done before + * formatting. + *************************************************************************/ + +class SwHookOut +{ + SwTxtSizeInfo* pInf; + OutputDevice* pOut; + sal_Bool bOnWin; +public: + SwHookOut( SwTxtSizeInfo& rInfo ); + ~SwHookOut(); +}; + +/************************************************************************* + * Inline-Implementierungen + *************************************************************************/ + +inline sal_Bool SwTxtIter::SeekAndChg( SwTxtSizeInfo &rInf ) +{ + return SeekAndChgAttrIter( rInf.GetIdx(), rInf.GetOut() ); +} + +inline sal_Bool SwTxtIter::SeekAndChgBefore( SwTxtSizeInfo &rInf ) +{ + if ( rInf.GetIdx() ) + return SeekAndChgAttrIter( rInf.GetIdx()-1, rInf.GetOut() ); + else + return SeekAndChgAttrIter( rInf.GetIdx(), rInf.GetOut() ); +} + +inline sal_Bool SwTxtIter::SeekStartAndChg( SwTxtSizeInfo &rInf, const sal_Bool bPara ) +{ + return SeekStartAndChgAttrIter( rInf.GetOut(), bPara ); +} + +inline SwTwips SwTxtMargin::GetLeftMargin() const +{ + return IsFirstTxtLine() ? nFirst : Left(); +} + +inline SwTwips SwTxtMargin::Left() const +{ + return (nDropLines >= nLineNr && 1 != nLineNr) ? nFirst + nDropLeft : nLeft; +} + + + +#endif diff --git a/sw/source/core/text/makefile.mk b/sw/source/core/text/makefile.mk new file mode 100644 index 000000000000..fbc000002ee1 --- /dev/null +++ b/sw/source/core/text/makefile.mk @@ -0,0 +1,130 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.16 $ +# +# 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=text + +AUTOSEG=true + +# --- Settings ----------------------------------------------------- + +.INCLUDE : $(PRJ)$/inc$/swpre.mk +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/inc$/sw.mk + +.IF "$(mydebug)" != "" +CDEFS+=-Dmydebug +.ENDIF + +.IF "$(ENABLE_GRAPHITE)" == "TRUE" +CFLAGS+=-DENABLE_GRAPHITE +.ENDIF +# --- Files -------------------------------------------------------- + +.IF "$(product)$(cap)" == "" +CXXFILES += \ + txtio.cxx +.ENDIF + + + +SLOFILES = \ + $(SLO)$/atrstck.obj \ + $(SLO)$/EnhancedPDFExportHelper.obj \ + $(SLO)$/frmcrsr.obj \ + $(SLO)$/frmform.obj \ + $(SLO)$/frminf.obj \ + $(SLO)$/frmpaint.obj \ + $(SLO)$/guess.obj \ + $(SLO)$/inftxt.obj \ + $(SLO)$/itradj.obj \ + $(SLO)$/itratr.obj \ + $(SLO)$/itrcrsr.obj \ + $(SLO)$/itrform2.obj \ + $(SLO)$/itrpaint.obj \ + $(SLO)$/itrtxt.obj \ + $(SLO)$/porexp.obj \ + $(SLO)$/porfld.obj \ + $(SLO)$/porfly.obj \ + $(SLO)$/porglue.obj \ + $(SLO)$/porlay.obj \ + $(SLO)$/porlin.obj \ + $(SLO)$/pormulti.obj \ + $(SLO)$/porref.obj \ + $(SLO)$/porrst.obj \ + $(SLO)$/portox.obj \ + $(SLO)$/portxt.obj \ + $(SLO)$/redlnitr.obj \ + $(SLO)$/txtcache.obj \ + $(SLO)$/txtdrop.obj \ + $(SLO)$/txtfld.obj \ + $(SLO)$/txtfly.obj \ + $(SLO)$/txtfrm.obj \ + $(SLO)$/txtftn.obj \ + $(SLO)$/txthyph.obj \ + $(SLO)$/txtinit.obj \ + $(SLO)$/txtpaint.obj \ + $(SLO)$/txttab.obj \ + $(SLO)$/widorp.obj \ + $(SLO)$/blink.obj \ + $(SLO)$/noteurl.obj \ + $(SLO)$/SwGrammarMarkUp.obj \ + $(SLO)$/wrong.obj + +.IF "$(product)$(cap)" == "" +SLOFILES += \ + $(SLO)$/txtio.obj +.ENDIF + +EXCEPTIONSFILES = \ + $(SLO)$/EnhancedPDFExportHelper.obj \ + $(SLO)$/inftxt.obj \ + $(SLO)$/itradj.obj \ + $(SLO)$/itrcrsr.obj \ + $(SLO)$/porlay.obj \ + $(SLO)$/pormulti.obj \ + $(SLO)$/SwGrammarMarkUp.obj \ + $(SLO)$/txtfly.obj \ + $(SLO)$/wrong.obj + + +.IF "$(CPUNAME)" == "SPARC" +.IF "$(OS)" == "NETBSD" +NOOPTFILES = \ + $(SLO)$/txtftn.obj +.ENDIF +.ENDIF + +# --- Tagets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/sw/source/core/text/noteurl.cxx b/sw/source/core/text/noteurl.cxx new file mode 100644 index 000000000000..ce8312c58908 --- /dev/null +++ b/sw/source/core/text/noteurl.cxx @@ -0,0 +1,90 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: noteurl.cxx,v $ + * $Revision: 1.6 $ + * + * 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 "swtypes.hxx" +#include <vcl/outdev.hxx> +#include <svtools/imaprect.hxx> +#include <svtools/imap.hxx> + +#include "txttypes.hxx" +#include "noteurl.hxx" + +// globale Variable, wird in noteurl.Hxx bekanntgegeben +SwNoteURL *pNoteURL = NULL; + +SV_IMPL_PTRARR( SwURLNoteList, SwURLNotePtr ) + + +void SwNoteURL::InsertURLNote( const XubString& rURL, const XubString& rTarget, + const SwRect& rRect ) +{ + MSHORT i; + MSHORT nCount = aList.Count(); + for( i = 0; i < nCount; i++ ) + if( rRect == aList.GetObject(i)->GetRect() ) + break; + if( i == nCount ) + { + SwURLNote *pNew = new SwURLNote( rURL, rTarget, rRect ); + aList.Insert( pNew, nCount ); + } +} + + +void SwNoteURL::FillImageMap( ImageMap *pMap, const Point &rPos, + const MapMode& rMap ) +{ + ASSERT( pMap, "FillImageMap: No ImageMap, no cookies!" ); + MSHORT nCount = Count(); + if( nCount ) + { + MapMode aMap( MAP_100TH_MM ); + for( MSHORT i = 0; i < nCount; ++i ) + { + const SwURLNote &rNote = GetURLNote( i ); + SwRect aSwRect( rNote.GetRect() ); + aSwRect -= rPos; + Rectangle aRect( OutputDevice::LogicToLogic( aSwRect.SVRect(), + rMap, aMap ) ); + IMapRectangleObject aObj( aRect, rNote.GetURL(), aEmptyStr, aEmptyStr, + rNote.GetTarget(), aEmptyStr, sal_True, sal_False ); + pMap->InsertIMapObject( aObj ); + } + } +} + + + + diff --git a/sw/source/core/text/pordrop.hxx b/sw/source/core/text/pordrop.hxx new file mode 100644 index 000000000000..26e95a42900c --- /dev/null +++ b/sw/source/core/text/pordrop.hxx @@ -0,0 +1,122 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pordrop.hxx,v $ + * $Revision: 1.7 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORDROP_HXX +#define _PORDROP_HXX + +#include "portxt.hxx" + +class SwFont; + +// DropCap-Cache, globale Variable, in txtinit.cxx initialisiert/zerstoert +// und in txtdrop.cxx benutzt bei der Initialenberechnung + +class SwDropCapCache; +extern SwDropCapCache *pDropCapCache; + +/************************************************************************* + * class SwDropPortionPart + * + * A drop portion can consist of one or more parts in order to allow + * attribute changes inside them. + *************************************************************************/ + +class SwDropPortionPart +{ + SwDropPortionPart* pFollow; + SwFont* pFnt; + xub_StrLen nLen; + USHORT nWidth; + +public: + SwDropPortionPart( SwFont& rFont, const xub_StrLen nL ) + : pFollow( 0 ), pFnt( &rFont ), nLen( nL ), nWidth( 0 ) {}; + ~SwDropPortionPart(); + + inline SwDropPortionPart* GetFollow() const { return pFollow; }; + inline void SetFollow( SwDropPortionPart* pNew ) { pFollow = pNew; }; + inline SwFont& GetFont() const { return *pFnt; } + inline xub_StrLen GetLen() const { return nLen; } + inline USHORT GetWidth() const { return nWidth; } + inline void SetWidth( USHORT nNew ) { nWidth = nNew; } +}; + +/************************************************************************* + * class SwDropPortion + *************************************************************************/ + +class SwDropPortion : public SwTxtPortion +{ + friend class SwDropCapCache; + SwDropPortionPart* pPart; // due to script / attribute changes + MSHORT nLines; // Anzahl der Zeilen + KSHORT nDropHeight; // Hoehe + KSHORT nDropDescent; // Abstand zur naechsten Zeile + KSHORT nDistance; // Abstand zum Text + KSHORT nFix; // Fixposition + short nX; // X-PaintOffset + short nY; // Y-Offset + + sal_Bool FormatTxt( SwTxtFormatInfo &rInf ); + void PaintTxt( const SwTxtPaintInfo &rInf ) const; + + inline void Fix( const KSHORT nNew ) { nFix = nNew; } +public: + SwDropPortion( const MSHORT nLineCnt, + const KSHORT nDropHeight, + const KSHORT nDropDescent, + const KSHORT nDistance ); + virtual ~SwDropPortion(); + + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + void PaintDrop( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + virtual xub_StrLen GetCrsrOfst( const MSHORT nOfst ) const; + + inline MSHORT GetLines() const { return nLines; } + inline KSHORT GetDistance() const { return nDistance; } + inline KSHORT GetDropHeight() const { return nDropHeight; } + inline KSHORT GetDropDescent() const { return nDropDescent; } + inline KSHORT GetDropLeft() const { return Width() + nFix; } + + inline SwDropPortionPart* GetPart() const { return pPart; } + inline void SetPart( SwDropPortionPart* pNew ) { pPart = pNew; } + + inline void SetY( short nNew ) { nY = nNew; } + + inline SwFont* GetFnt() const { return pPart ? &pPart->GetFont() : NULL; } + + static void DeleteDropCapCache(); + + OUTPUT_OPERATOR +}; + + +#endif diff --git a/sw/source/core/text/porexp.cxx b/sw/source/core/text/porexp.cxx new file mode 100644 index 000000000000..51563d1bdf6c --- /dev/null +++ b/sw/source/core/text/porexp.cxx @@ -0,0 +1,315 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porexp.cxx,v $ + * $Revision: 1.19 $ + * + * 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 <viewopt.hxx> // SwViewOptions +#include <SwPortionHandler.hxx> +#include <inftxt.hxx> +#include <porexp.hxx> + +/************************************************************************* + * class SwExpandPortion + *************************************************************************/ + +xub_StrLen SwExpandPortion::GetCrsrOfst( const MSHORT nOfst ) const +{ return SwLinePortion::GetCrsrOfst( nOfst ); } + +/************************************************************************* + * virtual SwExpandPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwExpandPortion::GetExpTxt( const SwTxtSizeInfo&, + XubString &rTxt ) const +{ + rTxt.Erase(); + // Nicht etwa: return 0 != rTxt.Len(); + // Weil: leere Felder ersetzen CH_TXTATR gegen einen Leerstring + return sal_True; +} + +/************************************************************************* + * virtual SwExpandPortion::HandlePortion() + *************************************************************************/ + +void SwExpandPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString; + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + +/************************************************************************* + * virtual SwExpandPortion::GetTxtSize() + *************************************************************************/ + +SwPosSize SwExpandPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const +{ + SwTxtSlot aDiffTxt( &rInf, this, false, false ); + return rInf.GetTxtSize(); +} + +/************************************************************************* + * virtual SwExpandPortion::Format() + *************************************************************************/ + +// 5010: Exp und Tabs + +sal_Bool SwExpandPortion::Format( SwTxtFormatInfo &rInf ) +{ + SwTxtSlot aDiffTxt( &rInf, this, true, false ); + const xub_StrLen nFullLen = rInf.GetLen(); + + // So komisch es aussieht, die Abfrage auf GetLen() muss wegen der + // ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs) + // sal_False returnen wegen SetFull ... + if( !nFullLen ) + { + // nicht Init(), weil wir Hoehe und Ascent brauchen + Width(0); + return sal_False; + } + return SwTxtPortion::Format( rInf ); +} + +/************************************************************************* + * virtual SwExpandPortion::Paint() + *************************************************************************/ + +void SwExpandPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + SwTxtSlot aDiffTxt( &rInf, this, true, true ); + + rInf.DrawBackBrush( *this ); + + // do we have to repaint a post it portion? + if( rInf.OnWin() && pPortion && !pPortion->Width() ) + pPortion->PrePaint( rInf, this ); + + // The contents of field portions is not considered during the + // calculation of the directions. Therefore we let vcl handle + // the calculation by removing the BIDI_STRONG_FLAG temporarily. + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + aLayoutModeModifier.SetAuto(); + + // ST2 + if ( rInf.GetSmartTags() || rInf.GetGrammarCheckList() ) + rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, sal_False, + 0 != rInf.GetSmartTags(), 0 != rInf.GetGrammarCheckList() ); + else + rInf.DrawText( *this, rInf.GetLen(), sal_False ); +} + +/************************************************************************* + * class SwBlankPortion + *************************************************************************/ + +SwLinePortion *SwBlankPortion::Compress() { return this; } + +/************************************************************************* + * SwBlankPortion::MayUnderFlow() + *************************************************************************/ + +// 5497: Es gibt schon Gemeinheiten auf der Welt... +// Wenn eine Zeile voll mit HardBlanks ist und diese ueberlaeuft, +// dann duerfen keine Underflows generiert werden! +// Komplikationen bei Flys... + +MSHORT SwBlankPortion::MayUnderFlow( const SwTxtFormatInfo &rInf, + xub_StrLen nIdx, sal_Bool bUnderFlow ) const +{ + if( rInf.StopUnderFlow() ) + return 0; + const SwLinePortion *pPos = rInf.GetRoot(); + if( pPos->GetPortion() ) + pPos = pPos->GetPortion(); + while( pPos && pPos->IsBlankPortion() ) + pPos = pPos->GetPortion(); + if( !pPos || !rInf.GetIdx() || ( !pPos->GetLen() && pPos == rInf.GetRoot() ) ) + return 0; // Nur noch BlankPortions unterwegs + // Wenn vor uns ein Blank ist, brauchen wir kein Underflow ausloesen, + // wenn hinter uns ein Blank ist, brauchen wir kein Underflow weiterreichen + if( bUnderFlow && CH_BLANK == rInf.GetTxt().GetChar( nIdx + 1) ) + return 0; + if( nIdx && !((SwTxtFormatInfo&)rInf).GetFly() ) + { + while( pPos && !pPos->IsFlyPortion() ) + pPos = pPos->GetPortion(); + if( !pPos ) + { + //Hier wird ueberprueft, ob es in dieser Zeile noch sinnvolle Umbrueche + //gibt, Blanks oder Felder etc., wenn nicht, kein Underflow. + //Wenn Flys im Spiel sind, lassen wir das Underflow trotzdem zu. + xub_StrLen nBlank = nIdx; + while( --nBlank > rInf.GetLineStart() ) + { + const xub_Unicode cCh = rInf.GetChar( nBlank ); + if( CH_BLANK == cCh || + (( CH_TXTATR_BREAKWORD == cCh || CH_TXTATR_INWORD == cCh ) + && rInf.HasHint( nBlank ) ) ) + break; + } + if( nBlank <= rInf.GetLineStart() ) + return 0; + } + } + xub_Unicode cCh; + if( nIdx < 2 || CH_BLANK == (cCh = rInf.GetChar( nIdx - 1 )) ) + return 1; + if( CH_BREAK == cCh ) + return 0; + return 2; +} + +/************************************************************************* + * virtual SwBlankPortion::FormatEOL() + *************************************************************************/ +// Format end of Line + +void SwBlankPortion::FormatEOL( SwTxtFormatInfo &rInf ) +{ + MSHORT nMay = MayUnderFlow( rInf, rInf.GetIdx() - nLineLength, sal_True ); + if( nMay ) + { + if( nMay > 1 ) + { + if( rInf.GetLast() == this ) + rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) ); + rInf.X( rInf.X() - PrtWidth() ); + rInf.SetIdx( rInf.GetIdx() - GetLen() ); + } + Truncate(); + rInf.SetUnderFlow( this ); + if( rInf.GetLast()->IsKernPortion() ) + rInf.SetUnderFlow( rInf.GetLast() ); + } +} + +/************************************************************************* + * virtual SwBlankPortion::Format() + *************************************************************************/ + +// 7771: UnderFlows weiterreichen und selbst ausloesen! +sal_Bool SwBlankPortion::Format( SwTxtFormatInfo &rInf ) +{ + const sal_Bool bFull = rInf.IsUnderFlow() || SwExpandPortion::Format( rInf ); + if( bFull && MayUnderFlow( rInf, rInf.GetIdx(), rInf.IsUnderFlow() ) ) + { + Truncate(); + rInf.SetUnderFlow( this ); + if( rInf.GetLast()->IsKernPortion() ) + rInf.SetUnderFlow( rInf.GetLast() ); + } + return bFull; +} + +/************************************************************************* + * virtual SwBlankPortion::Paint() + *************************************************************************/ + +void SwBlankPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( !bMulti ) // No gray background for multiportion brackets + rInf.DrawViewOpt( *this, POR_BLANK ); + SwExpandPortion::Paint( rInf ); +} + +/************************************************************************* + * virtual SwBlankPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwBlankPortion::GetExpTxt( const SwTxtSizeInfo&, XubString &rTxt ) const +{ + rTxt = cChar; + return sal_True; +} + +/************************************************************************* + * virtual SwBlankPortion::HandlePortion() + *************************************************************************/ + +void SwBlankPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString( cChar ); + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + +/************************************************************************* + * class SwPostItsPortion + *************************************************************************/ + +SwPostItsPortion::SwPostItsPortion( sal_Bool bScrpt ) + : nViewWidth(0), bScript( bScrpt ) +{ + nLineLength = 1; + SetWhichPor( POR_POSTITS ); +} + +void SwPostItsPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( rInf.OnWin() && Width() ) + rInf.DrawPostIts( *this, IsScript() ); +} + +KSHORT SwPostItsPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const +{ + // Nicht zu fassen: PostIts sind immer zu sehen. + return rInf.OnWin() ? + (KSHORT)rInf.GetOpt().GetPostItsWidth( rInf.GetOut() ) : 0; +} + +/************************************************************************* + * virtual SwPostItsPortion::Format() + *************************************************************************/ + +sal_Bool SwPostItsPortion::Format( SwTxtFormatInfo &rInf ) +{ + sal_Bool bRet = SwLinePortion::Format( rInf ); + // 32749: PostIts sollen keine Auswirkung auf Zeilenhoehe etc. haben + SetAscent( 1 ); + Height( 1 ); + return bRet; +} + +/************************************************************************* + * virtual SwPostItsPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwPostItsPortion::GetExpTxt( const SwTxtSizeInfo &rInf, + XubString &rTxt ) const +{ + if( rInf.OnWin() && rInf.GetOpt().IsPostIts() ) + rTxt = ' '; + else + rTxt.Erase(); + return sal_True; +} + diff --git a/sw/source/core/text/porexp.hxx b/sw/source/core/text/porexp.hxx new file mode 100644 index 000000000000..3967fb8c1b91 --- /dev/null +++ b/sw/source/core/text/porexp.hxx @@ -0,0 +1,111 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porexp.hxx,v $ + * $Revision: 1.7 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _POREXP_HXX +#define _POREXP_HXX + +#include "portxt.hxx" + +/************************************************************************* + * class SwExpandPortion + *************************************************************************/ + +class SwExpandPortion : public SwTxtPortion +{ +public: + inline SwExpandPortion() { SetWhichPor( POR_EXP ); } + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual xub_StrLen GetCrsrOfst( const MSHORT nOfst ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + + +/************************************************************************* + * class SwBlankPortion + *************************************************************************/ + +class SwBlankPortion : public SwExpandPortion +{ + xub_Unicode cChar; + BOOL bMulti; // For multiportion brackets +public: + inline SwBlankPortion( xub_Unicode cCh, BOOL bMult = sal_False ) + : cChar( cCh ), bMulti( bMult ) + { cChar = cCh; SetLen(1); SetWhichPor( POR_BLANK ); } + + BOOL IsMulti() const { return bMulti; } + void SetMulti( BOOL bNew ) { bMulti = bNew; } + + virtual SwLinePortion *Compress(); + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + MSHORT MayUnderFlow( const SwTxtFormatInfo &rInf, xub_StrLen nIdx, + sal_Bool bUnderFlow ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwPostItsPortion + *************************************************************************/ + +class SwPostItsPortion : public SwExpandPortion +{ + KSHORT nViewWidth; + sal_Bool bScript; +public: + SwPostItsPortion( sal_Bool bScrpt ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + sal_Bool IsScript() const { return bScript; } + OUTPUT_OPERATOR +}; + + +CLASSIO( SwExpandPortion ) +CLASSIO( SwBlankPortion ) +CLASSIO( SwPostItsPortion ) + + +#endif diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx new file mode 100644 index 000000000000..162c0fddd07b --- /dev/null +++ b/sw/source/core/text/porfld.cxx @@ -0,0 +1,1393 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porfld.cxx,v $ + * $Revision: 1.62.110.1 $ + * + * 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 _GRAPH_HXX //autogen +#include <vcl/graph.hxx> +#endif +#include <svx/brshitem.hxx> +#ifndef _METRIC_HXX //autogen +#include <vcl/metric.hxx> +#endif +#ifndef _OUTDEV_HXX //autogen +#include <vcl/outdev.hxx> +#endif +#include <viewopt.hxx> // SwViewOptions +#include <txtcfg.hxx> +#include <SwPortionHandler.hxx> +#include <porlay.hxx> +#include <porfld.hxx> +#include <inftxt.hxx> +#include <blink.hxx> // pBlink +#include <frmtool.hxx> // DrawGraphic +#include <viewsh.hxx> +#ifndef _DOCSH_HXX +#include <docsh.hxx> +#endif +#include <doc.hxx> +#include <breakit.hxx> +#include <porrst.hxx> +#include <porftn.hxx> // SwFtnPortion +#include <accessibilityoptions.hxx> +#include <svx/lrspitem.hxx> + +#include <unicode/ubidi.h> + +using namespace ::com::sun::star; + +/************************************************************************* + * class SwFldPortion + *************************************************************************/ + +SwLinePortion *SwFldPortion::Compress() +{ return (GetLen() || aExpand.Len() || SwLinePortion::Compress()) ? this : 0; } + +SwFldPortion *SwFldPortion::Clone( const XubString &rExpand ) const +{ + SwFont *pNewFnt; + if( 0 != ( pNewFnt = pFnt ) ) + { + pNewFnt = new SwFont( *pFnt ); + } + // --> OD 2009-11-25 #i107143# + // pass placeholder property to created <SwFldPortion> instance. + SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder ); + // <-- + pClone->SetNextOffset( nNextOffset ); + pClone->m_bNoLength = this->m_bNoLength; + return pClone; +} + +void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld ) +{ + ASSERT( pFld, "TakeNextOffset: Missing Source" ); + nNextOffset = pFld->GetNextOffset(); + aExpand.Erase( 0, nNextOffset ); + bFollow = sal_True; +} + +SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold ) + : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0), + bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold ) + , m_bNoLength( sal_False ) +{ + SetWhichPor( POR_FLD ); +} + +SwFldPortion::SwFldPortion( const SwFldPortion& rFld ) + : SwExpandPortion( rFld ), + aExpand( rFld.GetExp() ), + nNextOffset( rFld.GetNextOffset() ), + nNextScriptChg( rFld.GetNextScriptChg() ), + bFollow( rFld.IsFollow() ), + bLeft( rFld.IsLeft() ), + bHide( rFld.IsHide() ), + bCenter( rFld.IsCenter() ), + bHasFollow( rFld.HasFollow() ), + bPlaceHolder( rFld.bPlaceHolder ) + , m_bNoLength( rFld.m_bNoLength ) +{ + if ( rFld.HasFont() ) + pFnt = new SwFont( *rFld.GetFont() ); + else + pFnt = 0; + + SetWhichPor( POR_FLD ); +} + +SwFldPortion::~SwFldPortion() +{ + delete pFnt; + if( pBlink ) + pBlink->Delete( this ); +} + +/************************************************************************* + * virtual SwFldPortion::GetViewWidth() + *************************************************************************/ + +KSHORT SwFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const +{ + // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten + // Moment errechnet werden: + SwFldPortion* pThis = (SwFldPortion*)this; + if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() && + !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() ) + { + if( !nViewWidth ) + pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width(); + } + else + pThis->nViewWidth = 0; + return nViewWidth; +} + +/************************************************************************* + * virtual SwFldPortion::Format() + *************************************************************************/ + +// 8653: in keinem Fall nur SetLen(0); + +/************************************************************************* + * Hilfsklasse SwFldSlot + **************************************************************************/ + +class SwFldSlot +{ + const XubString *pOldTxt; + XubString aTxt; + xub_StrLen nIdx; + xub_StrLen nLen; + sal_Bool bOn; + SwTxtFormatInfo *pInf; +public: + SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor ); + ~SwFldSlot(); +}; + +SwFldSlot::SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor ) +{ + bOn = pPor->GetExpTxt( *pNew, aTxt ); + + // Der Text wird ausgetauscht... + if( bOn ) + { + pInf = (SwTxtFormatInfo*)pNew; + nIdx = pInf->GetIdx(); + nLen = pInf->GetLen(); + pOldTxt = &(pInf->GetTxt()); + pInf->SetLen( aTxt.Len() ); + if( pPor->IsFollow() ) + { + pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() ); + pInf->SetIdx( 0 ); + } + else + { + XubString aTmp( aTxt ); + aTxt = *pOldTxt; + aTxt.Erase( nIdx, 1 ); + aTxt.Insert( aTmp, nIdx ); + } + pInf->SetTxt( aTxt ); + } +} + +SwFldSlot::~SwFldSlot() +{ + if( bOn ) + { + pInf->SetTxt( *pOldTxt ); + pInf->SetIdx( nIdx ); + pInf->SetLen( nLen ); + pInf->SetFakeLineStart( sal_False ); + } +} + +void SwFldPortion::CheckScript( const SwTxtSizeInfo &rInf ) +{ + String aTxt; + if( GetExpTxt( rInf, aTxt ) && aTxt.Len() && pBreakIt->GetBreakIter().is() ) + { + BYTE nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual(); + USHORT nScript; + { + nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, 0 ); + xub_StrLen nChg = 0; + if( i18n::ScriptType::WEAK == nScript ) + { + nChg =(xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(aTxt,0,nScript); + if( nChg < aTxt.Len() ) + nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, nChg ); + } + + // + // nNextScriptChg will be evaluated during SwFldPortion::Format() + // + if ( nChg < aTxt.Len() ) + nNextScriptChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( aTxt, nChg, nScript ); + else + nNextScriptChg = aTxt.Len(); + + } + BYTE nTmp; + switch ( nScript ) { + case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break; + case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break; + case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break; + default: nTmp = nActual; + } + + // #i16354# Change script type for RTL text to CTL. + const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo(); + // --> OD 2009-01-29 #i98418# +// const BYTE nFldDir = IsNumberPortion() ? + const BYTE nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ? + rSI.GetDefaultDir() : + rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() ); + // <-- + if ( UBIDI_RTL == nFldDir ) + { + UErrorCode nError = U_ZERO_ERROR; + UBiDi* pBidi = ubidi_openSized( aTxt.Len(), 0, &nError ); + ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aTxt.GetBuffer()), aTxt.Len(), nFldDir, NULL, &nError ); + int32_t nEnd; + UBiDiLevel nCurrDir; + ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir ); + ubidi_close( pBidi ); + const xub_StrLen nNextDirChg = (xub_StrLen)nEnd; + nNextScriptChg = Min( nNextScriptChg, nNextDirChg ); + + // #i89825# change the script type also to CTL + // if there is no strong LTR char in the LTR run (numbers) + if ( nCurrDir != UBIDI_RTL ) + { + nCurrDir = UBIDI_RTL; + for ( xub_StrLen nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx ) + { + UCharDirection nCharDir = u_charDirection ( aTxt.GetChar ( nCharIdx )); + if ( nCharDir == U_LEFT_TO_RIGHT || + nCharDir == U_LEFT_TO_RIGHT_EMBEDDING || + nCharDir == U_LEFT_TO_RIGHT_OVERRIDE ) + { + nCurrDir = UBIDI_LTR; + break; + } + } + } + + if ( nCurrDir == UBIDI_RTL ) + nTmp = SW_CTL; + } + + // --> OD 2009-01-29 #i98418# + // keep determined script type for footnote portions as preferred script type. + // For footnote portions a font can not be created directly - see footnote + // portion format method. +// if( !IsFtnPortion() && nTmp != nActual ) + if ( IsFtnPortion() ) + { + dynamic_cast<SwFtnPortion*>(this)->SetPreferredScriptType( nTmp ); + } + else if ( nTmp != nActual ) + { + if( !pFnt ) + pFnt = new SwFont( *rInf.GetFont() ); + pFnt->SetActual( nTmp ); + } + // <-- + } +} + +sal_Bool SwFldPortion::Format( SwTxtFormatInfo &rInf ) +{ + // Scope wegen aDiffTxt::DTOR! + xub_StrLen nRest; + sal_Bool bFull; + sal_Bool bEOL = sal_False; + long nTxtRest = rInf.GetTxt().Len() - rInf.GetIdx(); + { + SwFldSlot aDiffTxt( &rInf, this ); + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + aLayoutModeModifier.SetAuto(); + + // Field portion has to be split in several parts if + // 1. There are script/direction changes inside the field + // 2. There are portion breaks (tab, break) inside the field: + const xub_StrLen nOldFullLen = rInf.GetLen(); + xub_StrLen nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx(); + if ( nNextScriptChg < nFullLen ) + { + nFullLen = nNextScriptChg; + rInf.SetHookChar( 0 ); + } + rInf.SetLen( nFullLen ); + + if ( STRING_LEN != rInf.GetUnderScorePos() && + rInf.GetUnderScorePos() > rInf.GetIdx() ) + rInf.SetUnderScorePos( rInf.GetIdx() ); + + if( pFnt ) + pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() ); + + SwFontSave aSave( rInf, pFnt ); + + // 8674: Laenge muss 0 sein, bei bFull nach Format ist die Laenge + // gesetzt und wird in nRest uebertragen. Ansonsten bleibt die + // Laenge erhalten und wuerde auch in nRest einfliessen! + SetLen(0); + const MSHORT nFollow = IsFollow() ? 0 : 1; + + // So komisch es aussieht, die Abfrage auf GetLen() muss wegen der + // ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs) + // sal_False returnen wegen SetFull ... + if( !nFullLen ) + { + // nicht Init(), weil wir Hoehe und Ascent brauchen + Width(0); + bFull = rInf.Width() <= rInf.GetPos().X(); + } + else + { + xub_StrLen nOldLineStart = rInf.GetLineStart(); + if( IsFollow() ) + rInf.SetLineStart( 0 ); + rInf.SetNotEOL( nFullLen == nOldFullLen && nTxtRest > nFollow ); + + // the height depending on the fields font is set, + // this is required for SwTxtGuess::Guess + Height( rInf.GetTxtHeight() ); + // If a kerning portion is inserted after our field portion, + // the ascent and height must be known + SetAscent( rInf.GetAscent() ); + bFull = SwTxtPortion::Format( rInf ); + rInf.SetNotEOL( sal_False ); + rInf.SetLineStart( nOldLineStart ); + } + xub_StrLen nTmpLen = GetLen(); + bEOL = !nTmpLen && nFollow && bFull; + nRest = nOldFullLen - nTmpLen; + + // Das Zeichen wird in der ersten Portion gehalten. + // Unbedingt nach Format! + SetLen( (m_bNoLength) ? 0 : nFollow ); + + if( nRest ) + { + // aExpand ist noch nicht gekuerzt worden, der neue Ofst + // ergibt sich durch nRest. + xub_StrLen nNextOfst = aExpand.Len() - nRest; + + if ( IsQuoVadisPortion() ) + nNextOfst = nNextOfst + ((SwQuoVadisPortion*)this)->GetContTxt().Len(); + + XubString aNew( aExpand, nNextOfst, STRING_LEN ); + aExpand.Erase( nNextOfst, STRING_LEN ); + + // These characters should not be contained in the follow + // field portion. They are handled via the HookChar mechanism. + switch( aNew.GetChar( 0 )) + { + case CH_BREAK : bFull = sal_True; + // kein break; + case ' ' : + case CH_TAB : + case CHAR_HARDHYPHEN: // non-breaking hyphen + case CHAR_SOFTHYPHEN: + case CHAR_HARDBLANK: + // --> FME 2006-01-11 #i59759# Erase additional control + // characters from field string, otherwise we get stuck in + // a loop. + case CHAR_ZWSP : + case CHAR_ZWNBSP : + // case CHAR_RLM : + // case CHAR_LRM : + // <-- + { + aNew.Erase( 0, 1 ); + ++nNextOfst; + break; + } + default: ; + } + + // Even if there is no more text left for a follow field, + // we have to build a follow field portion (without font), + // otherwise the HookChar mechanism would not work. + SwFldPortion *pFld = Clone( aNew ); + if( aNew.Len() && !pFld->GetFont() ) + { + SwFont *pNewFnt = new SwFont( *rInf.GetFont() ); + pFld->SetFont( pNewFnt ); + } + pFld->SetFollow( sal_True ); + SetHasFollow( sal_True ); + // In nNextOffset steht bei einem neuangelegten Feld zunaechst + // der Offset, an dem es selbst im Originalstring beginnt. + // Wenn beim Formatieren ein FollowFeld angelegt wird, wird + // der Offset dieses FollowFelds in nNextOffset festgehalten. + nNextOffset = nNextOffset + nNextOfst; + pFld->SetNextOffset( nNextOffset ); + rInf.SetRest( pFld ); + } + } + + if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() ) + rInf.GetLast()->FormatEOL( rInf ); + return bFull; +} + +/************************************************************************* + * virtual SwFldPortion::Paint() + *************************************************************************/ + +void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + SwFontSave aSave( rInf, pFnt ); + + ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" ); + if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) ) + { + // Dies ist eine freizuegige Auslegung der Hintergrundbelegung ... + rInf.DrawViewOpt( *this, POR_FLD ); + SwExpandPortion::Paint( rInf ); + } +} + +/************************************************************************* + * virtual SwFldPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const +{ + rTxt = aExpand; + if( !rTxt.Len() && rInf.OnWin() && + !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && + SwViewOption::IsFieldShadings() && + !HasFollow() ) + rTxt = ' '; + return sal_True; +} + +/************************************************************************* + * virtual SwFldPortion::HandlePortion() + *************************************************************************/ + +void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Special( GetLen(), aExpand, GetWhichPor() ); +} + +/************************************************************************* + * virtual SwFldPortion::GetTxtSize() + *************************************************************************/ + +SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const +{ + SwFontSave aSave( rInf, pFnt ); + SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) ); + return aSize; +} + +/************************************************************************* + * class SwHiddenPortion + *************************************************************************/ + +SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const +{ + SwFont *pNewFnt; + if( 0 != ( pNewFnt = pFnt ) ) + pNewFnt = new SwFont( *pFnt ); + return new SwHiddenPortion( rExpand, pNewFnt ); +} + +/************************************************************************* + * virtual SwHiddenPortion::Paint() + *************************************************************************/ + +void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + { + SwFontSave aSave( rInf, pFnt ); + rInf.DrawViewOpt( *this, POR_HIDDEN ); + SwExpandPortion::Paint( rInf ); + } +} + +/************************************************************************* + * virtual SwHiddenPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const +{ + // Nicht auf IsHidden() abfragen ! + return SwFldPortion::GetExpTxt( rInf, rTxt ); +} + +/************************************************************************* + * class SwNumberPortion + *************************************************************************/ + +// --> OD 2008-01-23 #newlistlevelattrs# +SwNumberPortion::SwNumberPortion( const XubString &rExpand, + SwFont *pFont, + const sal_Bool bLft, + const sal_Bool bCntr, + const KSHORT nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ) + : SwFldPortion( rExpand, pFont ), + nFixWidth(0), + nMinDist( nMinDst ), + // --> OD 2008-01-23 #newlistlevelattrs# + mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive ) + // <-- +{ + SetWhichPor( POR_NUMBER ); + SetLeft( bLft ); + SetHide( sal_False ); + SetCenter( bCntr ); +} + +xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const +{ + return 0; +} + +SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const +{ + SwFont *pNewFnt; + if( 0 != ( pNewFnt = pFnt ) ) + pNewFnt = new SwFont( *pFnt ); + // --> OD 2008-01-23 #newlistlevelattrs# + return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(), + nMinDist, mbLabelAlignmentPosAndSpaceModeActive ); + // <-- +} + +/************************************************************************* + * virtual SwNumberPortion::Format() + *************************************************************************/ + +// 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen! +// 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text +// eingibt, bis die Zeile ueberlaeuft. +// Man muss die Fly-Ausweichmanoever beachten! + +sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf ) +{ + SetHide( sal_False ); + const sal_Bool bFull = SwFldPortion::Format( rInf ); + SetLen( 0 ); + // a numbering portion can be contained in a rotated portion!!! + nFixWidth = rInf.IsMulti() ? Height() : Width(); + rInf.SetNumDone( !rInf.GetRest() ); + if( rInf.IsNumDone() ) + { +// SetAscent( rInf.GetAscent() ); + ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" ); + + long nDiff( 0 ); + // --> OD 2008-01-23 #newlistlevelattrs# + if ( !mbLabelAlignmentPosAndSpaceModeActive ) + { + if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) && + // --> FME 2004-08-13 #i32902# + !IsFtnNumPortion() ) + // <-- + { + nDiff = rInf.Left() + + rInf.GetTxtFrm()->GetTxtNode()-> + GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst() + - rInf.First() + + rInf.ForcedLeftMargin(); + } + else + { + nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin(); + } + } + // <-- + // Ein Vorschlag von Juergen und Volkmar: + // Der Textteil hinter der Numerierung sollte immer + // mindestens beim linken Rand beginnen. + if( nDiff < 0 ) + nDiff = 0; + else if ( nDiff > rInf.X() ) + nDiff -= rInf.X(); + else + nDiff = 0; + + if( nDiff < nFixWidth + nMinDist ) + nDiff = nFixWidth + nMinDist; + // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde + // fieser Sonderfall: FlyFrm liegt in dem Bereich, + // den wir uns gerade unter den Nagel reissen wollen. + // Die NumberPortion wird als verborgen markiert. + const sal_Bool bFly = rInf.GetFly() || + ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() ); + if( nDiff > rInf.Width() ) + { + nDiff = rInf.Width(); + if ( bFly ) + SetHide( sal_True ); + } + + // A numbering portion can be inside a SwRotatedPortion. Then the + // Height has to be changed + if ( rInf.IsMulti() ) + { + if ( Height() < nDiff ) + Height( KSHORT( nDiff ) ); + } + else if( Width() < nDiff ) + Width( KSHORT(nDiff) ); + } + return bFull; +} + +void SwNumberPortion::FormatEOL( SwTxtFormatInfo& ) +{ +/* Ein FormatEOL deutet daraufhin, dass der folgende Text + * nicht mit auf die Zeile passte. Damit die Numerierung mitwandert, + * wird diese NumberPortion verborgen. + */ + + // This caused trouble with flys anchored as characters. + // If one of these is numbered but does not fit to the line, + // it calls this function, causing a loop because both the number + // portion and the fly portion go to the next line +// SetHide( sal_True ); +} + +/************************************************************************* + * virtual SwNumberPortion::Paint() + *************************************************************************/ + +void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ +/* Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt + * Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile. + */ + + if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() ) + { + SwLinePortion *pTmp = GetPortion(); + while ( pTmp && !pTmp->InTxtGrp() ) + pTmp = pTmp->GetPortion(); + if ( !pTmp ) + return; + } + + // calculate the width of the number portion, including follows + const KSHORT nOldWidth = Width(); + USHORT nSumWidth = 0; + USHORT nOffset = 0; + + const SwLinePortion* pTmp = this; + while ( pTmp && pTmp->InNumberGrp() ) + { + nSumWidth = nSumWidth + pTmp->Width(); + if ( ((SwNumberPortion*)pTmp)->HasFollow() ) + pTmp = pTmp->GetPortion(); + else + { + nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth; + break; + } + } + + // The master portion takes care for painting the background of the + // follow field portions + if ( ! IsFollow() ) + { + SwLinePortion *pThis = (SwLinePortion*)this; + pThis->Width( nSumWidth ); + rInf.DrawViewOpt( *this, POR_NUMBER ); + pThis->Width( nOldWidth ); + } + + if( aExpand.Len() ) + { + const SwFont *pTmpFnt = rInf.GetFont(); + sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() || + UNDERLINE_NONE != pTmpFnt->GetOverline() || + STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) && + !pTmpFnt->IsWordLineMode(); + if( bPaintSpace && pFnt ) + bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() || + UNDERLINE_NONE != pFnt->GetOverline() || + STRIKEOUT_NONE != pFnt->GetStrikeout() ) && + !pFnt->IsWordLineMode(); + + SwFontSave aSave( rInf, pFnt ); + + if( nFixWidth == Width() && ! HasFollow() ) + SwExpandPortion::Paint( rInf ); + else + { + // logisches const: Width wird wieder zurueckgesetzt + SwLinePortion *pThis = (SwLinePortion*)this; + bPaintSpace = bPaintSpace && nFixWidth < nOldWidth; + KSHORT nSpaceOffs = nFixWidth; + pThis->Width( nFixWidth ); + + if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) || + ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) ) + SwExpandPortion::Paint( rInf ); + else + { + SwTxtPaintInfo aInf( rInf ); + if( nOffset < nMinDist ) + nOffset = 0; + else + { + if( IsCenter() ) + { + /* #110778# a / 2 * 2 == a is not a tautology */ + KSHORT nTmpOffset = nOffset; + nOffset /= 2; + if( nOffset < nMinDist ) + nOffset = nTmpOffset - nMinDist; + } + else + nOffset = nOffset - nMinDist; + } + aInf.X( aInf.X() + nOffset ); + SwExpandPortion::Paint( aInf ); + if( bPaintSpace ) + nSpaceOffs = nSpaceOffs + nOffset; + } + if( bPaintSpace && nOldWidth > nSpaceOffs ) + { + SwTxtPaintInfo aInf( rInf ); +static sal_Char __READONLY_DATA sDoubleSpace[] = " "; + aInf.X( aInf.X() + nSpaceOffs ); + + // --> FME 2005-08-12 #i53199# Adjust position of underline: + if ( rInf.GetUnderFnt() ) + { + const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() ); + rInf.GetUnderFnt()->SetPos( aNewPos ); + } + // <-- + + pThis->Width( nOldWidth - nSpaceOffs + 12 ); + { + SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace ); + aInf.DrawText( *this, aInf.GetLen(), sal_True ); + } + } + pThis->Width( nOldWidth ); + } + } +} + + +/************************************************************************* + * class SwBulletPortion + *************************************************************************/ + +// --> OD 2008-01-23 #newlistlevelattrs# +SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet, + const XubString& rBulletFollowedBy, + SwFont *pFont, + const sal_Bool bLft, + const sal_Bool bCntr, + const KSHORT nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ) + : SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) , + pFont, bLft, bCntr, nMinDst, + bLabelAlignmentPosAndSpaceModeActive ) +// <-- +{ + SetWhichPor( POR_BULLET ); +} + +/************************************************************************* + * class SwGrfNumPortion + *************************************************************************/ + +#define GRFNUM_SECURE 10 + +// --> OD 2008-01-23 #newlistlevelattrs# +SwGrfNumPortion::SwGrfNumPortion( + SwFrm *pFrm, + const XubString& rGraphicFollowedBy, + const SvxBrushItem* pGrfBrush, + const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize, + const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ) : + SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst, + bLabelAlignmentPosAndSpaceModeActive ), +// <-- + pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 ) +{ + SetWhichPor( POR_GRFNUM ); + SetAnimated( sal_False ); + bReplace = sal_False; + if( pGrfBrush ) + { + *pBrush = *pGrfBrush; + SwDocShell *pSh = pFrm->GetShell()->GetDoc()->GetDocShell(); + const Graphic* pGraph = pGrfBrush->GetGraphic( pSh ); + if( pGraph ) + SetAnimated( pGraph->IsAnimated() ); + else + bReplace = sal_True; + } + if( pGrfOrient ) + { + nYPos = pGrfOrient->GetPos(); + eOrient = pGrfOrient->GetVertOrient(); + } + else + { + nYPos = 0; + eOrient = text::VertOrientation::TOP; + } + Width( static_cast<USHORT>(rGrfSize.Width() + 2 * GRFNUM_SECURE) ); + nFixWidth = Width(); + nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE; + Height( KSHORT(nGrfHeight) ); + bNoPaint = sal_False; +} + +SwGrfNumPortion::~SwGrfNumPortion() +{ + if ( IsAnimated() ) + ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId ); + delete pBrush; +} + +void SwGrfNumPortion::StopAnimation( OutputDevice* pOut ) +{ + if ( IsAnimated() ) + ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId ); +} + +sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf ) +{ + SetHide( sal_False ); + // --> OD 2008-01-29 #newlistlevelattrs# +// Width( nFixWidth ); + KSHORT nFollowedByWidth( 0 ); + if ( mbLabelAlignmentPosAndSpaceModeActive ) + { + SwFldPortion::Format( rInf ); + nFollowedByWidth = Width(); + SetLen( 0 ); + } + Width( nFixWidth + nFollowedByWidth ); + // <-- + const sal_Bool bFull = rInf.Width() < rInf.X() + Width(); + const sal_Bool bFly = rInf.GetFly() || + ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() ); + SetAscent( static_cast<USHORT>(GetRelPos() > 0 ? GetRelPos() : 0) ); + if( GetAscent() > Height() ) + Height( GetAscent() ); + + if( bFull ) + { + Width( rInf.Width() - (KSHORT)rInf.X() ); + if( bFly ) + { + SetLen( 0 ); + SetNoPaint( sal_True ); + rInf.SetNumDone( sal_False ); + return sal_True; + } + } + rInf.SetNumDone( sal_True ); + // --> OD 2008-01-23 #newlistlevelattrs# +// long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin(); + long nDiff = mbLabelAlignmentPosAndSpaceModeActive + ? 0 + : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin(); + // <-- + // Ein Vorschlag von Juergen und Volkmar: + // Der Textteil hinter der Numerierung sollte immer + // mindestens beim linken Rand beginnen. + if( nDiff < 0 ) + nDiff = 0; + else if ( nDiff > rInf.X() ) + nDiff -= rInf.X(); + if( nDiff < nFixWidth + nMinDist ) + nDiff = nFixWidth + nMinDist; + // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde + // fieser Sonderfall: FlyFrm liegt in dem Bereich, + // den wir uns gerade unter den Nagel reissen wollen. + // Die NumberPortion wird als verborgen markiert. + if( nDiff > rInf.Width() ) + { + nDiff = rInf.Width(); + if( bFly ) + SetHide( sal_True ); + } + + if( Width() < nDiff ) + Width( KSHORT(nDiff) ); + return bFull; +} + +void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( DontPaint() ) + return; +/* Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt + * Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile. + */ + if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() ) + { + SwLinePortion *pTmp = GetPortion(); + while ( pTmp && !pTmp->InTxtGrp() ) + pTmp = pTmp->GetPortion(); + if ( !pTmp ) + return; + } + Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE ); + long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) ); + Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE ); + + // --> OD 2008-02-05 #newlistlevelattrs# + const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive || + ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) || + ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ); + // <-- + + if( nFixWidth < Width() && !bTmpLeft ) + { + KSHORT nOffset = Width() - nFixWidth; + if( nOffset < nMinDist ) + nOffset = 0; + else + { + if( IsCenter() ) + { + nOffset /= 2; + if( nOffset < nMinDist ) + nOffset = Width() - nFixWidth - nMinDist; + } + else + nOffset = nOffset - nMinDist; + } + aPos.X() += nOffset; + } + + if( bReplace ) + { + KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120; + aSize = Size( nTmpH, nTmpH ); + aPos.Y() = rInf.Y() - nTmpH; + } + SwRect aTmp( aPos, aSize ); + + sal_Bool bDraw = sal_True; + + if ( IsAnimated() ) + { + bDraw = !rInf.GetOpt().IsGraphic(); + if( !nId ) + { + SetId( long( rInf.GetTxtFrm() ) ); + rInf.GetTxtFrm()->SetAnimation(); + } + if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw ) + { + rInf.NoteAnimation(); + const ViewShell* pViewShell = rInf.GetVsh(); + + // virtual device, not pdf export + if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() && + pViewShell && pViewShell->GetWin() ) + { + ( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId); + rInf.GetTxtFrm()->GetShell()->InvalidateWindows( aTmp ); + } + + + else if ( pViewShell && + !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() && + !pViewShell->IsPreView() && + // --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export. + pViewShell->GetWin() ) + // <-- + { + ( (Graphic*) pBrush->GetGraphic() )->StartAnimation( + (OutputDevice*)rInf.GetOut(), aPos, aSize, nId ); + } + + // pdf export, printing, preview, stop animations... + else + bDraw = sal_True; + } + if( bDraw ) + ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId ); + } + + SwRect aRepaint( rInf.GetPaintRect() ); + const SwTxtFrm& rFrm = *rInf.GetTxtFrm(); + if( rFrm.IsVertical() ) + { + rFrm.SwitchHorizontalToVertical( aTmp ); + rFrm.SwitchHorizontalToVertical( aRepaint ); + } + + if( rFrm.IsRightToLeft() ) + { + rFrm.SwitchLTRtoRTL( aTmp ); + rFrm.SwitchLTRtoRTL( aRepaint ); + } + + if( bDraw && aTmp.HasArea() ) + DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(), + aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES ); +} + +void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent, + long nFlyAsc, long nFlyDesc ) +{ + if ( GetOrient() != text::VertOrientation::NONE ) + { + SetRelPos( 0 ); + if ( GetOrient() == text::VertOrientation::CENTER ) + SetRelPos( GetGrfHeight() / 2 ); + else if ( GetOrient() == text::VertOrientation::TOP ) + SetRelPos( GetGrfHeight() - GRFNUM_SECURE ); + else if ( GetOrient() == text::VertOrientation::BOTTOM ) + ; + else if ( GetOrient() == text::VertOrientation::CHAR_CENTER ) + SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 ); + else if ( GetOrient() == text::VertOrientation::CHAR_TOP ) + SetRelPos( nLnAscent ); + else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM ) + SetRelPos( GetGrfHeight() - nLnDescent ); + else + { + if( GetGrfHeight() >= nFlyAsc + nFlyDesc ) + { + // wenn ich genauso gross bin wie die Zeile, brauche ich mich + // nicht an der Zeile nicht weiter ausrichten, ich lasse + // dann auch den max. Ascent der Zeile unveraendert + + SetRelPos( nFlyAsc ); + } + else if ( GetOrient() == text::VertOrientation::LINE_CENTER ) + SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 ); + else if ( GetOrient() == text::VertOrientation::LINE_TOP ) + SetRelPos( nFlyAsc ); + else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM ) + SetRelPos( GetGrfHeight() - nFlyDesc ); + } + } +} + +void SwTxtFrm::StopAnimation( OutputDevice* pOut ) +{ + ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" ); + if( HasPara() ) + { + SwLineLayout *pLine = GetPara(); + while( pLine ) + { + SwLinePortion *pPor = pLine->GetPortion(); + while( pPor ) + { + if( pPor->IsGrfNumPortion() ) + ((SwGrfNumPortion*)pPor)->StopAnimation( pOut ); + // Die Numerierungsportion sitzt immer vor dem ersten Zeichen, + // deshalb koennen wir abbrechen, sobald wir eine Portion mit + // einer Laenge > 0 erreicht haben. + pPor = pPor->GetLen() ? 0 : pPor->GetPortion(); + } + pLine = pLine->GetLen() ? 0 : pLine->GetNext(); + } + } +} + +/************************************************************************* + * SwCombinedPortion::SwCombinedPortion(..) + * initializes the script array and clears the width array + *************************************************************************/ + +SwCombinedPortion::SwCombinedPortion( const XubString &rTxt ) + : SwFldPortion( rTxt ) +{ + SetLen(1); + SetWhichPor( POR_COMBINED ); + if( aExpand.Len() > 6 ) + aExpand.Erase( 6 ); + // Initialization of the scripttype array, + // the arrays of width and position are filled by the format function + if( pBreakIt->GetBreakIter().is() ) + { + BYTE nScr = SW_SCRIPTS; + for( USHORT i = 0; i < rTxt.Len(); ++i ) + { + USHORT nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i ); + switch ( nScript ) { + case i18n::ScriptType::LATIN : nScr = SW_LATIN; break; + case i18n::ScriptType::ASIAN : nScr = SW_CJK; break; + case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break; + } + aScrType[i] = nScr; + } + } + else + { + for( USHORT i = 0; i < 6; aScrType[i++] = 0 ) + ; // nothing + } + memset( &aWidth, 0, sizeof(aWidth) ); +} + +/************************************************************************* + * SwCombinedPortion::Paint(..) + *************************************************************************/ + +void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" ); + if( Width() ) + { + rInf.DrawBackBrush( *this ); + rInf.DrawViewOpt( *this, POR_FLD ); + + // do we have to repaint a post it portion? + if( rInf.OnWin() && pPortion && !pPortion->Width() ) + pPortion->PrePaint( rInf, this ); + + USHORT nCount = aExpand.Len(); + if( !nCount ) + return; + ASSERT( nCount < 7, "Too much combined characters" ); + + // the first character of the second row + USHORT nTop = ( nCount + 1 ) / 2; + + SwFont aTmpFont( *rInf.GetFont() ); + aTmpFont.SetProportion( nProportion ); // a smaller font + SwFontSave aFontSave( rInf, &aTmpFont ); + + USHORT i = 0; + Point aOldPos = rInf.GetPos(); + Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row + while( i < nCount ) + { + if( i == nTop ) // change the row + aOutPos.Y() = aOldPos.Y() + nLowPos; // Y of the second row + aOutPos.X() = aOldPos.X() + aPos[i]; // X position + const BYTE nAct = aScrType[i]; // script type + aTmpFont.SetActual( nAct ); + // if there're more than 4 characters to display, we choose fonts + // with 2/3 of the original font width. + if( aWidth[ nAct ] ) + { + Size aTmpSz = aTmpFont.GetSize( nAct ); + if( aTmpSz.Width() != aWidth[ nAct ] ) + { + aTmpSz.Width() = aWidth[ nAct ]; + aTmpFont.SetSize( aTmpSz, nAct ); + } + } + ((SwTxtPaintInfo&)rInf).SetPos( aOutPos ); + rInf.DrawText( aExpand, *this, i, 1 ); + ++i; + } + // rInf is const, so we have to take back our manipulations + ((SwTxtPaintInfo&)rInf).SetPos( aOldPos ); + } +} + +/************************************************************************* + * SwCombinedPortion::Format(..) + *************************************************************************/ + +sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf ) +{ + USHORT nCount = aExpand.Len(); + if( !nCount ) + { + Width( 0 ); + return sal_False; + } + + ASSERT( nCount < 7, "Too much combined characters" ); + // If there are leading "weak"-scripttyped characters in this portion, + // they get the actual scripttype. + USHORT i = 0; + while( i < nCount && SW_SCRIPTS == aScrType[i] ) + aScrType[i++] = rInf.GetFont()->GetActual(); + if( nCount > 4 ) + { + // more than four? Ok, then we need the 2/3 font width + i = 0; + while( i < aExpand.Len() ) + { + ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" ); + if( !aWidth[ aScrType[i] ] ) + { + rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) ); + aWidth[ aScrType[i] ] = + static_cast<USHORT>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3); + } + ++i; + } + } + + USHORT nTop = ( nCount + 1 ) / 2; // the first character of the second line + ViewShell *pSh = rInf.GetTxtFrm()->GetShell(); + SwFont aTmpFont( *rInf.GetFont() ); + SwFontSave aFontSave( rInf, &aTmpFont ); + nProportion = 55; + // In nMainAscent/Descent we store the ascent and descent + // of the original surrounding font + USHORT nMaxDescent, nMaxAscent, nMaxWidth; + USHORT nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() ); + const USHORT nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() ); + nMainDescent = nMainDescent - nMainAscent; + // we start with a 50% font, but if we notice that the combined portion + // becomes bigger than the surrounding font, we check 45% and maybe 40%. + do + { + nProportion -= 5; + aTmpFont.SetProportion( nProportion ); + i = 0; + memset( &aPos, 0, sizeof(aPos) ); + nMaxDescent = 0; + nMaxAscent = 0; + nMaxWidth = 0; + nUpPos = nLowPos = 0; + + // Now we get the width of all characters. + // The ascent and the width of the first line are stored in the + // ascent member of the portion, the descent in nLowPos. + // The ascent, descent and width of the second line are stored in the + // local nMaxAscent, nMaxDescent and nMaxWidth variables. + while( i < nCount ) + { + BYTE nScrp = aScrType[i]; + aTmpFont.SetActual( nScrp ); + if( aWidth[ nScrp ] ) + { + Size aFontSize( aTmpFont.GetSize( nScrp ) ); + aFontSize.Width() = aWidth[ nScrp ]; + aTmpFont.SetSize( aFontSize, nScrp ); + } + + SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 ); + Size aSize = aTmpFont._GetTxtSize( aDrawInf ); + USHORT nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() ); + aPos[ i ] = (USHORT)aSize.Width(); + if( i == nTop ) // enter the second line + { + nLowPos = nMaxDescent; + Height( nMaxDescent + nMaxAscent ); + Width( nMaxWidth ); + SetAscent( nMaxAscent ); + nMaxAscent = 0; + nMaxDescent = 0; + nMaxWidth = 0; + } + nMaxWidth = nMaxWidth + aPos[ i++ ]; + if( nAsc > nMaxAscent ) + nMaxAscent = nAsc; + if( aSize.Height() - nAsc > nMaxDescent ) + nMaxDescent = static_cast<USHORT>(aSize.Height() - nAsc); + } + // for one or two characters we double the width of the portion + if( nCount < 3 ) + { + nMaxWidth *= 2; + Width( 2*Width() ); + if( nCount < 2 ) + { + Height( nMaxAscent + nMaxDescent ); + nLowPos = nMaxDescent; + } + } + Height( Height() + nMaxDescent + nMaxAscent ); + nUpPos = nMaxAscent; + SetAscent( Height() - nMaxDescent - nLowPos ); + } while( nProportion > 40 && ( GetAscent() > nMainAscent || + Height() - GetAscent() > nMainDescent ) ); + // if the combined portion is smaller than the surrounding text, + // the portion grows. This looks better, if there's a character background. + if( GetAscent() < nMainAscent ) + { + Height( Height() + nMainAscent - GetAscent() ); + SetAscent( nMainAscent ); + } + if( Height() < nMainAscent + nMainDescent ) + Height( nMainAscent + nMainDescent ); + + // We calculate the x positions of the characters in both lines.. + USHORT nTopDiff = 0; + USHORT nBotDiff = 0; + if( nMaxWidth > Width() ) + { + nTopDiff = ( nMaxWidth - Width() ) / 2; + Width( nMaxWidth ); + } + else + nBotDiff = ( Width() - nMaxWidth ) / 2; + switch( nTop) + { + case 3: aPos[1] = aPos[0] + nTopDiff; // no break + case 2: aPos[nTop-1] = Width() - aPos[nTop-1]; + } + aPos[0] = 0; + switch( nCount ) + { + case 5: aPos[4] = aPos[3] + nBotDiff; // no break + case 3: aPos[nTop] = nBotDiff; break; + case 6: aPos[4] = aPos[3] + nBotDiff; // no break + case 4: aPos[nTop] = 0; // no break + case 2: aPos[nCount-1] = Width() - aPos[nCount-1]; + } + + // Does the combined portion fit the line? + const sal_Bool bFull = rInf.Width() < rInf.X() + Width(); + if( bFull ) + { + if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFldGrp() + || !((SwFldPortion*)rInf.GetLast())->IsFollow() ) ) + Width( (USHORT)( rInf.Width() - rInf.X() ) ); + else + { + Truncate(); + Width( 0 ); + SetLen( 0 ); + if( rInf.GetLast() ) + rInf.GetLast()->FormatEOL( rInf ); + } + } + return bFull; +} + +/************************************************************************* + * SwCombinedPortion::GetViewWidth(..) + *************************************************************************/ + +KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const +{ + if( !GetLen() ) // for the dummy part at the end of the line, where + return 0; // the combined portion doesn't fit. + return SwFldPortion::GetViewWidth( rInf ); +} diff --git a/sw/source/core/text/porfld.hxx b/sw/source/core/text/porfld.hxx new file mode 100644 index 000000000000..b2ece8ffe8c6 --- /dev/null +++ b/sw/source/core/text/porfld.hxx @@ -0,0 +1,277 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porfld.hxx,v $ + * $Revision: 1.16.100.1 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORFLD_HXX +#define _PORFLD_HXX + +#include "swtypes.hxx" +#include "porexp.hxx" +#include <fmtornt.hxx> + +class SwFont; +class SvxBrushItem; +class SwFmtVertOrient; +class SwFrm; + +/************************************************************************* + * class SwFldPortion + *************************************************************************/ + +class SwFldPortion : public SwExpandPortion +{ + friend class SwTxtFormatter; +protected: + XubString aExpand; // das expandierte Feld + SwFont *pFnt; // Fuer mehrzeilige Felder + xub_StrLen nNextOffset; // Offset des Follows im Originalstring + xub_StrLen nNextScriptChg; + KSHORT nViewWidth; // Screenbreite fuer leere Felder + sal_Bool bFollow : 1; // 2. oder weiterer Teil eines Feldes + sal_Bool bLeft : 1; // wird von SwNumberPortion benutzt + sal_Bool bHide : 1; // wird von SwNumberPortion benutzt + sal_Bool bCenter : 1; // wird von SwNumberPortion benutzt + sal_Bool bHasFollow : 1; // geht in der naechsten Zeile weiter + sal_Bool bAnimated : 1; // wird von SwGrfNumPortion benutzt + sal_Bool bNoPaint : 1; // wird von SwGrfNumPortion benutzt + sal_Bool bReplace : 1; // wird von SwGrfNumPortion benutzt + const sal_Bool bPlaceHolder : 1; + sal_Bool m_bNoLength : 1; // HACK for meta suffix (no CH_TXTATR) + + inline void SetFont( SwFont *pNew ) { pFnt = pNew; } + inline bool IsNoLength() const { return m_bNoLength; } + inline void SetNoLength() { m_bNoLength = sal_True; } + +public: + SwFldPortion( const SwFldPortion& rFld ); + SwFldPortion( const XubString &rExpand, SwFont *pFnt = 0, sal_Bool bPlaceHolder = sal_False ); + ~SwFldPortion(); + + void TakeNextOffset( const SwFldPortion* pFld ); + void CheckScript( const SwTxtSizeInfo &rInf ); + inline sal_Bool HasFont() const { return 0 != pFnt; } + // --> OD 2008-06-05 #i89179# - made public + inline const SwFont *GetFont() const { return pFnt; } + // <-- + + inline const XubString &GetExp() const { return aExpand; } + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + + // leere Felder sind auch erlaubt + virtual SwLinePortion *Compress(); + + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + + inline sal_Bool IsFollow() const { return bFollow; } + inline void SetFollow( sal_Bool bNew ) { bFollow = bNew; } + + inline sal_Bool IsLeft() const { return bLeft; } + inline void SetLeft( sal_Bool bNew ) { bLeft = bNew; } + + inline sal_Bool IsHide() const { return bHide; } + inline void SetHide( sal_Bool bNew ) { bHide = bNew; } + + inline sal_Bool IsCenter() const { return bCenter; } + inline void SetCenter( sal_Bool bNew ) { bCenter = bNew; } + + inline sal_Bool HasFollow() const { return bHasFollow; } + inline void SetHasFollow( sal_Bool bNew ) { bHasFollow = bNew; } + + inline xub_StrLen GetNextOffset() const { return nNextOffset; } + inline void SetNextOffset( xub_StrLen nNew ) { nNextOffset = nNew; } + + inline xub_StrLen GetNextScriptChg() const { return nNextScriptChg; } + inline void SetNextScriptChg( xub_StrLen nNew ) { nNextScriptChg = nNew; } + + // Felder-Cloner fuer SplitGlue + virtual SwFldPortion *Clone( const XubString &rExpand ) const; + + // Extra-GetTxtSize wegen pFnt + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwHiddenPortion + *************************************************************************/ +// Unterscheidung nur fuer's Painten/verstecken. + +class SwHiddenPortion : public SwFldPortion +{ +public: + inline SwHiddenPortion( const XubString &rExpand, SwFont *pFntL = 0 ) + : SwFldPortion( rExpand, pFntL ) + { SetLen(1); SetWhichPor( POR_HIDDEN ); } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + + // Felder-Cloner fuer SplitGlue + virtual SwFldPortion *Clone( const XubString &rExpand ) const; + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwNumberPortion + *************************************************************************/ + +class SwNumberPortion : public SwFldPortion +{ +protected: + KSHORT nFixWidth; // vgl. Glues + KSHORT nMinDist; // minimaler Abstand zum Text + // --> OD 2008-01-23 #newlistlevelattrs# + bool mbLabelAlignmentPosAndSpaceModeActive; + // <-- + +public: + // --> OD 2008-01-23 #newlistlevelattrs# + SwNumberPortion( const XubString &rExpand, + SwFont *pFnt, + const sal_Bool bLeft, + const sal_Bool bCenter, + const KSHORT nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ); + // <-- + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual xub_StrLen GetCrsrOfst( const MSHORT nOfst ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + + // Felder-Cloner fuer SplitGlue + virtual SwFldPortion *Clone( const XubString &rExpand ) const; + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwBulletPortion + *************************************************************************/ + +class SwBulletPortion : public SwNumberPortion +{ +public: + // --> OD 2008-01-23 #newlistlevelattrs# + SwBulletPortion( const xub_Unicode cCh, + const XubString& rBulletFollowedBy, + SwFont *pFnt, + const sal_Bool bLeft, + const sal_Bool bCenter, + const KSHORT nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ); + // <-- + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwBmpBulletPortion + *************************************************************************/ + +class SwGrfNumPortion : public SwNumberPortion +{ + SvxBrushItem* pBrush; + long nId; //fuer StopAnimation + SwTwips nYPos; //Enthaelt _immer_ die aktuelle RelPos. + SwTwips nGrfHeight; + sal_Int16 eOrient; +public: + // --> OD 2008-01-23 #newlistlevelattrs# + SwGrfNumPortion( SwFrm *pFrm, + const XubString& rGraphicFollowedBy, + const SvxBrushItem* pGrfBrush, + const SwFmtVertOrient* pGrfOrient, + const Size& rGrfSize, + const sal_Bool bLeft, + const sal_Bool bCenter, + const KSHORT nMinDst, + const bool bLabelAlignmentPosAndSpaceModeActive ); + // <-- + ~SwGrfNumPortion(); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + + void SetBase( long nLnAscent, long nLnDescent, + long nFlyAscent, long nFlyDescent ); + + void StopAnimation( OutputDevice* pOut ); + + inline sal_Bool IsAnimated() const { return bAnimated; } + inline void SetAnimated( sal_Bool bNew ) { bAnimated = bNew; } + inline sal_Bool DontPaint() const { return bNoPaint; } + inline void SetNoPaint( sal_Bool bNew ) { bNoPaint = bNew; } + inline void SetRelPos( SwTwips nNew ) { nYPos = nNew; } + inline void SetId( long nNew ) const + { ((SwGrfNumPortion*)this)->nId = nNew; } + inline SwTwips GetRelPos() const { return nYPos; } + inline SwTwips GetGrfHeight() const { return nGrfHeight; } + inline SwTwips GetId() const { return nId; } + inline sal_Int16 GetOrient() const { return eOrient; } + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwCombinedPortion + * Used in for asian layout specialities to display up to six characters + * in 2 rows and 2-3 columns. + * e.g. + * + * A.. A.. A.B A.B A.B.C A.B.C + * ... ..B .C. C.D .D.E. D.E.F + *************************************************************************/ + +class SwCombinedPortion : public SwFldPortion +{ + USHORT aPos[6]; // up to six X positions + USHORT aWidth[3]; // one width for every scripttype + BYTE aScrType[6]; // scripttype of every character + USHORT nUpPos; // the Y position of the upper baseline + USHORT nLowPos; // the Y position of the lower baseline + BYTE nProportion; // relative font height +public: + SwCombinedPortion( const XubString &rExpand ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + OUTPUT_OPERATOR +}; + + +CLASSIO( SwHiddenPortion ) +CLASSIO( SwNumberPortion ) +CLASSIO( SwBulletPortion ) +CLASSIO( SwGrfNumPortion ) +CLASSIO( SwCombinedPortion ) + + +#endif diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx new file mode 100644 index 000000000000..55c905b7e679 --- /dev/null +++ b/sw/source/core/text/porfly.cxx @@ -0,0 +1,458 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porfly.cxx,v $ + * $Revision: 1.38 $ + * + * 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 "dcontact.hxx" // SwDrawContact +#include "dflyobj.hxx" // SwVirtFlyDrawObj +#include "pam.hxx" // SwPosition +#include "flyfrm.hxx" // SwFlyInCntFrm +#include "frmfmt.hxx" // SwFrmFmt +#include "viewsh.hxx" + +#ifndef _OUTDEV_HXX //autogen +#include <vcl/outdev.hxx> +#endif +#include <svx/lrspitem.hxx> +#include <svx/ulspitem.hxx> +#include <fmtanchr.hxx> +#include <fmtflcnt.hxx> +#include <fmtornt.hxx> +#include <frmatr.hxx> +#include "flyfrms.hxx" +#include "txatbase.hxx" // SwTxtAttr +#include "porfly.hxx" +#include "porlay.hxx" // SetFly +#include "inftxt.hxx" // SwTxtPaintInfo + +// OD 2004-05-24 #i28701# +#include <sortedobjs.hxx> + +/************************************************************************* + * class SwFlyPortion + * + * Wir erwarten ein framelokales SwRect ! + *************************************************************************/ + +void SwFlyPortion::Paint( const SwTxtPaintInfo& ) const +{ +} + +/************************************************************************* + * virtual SwFlyPortion::Format() + *************************************************************************/ +sal_Bool SwFlyPortion::Format( SwTxtFormatInfo &rInf ) +{ + ASSERT( Fix() >= rInf.X(), "SwFlyPortion::Format: rush hour" ); + // 8537: Tabs muessen expandiert werden. + if( rInf.GetLastTab() ) + ((SwLinePortion*)rInf.GetLastTab())->FormatEOL( rInf ); + + // Der Glue wird aufgespannt. + rInf.GetLast()->FormatEOL( rInf ); + PrtWidth( static_cast<USHORT>(Fix() - rInf.X() + PrtWidth()) ); + if( !Width() ) + { + ASSERT( Width(), "+SwFlyPortion::Format: a fly is a fly is a fly" ); + Width(1); + } + + // Restaurierung + rInf.SetFly( 0 ); + rInf.Width( rInf.RealWidth() ); + rInf.GetParaPortion()->SetFly( sal_True ); + + // trailing blank: + if( rInf.GetIdx() < rInf.GetTxt().Len() && 1 < rInf.GetIdx() + && !rInf.GetRest() + && ' ' == rInf.GetChar( rInf.GetIdx() ) + && ' ' != rInf.GetChar( rInf.GetIdx() - 1 ) + && ( !rInf.GetLast() || !rInf.GetLast()->IsBreakPortion() ) ) + { + SetBlankWidth( rInf.GetTxtSize( ' ' ).Width() ); + SetLen( 1 ); + } + + const USHORT nNewWidth = static_cast<USHORT>(rInf.X() + PrtWidth()); + if( rInf.Width() <= nNewWidth ) + { + Truncate(); + if( nNewWidth > rInf.Width() ) + { + PrtWidth( nNewWidth - rInf.Width() ); + SetFixWidth( PrtWidth() ); + } + return sal_True; + } + return sal_False; +} + +/************************************************************************* + * virtual SwFlyCntPortion::Format() + *************************************************************************/ +sal_Bool SwFlyCntPortion::Format( SwTxtFormatInfo &rInf ) +{ + sal_Bool bFull = rInf.Width() < rInf.X() + PrtWidth(); + + if( bFull ) + { + // 3924: wenn die Zeile voll ist und der zeichengebundene Frame am + // Anfang der Zeile steht. + // 5157: nicht wenn einem Fly ausgewichen werden kann! + // "Begin of line" criteria ( ! rInf.X() ) has to be extended. + // KerningPortions at beginning of line, e.g., for grid layout + // must be considered. + const SwLinePortion* pLastPor = rInf.GetLast(); + const USHORT nLeft = ( pLastPor && + ( pLastPor->IsKernPortion() || + pLastPor->IsErgoSumPortion() ) ) ? + pLastPor->Width() : + 0; + + if( nLeft == rInf.X() && ! rInf.GetFly() ) + { + Width( rInf.Width() ); + bFull = sal_False; // Damit Notizen noch in dieser Zeile landen + } + else + { + if( !rInf.GetFly() ) + rInf.SetNewLine( sal_True ); + Width(0); + SetAscent(0); + SetLen(0); + if( rInf.GetLast() ) + rInf.GetLast()->FormatEOL( rInf ); + + return bFull; + } + } + + rInf.GetParaPortion()->SetFly( sal_True ); + return bFull; +} + +/************************************************************************* + * SwTxtFrm::MoveFlyInCnt() haengt jetzt die zeichengebundenen Objekte + * innerhalb des angegebenen Bereichs um, damit koennen diese vom Master + * zum Follow oder umgekehrt wandern. + *************************************************************************/ +void SwTxtFrm::MoveFlyInCnt( SwTxtFrm *pNew, xub_StrLen nStart, xub_StrLen nEnd ) +{ + SwSortedObjs *pObjs = 0L; + if ( 0 != (pObjs = GetDrawObjs()) ) + { + for ( sal_uInt32 i = 0; GetDrawObjs() && i < pObjs->Count(); ++i ) + { + // OD 2004-03-29 #i26791# + // --> OD 2004-07-06 #i28701# - consider changed type of + // <SwSortedList> entries + SwAnchoredObject* pAnchoredObj = (*pObjs)[i]; + const SwFmtAnchor& rAnch = pAnchoredObj->GetFrmFmt().GetAnchor(); + if ( rAnch.GetAnchorId() == FLY_IN_CNTNT ) + { + const SwPosition* pPos = rAnch.GetCntntAnchor(); + xub_StrLen nIdx = pPos->nContent.GetIndex(); + if ( nIdx >= nStart && nEnd > nIdx ) + { + if ( pAnchoredObj->ISA(SwFlyFrm) ) + { + RemoveFly( static_cast<SwFlyFrm*>(pAnchoredObj) ); + pNew->AppendFly( static_cast<SwFlyFrm*>(pAnchoredObj) ); + } + else if ( pAnchoredObj->ISA(SwAnchoredDrawObject) ) + { + RemoveDrawObj( *pAnchoredObj ); + pNew->AppendDrawObj( *pAnchoredObj ); + } + --i; + } + } + // <-- + } + } +} + +/************************************************************************* + * SwTxtFrm::CalcFlyPos() + *************************************************************************/ +xub_StrLen SwTxtFrm::CalcFlyPos( SwFrmFmt* pSearch ) +{ + SwpHints* pHints = GetTxtNode()->GetpSwpHints(); + ASSERT( pHints, "CalcFlyPos: Why me?" ); + if( !pHints ) + return STRING_LEN; + SwTxtAttr* pFound = NULL; + for ( USHORT i = 0; i < pHints->Count(); i++) + { + SwTxtAttr *pHt = pHints->GetTextHint( i ); + if( RES_TXTATR_FLYCNT == pHt->Which() ) + { + SwFrmFmt* pFrmFmt = pHt->GetFlyCnt().GetFrmFmt(); + if( pFrmFmt == pSearch ) + pFound = pHt; + } + } + ASSERT( pHints, "CalcFlyPos: Not Found!" ); + if( !pFound ) + return STRING_LEN; + return *pFound->GetStart(); +} + +/************************************************************************* + * virtual SwFlyCntPortion::Paint() + *************************************************************************/ +void SwFlyCntPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( bDraw ) + { + if( !((SwDrawContact*)pContact)->GetAnchorFrm() ) + { + // OD 2004-04-01 #i26791# - no direct positioning of the drawing + // object is needed. + SwDrawContact* pDrawContact = static_cast<SwDrawContact*>(pContact); + pDrawContact->ConnectToLayout(); + } + } + else + { + // Baseline-Ausgabe ! + // 7922: Bei CompletePaint alles painten + SwRect aRepaintRect( rInf.GetPaintRect() ); + + if ( rInf.GetTxtFrm()->IsRightToLeft() ) + rInf.GetTxtFrm()->SwitchLTRtoRTL( aRepaintRect ); + + if ( rInf.GetTxtFrm()->IsVertical() ) + rInf.GetTxtFrm()->SwitchHorizontalToVertical( aRepaintRect ); + + if( (GetFlyFrm()->IsCompletePaint() || + GetFlyFrm()->Frm().IsOver( aRepaintRect )) && + SwFlyFrm::IsPaint( (SdrObject*)GetFlyFrm()->GetVirtDrawObj(), + GetFlyFrm()->GetShell() )) + { + SwRect aRect( GetFlyFrm()->Frm() ); + if( !GetFlyFrm()->IsCompletePaint() ) + aRect._Intersection( aRepaintRect ); + + + // GetFlyFrm() may change the layout mode at the output device. + { + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + GetFlyFrm()->Paint( aRect ); + } + ((SwTxtPaintInfo&)rInf).GetRefDev()->SetLayoutMode( + rInf.GetOut()->GetLayoutMode() ); + + // Es hilft alles nichts, im zeichengebundenen Frame kann wer weiss + // was am OutputDevice eingestellt sein, wir muessen unseren Font + // wieder hineinselektieren. Dass wir im const stehen, soll uns + // daran nicht hindern: + ((SwTxtPaintInfo&)rInf).SelectFont(); + + // I want to know if this can really happen. So here comes a new + ASSERT( ! rInf.GetVsh() || rInf.GetVsh()->GetOut() == rInf.GetOut(), + "SwFlyCntPortion::Paint: Outdev has changed" ) + if( rInf.GetVsh() ) + ((SwTxtPaintInfo&)rInf).SetOut( rInf.GetVsh()->GetOut() ); + } + } +} + +/************************************************************************* + * SwFlyCntPortion::SwFlyCntPortion() + * + * Es werden die Masze vom pFly->OutRect() eingestellt. + * Es erfolgt ein SetBase() ! + *************************************************************************/ +// OD 29.07.2003 #110978# - use new datatype for parameter <nFlags> +SwFlyCntPortion::SwFlyCntPortion( const SwTxtFrm& rFrm, + SwFlyInCntFrm *pFly, const Point &rBase, + long nLnAscent, long nLnDescent, + long nFlyAsc, long nFlyDesc, + objectpositioning::AsCharFlags nFlags ) : + pContact( pFly ), + bDraw( sal_False ), + bMax( sal_False ), + nAlign( 0 ) +{ + ASSERT( pFly, "SwFlyCntPortion::SwFlyCntPortion: no SwFlyInCntFrm!" ); + nLineLength = 1; + nFlags |= AS_CHAR_ULSPACE | AS_CHAR_INIT; + SetBase( rFrm, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags ); + SetWhichPor( POR_FLYCNT ); +} + +// OD 29.07.2003 #110978# - use new datatype for parameter <nFlags> +SwFlyCntPortion::SwFlyCntPortion( const SwTxtFrm& rFrm, + SwDrawContact *pDrawContact, const Point &rBase, + long nLnAscent, long nLnDescent, + long nFlyAsc, long nFlyDesc, + objectpositioning::AsCharFlags nFlags ) : + pContact( pDrawContact ), + bDraw( sal_True ), + bMax( sal_False ), + nAlign( 0 ) +{ + ASSERT( pDrawContact, "SwFlyCntPortion::SwFlyCntPortion: no SwDrawContact!" ); + if( !pDrawContact->GetAnchorFrm() ) + { + // OD 2004-04-01 #i26791# - no direct positioning needed any more + pDrawContact->ConnectToLayout(); + // --> OD 2005-01-14 #i40333# - follow-up of #i35635# + // move object to visible layer + pDrawContact->MoveObjToVisibleLayer( pDrawContact->GetMaster() ); + // <-- + } + nLineLength = 1; + nFlags |= AS_CHAR_ULSPACE | AS_CHAR_INIT; + + SetBase( rFrm, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags ); + + SetWhichPor( POR_FLYCNT ); +} + + +/************************************************************************* + * SwFlyCntPortion::SetBase() + * + * Nach dem Setzen des RefPoints muss der Ascent neu berechnet werden, + * da er von der RelPos abhaengt. + * pFly->GetRelPos().Y() bezeichnet die relative Position zur Baseline. + * Bei 0 liegt der obere Rand des FlyCnt auf der Baseline der Zeile. + *************************************************************************/ +// OD 29.07.2003 #110978# - use new datatype for parameter <nFlags> +void SwFlyCntPortion::SetBase( const SwTxtFrm& rFrm, const Point &rBase, + long nLnAscent, long nLnDescent, + long nFlyAsc, long nFlyDesc, + objectpositioning::AsCharFlags nFlags ) +{ + // Note: rBase have to be an absolute value + + // OD 28.10.2003 #113049# - use new class to position object + // determine drawing object + SdrObject* pSdrObj = 0L; + if( bDraw ) + { + // OD 20.06.2003 #108784# - determine drawing object ('master' or 'virtual') + // by frame. + pSdrObj = GetDrawContact()->GetDrawObjectByAnchorFrm( rFrm ); + if ( !pSdrObj ) + { + ASSERT( false, "SwFlyCntPortion::SetBase(..) - No drawing object found by <GetDrawContact()->GetDrawObjectByAnchorFrm( rFrm )>" ); + pSdrObj = GetDrawContact()->GetMaster(); + } + // --> OD 2007-11-29 #i65798# + // call <SwAnchoredDrawObject::MakeObjPos()> to assure that flag at + // the <DrawFrmFmt> and at the <SwAnchoredDrawObject> instance are + // correctly set. + if ( pSdrObj ) + { + GetDrawContact()->GetAnchoredObj( pSdrObj )->MakeObjPos(); + } + // <-- + } + else + { + pSdrObj = GetFlyFrm()->GetVirtDrawObj(); + } + + // position object + objectpositioning::SwAsCharAnchoredObjectPosition aObjPositioning( + *pSdrObj, + rBase, nFlags, + nLnAscent, nLnDescent, nFlyAsc, nFlyDesc ); + + // OD 2004-04-13 #i26791# - scope of local variable <aObjPosInProgress> + { + // OD 2004-04-13 #i26791# + SwObjPositioningInProgress aObjPosInProgress( *pSdrObj ); + aObjPositioning.CalcPosition(); + } + + SetAlign( aObjPositioning.GetLineAlignment() ); + + aRef = aObjPositioning.GetAnchorPos(); + if( nFlags & AS_CHAR_ROTATE ) + SvXSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() ); + else + SvLSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() ); + if( Height() ) + { + SwTwips nRelPos = aObjPositioning.GetRelPosY(); + if ( nRelPos < 0 ) + { + nAscent = static_cast<USHORT>(-nRelPos); + if( nAscent > Height() ) + Height( nAscent ); + } + else + { + nAscent = 0; + Height( Height() + static_cast<USHORT>(nRelPos) ); + } + } + else + { + Height( 1 ); + nAscent = 0; + } +} + +/************************************************************************* + * virtual SwFlyCntPortion::GetFlyCrsrOfst() + *************************************************************************/ + +xub_StrLen SwFlyCntPortion::GetFlyCrsrOfst( const KSHORT nOfst, + const Point &rPoint, SwPosition *pPos, SwCrsrMoveState* pCMS ) const +{ + // Da die FlyCnt nicht an der Seite haengen, wird ihr + // GetCrsrOfst() nicht gerufen. Um die Layoutseite + // von unnoetiger Verwaltung zu entlasten, ruft der Absatz + // das GetCrsrOfst des FlyFrm, wenn es erforderlich ist. + Point aPoint( rPoint ); + if( !pPos || bDraw || !( GetFlyFrm()->GetCrsrOfst( pPos, aPoint, pCMS ) ) ) + return SwLinePortion::GetCrsrOfst( nOfst ); + else + return 0; +} + +/************************************************************************* + * virtual SwFlyCntPortion::GetCrsrOfst() + *************************************************************************/ + +xub_StrLen SwFlyCntPortion::GetCrsrOfst( const KSHORT nOfst ) const +{ + // ASSERT( !this, "SwFlyCntPortion::GetCrsrOfst: use GetFlyCrsrOfst()" ); + return SwLinePortion::GetCrsrOfst( nOfst ); +} + diff --git a/sw/source/core/text/porfly.hxx b/sw/source/core/text/porfly.hxx new file mode 100644 index 000000000000..7c98144b05b6 --- /dev/null +++ b/sw/source/core/text/porfly.hxx @@ -0,0 +1,111 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porfly.hxx,v $ + * $Revision: 1.14.214.1 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORFLY_HXX +#define _PORFLY_HXX +// OD 28.10.2003 #113049# +#include <ascharanchoredobjectposition.hxx> + +#include "porglue.hxx" + +class SwDrawContact; +class SwFlyInCntFrm; +class SwTxtFrm; +struct SwCrsrMoveState; + +/************************************************************************* + * class SwFlyPortion + *************************************************************************/ + +class SwFlyPortion : public SwFixPortion +{ + KSHORT nBlankWidth; +public: + inline SwFlyPortion( const SwRect &rFlyRect ) + : SwFixPortion(rFlyRect), nBlankWidth( 0 ) { SetWhichPor( POR_FLY ); } + inline KSHORT GetBlankWidth( ) const { return nBlankWidth; } + inline void SetBlankWidth( const KSHORT nNew ) { nBlankWidth = nNew; } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwFlyCntPortion + *************************************************************************/ + +class SwFlyCntPortion : public SwLinePortion +{ + void *pContact; // bDraw ? DrawContact : FlyInCntFrm + Point aRef; // Relativ zu diesem Point wird die AbsPos berechnet. + sal_Bool bDraw : 1; // DrawContact? + sal_Bool bMax : 1; // Zeilenausrichtung und Hoehe == Zeilenhoehe + sal_uInt8 nAlign : 3; // Zeilenausrichtung? Nein, oben, mitte, unten + virtual xub_StrLen GetCrsrOfst( const KSHORT nOfst ) const; + +public: + // OD 29.07.2003 #110978# - use new datatype for parameter <nFlags> + SwFlyCntPortion( const SwTxtFrm& rFrm, SwFlyInCntFrm *pFly, + const Point &rBase, + long nAscent, long nDescent, long nFlyAsc, long nFlyDesc, + objectpositioning::AsCharFlags nFlags ); + // OD 29.07.2003 #110978# - use new datatype for parameter <nFlags> + SwFlyCntPortion( const SwTxtFrm& rFrm, SwDrawContact *pDrawContact, + const Point &rBase, + long nAscent, long nDescent, long nFlyAsc, long nFlyDesc, + objectpositioning::AsCharFlags nFlags ); + inline const Point& GetRefPoint() const { return aRef; } + inline SwFlyInCntFrm *GetFlyFrm() { return (SwFlyInCntFrm*)pContact; } + inline const SwFlyInCntFrm *GetFlyFrm() const + { return (SwFlyInCntFrm*)pContact; } + inline SwDrawContact *GetDrawContact() { return (SwDrawContact*)pContact; } + inline const SwDrawContact* GetDrawContact() const + { return (SwDrawContact*)pContact; } + inline sal_Bool IsDraw() const { return bDraw; } + inline sal_Bool IsMax() const { return bMax; } + inline sal_uInt8 GetAlign() const { return nAlign; } + inline void SetAlign( sal_uInt8 nNew ) { nAlign = nNew; } + inline void SetMax( sal_Bool bNew ) { bMax = bNew; } + // OD 29.07.2003 #110978# - use new datatype for parameter <nFlags> + void SetBase( const SwTxtFrm& rFrm, const Point &rBase, + long nLnAscent, long nLnDescent, + long nFlyAscent, long nFlyDescent, + objectpositioning::AsCharFlags nFlags ); + xub_StrLen GetFlyCrsrOfst( const KSHORT nOfst, const Point &rPoint, + SwPosition *pPos, SwCrsrMoveState* pCMS ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + OUTPUT_OPERATOR +}; + +CLASSIO( SwFlyPortion ) +CLASSIO( SwFlyCntPortion ) + + +#endif diff --git a/sw/source/core/text/porftn.hxx b/sw/source/core/text/porftn.hxx new file mode 100644 index 000000000000..279d222d3e98 --- /dev/null +++ b/sw/source/core/text/porftn.hxx @@ -0,0 +1,133 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porftn.hxx,v $ + * $Revision: 1.12 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORFTN_HXX +#define _PORFTN_HXX + +#include "porfld.hxx" + +class SwTxtFrm; +class SwTxtFtn; + +/************************************************************************* + * class SwFtnPortion + *************************************************************************/ + +class SwFtnPortion : public SwFldPortion +{ + SwTxtFrm *pFrm; // um im Dtor RemoveFtn rufen zu koennen. + SwTxtFtn *pFtn; + KSHORT nOrigHeight; + // --> OD 2009-01-29 #i98418# + bool mbPreferredScriptTypeSet; + BYTE mnPreferredScriptType; + // <-- +public: + SwFtnPortion( const XubString &rExpand, SwTxtFrm *pFrm, SwTxtFtn *pFtn, + KSHORT nOrig = KSHRT_MAX ); + inline KSHORT& Orig() { return nOrigHeight; } + + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + + // --> OD 2009-01-29 #i98418# + void SetPreferredScriptType( BYTE nPreferredScriptType ); + // <-- + + const SwTxtFtn* GetTxtFtn() const { return pFtn; }; + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwFtnNumPortion + *************************************************************************/ + +class SwFtnNumPortion : public SwNumberPortion +{ +public: + inline SwFtnNumPortion( const XubString &rExpand, SwFont *pFntL ) + // --> OD 2008-01-23 #newlistlevelattrs# + : SwNumberPortion( rExpand, pFntL, sal_True, sal_False, 0, false ) + // <-- + { SetWhichPor( POR_FTNNUM ); } + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwQuoVadisPortion + *************************************************************************/ + +class SwQuoVadisPortion : public SwFldPortion +{ + XubString aErgo; +public: + SwQuoVadisPortion( const XubString &rExp, const XubString& rStr ); + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + + inline void SetNumber( const XubString& rStr ) { aErgo = rStr; } + inline const XubString &GetQuoTxt() const { return aExpand; } + inline const XubString &GetContTxt() const { return aErgo; } + + // Felder-Cloner fuer SplitGlue + virtual SwFldPortion *Clone( const XubString &rExpand ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwErgoSumPortion + *************************************************************************/ + +class SwErgoSumPortion : public SwFldPortion +{ +public: + SwErgoSumPortion( const XubString &rExp, const XubString& rStr ); + virtual xub_StrLen GetCrsrOfst( const KSHORT nOfst ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + + // Felder-Cloner fuer SplitGlue + virtual SwFldPortion *Clone( const XubString &rExpand ) const; + OUTPUT_OPERATOR +}; + +CLASSIO( SwFtnPortion ) +CLASSIO( SwFtnNumPortion ) +CLASSIO( SwQuoVadisPortion ) +CLASSIO( SwErgoSumPortion ) + + +#endif diff --git a/sw/source/core/text/porglue.cxx b/sw/source/core/text/porglue.cxx new file mode 100644 index 000000000000..6dec3c790283 --- /dev/null +++ b/sw/source/core/text/porglue.cxx @@ -0,0 +1,321 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porglue.cxx,v $ + * $Revision: 1.13 $ + * + * 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 "swrect.hxx" +#include "paratr.hxx" // pTabStop, ADJ* +#include "viewopt.hxx" // SwViewOptions +#include "errhdl.hxx" // ASSERT +#include <SwPortionHandler.hxx> + +#include "txtcfg.hxx" +#include "porglue.hxx" +#include "inftxt.hxx" +#include "porlay.hxx" // SwParaPortion, SetFull +#include "porfly.hxx" // SwParaPortion, SetFull + +/************************************************************************* + * class SwGluePortion + *************************************************************************/ + +SwGluePortion::SwGluePortion( const KSHORT nInitFixWidth ) + : nFixWidth( nInitFixWidth ) +{ + PrtWidth( nFixWidth ); + SetWhichPor( POR_GLUE ); +} + +/************************************************************************* + * virtual SwGluePortion::GetCrsrOfst() + *************************************************************************/ + +xub_StrLen SwGluePortion::GetCrsrOfst( const KSHORT nOfst ) const +{ + if( !GetLen() || nOfst > GetLen() || !Width() ) + return SwLinePortion::GetCrsrOfst( nOfst ); + else + return nOfst / (Width() / GetLen()); +} + +/************************************************************************* + * virtual SwGluePortion::GetTxtSize() + *************************************************************************/ + +SwPosSize SwGluePortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const +{ + if( 1 >= GetLen() || rInf.GetLen() > GetLen() || !Width() || !GetLen() ) + return SwPosSize(*this); + else + return SwPosSize( (Width() / GetLen()) * rInf.GetLen(), Height() ); +} + +/************************************************************************* + * virtual SwGluePortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwGluePortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const +{ + if( GetLen() && rInf.OnWin() && + rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() ) + { + rTxt.Fill( GetLen(), CH_BULLET ); + return sal_True; + } + return sal_False; +} + +/************************************************************************* + * virtual SwGluePortion::Paint() + *************************************************************************/ + +void SwGluePortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( !GetLen() ) + return; + + if( rInf.GetFont()->IsPaintBlank() ) + { + XubString aTxt; + aTxt.Fill( GetFixWidth() / GetLen(), ' ' ); + SwTxtPaintInfo aInf( rInf, aTxt ); + aInf.DrawText( *this, aTxt.Len(), sal_True ); + } + + if( rInf.OnWin() && rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() ) + { +#ifndef PRODUCT + const xub_Unicode cChar = rInf.GetChar( rInf.GetIdx() ); + ASSERT( CH_BLANK == cChar || CH_BULLET == cChar, + "SwGluePortion::Paint: blank expected" ); +#endif + if( 1 == GetLen() ) + { + String aBullet( CH_BULLET ); + SwPosSize aBulletSize( rInf.GetTxtSize( aBullet ) ); + Point aPos( rInf.GetPos() ); + aPos.X() += (Width()/2) - (aBulletSize.Width()/2); + SwTxtPaintInfo aInf( rInf, aBullet ); + aInf.SetPos( aPos ); + SwTxtPortion aBulletPor; + aBulletPor.Width( aBulletSize.Width() ); + aBulletPor.Height( aBulletSize.Height() ); + aBulletPor.SetAscent( GetAscent() ); + aInf.DrawText( aBulletPor, aBullet.Len(), sal_True ); + } + else + { + SwTxtSlot aSlot( &rInf, this, true, false ); + rInf.DrawText( *this, rInf.GetLen(), sal_True ); + } + } +} + +/************************************************************************* + * SwGluePortion::MoveGlue() + *************************************************************************/ + +void SwGluePortion::MoveGlue( SwGluePortion *pTarget, const short nPrtGlue ) +{ + short nPrt = Min( nPrtGlue, GetPrtGlue() ); + if( 0 < nPrt ) + { + pTarget->AddPrtWidth( nPrt ); + SubPrtWidth( nPrt ); + } +} + +/************************************************************************* + * void SwGluePortion::Join() + *************************************************************************/ + +void SwGluePortion::Join( SwGluePortion *pVictim ) +{ + // Die GluePortion wird ausgesogen und weggespuelt ... + AddPrtWidth( pVictim->PrtWidth() ); + SetLen( pVictim->GetLen() + GetLen() ); + if( Height() < pVictim->Height() ) + Height( pVictim->Height() ); + + AdjFixWidth(); + Cut( pVictim ); + delete pVictim; +} + +/************************************************************************* + * class SwFixPortion + *************************************************************************/ + +// Wir erwarten ein framelokales SwRect ! +SwFixPortion::SwFixPortion( const SwRect &rRect ) + :SwGluePortion( KSHORT(rRect.Width()) ), nFix( KSHORT(rRect.Left()) ) +{ + Height( KSHORT(rRect.Height()) ); + SetWhichPor( POR_FIX ); +} + +SwFixPortion::SwFixPortion(const KSHORT nFixedWidth, const KSHORT nFixedPos) + : SwGluePortion(nFixedWidth), nFix(nFixedPos) +{ + SetWhichPor( POR_FIX ); +} + +/************************************************************************* + * class SwMarginPortion + *************************************************************************/ + +SwMarginPortion::SwMarginPortion( const KSHORT nFixedWidth ) + :SwGluePortion( nFixedWidth ) +{ + SetWhichPor( POR_MARGIN ); +} + +/************************************************************************* + * SwMarginPortion::AdjustRight() + * + * In der umschliessenden Schleife werden alle Portions durchsucht, + * dabei werden erst die am Ende liegenden GluePortions verarbeitet. + * Das Ende wird nach jeder Schleife nach vorne verlegt, bis keine + * GluePortions mehr vorhanden sind. + * Es werden immer GluePortion-Paare betrachtet (pLeft und pRight), + * wobei Textportions zwischen pLeft und pRight hinter pRight verschoben + * werden, wenn pRight genuegend Glue besitzt. Bei jeder Verschiebung + * wandert ein Teil des Glues von pRight nach pLeft. + * Im naechsten Schleifendurchlauf ist pLeft das pRight und das Spiel + * beginnt von vorne. + *************************************************************************/ + +void SwMarginPortion::AdjustRight( const SwLineLayout *pCurr ) +{ + SwGluePortion *pRight = 0; + BOOL bNoMove = 0 != pCurr->GetpKanaComp(); + while( pRight != this ) + { + + // 1) Wir suchen den linken Glue + SwLinePortion *pPos = (SwLinePortion*)this; + SwGluePortion *pLeft = 0; + while( pPos ) + { + DBG_LOOP; + if( pPos->InFixMargGrp() ) + pLeft = (SwGluePortion*)pPos; + pPos = pPos->GetPortion(); + if( pPos == pRight) + pPos = 0; + } + + // Zwei nebeneinander liegende FlyPortions verschmelzen + if( pRight && pLeft->GetPortion() == pRight ) + { + pRight->MoveAllGlue( pLeft ); + pRight = 0; + } + KSHORT nRightGlue = pRight && 0 < pRight->GetPrtGlue() + ? KSHORT(pRight->GetPrtGlue()) : 0; + // 2) linken und rechten Glue ausgleichen + // Bei Tabs haengen wir nix um ... + if( pLeft && nRightGlue && !pRight->InTabGrp() ) + { + // pPrev ist die Portion, die unmittelbar vor pRight liegt. + SwLinePortion *pPrev = pRight->FindPrevPortion( pLeft ); + + if ( pRight->IsFlyPortion() && pRight->GetLen() ) + { + SwFlyPortion *pFly = (SwFlyPortion *)pRight; + if ( pFly->GetBlankWidth() < nRightGlue ) + { + // Hier entsteht eine neue TxtPortion, die dass zuvor + // vom Fly verschluckte Blank reaktiviert. + nRightGlue = nRightGlue - pFly->GetBlankWidth(); + pFly->SubPrtWidth( pFly->GetBlankWidth() ); + pFly->SetLen( 0 ); + SwTxtPortion *pNewPor = new SwTxtPortion; + pNewPor->SetLen( 1 ); + pNewPor->Height( pFly->Height() ); + pNewPor->Width( pFly->GetBlankWidth() ); + pFly->Insert( pNewPor ); + } + else + pPrev = pLeft; + } + while( pPrev != pLeft ) + { + DBG_LOOP; + + if( bNoMove || pPrev->PrtWidth() >= nRightGlue || + pPrev->InHyphGrp() || pPrev->IsKernPortion() ) + { + // Die Portion, die vor pRight liegt kann nicht + // verschoben werden, weil kein Glue mehr vorhanden ist. + // Wir fuehren die Abbruchbedingung herbei: + pPrev = pLeft; + } + else + { + nRightGlue = nRightGlue - pPrev->PrtWidth(); + // pPrev wird hinter pRight verschoben. + // Dazu wird der Gluewert zwischen pRight und pLeft + // ausgeglichen. + pRight->MoveGlue( pLeft, short( pPrev->PrtWidth() ) ); + // Jetzt wird die Verkettung gerichtet. + SwLinePortion *pPrevPrev = pPrev->FindPrevPortion( pLeft ); + pPrevPrev->SetPortion( pRight ); + pPrev->SetPortion( pRight->GetPortion() ); + pRight->SetPortion( pPrev ); + if ( pPrev->GetPortion() && pPrev->InTxtGrp() + && pPrev->GetPortion()->IsHolePortion() ) + { + SwHolePortion *pHolePor = + (SwHolePortion*)pPrev->GetPortion(); + if ( !pHolePor->GetPortion() || + !pHolePor->GetPortion()->InFixMargGrp() ) + { + pPrev->AddPrtWidth( pHolePor->GetBlankWidth() ); + pPrev->SetLen( pPrev->GetLen() + 1 ); + pPrev->SetPortion( pHolePor->GetPortion() ); + delete pHolePor; + } + } + pPrev = pPrevPrev; + } + } + } + // Wenn es keinen linken Glue mehr gibt, wird die Abbruchbedingung + // herbeigefuehrt. + pRight = pLeft ? pLeft : (SwGluePortion*)this; + } +} + + + diff --git a/sw/source/core/text/porglue.hxx b/sw/source/core/text/porglue.hxx new file mode 100644 index 000000000000..3fff7546d0c3 --- /dev/null +++ b/sw/source/core/text/porglue.hxx @@ -0,0 +1,137 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porglue.hxx,v $ + * $Revision: 1.8 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORGLUE_HXX +#define _PORGLUE_HXX + +//#include <stdlib.h> + +#include "porlin.hxx" + +class SwRect; +class SwLineLayout; + +/************************************************************************* + * class SwGluePortion + *************************************************************************/ + +class SwGluePortion : public SwLinePortion +{ +private: + KSHORT nFixWidth; +public: + SwGluePortion( const KSHORT nInitFixWidth ); + + void Join( SwGluePortion *pVictim ); + + inline short GetPrtGlue() const; + inline KSHORT GetFixWidth() const { return nFixWidth; } + inline void SetFixWidth( const KSHORT nNew ) { nFixWidth = nNew; } + void MoveGlue( SwGluePortion *pTarget, const short nPrtGlue ); + inline void MoveAllGlue( SwGluePortion *pTarget ); + inline void MoveHalfGlue( SwGluePortion *pTarget ); + inline void AdjFixWidth(); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual xub_StrLen GetCrsrOfst( const KSHORT nOfst ) const; + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwFixPortion + *************************************************************************/ + +class SwFixPortion : public SwGluePortion +{ + KSHORT nFix; // der Width-Offset in der Zeile +public: + SwFixPortion( const SwRect &rFlyRect ); + SwFixPortion( const KSHORT nFixWidth, const KSHORT nFixPos ); + inline void Fix( const KSHORT nNewFix ) { nFix = nNewFix; } + inline KSHORT Fix() const { return nFix; } + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwMarginPortion + *************************************************************************/ + +class SwMarginPortion : public SwGluePortion +{ +public: + SwMarginPortion( const KSHORT nFixWidth ); + void AdjustRight( const SwLineLayout* pCurr ); + OUTPUT_OPERATOR +}; + +/************************************************************************* + * inline SwGluePortion::GetPrtGlue() + *************************************************************************/ + +inline short SwGluePortion::GetPrtGlue() const +{ return Width() - nFixWidth; } + +/************************************************************************* + * inline SwGluePortion::AdjFixWidth() + * Die FixWidth darf niemals groesser sein als die Gesamtbreite ! + *************************************************************************/ + +inline void SwGluePortion::AdjFixWidth() +{ + if( nFixWidth > PrtWidth() ) + nFixWidth = PrtWidth(); +} + +/************************************************************************* + * inline SwGluePortion::MoveGlue() + *************************************************************************/ + +inline void SwGluePortion::MoveAllGlue( SwGluePortion *pTarget ) +{ + MoveGlue( pTarget, GetPrtGlue() ); +} + +/************************************************************************* + * inline SwGluePortion::MoveHalfGlue() + *************************************************************************/ + +inline void SwGluePortion::MoveHalfGlue( SwGluePortion *pTarget ) +{ + MoveGlue( pTarget, GetPrtGlue() / 2 ); +} + +CLASSIO( SwGluePortion ) +CLASSIO( SwFixPortion ) +CLASSIO( SwMarginPortion ) + + +#endif + diff --git a/sw/source/core/text/porhyph.hxx b/sw/source/core/text/porhyph.hxx new file mode 100644 index 000000000000..3cf8e9ac3c4f --- /dev/null +++ b/sw/source/core/text/porhyph.hxx @@ -0,0 +1,122 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porhyph.hxx,v $ + * $Revision: 1.5 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORHYPH_HXX +#define _PORHYPH_HXX + +#include "porexp.hxx" + +/************************************************************************* + * class SwHyphPortion + *************************************************************************/ + +class SwHyphPortion : public SwExpandPortion +{ +public: + inline SwHyphPortion( ) { SetWhichPor( POR_HYPH ); } + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwHyphStrPortion + *************************************************************************/ + +class SwHyphStrPortion : public SwHyphPortion +{ + XubString aExpand; +public: + inline SwHyphStrPortion( const XubString &rStr ); + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwSoftHyphPortion + *************************************************************************/ + +class SwSoftHyphPortion : public SwHyphPortion +{ + sal_Bool bExpand; + KSHORT nViewWidth; + KSHORT nHyphWidth; + +public: + SwSoftHyphPortion(); + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual SwLinePortion *Compress(); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + inline void SetExpand( const sal_Bool bNew ) { bExpand = bNew; } + sal_Bool IsExpand() const { return bExpand; } + + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwSoftHyphStrPortion + *************************************************************************/ + +class SwSoftHyphStrPortion : public SwHyphStrPortion +{ +public: + SwSoftHyphStrPortion( const XubString &rStr ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + OUTPUT_OPERATOR +}; + +inline SwHyphStrPortion::SwHyphStrPortion( const XubString &rStr ) + : aExpand( rStr ) +{ + aExpand += '-'; + SetWhichPor( POR_HYPHSTR ); +} + +CLASSIO( SwHyphPortion ) +CLASSIO( SwHyphStrPortion ) +CLASSIO( SwSoftHyphPortion ) +CLASSIO( SwSoftHyphStrPortion ) + + +#endif diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx new file mode 100644 index 000000000000..76eba6b1e77b --- /dev/null +++ b/sw/source/core/text/porlay.cxx @@ -0,0 +1,2465 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * 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 "errhdl.hxx" // ASSERT + +#include "txtcfg.hxx" +#include "porlay.hxx" +#include "itrform2.hxx" +#include "porglue.hxx" +#include "porexp.hxx" // SwQuoVadisPortion +#include "blink.hxx" // pBlink +#include "redlnitr.hxx" // SwRedlineItr +#include "porfly.hxx" // SwFlyCntPortion +#include <porrst.hxx> // SwHangingPortion +#include <pormulti.hxx> // SwMultiPortion +#include <breakit.hxx> +#include <unicode/uchar.h> +#include <com/sun/star/i18n/ScriptType.hdl> +#include <com/sun/star/i18n/CTLScriptType.hdl> +#include <com/sun/star/i18n/WordType.hdl> +#include <paratr.hxx> +#include <svx/adjitem.hxx> +#include <svx/scripttypeitem.hxx> +#include <svx/charhiddenitem.hxx> +#include <vcl/outdev.hxx> +#include <svx/blnkitem.hxx> +#include <tools/multisel.hxx> +#include <unotools/charclass.hxx> +#include <i18npool/mslangid.hxx> +#include <charfmt.hxx> +#include <fchrfmt.hxx> +#include <docary.hxx> // SwRedlineTbl +#include <redline.hxx> // SwRedline + +// --> FME 2004-06-08 #i12836# enhanced pdf export +#include <section.hxx> +// <-- + +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentContentOperations.hxx> + +using namespace ::com::sun::star; +using namespace i18n::ScriptType; + +//#ifdef BIDI +#include <unicode/ubidi.h> +#include <i18nutil/unicode.hxx> //unicode::getUnicodeScriptType + +sal_Bool isAlefChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x622 || cCh == 0x623 || cCh == 0x625 || cCh == 0x627 || + cCh == 0x622 || cCh == 0x671 || cCh == 0x672 || cCh == 0x673 || cCh == 0x675 ); +} + +sal_Bool isWawChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x624 || cCh == 0x648 || cCh == 0x676 || cCh == 0x677 || + ( cCh >= 0x6C4 && cCh <= 0x6CB ) || cCh == 0x6CF ); +} + +sal_Bool isDalChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x62F || cCh == 0x630 || cCh == 0x688 || cCh == 0x689 || cCh == 0x690 ); +} + +sal_Bool isRehChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x631 || cCh == 0x632 || ( cCh >= 0x691 && cCh <= 0x699 )); +} + +sal_Bool isTehMarbutaChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x629 || cCh == 0x6C0 ); +} + +sal_Bool isBaaChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x628 || cCh == 0x62A || cCh == 0x62B || cCh == 0x679 || cCh == 0x680 ); +} + +sal_Bool isYehChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x626 || cCh == 0x649 || cCh == 0x64A || cCh == 0x678 || cCh == 0x6CC || + cCh == 0x6CE || cCh == 0x6D0 || cCh == 0x6D1 ); +} + +sal_Bool isSeenOrSadChar ( xub_Unicode cCh ) +{ + return ( ( cCh >= 0x633 && cCh <= 0x636 ) || ( cCh >= 0x69A && cCh <= 0x69E ) + || cCh == 0x6FA || cCh == 0x6FB ); +} + +sal_Bool isHahChar ( xub_Unicode cCh ) +{ + return ( ( cCh >= 0x62C && cCh <= 0x62E ) || ( cCh >= 0x681 && cCh <= 0x687 ) + || cCh == 0x6BF ); +} + +sal_Bool isTahChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x637 || cCh == 0x638 || cCh == 0x69F ); +} + +sal_Bool isAinChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x639 || cCh == 0x63A || cCh == 0x6A0 || cCh == 0x6FC ); +} + +sal_Bool isKafChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x643 || ( cCh >= 0x6AC && cCh <= 0x6AE ) ); +} + +sal_Bool isLamChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x644 || ( cCh >= 0x6B5 && cCh <= 0x6B8 ) ); +} + +sal_Bool isGafChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x6A9 || cCh == 0x6AB ||( cCh >= 0x6AF && cCh <= 0x6B4 ) ); +} + +sal_Bool isQafChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x642 || cCh == 0x6A7 || cCh == 0x6A8 ); +} + +sal_Bool isFeChar ( xub_Unicode cCh ) +{ + return ( cCh == 0x641 || ( cCh >= 0x6A1 && cCh <= 0x6A6 ) ); +} +sal_Bool isTransparentChar ( xub_Unicode cCh ) +{ + return ( ( cCh >= 0x610 && cCh <= 0x61A ) || + ( cCh >= 0x64B && cCh <= 0x65E ) || + ( cCh == 0x670 ) || + ( cCh >= 0x6D6 && cCh <= 0x6DC ) || + ( cCh >= 0x6DF && cCh <= 0x6E4 ) || + ( cCh >= 0x6E7 && cCh <= 0x6E8 ) || + ( cCh >= 0x6EA && cCh <= 0x6ED )); +} + +/************************************************************************* + * lcl_IsLigature + * + * Checks if cCh + cNectCh builds a ligature (used for Kashidas) + *************************************************************************/ + +sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh ) +{ + // Lam + Alef + return ( isLamChar ( cCh ) && isAlefChar ( cNextCh )); +} + +/************************************************************************* + * lcl_ConnectToPrev + * + * Checks if cCh is connectable to cPrevCh (used for Kashidas) + *************************************************************************/ + +sal_Bool lcl_ConnectToPrev( xub_Unicode cCh, xub_Unicode cPrevCh ) +{ + // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left + // Uh, there seem to be some more characters that are not connectable + // to the left. So we look for the characters that are actually connectable + // to the left. Here is the complete list of WH: + + // (hennerdrewes): + // added lam forms 0x06B5..0x06B8 + // added 0x6FA..0x6FC, according to unicode documentation, although not present in my fonts + // added heh goal 0x6C1 + sal_Bool bRet = 0x628 == cPrevCh || + ( 0x62A <= cPrevCh && cPrevCh <= 0x62E ) || + ( 0x633 <= cPrevCh && cPrevCh <= 0x647 ) || + 0x649 == cPrevCh || // Alef Maksura does connect !!! + 0x64A == cPrevCh || + ( 0x678 <= cPrevCh && cPrevCh <= 0x687 ) || + ( 0x69A <= cPrevCh && cPrevCh <= 0x6C1 ) || + ( 0x6C3 <= cPrevCh && cPrevCh <= 0x6D3 ) || + ( 0x6FA <= cPrevCh && cPrevCh <= 0x6FC ) ; + + // check for ligatures cPrevChar + cChar + if( bRet ) + bRet = !lcl_IsLigature( cPrevCh, cCh ); + return bRet; +} + +/************************************************************************* + * lcl_HasStrongLTR + *************************************************************************/ + bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd ) + { + for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx ) + { + const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx )); + if ( nCharDir == U_LEFT_TO_RIGHT || + nCharDir == U_LEFT_TO_RIGHT_EMBEDDING || + nCharDir == U_LEFT_TO_RIGHT_OVERRIDE ) + return true; + } + return false; + } + +/************************************************************************* + * SwLineLayout::~SwLineLayout() + * + * class SwLineLayout: Das Layout einer einzelnen Zeile. Dazu + * gehoeren vor allen Dingen die Dimension, die Anzahl der + * Character und der Wortzwischenraeume in der Zeile. + * Zeilenobjekte werden in einem eigenen Pool verwaltet, um zu + * erreichen, dass sie im Speicher moeglichst beeinander liegen + * (d.h. zusammen gepaged werden und den Speicher nicht + * fragmentieren). + *************************************************************************/ + +SwLineLayout::~SwLineLayout() +{ + Truncate(); + if( GetNext() ) + delete GetNext(); + if( pBlink ) + pBlink->Delete( this ); + delete pLLSpaceAdd; + if ( pKanaComp ) + delete pKanaComp; +} + +/************************************************************************* + * virtual SwLineLayout::Insert() + *************************************************************************/ + +SwLinePortion *SwLineLayout::Insert( SwLinePortion *pIns ) +{ + // Erster Attributwechsel, Masse und Laengen + // aus *pCurr in die erste Textportion kopieren. + if( !pPortion ) + { + if( GetLen() ) + { + pPortion = new SwTxtPortion( *(SwLinePortion*)this ); + if( IsBlinking() && pBlink ) + { + SetBlinking( sal_False ); + pBlink->Replace( this, pPortion ); + } + } + else + { + SetPortion( pIns ); + return pIns; + } + } + // mit Skope aufrufen, sonst Rekursion ! + return pPortion->SwLinePortion::Insert( pIns ); +} + +/************************************************************************* + * virtual SwLineLayout::Append() + *************************************************************************/ + +SwLinePortion *SwLineLayout::Append( SwLinePortion *pIns ) +{ + // Erster Attributwechsel, Masse und Laengen + // aus *pCurr in die erste Textportion kopieren. + if( !pPortion ) + pPortion = new SwTxtPortion( *(SwLinePortion*)this ); + // mit Skope aufrufen, sonst Rekursion ! + return pPortion->SwLinePortion::Append( pIns ); +} + +/************************************************************************* + * virtual SwLineLayout::Format() + *************************************************************************/ + +// fuer die Sonderbehandlung bei leeren Zeilen + +sal_Bool SwLineLayout::Format( SwTxtFormatInfo &rInf ) +{ + if( GetLen() ) + return SwTxtPortion::Format( rInf ); + else + { + Height( rInf.GetTxtHeight() ); + return sal_True; + } +} + +/************************************************************************* + * SwLineLayout::CalcLeftMargin() + * + * Wir sammeln alle FlyPortions am Anfang der Zeile zu einer MarginPortion. + *************************************************************************/ + +SwMarginPortion *SwLineLayout::CalcLeftMargin() +{ + SwMarginPortion *pLeft = (GetPortion() && GetPortion()->IsMarginPortion()) ? + (SwMarginPortion *)GetPortion() : 0; + if( !GetPortion() ) + SetPortion( new SwTxtPortion( *(SwLinePortion*)this ) ); + if( !pLeft ) + { + pLeft = new SwMarginPortion( 0 ); + pLeft->SetPortion( GetPortion() ); + SetPortion( pLeft ); + } + else + { + pLeft->Height( 0 ); + pLeft->Width( 0 ); + pLeft->SetLen( 0 ); + pLeft->SetAscent( 0 ); + pLeft->SetPortion( NULL ); + pLeft->SetFixWidth(0); + } + + SwLinePortion *pPos = pLeft->GetPortion(); + while( pPos ) + { + DBG_LOOP; + if( pPos->IsFlyPortion() ) + { + // Die FlyPortion wird ausgesogen ... + pLeft->Join( (SwGluePortion*)pPos ); + pPos = pLeft->GetPortion(); + if( GetpKanaComp() ) + GetKanaComp().Remove( 0, 1 ); + } + else + pPos = 0; + } + return pLeft; +} + +/************************************************************************* + * SwLineLayout::InitSpaceAdd() + *************************************************************************/ + +void SwLineLayout::InitSpaceAdd() +{ + if ( !pLLSpaceAdd ) + CreateSpaceAdd(); + else + SetLLSpaceAdd( 0, 0 ); +} + +/************************************************************************* + * SwLineLayout::CreateSpaceAdd() + *************************************************************************/ + +void SwLineLayout::CreateSpaceAdd( const long nInit ) +{ + pLLSpaceAdd = new std::vector<long>; + SetLLSpaceAdd( nInit, 0 ); +} + +/************************************************************************* + * Local helper function. Returns true if there are only blanks + * in [nStt, nEnd[ + *************************************************************************/ + +bool lcl_HasOnlyBlanks( const XubString& rTxt, xub_StrLen nStt, xub_StrLen nEnd ) +{ + bool bBlankOnly = true; + while ( nStt < nEnd ) + { + const xub_Unicode cChar = rTxt.GetChar( nStt++ ); + if ( ' ' != cChar && 0x3000 != cChar ) + { + bBlankOnly = false; + break; + } + } + return bBlankOnly; +} + +/************************************************************************* + * SwLineLayout::CalcLine() + * + * Aus FormatLine() ausgelagert. + *************************************************************************/ + +void SwLineLayout::CalcLine( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf ) +{ + const KSHORT nLineWidth = rInf.RealWidth(); + + KSHORT nFlyAscent = 0; + KSHORT nFlyHeight = 0; + KSHORT nFlyDescent = 0; + sal_Bool bOnlyPostIts = sal_True; + SetHanging( sal_False ); + + sal_Bool bTmpDummy = ( 0 == GetLen() ); + SwFlyCntPortion* pFlyCnt = 0; + if( bTmpDummy ) + { + nFlyAscent = 0; + nFlyHeight = 0; + nFlyDescent = 0; + } + + // --> FME 2006-03-01 #i3952# + const bool bIgnoreBlanksAndTabsForLineHeightCalculation = + rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION); + + bool bHasBlankPortion = false; + bool bHasOnlyBlankPortions = true; + // <-- + + if( pPortion ) + { + SetCntnt( sal_False ); + if( pPortion->IsBreakPortion() ) + { + SetLen( pPortion->GetLen() ); + if( GetLen() ) + bTmpDummy = sal_False; + } + else + { + Init( GetPortion() ); + SwLinePortion *pPos = pPortion; + SwLinePortion *pLast = this; + KSHORT nMaxDescent = 0; + + // Eine Gruppe ist ein Abschnitt in der Portion-Kette von + // pCurr oder einer Fix-Portion bis zum Ende bzw. zur naechsten + // Fix-Portion. + while( pPos ) + { + DBG_LOOP; + ASSERT( POR_LIN != pPos->GetWhichPor(), + "SwLineLayout::CalcLine: don't use SwLinePortions !" ); + + // Null-Portions werden eliminiert. Sie koennen entstehen, + // wenn zwei FlyFrms ueberlappen. + if( !pPos->Compress() ) + { + // 8110: Hoehe und Ascent nur uebernehmen, wenn sonst in der + // Zeile nichts mehr los ist. + if( !pPos->GetPortion() ) + { + if( !Height() ) + Height( pPos->Height() ); + if( !GetAscent() ) + SetAscent( pPos->GetAscent() ); + } + delete pLast->Cut( pPos ); + pPos = pLast->GetPortion(); + continue; + } + + const xub_StrLen nPorSttIdx = rInf.GetLineStart() + nLineLength; + nLineLength = nLineLength + pPos->GetLen(); + AddPrtWidth( pPos->Width() ); + + // --> FME 2006-03-01 #i3952# + if ( bIgnoreBlanksAndTabsForLineHeightCalculation ) + { + if ( pPos->InTabGrp() || pPos->IsHolePortion() || + ( pPos->IsTextPortion() && + lcl_HasOnlyBlanks( rInf.GetTxt(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) ) + { + pLast = pPos; + pPos = pPos->GetPortion(); + bHasBlankPortion = true; + continue; + } + } + // <-- + + bHasOnlyBlankPortions = false; + + // Es gab Attributwechsel: Laengen und Masse aufaddieren; + // bzw.Maxima bilden. + + KSHORT nPosHeight = pPos->Height(); + KSHORT nPosAscent = pPos->GetAscent(); + + ASSERT( nPosHeight >= nPosAscent, + "SwLineLayout::CalcLine: bad ascent or height" ); + + if( pPos->IsHangingPortion() ) + { + SetHanging( sal_True ); + rInf.GetParaPortion()->SetMargin( sal_True ); + } + + // Damit ein Paragraphende-Zeichen nicht durch ein Descent zu einer + // geaenderten Zeilenhoehe und zum Umformatieren fuehrt. + if ( !pPos->IsBreakPortion() || !Height() ) + { + bOnlyPostIts &= pPos->IsPostItsPortion(); + + if( bTmpDummy && !nLineLength ) + { + if( pPos->IsFlyPortion() ) + { + if( nFlyHeight < nPosHeight ) + nFlyHeight = nPosHeight; + if( nFlyAscent < nPosAscent ) + nFlyAscent = nPosAscent; + if( nFlyDescent < nPosHeight - nPosAscent ) + nFlyDescent = nPosHeight - nPosAscent; + } + else + { + if( pPos->InNumberGrp() ) + { + KSHORT nTmp = rInf.GetFont()->GetAscent( + rInf.GetVsh(), *rInf.GetOut() ); + if( nTmp > nPosAscent ) + { + nPosHeight += nTmp - nPosAscent; + nPosAscent = nTmp; + } + nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(), + *rInf.GetOut() ); + if( nTmp > nPosHeight ) + nPosHeight = nTmp; + } + Height( nPosHeight ); + nAscent = nPosAscent; + nMaxDescent = nPosHeight - nPosAscent; + } + } + else if( !pPos->IsFlyPortion() ) + { + if( Height() < nPosHeight ) + Height( nPosHeight ); + if( pPos->IsFlyCntPortion() || ( pPos->IsMultiPortion() + && ((SwMultiPortion*)pPos)->HasFlyInCntnt() ) ) + rLine.SetFlyInCntBase(); + if( pPos->IsFlyCntPortion() && + ((SwFlyCntPortion*)pPos)->GetAlign() ) + { + ((SwFlyCntPortion*)pPos)->SetMax( sal_False ); + if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() ) + pFlyCnt = (SwFlyCntPortion*)pPos; + } + else + { + if( nAscent < nPosAscent ) + nAscent = nPosAscent; + if( nMaxDescent < nPosHeight - nPosAscent ) + nMaxDescent = nPosHeight - nPosAscent; + } + } + } + else if( pPos->GetLen() ) + bTmpDummy = sal_False; + + if( !HasCntnt() && !pPos->InNumberGrp() ) + { + if ( pPos->InExpGrp() ) + { + XubString aTxt; + if( pPos->GetExpTxt( rInf, aTxt ) && aTxt.Len() ) + SetCntnt( sal_True ); + } + else if( ( pPos->InTxtGrp() || pPos->IsMultiPortion() ) && + pPos->GetLen() ) + SetCntnt( sal_True ); + } + + bTmpDummy = bTmpDummy && !HasCntnt() && + ( !pPos->Width() || pPos->IsFlyPortion() ); + + pLast = pPos; + pPos = pPos->GetPortion(); + } + + if( pFlyCnt ) + { + if( pFlyCnt->Height() == Height() ) + { + pFlyCnt->SetMax( sal_True ); + if( Height() > nMaxDescent + nAscent ) + { + if( 3 == pFlyCnt->GetAlign() ) // Bottom + nAscent = Height() - nMaxDescent; + else if( 2 == pFlyCnt->GetAlign() ) // Center + nAscent = ( Height() + nAscent - nMaxDescent ) / 2; + } + pFlyCnt->SetAscent( nAscent ); + } + } + + if( bTmpDummy && nFlyHeight ) + { + nAscent = nFlyAscent; + if( nFlyDescent > nFlyHeight - nFlyAscent ) + Height( nFlyHeight + nFlyDescent ); + else + Height( nFlyHeight ); + } + else if( nMaxDescent > Height() - nAscent ) + Height( nMaxDescent + nAscent ); + + if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) ) + { + Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) ); + nAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() ); + } + } + } + else + { + SetCntnt( !bTmpDummy ); + + // --> FME 2006-03-01 #i3952# + if ( bIgnoreBlanksAndTabsForLineHeightCalculation && + lcl_HasOnlyBlanks( rInf.GetTxt(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) ) + { + bHasBlankPortion = true; + } + // <-- + } + + // --> FME 2006-03-01 #i3952# + if ( bHasBlankPortion && bHasOnlyBlankPortions ) + { + USHORT nTmpAscent = GetAscent(); + USHORT nTmpHeight = Height(); + rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight ); + SetAscent( nTmpAscent ); + Height( nTmpHeight ); + } + // <-- + + // Robust: + if( nLineWidth < Width() ) + Width( nLineWidth ); + ASSERT( nLineWidth >= Width(), "SwLineLayout::CalcLine: line is bursting" ); + SetDummy( bTmpDummy ); + SetRedline( rLine.GetRedln() && + rLine.GetRedln()->CheckLine( rLine.GetStart(), rLine.GetEnd() ) ); +} + +// --> OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor> +// to control, if the fly content portions and line portion are considered. +void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent, + SwTwips& _orDescent, + SwTwips& _orObjAscent, + SwTwips& _orObjDescent, + const SwLinePortion* _pDontConsiderPortion, + const bool _bNoFlyCntPorAndLinePor ) const +{ + _orAscent = 0; + _orDescent = 0; + _orObjAscent = 0; + _orObjDescent = 0; + + const SwLinePortion* pTmpPortion = this; + if ( !pTmpPortion->GetLen() && pTmpPortion->GetPortion() ) + { + pTmpPortion = pTmpPortion->GetPortion(); + } + + while ( pTmpPortion ) + { + if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() && + ( !_bNoFlyCntPorAndLinePor || + ( !pTmpPortion->IsFlyCntPortion() && + !(pTmpPortion == this && pTmpPortion->GetPortion() ) ) ) ) + { + SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent()); + SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) - + nPortionAsc; + + const sal_Bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ? + static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() : + !( pTmpPortion == _pDontConsiderPortion ); + + if ( bFlyCmp ) + { + _orObjAscent = Max( _orObjAscent, nPortionAsc ); + _orObjDescent = Max( _orObjDescent, nPortionDesc ); + } + + if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() ) + { + _orAscent = Max( _orAscent, nPortionAsc ); + _orDescent = Max( _orDescent, nPortionDesc ); + } + } + pTmpPortion = pTmpPortion->GetPortion(); + } +} + +/************************************************************************* + * class SwCharRange + *************************************************************************/ + +SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange) +{ + if(0 != rRange.nLen ) { + if(0 == nLen) { + nStart = rRange.nStart; + nLen = rRange.nLen ; + } + else { + if(rRange.nStart + rRange.nLen > nStart + nLen) { + nLen = rRange.nStart + rRange.nLen - nStart; + } + if(rRange.nStart < nStart) { + nLen += nStart - rRange.nStart; + nStart = rRange.nStart; + } + } + } + return *this; +} + +/************************************************************************* + * SwScriptInfo::SwScriptInfo() + *************************************************************************/ +SwScriptInfo::SwScriptInfo() : + nInvalidityPos( 0 ), + nDefaultDir( 0 ) +{ +}; + +/************************************************************************* + * SwScriptInfo::~SwScriptInfo() + *************************************************************************/ +SwScriptInfo::~SwScriptInfo() +{ +} + +/************************************************************************* + * SwScriptInfo::WhichFont() + * + * Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to + * Sw Script Types (SW_LATIN, SW_CJK, SW_CTL), used to identify the font + *************************************************************************/ +BYTE SwScriptInfo::WhichFont( xub_StrLen nIdx, const String* pTxt, const SwScriptInfo* pSI ) +{ + ASSERT( pTxt || pSI,"How should I determine the script type?" ); + USHORT nScript; + + // First we try to use our SwScriptInfo + if ( pSI ) + nScript = pSI->ScriptType( nIdx ); + else + // Ok, we have to ask the break iterator + nScript = pBreakIt->GetRealScriptOfText( *pTxt, nIdx ); + + switch ( nScript ) { + case i18n::ScriptType::LATIN : return SW_LATIN; + case i18n::ScriptType::ASIAN : return SW_CJK; + case i18n::ScriptType::COMPLEX : return SW_CTL; + } + + ASSERT( sal_False, "Somebody tells lies about the script type!" ); + return SW_LATIN; +} + +/************************************************************************* + * SwScriptInfo::InitScriptInfo() + * + * searches for script changes in rTxt and stores them + *************************************************************************/ + +void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode ) +{ + InitScriptInfo( rNode, nDefaultDir == UBIDI_RTL ); +} + +void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode, sal_Bool bRTL ) +{ + if( !pBreakIt->GetBreakIter().is() ) + return; + + const String& rTxt = rNode.GetTxt(); + + // + // HIDDEN TEXT INFORMATION + // + Range aRange( 0, rTxt.Len() ? rTxt.Len() - 1 : 0 ); + MultiSelection aHiddenMulti( aRange ); + CalcHiddenRanges( rNode, aHiddenMulti ); + + aHiddenChg.Remove( 0, aHiddenChg.Count() ); + USHORT nHiddenIdx = 0; + USHORT i = 0; + for( i = 0; i < aHiddenMulti.GetRangeCount(); ++i ) + { + const Range& rRange = aHiddenMulti.GetRange( i ); + const xub_StrLen nStart = (xub_StrLen)rRange.Min(); + const xub_StrLen nEnd = (xub_StrLen)rRange.Max() + 1; + + aHiddenChg.Insert( nStart, nHiddenIdx++ ); + aHiddenChg.Insert( nEnd, nHiddenIdx++ ); + } + + // + // SCRIPT AND SCRIPT RELATED INFORMATION + // + + xub_StrLen nChg = nInvalidityPos; + + // STRING_LEN means the data structure is up to date + nInvalidityPos = STRING_LEN; + + // this is the default direction + nDefaultDir = static_cast<BYTE>(bRTL ? UBIDI_RTL : UBIDI_LTR); + + // counter for script info arrays + USHORT nCnt = 0; + // counter for compression information arrays + USHORT nCntComp = 0; + // counter for kashida array + USHORT nCntKash = 0; + + BYTE nScript = i18n::ScriptType::LATIN; + + // compression type + const SwCharCompressType aCompEnum = rNode.getIDocumentSettingAccess()->getCharacterCompressionType(); + + // justification type + const sal_Bool bAdjustBlock = SVX_ADJUST_BLOCK == + rNode.GetSwAttrSet().GetAdjust().GetAdjust(); + + // + // FIND INVALID RANGES IN SCRIPT INFO ARRAYS: + // + + if( nChg ) + { + // if change position = 0 we do not use any data from the arrays + // because by deleting all characters of the first group at the beginning + // of a paragraph nScript is set to a wrong value + ASSERT( CountScriptChg(), "Where're my changes of script?" ); + while( nCnt < CountScriptChg() ) + { + if ( nChg > GetScriptChg( nCnt ) ) + nCnt++; + else + { + nScript = GetScriptType( nCnt ); + break; + } + } + if( CHARCOMPRESS_NONE != aCompEnum ) + { + while( nCntComp < CountCompChg() ) + { + if ( nChg > GetCompStart( nCntComp ) ) + nCntComp++; + else + break; + } + } + if ( bAdjustBlock ) + { + while( nCntKash < CountKashida() ) + { + if ( nChg > GetKashida( nCntKash ) ) + nCntKash++; + else + break; + } + } + } + + // + // ADJUST nChg VALUE: + // + + // by stepping back one position we know that we are inside a group + // declared as an nScript group + if ( nChg ) + --nChg; + + const xub_StrLen nGrpStart = nCnt ? GetScriptChg( nCnt - 1 ) : 0; + + // we go back in our group until we reach the first character of + // type nScript + while ( nChg > nGrpStart && + nScript != pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) ) + --nChg; + + // If we are at the start of a group, we do not trust nScript, + // we better get nScript from the breakiterator: + if ( nChg == nGrpStart ) + nScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ); + + // + // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED: + // + + // remove invalid entries from script information arrays + const USHORT nScriptRemove = aScriptChg.Count() - nCnt; + aScriptChg.Remove( nCnt, nScriptRemove ); + aScriptType.Remove( nCnt, nScriptRemove ); + + // get the start of the last compression group + USHORT nLastCompression = nChg; + if( nCntComp ) + { + --nCntComp; + nLastCompression = GetCompStart( nCntComp ); + if( nChg >= nLastCompression + GetCompLen( nCntComp ) ) + { + nLastCompression = nChg; + ++nCntComp; + } + } + + // remove invalid entries from compression information arrays + const USHORT nCompRemove = aCompChg.Count() - nCntComp; + aCompChg.Remove( nCntComp, nCompRemove ); + aCompLen.Remove( nCntComp, nCompRemove ); + aCompType.Remove( nCntComp, nCompRemove ); + + // get the start of the last kashida group + USHORT nLastKashida = nChg; + if( nCntKash && i18n::ScriptType::COMPLEX == nScript ) + { + --nCntKash; + nLastKashida = GetKashida( nCntKash ); + } + + // remove invalid entries from kashida array + aKashida.Remove( nCntKash, aKashida.Count() - nCntKash ); + + // + // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE + // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH + // + + if( WEAK == pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) ) + { + // If the beginning of the current group is weak, this means that + // all of the characters in this grounp are weak. We have to assign + // the scripts to these characters depending on the fonts which are + // set for these characters to display them. + xub_StrLen nEnd = + (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nChg, WEAK ); + + if( nEnd > rTxt.Len() ) + nEnd = rTxt.Len(); + + nScript = (BYTE)GetI18NScriptTypeOfLanguage( (USHORT)GetAppLanguage() ); + + ASSERT( i18n::ScriptType::LATIN == nScript || + i18n::ScriptType::ASIAN == nScript || + i18n::ScriptType::COMPLEX == nScript, "Wrong default language" ); + + nChg = nEnd; + + // Get next script type or set to weak in order to exit + BYTE nNextScript = ( nEnd < rTxt.Len() ) ? + (BYTE)pBreakIt->GetBreakIter()->getScriptType( rTxt, nEnd ) : + (BYTE)WEAK; + + if ( nScript != nNextScript ) + { + aScriptChg.Insert( nEnd, nCnt ); + aScriptType.Insert( nScript, nCnt++ ); + nScript = nNextScript; + } + } + + // + // UPDATE THE SCRIPT INFO ARRAYS: + // + + while ( nChg < rTxt.Len() || ( !aScriptChg.Count() && !rTxt.Len() ) ) + { + ASSERT( i18n::ScriptType::WEAK != nScript, + "Inserting WEAK into SwScriptInfo structure" ); + ASSERT( STRING_LEN != nChg, "65K? Strange length of script section" ); + + xub_StrLen nSearchStt = nChg; + nChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nSearchStt, nScript ); + + if ( nChg > rTxt.Len() ) + nChg = rTxt.Len(); + + // --> FME 2008-09-17 #i28203# + // for 'complex' portions, we make sure that a portion does not contain more + // than one script: + if( i18n::ScriptType::COMPLEX == nScript && pBreakIt->GetScriptTypeDetector().is() ) + { + const short nScriptType = pBreakIt->GetScriptTypeDetector()->getCTLScriptType( rTxt, nSearchStt ); + xub_StrLen nNextCTLScriptStart = nSearchStt; + short nCurrentScriptType = nScriptType; + while( com::sun::star::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType ) + { + nNextCTLScriptStart = (xub_StrLen)pBreakIt->GetScriptTypeDetector()->endOfCTLScriptType( rTxt, nNextCTLScriptStart ); + if( nNextCTLScriptStart < rTxt.Len() && nNextCTLScriptStart < nChg ) + nCurrentScriptType = pBreakIt->GetScriptTypeDetector()->getCTLScriptType( rTxt, nNextCTLScriptStart ); + else + break; + } + nChg = Min( nChg, nNextCTLScriptStart ); + } + // <-- + + // special case for dotted circle since it can be used with complex + // before a mark, so we want it associated with the mark's script + if (nChg < rTxt.Len() && nChg > 0 && (i18n::ScriptType::WEAK == + pBreakIt->GetBreakIter()->getScriptType(rTxt,nChg - 1))) + { + int8_t nType = u_charType(rTxt.GetChar(nChg) ); + if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK || + nType == U_COMBINING_SPACING_MARK ) + { + aScriptChg.Insert( nChg - 1, nCnt ); + } + else + { + aScriptChg.Insert( nChg, nCnt ); + } + } + else + { + aScriptChg.Insert( nChg, nCnt ); + } + aScriptType.Insert( nScript, nCnt++ ); + + // if current script is asian, we search for compressable characters + // in this range + if ( CHARCOMPRESS_NONE != aCompEnum && + i18n::ScriptType::ASIAN == nScript ) + { + BYTE ePrevState = NONE; + BYTE eState; + USHORT nPrevChg = nLastCompression; + + while ( nLastCompression < nChg ) + { + xub_Unicode cChar = rTxt.GetChar( nLastCompression ); + + // examine current character + switch ( cChar ) + { + // Left punctuation found + case 0x3008: case 0x300A: case 0x300C: case 0x300E: + case 0x3010: case 0x3014: case 0x3016: case 0x3018: + case 0x301A: case 0x301D: + eState = SPECIAL_LEFT; + break; + // Right punctuation found + case 0x3001: case 0x3002: case 0x3009: case 0x300B: + case 0x300D: case 0x300F: case 0x3011: case 0x3015: + case 0x3017: case 0x3019: case 0x301B: case 0x301E: + case 0x301F: + eState = SPECIAL_RIGHT; + break; + default: + eState = static_cast<BYTE>( ( 0x3040 <= cChar && 0x3100 > cChar ) ? KANA : NONE ); + } + + // insert range of compressable characters + if( ePrevState != eState ) + { + if ( ePrevState != NONE ) + { + // insert start and type + if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum || + ePrevState != KANA ) + { + aCompChg.Insert( nPrevChg, nCntComp ); + BYTE nTmpType = ePrevState; + aCompType.Insert( nTmpType, nCntComp ); + aCompLen.Insert( nLastCompression - nPrevChg, nCntComp++ ); + } + } + + ePrevState = eState; + nPrevChg = nLastCompression; + } + + nLastCompression++; + } + + // we still have to examine last entry + if ( ePrevState != NONE ) + { + // insert start and type + if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum || + ePrevState != KANA ) + { + aCompChg.Insert( nPrevChg, nCntComp ); + BYTE nTmpType = ePrevState; + aCompType.Insert( nTmpType, nCntComp ); + aCompLen.Insert( nLastCompression - nPrevChg, nCntComp++ ); + } + } + } + + // we search for connecting opportunities (kashida) + else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript ) + { + SwScanner aScanner( rNode, rNode.GetTxt(), 0, 0, + i18n::WordType::DICTIONARY_WORD, + nLastKashida, nChg ); + + // the search has to be performed on a per word base + while ( aScanner.NextWord() ) + { + const XubString& rWord = aScanner.GetWord(); + + xub_StrLen nIdx = 0; + xub_StrLen nKashidaPos = STRING_LEN; + xub_Unicode cCh; + xub_Unicode cPrevCh = 0; + + USHORT nPriorityLevel = 7; // 0..6 = level found + // 7 not found + + xub_StrLen nWordLen = rWord.Len(); + + // ignore trailing vowel chars + while( nWordLen && isTransparentChar( rWord.GetChar( nWordLen - 1 ))) + --nWordLen; + + while (nIdx < nWordLen) + { + cCh = rWord.GetChar( nIdx ); + + // 1. Priority: + // after user inserted kashida + if ( 0x640 == cCh ) + { + nKashidaPos = aScanner.GetBegin() + nIdx; + nPriorityLevel = 0; + } + + // 2. Priority: + // after a Seen or Sad + if (nPriorityLevel >= 1 && nIdx < nWordLen - 1) + { + if( isSeenOrSadChar( cCh ) + && (rWord.GetChar( nIdx+1 ) != 0x200C) ) // #i98410#: prevent ZWNJ expansion + { + nKashidaPos = aScanner.GetBegin() + nIdx; + nPriorityLevel = 1; + } + } + + // 3. Priority: + // before final form of Teh Marbuta, Hah, Dal + if ( nPriorityLevel >= 2 && nIdx > 0 ) + { + if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining) + isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word + ( isHahChar ( cCh ) && nIdx == nWordLen - 1)) // Hah (dual joining) only at end of word + { + + ASSERT( 0 != cPrevCh, "No previous character" ) + // check if character is connectable to previous character, + if ( lcl_ConnectToPrev( cCh, cPrevCh ) ) + { + nKashidaPos = aScanner.GetBegin() + nIdx - 1; + nPriorityLevel = 2; + } + } + } + + // 4. Priority: + // before final form of Alef, Lam or Kaf + if ( nPriorityLevel >= 3 && nIdx > 0 ) + { + if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word + (( isLamChar ( cCh ) || // Lam + isKafChar ( cCh ) || // Kaf (both dual joining) + isGafChar ( cCh ) ) + && nIdx == nWordLen - 1)) // only at end of word + { + ASSERT( 0 != cPrevCh, "No previous character" ) + // check if character is connectable to previous character, + if ( lcl_ConnectToPrev( cCh, cPrevCh ) ) + { + nKashidaPos = aScanner.GetBegin() + nIdx - 1; + nPriorityLevel = 3; + } + } + } + + // 5. Priority: + // before media Bah + if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 ) + { + if ( isBaaChar ( cCh )) // Bah + { + // check if next character is Reh, Yeh or Alef Maksura + xub_Unicode cNextCh = rWord.GetChar( nIdx + 1 ); + if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh )) + { + ASSERT( 0 != cPrevCh, "No previous character" ) + // check if character is connectable to previous character, + if ( lcl_ConnectToPrev( cCh, cPrevCh ) ) + { + nKashidaPos = aScanner.GetBegin() + nIdx - 1; + nPriorityLevel = 4; + } + } + } + } + + // 6. Priority: + // before the final form of Waw, Ain, Qaf and Fa + if ( nPriorityLevel >= 5 && nIdx > 0 ) + { + if ( isWawChar ( cCh ) || // Wav (right joining) + // final form may appear in the middle of word + (( isAinChar ( cCh ) || // Ain (dual joining) + isQafChar ( cCh ) || // Qaf (dual joining) + isFeChar ( cCh ) ) // Feh (dual joining) + && nIdx == nWordLen - 1)) // only at end of word + { + ASSERT( 0 != cPrevCh, "No previous character" ) + // check if character is connectable to previous character, + if ( lcl_ConnectToPrev( cCh, cPrevCh ) ) + { + nKashidaPos = aScanner.GetBegin() + nIdx - 1; + nPriorityLevel = 5; + } + } + } + + // other connecting possibilities + if ( nPriorityLevel >= 6 && nIdx > 0 ) + { + // remaining right joiners + // Reh, Zain, Thal, + if ( isRehChar ( cCh ) || // Reh Zain (right joining) + // final form may appear in the middle of word + ( 0x60C <= cCh && 0x6FE >= cCh // all others + && nIdx == nWordLen - 1)) // only at end of word + { + ASSERT( 0 != cPrevCh, "No previous character" ) + // check if character is connectable to previous character, + if ( lcl_ConnectToPrev( cCh, cPrevCh ) ) + { + nKashidaPos = aScanner.GetBegin() + nIdx - 1; + nPriorityLevel = 6; + } + } + } + + // Do not consider Fathatan, Dammatan, Kasratan, Fatha, + // Damma, Kasra, Shadda and Sukun when checking if + // a character can be connected to previous character. + if ( !isTransparentChar ( cCh) ) + cPrevCh = cCh; + + ++nIdx; + } // end of current word + + if ( STRING_LEN != nKashidaPos ) + aKashida.Insert( nKashidaPos, nCntKash++ ); + } // end of kashida search + } + + if ( nChg < rTxt.Len() ) + nScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ); + + nLastCompression = nChg; + nLastKashida = nChg; + }; + +#ifndef PRODUCT + // check kashida data + long nTmpKashidaPos = -1; + sal_Bool bWrongKash = sal_False; + for (i = 0; i < aKashida.Count(); ++i ) + { + long nCurrKashidaPos = GetKashida( i ); + if ( nCurrKashidaPos <= nTmpKashidaPos ) + { + bWrongKash = sal_True; + break; + } + nTmpKashidaPos = nCurrKashidaPos; + } + ASSERT( ! bWrongKash, "Kashida array contains wrong data" ) +#endif + + // remove invalid entries from direction information arrays + const USHORT nDirRemove = aDirChg.Count(); + aDirChg.Remove( 0, nDirRemove ); + aDirType.Remove( 0, nDirRemove ); + + // Perform Unicode Bidi Algorithm for text direction information + bool bPerformUBA = UBIDI_LTR != nDefaultDir; + nCnt = 0; + while( !bPerformUBA && nCnt < CountScriptChg() ) + { + if ( i18n::ScriptType::COMPLEX == GetScriptType( nCnt++ ) ) + bPerformUBA = true; + } + + // do not call the unicode bidi algorithm if not required + if ( bPerformUBA ) + { + UpdateBidiInfo( rTxt ); + + // #i16354# Change script type for RTL text to CTL: + // 1. All text in RTL runs will use the CTL font + // #i89825# change the script type also to CTL (hennerdrewes) + // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!) + for ( USHORT nDirIdx = 0; nDirIdx < aDirChg.Count(); ++nDirIdx ) + { + const BYTE nCurrDirType = GetDirType( nDirIdx ); + // nStart ist start of RTL run: + const xub_StrLen nStart = nDirIdx > 0 ? GetDirChg( nDirIdx - 1 ) : 0; + // nEnd is end of RTL run: + const xub_StrLen nEnd = GetDirChg( nDirIdx ); + + if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run + ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( rTxt, nStart, nEnd ) ) ) // non-strong text in embedded LTR run + { + // nScriptIdx points into the ScriptArrays: + USHORT nScriptIdx = 0; + + // Skip entries in ScriptArray which are not inside the RTL run: + // Make nScriptIdx become the index of the script group with + // 1. nStartPosOfGroup <= nStart and + // 2. nEndPosOfGroup > nStart + while ( GetScriptChg( nScriptIdx ) <= nStart ) + ++nScriptIdx; + + const xub_StrLen nStartPosOfGroup = nScriptIdx ? GetScriptChg( nScriptIdx - 1 ) : 0; + const BYTE nScriptTypeOfGroup = GetScriptType( nScriptIdx ); + + ASSERT( nStartPosOfGroup <= nStart && GetScriptChg( nScriptIdx ) > nStart, + "Script override with CTL font trouble" ) + + // Check if we have to insert a new script change at + // position nStart. If nStartPosOfGroup < nStart, + // we have to insert a new script change: + if ( nStart > 0 && nStartPosOfGroup < nStart ) + { + aScriptChg.Insert( nStart, nScriptIdx ); + aScriptType.Insert( nScriptTypeOfGroup, nScriptIdx ); + ++nScriptIdx; + } + + // Remove entries in ScriptArray which end inside the RTL run: + while ( nScriptIdx < aScriptChg.Count() && GetScriptChg( nScriptIdx ) <= nEnd ) + { + aScriptChg.Remove( nScriptIdx, 1 ); + aScriptType.Remove( nScriptIdx, 1 ); + } + + // Insert a new entry in ScriptArray for the end of the RTL run: + aScriptChg.Insert( nEnd, nScriptIdx ); + aScriptType.Insert( i18n::ScriptType::COMPLEX, nScriptIdx ); + +#if OSL_DEBUG_LEVEL > 1 + BYTE nScriptType; + BYTE nLastScriptType = i18n::ScriptType::WEAK; + xub_StrLen nScriptChg; + xub_StrLen nLastScriptChg = 0; + (void) nLastScriptChg; + (void) nLastScriptType; + + for ( USHORT i2 = 0; i2 < aScriptChg.Count(); ++i2 ) + { + nScriptChg = GetScriptChg( i2 ); + nScriptType = GetScriptType( i2 ); + ASSERT( nLastScriptType != nScriptType && + nLastScriptChg < nScriptChg, + "Heavy InitScriptType() confusion" ) + } +#endif + } + } + } +} + +void SwScriptInfo::UpdateBidiInfo( const String& rTxt ) +{ + // remove invalid entries from direction information arrays + const USHORT nDirRemove = aDirChg.Count(); + aDirChg.Remove( 0, nDirRemove ); + aDirType.Remove( 0, nDirRemove ); + + // + // Bidi functions from icu 2.0 + // + UErrorCode nError = U_ZERO_ERROR; + UBiDi* pBidi = ubidi_openSized( rTxt.Len(), 0, &nError ); + nError = U_ZERO_ERROR; + + ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rTxt.GetBuffer()), rTxt.Len(), // UChar != sal_Unicode in MinGW + nDefaultDir, NULL, &nError ); + nError = U_ZERO_ERROR; + long nCount = ubidi_countRuns( pBidi, &nError ); + int32_t nStart = 0; + int32_t nEnd; + UBiDiLevel nCurrDir; + // counter for direction information arrays + USHORT nCntDir = 0; + + for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx ) + { + ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir ); + aDirChg.Insert( (USHORT)nEnd, nCntDir ); + aDirType.Insert( (BYTE)nCurrDir, nCntDir++ ); + nStart = nEnd; + } + + ubidi_close( pBidi ); +} + + +/************************************************************************* + * SwScriptInfo::NextScriptChg(..) + * returns the position of the next character which belongs to another script + * than the character of the actual (input) position. + * If there's no script change until the end of the paragraph, it will return + * STRING_LEN. + * Scripts are Asian (Chinese, Japanese, Korean), + * Latin ( English etc.) + * and Complex ( Hebrew, Arabian ) + *************************************************************************/ + +xub_StrLen SwScriptInfo::NextScriptChg( const xub_StrLen nPos ) const +{ + USHORT nEnd = CountScriptChg(); + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + if( nPos < GetScriptChg( nX ) ) + return GetScriptChg( nX ); + } + + return STRING_LEN; +} + +/************************************************************************* + * SwScriptInfo::ScriptType(..) + * returns the script of the character at the input position + *************************************************************************/ + +BYTE SwScriptInfo::ScriptType( const xub_StrLen nPos ) const +{ + USHORT nEnd = CountScriptChg(); + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + if( nPos < GetScriptChg( nX ) ) + return GetScriptType( nX ); + } + + // the default is the application language script + return (BYTE)GetI18NScriptTypeOfLanguage( (USHORT)GetAppLanguage() ); +} + +xub_StrLen SwScriptInfo::NextDirChg( const xub_StrLen nPos, + const BYTE* pLevel ) const +{ + BYTE nCurrDir = pLevel ? *pLevel : 62; + USHORT nEnd = CountDirChg(); + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + if( nPos < GetDirChg( nX ) && + ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) ) + return GetDirChg( nX ); + } + + return STRING_LEN; +} + +BYTE SwScriptInfo::DirType( const xub_StrLen nPos ) const +{ + USHORT nEnd = CountDirChg(); + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + if( nPos < GetDirChg( nX ) ) + return GetDirType( nX ); + } + + return 0; +} + +/************************************************************************* + * SwScriptInfo::MaskHiddenRanges(..) + * Takes a string and replaced the hidden ranges with cChar. + **************************************************************************/ + +USHORT SwScriptInfo::MaskHiddenRanges( const SwTxtNode& rNode, XubString& rText, + const xub_StrLen nStt, const xub_StrLen nEnd, + const xub_Unicode cChar ) +{ + ASSERT( rNode.GetTxt().Len() == rText.Len(), "MaskHiddenRanges, string len mismatch" ) + + PositionList aList; + xub_StrLen nHiddenStart; + xub_StrLen nHiddenEnd; + USHORT nNumOfHiddenChars = 0; + GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList ); + PositionList::const_reverse_iterator rFirst( aList.end() ); + PositionList::const_reverse_iterator rLast( aList.begin() ); + while ( rFirst != rLast ) + { + nHiddenEnd = *(rFirst++); + nHiddenStart = *(rFirst++); + + if ( nHiddenEnd < nStt || nHiddenStart > nEnd ) + continue; + + while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd ) + { + if ( nHiddenStart >= nStt && nHiddenStart < nEnd ) + { + rText.SetChar( nHiddenStart, cChar ); + ++nNumOfHiddenChars; + } + ++nHiddenStart; + } + } + + return nNumOfHiddenChars; +} + +/************************************************************************* + * SwScriptInfo::DeleteHiddenRanges(..) + * Takes a SwTxtNode and deletes the hidden ranges from the node. + **************************************************************************/ + +void SwScriptInfo::DeleteHiddenRanges( SwTxtNode& rNode ) +{ + PositionList aList; + xub_StrLen nHiddenStart; + xub_StrLen nHiddenEnd; + GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList ); + PositionList::const_reverse_iterator rFirst( aList.end() ); + PositionList::const_reverse_iterator rLast( aList.begin() ); + while ( rFirst != rLast ) + { + nHiddenEnd = *(rFirst++); + nHiddenStart = *(rFirst++); + + SwPaM aPam( rNode, nHiddenStart, rNode, nHiddenEnd ); + rNode.getIDocumentContentOperations()->DeleteRange( aPam ); + } +} + +/************************************************************************* + * SwScriptInfo::GetBoundsOfHiddenRange(..) + * static version + **************************************************************************/ + +bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTxtNode& rNode, xub_StrLen nPos, + xub_StrLen& rnStartPos, xub_StrLen& rnEndPos, + PositionList* pList ) +{ + rnStartPos = STRING_LEN; + rnEndPos = 0; + + bool bNewContainsHiddenChars = false; + + // + // Optimization: First examine the flags at the text node: + // + if ( !rNode.IsCalcHiddenCharFlags() ) + { + bool bWholePara = rNode.HasHiddenCharAttribute( true ); + bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false ); + if ( !bContainsHiddenChars ) + return false; + + if ( bWholePara ) + { + if ( pList ) + { + pList->push_back( 0 ); + pList->push_back( rNode.GetTxt().Len() ); + } + + rnStartPos = 0; + rnEndPos = rNode.GetTxt().Len(); + return true; + } + } + + const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode ); + if ( pSI ) + { + // + // Check first, if we have a valid SwScriptInfo object for this text node: + // + bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList ); + const bool bNewHiddenCharsHidePara = ( rnStartPos == 0 && rnEndPos >= rNode.GetTxt().Len() ); + rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars ); + } + else + { + // + // No valid SwScriptInfo Object, we have to do it the hard way: + // + Range aRange( 0, rNode.GetTxt().Len() ? rNode.GetTxt().Len() - 1 : 0 ); + MultiSelection aHiddenMulti( aRange ); + SwScriptInfo::CalcHiddenRanges( rNode, aHiddenMulti ); + for( USHORT i = 0; i < aHiddenMulti.GetRangeCount(); ++i ) + { + const Range& rRange = aHiddenMulti.GetRange( i ); + const xub_StrLen nHiddenStart = (xub_StrLen)rRange.Min(); + const xub_StrLen nHiddenEnd = (xub_StrLen)rRange.Max() + 1; + + if ( nHiddenStart > nPos ) + break; + else if ( nHiddenStart <= nPos && nPos < nHiddenEnd ) + { + rnStartPos = nHiddenStart; + rnEndPos = Min( nHiddenEnd, rNode.GetTxt().Len() ); + break; + } + } + + if ( pList ) + { + for( USHORT i = 0; i < aHiddenMulti.GetRangeCount(); ++i ) + { + const Range& rRange = aHiddenMulti.GetRange( i ); + pList->push_back( (xub_StrLen)rRange.Min() ); + pList->push_back( (xub_StrLen)rRange.Max() + 1 ); + } + } + + bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0; + } + + return bNewContainsHiddenChars; +} + +/************************************************************************* + * SwScriptInfo::GetBoundsOfHiddenRange(..) + * non-static version + **************************************************************************/ + +bool SwScriptInfo::GetBoundsOfHiddenRange( xub_StrLen nPos, xub_StrLen& rnStartPos, + xub_StrLen& rnEndPos, PositionList* pList ) const +{ + rnStartPos = STRING_LEN; + rnEndPos = 0; + + USHORT nEnd = CountHiddenChg(); + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + const xub_StrLen nHiddenStart = GetHiddenChg( nX++ ); + const xub_StrLen nHiddenEnd = GetHiddenChg( nX ); + + if ( nHiddenStart > nPos ) + break; + else if ( nHiddenStart <= nPos && nPos < nHiddenEnd ) + { + rnStartPos = nHiddenStart; + rnEndPos = nHiddenEnd; + break; + } + } + + if ( pList ) + { + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + pList->push_back( GetHiddenChg( nX++ ) ); + pList->push_back( GetHiddenChg( nX ) ); + } + } + + return CountHiddenChg() > 0; +} + +/************************************************************************* + * SwScriptInfo::IsInHiddenRange() + **************************************************************************/ + +bool SwScriptInfo::IsInHiddenRange( const SwTxtNode& rNode, xub_StrLen nPos ) +{ + xub_StrLen nStartPos; + xub_StrLen nEndPos; + SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos ); + return nStartPos != STRING_LEN; +} + + +#if OSL_DEBUG_LEVEL > 1 +/************************************************************************* + * SwScriptInfo::CompType(..) + * returns the type of the compressed character + *************************************************************************/ + +BYTE SwScriptInfo::CompType( const xub_StrLen nPos ) const +{ + USHORT nEnd = CountCompChg(); + for( USHORT nX = 0; nX < nEnd; ++nX ) + { + xub_StrLen nChg = GetCompStart( nX ); + + if ( nPos < nChg ) + return NONE; + + if( nPos < nChg + GetCompLen( nX ) ) + return GetCompType( nX ); + } + return NONE; +} +#endif + +/************************************************************************* + * SwScriptInfo::HasKana() + * returns, if there are compressable kanas or specials + * betwenn nStart and nEnd + *************************************************************************/ + +USHORT SwScriptInfo::HasKana( xub_StrLen nStart, const xub_StrLen nLen ) const +{ + USHORT nCnt = CountCompChg(); + xub_StrLen nEnd = nStart + nLen; + + for( USHORT nX = 0; nX < nCnt; ++nX ) + { + xub_StrLen nKanaStart = GetCompStart( nX ); + xub_StrLen nKanaEnd = nKanaStart + GetCompLen( nX ); + + if ( nKanaStart >= nEnd ) + return USHRT_MAX; + + if ( nStart < nKanaEnd ) + return nX; + } + + return USHRT_MAX; +} + +/************************************************************************* + * SwScriptInfo::Compress() + *************************************************************************/ + +long SwScriptInfo::Compress( sal_Int32* pKernArray, xub_StrLen nIdx, xub_StrLen nLen, + const USHORT nCompress, const USHORT nFontHeight, + Point* pPoint ) const +{ + ASSERT( nCompress, "Compression without compression?!" ); + ASSERT( nLen, "Compression without text?!" ); + USHORT nCompCount = CountCompChg(); + + // In asian typography, there are full width and half width characters. + // Full width punctuation characters can be compressed by 50 % + // to determine this, we compare the font width with 75 % of its height + USHORT nMinWidth = ( 3 * nFontHeight ) / 4; + + USHORT nCompIdx = HasKana( nIdx, nLen ); + + if ( USHRT_MAX == nCompIdx ) + return 0; + + xub_StrLen nChg = GetCompStart( nCompIdx ); + xub_StrLen nCompLen = GetCompLen( nCompIdx ); + USHORT nI = 0; + nLen = nLen + nIdx; + + if( nChg > nIdx ) + { + nI = nChg - nIdx; + nIdx = nChg; + } + else if( nIdx < nChg + nCompLen ) + nCompLen -= nIdx - nChg; + + if( nIdx > nLen || nCompIdx >= nCompCount ) + return 0; + + long nSub = 0; + long nLast = nI ? pKernArray[ nI - 1 ] : 0; + do + { + USHORT nType = GetCompType( nCompIdx ); +#if OSL_DEBUG_LEVEL > 1 + ASSERT( nType == CompType( nIdx ), "Gimme the right type!" ); +#endif + nCompLen = nCompLen + nIdx; + if( nCompLen > nLen ) + nCompLen = nLen; + + // are we allowed to compress the character? + if ( pKernArray[ nI ] - nLast < nMinWidth ) + { + nIdx++; nI++; + } + else + { + while( nIdx < nCompLen ) + { + ASSERT( SwScriptInfo::NONE != nType, "None compression?!" ); + + // nLast is width of current character + nLast -= pKernArray[ nI ]; + + nLast *= nCompress; + long nMove = 0; + if( SwScriptInfo::KANA != nType ) + { + nLast /= 20000; + if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType ) + { + if( nI ) + nMove = nLast; + else + { + pPoint->X() += nLast; + nLast = 0; + } + } + } + else + nLast /= 100000; + nSub -= nLast; + nLast = pKernArray[ nI ]; + if( nMove ) + pKernArray[ nI - 1 ] += nMove; + pKernArray[ nI++ ] -= nSub; + ++nIdx; + } + } + + if( nIdx < nLen ) + { + xub_StrLen nTmpChg; + if( ++nCompIdx < nCompCount ) + { + nTmpChg = GetCompStart( nCompIdx ); + if( nTmpChg > nLen ) + nTmpChg = nLen; + nCompLen = GetCompLen( nCompIdx ); + } + else + nTmpChg = nLen; + while( nIdx < nTmpChg ) + { + nLast = pKernArray[ nI ]; + pKernArray[ nI++ ] -= nSub; + ++nIdx; + } + } + else + break; + } while( nIdx < nLen ); + return nSub; +} + +/************************************************************************* + * SwScriptInfo::KashidaJustify() + *************************************************************************/ + +// Note on calling KashidaJustify(): +// Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean +// total number of kashida positions, or the number of kashida positions after some positions +// have been dropped, depending on the state of the aKashidaInvalid array. + +USHORT SwScriptInfo::KashidaJustify( sal_Int32* pKernArray, + sal_Int32* pScrArray, + xub_StrLen nStt, + xub_StrLen nLen, + long nSpaceAdd ) const +{ + ASSERT( nLen, "Kashida justification without text?!" ) + + if( !IsKashidaLine(nStt)) + return STRING_LEN; + + // evaluate kashida informatin in collected in SwScriptInfo + + USHORT nCntKash = 0; + while( nCntKash < CountKashida() ) + { + if ( nStt <= GetKashida( nCntKash ) ) + break; + else + nCntKash++; + } + + const xub_StrLen nEnd = nStt + nLen; + + USHORT nCntKashEnd = nCntKash; + while ( nCntKashEnd < CountKashida() ) + { + if ( nEnd <= GetKashida( nCntKashEnd ) ) + break; + else + nCntKashEnd++; + } + + USHORT nActualKashCount = nCntKashEnd - nCntKash; + for ( USHORT i = nCntKash; i < nCntKashEnd; ++i ) + { + if ( nActualKashCount && !IsKashidaValid ( i ) ) + --nActualKashCount; + } + + if ( !pKernArray ) + return nActualKashCount; + + // do nothing if there is no more kashida + if ( nCntKash < CountKashida() ) + { + // skip any invalid kashidas + while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd ) + ++nCntKash; + + xub_StrLen nKashidaPos = GetKashida( nCntKash ); + xub_StrLen nIdx = nKashidaPos; + long nKashAdd = nSpaceAdd; + + while ( nIdx < nEnd ) + { + USHORT nArrayPos = nIdx - nStt; + + // next kashida position + ++nCntKash; + while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd ) + ++nCntKash; + + nIdx = nCntKash < CountKashida() && IsKashidaValid ( nCntKash ) ? GetKashida( nCntKash ) : nEnd; + if ( nIdx > nEnd ) + nIdx = nEnd; + + const USHORT nArrayEnd = nIdx - nStt; + + while ( nArrayPos < nArrayEnd ) + { + pKernArray[ nArrayPos ] += nKashAdd; + if ( pScrArray ) + pScrArray[ nArrayPos ] += nKashAdd; + ++nArrayPos; + } + nKashAdd += nSpaceAdd; + } + } + + return 0; +} + +/************************************************************************* + * SwScriptInfo::IsArabicText() + * + * Checks if the current text is 'Arabic' text. Note that only the first + * character has to be checked because a ctl portion only contains one + * script, see NewTxtPortion + *************************************************************************/ +sal_Bool SwScriptInfo::IsArabicText( const XubString& rTxt, xub_StrLen nStt, xub_StrLen nLen ) +{ + using namespace ::com::sun::star::i18n; + static ScriptTypeList typeList[] = { + { UnicodeScript_kArabic, UnicodeScript_kArabic, UnicodeScript_kArabic }, // 11, + { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, UnicodeScript_kScriptCount } // 88 + }; + + // go forward if current position does not hold a regular character: + const CharClass& rCC = GetAppCharClass(); + sal_Int32 nIdx = nStt; + const xub_StrLen nEnd = nStt + nLen; + while ( nIdx < nEnd && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) ) + { + ++nIdx; + } + + if( nIdx == nEnd ) + { + // no regular character found in this portion. Go backward: + --nIdx; + while ( nIdx >= 0 && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) ) + { + --nIdx; + } + } + + if( nIdx >= 0 ) + { + const xub_Unicode cCh = rTxt.GetChar( (xub_StrLen)nIdx ); + const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, UnicodeScript_kScriptCount ); + return type == UnicodeScript_kArabic; + } + return sal_False; +} + +/************************************************************************* + * SwScriptInfo::IsKashidaValid() + *************************************************************************/ + +sal_Bool SwScriptInfo::IsKashidaValid ( xub_StrLen nKashPos ) const +{ + for ( xub_StrLen i = 0; i < aKashidaInvalid.Count(); ++i ) + { + if ( aKashidaInvalid [ i ] == nKashPos ) + return false; + } + return true; +} + +/************************************************************************* + * SwScriptInfo::ClearKashidaInvalid() + *************************************************************************/ + +void SwScriptInfo::ClearKashidaInvalid ( xub_StrLen nKashPos ) +{ + for ( xub_StrLen i = 0; i < aKashidaInvalid.Count(); ++i ) + { + if ( aKashidaInvalid [ i ] == nKashPos ) + { + aKashidaInvalid.Remove (i, 1); + return; + } + } +} + +/************************************************************************* + * SwScriptInfo::MarkOrClearKashidaInvalid() + *************************************************************************/ +// bMark == true: +// marks the first valid kashida in the given text range as invalid + +// bMark == false: +// clears all kashida invalid flags in the given text range + +bool SwScriptInfo::MarkOrClearKashidaInvalid ( xub_StrLen nStt, xub_StrLen nLen, bool bMark, xub_StrLen nMarkCount ) +{ + USHORT nCntKash = 0; + while( nCntKash < CountKashida() ) + { + if ( nStt <= GetKashida( nCntKash ) ) + break; + else + nCntKash++; + } + + const xub_StrLen nEnd = nStt + nLen; + + while ( nCntKash < CountKashida() ) + { + if ( nEnd <= GetKashida( nCntKash ) ) + break; + else + { + if(bMark) + { + if ( IsKashidaValid ( nCntKash ) ) + { + MarkKashidaInvalid ( nCntKash ); + --nMarkCount; + if(!nMarkCount) + return true; + } + } + else + { + ClearKashidaInvalid ( nCntKash ); + } + nCntKash++; + } + } + return false; +} + +void SwScriptInfo::MarkKashidaInvalid ( xub_StrLen nKashPos ) +{ + aKashidaInvalid.Insert( nKashPos, aKashidaInvalid.Count() ); +} + +/************************************************************************* + * SwScriptInfo::GetKashidaPositions() + *************************************************************************/ +// retrieve the kashida positions in the given text range +USHORT SwScriptInfo::GetKashidaPositions ( xub_StrLen nStt, xub_StrLen nLen, + xub_StrLen* pKashidaPosition ) +{ + USHORT nCntKash = 0; + while( nCntKash < CountKashida() ) + { + if ( nStt <= GetKashida( nCntKash ) ) + break; + else + nCntKash++; + } + + const xub_StrLen nEnd = nStt + nLen; + + USHORT nCntKashEnd = nCntKash; + while ( nCntKashEnd < CountKashida() ) + { + if ( nEnd <= GetKashida( nCntKashEnd ) ) + break; + else + { + pKashidaPosition [ nCntKashEnd - nCntKash ] = GetKashida ( nCntKashEnd ); + nCntKashEnd++; + } + } + return nCntKashEnd - nCntKash; +} + +void SwScriptInfo::SetNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen ) +{ + aNoKashidaLine.Insert( nStt, aNoKashidaLine.Count()); + aNoKashidaLineEnd.Insert( nStt+nLen, aNoKashidaLineEnd.Count()); +} + +/************************************************************************* + * SwScriptInfo::IsKashidaLine() + *************************************************************************/ +// determines if the line uses kashida justification + +bool SwScriptInfo::IsKashidaLine ( xub_StrLen nCharIdx ) const +{ + for( xub_StrLen i = 0; i < aNoKashidaLine.Count(); ++i ) + { + if( nCharIdx >= aNoKashidaLine[ i ] && nCharIdx < aNoKashidaLineEnd[ i ]) + return false; + } + return true; +} +/************************************************************************* + * SwScriptInfo::ClearKashidaLine() + *************************************************************************/ + +void SwScriptInfo::ClearNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen ) +{ + xub_StrLen i = 0; + while( i < aNoKashidaLine.Count()) + { + if( nStt + nLen >= aNoKashidaLine[ i ] && nStt < aNoKashidaLineEnd [ i ] ) + { + aNoKashidaLine.Remove(i, 1); + aNoKashidaLineEnd.Remove(i, 1); + } + else + ++i; + } +} + +/************************************************************************* + * SwScriptInfo::MarkKashidasInvalid() + *************************************************************************/ +// mark the given character indices as invalid kashida positions +bool SwScriptInfo::MarkKashidasInvalid ( xub_StrLen nCnt, xub_StrLen* pKashidaPositions ) +{ + ASSERT( pKashidaPositions && nCnt > 0, "Where are kashidas?" ) + + USHORT nCntKash = 0; + xub_StrLen nKashidaPosIdx = 0; + + while ( nCntKash < CountKashida() && nKashidaPosIdx < nCnt ) + { + if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) ) + { + nCntKash++; + continue; + } + + if ( pKashidaPositions [nKashidaPosIdx] == GetKashida( nCntKash ) && IsKashidaValid ( nCntKash ) ) + { + MarkKashidaInvalid ( nCntKash ); + } + else + return false; // something is wrong + nKashidaPosIdx++; + } + return true; +} + +/************************************************************************* + * SwScriptInfo::ThaiJustify() + *************************************************************************/ + +USHORT SwScriptInfo::ThaiJustify( const XubString& rTxt, sal_Int32* pKernArray, + sal_Int32* pScrArray, xub_StrLen nStt, + xub_StrLen nLen, xub_StrLen nNumberOfBlanks, + long nSpaceAdd ) +{ + ASSERT( nStt + nLen <= rTxt.Len(), "String in ThaiJustify too small" ) + + SwTwips nNumOfTwipsToDistribute = nSpaceAdd * nNumberOfBlanks / + SPACING_PRECISION_FACTOR; + + long nSpaceSum = 0; + USHORT nCnt = 0; + + for ( USHORT nI = 0; nI < nLen; ++nI ) + { + const xub_Unicode cCh = rTxt.GetChar( nStt + nI ); + + // check if character is not above or below base + if ( ( 0xE34 > cCh || cCh > 0xE3A ) && + ( 0xE47 > cCh || cCh > 0xE4E ) && cCh != 0xE31 ) + { + if ( nNumberOfBlanks > 0 ) + { + nSpaceAdd = nNumOfTwipsToDistribute / nNumberOfBlanks; + --nNumberOfBlanks; + nNumOfTwipsToDistribute -= nSpaceAdd; + } + nSpaceSum += nSpaceAdd; + ++nCnt; + } + + if ( pKernArray ) pKernArray[ nI ] += nSpaceSum; + if ( pScrArray ) pScrArray[ nI ] += nSpaceSum; + } + + return nCnt; +} + +/************************************************************************* + * SwScriptInfo::GetScriptInfo() + *************************************************************************/ + +SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTxtNode& rTNd, + sal_Bool bAllowInvalid ) +{ + SwClientIter aClientIter( (SwTxtNode&)rTNd ); + SwClient* pLast = aClientIter.GoStart(); + SwScriptInfo* pScriptInfo = 0; + + while( pLast ) + { + if ( pLast->ISA( SwTxtFrm ) ) + { + pScriptInfo = (SwScriptInfo*)((SwTxtFrm*)pLast)->GetScriptInfo(); + if ( pScriptInfo ) + { + if ( !bAllowInvalid && STRING_LEN != pScriptInfo->GetInvalidity() ) + pScriptInfo = 0; + else break; + } + } + pLast = ++aClientIter; + } + + return pScriptInfo; +} + +/************************************************************************* + * SwParaPortion::SwParaPortion() + *************************************************************************/ +SwParaPortion::SwParaPortion() +{ + FormatReset(); + bFlys = bFtnNum = bMargin = sal_False; + SetWhichPor( POR_PARA ); +} + +/************************************************************************* + * SwParaPortion::~SwParaPortion() + *************************************************************************/ +SwParaPortion::~SwParaPortion() +{ +} + +/************************************************************************* + * SwParaPortion::GetParLen() + *************************************************************************/ +xub_StrLen SwParaPortion::GetParLen() const +{ + xub_StrLen nLen = 0; + const SwLineLayout *pLay = this; + while( pLay ) + { + DBG_LOOP; + nLen = nLen + pLay->GetLen(); + pLay = pLay->GetNext(); + } + return nLen; +} + +/************************************************************************* + * SwParaPortion::FindDropPortion() + *************************************************************************/ + +const SwDropPortion *SwParaPortion::FindDropPortion() const +{ + const SwLineLayout *pLay = this; + while( pLay && pLay->IsDummy() ) + pLay = pLay->GetNext(); + while( pLay ) + { + const SwLinePortion *pPos = pLay->GetPortion(); + while ( pPos && !pPos->GetLen() ) + pPos = pPos->GetPortion(); + if( pPos && pPos->IsDropPortion() ) + return (SwDropPortion *)pPos; + pLay = pLay->GetLen() ? NULL : pLay->GetNext(); + } + return NULL; +} + +/************************************************************************* + * SwLineLayout::Init() + *************************************************************************/ + +void SwLineLayout::Init( SwLinePortion* pNextPortion ) +{ + Height( 0 ); + Width( 0 ); + SetLen( 0 ); + SetAscent( 0 ); + SetRealHeight( 0 ); + SetPortion( pNextPortion ); +} + +/*-----------------16.11.00 11:04------------------- + * HangingMargin() + * looks for hanging punctuation portions in the paragraph + * and return the maximum right offset of them. + * If no such portion is found, the Margin/Hanging-flags will be atualized. + * --------------------------------------------------*/ + +SwTwips SwLineLayout::_GetHangingMargin() const +{ + SwLinePortion* pPor = GetPortion(); + BOOL bFound = sal_False; + SwTwips nDiff = 0; + while( pPor) + { + if( pPor->IsHangingPortion() ) + { + nDiff = ((SwHangingPortion*)pPor)->GetInnerWidth() - pPor->Width(); + if( nDiff ) + bFound = sal_True; + } + // the last post its portion + else if ( pPor->IsPostItsPortion() && ! pPor->GetPortion() ) + nDiff = nAscent; + + pPor = pPor->GetPortion(); + } + if( !bFound ) // actualize the hanging-flag + ((SwLineLayout*)this)->SetHanging( sal_False ); + return nDiff; +} + +SwTwips SwTxtFrm::HangingMargin() const +{ + ASSERT( HasPara(), "Don't call me without a paraportion" ); + if( !GetPara()->IsMargin() ) + return 0; + const SwLineLayout* pLine = GetPara(); + SwTwips nRet = 0; + do + { + SwTwips nDiff = pLine->GetHangingMargin(); + if( nDiff > nRet ) + nRet = nDiff; + pLine = pLine->GetNext(); + } while ( pLine ); + if( !nRet ) // actualize the margin-flag + ((SwParaPortion*)GetPara())->SetMargin( sal_False ); + return nRet; +} + + +/************************************************************************* + * SwScriptInfo::CalcHiddenRanges() + * + * Returns a MultiSection indicating the hidden ranges. + *************************************************************************/ + +void SwScriptInfo::CalcHiddenRanges( const SwTxtNode& rNode, MultiSelection& rHiddenMulti ) +{ + const SfxPoolItem* pItem = 0; + if( SFX_ITEM_SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, TRUE, &pItem ) && + ((SvxCharHiddenItem*)pItem)->GetValue() ) + { + rHiddenMulti.SelectAll(); + } + + const SwpHints* pHints = rNode.GetpSwpHints(); + const SwTxtAttr* pTxtAttr = 0; + + if( pHints ) + { + MSHORT nTmp = 0; + + while( nTmp < pHints->GetStartCount() ) + { + pTxtAttr = pHints->GetStart( nTmp++ ); + const SvxCharHiddenItem* pHiddenItem = static_cast<const SvxCharHiddenItem*>( CharFmt::GetItem( *pTxtAttr, RES_CHRATR_HIDDEN ) ); + if( pHiddenItem ) + { + xub_StrLen nSt = *pTxtAttr->GetStart(); + xub_StrLen nEnd = *pTxtAttr->GetEnd(); + if( nEnd > nSt ) + { + Range aTmp( nSt, nEnd - 1 ); + rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() ); + } + } + } + } + + // If there are any hidden ranges in the current text node, we have + // to unhide the redlining ranges: + const IDocumentRedlineAccess& rIDRA = *rNode.getIDocumentRedlineAccess(); + if ( rHiddenMulti.GetRangeCount() && IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineMode() ) ) + { + USHORT nAct = rIDRA.GetRedlinePos( rNode, USHRT_MAX ); + + for ( ; nAct < rIDRA.GetRedlineTbl().Count(); nAct++ ) + { + const SwRedline* pRed = rIDRA.GetRedlineTbl()[ nAct ]; + + if ( pRed->Start()->nNode > rNode.GetIndex() ) + break; + + xub_StrLen nRedlStart; + xub_StrLen nRedlnEnd; + pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd ); + if ( nRedlnEnd > nRedlStart ) + { + Range aTmp( nRedlStart, nRedlnEnd - 1 ); + rHiddenMulti.Select( aTmp, false ); + } + } + } + + // + // We calculated a lot of stuff. Finally we can update the flags at the text node. + // + const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0; + bool bNewHiddenCharsHidePara = false; + if ( bNewContainsHiddenChars ) + { + const Range& rRange = rHiddenMulti.GetRange( 0 ); + const xub_StrLen nHiddenStart = (xub_StrLen)rRange.Min(); + const xub_StrLen nHiddenEnd = (xub_StrLen)rRange.Max() + 1; + bNewHiddenCharsHidePara = ( nHiddenStart == 0 && nHiddenEnd >= rNode.GetTxt().Len() ); + } + rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars ); +} + diff --git a/sw/source/core/text/porlay.hxx b/sw/source/core/text/porlay.hxx new file mode 100644 index 000000000000..db21e72d04ec --- /dev/null +++ b/sw/source/core/text/porlay.hxx @@ -0,0 +1,430 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porlay.hxx,v $ + * $Revision: 1.24 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORLAY_HXX +#define _PORLAY_HXX + +#include <tools/string.hxx> +#include <tools/fract.hxx> +#include <scriptinfo.hxx> + +#include "swrect.hxx" // SwRepaint +#include "portxt.hxx" +#include "swfont.hxx" + +#include <vector> + +class SwMarginPortion; +class SwDropPortion; +class SvStream; +class SwTxtFormatter; + +/************************************************************************* + * class SwCharRange + *************************************************************************/ + +class SwCharRange +{ + xub_StrLen nStart, nLen; +public: + inline SwCharRange( const xub_StrLen nInitStart = 0, + const xub_StrLen nInitLen = 0): nStart( nInitStart ), nLen(nInitLen) {} + inline xub_StrLen &Start() { return nStart; } + inline const xub_StrLen &Start() const { return nStart; } + inline void LeftMove( xub_StrLen nNew ) + { if ( nNew < nStart ) { nLen += nStart-nNew; nStart = nNew; } } + inline xub_StrLen End() const + { return nStart + nLen; } + inline xub_StrLen &Len() { return nLen; } + inline const xub_StrLen &Len() const { return nLen; } + inline sal_Bool operator<(const SwCharRange &rRange) const + { return nStart < rRange.nStart; } + inline sal_Bool operator>(const SwCharRange &rRange) const + { return nStart + nLen > rRange.nStart + rRange.nLen; } + inline sal_Bool operator!=(const SwCharRange &rRange) const + { return *this < rRange || *this > rRange; } + SwCharRange &operator+=(const SwCharRange &rRange); +}; + +/************************************************************************* + * class SwRepaint + *************************************************************************/ + +// SwRepaint ist ein dokumentglobales SwRect mit einem nOfst der angibt, +// ab wo in der ersten Zeile gepaintet werden soll +// und einem nRightOfst, der den rechten Rand bestimmt +class SwRepaint : public SwRect +{ + SwTwips nOfst; + SwTwips nRightOfst; +public: + SwRepaint() : SwRect(), nOfst( 0 ), nRightOfst( 0 ) {} + SwRepaint( const SwRepaint& rRep ) : SwRect( rRep ), nOfst( rRep.nOfst ), + nRightOfst( rRep.nRightOfst ) {} + + SwTwips GetOfst() const { return nOfst; } + void SetOfst( const SwTwips nNew ) { nOfst = nNew; } + SwTwips GetRightOfst() const { return nRightOfst; } + void SetRightOfst( const SwTwips nNew ) { nRightOfst = nNew; } +}; + +/************************************************************************* + * class SwLineLayout + *************************************************************************/ + +class SwLineLayout : public SwTxtPortion +{ +private: + SwLineLayout *pNext; // Die naechste Zeile. + std::vector<long>* pLLSpaceAdd; // Used for justified alignment. + SvUShorts* pKanaComp; // Used for Kana compression. + KSHORT nRealHeight; // Die aus Zeilenabstand/Register resultierende Hoehe. + sal_Bool bFormatAdj : 1; + sal_Bool bDummy : 1; + sal_Bool bFntChg : 1; + sal_Bool bEndHyph : 1; + sal_Bool bMidHyph : 1; + sal_Bool bTab : 1; + sal_Bool bFly : 1; + sal_Bool bRest : 1; + sal_Bool bBlinking : 1; + sal_Bool bClipping : 1; // Clipping erforderlich wg. exakter Zeilenhoehe + sal_Bool bContent : 1; // enthaelt Text, fuer Zeilennumerierung + sal_Bool bRedline : 1; // enthaelt Redlining + sal_Bool bForcedLeftMargin : 1; // vom Fly verschobener linker Einzug + sal_Bool bHanging : 1; // contents a hanging portion in the margin + sal_Bool bUnderscore : 1; + + SwTwips _GetHangingMargin() const; + +public: + // von SwLinePortion + virtual SwLinePortion *Insert( SwLinePortion *pPortion ); + virtual SwLinePortion *Append( SwLinePortion *pPortion ); + inline SwLinePortion *GetFirstPortion() const; + + // Flags + inline void ResetFlags(); + inline void SetFormatAdj( const sal_Bool bNew ) { bFormatAdj = bNew; } + inline sal_Bool IsFormatAdj() const { return bFormatAdj; } + inline void SetFntChg( const sal_Bool bNew ) { bFntChg = bNew; } + inline sal_Bool IsFntChg() const { return bFntChg; } + inline void SetEndHyph( const sal_Bool bNew ) { bEndHyph = bNew; } + inline sal_Bool IsEndHyph() const { return bEndHyph; } + inline void SetMidHyph( const sal_Bool bNew ) { bMidHyph = bNew; } + inline sal_Bool IsMidHyph() const { return bMidHyph; } + inline void SetTab( const sal_Bool bNew ) { bTab = bNew; } + inline sal_Bool IsTab() const { return bTab; } + inline void SetFly( const sal_Bool bNew ) { bFly = bNew; } + inline sal_Bool IsFly() const { return bFly; } + inline void SetRest( const sal_Bool bNew ) { bRest = bNew; } + inline sal_Bool IsRest() const { return bRest; } + inline void SetBlinking( const sal_Bool bNew = sal_True ) { bBlinking = bNew; } + inline sal_Bool IsBlinking() const { return bBlinking; } + inline void SetCntnt( const sal_Bool bNew = sal_True ) { bContent = bNew; } + inline sal_Bool HasCntnt() const { return bContent; } + inline void SetRedline( const sal_Bool bNew = sal_True ) { bRedline = bNew; } + inline sal_Bool HasRedline() const { return bRedline; } + inline void SetForcedLeftMargin( const sal_Bool bNew = sal_True ) { bForcedLeftMargin = bNew; } + inline sal_Bool HasForcedLeftMargin() const { return bForcedLeftMargin; } + inline void SetHanging( const sal_Bool bNew = sal_True ) { bHanging = bNew; } + inline sal_Bool IsHanging() const { return bHanging; } + inline void SetUnderscore( const sal_Bool bNew = sal_True ) { bUnderscore = bNew; } + inline sal_Bool HasUnderscore() const { return bUnderscore; } + + // Beruecksichtigung von Dummyleerzeilen + // 4147, 8221: + inline void SetDummy( const sal_Bool bNew ) { bDummy = bNew; } + inline sal_Bool IsDummy() const { return bDummy; } + + inline void SetClipping( const sal_Bool bNew ) { bClipping = bNew; } + inline sal_Bool IsClipping() const { return bClipping; } + + inline SwLineLayout(); + virtual ~SwLineLayout(); + + inline SwLineLayout *GetNext() { return pNext; } + inline const SwLineLayout *GetNext() const { return pNext; } + inline void SetNext( SwLineLayout *pNew ) { pNext = pNew; } + + void Init( SwLinePortion *pNextPortion = NULL); + + // Sammelt die Daten fuer die Zeile. + void CalcLine( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf ); + + inline void SetRealHeight( KSHORT nNew ) { nRealHeight = nNew; } + inline KSHORT GetRealHeight() const { return nRealHeight; } + + // Erstellt bei kurzen Zeilen die Glue-Kette. + SwMarginPortion *CalcLeftMargin(); + + inline SwTwips GetHangingMargin() const + { return _GetHangingMargin(); } + + // fuer die Sonderbehandlung bei leeren Zeilen + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + + // + // STUFF FOR JUSTIFIED ALIGNMENT + // + inline sal_Bool IsSpaceAdd() { return pLLSpaceAdd != NULL; } + void InitSpaceAdd(); // Creates pLLSpaceAdd if necessary + void CreateSpaceAdd( const long nInit = 0 ); + inline void FinishSpaceAdd() { delete pLLSpaceAdd; pLLSpaceAdd = NULL; } + inline USHORT GetLLSpaceAddCount() const { return sal::static_int_cast< USHORT >(pLLSpaceAdd->size()); } + inline void SetLLSpaceAdd( long nNew, USHORT nIdx ) + { + if ( nIdx == GetLLSpaceAddCount() ) + pLLSpaceAdd->push_back( nNew ); + else + (*pLLSpaceAdd)[ nIdx ] = nNew; + } + inline long GetLLSpaceAdd( USHORT nIdx ) { return (*pLLSpaceAdd)[ nIdx ]; } + inline void RemoveFirstLLSpaceAdd() { pLLSpaceAdd->erase( pLLSpaceAdd->begin() ); } + inline std::vector<long>* GetpLLSpaceAdd() const { return pLLSpaceAdd; } + + // + // STUFF FOR KANA COMPRESSION + // + inline void SetKanaComp( SvUShorts* pNew ){ pKanaComp = pNew; } + inline void FinishKanaComp() { delete pKanaComp; pKanaComp = NULL; } + inline SvUShorts* GetpKanaComp() const { return pKanaComp; } + inline SvUShorts& GetKanaComp() { return *pKanaComp; } + + /** determine ascent and descent for positioning of as-character anchored + object + + OD 07.01.2004 #i11859# - previously local method <lcl_MaxAscDescent> + Method calculates maximum ascents and descents of the line layout. + One value considering as-character anchored objects, one without these + objects. + Portions for other anchored objects aren't considered. + OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor> + to control, if the fly content portions and line portion are considered. + + @param _orAscent + output parameter - maximum ascent without as-character anchored objects + + @param _orDescent + output parameter - maximum descent without as-character anchored objects + + @param _orObjAscent + output parameter - maximum ascent with as-character anchored objects + + @param _orObjDescent + output parameter - maximum descent with as-character anchored objects + + @param _pDontConsiderPortion + input parameter - portion, which isn't considered for calculating + <_orObjAscent> and <_orObjDescent>, if it isn't a portion for a + as-character anchored object or it isn't as high as the line. + + @param _bNoFlyCntPorAndLinePor + optional input parameter - boolean, indicating that fly content portions + and the line portion are considered or not. + + @author OD + */ + void MaxAscentDescent( SwTwips& _orAscent, + SwTwips& _orDescent, + SwTwips& _orObjAscent, + SwTwips& _orObjDescent, + const SwLinePortion* _pDontConsiderPortion = NULL, + const bool _bNoFlyCntPorAndLinePor = false ) const; + +#ifndef PRODUCT + void DebugPortions( SvStream &rOs, const XubString &rTxt, + const xub_StrLen nStart ); //$ ostream +#endif + + OUTPUT_OPERATOR + DECL_FIXEDMEMPOOL_NEWDEL(SwLineLayout) +}; + +class SwParaPortion : public SwLineLayout +{ + // neu zu paintender Bereich + SwRepaint aRepaint; + // neu zu formatierender Bereich + SwCharRange aReformat; + SwScriptInfo aScriptInfo; +// Fraction aZoom; + long nDelta; + + // Wenn ein SwTxtFrm gelocked ist, werden keine Veraenderungen an den + // Formatierungsdaten (unter pLine) vorgenommen (vgl. ORPHANS) + sal_Bool bFlys : 1; // Ueberlappen Flys ? + sal_Bool bPrep : 1; // PREP_* + sal_Bool bPrepWidows : 1; // PREP_WIDOWS + sal_Bool bPrepAdjust : 1; // PREP_ADJUST_FRM + sal_Bool bPrepMustFit : 1; // PREP_MUST_FIT + sal_Bool bFollowField : 1; // Es steht noch ein Feldrest fuer den Follow an. + + sal_Bool bFixLineHeight : 1; // Feste Zeilenhoehe + sal_Bool bFtnNum : 1; // contents a footnotenumberportion + sal_Bool bMargin : 1; // contents a hanging punctuation in the margin + + sal_Bool bFlag00 : 1; // + sal_Bool bFlag11 : 1; // + sal_Bool bFlag12 : 1; // + sal_Bool bFlag13 : 1; // + sal_Bool bFlag14 : 1; // + sal_Bool bFlag15 : 1; // + sal_Bool bFlag16 : 1; // + +public: + SwParaPortion(); + virtual ~SwParaPortion(); + + // setzt alle Formatinformationen zurueck (ausser bFlys wg. 9916) + inline void FormatReset(); + + // Setzt die Flags zurueck + inline void ResetPreps(); + + // Get/Set-Methoden + inline SwRepaint *GetRepaint() { return &aRepaint; } + inline const SwRepaint *GetRepaint() const { return &aRepaint; } + inline SwCharRange *GetReformat() { return &aReformat; } + inline const SwCharRange *GetReformat() const { return &aReformat; } + inline long *GetDelta() { return &nDelta; } + inline const long *GetDelta() const { return &nDelta; } + inline SwScriptInfo& GetScriptInfo() { return aScriptInfo; } + inline const SwScriptInfo& GetScriptInfo() const { return aScriptInfo; } + + // fuer SwTxtFrm::Format: liefert die aktuelle Laenge des Absatzes + xub_StrLen GetParLen() const; + + // fuer Prepare() + sal_Bool UpdateQuoVadis( const XubString &rQuo ); + + // Flags + inline void SetFly( const sal_Bool bNew = sal_True ) { bFlys = bNew; } + inline sal_Bool HasFly() const { return bFlys; } + + // Preps + inline void SetPrep( const sal_Bool bNew = sal_True ) { bPrep = bNew; } + inline sal_Bool IsPrep() const { return bPrep; } + inline void SetPrepWidows( const sal_Bool bNew = sal_True ) { bPrepWidows = bNew; } + inline sal_Bool IsPrepWidows() const { return bPrepWidows; } + inline void SetPrepMustFit( const sal_Bool bNew = sal_True ) { bPrepMustFit = bNew; } + inline sal_Bool IsPrepMustFit() const { return bPrepMustFit; } + inline void SetPrepAdjust( const sal_Bool bNew = sal_True ) { bPrepAdjust = bNew; } + inline sal_Bool IsPrepAdjust() const { return bPrepAdjust; } + inline void SetFollowField( const sal_Bool bNew = sal_True ) { bFollowField = bNew; } + inline sal_Bool IsFollowField() const { return bFollowField; } + inline void SetFixLineHeight( const sal_Bool bNew = sal_True ) { bFixLineHeight = bNew; } + inline sal_Bool IsFixLineHeight() const { return bFixLineHeight; } + + inline void SetFtnNum( const sal_Bool bNew = sal_True ) { bFtnNum = bNew; } + inline sal_Bool IsFtnNum() const { return bFtnNum; } + inline void SetMargin( const sal_Bool bNew = sal_True ) { bMargin = bNew; } + inline sal_Bool IsMargin() const { return bMargin; } + inline void SetFlag00( const sal_Bool bNew = sal_True ) { bFlag00 = bNew; } + inline sal_Bool IsFlag00() const { return bFlag00; } + inline void SetFlag11( const sal_Bool bNew = sal_True ) { bFlag11 = bNew; } + inline sal_Bool IsFlag11() const { return bFlag11; } + inline void SetFlag12( const sal_Bool bNew = sal_True ) { bFlag12 = bNew; } + inline sal_Bool IsFlag12() const { return bFlag12; } + inline void SetFlag13( const sal_Bool bNew = sal_True ) { bFlag13 = bNew; } + inline sal_Bool IsFlag13() const { return bFlag13; } + inline void SetFlag14( const sal_Bool bNew = sal_True ) { bFlag14 = bNew; } + inline sal_Bool IsFlag14() const { return bFlag14; } + inline void SetFlag15( const sal_Bool bNew = sal_True ) { bFlag15 = bNew; } + inline sal_Bool IsFlag15() const { return bFlag15; } + inline void SetFlag16( const sal_Bool bNew = sal_True ) { bFlag16 = bNew; } + inline sal_Bool IsFlag16() const { return bFlag16; } + + // schneller, hoeher, weiter: Read/Write-Methoden fuer den SWG-Filter + SvStream &ReadSwg ( SvStream& rStream ); //$ istream + SvStream &WriteSwg( SvStream& rStream ); //$ ostream + + // nErgo in der QuoVadisPortion setzen + void SetErgoSumNum( const XubString &rErgo ); + + const SwDropPortion *FindDropPortion() const; + + OUTPUT_OPERATOR + DECL_FIXEDMEMPOOL_NEWDEL(SwParaPortion) +}; + +/************************************************************************* + * Inline-Implementierungen + *************************************************************************/ + +inline void SwLineLayout::ResetFlags() +{ + bFormatAdj = bDummy = bFntChg = bTab = bEndHyph = bMidHyph = bFly + = bRest = bBlinking = bClipping = bContent = bRedline + = bForcedLeftMargin = bHanging = sal_False; +} + +inline SwLineLayout::SwLineLayout() + : pNext( 0 ), pLLSpaceAdd( 0 ), pKanaComp( 0 ), nRealHeight( 0 ), + bUnderscore( sal_False ) +{ + ResetFlags(); + SetWhichPor( POR_LAY ); +} + +inline void SwParaPortion::ResetPreps() +{ + bPrep = bPrepWidows = bPrepAdjust = bPrepMustFit = sal_False; +} + +inline void SwParaPortion::FormatReset() +{ + nDelta = 0; + aReformat = SwCharRange( 0, STRING_LEN ); +// AMA 9916: bFlys muss in SwTxtFrm::_Format() erhalten bleiben, damit +// leere Absaetze, die Rahmen ohne Umfluss ausweichen mussten, sich +// neu formatieren, wenn der Rahmen aus dem Bereich verschwindet. +// bFlys = sal_False; + ResetPreps(); + bFollowField = bFixLineHeight = bMargin = sal_False; +} + +#ifdef UNX +// C30 ist mit dem ternaeren Ausdruck ueberfordert. +inline SwLinePortion *SwLineLayout::GetFirstPortion() const +{ + SwLinePortion *pTmp = pPortion; + if ( !pPortion ) + pTmp = (SwLinePortion*)this; + return( pTmp ); +} +#else +inline SwLinePortion *SwLineLayout::GetFirstPortion() const +{ return( pPortion ? pPortion : (SwLinePortion*)this ); } +#endif + +CLASSIO( SwLineLayout ) +CLASSIO( SwParaPortion ) + +#endif diff --git a/sw/source/core/text/porlin.cxx b/sw/source/core/text/porlin.cxx new file mode 100644 index 000000000000..08561df3f3b6 --- /dev/null +++ b/sw/source/core/text/porlin.cxx @@ -0,0 +1,429 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porlin.cxx,v $ + * $Revision: 1.27 $ + * + * 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 +#include <SwPortionHandler.hxx> + +#include "errhdl.hxx" // ASSERT + +#include "txtcfg.hxx" +#include "porlin.hxx" +#include "inftxt.hxx" +#include "portxt.hxx" +#include "pormulti.hxx" +#include "porglue.hxx" +#include "inftxt.hxx" +#include "blink.hxx" +#ifndef PRODUCT + +sal_Bool ChkChain( SwLinePortion *pStart ) +{ + SwLinePortion *pPor = pStart->GetPortion(); + MSHORT nCount = 0; + while( pPor ) + { + ++nCount; + ASSERT( nCount < 200 && pPor != pStart, + "ChkChain(): lost in chains" ); + if( nCount >= 200 || pPor == pStart ) + { + // der Lebensretter + pPor = pStart->GetPortion(); + pStart->SetPortion(0); + pPor->Truncate(); + pStart->SetPortion( pPor ); + return sal_False; + } + pPor = pPor->GetPortion(); + } + return sal_True; +} +#endif + +#if OSL_DEBUG_LEVEL > 1 +const sal_Char *GetPortionName( const MSHORT nType ); +#endif + +SwLinePortion::~SwLinePortion() +{ + if( pBlink ) + pBlink->Delete( this ); +} + +SwLinePortion *SwLinePortion::Compress() +{ + return GetLen() || Width() ? this : 0; +} + +KSHORT SwLinePortion::GetViewWidth( const SwTxtSizeInfo & ) const +{ + return 0; +} + +/************************************************************************* + * SwLinePortion::SwLinePortion( ) + *************************************************************************/ + +SwLinePortion::SwLinePortion( ) : + pPortion( NULL ), + nLineLength( 0 ), + nAscent( 0 ) +{ +} + +/************************************************************************* + * SwLinePortion::PrePaint() + *************************************************************************/ + +void SwLinePortion::PrePaint( const SwTxtPaintInfo& rInf, + const SwLinePortion* pLast ) const +{ + ASSERT( rInf.OnWin(), "SwLinePortion::PrePaint: don't prepaint on a printer"); + ASSERT( !Width(), "SwLinePortion::PrePaint: For Width()==0 only!"); + + const KSHORT nViewWidth = GetViewWidth( rInf ); + + if( ! nViewWidth ) + return; + + const KSHORT nHalfView = nViewWidth / 2; + USHORT nLastWidth = pLast->Width(); + + if ( pLast->InSpaceGrp() && rInf.GetSpaceAdd() ) + nLastWidth = nLastWidth + (USHORT)pLast->CalcSpacing( rInf.GetSpaceAdd(), rInf ); + + KSHORT nPos; + SwTxtPaintInfo aInf( rInf ); + + const BOOL bBidiPor = ( rInf.GetTxtFrm()->IsRightToLeft() ) != + ( 0 != ( TEXT_LAYOUT_BIDI_RTL & rInf.GetOut()->GetLayoutMode() ) ); + + USHORT nDir = bBidiPor ? + 1800 : + rInf.GetFont()->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); + + switch ( nDir ) + { + case 0 : + nPos = KSHORT( rInf.X() ); + if( nLastWidth > nHalfView ) + nPos += nLastWidth - nHalfView; + aInf.X( nPos ); + break; + case 900 : + nPos = KSHORT( rInf.Y() ); + if( nLastWidth > nHalfView ) + nPos -= nLastWidth + nHalfView; + aInf.Y( nPos ); + break; + case 1800 : + nPos = KSHORT( rInf.X() ); + if( nLastWidth > nHalfView ) + nPos -= nLastWidth + nHalfView; + aInf.X( nPos ); + break; + case 2700 : + nPos = KSHORT( rInf.Y() ); + if( nLastWidth > nHalfView ) + nPos += nLastWidth - nHalfView; + aInf.Y( nPos ); + break; + } + + SwLinePortion *pThis = (SwLinePortion*)this; + pThis->Width( nViewWidth ); + Paint( aInf ); + pThis->Width(0); +} + +/************************************************************************* + * SwLinePortion::CalcTxtSize() + *************************************************************************/ + +void SwLinePortion::CalcTxtSize( const SwTxtSizeInfo &rInf ) +{ + if( GetLen() == rInf.GetLen() ) + *((SwPosSize*)this) = GetTxtSize( rInf ); + else + { + SwTxtSizeInfo aInf( rInf ); + aInf.SetLen( GetLen() ); + *((SwPosSize*)this) = GetTxtSize( aInf ); + } +} + +/************************************************************************* + * SwLinePortion::Truncate() + * + * Es werden alle nachfolgenden Portions geloescht. + *************************************************************************/ + +void SwLinePortion::_Truncate() +{ + SwLinePortion *pPos = pPortion; + do + { ASSERT( pPos != this, "SwLinePortion::Truncate: loop" ); + SwLinePortion *pLast = pPos; + pPos = pPos->GetPortion(); + pLast->SetPortion( 0 ); + delete pLast; + + } while( pPos ); + + pPortion = 0; +} + +/************************************************************************* + * virtual SwLinePortion::Insert() + * + * Es wird immer hinter uns eingefuegt. + *************************************************************************/ + +SwLinePortion *SwLinePortion::Insert( SwLinePortion *pIns ) +{ + pIns->FindLastPortion()->SetPortion( pPortion ); + SetPortion( pIns ); +#ifndef PRODUCT + ChkChain( this ); +#endif + return pIns; +} + +/************************************************************************* + * SwLinePortion::FindLastPortion() + *************************************************************************/ + +SwLinePortion *SwLinePortion::FindLastPortion() +{ + SwLinePortion *pPos = this; + // An das Ende wandern und pLinPortion an den letzten haengen ... + while( pPos->GetPortion() ) + { + DBG_LOOP; + pPos = pPos->GetPortion(); + } + return pPos; +} + +/************************************************************************* + * virtual SwLinePortion::Append() + *************************************************************************/ + +SwLinePortion *SwLinePortion::Append( SwLinePortion *pIns ) +{ + SwLinePortion *pPos = FindLastPortion(); + pPos->SetPortion( pIns ); + pIns->SetPortion( 0 ); +#ifndef PRODUCT + ChkChain( this ); +#endif + return pIns; +} + +/************************************************************************* + * virtual SwLinePortion::Cut() + *************************************************************************/ + +SwLinePortion *SwLinePortion::Cut( SwLinePortion *pVictim ) +{ + SwLinePortion *pPrev = pVictim->FindPrevPortion( this ); + ASSERT( pPrev, "SwLinePortion::Cut(): can't cut" ); + pPrev->SetPortion( pVictim->GetPortion() ); + pVictim->SetPortion(0); + return pVictim; +} + +/************************************************************************* + * SwLinePortion::FindPrevPortion() + *************************************************************************/ + +SwLinePortion *SwLinePortion::FindPrevPortion( const SwLinePortion *pRoot ) +{ + ASSERT( pRoot != this, "SwLinePortion::FindPrevPortion(): invalid root" ); + SwLinePortion *pPos = (SwLinePortion*)pRoot; + while( pPos->GetPortion() && pPos->GetPortion() != this ) + { + DBG_LOOP; + pPos = pPos->GetPortion(); + } + ASSERT( pPos->GetPortion(), + "SwLinePortion::FindPrevPortion: blowing in the wind"); + return pPos; +} + +/************************************************************************* + * virtual SwLinePortion::GetCrsrOfst() + *************************************************************************/ + +xub_StrLen SwLinePortion::GetCrsrOfst( const KSHORT nOfst ) const +{ + if( nOfst > ( PrtWidth() / 2 ) ) + return GetLen(); + else + return 0; +} + +/************************************************************************* + * virtual SwLinePortion::GetTxtSize() + *************************************************************************/ + +SwPosSize SwLinePortion::GetTxtSize( const SwTxtSizeInfo & ) const +{ + ASSERT( !this, "SwLinePortion::GetTxtSize: don't ask me about sizes, " + "I'm only a stupid SwLinePortion" ); + return SwPosSize(); +} + +#ifndef PRODUCT + +/************************************************************************* + * virtual SwLinePortion::Check() + *************************************************************************/ + +sal_Bool SwLinePortion::Check( SvStream &, SwTxtSizeInfo & ) //$ ostream +{ + return sal_True; +} +#endif + +/************************************************************************* + * virtual SwLinePortion::Format() + *************************************************************************/ + +sal_Bool SwLinePortion::Format( SwTxtFormatInfo &rInf ) +{ + if( rInf.X() > rInf.Width() ) + { + Truncate(); + rInf.SetUnderFlow( this ); + return sal_True; + } + + const SwLinePortion *pLast = rInf.GetLast(); + Height( pLast->Height() ); + SetAscent( pLast->GetAscent() ); + const KSHORT nNewWidth = static_cast<USHORT>(rInf.X() + PrtWidth()); + // Nur Portions mit echter Breite koennen ein sal_True zurueckliefern + // Notizen beispielsweise setzen niemals bFull==sal_True + if( rInf.Width() <= nNewWidth && PrtWidth() && ! IsKernPortion() ) + { + Truncate(); + if( nNewWidth > rInf.Width() ) + PrtWidth( nNewWidth - rInf.Width() ); + rInf.GetLast()->FormatEOL( rInf ); + return sal_True; + } + return sal_False; +} + +/************************************************************************* + * virtual SwLinePortion::FormatEOL() + *************************************************************************/ + +// Format end of line + +void SwLinePortion::FormatEOL( SwTxtFormatInfo & ) +{ } + +/************************************************************************* + * SwLinePortion::Move() + *************************************************************************/ + +void SwLinePortion::Move( SwTxtPaintInfo &rInf ) +{ + BOOL bB2T = rInf.GetDirection() == DIR_BOTTOM2TOP; + const BOOL bFrmDir = rInf.GetTxtFrm()->IsRightToLeft(); + BOOL bCounterDir = ( ! bFrmDir && DIR_RIGHT2LEFT == rInf.GetDirection() ) || + ( bFrmDir && DIR_LEFT2RIGHT == rInf.GetDirection() ); + + if ( InSpaceGrp() && rInf.GetSpaceAdd() ) + { + SwTwips nTmp = PrtWidth() + CalcSpacing( rInf.GetSpaceAdd(), rInf ); + if( rInf.IsRotated() ) + rInf.Y( rInf.Y() + ( bB2T ? -nTmp : nTmp ) ); + else if ( bCounterDir ) + rInf.X( rInf.X() - nTmp ); + else + rInf.X( rInf.X() + nTmp ); + } + else + { + if( InFixMargGrp() && !IsMarginPortion() ) + { + rInf.IncSpaceIdx(); + rInf.IncKanaIdx(); + } + if( rInf.IsRotated() ) + rInf.Y( rInf.Y() + ( bB2T ? -PrtWidth() : PrtWidth() ) ); + else if ( bCounterDir ) + rInf.X( rInf.X() - PrtWidth() ); + else + rInf.X( rInf.X() + PrtWidth() ); + } + if( IsMultiPortion() && ((SwMultiPortion*)this)->HasTabulator() ) + rInf.IncSpaceIdx(); + + rInf.SetIdx( rInf.GetIdx() + GetLen() ); +} + +/************************************************************************* + * virtual SwLinePortion::CalcSpacing() + *************************************************************************/ + +long SwLinePortion::CalcSpacing( long , const SwTxtSizeInfo & ) const +{ + return 0; +} + +/************************************************************************* + * virtual SwLinePortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwLinePortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const +{ + return sal_False; +} + +/************************************************************************* + * virtual SwLinePortion::HandlePortion() + *************************************************************************/ + +void SwLinePortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString; + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx new file mode 100644 index 000000000000..7777e5968969 --- /dev/null +++ b/sw/source/core/text/porlin.hxx @@ -0,0 +1,254 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porlin.hxx,v $ + * $Revision: 1.17.214.1 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORLIN_HXX +#define _PORLIN_HXX + +#include "possiz.hxx" // SwPosSize + +class XubString; +class SwTxtSizeInfo; +class SwTxtPaintInfo; +class SwTxtFormatInfo; +class SwPortionHandler; + +// Die Ausgabeoperatoren der Portions sind virtuelle Methoden der Portion. +// Das CLASSIO-Makro implementiert die 'freischwebende' Funktion. +// Auf diese Weise erhaelt man beide Vorteile: virtuelle Ausgabeoperatoren +// und allgemeine Verwendbarkeit. +#ifndef PRODUCT +#define OUTPUT_OPERATOR virtual SvStream &operator<<( SvStream & aOs ) const; +#else +#define OUTPUT_OPERATOR +#endif + +// Portiongruppen +#define PORGRP_TXT 0x8000 +#define PORGRP_EXP 0x4000 +#define PORGRP_FLD 0x2000 +#define PORGRP_HYPH 0x1000 +#define PORGRP_NUMBER 0x0800 +#define PORGRP_GLUE 0x0400 +#define PORGRP_FIX 0x0200 +#define PORGRP_TAB 0x0100 +#define PORGRP_NOTRECY 0x0080 +// kleine Spezialgruppen +#define PORGRP_FIXMARG 0x0040 +//#define PORGRP_? 0x0020 +#define PORGRP_TABNOTLFT 0x0010 +#define PORGRP_TOXREF 0x0008 + +/************************************************************************* + * class SwLinePortion + *************************************************************************/ + +class SwLinePortion: public SwPosSize +{ +protected: + // Hier gibt es Bereiche mit unterschiedlichen Attributen. + SwLinePortion *pPortion; + // Anzahl der Zeichen und Spaces auf der Zeile + xub_StrLen nLineLength; + KSHORT nAscent; // Maximaler Ascender + + SwLinePortion(); +private: + MSHORT nWhichPor; // Who's who? + + void _Truncate(); + +public: + inline SwLinePortion(const SwLinePortion &rPortion); + virtual ~SwLinePortion(); + + // Zugriffsmethoden + inline SwLinePortion *GetPortion() const { return( pPortion ); } + inline SwLinePortion &operator=(const SwLinePortion &rPortion); + inline sal_Bool operator==( const SwLinePortion &rPortion ) const; + inline xub_StrLen GetLen() const { return nLineLength; } + inline void SetLen( const xub_StrLen nLen ) { nLineLength = nLen; } + inline void SetPortion( SwLinePortion *pNew ){ pPortion = pNew; } + inline KSHORT &GetAscent() { return nAscent; } + inline KSHORT GetAscent() const { return nAscent; } + inline void SetAscent( const KSHORT nNewAsc ) { nAscent = nNewAsc; } + inline void PrtWidth( KSHORT nNewWidth ) { Width( nNewWidth ); } + inline KSHORT PrtWidth() const { return Width(); } + inline void AddPrtWidth( const KSHORT nNew ) { Width( Width() + nNew ); } + inline void SubPrtWidth( const KSHORT nNew ) { Width( Width() - nNew ); } + + inline const SwPosSize &PrtSize() const { return *this; } + + // Einfuegeoperationen: + virtual SwLinePortion *Insert( SwLinePortion *pPortion ); + virtual SwLinePortion *Append( SwLinePortion *pPortion ); + SwLinePortion *Cut( SwLinePortion *pVictim ); + inline void Truncate(); + + // liefert 0 zurueck, wenn keine Nutzdaten enthalten sind. + virtual SwLinePortion *Compress(); + + inline void SetWhichPor( const MSHORT nNew ) { nWhichPor = nNew; } + inline MSHORT GetWhichPor( ) const { return nWhichPor; } + +// Gruppenabfragen: + inline sal_Bool InTxtGrp( ) const { return nWhichPor & PORGRP_TXT ? sal_True : sal_False; } + inline sal_Bool InGlueGrp( ) const { return nWhichPor & PORGRP_GLUE ? sal_True : sal_False;} + inline sal_Bool InTabGrp( ) const { return nWhichPor & PORGRP_TAB ? sal_True : sal_False; } + inline sal_Bool InHyphGrp( ) const { return nWhichPor & PORGRP_HYPH ? sal_True : sal_False;} + inline sal_Bool InNumberGrp( )const { return nWhichPor & PORGRP_NUMBER ? sal_True : sal_False;} + inline sal_Bool InFixGrp( ) const { return nWhichPor & PORGRP_FIX ? sal_True : sal_False; } + inline sal_Bool InFldGrp( ) const { return nWhichPor & PORGRP_FLD ? sal_True : sal_False; } + inline sal_Bool InToxRefGrp( ) const { return nWhichPor & PORGRP_TOXREF ? sal_True : sal_False; } + inline sal_Bool InToxRefOrFldGrp( ) const { return nWhichPor & + ( PORGRP_FLD | PORGRP_TOXREF ) ? sal_True : sal_False; } + inline sal_Bool InExpGrp( ) const { return nWhichPor & PORGRP_EXP ? sal_True : sal_False; } + inline sal_Bool InTabnLftGrp( ) const + { return nWhichPor & PORGRP_TABNOTLFT ? sal_True : sal_False; } + inline sal_Bool InFixMargGrp( )const + { return nWhichPor & PORGRP_FIXMARG ? sal_True : sal_False; } + inline sal_Bool InSpaceGrp( )const + { return InTxtGrp() || IsMultiPortion(); } +// Individuelle Abfragen: + inline sal_Bool IsGrfNumPortion( )const{ return nWhichPor == POR_GRFNUM; } + inline sal_Bool IsFlyCntPortion( )const{ return nWhichPor == POR_FLYCNT; } + inline sal_Bool IsBlankPortion( ) const{ return nWhichPor == POR_BLANK; } + inline sal_Bool IsBreakPortion( ) const{ return nWhichPor == POR_BRK; } + inline sal_Bool IsErgoSumPortion()const{ return nWhichPor == POR_ERGOSUM;} + inline sal_Bool IsQuoVadisPortion()const{ return nWhichPor==POR_QUOVADIS;} + inline sal_Bool IsTabCntPortion( )const{ return nWhichPor==POR_TABCENTER;} + inline sal_Bool IsTabDecimalPortion() const { return nWhichPor == POR_TABDECIMAL;} + inline sal_Bool IsTabLeftPortion()const{ return nWhichPor == POR_TABLEFT;} + inline sal_Bool IsFtnNumPortion( )const{ return nWhichPor == POR_FTNNUM; } + inline sal_Bool IsFtnPortion( ) const{ return nWhichPor == POR_FTN; } + inline sal_Bool IsTmpEndPortion( )const{ return nWhichPor == POR_TMPEND; } + inline sal_Bool IsDropPortion( ) const{ return nWhichPor == POR_DROP; } + inline sal_Bool IsLayPortion( ) const{ return nWhichPor == POR_LAY; } + inline sal_Bool IsParaPortion( ) const{ return nWhichPor == POR_PARA; } + inline sal_Bool IsMarginPortion( )const{ return nWhichPor == POR_MARGIN; } + inline sal_Bool IsFlyPortion( ) const{ return nWhichPor == POR_FLY; } + inline sal_Bool IsHolePortion( ) const{ return nWhichPor == POR_HOLE; } + inline sal_Bool IsSoftHyphPortion()const{ return nWhichPor==POR_SOFTHYPH;} + inline sal_Bool IsPostItsPortion()const{ return nWhichPor == POR_POSTITS;} + inline sal_Bool IsCombinedPortion()const{ return nWhichPor==POR_COMBINED;} + inline sal_Bool IsTextPortion( ) const{ return nWhichPor == POR_TXT; } + inline sal_Bool IsURLPortion( ) const{ return nWhichPor == POR_URL; } + inline sal_Bool IsHangingPortion( ) const{ return nWhichPor == POR_HNG; } + inline sal_Bool IsKernPortion( ) const{ return nWhichPor == POR_KERN; } + inline sal_Bool IsArrowPortion( ) const{ return nWhichPor == POR_ARROW; } + inline sal_Bool IsMultiPortion( ) const{ return nWhichPor == POR_MULTI; } + inline sal_Bool IsNumberPortion( ) const{ return nWhichPor == POR_NUMBER; } // #i23726# + inline sal_Bool IsControlCharPortion() const { return nWhichPor == POR_CONTROLCHAR; } + + // Positionierung + SwLinePortion *FindPrevPortion( const SwLinePortion *pRoot ); + SwLinePortion *FindLastPortion(); + + virtual xub_StrLen GetCrsrOfst( const KSHORT nOfst ) const; + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + void CalcTxtSize( const SwTxtSizeInfo &rInfo ); + + // Ausgabe + virtual void Paint( const SwTxtPaintInfo &rInf ) const = 0; + void PrePaint( const SwTxtPaintInfo &rInf, const SwLinePortion *pLast ) const; + +#ifndef PRODUCT + virtual sal_Bool Check( SvStream &rOs, SwTxtSizeInfo &rInfo ); //$ ostream +#endif + + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + // wird fuer die letzte Portion der Zeile extra gerufen + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + void Move( SwTxtPaintInfo &rInf ); + + // Fuer SwTxtSlot + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + + // fuer SwFldPortion, SwSoftHyphPortion + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + + // for text- and multi-portions + virtual long CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + + +/************************************************************************* + * inline - Implementations + *************************************************************************/ + +inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion) +{ + *(SwPosSize*)this = rPortion; + nLineLength = rPortion.nLineLength; + nAscent = rPortion.nAscent; + nWhichPor = rPortion.nWhichPor; + return *this; +} + +inline sal_Bool SwLinePortion::operator==(const SwLinePortion &rPortion ) const +{ + return( Height() == rPortion.Height() && + Width() == rPortion.Width() && + nLineLength == rPortion.GetLen() && + nAscent == rPortion.GetAscent() ); +} + +inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) : + SwPosSize( rPortion ), + pPortion( 0 ), + nLineLength( rPortion.nLineLength ), + nAscent( rPortion.nAscent ), + nWhichPor( rPortion.nWhichPor ) +{ +} + +inline void SwLinePortion::Truncate() +{ + if ( pPortion ) + _Truncate(); +} + + +//$ ostream +#ifdef DBGTXT +#define CLASSIO( class ) \ + inline SvStream &operator<<( SvStream &rOs, const class &rClass ) {\ + return rClass.operator<<( rOs );\ + } +#else +#define CLASSIO( class ) +#endif + +CLASSIO( SwLinePortion ) + +#endif diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx new file mode 100644 index 000000000000..2124b4156d75 --- /dev/null +++ b/sw/source/core/text/pormulti.cxx @@ -0,0 +1,2418 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pormulti.cxx,v $ + * $Revision: 1.89.112.2 $ + * + * 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 <com/sun/star/i18n/ScriptType.hdl> +#include <svx/twolinesitem.hxx> +#include <svx/charrotateitem.hxx> +#include <vcl/outdev.hxx> +#include <fmtfld.hxx> +#include <fldbas.hxx> // SwField +#include <txatbase.hxx> +#include <fmtruby.hxx> // SwFmtRuby +#include <txtatr.hxx> // SwTxtRuby +#include <charfmt.hxx> +#include <txtinet.hxx> +#include <fchrfmt.hxx> +#include <layfrm.hxx> // GetUpper() +#include <SwPortionHandler.hxx> +#include <pormulti.hxx> // SwMultiPortion +#include <inftxt.hxx> // SwTxtSizeInfo +#include <itrpaint.hxx> // SwTxtPainter +#include <viewopt.hxx> // SwViewOptions +#include <itrform2.hxx> // SwTxtFormatter +#include <porfld.hxx> // SwFldPortion +#include <porglue.hxx> +#include <breakit.hxx> +#include <pagefrm.hxx> +#include <rowfrm.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <swtable.hxx> +#include <fmtfsize.hxx> + +using namespace ::com::sun::star; +extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ); + +/*-----------------10.10.00 15:23------------------- + * class SwMultiPortion + * + * A SwMultiPortion is not a simple portion, + * it's a container, which contains almost a SwLineLayoutPortion. + * This SwLineLayout could be followed by other textportions via pPortion + * and by another SwLineLayout via pNext to realize a doubleline portion. + * --------------------------------------------------*/ + +SwMultiPortion::~SwMultiPortion() +{ + delete pFldRest; +} + +void SwMultiPortion::Paint( const SwTxtPaintInfo & ) const +{ + ASSERT( FALSE, + "Don't try SwMultiPortion::Paint, try SwTxtPainter::PaintMultiPortion" ); +} + +/*-----------------13.10.00 16:21------------------- + * Summarize the internal lines to calculate the (external) size. + * The internal line has to calculate first. + * --------------------------------------------------*/ + +void SwMultiPortion::CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf ) +{ + Width( 0 ); + Height( 0 ); + SetAscent( 0 ); + SetFlyInCntnt( sal_False ); + SwLineLayout *pLay = &GetRoot(); + do + { + pLay->CalcLine( rLine, rInf ); + if( rLine.IsFlyInCntBase() ) + SetFlyInCntnt( sal_True ); + if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) ) + { + // An empty phonetic line don't need an ascent or a height. + if( !pLay->Width() ) + { + pLay->SetAscent( 0 ); + pLay->Height( 0 ); + } + if( OnTop() ) + SetAscent( GetAscent() + pLay->Height() ); + } + else + SetAscent( GetAscent() + pLay->GetAscent() ); + Height( Height() + pLay->Height() ); + if( Width() < pLay->Width() ) + Width( pLay->Width() ); + pLay = pLay->GetNext(); + } while ( pLay ); + if( HasBrackets() ) + { + KSHORT nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nHeight; + if( nTmp > Height() ) + { + KSHORT nAdd = ( nTmp - Height() ) / 2; + GetRoot().SetAscent( GetRoot().GetAscent() + nAdd ); + GetRoot().Height( GetRoot().Height() + nAdd ); + Height( nTmp ); + } + nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nAscent; + if( nTmp > GetAscent() ) + SetAscent( nTmp ); + } +} + +long SwMultiPortion::CalcSpacing( long , const SwTxtSizeInfo & ) const +{ + return 0; +} + +sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const +{ + return sal_False; +} + +/************************************************************************* + * virtual SwMultiPortion::HandlePortion() + *************************************************************************/ + +void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Text( GetLen(), GetWhichPor() ); +} + +/*-----------------01.11.00 14:21------------------- + * SwMultiPortion::ActualizeTabulator() + * sets the tabulator-flag, if there's any tabulator-portion inside. + * --------------------------------------------------*/ + +void SwMultiPortion::ActualizeTabulator() +{ + SwLinePortion* pPor = GetRoot().GetFirstPortion(); + // First line + for( bTab1 = bTab2 = sal_False; pPor; pPor = pPor->GetPortion() ) + if( pPor->InTabGrp() ) + SetTab1( sal_True ); + if( GetRoot().GetNext() ) + { + // Second line + pPor = GetRoot().GetNext()->GetFirstPortion(); + do + { + if( pPor->InTabGrp() ) + SetTab2( sal_True ); + pPor = pPor->GetPortion(); + } while ( pPor ); + } +} + +/*-----------------16.02.01 12:07------------------- + * SwRotatedPortion::SwRotatedPortion(..) + * --------------------------------------------------*/ + +SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate, + xub_StrLen nEnd, sal_Bool bRTL ) : SwMultiPortion( nEnd ) +{ + const SvxCharRotateItem* pRot = (SvxCharRotateItem*)rCreate.pItem; + if( !pRot ) + { + const SwTxtAttr& rAttr = *rCreate.pAttr; + const SfxPoolItem *const pItem = + CharFmt::GetItem(rAttr, RES_CHRATR_ROTATE); + if ( pItem ) + { + pRot = static_cast<const SvxCharRotateItem*>(pItem); + } + } + if( pRot ) + { + sal_uInt8 nDir; + if ( bRTL ) + nDir = pRot->IsBottomToTop() ? 3 : 1; + else + nDir = pRot->IsBottomToTop() ? 1 : 3; + + SetDirection( nDir ); + } +} + +/*--------------------------------------------------- + * SwBidiPortion::SwBidiPortion(..) + * --------------------------------------------------*/ + +SwBidiPortion::SwBidiPortion( xub_StrLen nEnd, BYTE nLv ) + : SwMultiPortion( nEnd ), nLevel( nLv ) +{ + SetBidi(); + + if ( nLevel % 2 ) + SetDirection( DIR_RIGHT2LEFT ); + else + SetDirection( DIR_LEFT2RIGHT ); +} + + +long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo& rInf ) const +{ + return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR; +} + +sal_Bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const +{ + sal_Bool bRet = sal_False; + if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() ) + { + pCurr->CreateSpaceAdd(); + pCurr->SetLLSpaceAdd( nSpaceAdd, 0 ); + bRet = sal_True; + } + + return bRet; +} + +xub_StrLen SwBidiPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf ) const +{ + // Calculate number of blanks for justified alignment + SwLinePortion* pPor = GetRoot().GetFirstPortion(); + xub_StrLen nTmpStart = rInf.GetIdx(); + xub_StrLen nNull = 0; + xub_StrLen nBlanks; + + for( nBlanks = 0; pPor; pPor = pPor->GetPortion() ) + { + if( pPor->InTxtGrp() ) + nBlanks = nBlanks + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); + else if ( pPor->IsMultiPortion() && + ((SwMultiPortion*)pPor)->IsBidi() ) + nBlanks = nBlanks + ((SwBidiPortion*)pPor)->GetSpaceCnt( rInf ); + + ((SwTxtSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() ); + } + ((SwTxtSizeInfo &)rInf).SetIdx( nTmpStart ); + return nBlanks; +} + +/*-----------------01.11.00 14:22------------------- + * SwDoubleLinePortion::SwDoubleLinePortion(..) + * This constructor is for the continuation of a doubleline portion + * in the next line. + * It takes the same brackets and if the original has no content except + * brackets, these will be deleted. + * --------------------------------------------------*/ + +SwDoubleLinePortion::SwDoubleLinePortion( SwDoubleLinePortion& rDouble, + xub_StrLen nEnd ) : + SwMultiPortion( nEnd ), + pBracket( 0 ) +{ + SetDirection( rDouble.GetDirection() ); + SetDouble(); + if( rDouble.GetBrackets() ) + { + SetBrackets( rDouble ); + // An empty multiportion needs no brackets. + // Notice: GetLen() might be zero, if the multiportion contains + // the second part of a field and the width might be zero, if + // it contains a note only. In this cases the brackets are okay. + // But if the length and the width are both zero, the portion + // is really empty. + if( rDouble.Width() == rDouble.BracketWidth() ) + rDouble.ClearBrackets(); + } +} + +/*-----------------01.11.00 14:22------------------- + * SwDoubleLinePortion::SwDoubleLinePortion(..) + * This constructor uses the textattribut to get the right brackets. + * The textattribut could be a 2-line-attribute or a character- or + * internetstyle, which contains the 2-line-attribute. + * --------------------------------------------------*/ + +SwDoubleLinePortion::SwDoubleLinePortion( const SwMultiCreator& rCreate, + xub_StrLen nEnd ) : SwMultiPortion( nEnd ), pBracket( new SwBracket() ) +{ + SetDouble(); + const SvxTwoLinesItem* pTwo = (SvxTwoLinesItem*)rCreate.pItem; + if( pTwo ) + pBracket->nStart = 0; + else + { + const SwTxtAttr& rAttr = *rCreate.pAttr; + pBracket->nStart = *rAttr.GetStart(); + + const SfxPoolItem * const pItem = + CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES ); + if ( pItem ) + { + pTwo = static_cast<const SvxTwoLinesItem*>(pItem); + } + } + if( pTwo ) + { + pBracket->cPre = pTwo->GetStartBracket(); + pBracket->cPost = pTwo->GetEndBracket(); + } + else + { + pBracket->cPre = 0; + pBracket->cPost = 0; + } + BYTE nTmp = SW_SCRIPTS; + if( pBracket->cPre > 255 ) + { + String aTxt( pBracket->cPre ); + nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 ); + } + pBracket->nPreScript = nTmp; + nTmp = SW_SCRIPTS; + if( pBracket->cPost > 255 ) + { + String aTxt( pBracket->cPost ); + nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 ); + } + pBracket->nPostScript = nTmp; + + if( !pBracket->cPre && !pBracket->cPost ) + { + delete pBracket; + pBracket = 0; + } + + // double line portions have the same direction as the frame directions + if ( rCreate.nLevel % 2 ) + SetDirection( DIR_RIGHT2LEFT ); + else + SetDirection( DIR_LEFT2RIGHT ); +} + + +/*-----------------25.10.00 09:51------------------- + * SwMultiPortion::PaintBracket paints the wished bracket, + * if the multiportion has surrounding brackets. + * The X-position of the SwTxtPaintInfo will be modified: + * the open bracket sets position behind itself, + * the close bracket in front of itself. + * --------------------------------------------------*/ + +void SwDoubleLinePortion::PaintBracket( SwTxtPaintInfo &rInf, + long nSpaceAdd, + sal_Bool bOpen ) const +{ + sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost; + if( !cCh ) + return; + KSHORT nChWidth = bOpen ? PreWidth() : PostWidth(); + if( !nChWidth ) + return; + if( !bOpen ) + rInf.X( rInf.X() + Width() - PostWidth() + + ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) ); + + SwBlankPortion aBlank( cCh, sal_True ); + aBlank.SetAscent( pBracket->nAscent ); + aBlank.Width( nChWidth ); + aBlank.Height( pBracket->nHeight ); + { + SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); + BYTE nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript; + if( SW_SCRIPTS > nAct ) + pTmpFnt->SetActual( nAct ); + pTmpFnt->SetProportion( 100 ); + SwFontSave aSave( rInf, pTmpFnt ); + aBlank.Paint( rInf ); + delete pTmpFnt; + } + if( bOpen ) + rInf.X( rInf.X() + PreWidth() ); +} + +/*-----------------25.10.00 16:26------------------- + * SwDoubleLinePortion::SetBrackets creates the bracket-structur + * and fills it, if not both characters are 0x00. + * --------------------------------------------------*/ + +void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble ) +{ + if( rDouble.pBracket ) + { + pBracket = new SwBracket; + pBracket->cPre = rDouble.pBracket->cPre; + pBracket->cPost = rDouble.pBracket->cPost; + pBracket->nPreScript = rDouble.pBracket->nPreScript; + pBracket->nPostScript = rDouble.pBracket->nPostScript; + pBracket->nStart = rDouble.pBracket->nStart; + } +} + +/*-----------------25.10.00 16:29------------------- + * SwDoubleLinePortion::FormatBrackets + * calculates the size of the brackets => pBracket, + * reduces the nMaxWidth-parameter ( minus bracket-width ) + * and moves the rInf-x-position behind the opening bracket. + * --------------------------------------------------*/ + +void SwDoubleLinePortion::FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth ) +{ + nMaxWidth -= rInf.X(); + SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); + pTmpFnt->SetProportion( 100 ); + pBracket->nAscent = 0; + pBracket->nHeight = 0; + if( pBracket->cPre ) + { + String aStr( pBracket->cPre ); + BYTE nActualScr = pTmpFnt->GetActual(); + if( SW_SCRIPTS > pBracket->nPreScript ) + pTmpFnt->SetActual( pBracket->nPreScript ); + SwFontSave aSave( rInf, pTmpFnt ); + SwPosSize aSize = rInf.GetTxtSize( aStr ); + pBracket->nAscent = rInf.GetAscent(); + pBracket->nHeight = aSize.Height(); + pTmpFnt->SetActual( nActualScr ); + if( nMaxWidth > aSize.Width() ) + { + pBracket->nPreWidth = aSize.Width(); + nMaxWidth -= aSize.Width(); + rInf.X( rInf.X() + aSize.Width() ); + } + else + { + pBracket->nPreWidth = 0; + nMaxWidth = 0; + } + } + else + pBracket->nPreWidth = 0; + if( pBracket->cPost ) + { + String aStr( pBracket->cPost ); + if( SW_SCRIPTS > pBracket->nPostScript ) + pTmpFnt->SetActual( pBracket->nPostScript ); + SwFontSave aSave( rInf, pTmpFnt ); + SwPosSize aSize = rInf.GetTxtSize( aStr ); + KSHORT nTmpAsc = rInf.GetAscent(); + if( nTmpAsc > pBracket->nAscent ) + { + pBracket->nHeight += nTmpAsc - pBracket->nAscent; + pBracket->nAscent = nTmpAsc; + } + if( aSize.Height() > pBracket->nHeight ) + pBracket->nHeight = aSize.Height(); + if( nMaxWidth > aSize.Width() ) + { + pBracket->nPostWidth = aSize.Width(); + nMaxWidth -= aSize.Width(); + } + else + { + pBracket->nPostWidth = 0; + nMaxWidth = 0; + } + } + else + pBracket->nPostWidth = 0; + nMaxWidth += rInf.X(); +} + +/*-----------------26.10.00 10:36------------------- + * SwDoubleLinePortion::CalcBlanks + * calculates the number of blanks in each line and + * the difference of the width of the two lines. + * These results are used from the text adjustment. + * --------------------------------------------------*/ + +void SwDoubleLinePortion::CalcBlanks( SwTxtFormatInfo &rInf ) +{ + SwLinePortion* pPor = GetRoot().GetFirstPortion(); + xub_StrLen nNull = 0; + xub_StrLen nStart = rInf.GetIdx(); + SetTab1( sal_False ); + SetTab2( sal_False ); + for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() ) + { + if( pPor->InTxtGrp() ) + nBlank1 = nBlank1 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); + rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); + if( pPor->InTabGrp() ) + SetTab1( sal_True ); + } + nLineDiff = GetRoot().Width(); + if( GetRoot().GetNext() ) + { + pPor = GetRoot().GetNext()->GetFirstPortion(); + nLineDiff -= GetRoot().GetNext()->Width(); + } + for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() ) + { + if( pPor->InTxtGrp() ) + nBlank2 = nBlank2 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); + rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); + if( pPor->InTabGrp() ) + SetTab2( sal_True ); + } + rInf.SetIdx( nStart ); +} + +long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo & ) const +{ + return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR; +} + +/*-----------------01.11.00 14:29------------------- + * SwDoubleLinePortion::ChangeSpaceAdd(..) + * merges the spaces for text adjustment from the inner and outer part. + * Inside the doubleline portion the wider line has no spaceadd-array, the + * smaller line has such an array to reach width of the wider line. + * If the surrounding line has text adjustment and the doubleline portion + * contains no tabulator, it is necessary to create/manipulate the inner + * space arrays. + * --------------------------------------------------*/ + +sal_Bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr, + long nSpaceAdd ) const +{ + sal_Bool bRet = sal_False; + if( !HasTabulator() && nSpaceAdd > 0 ) + { + if( !pCurr->IsSpaceAdd() ) + { + // The wider line gets the spaceadd from the surrounding line direct + pCurr->CreateSpaceAdd(); + pCurr->SetLLSpaceAdd( nSpaceAdd, 0 ); + bRet = sal_True; + } + else + { + xub_StrLen nMyBlank = GetSmallerSpaceCnt(); + xub_StrLen nOther = GetSpaceCnt(); + SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd; + + if( nMyBlank ) + nMultiSpace /= nMyBlank; + + if( nMultiSpace < KSHRT_MAX * SPACING_PRECISION_FACTOR ) + { +// pCurr->SetLLSpaceAdd( nMultiSpace, 0 ); + // --> FME 2006-07-11 #i65711# SetLLSpaceAdd replaces the first value, + // instead we want to insert a new first value: + std::vector<long>* pVec = pCurr->GetpLLSpaceAdd(); + pVec->insert( pVec->begin(), nMultiSpace ); + // <-- + bRet = sal_True; + } + } + } + return bRet; +} +/*-----------------01.11.00 14:29------------------- + * SwDoubleLinePortion::ResetSpaceAdd(..) + * cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..) + * --------------------------------------------------*/ + +void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr ) +{ + pCurr->RemoveFirstLLSpaceAdd();; + if( !pCurr->GetLLSpaceAddCount() ) + pCurr->FinishSpaceAdd(); +} + +SwDoubleLinePortion::~SwDoubleLinePortion() +{ + delete pBracket; +} + +/*-----------------13.11.00 14:50------------------- + * SwRubyPortion::SwRubyPortion(..) + * constructs a ruby portion, i.e. an additional text is displayed + * beside the main text, e.g. phonetic characters. + * --------------------------------------------------*/ + + +SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ) : + SwMultiPortion( nEnd ), + nRubyOffset( rRuby.GetRubyOffset() ), + nAdjustment( rRuby.GetAdjustment() ) +{ + SetDirection( rRuby.GetDirection() ), + SetTop( rRuby.OnTop() ); + SetRuby(); +} + +/*-----------------13.11.00 14:50------------------- + * SwRubyPortion::SwRubyPortion(..) + * constructs a ruby portion, i.e. an additional text is displayed + * beside the main text, e.g. phonetic characters. + * --------------------------------------------------*/ + +SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt, + const IDocumentSettingAccess& rIDocumentSettingAccess, + xub_StrLen nEnd, xub_StrLen nOffs, + const sal_Bool* pForceRubyPos ) + : SwMultiPortion( nEnd ) +{ + SetRuby(); + ASSERT( SW_MC_RUBY == rCreate.nId, "Ruby expected" ); + ASSERT( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" ); + const SwFmtRuby& rRuby = rCreate.pAttr->GetRuby(); + nAdjustment = rRuby.GetAdjustment(); + nRubyOffset = nOffs; + + // in grid mode we force the ruby text to the upper or lower line + if ( pForceRubyPos ) + SetTop( *pForceRubyPos ); + else + SetTop( ! rRuby.GetPosition() ); + + const SwCharFmt* pFmt = ((SwTxtRuby*)rCreate.pAttr)->GetCharFmt(); + SwFont *pRubyFont; + if( pFmt ) + { + const SwAttrSet& rSet = pFmt->GetAttrSet(); + pRubyFont = new SwFont( rFnt ); + pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess ); + + // we do not allow a vertical font for the ruby text + pRubyFont->SetVertical( rFnt.GetOrientation() ); + } + else + pRubyFont = NULL; + + String aStr( rRuby.GetText(), nOffs, STRING_LEN ); + SwFldPortion *pFld = new SwFldPortion( aStr, pRubyFont ); + pFld->SetNextOffset( nOffs ); + pFld->SetFollow( sal_True ); + + if( OnTop() ) + GetRoot().SetPortion( pFld ); + else + { + GetRoot().SetNext( new SwLineLayout() ); + GetRoot().GetNext()->SetPortion( pFld ); + } + + // ruby portions have the same direction as the frame directions + if ( rCreate.nLevel % 2 ) + { + // switch right and left ruby adjustment in rtl environment + if ( 0 == nAdjustment ) + nAdjustment = 2; + else if ( 2 == nAdjustment ) + nAdjustment = 0; + + SetDirection( DIR_RIGHT2LEFT ); + } + else + SetDirection( DIR_LEFT2RIGHT ); +} + +/*-----------------13.11.00 14:56------------------- + * SwRubyPortion::_Adjust(..) + * In ruby portion there are different alignments for + * the ruby text and the main text. + * Left, right, centered and two possibilities of block adjustment + * The block adjustment is realized by spacing between the characteres, + * either with a half space or no space in front of the first letter and + * a half space at the end of the last letter. + * Notice: the smaller line will be manipulated, normally it's the ruby line, + * but it could be the main text, too. + * If there is a tabulator in smaller line, no adjustment is possible. + * --------------------------------------------------*/ + +void SwRubyPortion::_Adjust( SwTxtFormatInfo &rInf ) +{ + SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width(); + xub_StrLen nOldIdx = rInf.GetIdx(); + if( !nLineDiff ) + return; + SwLineLayout *pCurr; + if( nLineDiff < 0 ) + { // The first line has to be adjusted. + if( GetTab1() ) + return; + pCurr = &GetRoot(); + nLineDiff = -nLineDiff; + } + else + { // The second line has to be adjusted. + if( GetTab2() ) + return; + pCurr = GetRoot().GetNext(); + rInf.SetIdx( nOldIdx + GetRoot().GetLen() ); + } + KSHORT nLeft = 0; // the space in front of the first letter + KSHORT nRight = 0; // the space at the end of the last letter + USHORT nSub = 0; + switch ( nAdjustment ) + { + case 1: nRight = static_cast<USHORT>(nLineDiff / 2); // no break + case 2: nLeft = static_cast<USHORT>(nLineDiff - nRight); break; + case 3: nSub = 1; // no break + case 4: + { + xub_StrLen nCharCnt = 0; + SwLinePortion *pPor; + for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() ) + { + if( pPor->InTxtGrp() ) + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nCharCnt ); + rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); + } + if( nCharCnt > nSub ) + { + SwTwips nCalc = nLineDiff / ( nCharCnt - nSub ); + short nTmp; + if( nCalc < SHRT_MAX ) + nTmp = -short(nCalc); + else + nTmp = SHRT_MIN; + + pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp ); + nLineDiff -= nCalc * ( nCharCnt - 1 ); + } + if( nLineDiff > 1 ) + { + nRight = static_cast<USHORT>(nLineDiff / 2); + nLeft = static_cast<USHORT>(nLineDiff - nRight); + } + break; + } + default: ASSERT( sal_False, "New ruby adjustment" ); + } + if( nLeft || nRight ) + { + if( !pCurr->GetPortion() ) + pCurr->SetPortion( new SwTxtPortion( *pCurr ) ); + SwMarginPortion *pMarg = new SwMarginPortion( 0 ); + if( nLeft ) + { + pMarg->AddPrtWidth( nLeft ); + pMarg->SetPortion( pCurr->GetPortion() ); + pCurr->SetPortion( pMarg ); + } + if( nRight ) + { + pMarg = new SwMarginPortion( 0 ); + pMarg->AddPrtWidth( nRight ); + pCurr->FindLastPortion()->Append( pMarg ); + } + } + + pCurr->Width( Width() ); + rInf.SetIdx( nOldIdx ); +} + +/*-----------------08.11.00 14:14------------------- + * CalcRubyOffset() + * has to change the nRubyOffset, if there's a fieldportion + * in the phonetic line. + * The nRubyOffset is the position in the rubystring, where the + * next SwRubyPortion has start the displaying of the phonetics. + * --------------------------------------------------*/ + +void SwRubyPortion::CalcRubyOffset() +{ + const SwLineLayout *pCurr = &GetRoot(); + if( !OnTop() ) + { + pCurr = pCurr->GetNext(); + if( !pCurr ) + return; + } + const SwLinePortion *pPor = pCurr->GetFirstPortion(); + const SwFldPortion *pFld = NULL; + while( pPor ) + { + if( pPor->InFldGrp() ) + pFld = (SwFldPortion*)pPor; + pPor = pPor->GetPortion(); + } + if( pFld ) + { + if( pFld->HasFollow() ) + nRubyOffset = pFld->GetNextOffset(); + else + nRubyOffset = STRING_LEN; + } +} + +/*-----------------13.10.00 16:22------------------- + * SwTxtSizeInfo::GetMultiCreator(..) + * If we (e.g. the position rPos) are inside a two-line-attribute or + * a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct, + * otherwise the function returns zero. + * The rPos parameter is set to the end of the multiportion, + * normally this is the end of the attribute, + * but sometimes it is the start of another attribute, which finished or + * interrupts the first attribute. + * E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute + * with different brackets interrupts another 2-line-attribute. + * --------------------------------------------------*/ + +/*-----------------13.11.00 15:38------------------- + * lcl_Has2Lines(..) + * is a little help function for GetMultiCreator(..) + * It extracts the 2-line-format from a 2-line-attribute or a character style. + * The rValue is set to TRUE, if the 2-line-attribute's value is set and + * no 2-line-format reference is passed. If there is a 2-line-format reference, + * then the rValue is set only, if the 2-line-attribute's value is set _and_ + * the 2-line-formats has the same brackets. + * --------------------------------------------------*/ + +sal_Bool lcl_Has2Lines( const SwTxtAttr& rAttr, const SvxTwoLinesItem* &rpRef, + sal_Bool &rValue ) +{ + const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES ); + if( pItem ) + { + rValue = ((SvxTwoLinesItem*)pItem)->GetValue(); + if( !rpRef ) + rpRef = (SvxTwoLinesItem*)pItem; + else if( ((SvxTwoLinesItem*)pItem)->GetEndBracket() != + rpRef->GetEndBracket() || + ((SvxTwoLinesItem*)pItem)->GetStartBracket() != + rpRef->GetStartBracket() ) + rValue = sal_False; + return sal_True; + } + return sal_False; +} + +/*-----------------16.02.01 16:39------------------- + * lcl_HasRotation(..) + * is a little help function for GetMultiCreator(..) + * It extracts the charrotation from a charrotate-attribute or a character style. + * The rValue is set to TRUE, if the charrotate-attribute's value is set and + * no charrotate-format reference is passed. + * If there is a charrotate-format reference, then the rValue is set only, + * if the charrotate-attribute's value is set _and_ identical + * to the charrotate-format's value. + * --------------------------------------------------*/ + +sal_Bool lcl_HasRotation( const SwTxtAttr& rAttr, + const SvxCharRotateItem* &rpRef, sal_Bool &rValue ) +{ + const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_ROTATE ); + if ( pItem ) + { + rValue = 0 != ((SvxCharRotateItem*)pItem)->GetValue(); + if( !rpRef ) + rpRef = (SvxCharRotateItem*)pItem; + else if( ((SvxCharRotateItem*)pItem)->GetValue() != + rpRef->GetValue() ) + rValue = sal_False; + return sal_True; + } + + return sal_False; +} + +SwMultiCreator* SwTxtSizeInfo::GetMultiCreator( xub_StrLen &rPos, + SwMultiPortion* pMulti ) const +{ + SwScriptInfo& rSI = ((SwParaPortion*)GetParaPortion())->GetScriptInfo(); + + // get the last embedding level + BYTE nCurrLevel; + if ( pMulti ) + { + ASSERT( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" ) + // level associated with bidi-portion; + nCurrLevel = ((SwBidiPortion*)pMulti)->GetLevel(); + } + else + // no nested bidi portion required + nCurrLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; + + // check if there is a field at rPos: + BYTE nNextLevel = nCurrLevel; + sal_Bool bFldBidi = sal_False; + + if ( CH_TXTATR_BREAKWORD == GetChar( rPos ) ) + { + bFldBidi = sal_True; +/* + // examining the script of the field text should be sufficient + // for 99% of all cases + XubString aTxt = GetTxtFrm()->GetTxtNode()->GetExpandTxt( rPos, 1 ); + + if ( pBreakIt->GetBreakIter().is() && aTxt.Len() ) + { + sal_Bool bFldDir = ( i18n::ScriptType::COMPLEX == + pBreakIt->GetRealScriptOfText( aTxt, 0 ) ); + sal_Bool bCurrDir = ( 0 != ( nCurrLevel % 2 ) ); + if ( bFldDir != bCurrDir ) + { + nNextLevel = nCurrLevel + 1; + bFldBidi = sal_True; + } + }*/ + } + else + nNextLevel = rSI.DirType( rPos ); + + if ( GetTxt().Len() != rPos && nNextLevel > nCurrLevel ) + { + rPos = bFldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel ); + if ( STRING_LEN == rPos ) + return NULL; + SwMultiCreator *pRet = new SwMultiCreator; + pRet->pItem = NULL; + pRet->pAttr = NULL; + pRet->nId = SW_MC_BIDI; + pRet->nLevel = nCurrLevel + 1; + return pRet; + } + + // a bidi portion can only contain other bidi portions + if ( pMulti ) + return NULL; + + const SvxCharRotateItem* pRotate = NULL; + const SfxPoolItem* pRotItem; + if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet(). + GetItemState( RES_CHRATR_ROTATE, TRUE, &pRotItem ) && + ((SvxCharRotateItem*)pRotItem)->GetValue() ) + pRotate = (SvxCharRotateItem*)pRotItem; + else + pRotItem = NULL; + const SvxTwoLinesItem* p2Lines = NULL; + const SfxPoolItem* pItem; + if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet(). + GetItemState( RES_CHRATR_TWO_LINES, TRUE, &pItem ) && + ((SvxTwoLinesItem*)pItem)->GetValue() ) + p2Lines = (SvxTwoLinesItem*)pItem; + else + pItem = NULL; + + const SwpHints *pHints = pFrm->GetTxtNode()->GetpSwpHints(); + if( !pHints && !p2Lines && !pRotate ) + return NULL; + const SwTxtAttr *pRuby = NULL; + sal_Bool bTwo = sal_False; + sal_Bool bRot = sal_False; + USHORT n2Lines = USHRT_MAX; + USHORT nRotate = USHRT_MAX; + USHORT nCount = pHints ? pHints->Count() : 0; + USHORT i; + for( i = 0; i < nCount; ++i ) + { + const SwTxtAttr *pTmp = (*pHints)[i]; + xub_StrLen nStart = *pTmp->GetStart(); + if( rPos < nStart ) + break; + if( *pTmp->GetAnyEnd() > rPos ) + { + if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) + pRuby = pTmp; + else + { + const SvxCharRotateItem* pRoTmp = NULL; + if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) ) + { + nRotate = bRot ? i : nCount; + pRotate = pRoTmp; + } + const SvxTwoLinesItem* p2Tmp = NULL; + if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) ) + { + n2Lines = bTwo ? i : nCount; + p2Lines = p2Tmp; + } + } + } + } + if( pRuby ) + { // The winner is ... a ruby attribute and so + // the end of the multiportion is the end of the ruby attribute. + rPos = *pRuby->GetEnd(); + SwMultiCreator *pRet = new SwMultiCreator; + pRet->pItem = NULL; + pRet->pAttr = pRuby; + pRet->nId = SW_MC_RUBY; + pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; + return pRet; + } + if( n2Lines < nCount || ( pItem && pItem == p2Lines && + rPos < GetTxt().Len() ) ) + { // The winner is a 2-line-attribute, + // the end of the multiportion depends on the following attributes... + SwMultiCreator *pRet = new SwMultiCreator; + + // We note the endpositions of the 2-line attributes in aEnd as stack + SvXub_StrLens aEnd; + + // The bOn flag signs the state of the last 2-line attribute in the + // aEnd-stack, it is compatible with the winner-attribute or + // it interrupts the other attribute. + sal_Bool bOn = sal_True; + + if( n2Lines < nCount ) + { + pRet->pItem = NULL; + pRet->pAttr = (*pHints)[n2Lines]; + aEnd.Insert( *pRet->pAttr->GetEnd(), 0 ); + if( pItem ) + { + aEnd[ 0 ] = GetTxt().Len(); + bOn = ((SvxTwoLinesItem*)pItem)->GetEndBracket() == + p2Lines->GetEndBracket() && + ((SvxTwoLinesItem*)pItem)->GetStartBracket() == + p2Lines->GetStartBracket(); + } + } + else + { + pRet->pItem = pItem; + pRet->pAttr = NULL; + aEnd.Insert( GetTxt().Len(), 0 ); + } + pRet->nId = SW_MC_DOUBLE; + pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; + + // n2Lines is the index of the last 2-line-attribute, which contains + // the actual position. + i = 0; + // At this moment we know that at position rPos the "winner"-attribute + // causes a 2-line-portion. The end of the attribute is the end of the + // portion, if there's no interrupting attribute. + // There are two kinds of interruptors: + // - ruby attributes stops the 2-line-attribute, the end of the + // multiline is the start of the ruby attribute + // - 2-line-attributes with value "Off" or with different brackets, + // these attributes may interrupt the winner, but they could be + // neutralized by another 2-line-attribute starting at the same + // position with the same brackets as the winner-attribute. + + // In the following loop rPos is the critical position and it will be + // evaluated, if at rPos starts a interrupting or a maintaining + // continuity attribute. + while( i < nCount ) + { + const SwTxtAttr *pTmp = (*pHints)[i++]; + if( *pTmp->GetAnyEnd() <= rPos ) + continue; + if( rPos < *pTmp->GetStart() ) + { + // If bOn is FALSE and the next attribute starts later than rPos + // the winner attribute is interrupted at rPos. + // If the start of the next atribute is behind the end of + // the last attribute on the aEnd-stack, this is the endposition + // on the stack is the end of the 2-line portion. + if( !bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() ) + break; + // At this moment, bOn is TRUE and the next attribute starts + // behind rPos, so we could move rPos to the next startpoint + rPos = *pTmp->GetStart(); + // We clean up the aEnd-stack, endpositions equal to rPos are + // superfluous. + while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= rPos ) + { + bOn = !bOn; + aEnd.Remove( aEnd.Count()-1, 1 ); + } + // If the endstack is empty, we simulate an attribute with + // state TRUE and endposition rPos + if( !aEnd.Count() ) + { + aEnd.Insert( rPos, 0 ); + bOn = sal_True; + } + } + // A ruby attribute stops the 2-line immediately + if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) + return pRet; + if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) ) + { // We have an interesting attribute.. + if( bTwo == bOn ) + { // .. with the same state, so the last attribute could + // be continued. + if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() ) + aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd(); + } + else + { // .. with a different state. + bOn = bTwo; + // If this is smaller than the last on the stack, we put + // it on the stack. If it has the same endposition, the last + // could be removed. + if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() ) + aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() ); + else if( aEnd.Count() > 1 ) + aEnd.Remove( aEnd.Count()-1, 1 ); + else + aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd(); + } + } + } + if( bOn && aEnd.Count() ) + rPos = aEnd[ aEnd.Count()-1 ]; + return pRet; + } + if( nRotate < nCount || ( pRotItem && pRotItem == pRotate && + rPos < GetTxt().Len() ) ) + { // The winner is a rotate-attribute, + // the end of the multiportion depends on the following attributes... + SwMultiCreator *pRet = new SwMultiCreator; + pRet->nId = SW_MC_ROTATE; + + // We note the endpositions of the 2-line attributes in aEnd as stack + SvXub_StrLens aEnd; + + // The bOn flag signs the state of the last 2-line attribute in the + // aEnd-stack, which could interrupts the winning rotation attribute. + sal_Bool bOn = pItem ? sal_True : sal_False; + aEnd.Insert( GetTxt().Len(), 0 ); + // n2Lines is the index of the last 2-line-attribute, which contains + // the actual position. + i = 0; + xub_StrLen n2Start = rPos; + while( i < nCount ) + { + const SwTxtAttr *pTmp = (*pHints)[i++]; + if( *pTmp->GetAnyEnd() <= n2Start ) + continue; + if( n2Start < *pTmp->GetStart() ) + { + if( bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() ) + break; + n2Start = *pTmp->GetStart(); + while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= n2Start ) + { + bOn = !bOn; + aEnd.Remove( aEnd.Count()-1, 1 ); + } + if( !aEnd.Count() ) + { + aEnd.Insert( n2Start, 0 ); + bOn = sal_False; + } + } + // A ruby attribute stops immediately + if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) + { + bOn = sal_True; + break; + } + p2Lines = NULL; + if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) ) + { + if( bTwo == bOn ) + { + if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() ) + aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd(); + } + else + { + bOn = bTwo; + if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() ) + aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() ); + else if( aEnd.Count() > 1 ) + aEnd.Remove( aEnd.Count()-1, 1 ); + else + aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd(); + } + } + } + if( !bOn && aEnd.Count() ) + n2Start = aEnd[ aEnd.Count()-1 ]; + + if( aEnd.Count() ) + aEnd.Remove( 0, aEnd.Count() ); + + bOn = sal_True; + if( nRotate < nCount ) + { + pRet->pItem = NULL; + pRet->pAttr = (*pHints)[nRotate]; + aEnd.Insert( *pRet->pAttr->GetEnd(), 0 ); + if( pRotItem ) + { + aEnd[ 0 ] = GetTxt().Len(); + bOn = ((SvxCharRotateItem*)pRotItem)->GetValue() == + pRotate->GetValue(); + } + } + else + { + pRet->pItem = pRotItem; + pRet->pAttr = NULL; + aEnd.Insert( GetTxt().Len(), 0 ); + } + i = 0; + while( i < nCount ) + { + const SwTxtAttr *pTmp = (*pHints)[i++]; + if( *pTmp->GetAnyEnd() <= rPos ) + continue; + if( rPos < *pTmp->GetStart() ) + { + if( !bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() ) + break; + rPos = *pTmp->GetStart(); + while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= rPos ) + { + bOn = !bOn; + aEnd.Remove( aEnd.Count()-1, 1 ); + } + if( !aEnd.Count() ) + { + aEnd.Insert( rPos, 0 ); + bOn = sal_True; + } + } + if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) + { + bOn = sal_False; + break; + } + if( lcl_HasRotation( *pTmp, pRotate, bTwo ) ) + { + if( bTwo == bOn ) + { + if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() ) + aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd(); + } + else + { + bOn = bTwo; + if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() ) + aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() ); + else if( aEnd.Count() > 1 ) + aEnd.Remove( aEnd.Count()-1, 1 ); + else + aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd(); + } + } + } + if( bOn && aEnd.Count() ) + rPos = aEnd[ aEnd.Count()-1 ]; + if( rPos > n2Start ) + rPos = n2Start; + return pRet; + } + return NULL; +} + +/*-----------------01.11.00 14:52------------------- + * SwSpaceManipulator + * is a little helper class to manage the spaceadd-arrays of the text adjustment + * during a PaintMultiPortion. + * The constructor prepares the array for the first line of multiportion, + * the SecondLine-function restores the values for the first line and prepares + * the second line. + * The destructor restores the values of the last manipulation. + * --------------------------------------------------*/ + +class SwSpaceManipulator +{ + SwTxtPaintInfo& rInfo; + SwMultiPortion& rMulti; + std::vector<long>* pOldSpaceAdd; + MSHORT nOldSpIdx; + long nSpaceAdd; + sal_Bool bSpaceChg : 1; + sal_uInt8 nOldDir : 2; +public: + SwSpaceManipulator( SwTxtPaintInfo& rInf, SwMultiPortion& rMult ); + ~SwSpaceManipulator(); + void SecondLine(); + inline long GetSpaceAdd() const { return nSpaceAdd; } +}; + +SwSpaceManipulator::SwSpaceManipulator( SwTxtPaintInfo& rInf, + SwMultiPortion& rMult ) : + rInfo( rInf ), rMulti( rMult ) +{ + pOldSpaceAdd = rInfo.GetpSpaceAdd(); + nOldSpIdx = rInfo.GetSpaceIdx(); + nOldDir = rInfo.GetDirection(); + rInfo.SetDirection( rMulti.GetDirection() ); + bSpaceChg = sal_False; + + if( rMulti.IsDouble() ) + { + nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ? + rInfo.GetSpaceAdd() : 0; + if( rMulti.GetRoot().IsSpaceAdd() ) + { + rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); + rInfo.ResetSpaceIdx(); + bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd ); + } + else if( rMulti.HasTabulator() ) + rInfo.SetpSpaceAdd( NULL ); + } + else if ( ! rMulti.IsBidi() ) + { + rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); + rInfo.ResetSpaceIdx(); + } +} + +void SwSpaceManipulator::SecondLine() +{ + if( bSpaceChg ) + { + rInfo.RemoveFirstSpaceAdd(); + bSpaceChg = sal_False; + } + SwLineLayout *pLay = rMulti.GetRoot().GetNext(); + if( pLay->IsSpaceAdd() ) + { + rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() ); + rInfo.ResetSpaceIdx(); + bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd ); + } + else + { + rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ? + 0 : pOldSpaceAdd ); + rInfo.SetSpaceIdx( nOldSpIdx); + } +} + +SwSpaceManipulator::~SwSpaceManipulator() +{ + if( bSpaceChg ) + { + rInfo.RemoveFirstSpaceAdd(); + bSpaceChg = sal_False; + } + rInfo.SetpSpaceAdd( pOldSpaceAdd ); + rInfo.SetSpaceIdx( nOldSpIdx); + rInfo.SetDirection( nOldDir ); +} + +/*-----------------13.10.00 16:24------------------- + * SwTxtPainter::PaintMultiPortion manages the paint for a SwMultiPortion. + * External, for the calling function, it seems to be a normal Paint-function, + * internal it is like a SwTxtFrm::Paint with multiple DrawTextLines + * --------------------------------------------------*/ + +void SwTxtPainter::PaintMultiPortion( const SwRect &rPaint, + SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor ) +{ + GETGRID( pFrm->FindPageFrm() ) + const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid(); + USHORT nGridWidth = 0; + USHORT nRubyHeight = 0; + sal_Bool bRubyTop = sal_False; + + if ( bHasGrid ) + { + nGridWidth = pGrid->GetBaseHeight(); + nRubyHeight = pGrid->GetRubyHeight(); + bRubyTop = ! pGrid->GetRubyTextBelow(); + } + + // do not allow grid mode for first line in ruby portion + const sal_Bool bRubyInGrid = bHasGrid && rMulti.IsRuby(); + + const USHORT nOldHeight = rMulti.Height(); + const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid(); + + if ( bRubyInGrid ) + { + GetInfo().SetSnapToGrid( ! bRubyTop ); + rMulti.Height( pCurr->Height() ); + } + + SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); + BYTE nEnvDir = 0; + BYTE nThisDir = 0; + BYTE nFrmDir = 0; + if ( rMulti.IsBidi() ) + { + // these values are needed for the calculation of the x coordinate + // and the layout mode + ASSERT( ! pEnvPor || pEnvPor->IsBidi(), + "Oh no, I expected a BidiPortion" ) + nFrmDir = GetInfo().GetTxtFrm()->IsRightToLeft() ? 1 : 0; + nEnvDir = pEnvPor ? ((SwBidiPortion*)pEnvPor)->GetLevel() % 2 : nFrmDir; + nThisDir = ((SwBidiPortion&)rMulti).GetLevel() % 2; + } + +#if OSL_DEBUG_LEVEL > 1 + // only paint first level bidi portions + if( rMulti.Width() > 1 && ! pEnvPor ) + GetInfo().DrawViewOpt( rMulti, POR_FLD ); +#endif + + if ( bRubyInGrid ) + rMulti.Height( nOldHeight ); + + // do we have to repaint a post it portion? + if( GetInfo().OnWin() && rMulti.GetPortion() && + ! rMulti.GetPortion()->Width() ) + rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti ); + + // old values must be saved and restored at the end + xub_StrLen nOldLen = GetInfo().GetLen(); + KSHORT nOldX = KSHORT(GetInfo().X()); + long nOldY = GetInfo().Y(); + xub_StrLen nOldIdx = GetInfo().GetIdx(); + + SwSpaceManipulator aManip( GetInfo(), rMulti ); + + SwFontSave *pFontSave; + SwFont* pTmpFnt; + + if( rMulti.IsDouble() ) + { + pTmpFnt = new SwFont( *GetInfo().GetFont() ); + if( rMulti.IsDouble() ) + { + SetPropFont( 50 ); + pTmpFnt->SetProportion( GetPropFont() ); + } + pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this ); + } + else + { + pFontSave = NULL; + pTmpFnt = NULL; + } + + if( rMulti.HasBrackets() ) + { + xub_StrLen nTmpOldIdx = GetInfo().GetIdx(); + GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart); + SeekAndChg( GetInfo() ); + ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True ); + GetInfo().SetIdx( nTmpOldIdx ); + } + + KSHORT nTmpX = KSHORT(GetInfo().X()); + + SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion + SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line + SwTwips nOfst = 0; + + // GetInfo().Y() is the baseline from the surrounding line. We must switch + // this temporary to the baseline of the inner lines of the multiportion. + if( rMulti.HasRotation() ) + { + if( rMulti.IsRevers() ) + { + GetInfo().Y( nOldY - rMulti.GetAscent() ); + nOfst = nTmpX + rMulti.Width(); + } + else + { + GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() ); + nOfst = nTmpX; + } + } + else if ( rMulti.IsBidi() ) + { + // does the current bidi portion has the same direction + // as its environment? + if ( nEnvDir != nThisDir ) + { + // different directions, we have to adjust the x coordinate + SwTwips nMultiWidth = rMulti.Width() + + rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() ); + + if ( nFrmDir == nThisDir ) + GetInfo().X( GetInfo().X() - nMultiWidth ); + else + GetInfo().X( GetInfo().X() + nMultiWidth ); + } + + nOfst = nOldY - rMulti.GetAscent(); + + // set layout mode + aLayoutModeModifier.Modify( nThisDir ); + } + else + nOfst = nOldY - rMulti.GetAscent(); + + sal_Bool bRest = pLay->IsRest(); + sal_Bool bFirst = sal_True; + + ASSERT( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(), + " Only BiDi portions are allowed to use the common underlining font" ) + + do + { + if ( bHasGrid ) + { + if( rMulti.HasRotation() ) + { + const USHORT nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 + + pPor->GetAscent(); + if( rMulti.IsRevers() ) + GetInfo().X( nOfst - nAdjustment ); + else + GetInfo().X( nOfst + nAdjustment ); + } + else + { + // special treatment for ruby portions in grid mode + SwTwips nAdjustment = 0; + if ( rMulti.IsRuby() ) + { + if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) ) + // adjust base text + nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2; + else if ( bRubyTop ) + // adjust upper ruby text + nAdjustment = nRubyHeight - pPor->Height(); + // else adjust lower ruby text + } + + GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() ); + } + } + else if( rMulti.HasRotation() ) + { + if( rMulti.IsRevers() ) + GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, sal_True ) ); + else + GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) ); + } + else + GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) ); + + sal_Bool bSeeked = sal_True; + GetInfo().SetLen( pPor->GetLen() ); + + if( bRest && pPor->InFldGrp() && !pPor->GetLen() ) + { + if( ((SwFldPortion*)pPor)->HasFont() ) + bSeeked = sal_False; + else + SeekAndChgBefore( GetInfo() ); + } + else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() ) + SeekAndChg( GetInfo() ); + else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() ) + { + if( GetRedln() ) + SeekAndChg( GetInfo() ); + else + SeekAndChgBefore( GetInfo() ); + } + else + bSeeked = sal_False; + + SwLinePortion *pNext = pPor->GetPortion(); + if(GetInfo().OnWin() && pNext && !pNext->Width() ) + { + if ( !bSeeked ) + SeekAndChg( GetInfo() ); + pNext->PrePaint( GetInfo(), pPor ); + } + + CheckSpecialUnderline( pPor ); + SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt(); + if ( pUnderLineFnt ) + { + if ( rMulti.IsDouble() ) + pUnderLineFnt->GetFont().SetProportion( 50 ); + pUnderLineFnt->SetPos( GetInfo().GetPos() ); + } + + if ( rMulti.IsBidi() ) + { + // we do not allow any rotation inside a bidi portion + SwFont* pTmpFont = GetInfo().GetFont(); + pTmpFont->SetVertical( 0, GetInfo().GetTxtFrm()->IsVertical() ); + } + + if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) + { + // but we do allow nested bidi portions + ASSERT( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" ) + PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor, &rMulti ); + } + else + pPor->Paint( GetInfo() ); + + if( GetFnt()->IsURL() && pPor->InTxtGrp() ) + GetInfo().NotifyURL( *pPor ); + + bFirst &= !pPor->GetLen(); + if( pNext || !pPor->IsMarginPortion() ) + pPor->Move( GetInfo() ); + + pPor = pNext; + + // If there's no portion left, we go to the next line + if( !pPor && pLay->GetNext() ) + { + pLay = pLay->GetNext(); + pPor = pLay->GetFirstPortion(); + bRest = pLay->IsRest(); + aManip.SecondLine(); + + // delete underline font + delete GetInfo().GetUnderFnt(); + GetInfo().SetUnderFnt( 0 ); + + if( rMulti.HasRotation() ) + { + if( rMulti.IsRevers() ) + { + nOfst += pLay->Height(); + GetInfo().Y( nOldY - rMulti.GetAscent() ); + } + else + { + nOfst -= pLay->Height(); + GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() ); + } + } + else if ( bHasGrid && rMulti.IsRuby() ) + { + GetInfo().X( nTmpX ); + if ( bRubyTop ) + { + nOfst += nRubyHeight; + GetInfo().SetSnapToGrid( sal_True ); + } + else + { + nOfst += pCurr->Height() - nRubyHeight; + GetInfo().SetSnapToGrid( sal_False ); + } + } else + { + GetInfo().X( nTmpX ); + // We switch to the baseline of the next inner line + nOfst += rMulti.GetRoot().Height(); + } + } + } while( pPor ); + + if ( bRubyInGrid ) + GetInfo().SetSnapToGrid( bOldGridModeAllowed ); + + // delete underline font + if ( ! rMulti.IsBidi() ) + { + delete GetInfo().GetUnderFnt(); + GetInfo().SetUnderFnt( 0 ); + } + + GetInfo().SetIdx( nOldIdx ); + GetInfo().Y( nOldY ); + + if( rMulti.HasBrackets() ) + { + xub_StrLen nTmpOldIdx = GetInfo().GetIdx(); + GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart); + SeekAndChg( GetInfo() ); + GetInfo().X( nOldX ); + ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), + aManip.GetSpaceAdd(), sal_False ); + GetInfo().SetIdx( nTmpOldIdx ); + } + // Restore the saved values + GetInfo().X( nOldX ); + GetInfo().SetLen( nOldLen ); + delete pFontSave; + delete pTmpFnt; + SetPropFont( 0 ); +} + +sal_Bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpFld ) +{ + SwLinePortion* pLast = pLine; + rpFld = pLine->GetPortion(); + while( rpFld && !rpFld->InFldGrp() ) + { + pLast = rpFld; + rpFld = rpFld->GetPortion(); + } + sal_Bool bRet = rpFld != 0; + if( bRet ) + { + if( ((SwFldPortion*)rpFld)->IsFollow() ) + { + rpFld->Truncate(); + pLast->SetPortion( NULL ); + } + else + rpFld = NULL; + } + pLine->Truncate(); + return bRet; +} + +/*---------------------------------------------------- + * lcl_TruncateMultiPortion + * If a multi portion completely has to go to the + * next line, this function is called to trunctate + * the rest of the remaining multi portion + * --------------------------------------------------*/ + +void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTxtFormatInfo& rInf, + xub_StrLen nStartIdx ) +{ + rMulti.GetRoot().Truncate(); + rMulti.GetRoot().SetLen(0); + rMulti.GetRoot().Width(0); +// rMulti.CalcSize( *this, aInf ); + if ( rMulti.GetRoot().GetNext() ) + { + rMulti.GetRoot().GetNext()->Truncate(); + rMulti.GetRoot().GetNext()->SetLen( 0 ); + rMulti.GetRoot().GetNext()->Width( 0 ); + } + rMulti.Width( 0 ); + rMulti.SetLen(0); + rInf.SetIdx( nStartIdx ); +} + +/*----------------------------------------------------------------------------- + * SwTxtFormatter::BuildMultiPortion + * manages the formatting of a SwMultiPortion. External, for the calling + * function, it seems to be a normal Format-function, internal it is like a + * SwTxtFrm::_Format with multiple BuildPortions + *---------------------------------------------------------------------------*/ + +BOOL SwTxtFormatter::BuildMultiPortion( SwTxtFormatInfo &rInf, + SwMultiPortion& rMulti ) +{ + SwTwips nMaxWidth = rInf.Width(); + KSHORT nOldX = 0; + + if( rMulti.HasBrackets() ) + { + xub_StrLen nOldIdx = rInf.GetIdx(); + rInf.SetIdx( ((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart ); + SeekAndChg( rInf ); + nOldX = KSHORT(GetInfo().X()); + ((SwDoubleLinePortion&)rMulti).FormatBrackets( rInf, nMaxWidth ); + rInf.SetIdx( nOldIdx ); + } + + SeekAndChg( rInf ); + SwFontSave *pFontSave; + if( rMulti.IsDouble() ) + { + SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); + if( rMulti.IsDouble() ) + { + SetPropFont( 50 ); + pTmpFnt->SetProportion( GetPropFont() ); + } + pFontSave = new SwFontSave( rInf, pTmpFnt, this ); + } + else + pFontSave = NULL; + + SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); + if ( rMulti.IsBidi() ) + { + // set layout mode + aLayoutModeModifier.Modify( ! rInf.GetTxtFrm()->IsRightToLeft() ); + } + + SwTwips nTmpX = 0; + + if( rMulti.HasRotation() ) + { + // For nMaxWidth we take the height of the body frame. + // #i25067#: If the current frame is inside a table, we restrict + // nMaxWidth to the current frame height, unless the frame size + // attribute is set to variable size: + + // We set nTmpX (which is used for portion calculating) to the + // current Y value + const SwPageFrm* pPage = pFrm->FindPageFrm(); + ASSERT( pPage, "No page in frame!"); + const SwLayoutFrm* pUpperFrm = pPage; + + if ( pFrm->IsInTab() ) + { + pUpperFrm = pFrm->GetUpper(); + while ( pUpperFrm && !pUpperFrm->IsCellFrm() ) + pUpperFrm = pUpperFrm->GetUpper(); + ASSERT( pUpperFrm, "pFrm is in table but does not have an upper cell frame" ) + const SwTableLine* pLine = ((SwRowFrm*)pUpperFrm->GetUpper())->GetTabLine(); + const SwFmtFrmSize& rFrmFmtSize = pLine->GetFrmFmt()->GetFrmSize(); + if ( ATT_VAR_SIZE == rFrmFmtSize.GetHeightSizeType() ) + pUpperFrm = pPage; + } + if ( pUpperFrm == pPage && !pFrm->IsInFtn() ) + pUpperFrm = pPage->FindBodyCont(); + + nMaxWidth = pUpperFrm ? + ( rInf.GetTxtFrm()->IsVertical() ? + pUpperFrm->Prt().Width() : + pUpperFrm->Prt().Height() ) : + USHRT_MAX; + } + else + nTmpX = rInf.X(); + + SwMultiPortion* pOldMulti = pMulti; + + pMulti = &rMulti; + SwLineLayout *pOldCurr = pCurr; + xub_StrLen nOldStart = GetStart(); + SwTwips nMinWidth = nTmpX + 1; + SwTwips nActWidth = nMaxWidth; + const xub_StrLen nStartIdx = rInf.GetIdx(); + xub_StrLen nMultiLen = rMulti.GetLen(); + + SwLinePortion *pFirstRest; + SwLinePortion *pSecondRest; + if( rMulti.IsFormatted() ) + { + if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest ) + && rMulti.IsDouble() && rMulti.GetRoot().GetNext() ) + lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest ); + if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() ) + lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest ); + else + pSecondRest = NULL; + } + else + { + pFirstRest = rMulti.GetRoot().GetPortion(); + pSecondRest = rMulti.GetRoot().GetNext() ? + rMulti.GetRoot().GetNext()->GetPortion() : NULL; + if( pFirstRest ) + rMulti.GetRoot().SetPortion( NULL ); + if( pSecondRest ) + rMulti.GetRoot().GetNext()->SetPortion( NULL ); + rMulti.SetFormatted(); + nMultiLen = nMultiLen - rInf.GetIdx(); + } + + // save some values + const XubString* pOldTxt = &(rInf.GetTxt()); + const SwTwips nOldPaintOfst = rInf.GetPaintOfst(); + + XubString aMultiStr( rInf.GetTxt(), 0, nMultiLen + rInf.GetIdx() ); + rInf.SetTxt( aMultiStr ); + SwTxtFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth ); + // Do we allow break cuts? The FirstMulti-Flag is evaluated during + // line break determination. + sal_Bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart(); + + SwLinePortion *pNextFirst = NULL; + SwLinePortion *pNextSecond = NULL; + BOOL bRet = FALSE; + + GETGRID( pFrm->FindPageFrm() ) + const sal_Bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType(); + + USHORT nGridWidth = 0; + USHORT nRubyHeight = 0; + sal_Bool bRubyTop = sal_False; + + if ( bHasGrid ) + { + nGridWidth = pGrid->GetBaseHeight(); + nRubyHeight = pGrid->GetRubyHeight(); + bRubyTop = ! pGrid->GetRubyTextBelow(); + } + + do + { + pCurr = &rMulti.GetRoot(); + nStart = nStartIdx; + bRet = FALSE; + FormatReset( aInf ); + aInf.X( nTmpX ); + aInf.Width( KSHORT(nActWidth) ); + aInf.RealWidth( KSHORT(nActWidth) ); + aInf.SetFirstMulti( bFirstMulti ); + aInf.SetNumDone( rInf.IsNumDone() ); + aInf.SetFtnDone( rInf.IsFtnDone() ); + + if( pFirstRest ) + { + ASSERT( pFirstRest->InFldGrp(), "BuildMulti: Fieldrest expected"); + SwFldPortion *pFld = + ((SwFldPortion*)pFirstRest)->Clone( + ((SwFldPortion*)pFirstRest)->GetExp() ); + pFld->SetFollow( sal_True ); + aInf.SetRest( pFld ); + } + aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() ); + + // in grid mode we temporarily have to disable the grid for the ruby line + const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid(); + if ( bHasGrid && aInf.IsRuby() && bRubyTop ) + aInf.SetSnapToGrid( sal_False ); + + // If there's no more rubytext, then buildportion is forbidden + if( pFirstRest || !aInf.IsRuby() ) + BuildPortions( aInf ); + + aInf.SetSnapToGrid( bOldGridModeAllowed ); + + rMulti.CalcSize( *this, aInf ); + pCurr->SetRealHeight( pCurr->Height() ); + + if( rMulti.IsBidi() ) + { + pNextFirst = aInf.GetRest(); + break; + } + + if( rMulti.HasRotation() && !rMulti.IsDouble() ) + break; + // second line has to be formatted + else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest()) + { + xub_StrLen nFirstLen = pCurr->GetLen(); + delete pCurr->GetNext(); + pCurr->SetNext( new SwLineLayout() ); + pCurr = pCurr->GetNext(); + nStart = aInf.GetIdx(); + aInf.X( nTmpX ); + SwTxtFormatInfo aTmp( aInf, *pCurr, nActWidth ); + if( rMulti.IsRuby() ) + { + aTmp.SetRuby( !rMulti.OnTop() ); + pNextFirst = aInf.GetRest(); + if( pSecondRest ) + { + ASSERT( pSecondRest->InFldGrp(), "Fieldrest expected"); + SwFldPortion *pFld = ((SwFldPortion*)pSecondRest)->Clone( + ((SwFldPortion*)pSecondRest)->GetExp() ); + pFld->SetFollow( sal_True ); + aTmp.SetRest( pFld ); + } + if( !rMulti.OnTop() && nFirstLen < nMultiLen ) + bRet = sal_True; + } + else + aTmp.SetRest( aInf.GetRest() ); + aInf.SetRest( NULL ); + + // in grid mode we temporarily have to disable the grid for the ruby line + if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop ) + aTmp.SetSnapToGrid( sal_False ); + + BuildPortions( aTmp ); + + aTmp.SetSnapToGrid( bOldGridModeAllowed ); + + rMulti.CalcSize( *this, aInf ); + rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() ); + pCurr->SetRealHeight( pCurr->Height() ); + if( rMulti.IsRuby() ) + { + pNextSecond = aTmp.GetRest(); + if( pNextFirst ) + bRet = sal_True; + } + else + pNextFirst = aTmp.GetRest(); + if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen ) + || aTmp.GetRest() ) + // our guess for width of multiportion was too small, + // text did not fit into multiportion + bRet = sal_True; + } + if( rMulti.IsRuby() ) + break; + if( bRet ) + { + // our guess for multiportion width was too small, + // we set min to act + nMinWidth = nActWidth; + nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4; + if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() ) + // we have too less space, we must allow break cuts + // ( the first multi flag is considered during TxtPortion::_Format() ) + bFirstMulti = sal_False; + if( nActWidth <= nMinWidth ) + break; + } + else + { + // For Solaris, this optimisation can causes trouble: + // Setting this to the portion width ( = rMulti.Width() ) + // can make GetTextBreak inside SwTxtGuess::Guess return to small + // values. Therefore we add some extra twips. + if( nActWidth > nTmpX + rMulti.Width() + 6 ) + nActWidth = nTmpX + rMulti.Width() + 6; + nMaxWidth = nActWidth; + nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4; + if( nActWidth >= nMaxWidth ) + break; + // we do not allow break cuts during formatting + bFirstMulti = sal_True; + } + delete pNextFirst; + pNextFirst = NULL; + } while ( TRUE ); + + pMulti = pOldMulti; + + pCurr = pOldCurr; + nStart = nOldStart; + SetPropFont( 0 ); + + rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ? + rMulti.GetRoot().GetNext()->GetLen() : 0 ) ); + + if( rMulti.IsDouble() ) + { + ((SwDoubleLinePortion&)rMulti).CalcBlanks( rInf ); + if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() ) + { + SwLineLayout* pLine = &rMulti.GetRoot(); + if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() > 0 ) + { + rInf.SetIdx( nStartIdx + pLine->GetLen() ); + pLine = pLine->GetNext(); + } + if( pLine ) + { + GetInfo().SetMulti( sal_True ); + CalcNewBlock( pLine, NULL, rMulti.Width() ); + GetInfo().SetMulti( sal_False ); + } + rInf.SetIdx( nStartIdx ); + } + if( ((SwDoubleLinePortion&)rMulti).GetBrackets() ) + { + rMulti.Width( rMulti.Width() + + ((SwDoubleLinePortion&)rMulti).BracketWidth() ); + GetInfo().X( nOldX ); + } + } + else + { + rMulti.ActualizeTabulator(); + if( rMulti.IsRuby() ) + { + ((SwRubyPortion&)rMulti).Adjust( rInf ); + ((SwRubyPortion&)rMulti).CalcRubyOffset(); + } + } + if( rMulti.HasRotation() ) + { + SwTwips nH = rMulti.Width(); + SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2; + if( nAsc > nH ) + nAsc = nH; + else if( nAsc < 0 ) + nAsc = 0; + rMulti.Width( rMulti.Height() ); + rMulti.Height( KSHORT(nH) ); + rMulti.SetAscent( KSHORT(nAsc) ); + bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) && + nStartIdx != rInf.GetLineStart(); + } + else if ( rMulti.IsBidi() ) + { + bRet = rMulti.GetLen() < nMultiLen || pNextFirst; + } + + // line break has to be performed! + if( bRet ) + { + ASSERT( !pNextFirst || pNextFirst->InFldGrp(), + "BuildMultiPortion: Surprising restportion, field expected" ); + SwMultiPortion *pTmp; + if( rMulti.IsDouble() ) + pTmp = new SwDoubleLinePortion( ((SwDoubleLinePortion&)rMulti), + nMultiLen + rInf.GetIdx() ); + else if( rMulti.IsRuby() ) + { + ASSERT( !pNextSecond || pNextSecond->InFldGrp(), + "BuildMultiPortion: Surprising restportion, field expected" ); + + if ( rInf.GetIdx() == rInf.GetLineStart() ) + { + // the ruby portion has to be split in two portions + pTmp = new SwRubyPortion( ((SwRubyPortion&)rMulti), + nMultiLen + rInf.GetIdx() ); + + if( pNextSecond ) + { + pTmp->GetRoot().SetNext( new SwLineLayout() ); + pTmp->GetRoot().GetNext()->SetPortion( pNextSecond ); + } + pTmp->SetFollowFld(); + } + else + { + // we try to keep our ruby portion together + lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); + pTmp = 0; + } + } + else if( rMulti.HasRotation() ) + { + // we try to keep our rotated portion together + lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); + pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(), + rMulti.GetDirection() ); + } + // during a recursion of BuildMultiPortions we may not build + // a new SwBidiPortion, this would cause a memory leak + else if( rMulti.IsBidi() && ! pMulti ) + { + if ( ! rMulti.GetLen() ) + lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); + + // If there is a HolePortion at the end of the bidi portion, + // it has to be moved behind the bidi portion. Otherwise + // the visual cursor travelling gets into trouble. + SwLineLayout& aRoot = rMulti.GetRoot(); + SwLinePortion* pPor = aRoot.GetFirstPortion(); + while ( pPor ) + { + if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() ) + { + SwLinePortion* pHolePor = pPor->GetPortion(); + pPor->SetPortion( NULL ); + aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() ); + rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() ); + rMulti.SetPortion( pHolePor ); + break; + } + pPor = pPor->GetPortion(); + } + + pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(), + ((SwBidiPortion&)rMulti).GetLevel() ); + } + else + pTmp = NULL; + + if ( ! rMulti.GetLen() && rInf.GetLast() ) + { + SeekAndChgBefore( rInf ); + rInf.GetLast()->FormatEOL( rInf ); + } + + if( pNextFirst && pTmp ) + { + pTmp->SetFollowFld(); + pTmp->GetRoot().SetPortion( pNextFirst ); + } + else + // A follow field portion is still waiting. If nobody wants it, + // we delete it. + delete pNextFirst; + + rInf.SetRest( pTmp ); + } + + rInf.SetTxt( *pOldTxt ); + rInf.SetPaintOfst( nOldPaintOfst ); + rInf.SetStop( aInf.IsStop() ); + rInf.SetNumDone( sal_True ); + rInf.SetFtnDone( sal_True ); + SeekAndChg( rInf ); + delete pFirstRest; + delete pSecondRest; + delete pFontSave; + return bRet; +} + +/*-----------------08.11.00 09:29------------------- + * SwTxtFormatter::MakeRestPortion(..) + * When a fieldportion at the end of line breaks and needs a following + * fieldportion in the next line, then the "restportion" of the formatinfo + * has to be set. Normally this happens during the formatting of the first + * part of the fieldportion. + * But sometimes the formatting starts at the line with the following part, + * exspecally when the following part is on the next page. + * In this case the MakeRestPortion-function has to create the following part. + * The first parameter is the line that contains possibly a first part + * of a field. When the function finds such field part, it creates the right + * restportion. This may be a multiportion, e.g. if the field is surrounded by + * a doubleline- or ruby-portion. + * The second parameter is the start index of the line. + * --------------------------------------------------*/ + +SwLinePortion* SwTxtFormatter::MakeRestPortion( const SwLineLayout* pLine, + xub_StrLen nPosition ) +{ + if( !nPosition ) + return NULL; + xub_StrLen nMultiPos = nPosition - pLine->GetLen(); + const SwMultiPortion *pTmpMulti = NULL; + const SwMultiPortion *pHelpMulti = NULL; + const SwLinePortion* pPor = pLine->GetFirstPortion(); + SwFldPortion *pFld = NULL; + while( pPor ) + { + if( pPor->GetLen() ) + { + if( !pHelpMulti ) + { + nMultiPos = nMultiPos + pPor->GetLen(); + pTmpMulti = NULL; + } + } + if( pPor->InFldGrp() ) + { + if( !pHelpMulti ) + pTmpMulti = NULL; + pFld = (SwFldPortion*)pPor; + } + else if( pPor->IsMultiPortion() ) + { + ASSERT( !pHelpMulti || pHelpMulti->IsBidi(), + "Nested multiportions are forbidden." ); + + pFld = NULL; + pTmpMulti = (SwMultiPortion*)pPor; + } + pPor = pPor->GetPortion(); + // If the last portion is a multi-portion, we enter it + // and look for a field portion inside. + // If we are already in a multiportion, we could change to the + // next line + if( !pPor && pTmpMulti ) + { + if( pHelpMulti ) + { // We're already inside the multiportion, let's take the second + // line, if we are in a double line portion + if( !pHelpMulti->IsRuby() ) + pPor = pHelpMulti->GetRoot().GetNext(); + pTmpMulti = NULL; + } + else + { // Now we enter a multiportion, in a ruby portion we take the + // main line, not the phonetic line, in a doublelineportion we + // starts with the first line. + pHelpMulti = pTmpMulti; + nMultiPos = nMultiPos - pHelpMulti->GetLen(); + if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() ) + pPor = pHelpMulti->GetRoot().GetNext(); + else + pPor = pHelpMulti->GetRoot().GetFirstPortion(); + } + } + } + if( pFld && !pFld->HasFollow() ) + pFld = NULL; + + SwLinePortion *pRest = NULL; + if( pFld ) + { + const SwTxtAttr *pHint = GetAttr( nPosition - 1 ); + if( pHint && pHint->Which() == RES_TXTATR_FIELD ) + { + pRest = NewFldPortion( GetInfo(), pHint ); + if( pRest->InFldGrp() ) + ((SwFldPortion*)pRest)->TakeNextOffset( pFld ); + else + { + delete pRest; + pRest = NULL; + } + } + } + if( !pHelpMulti ) + return pRest; + + nPosition = nMultiPos + pHelpMulti->GetLen(); + SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 ); + + if ( !pCreate ) + { + ASSERT( !pHelpMulti->GetLen(), "Multiportion without attribut?" ); + if ( nMultiPos ) + --nMultiPos; + pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 ); + } + + if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() && + ((SwRubyPortion*)pHelpMulti)->GetRubyOffset() < STRING_LEN ) ) + { + SwMultiPortion* pTmp; + if( pHelpMulti->IsDouble() ) + pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos ); + else if( pHelpMulti->IsBidi() ) + pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel ); + else if( pHelpMulti->IsRuby() ) + { + sal_Bool bRubyTop; + sal_Bool* pRubyPos = 0; + + if ( GetInfo().SnapToGrid() ) + { + GETGRID( pFrm->FindPageFrm() ) + if ( pGrid ) + { + bRubyTop = ! pGrid->GetRubyTextBelow(); + pRubyPos = &bRubyTop; + } + } + + pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(), + *pFrm->GetTxtNode()->getIDocumentSettingAccess(), + nMultiPos, ((SwRubyPortion*)pHelpMulti)->GetRubyOffset(), + pRubyPos ); + } + else if( pHelpMulti->HasRotation() ) + pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() ); + else + { + delete pCreate; + return pRest; + } + delete pCreate; + pTmp->SetFollowFld(); + if( pRest ) + { + SwLineLayout *pLay = &pTmp->GetRoot(); + if( pTmp->IsRuby() && pTmp->OnTop() ) + { + pLay->SetNext( new SwLineLayout() ); + pLay = pLay->GetNext(); + } + pLay->SetPortion( pRest ); + } + return pTmp; + } + return pRest; +} + + + +/*-----------------23.10.00 10:47------------------- + * SwTxtCursorSave notes the start and current line of a SwTxtCursor, + * sets them to the values for GetCrsrOfst inside a multiportion + * and restores them in the destructor. + * --------------------------------------------------*/ + +SwTxtCursorSave::SwTxtCursorSave( SwTxtCursor* pTxtCursor, + SwMultiPortion* pMulti, + SwTwips nY, + USHORT& nX, + xub_StrLen nCurrStart, + long nSpaceAdd ) +{ + pTxtCrsr = pTxtCursor; + nStart = pTxtCursor->nStart; + pTxtCursor->nStart = nCurrStart; + pCurr = pTxtCursor->pCurr; + pTxtCursor->pCurr = &pMulti->GetRoot(); + while( pTxtCursor->Y() + pTxtCursor->GetLineHeight() < nY && + pTxtCursor->Next() ) + ; // nothing + nWidth = pTxtCursor->pCurr->Width(); + nOldProp = pTxtCursor->GetPropFont(); + + if ( pMulti->IsDouble() || pMulti->IsBidi() ) + { + bSpaceChg = pMulti->ChgSpaceAdd( pTxtCursor->pCurr, nSpaceAdd ); + + USHORT nSpaceCnt; + if ( pMulti->IsDouble() ) + { + pTxtCursor->SetPropFont( 50 ); + nSpaceCnt = ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt(); + } + else + { + const xub_StrLen nOldIdx = pTxtCursor->GetInfo().GetIdx(); + pTxtCursor->GetInfo().SetIdx ( nCurrStart ); + nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt(pTxtCursor->GetInfo()); + pTxtCursor->GetInfo().SetIdx ( nOldIdx ); + } + + if( nSpaceAdd > 0 && !pMulti->HasTabulator() ) + pTxtCursor->pCurr->Width( static_cast<USHORT>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) ); + + // For a BidiPortion we have to calculate the offset from the + // end of the portion + if ( nX && pMulti->IsBidi() ) + nX = pTxtCursor->pCurr->Width() - nX; + } + else + bSpaceChg = sal_False; +} + +SwTxtCursorSave::~SwTxtCursorSave() +{ + if( bSpaceChg ) + SwDoubleLinePortion::ResetSpaceAdd( pTxtCrsr->pCurr ); + pTxtCrsr->pCurr->Width( KSHORT(nWidth) ); + pTxtCrsr->pCurr = pCurr; + pTxtCrsr->nStart = nStart; + pTxtCrsr->SetPropFont( nOldProp ); +} + diff --git a/sw/source/core/text/pormulti.hxx b/sw/source/core/text/pormulti.hxx new file mode 100644 index 000000000000..fcb2fea9c818 --- /dev/null +++ b/sw/source/core/text/pormulti.hxx @@ -0,0 +1,271 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pormulti.hxx,v $ + * $Revision: 1.26.112.1 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORMULTI_HXX +#define _PORMULTI_HXX + +#include "porlay.hxx" +#include "porexp.hxx" + +class SwTxtFormatInfo; +class SwFldPortion; +class SwTxtCursor; +class SwLineLayout; +class SwTxtPaintInfo; +class SwTxtAttr; +class SfxPoolItem; +class SwFont; + +/*-----------------02.02.01 15:01------------------- + * SwMultiCreator is a small structur to create a multiportion. + * It contains the kind of multiportion and a textattribute + * or a poolitem. + * The GetMultiCreator-function fills this structur and + * the Ctor of the SwMultiPortion uses it. + * --------------------------------------------------*/ + +#define SW_MC_DOUBLE 0 +#define SW_MC_RUBY 1 +#define SW_MC_ROTATE 2 +#define SW_MC_BIDI 3 + +struct SwMultiCreator +{ + const SwTxtAttr* pAttr; + const SfxPoolItem* pItem; + BYTE nId; + BYTE nLevel; +}; + +/*-----------------25.10.00 16:19------------------- + * A two-line-portion (SwMultiPortion) could have surrounding brackets, + * in this case the structur SwBracket will be used. + * --------------------------------------------------*/ + +struct SwBracket +{ + xub_StrLen nStart; // Start of text attribute determins the font + KSHORT nAscent; // Ascent of the brackets + KSHORT nHeight; // Height of them + KSHORT nPreWidth; // Width of the opening bracket + KSHORT nPostWidth; // Width of the closing bracket + sal_Unicode cPre; // Initial character, e.g. '(' + sal_Unicode cPost; // Final character, e.g. ')' + BYTE nPreScript; // Script of the initial character + BYTE nPostScript; // Script of the final character +}; + +/*-----------------16.10.00 12:45------------------- + * The SwMultiPortion is line portion inside a line portion, + * it's a group of portions, + * e.g. a double line portion in a line + * or phonetics (ruby) + * or combined characters + * or a rotated portion. + * --------------------------------------------------*/ + +class SwMultiPortion : public SwLinePortion +{ + SwLineLayout aRoot; // One or more lines + SwFldPortion *pFldRest; // Field rest from the previous line + sal_Bool bTab1 :1; // First line tabulator + sal_Bool bTab2 :1; // Second line includes tabulator + sal_Bool bDouble :1; // Double line + sal_Bool bRuby :1; // Phonetics + sal_Bool bBidi :1; + sal_Bool bTop :1; // Phonetic position + sal_Bool bFormatted :1; // Already formatted + sal_Bool bFollowFld :1; // Field follow inside + sal_uInt8 nDirection:2; // Direction (0/90/180/270 degrees) + sal_Bool bFlyInCntnt:1; // Fly as character inside +protected: + SwMultiPortion( xub_StrLen nEnd ) : pFldRest( 0 ), bTab1( sal_False ), + bTab2( sal_False ), bDouble( sal_False ), bRuby( sal_False ), + bBidi( sal_False ), bFormatted( sal_False ), bFollowFld( sal_False ), + nDirection( 0 ), bFlyInCntnt( sal_False ) + { SetWhichPor( POR_MULTI ); SetLen( nEnd ); } + inline void SetDouble() { bDouble = sal_True; } + inline void SetRuby() { bRuby = sal_True; } + inline void SetBidi() { bBidi = sal_True; } + inline void SetTop( sal_Bool bNew ) { bTop = bNew; } + inline void SetTab1( sal_Bool bNew ) { bTab1 = bNew; } + inline void SetTab2( sal_Bool bNew ) { bTab2 = bNew; } + inline void SetDirection( sal_uInt8 nNew ) { nDirection = nNew; } + inline sal_Bool GetTab1() const { return bTab1; } + inline sal_Bool GetTab2() const { return bTab2; } +public: + ~SwMultiPortion(); + const SwLineLayout& GetRoot() const { return aRoot; } + SwLineLayout& GetRoot() { return aRoot; } + SwFldPortion* GetFldRest() { return pFldRest; } + void SetFldRest( SwFldPortion* pNew ) { pFldRest = pNew; } + + inline sal_Bool HasTabulator() const { return bTab1 || bTab2; } + inline sal_Bool IsFormatted() const { return bFormatted; } + inline void SetFormatted() { bFormatted = sal_True; } + inline sal_Bool IsFollowFld() const { return bFollowFld; } + inline void SetFollowFld() { bFollowFld = sal_True; } + inline sal_Bool HasFlyInCntnt() const { return bFlyInCntnt; } + inline void SetFlyInCntnt( sal_Bool bNew ) { bFlyInCntnt = bNew; } + inline sal_Bool IsDouble() const { return bDouble; } + inline sal_Bool IsRuby() const { return bRuby; } + inline sal_Bool IsBidi() const { return bBidi; } + inline sal_Bool OnTop() const { return bTop; } + void ActualizeTabulator(); + + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual long CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const; + virtual sal_Bool ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const; + + // Summarize the internal lines to calculate the (external) size + void CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf ); + + inline sal_Bool HasBrackets() const; + inline sal_Bool HasRotation() const { return 0 != (1 & nDirection); } + inline sal_Bool IsRevers() const { return 0 != (2 & nDirection); } + inline sal_uInt8 GetDirection() const { return nDirection; } + inline USHORT GetFontRotation() const + { return ( HasRotation() ? ( IsRevers() ? 2700 : 900 ) : 0 ); } + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +class SwDoubleLinePortion : public SwMultiPortion +{ + SwBracket* pBracket; // Surrounding brackets + SwTwips nLineDiff; // Difference of the width of the both lines + xub_StrLen nBlank1; // Number of blanks in the first line + xub_StrLen nBlank2; // Number of blanks in the second line +public: + SwDoubleLinePortion( SwDoubleLinePortion& rDouble, xub_StrLen nEnd ); + SwDoubleLinePortion( const SwMultiCreator& rCreate, xub_StrLen nEnd ); + ~SwDoubleLinePortion(); + + inline SwBracket* GetBrackets() const { return pBracket; } + void SetBrackets( const SwDoubleLinePortion& rDouble ); + void PaintBracket( SwTxtPaintInfo& rInf, long nSpaceAdd, sal_Bool bOpen ) const; + void FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth ); + inline KSHORT PreWidth() const { return pBracket->nPreWidth; }; + inline KSHORT PostWidth() const { return pBracket->nPostWidth; } + inline void ClearBrackets() + { pBracket->nPreWidth = pBracket->nPostWidth=0; Width( 0 ); } + inline KSHORT BracketWidth(){ return PreWidth() + PostWidth(); } + + void CalcBlanks( SwTxtFormatInfo &rInf ); + static void ResetSpaceAdd( SwLineLayout* pCurr ); + inline SwTwips GetLineDiff() const { return nLineDiff; } + inline xub_StrLen GetSpaceCnt() const + { return ( nLineDiff < 0 ) ? nBlank2 : nBlank1; } + inline xub_StrLen GetSmallerSpaceCnt() const + { return ( nLineDiff < 0 ) ? nBlank1 : nBlank2; } + inline xub_StrLen GetBlank1() const { return nBlank1; } + inline xub_StrLen GetBlank2() const { return nBlank2; } + + virtual long CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const; + virtual sal_Bool ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const; +}; + +class SwRubyPortion : public SwMultiPortion +{ + xub_StrLen nRubyOffset; + USHORT nAdjustment; + void _Adjust( SwTxtFormatInfo &rInf); +public: + SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ); + + SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt, + const IDocumentSettingAccess& rIDocumentSettingAccess, + xub_StrLen nEnd, xub_StrLen nOffs, + const sal_Bool* pForceRubyPos ); + + void CalcRubyOffset(); + inline void Adjust( SwTxtFormatInfo &rInf ) + { if(nAdjustment && GetRoot().GetNext()) _Adjust(rInf); } + inline USHORT GetAdjustment() const { return nAdjustment; } + inline xub_StrLen GetRubyOffset() const { return nRubyOffset; } +}; + +class SwRotatedPortion : public SwMultiPortion +{ +public: + SwRotatedPortion( xub_StrLen nEnd, sal_uInt8 nDir = 1 ) + : SwMultiPortion( nEnd ) { SetDirection( nDir ); } + SwRotatedPortion( const SwMultiCreator& rCreate, xub_StrLen nEnd, + sal_Bool bRTL ); +}; + +class SwBidiPortion : public SwMultiPortion +{ + BYTE nLevel; + +public: + SwBidiPortion( xub_StrLen nEnd, BYTE nLv ); + + inline BYTE GetLevel() const { return nLevel; } + // Get number of blanks for justified alignment + xub_StrLen GetSpaceCnt( const SwTxtSizeInfo &rInf ) const; + // Calculates extra spacing based on number of blanks + virtual long CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const; + // Manipulate the spacing array at pCurr + virtual sal_Bool ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const; +}; + +// For cursor travelling in multiportions + +class SwTxtCursorSave +{ + SwTxtCursor* pTxtCrsr; + SwLineLayout* pCurr; + SwTwips nWidth; + xub_StrLen nStart; + BYTE nOldProp; + sal_Bool bSpaceChg; +public: + SwTxtCursorSave( SwTxtCursor* pTxtCursor, SwMultiPortion* pMulti, + SwTwips nY, USHORT& nX, xub_StrLen nCurrStart, long nSpaceAdd ); + ~SwTxtCursorSave(); +}; + +/************************************************************************* + * inline - Implementations + *************************************************************************/ + +inline sal_Bool SwMultiPortion::HasBrackets() const +{ + return sal::static_int_cast< sal_Bool >( IsDouble() ? + 0 != ((SwDoubleLinePortion*)this)->GetBrackets() : + sal_False ); +} + +CLASSIO( SwMultiPortion ) + +#endif diff --git a/sw/source/core/text/porref.cxx b/sw/source/core/text/porref.cxx new file mode 100644 index 000000000000..67fe6359b5bc --- /dev/null +++ b/sw/source/core/text/porref.cxx @@ -0,0 +1,118 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porref.cxx,v $ + * $Revision: 1.9 $ + * + * 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 <SwPortionHandler.hxx> +#include "viewopt.hxx" // SwViewOptions + +#include "txtcfg.hxx" +#include "porref.hxx" +#include "inftxt.hxx" // GetTxtSize() + +/************************************************************************* + * virtual SwRefPortion::Paint() + *************************************************************************/ + +void SwRefPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + { + rInf.DrawViewOpt( *this, POR_REF ); + SwTxtPortion::Paint( rInf ); + } +} + +/************************************************************************* + * class SwIsoRefPortion + *************************************************************************/ + +SwLinePortion *SwIsoRefPortion::Compress() { return this; } + +SwIsoRefPortion::SwIsoRefPortion() : nViewWidth(0) +{ + SetLen(1); + SetWhichPor( POR_ISOREF ); +} + +/************************************************************************* + * virtual SwIsoRefPortion::GetViewWidth() + *************************************************************************/ + +KSHORT SwIsoRefPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const +{ + // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten + // Moment errechnet werden: + SwIsoRefPortion* pThis = (SwIsoRefPortion*)this; + if( !Width() && rInf.OnWin() && SwViewOption::IsFieldShadings() && + !rInf.GetOpt().IsReadonly() && !rInf.GetOpt().IsPagePreview() ) + { + if( !nViewWidth ) + pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width(); + } + else + pThis->nViewWidth = 0; + return nViewWidth; +} + +/************************************************************************* + * virtual SwIsoRefPortion::Format() + *************************************************************************/ + +sal_Bool SwIsoRefPortion::Format( SwTxtFormatInfo &rInf ) +{ + const sal_Bool bFull = SwLinePortion::Format( rInf ); + return bFull; +} + +/************************************************************************* + * virtual SwIsoRefPortion::Paint() + *************************************************************************/ + +void SwIsoRefPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + rInf.DrawViewOpt( *this, POR_REF ); +} + +/************************************************************************* + * virtual SwIsoRefPortion::HandlePortion() + *************************************************************************/ + +void SwIsoRefPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString; + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + + + diff --git a/sw/source/core/text/porref.hxx b/sw/source/core/text/porref.hxx new file mode 100644 index 000000000000..9ab26bb4cca5 --- /dev/null +++ b/sw/source/core/text/porref.hxx @@ -0,0 +1,76 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porref.hxx,v $ + * $Revision: 1.5 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORREF_HXX +#define _PORREF_HXX + +#include "portxt.hxx" + +/************************************************************************* + * class SwRefPortion + *************************************************************************/ + +class SwRefPortion : public SwTxtPortion +{ +public: + inline SwRefPortion(){ SetWhichPor( POR_REF ); } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwIsoRefPortion + *************************************************************************/ + +class SwIsoRefPortion : public SwRefPortion +{ + KSHORT nViewWidth; + +public: + SwIsoRefPortion(); + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual SwLinePortion *Compress(); + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * inline - Implementations + *************************************************************************/ + +CLASSIO( SwRefPortion ) +CLASSIO( SwIsoRefPortion ) + + +#endif diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx new file mode 100644 index 000000000000..a0b859cf0e12 --- /dev/null +++ b/sw/source/core/text/porrst.cxx @@ -0,0 +1,581 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porrst.cxx,v $ + * $Revision: 1.44 $ + * + * 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 <sfx2/printer.hxx> +#include <svx/lspcitem.hxx> +#include <svx/adjitem.hxx> +#include <svx/escpitem.hxx> +#include <svx/lrspitem.hxx> +#include <svx/pgrditem.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <viewsh.hxx> // ViewShell +#include <viewopt.hxx> +#include <ndtxt.hxx> // SwTxtNode +#include <pagefrm.hxx> // SwPageFrm +#include <paratr.hxx> +#include <SwPortionHandler.hxx> +#include <txtcfg.hxx> +#include <porrst.hxx> +#include <inftxt.hxx> +#include <txtpaint.hxx> // ClipVout +#include <swfntcch.hxx> // SwFontAccess +#include <tgrditem.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <frmatr.hxx> +#include <redlnitr.hxx> // SwRedlineItr +#include <porfly.hxx> // SwFlyPortion +#include <atrhndl.hxx> + +#include <IDocumentRedlineAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentDeviceAccess.hxx> + +/************************************************************************* + * class SwTmpEndPortion + *************************************************************************/ + +SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + SetWhichPor( POR_TMPEND ); +} + +/************************************************************************* + * virtual SwTmpEndPortion::Paint() + *************************************************************************/ + +void SwTmpEndPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( rInf.OnWin() && rInf.GetOpt().IsParagraph() ) + { + SwDefFontSave aSave( rInf ); + const XubString aTmp( CH_PAR ); + rInf.DrawText( aTmp, *this ); + } +} + +/************************************************************************* + * class SwBreakPortion + *************************************************************************/ +SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion ) + : SwLinePortion( rPortion ) +{ + nLineLength = 1; + SetWhichPor( POR_BRK ); +} + +xub_StrLen SwBreakPortion::GetCrsrOfst( const KSHORT ) const +{ return 0; } + +KSHORT SwBreakPortion::GetViewWidth( const SwTxtSizeInfo & ) const +{ return 0; } + +SwLinePortion *SwBreakPortion::Compress() +{ return (GetPortion() && GetPortion()->InTxtGrp() ? 0 : this); } + +void SwBreakPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( rInf.OnWin() && rInf.GetOpt().IsLineBreak() ) + rInf.DrawLineBreak( *this ); +} + +/************************************************************************* + * virtual SwBreakPortion::Format() + *************************************************************************/ + +sal_Bool SwBreakPortion::Format( SwTxtFormatInfo &rInf ) +{ + const SwLinePortion *pRoot = rInf.GetRoot(); + Width( 0 ); + Height( pRoot->Height() ); + SetAscent( pRoot->GetAscent() ); + if ( rInf.GetIdx()+1 == rInf.GetTxt().Len() ) + rInf.SetNewLine( sal_True ); + return sal_True; +} + +/************************************************************************* + * virtual SwBreakPortion::HandlePortion() + *************************************************************************/ + +void SwBreakPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Text( GetLen(), GetWhichPor() ); +} + + +SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn, + sal_Bool bBG, sal_Bool bGK ) : + nKern( nKrn ), bBackground( bBG ), bGridKern( bGK ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + nLineLength = 0; + SetWhichPor( POR_KERN ); + if( nKern > 0 ) + Width( nKern ); + rPortion.Insert( this ); +} + +SwKernPortion::SwKernPortion( const SwLinePortion& rPortion ) : + nKern( 0 ), bBackground( sal_False ), bGridKern( sal_True ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + + nLineLength = 0; + SetWhichPor( POR_KERN ); +} + +void SwKernPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + { + // bBackground is set for Kerning Portions between two fields + if ( bBackground ) + rInf.DrawViewOpt( *this, POR_FLD ); + + rInf.DrawBackBrush( *this ); + + // do we have to repaint a post it portion? + if( rInf.OnWin() && pPortion && !pPortion->Width() ) + pPortion->PrePaint( rInf, this ); + + if( rInf.GetFont()->IsPaintBlank() ) + { + static sal_Char __READONLY_DATA sDoubleSpace[] = " "; + XubString aTxtDouble( sDoubleSpace, RTL_TEXTENCODING_MS_1252 ); + // --> FME 2006-07-12 #b6439097# + SwRect aClipRect; + rInf.CalcRect( *this, &aClipRect, 0 ); + SwSaveClip aClip( (OutputDevice*)rInf.GetOut() ); + aClip.ChgClip( aClipRect, 0 ); + // <-- + rInf.DrawText( aTxtDouble, *this, 0, 2, sal_True ); + } + } +} + +void SwKernPortion::FormatEOL( SwTxtFormatInfo &rInf ) +{ + if ( bGridKern ) + return; + + if( rInf.GetLast() == this ) + rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) ); + if( nKern < 0 ) + Width( -nKern ); + else + Width( 0 ); + rInf.GetLast()->FormatEOL( rInf ); +} + +SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) : + bLeft( sal_True ) +{ + Height( rPortion.Height() ); + SetAscent( rPortion.GetAscent() ); + nLineLength = 0; + SetWhichPor( POR_ARROW ); +} + +SwArrowPortion::SwArrowPortion( const SwTxtPaintInfo &rInf ) + : bLeft( sal_False ) +{ + Height( (USHORT)(rInf.GetTxtFrm()->Prt().Height()) ); + aPos.X() = rInf.GetTxtFrm()->Frm().Left() + + rInf.GetTxtFrm()->Prt().Right(); + aPos.Y() = rInf.GetTxtFrm()->Frm().Top() + + rInf.GetTxtFrm()->Prt().Bottom(); +} + +void SwArrowPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + ((SwArrowPortion*)this)->aPos = rInf.GetPos(); +} + +SwLinePortion *SwArrowPortion::Compress() { return this; } + +SwTwips SwTxtFrm::EmptyHeight() const +{ + ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::EmptyHeight with swapped frame" ); + + SwFont *pFnt; + const SwTxtNode& rTxtNode = *GetTxtNode(); + const IDocumentSettingAccess* pIDSA = rTxtNode.getIDocumentSettingAccess(); + ViewShell *pSh = GetShell(); + if ( rTxtNode.HasSwAttrSet() ) + { + const SwAttrSet *pAttrSet = &( rTxtNode.GetSwAttrSet() ); + pFnt = new SwFont( pAttrSet, pIDSA ); + } + else + { + SwFontAccess aFontAccess( &rTxtNode.GetAnyFmtColl(), pSh); + pFnt = new SwFont( *aFontAccess.Get()->GetFont() ); + pFnt->ChkMagic( pSh, pFnt->GetActual() ); + } + + if ( IsVertical() ) + pFnt->SetVertical( 2700 ); + + OutputDevice* pOut = pSh ? pSh->GetOut() : 0; + if ( !pOut || !pIDSA->get(IDocumentSettingAccess::BROWSE_MODE) || + ( pSh->GetViewOptions()->IsPrtFormat() ) ) + { + pOut = rTxtNode.getIDocumentDeviceAccess()->getReferenceDevice(true); + } + + const IDocumentRedlineAccess* pIDRA = rTxtNode.getIDocumentRedlineAccess(); + if( IDocumentRedlineAccess::IsShowChanges( pIDRA->GetRedlineMode() ) ) + { + MSHORT nRedlPos = pIDRA->GetRedlinePos( rTxtNode, USHRT_MAX ); + if( MSHRT_MAX != nRedlPos ) + { + SwAttrHandler aAttrHandler; + aAttrHandler.Init( GetTxtNode()->GetSwAttrSet(), + *GetTxtNode()->getIDocumentSettingAccess(), NULL ); + SwRedlineItr aRedln( rTxtNode, *pFnt, aAttrHandler, + nRedlPos, sal_True ); + } + } + + SwTwips nRet; + if( !pOut ) + nRet = IsVertical() ? + Prt().SSize().Width() + 1 : + Prt().SSize().Height() + 1; + else + { + pFnt->SetFntChg( sal_True ); + pFnt->ChgPhysFnt( pSh, *pOut ); + nRet = pFnt->GetHeight( pSh, *pOut ); + } + delete pFnt; + return nRet; +} + +/************************************************************************* + * SwTxtFrm::FormatEmpty() + *************************************************************************/ + +sal_Bool SwTxtFrm::FormatEmpty() +{ + ASSERT( ! IsVertical() || ! IsSwapped(),"SwTxtFrm::FormatEmpty with swapped frame" ); + + if ( HasFollow() || GetTxtNode()->GetpSwpHints() || + 0 != GetTxtNode()->GetNumRule() || + GetTxtNode()->HasHiddenCharAttribute( true ) || + IsInFtn() || ( HasPara() && GetPara()->IsPrepMustFit() ) ) + return sal_False; + const SwAttrSet& aSet = GetTxtNode()->GetSwAttrSet(); + const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust(); + if( ( ( ! IsRightToLeft() && ( SVX_ADJUST_LEFT != nAdjust ) ) || + ( IsRightToLeft() && ( SVX_ADJUST_RIGHT != nAdjust ) ) ) || + aSet.GetRegister().GetValue() ) + return sal_False; + const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing(); + if( SVX_LINE_SPACE_MIN == rSpacing.GetLineSpaceRule() || + SVX_LINE_SPACE_FIX == rSpacing.GetLineSpaceRule() || + aSet.GetLRSpace().IsAutoFirst() ) + return sal_False; + else + { + SwTxtFly aTxtFly( this ); + SwRect aRect; + sal_Bool bFirstFlyCheck = 0 != Prt().Height(); + if ( bFirstFlyCheck && + aTxtFly.IsOn() && aTxtFly.IsAnyObj( aRect ) ) + return sal_False; + else + { + SwTwips nHeight = EmptyHeight(); + + if ( GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() && + IsInDocBody() ) + { + GETGRID( FindPageFrm() ) + if ( pGrid ) + nHeight = pGrid->GetBaseHeight() + pGrid->GetRubyHeight(); + } + + SWRECTFN( this ) + const SwTwips nChg = nHeight - (Prt().*fnRect->fnGetHeight)(); + + if( !nChg ) + SetUndersized( sal_False ); + AdjustFrm( nChg ); + + if( HasBlinkPor() ) + { + ClearPara(); + ResetBlinkPor(); + } + SetCacheIdx( MSHRT_MAX ); + if( !IsEmpty() ) + { + SetEmpty( sal_True ); + SetCompletePaint(); + } + if( !bFirstFlyCheck && + aTxtFly.IsOn() && aTxtFly.IsAnyObj( aRect ) ) + return sal_False; + + // --> OD 2004-11-17 #i35635# - call method <HideAndShowObjects()> + // to assure that objects anchored at the empty paragraph are + // correctly visible resp. invisible. + HideAndShowObjects(); + // <-- + return sal_True; + } + } +} + +sal_Bool SwTxtFrm::FillRegister( SwTwips& rRegStart, KSHORT& rRegDiff ) +{ + const SwFrm *pFrm = this; + rRegDiff = 0; + while( !( ( FRM_BODY | FRM_FLY ) + & pFrm->GetType() ) && pFrm->GetUpper() ) + pFrm = pFrm->GetUpper(); + if( ( FRM_BODY| FRM_FLY ) & pFrm->GetType() ) + { + SWRECTFN( pFrm ) + rRegStart = (pFrm->*fnRect->fnGetPrtTop)(); + pFrm = pFrm->FindPageFrm(); + if( pFrm->IsPageFrm() ) + { + SwPageDesc* pDesc = ((SwPageFrm*)pFrm)->FindPageDesc(); + if( pDesc ) + { + rRegDiff = pDesc->GetRegHeight(); + if( !rRegDiff ) + { + const SwTxtFmtColl *pFmt = pDesc->GetRegisterFmtColl(); + if( pFmt ) + { + const SvxLineSpacingItem &rSpace = pFmt->GetLineSpacing(); + if( SVX_LINE_SPACE_FIX == rSpace.GetLineSpaceRule() ) + { + rRegDiff = rSpace.GetLineHeight(); + pDesc->SetRegHeight( rRegDiff ); + pDesc->SetRegAscent( ( 4 * rRegDiff ) / 5 ); + } + else + { + ViewShell *pSh = GetShell(); + SwFontAccess aFontAccess( pFmt, pSh ); + SwFont aFnt( *aFontAccess.Get()->GetFont() ); + + OutputDevice *pOut = 0; + if( !GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::BROWSE_MODE) || + (pSh && pSh->GetViewOptions()->IsPrtFormat()) ) + pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true ); + + if( pSh && !pOut ) + pOut = pSh->GetWin(); + + if( !pOut ) + pOut = GetpApp()->GetDefaultDevice(); + + MapMode aOldMap( pOut->GetMapMode() ); + pOut->SetMapMode( MapMode( MAP_TWIP ) ); + + aFnt.ChgFnt( pSh, *pOut ); + rRegDiff = aFnt.GetHeight( pSh, *pOut ); + KSHORT nNettoHeight = rRegDiff; + + switch( rSpace.GetLineSpaceRule() ) + { + case SVX_LINE_SPACE_AUTO: + break; + case SVX_LINE_SPACE_MIN: + { + if( rRegDiff < KSHORT( rSpace.GetLineHeight() ) ) + rRegDiff = rSpace.GetLineHeight(); + break; + } + default: ASSERT( + sal_False, ": unknown LineSpaceRule" ); + } + switch( rSpace.GetInterLineSpaceRule() ) + { + case SVX_INTER_LINE_SPACE_OFF: + break; + case SVX_INTER_LINE_SPACE_PROP: + { + long nTmp = rSpace.GetPropLineSpace(); + if( nTmp < 50 ) + nTmp = nTmp ? 50 : 100; + nTmp *= rRegDiff; + nTmp /= 100; + if( !nTmp ) + ++nTmp; + rRegDiff = (KSHORT)nTmp; + nNettoHeight = rRegDiff; + break; + } + case SVX_INTER_LINE_SPACE_FIX: + { + rRegDiff = rRegDiff + rSpace.GetInterLineSpace(); + nNettoHeight = rRegDiff; + break; + } + default: ASSERT( sal_False, ": unknown InterLineSpaceRule" ); + } + pDesc->SetRegHeight( rRegDiff ); + pDesc->SetRegAscent( rRegDiff - nNettoHeight + + aFnt.GetAscent( pSh, *pOut ) ); + pOut->SetMapMode( aOldMap ); + } + } + } + const long nTmpDiff = pDesc->GetRegAscent() - rRegDiff; + if ( bVert ) + rRegStart -= nTmpDiff; + else + rRegStart += nTmpDiff; + } + } + } + return ( 0 != rRegDiff ); +} + +/************************************************************************* + * virtual SwHiddenTextPortion::Paint() + *************************************************************************/ + +void SwHiddenTextPortion::Paint( const SwTxtPaintInfo & rInf) const +{ + (void)rInf; +#if OSL_DEBUG_LEVEL > 1 + OutputDevice* pOut = (OutputDevice*)rInf.GetOut(); + Color aCol( SwViewOption::GetFieldShadingsColor() ); + Color aOldColor( pOut->GetFillColor() ); + pOut->SetFillColor( aCol ); + Point aPos( rInf.GetPos() ); + aPos.Y() -= 150; + aPos.X() -= 25; + SwRect aRect( aPos, Size( 100, 200 ) ); + ((OutputDevice*)pOut)->DrawRect( aRect.SVRect() ); + pOut->SetFillColor( aOldColor ); +#endif +} + +/************************************************************************* + * virtual SwHiddenTextPortion::Format() + *************************************************************************/ + +sal_Bool SwHiddenTextPortion::Format( SwTxtFormatInfo &rInf ) +{ + Width( 0 ); + rInf.GetTxtFrm()->HideFootnotes( rInf.GetIdx(), rInf.GetIdx() + GetLen() ); + + return sal_False; +}; + +/************************************************************************* + * virtual SwControlCharPortion::Paint() + *************************************************************************/ + +void SwControlCharPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if ( Width() ) // is only set during prepaint mode + { + rInf.DrawViewOpt( *this, POR_CONTROLCHAR ); + + if ( !rInf.GetOpt().IsPagePreview() && + !rInf.GetOpt().IsReadonly() && + SwViewOption::IsFieldShadings() && + CHAR_ZWNBSP != mcChar ) + { + SwFont aTmpFont( *rInf.GetFont() ); + aTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 ); + const USHORT nProp = 40; + aTmpFont.SetProportion( nProp ); // a smaller font + SwFontSave aFontSave( rInf, &aTmpFont ); + + String aOutString; + + switch ( mcChar ) + { + case CHAR_ZWSP : + aOutString = '/'; break; +// case CHAR_LRM : +// rTxt = sal_Unicode(0x2514); break; +// case CHAR_RLM : +// rTxt = sal_Unicode(0x2518); break; + } + + if ( !mnHalfCharWidth ) + mnHalfCharWidth = rInf.GetTxtSize( aOutString ).Width() / 2; + + Point aOldPos = rInf.GetPos(); + Point aNewPos( aOldPos ); + aNewPos.X() = aNewPos.X() + ( Width() / 2 ) - mnHalfCharWidth; + const_cast< SwTxtPaintInfo& >( rInf ).SetPos( aNewPos ); + + rInf.DrawText( aOutString, *this ); + + const_cast< SwTxtPaintInfo& >( rInf ).SetPos( aOldPos ); + } + } +} + +/************************************************************************* + * virtual SwControlCharPortion::Format() + *************************************************************************/ + +sal_Bool SwControlCharPortion::Format( SwTxtFormatInfo &rInf ) +{ + const SwLinePortion* pRoot = rInf.GetRoot(); + Width( 0 ); + Height( pRoot->Height() ); + SetAscent( pRoot->GetAscent() ); + + return sal_False; +} + +/************************************************************************* + * virtual SwControlCharPortion::GetViewWidth() + *************************************************************************/ + +KSHORT SwControlCharPortion::GetViewWidth( const SwTxtSizeInfo& rInf ) const +{ + if( !mnViewWidth ) + mnViewWidth = rInf.GetTxtSize( ' ' ).Width(); + + return mnViewWidth; +} diff --git a/sw/source/core/text/porrst.hxx b/sw/source/core/text/porrst.hxx new file mode 100644 index 000000000000..ef0a0dc2b02d --- /dev/null +++ b/sw/source/core/text/porrst.hxx @@ -0,0 +1,190 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: porrst.hxx,v $ + * $Revision: 1.19 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORRST_HXX +#define _PORRST_HXX +#include "porlay.hxx" +#include "porexp.hxx" + +#define LINE_BREAK_WIDTH 150 +#define SPECIAL_FONT_HEIGHT 200 + +class SwTxtFormatInfo; + +/************************************************************************* + * class SwTmpEndPortion + *************************************************************************/ + +class SwTmpEndPortion : public SwLinePortion +{ +public: + SwTmpEndPortion( const SwLinePortion &rPortion ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwBreakPortion + *************************************************************************/ + +class SwBreakPortion : public SwLinePortion +{ +public: + SwBreakPortion( const SwLinePortion &rPortion ); + // liefert 0 zurueck, wenn keine Nutzdaten enthalten sind. + virtual SwLinePortion *Compress(); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + virtual xub_StrLen GetCrsrOfst( const MSHORT nOfst ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwKernPortion + *************************************************************************/ + +class SwKernPortion : public SwLinePortion +{ + short nKern; + sal_Bool bBackground; + sal_Bool bGridKern; + +public: + + // This constructor automatically appends the portion to rPortion + // bBG indicates, that the background of the kerning portion has to + // be painted, e.g., if the portion if positioned between to fields. + // bGridKern indicates, that the kerning portion is used to provide + // additional space in grid mode. + SwKernPortion( SwLinePortion &rPortion, short nKrn, + sal_Bool bBG = sal_False, sal_Bool bGridKern = sal_False ); + + // This constructor only sets the height and ascent to the values + // of rPortion. It is only used for kerning portions for grid mode + SwKernPortion( const SwLinePortion &rPortion ); + + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwArrowPortion + *************************************************************************/ + +class SwArrowPortion : public SwLinePortion +{ + Point aPos; + sal_Bool bLeft; +public: + SwArrowPortion( const SwLinePortion &rPortion ); + SwArrowPortion( const SwTxtPaintInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual SwLinePortion *Compress(); + inline sal_Bool IsLeft() const { return bLeft; } + inline const Point& GetPos() const { return aPos; } + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwHangingPortion + * The characters which are forbidden at the start of a line like the dot and + * other punctuation marks are allowed to display in the margin of the page + * by a user option. + * The SwHangingPortion is the corresponding textportion to do that. + *************************************************************************/ + +class SwHangingPortion : public SwTxtPortion +{ + KSHORT nInnerWidth; +public: + inline SwHangingPortion( SwPosSize aSize ) : nInnerWidth( aSize.Width() ) + { SetWhichPor( POR_HNG ); SetLen( 1 ); Height( aSize.Height() ); } + + inline KSHORT GetInnerWidth() const { return nInnerWidth; } +}; + +/************************************************************************* + * class SwHiddenTextPortion + * Is used to hide text + *************************************************************************/ + +class SwHiddenTextPortion : public SwLinePortion +{ +public: + inline SwHiddenTextPortion( xub_StrLen nLen ) + { SetWhichPor( POR_HIDDEN_TXT ); SetLen( nLen ); } + + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); +}; + +/************************************************************************* + * class SwControlCharPortion + *************************************************************************/ + +class SwControlCharPortion : public SwLinePortion +{ + +private: + mutable USHORT mnViewWidth; // used to cache a calculated value + mutable USHORT mnHalfCharWidth; // used to cache a calculated value + sal_Unicode mcChar; + +public: + + inline SwControlCharPortion( sal_Unicode cChar ) + : mnViewWidth( 0 ), mnHalfCharWidth( 0 ), mcChar( cChar ) + { + SetWhichPor( POR_CONTROLCHAR ); SetLen( 1 ); + } + + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual KSHORT GetViewWidth( const SwTxtSizeInfo& rInf ) const; +}; + + + +/************************************************************************* + * inline - Implementations + *************************************************************************/ + +CLASSIO( SwBreakPortion ) +CLASSIO( SwEndPortion ) +CLASSIO( SwKernPortion ) +CLASSIO( SwArrowPortion ) + +#endif diff --git a/sw/source/core/text/portab.hxx b/sw/source/core/text/portab.hxx new file mode 100644 index 000000000000..33bac6e6b06e --- /dev/null +++ b/sw/source/core/text/portab.hxx @@ -0,0 +1,160 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: portab.hxx,v $ + * $Revision: 1.8 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORTAB_HXX +#define _PORTAB_HXX + +#include "porglue.hxx" + +/************************************************************************* + * class SwTabPortion + *************************************************************************/ + +class SwTabPortion : public SwFixPortion +{ + const KSHORT nTabPos; + const xub_Unicode cFill; + + // Das Format() verzweigt entweder in Pre- oder PostFormat() + sal_Bool PreFormat( SwTxtFormatInfo &rInf ); +public: + SwTabPortion( const KSHORT nTabPos, const xub_Unicode cFill = '\0' ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + sal_Bool PostFormat( SwTxtFormatInfo &rInf ); + inline sal_Bool IsFilled() const { return 0 != cFill; } + inline KSHORT GetTabPos() const { return nTabPos; } + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwTabLeftPortion + *************************************************************************/ + +class SwTabLeftPortion : public SwTabPortion +{ +public: + inline SwTabLeftPortion( const KSHORT nTabPosVal, const xub_Unicode cFillChar='\0' ) + : SwTabPortion( nTabPosVal, cFillChar ) + { SetWhichPor( POR_TABLEFT ); } + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwTabRightPortion + *************************************************************************/ + +class SwTabRightPortion : public SwTabPortion +{ +public: + inline SwTabRightPortion( const KSHORT nTabPosVal, const xub_Unicode cFillChar='\0' ) + : SwTabPortion( nTabPosVal, cFillChar ) + { SetWhichPor( POR_TABRIGHT ); } + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwTabCenterPortion + *************************************************************************/ + +class SwTabCenterPortion : public SwTabPortion +{ +public: + inline SwTabCenterPortion( const KSHORT nTabPosVal, const xub_Unicode cFillChar='\0' ) + : SwTabPortion( nTabPosVal, cFillChar ) + { SetWhichPor( POR_TABCENTER ); } + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwTabDecimalPortion + *************************************************************************/ + +class SwTabDecimalPortion : public SwTabPortion +{ + const xub_Unicode mcTab; + + /* + * During text formatting, we already store the width of the portions + * following the tab stop up to the decimal position. This value is + * evaluated during pLastTab->FormatEOL. FME 2006-01-06 #127428#. + */ + USHORT mnWidthOfPortionsUpTpDecimalPosition; + +public: + inline SwTabDecimalPortion( const KSHORT nTabPosVal, const xub_Unicode cTab, + const xub_Unicode cFillChar = '\0' ) + : SwTabPortion( nTabPosVal, cFillChar ), + mcTab(cTab), + mnWidthOfPortionsUpTpDecimalPosition( USHRT_MAX ) + { SetWhichPor( POR_TABDECIMAL ); } + + inline xub_Unicode GetTabDecimal() const { return mcTab; } + + inline void SetWidthOfPortionsUpToDecimalPosition( USHORT nNew ) + { + mnWidthOfPortionsUpTpDecimalPosition = nNew; + } + inline USHORT GetWidthOfPortionsUpToDecimalPosition() const + { + return mnWidthOfPortionsUpTpDecimalPosition; + } + + OUTPUT_OPERATOR +}; + + +/************************************************************************* + * class SwAutoTabDecimalPortion + *************************************************************************/ + +class SwAutoTabDecimalPortion : public SwTabDecimalPortion +{ +public: + inline SwAutoTabDecimalPortion( const KSHORT nTabPosVal, const xub_Unicode cTab, + const xub_Unicode cFillChar = '\0' ) + : SwTabDecimalPortion( nTabPosVal, cTab, cFillChar ) + { SetLen( 0 ); } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; +}; + + +CLASSIO( SwTabPortion ) +CLASSIO( SwTabLeftPortion ) +CLASSIO( SwTabRightPortion ) +CLASSIO( SwTabCenterPortion ) +CLASSIO( SwTabDecimalPortion ) + + +#endif diff --git a/sw/source/core/text/portox.cxx b/sw/source/core/text/portox.cxx new file mode 100644 index 000000000000..434b67f59472 --- /dev/null +++ b/sw/source/core/text/portox.cxx @@ -0,0 +1,118 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: portox.cxx,v $ + * $Revision: 1.9 $ + * + * 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 <SwPortionHandler.hxx> +#include "viewopt.hxx" // SwViewOptions + +#include "txtcfg.hxx" +#include "portox.hxx" +#include "inftxt.hxx" // GetTxtSize() + +/************************************************************************* + * virtual SwToxPortion::Paint() + *************************************************************************/ + +void SwToxPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + { + rInf.DrawViewOpt( *this, POR_TOX ); + SwTxtPortion::Paint( rInf ); + } +} + +/************************************************************************* + * class SwIsoToxPortion + *************************************************************************/ + +SwLinePortion *SwIsoToxPortion::Compress() { return this; } + +SwIsoToxPortion::SwIsoToxPortion() : nViewWidth(0) +{ + SetLen(1); + SetWhichPor( POR_ISOTOX ); +} + +/************************************************************************* + * virtual SwIsoToxPortion::GetViewWidth() + *************************************************************************/ + +KSHORT SwIsoToxPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const +{ + // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten + // Moment errechnet werden: + SwIsoToxPortion* pThis = (SwIsoToxPortion*)this; + // nViewWidth muss errechnet werden. + if( !Width() && rInf.OnWin() && + !rInf.GetOpt().IsPagePreview() && + !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() ) + { + if( !nViewWidth ) + pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width(); + } + else + pThis->nViewWidth = 0; + return nViewWidth; +} + +/************************************************************************* + * virtual SwIsoToxPortion::Format() + *************************************************************************/ + +sal_Bool SwIsoToxPortion::Format( SwTxtFormatInfo &rInf ) +{ + const sal_Bool bFull = SwLinePortion::Format( rInf ); + return bFull; +} + +/************************************************************************* + * virtual SwIsoToxPortion::Paint() + *************************************************************************/ + +void SwIsoToxPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + rInf.DrawViewOpt( *this, POR_TOX ); +} + +/************************************************************************* + * virtual SwIsoToxPortion::HandlePortion() + *************************************************************************/ + +void SwIsoToxPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString; + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + diff --git a/sw/source/core/text/portox.hxx b/sw/source/core/text/portox.hxx new file mode 100644 index 000000000000..cfd758a1c232 --- /dev/null +++ b/sw/source/core/text/portox.hxx @@ -0,0 +1,78 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: portox.hxx,v $ + * $Revision: 1.5 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _PORTOX_HXX +#define _PORTOX_HXX + +#include "portxt.hxx" +//#include "porglue.hxx" + +/************************************************************************* + * class SwToxPortion + *************************************************************************/ + +class SwToxPortion : public SwTxtPortion +{ +public: + inline SwToxPortion(){ SetWhichPor( POR_TOX ); } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + OUTPUT_OPERATOR +}; + +/************************************************************************* + * class SwIsoToxPortion + *************************************************************************/ + +class SwIsoToxPortion : public SwToxPortion +{ + KSHORT nViewWidth; + +public: + SwIsoToxPortion(); + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual SwLinePortion *Compress(); + virtual KSHORT GetViewWidth( const SwTxtSizeInfo &rInf ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR +}; + +/************************************************************************* + * inline - Implementations + *************************************************************************/ + +CLASSIO( SwToxPortion ) +CLASSIO( SwIsoToxPortion ) + + +#endif diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx new file mode 100644 index 000000000000..eec34ffe5b66 --- /dev/null +++ b/sw/source/core/text/portxt.cxx @@ -0,0 +1,819 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: portxt.cxx,v $ + * $Revision: 1.51.112.3 $ + * + * 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 <ctype.h> + +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <hintids.hxx> // CH_TXTATR +#include <errhdl.hxx> // ASSERT +#include <SwPortionHandler.hxx> +#include <txtcfg.hxx> +#include <porlay.hxx> +#include <inftxt.hxx> +#include <guess.hxx> // SwTxtGuess, Zeilenumbruch +#include <porglue.hxx> +#include <portab.hxx> // pLastTab-> +#include <porfld.hxx> // SwFldPortion +#include <wrong.hxx> +#include <viewsh.hxx> +#include <IDocumentSettingAccess.hxx> +#include <viewopt.hxx> // SwViewOptions + +#include <IMark.hxx> +#include <pam.hxx> +#include <doc.hxx> + +#if OSL_DEBUG_LEVEL > 1 +const sal_Char *GetLangName( const MSHORT nLang ); +#endif + +using namespace ::com::sun::star; +using namespace ::com::sun::star::i18n::ScriptType; + +/************************************************************************* + * lcl_AddSpace + * Returns for how many characters an extra space has to be added + * (for justified alignment). + *************************************************************************/ + +USHORT lcl_AddSpace( const SwTxtSizeInfo &rInf, const XubString* pStr, + const SwLinePortion& rPor ) +{ + xub_StrLen nPos, nEnd; + const SwScriptInfo* pSI = 0; + + if ( pStr ) + { + // passing a string means we are inside a field + nPos = 0; + nEnd = pStr->Len(); + } + else + { + nPos = rInf.GetIdx(); + nEnd = rInf.GetIdx() + rPor.GetLen(); + pStr = &rInf.GetTxt(); + pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo(); + } + + USHORT nCnt = 0; + BYTE nScript = 0; + + // If portion consists of Asian characters and language is not + // Korean, we add extra space to each character. + // first we get the script type + if ( pSI ) + nScript = pSI->ScriptType( nPos ); + else if ( pBreakIt->GetBreakIter().is() ) + nScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos ); + + // Note: rInf.GetIdx() can differ from nPos, + // e.g., when rPor is a field portion. nPos referes to the string passed + // to the function, rInf.GetIdx() referes to the original string. + + // We try to find out which justification mode is required. This is done by + // evaluating the script type and the language attribute set for this portion + + // Asian Justification: Each character get some extra space + if ( nEnd > nPos && ASIAN == nScript ) + { + LanguageType aLang = + rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript ); + + if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) + { + const SwLinePortion* pPor = rPor.GetPortion(); + if ( pPor && ( pPor->IsKernPortion() || + pPor->IsControlCharPortion() || + pPor->IsPostItsPortion() ) ) + pPor = pPor->GetPortion(); + + nCnt += nEnd - nPos; + + if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() || + pPor->IsBreakPortion() ) + --nCnt; + + return nCnt; + } + } + + // Kashida Justification: Insert Kashidas + if ( nEnd > nPos && pSI && COMPLEX == nScript ) + { + if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() ) + { + const USHORT nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos ); + // i60591: need to check result of KashidaJustify + // determine if kashida justification is applicable + if( nKashRes != STRING_LEN ) + return nKashRes; + } + } + + // Thai Justification: Each character cell gets some extra space + if ( nEnd > nPos && COMPLEX == nScript ) + { + LanguageType aLang = + rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript ); + + if ( LANGUAGE_THAI == aLang ) + { + nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos ); + + const SwLinePortion* pPor = rPor.GetPortion(); + if ( pPor && ( pPor->IsKernPortion() || + pPor->IsControlCharPortion() || + pPor->IsPostItsPortion() ) ) + pPor = pPor->GetPortion(); + + if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) ) + --nCnt; + + return nCnt; + } + } + + // Here starts the good old "Look for blanks and add space to them" part. + // Note: We do not want to add space to an isolated latin blank in front + // of some complex characters in RTL environment + const sal_Bool bDoNotAddSpace = + LATIN == nScript && ( nEnd == nPos + 1 ) && pSI && + ( i18n::ScriptType::COMPLEX == + pSI->ScriptType( nPos + 1 ) ) && + rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft(); + + if ( bDoNotAddSpace ) + return nCnt; + + for ( ; nPos < nEnd; ++nPos ) + { + if( CH_BLANK == pStr->GetChar( nPos ) ) + ++nCnt; + } + + // We still have to examine the next character: + // If the next character is ASIAN and not KOREAN we have + // to add an extra space + // nPos referes to the original string, even if a field string has + // been passed to this function + nPos = rInf.GetIdx() + rPor.GetLen(); + if ( nPos < rInf.GetTxt().Len() ) + { + BYTE nNextScript = 0; + const SwLinePortion* pPor = rPor.GetPortion(); + if ( pPor && pPor->IsKernPortion() ) + pPor = pPor->GetPortion(); + + if ( ! pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() ) + return nCnt; + + // next character is inside a field? + if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() ) + { + sal_Bool bOldOnWin = rInf.OnWin(); + ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False ); + + XubString aStr( aEmptyStr ); + pPor->GetExpTxt( rInf, aStr ); + ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin ); + + nNextScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( aStr, 0 ); + } + else + nNextScript = (BYTE)pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos ); + + if( ASIAN == nNextScript ) + { + LanguageType aLang = + rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript ); + + if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) + ++nCnt; + } + } + + return nCnt; +} + +/************************************************************************* + * class SwTxtPortion + *************************************************************************/ + +SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion ) + : SwLinePortion( rPortion ) +{ + SetWhichPor( POR_TXT ); +} + +/************************************************************************* + * SwTxtPortion::BreakCut() + *************************************************************************/ + +void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess ) +{ + // Das Wort/Zeichen ist groesser als die Zeile + // Sonderfall Nr.1: Das Wort ist groesser als die Zeile + // Wir kappen... + const KSHORT nLineWidth = (KSHORT)(rInf.Width() - rInf.X()); + xub_StrLen nLen = rGuess.CutPos() - rInf.GetIdx(); + if( nLen ) + { + // special case: guess does not always provide the correct + // width, only in common cases. + if ( !rGuess.BreakWidth() ) + { + rInf.SetLen( nLen ); + SetLen( nLen ); + CalcTxtSize( rInf ); + + // changing these values requires also changing them in + // guess.cxx + KSHORT nItalic = 0; + if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() ) + { + nItalic = Height() / 12; + } + Width( Width() + nItalic ); + } + else + { + Width( rGuess.BreakWidth() ); + SetLen( nLen ); + } + } + // special case: first character does not fit to line + else if ( rGuess.CutPos() == rInf.GetLineStart() ) + { + SetLen( 1 ); + Width( nLineWidth ); + } + else + { + SetLen( 0 ); + Width( 0 ); + } +} + +/************************************************************************* + * SwTxtPortion::BreakUnderflow() + *************************************************************************/ + +void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf ) +{ + Truncate(); + Height( 0 ); + Width( 0 ); + SetLen( 0 ); + SetAscent( 0 ); + rInf.SetUnderFlow( this ); +} + + /************************************************************************* + * SwTxtPortion::_Format() + *************************************************************************/ + +sal_Bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf ) +{ + String aTxt; + return rFld.GetExpTxt( rInf, aTxt ) && aTxt.Len(); +} + +sal_Bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf ) +{ + // 5744: wenn nur der Trennstrich nicht mehr passt, + // muss trotzdem das Wort umgebrochen werden, ansonsten return sal_True! + if( rInf.IsUnderFlow() && rInf.GetSoftHyphPos() ) + { + // soft hyphen portion has triggered an underflow event because + // of an alternative spelling position + sal_Bool bFull = sal_False; + const sal_Bool bHyph = rInf.ChgHyph( sal_True ); + if( rInf.IsHyphenate() ) + { + SwTxtGuess aGuess; + // check for alternative spelling left from the soft hyphen + // this should usually be true but + aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 ); + bFull = CreateHyphen( rInf, aGuess ); + ASSERT( bFull, "Problem with hyphenation!!!" ); + } + rInf.ChgHyph( bHyph ); + rInf.SetSoftHyphPos( 0 ); + return bFull; + } + + SwTxtGuess aGuess; + const sal_Bool bFull = !aGuess.Guess( *this, rInf, Height() ); + + // these are the possible cases: + // A Portion fits to current line + // B Portion does not fit to current line but a possible line break + // within the portion has been found by the break iterator, 2 subcases + // B1 break is hyphen + // B2 break is word end + // C Portion does not fit to current line and no possible line break + // has been found by break iterator, 2 subcases: + // C1 break iterator found a possible line break in portion before us + // ==> this break is used (underflow) + // C2 break iterator does not found a possible line break at all: + // ==> line break + + // case A: line not yet full + if ( !bFull ) + { + Width( aGuess.BreakWidth() ); + // Vorsicht ! + if( !InExpGrp() || InFldGrp() ) + SetLen( rInf.GetLen() ); + + short nKern = rInf.GetFont()->CheckKerning(); + if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern ) + { + nKern = (short)(rInf.Width() - rInf.X() - Width() - 1); + if( nKern < 0 ) + nKern = 0; + } + if( nKern ) + new SwKernPortion( *this, nKern ); + } + // special case: hanging portion + else if( bFull && aGuess.GetHangingPortion() ) + { + Width( aGuess.BreakWidth() ); + SetLen( aGuess.BreakPos() - rInf.GetIdx() ); + Insert( aGuess.GetHangingPortion() ); + aGuess.GetHangingPortion()->SetAscent( GetAscent() ); + aGuess.ClearHangingPortion(); + } + // breakPos >= index + else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != STRING_LEN ) + { + // case B1 + if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart() + && ( aGuess.BreakPos() > rInf.GetIdx() || + ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) ) + { + CreateHyphen( rInf, aGuess ); + if ( rInf.GetFly() ) + rInf.GetRoot()->SetMidHyph( sal_True ); + else + rInf.GetRoot()->SetEndHyph( sal_True ); + } + // case C1 + // - Footnote portions with fake line start (i.e., not at beginning of line) + // should keep together with the text portion. + // - TabPortions not at beginning of line should keep together with the + // text portion, if they are not followed by a blank + // (work around different definition of tab stop character - breaking or + // non breaking character - in compatibility mode) + else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() ) || + ( rInf.GetLast() && + rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) && + rInf.GetLast()->InTabGrp() && + rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() && + aGuess.BreakPos() == rInf.GetIdx() && + CH_BLANK != rInf.GetChar( rInf.GetIdx() ) && + 0x3000 != rInf.GetChar( rInf.GetIdx() ) ) ) + BreakUnderflow( rInf ); + // case B2 + else if( rInf.GetIdx() > rInf.GetLineStart() || + aGuess.BreakPos() > rInf.GetIdx() || + // this is weird: during formatting the follow of a field + // the values rInf.GetIdx and rInf.GetLineStart are replaced + // IsFakeLineStart indicates GetIdx > GetLineStart + rInf.IsFakeLineStart() || + rInf.GetFly() || + rInf.IsFirstMulti() || + ( rInf.GetLast() && + ( rInf.GetLast()->IsFlyPortion() || + ( rInf.GetLast()->InFldGrp() && + ! rInf.GetLast()->InNumberGrp() && + ! rInf.GetLast()->IsErgoSumPortion() && + lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) ) + { + if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() ) + Width( aGuess.BreakWidth() ); + else + // this actually should not happen + Width( KSHORT(rInf.Width() - rInf.X()) ); + + SetLen( aGuess.BreakPos() - rInf.GetIdx() ); + + ASSERT( aGuess.BreakStart() >= aGuess.FieldDiff(), + "Trouble with expanded field portions during line break" ); + const xub_StrLen nRealStart = aGuess.BreakStart() - aGuess.FieldDiff(); + if( aGuess.BreakPos() < nRealStart && !InExpGrp() ) + { + SwHolePortion *pNew = new SwHolePortion( *this ); + pNew->SetLen( nRealStart - aGuess.BreakPos() ); + Insert( pNew ); + } + } + else // case C2, last exit + BreakCut( rInf, aGuess ); + } + // breakPos < index or no breakpos at all + else + { + sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx(); + if( aGuess.BreakPos() != STRING_LEN && + aGuess.BreakPos() != rInf.GetLineStart() && + ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() || + rInf.IsFirstMulti() ) && + ( !rInf.GetLast()->IsBlankPortion() || ((SwBlankPortion*) + rInf.GetLast())->MayUnderFlow( rInf, rInf.GetIdx()-1, sal_True ))) + { // case C1 (former BreakUnderflow()) + BreakUnderflow( rInf ); + } + else + // case C2, last exit + BreakCut( rInf, aGuess ); + } + + return bFull; +} + +/************************************************************************* + * virtual SwTxtPortion::Format() + *************************************************************************/ + + + +sal_Bool SwTxtPortion::Format( SwTxtFormatInfo &rInf ) +{ +#if OSL_DEBUG_LEVEL > 1 + const XubString aDbgTxt( rInf.GetTxt().Copy( rInf.GetIdx(), rInf.GetLen() ) ); +#endif + + if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) ) + { + Height( 0 ); + Width( 0 ); + SetLen( 0 ); + SetAscent( 0 ); + SetPortion( NULL ); // ???? + return sal_True; + } + + ASSERT( rInf.RealWidth() || (rInf.X() == rInf.Width()), + "SwTxtPortion::Format: missing real width" ); + ASSERT( Height(), "SwTxtPortion::Format: missing height" ); + + return _Format( rInf ); +} + +/************************************************************************* + * virtual SwTxtPortion::FormatEOL() + *************************************************************************/ + +// Format end of line +// 5083: Es kann schon manchmal unguenstige Faelle geben... +// "vom {Nikolaus}", Nikolaus bricht um "vom " wird im Blocksatz +// zu "vom" und " ", wobei der Glue expandiert wird, statt in die +// MarginPortion aufzugehen. +// rInf.nIdx steht auf dem naechsten Wort, nIdx-1 ist der letzte +// Buchstabe der Portion. + + + +void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf ) +{ + if( ( !GetPortion() || ( GetPortion()->IsKernPortion() && + !GetPortion()->GetPortion() ) ) && GetLen() && + rInf.GetIdx() < rInf.GetTxt().Len() && + 1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 ) + && !rInf.GetLast()->IsHolePortion() ) + { + // calculate number of blanks + xub_StrLen nX = rInf.GetIdx() - 1; + USHORT nHoleLen = 1; + while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) ) + nHoleLen++; + + // Erst uns einstellen und dann Inserten, weil wir ja auch ein + // SwLineLayout sein koennten. + KSHORT nBlankSize; + if( nHoleLen == GetLen() ) + nBlankSize = Width(); + else + nBlankSize = nHoleLen * rInf.GetTxtSize( ' ' ).Width(); + Width( Width() - nBlankSize ); + rInf.X( rInf.X() - nBlankSize ); + SetLen( GetLen() - nHoleLen ); + SwLinePortion *pHole = new SwHolePortion( *this ); + ( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize ); + ( (SwHolePortion *)pHole )->SetLen( nHoleLen ); + Insert( pHole ); + } +} + +/************************************************************************* + * virtual SwTxtPortion::GetCrsrOfst() + *************************************************************************/ + + + +xub_StrLen SwTxtPortion::GetCrsrOfst( const KSHORT nOfst ) const +{ + ASSERT( !this, "SwTxtPortion::GetCrsrOfst: don't use this method!" ); + return SwLinePortion::GetCrsrOfst( nOfst ); +} + +/************************************************************************* + * virtual SwTxtPortion::GetTxtSize() + *************************************************************************/ +// Das GetTxtSize() geht davon aus, dass die eigene Laenge korrekt ist + +SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const +{ + return rInf.GetTxtSize(); +} + +/************************************************************************* + * virtual SwTxtPortion::Paint() + *************************************************************************/ + + + +void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt().GetChar(rInf.GetIdx())) + { + rInf.DrawBackBrush( *this ); + const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND); + rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false ); + } + else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt().GetChar(rInf.GetIdx())) + { + rInf.DrawBackBrush( *this ); + const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART); + rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false ); + } + else if( GetLen() ) + { + rInf.DrawBackBrush( *this ); + + // do we have to repaint a post it portion? + if( rInf.OnWin() && pPortion && !pPortion->Width() ) + pPortion->PrePaint( rInf, this ); + + const SwWrongList *pWrongList = rInf.GetpWrongList(); + const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList(); + // SMARTTAGS + const SwWrongList *pSmarttags = rInf.GetSmartTags(); + + const bool bWrong = 0 != pWrongList; + const bool bGrammarCheck = 0 != pGrammarCheckList; + const bool bSmartTags = 0 != pSmarttags; + + if ( bWrong || bSmartTags || bGrammarCheck ) + rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, bWrong, bSmartTags, bGrammarCheck ); + else + rInf.DrawText( *this, rInf.GetLen(), sal_False ); + } +} + +/************************************************************************* + * virtual SwTxtPortion::GetExpTxt() + *************************************************************************/ + + + +sal_Bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const +{ + return sal_False; +} + +/************************************************************************* + * xub_StrLen SwTxtPortion::GetSpaceCnt() + * long SwTxtPortion::CalcSpacing() + * sind fuer den Blocksatz zustaendig und ermitteln die Anzahl der Blanks + * und den daraus resultierenden zusaetzlichen Zwischenraum + *************************************************************************/ + +xub_StrLen SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf, + xub_StrLen& rCharCnt ) const +{ + xub_StrLen nCnt = 0; + xub_StrLen nPos = 0; + if ( InExpGrp() ) + { + if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() ) + { + // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank + // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen + sal_Bool bOldOnWin = rInf.OnWin(); + ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False ); + + XubString aStr( aEmptyStr ); + GetExpTxt( rInf, aStr ); + ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin ); + + nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this ); + nPos = aStr.Len(); + } + } + else if( !IsDropPortion() ) + { + nCnt = nCnt + lcl_AddSpace( rInf, 0, *this ); + nPos = GetLen(); + } + rCharCnt = rCharCnt + nPos; + return nCnt; +} + +long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const +{ + xub_StrLen nCnt = 0; + + if ( InExpGrp() ) + { + if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() ) + { + // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank + // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen + sal_Bool bOldOnWin = rInf.OnWin(); + ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False ); + + XubString aStr( aEmptyStr ); + GetExpTxt( rInf, aStr ); + ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin ); + if( nSpaceAdd > 0 ) + nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this ); + else + { + nSpaceAdd = -nSpaceAdd; + nCnt = aStr.Len(); + } + } + } + else if( !IsDropPortion() ) + { + if( nSpaceAdd > 0 ) + nCnt = nCnt + lcl_AddSpace( rInf, 0, *this ); + else + { + nSpaceAdd = -nSpaceAdd; + nCnt = GetLen(); + SwLinePortion* pPor = GetPortion(); + + // we do not want an extra space in front of margin portions + if ( nCnt ) + { + while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() ) + pPor = pPor->GetPortion(); + + if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() ) + --nCnt; + } + } + } + + return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR; +} + +/************************************************************************* + * virtual SwTxtPortion::HandlePortion() + *************************************************************************/ + +void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Text( GetLen(), GetWhichPor() ); +} + +/************************************************************************* + * class SwHolePortion + *************************************************************************/ + + + +SwHolePortion::SwHolePortion( const SwTxtPortion &rPor ) + : nBlankWidth( 0 ) +{ + SetLen( 1 ); + Height( rPor.Height() ); + SetAscent( rPor.GetAscent() ); + SetWhichPor( POR_HOLE ); +} + +SwLinePortion *SwHolePortion::Compress() { return this; } + +/************************************************************************* + * virtual SwHolePortion::Paint() + *************************************************************************/ + + + +void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + // --> FME 2004-06-24 #i16816# tagged pdf support + if( rInf.GetVsh() && rInf.GetVsh()->GetViewOptions()->IsPDFExport() ) + { + const XubString aTxt( ' ' ); + rInf.DrawText( aTxt, *this, 0, 1, false ); + } + // <-- +} + +/************************************************************************* + * virtual SwHolePortion::Format() + *************************************************************************/ + + + +sal_Bool SwHolePortion::Format( SwTxtFormatInfo &rInf ) +{ + return rInf.IsFull() || rInf.X() >= rInf.Width(); +} + +/************************************************************************* + * virtual SwHolePortion::HandlePortion() + *************************************************************************/ + +void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Text( GetLen(), GetWhichPor() ); +} + +void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & rInf) const +{ + SwTxtPortion::Paint(rInf); +} + +sal_Bool SwFieldMarkPortion::Format( SwTxtFormatInfo & ) +{ + sal_Bool ret=0; + Width(0); + return ret; +} + + +//FIXME Fieldbk +//void SwFieldFormPortion::Paint( const SwTxtPaintInfo& rInf ) const +void SwFieldFormPortion::Paint( const SwTxtPaintInfo& ) const +{ +// SwTxtNode *pNd=const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode()); +// const SwDoc *doc=pNd->GetDoc(); +// SwIndex aIndex( pNd, rInf.GetIdx() ); +// SwPosition aPosition(*pNd, aIndex); +// pMark = dynamic_cast< doc->getFieldmarkFor(aPosition); +// OSL_ENSURE(pMark, +// "SwFieldFormPortion::Paint(..)" +// " - Where is my form field bookmark???"); + +// bool checked=(pBM!=NULL?pBM->IsChecked():false); +// rInf.DrawCheckBox(*this , checked); +} + +sal_Bool SwFieldFormPortion::Format( SwTxtFormatInfo &rInf ) +{ + sal_Bool ret=0; +// ret=SwTxtPortion::Format(rInf); + + Width(rInf.GetTxtHeight()); + Height(rInf.GetTxtHeight()); + SetAscent(rInf.GetAscent()); + //int h=rInf.GetTxtHeight(); + +/* + Height(100); + SetAscent(100); +*/ + return ret; +} + + diff --git a/sw/source/core/text/portxt.hxx b/sw/source/core/text/portxt.hxx new file mode 100644 index 000000000000..bbb682aa5faf --- /dev/null +++ b/sw/source/core/text/portxt.hxx @@ -0,0 +1,120 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: portxt.hxx,v $ + * $Revision: 1.11 $ + * + * 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. + * + ************************************************************************/ +#ifndef _PORTXT_HXX +#define _PORTXT_HXX +#ifdef GCC +#include <sys/types.h> +#else +#include <new.h> //fuer size_t, FIXEDMEM aus tools +#endif +#include <tools/mempool.hxx> + +#include "porlin.hxx" + +class SwTxtGuess; + +/************************************************************************* + * class SwTxtPortion + *************************************************************************/ + +class SwTxtPortion : public SwLinePortion +{ + void BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess ); + void BreakUnderflow( SwTxtFormatInfo &rInf ); + sal_Bool _Format( SwTxtFormatInfo &rInf ); + +public: + inline SwTxtPortion(){ SetWhichPor( POR_TXT ); } + SwTxtPortion( const SwLinePortion &rPortion ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void FormatEOL( SwTxtFormatInfo &rInf ); + virtual xub_StrLen GetCrsrOfst( const KSHORT nOfst ) const; + virtual SwPosSize GetTxtSize( const SwTxtSizeInfo &rInfo ) const; + virtual sal_Bool GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const; + virtual long CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const; + + // zaehlt die Spaces fuer Blocksatz + xub_StrLen GetSpaceCnt( const SwTxtSizeInfo &rInf, xub_StrLen& rCnt ) const; + + sal_Bool CreateHyphen( SwTxtFormatInfo &rInf, SwTxtGuess &rGuess ); + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR + DECL_FIXEDMEMPOOL_NEWDEL(SwTxtPortion) +}; + +/************************************************************************* + * class SwHolePortion + *************************************************************************/ + +class SwHolePortion : public SwLinePortion +{ + KSHORT nBlankWidth; +public: + SwHolePortion( const SwTxtPortion &rPor ); + inline KSHORT GetBlankWidth( ) const { return nBlankWidth; } + inline void SetBlankWidth( const KSHORT nNew ) { nBlankWidth = nNew; } + virtual SwLinePortion *Compress(); + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + + // Accessibility: pass information about this portion to the PortionHandler + virtual void HandlePortion( SwPortionHandler& rPH ) const; + + OUTPUT_OPERATOR + DECL_FIXEDMEMPOOL_NEWDEL(SwHolePortion) +}; + +class SwFieldMarkPortion : public SwTxtPortion +{ + public: + inline SwFieldMarkPortion() : SwTxtPortion() + { } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); +}; + +class SwFieldFormPortion : public SwTxtPortion +{ + public: + inline SwFieldFormPortion() : SwTxtPortion() + { } + virtual void Paint( const SwTxtPaintInfo &rInf ) const; + virtual sal_Bool Format( SwTxtFormatInfo &rInf ); +}; + + +CLASSIO( SwTxtPortion ) +CLASSIO( SwHolePortion ) + +#endif diff --git a/sw/source/core/text/possiz.hxx b/sw/source/core/text/possiz.hxx new file mode 100644 index 000000000000..ba4001e66f07 --- /dev/null +++ b/sw/source/core/text/possiz.hxx @@ -0,0 +1,87 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: possiz.hxx,v $ + * $Revision: 1.5 $ + * + * 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. + * + ************************************************************************/ +#ifndef _POSSIZ_HXX +#define _POSSIZ_HXX + + +#include <tools/gen.hxx> +#include "txttypes.hxx" + +// Im Gegensazt zu den SV-Sizes ist die SwPosSize immer positiv +class SwPosSize +{ + KSHORT nWidth; + KSHORT nHeight; +public: + inline SwPosSize( const KSHORT nW = 0, const KSHORT nH = 0 ) + : nWidth(nW), nHeight(nH) { } + inline SwPosSize( const Size &rSize ) + : nWidth(KSHORT(rSize.Width())), nHeight(KSHORT(rSize.Height())){ } + inline KSHORT Height() const { return nHeight; } + inline void Height( const KSHORT nNew ) { nHeight = nNew; } + inline KSHORT Width() const { return nWidth; } + inline void Width( const KSHORT nNew ) { nWidth = nNew; } + + inline Size SvLSize() const { return Size( nWidth, nHeight ); } + inline void SvLSize( const Size &rSize ); + inline void SvXSize( const Size &rSize ); + inline SwPosSize &operator=( const SwPosSize &rSize ); + inline SwPosSize &operator=( const Size &rSize ); +}; + +inline SwPosSize &SwPosSize::operator=(const SwPosSize &rSize ) +{ + nWidth = rSize.Width(); + nHeight = rSize.Height(); + return *this; +} + +inline void SwPosSize::SvLSize( const Size &rSize ) +{ + nWidth = KSHORT(rSize.Width()); + nHeight = KSHORT(rSize.Height()); +} + +inline void SwPosSize::SvXSize( const Size &rSize ) +{ + nHeight = KSHORT(rSize.Width()); + nWidth = KSHORT(rSize.Height()); +} + +inline SwPosSize &SwPosSize::operator=( const Size &rSize ) +{ + nWidth = KSHORT(rSize.Width()); + nHeight = KSHORT(rSize.Height()); + return *this; +} + + +#endif + diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx new file mode 100644 index 000000000000..3ecc266865ed --- /dev/null +++ b/sw/source/core/text/redlnitr.cxx @@ -0,0 +1,508 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: redlnitr.cxx,v $ + * $Revision: 1.42 $ + * + * 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 <svtools/whiter.hxx> +#include <tools/shl.hxx> +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <swmodule.hxx> +#include <redline.hxx> // SwRedline +#include <txtatr.hxx> // SwTxt ... +#include <docary.hxx> // SwRedlineTbl +#include <itratr.hxx> // SwAttrIter +#include <ndtxt.hxx> // SwTxtNode +#include <doc.hxx> // SwDoc +#include <rootfrm.hxx> +#include <breakit.hxx> +#include <vcl/keycodes.hxx> +#include <vcl/cmdevt.hxx> +#include <vcl/settings.hxx> +#include <txtfrm.hxx> // SwTxtFrm +#ifndef _APP_HXX //autogen +#include <vcl/svapp.hxx> +#endif +#include <redlnitr.hxx> +#include <extinput.hxx> +#include <sfx2/printer.hxx> +#include <vcl/window.hxx> + +using namespace ::com::sun::star; + +/************************************************************************* + * SwAttrIter::CtorInitAttrIter() + *************************************************************************/ +void SwAttrIter::CtorInitAttrIter( SwTxtNode& rTxtNode, SwScriptInfo& rScrInf, SwTxtFrm* pFrm ) +{ + // Beim HTML-Import kann es vorkommen, dass kein Layout existiert. + SwRootFrm* pRootFrm = rTxtNode.getIDocumentLayoutAccess()->GetRootFrm(); + pShell = pRootFrm ? pRootFrm->GetShell() : 0; + + pScriptInfo = &rScrInf; + + // attributes set at the whole paragraph + pAttrSet = rTxtNode.GetpSwAttrSet(); + // attribute array + pHints = rTxtNode.GetpSwpHints(); + + // Build a font matching the default paragraph style: + SwFontAccess aFontAccess( &rTxtNode.GetAnyFmtColl(), pShell ); + delete pFnt; + pFnt = new SwFont( *aFontAccess.Get()->GetFont() ); + + // set font to vertical if frame layout is vertical + sal_Bool bVertLayout = sal_False; + sal_Bool bRTL = sal_False; + if ( pFrm ) + { + if ( pFrm->IsVertical() ) + { + bVertLayout = sal_True; + pFnt->SetVertical( pFnt->GetOrientation(), sal_True ); + } + bRTL = pFrm->IsRightToLeft(); + } + + // Initialize the default attribute of the attribute handler + // based on the attribute array cached together with the font. + // If any further attributes for the paragraph are given in pAttrSet + // consider them during construction of the default array, and apply + // them to the font + aAttrHandler.Init( aFontAccess.Get()->GetDefault(), pAttrSet, + *rTxtNode.getIDocumentSettingAccess(), pShell, *pFnt, bVertLayout ); + + aMagicNo[SW_LATIN] = aMagicNo[SW_CJK] = aMagicNo[SW_CTL] = NULL; + + // determine script changes if not already done for current paragraph + ASSERT( pScriptInfo, "No script info available"); + if ( pScriptInfo->GetInvalidity() != STRING_LEN ) + pScriptInfo->InitScriptInfo( rTxtNode, bRTL ); + + if ( pBreakIt->GetBreakIter().is() ) + { + pFnt->SetActual( SwScriptInfo::WhichFont( 0, 0, pScriptInfo ) ); + + xub_StrLen nChg = 0; + USHORT nCnt = 0; + + do + { + nChg = pScriptInfo->GetScriptChg( nCnt ); + USHORT nScript = pScriptInfo->GetScriptType( nCnt++ ); + BYTE nTmp = 4; + switch ( nScript ) { + case i18n::ScriptType::ASIAN : + if( !aMagicNo[SW_CJK] ) nTmp = SW_CJK; break; + case i18n::ScriptType::COMPLEX : + if( !aMagicNo[SW_CTL] ) nTmp = SW_CTL; break; + default: + if( !aMagicNo[SW_LATIN ] ) nTmp = SW_LATIN; + } + if( nTmp < 4 ) + { + pFnt->ChkMagic( pShell, nTmp ); + pFnt->GetMagic( aMagicNo[ nTmp ], aFntIdx[ nTmp ], nTmp ); + } + } while( nChg < rTxtNode.GetTxt().Len() ); + } + else + { + pFnt->ChkMagic( pShell, SW_LATIN ); + pFnt->GetMagic( aMagicNo[ SW_LATIN ], aFntIdx[ SW_LATIN ], SW_LATIN ); + } + + nStartIndex = nEndIndex = nPos = nChgCnt = 0; + nPropFont = 0; + SwDoc* pDoc = rTxtNode.GetDoc(); + const IDocumentRedlineAccess* pIDRA = rTxtNode.getIDocumentRedlineAccess(); + + const SwExtTextInput* pExtInp = pDoc->GetExtTextInput( rTxtNode ); + const bool bShow = IDocumentRedlineAccess::IsShowChanges( pIDRA->GetRedlineMode() ); + if( pExtInp || bShow ) + { + MSHORT nRedlPos = pIDRA->GetRedlinePos( rTxtNode, USHRT_MAX ); + if( pExtInp || MSHRT_MAX != nRedlPos ) + { + const SvUShorts* pArr = 0; + xub_StrLen nInputStt = 0; + if( pExtInp ) + { + pArr = &pExtInp->GetAttrs(); + nInputStt = pExtInp->Start()->nContent.GetIndex(); + Seek( 0 ); + } + + pRedln = new SwRedlineItr( rTxtNode, *pFnt, aAttrHandler, nRedlPos, + bShow, pArr, nInputStt ); + + if( pRedln->IsOn() ) + ++nChgCnt; + } + } +} + +/************************************************************************* + * SwRedlineItr - Der Redline-Iterator + * + * Folgende Informationen/Zustaende gibt es im RedlineIterator: + * + * nFirst ist der erste Index der RedlineTbl, der mit dem Absatz ueberlappt. + * + * nAct ist der zur Zeit aktive ( wenn bOn gesetzt ist ) oder der naechste + * in Frage kommende Index. + * nStart und nEnd geben die Grenzen des Objekts innerhalb des Absatzes an. + * + * Wenn bOn gesetzt ist, ist der Font entsprechend manipuliert worden. + * + * Wenn nAct auf MSHRT_MAX gesetzt wurde ( durch Reset() ), so ist zur Zeit + * kein Redline aktiv, nStart und nEnd sind invalid. + *************************************************************************/ + +SwRedlineItr::SwRedlineItr( const SwTxtNode& rTxtNd, SwFont& rFnt, + SwAttrHandler& rAH, MSHORT nRed, sal_Bool bShw, const SvUShorts *pArr, + xub_StrLen nExtStart ) + : rDoc( *rTxtNd.GetDoc() ), rNd( rTxtNd ), rAttrHandler( rAH ), pSet( 0 ), + nNdIdx( rTxtNd.GetIndex() ), nFirst( nRed ), + nAct( MSHRT_MAX ), bOn( sal_False ), bShow( bShw ) +{ + if( pArr ) + pExt = new SwExtend( *pArr, nExtStart ); + else + pExt = NULL; + Seek( rFnt, 0, STRING_LEN ); +} + +SwRedlineItr::~SwRedlineItr() +{ + Clear( NULL ); + delete pSet; + delete pExt; +} + +// Der Return-Wert von SwRedlineItr::Seek gibt an, ob der aktuelle Font +// veraendert wurde durch Verlassen (-1) oder Betreten eines Bereichs (+1) + +short SwRedlineItr::_Seek( SwFont& rFnt, xub_StrLen nNew, xub_StrLen nOld ) +{ + short nRet = 0; + if( ExtOn() ) + return 0; // Abkuerzung: wenn wir innerhalb eines ExtendTextInputs sind + // kann es keine anderen Attributwechsel (auch nicht durch Redlining) geben + if( bShow ) + { + if( bOn ) + { + if( nNew >= nEnd ) + { + --nRet; + _Clear( &rFnt ); // Wir gehen hinter den aktuellen Bereich + ++nAct; // und pruefen gleich den naechsten + } + else if( nNew < nStart ) + { + --nRet; + _Clear( &rFnt ); // Wir gehen vor den aktuellen Bereich + if( nAct > nFirst ) + nAct = nFirst; // Die Pruefung muss von vorne beginnen + else + return nRet + EnterExtend( rFnt, nNew ); // Es gibt keinen vor uns. + } + else + return nRet + EnterExtend( rFnt, nNew ); // Wir sind im gleichen Bereich geblieben. + } + if( MSHRT_MAX == nAct || nOld > nNew ) + nAct = nFirst; + + nStart = STRING_LEN; + nEnd = STRING_LEN; + + for( ; nAct < rDoc.GetRedlineTbl().Count() ; ++nAct ) + { + rDoc.GetRedlineTbl()[ nAct ]->CalcStartEnd( nNdIdx, nStart, nEnd ); + + if( nNew < nEnd ) + { + if( nNew >= nStart ) // der einzig moegliche Kandidat + { + bOn = sal_True; + const SwRedline *pRed = rDoc.GetRedlineTbl()[ nAct ]; + + if (pSet) + pSet->ClearItem(); + else + { + SwAttrPool& rPool = + const_cast<SwDoc&>(rDoc).GetAttrPool(); + pSet = new SfxItemSet(rPool, RES_CHRATR_BEGIN, RES_CHRATR_END-1); + } + + if( 1 < pRed->GetStackCount() ) + FillHints( pRed->GetAuthor( 1 ), pRed->GetType( 1 ) ); + FillHints( pRed->GetAuthor(), pRed->GetType() ); + + SfxWhichIter aIter( *pSet ); + MSHORT nWhich = aIter.FirstWhich(); + while( nWhich ) + { + const SfxPoolItem* pItem; + if( ( nWhich < RES_CHRATR_END ) && + ( SFX_ITEM_SET == pSet->GetItemState( nWhich, sal_True, &pItem ) ) ) + { + SwTxtAttr* pAttr = MakeRedlineTxtAttr( + const_cast<SwDoc&>(rDoc), + *const_cast<SfxPoolItem*>(pItem) ); + pAttr->SetPriorityAttr( sal_True ); + aHints.C40_INSERT( SwTxtAttr, pAttr, aHints.Count()); + rAttrHandler.PushAndChg( *pAttr, rFnt ); + if( RES_CHRATR_COLOR == nWhich ) + rFnt.SetNoCol( sal_True ); + } + nWhich = aIter.NextWhich(); + } + + ++nRet; + } + break; + } + nStart = STRING_LEN; + nEnd = STRING_LEN; + } + } + return nRet + EnterExtend( rFnt, nNew ); +} + +void SwRedlineItr::FillHints( MSHORT nAuthor, RedlineType_t eType ) +{ + switch ( eType ) + { + case nsRedlineType_t::REDLINE_INSERT: + SW_MOD()->GetInsertAuthorAttr(nAuthor, *pSet); + break; + case nsRedlineType_t::REDLINE_DELETE: + SW_MOD()->GetDeletedAuthorAttr(nAuthor, *pSet); + break; + case nsRedlineType_t::REDLINE_FORMAT: + case nsRedlineType_t::REDLINE_FMTCOLL: + SW_MOD()->GetFormatAuthorAttr(nAuthor, *pSet); + break; + default: + break; + } +} + +void SwRedlineItr::ChangeTxtAttr( SwFont* pFnt, SwTxtAttr &rHt, sal_Bool bChg ) +{ + ASSERT( IsOn(), "SwRedlineItr::ChangeTxtAttr: Off?" ); + + if( !bShow && !pExt ) + return; + + if( bChg ) + { + if ( pExt && pExt->IsOn() ) + rAttrHandler.PushAndChg( rHt, *pExt->GetFont() ); + else + rAttrHandler.PushAndChg( rHt, *pFnt ); + } + else + { + ASSERT( ! pExt || ! pExt->IsOn(), "Pop of attribute during opened extension" ) + rAttrHandler.PopAndChg( rHt, *pFnt ); + } +} + +void SwRedlineItr::_Clear( SwFont* pFnt ) +{ + ASSERT( bOn, "SwRedlineItr::Clear: Off?" ); + bOn = sal_False; + while( aHints.Count() ) + { + SwTxtAttr *pPos = aHints[ 0 ]; + aHints.Remove(0); + if( pFnt ) + rAttrHandler.PopAndChg( *pPos, *pFnt ); + else + rAttrHandler.Pop( *pPos ); + SwTxtAttr::Destroy(pPos, const_cast<SwDoc&>(rDoc).GetAttrPool() ); + } + if( pFnt ) + pFnt->SetNoCol( sal_False ); +} + +xub_StrLen SwRedlineItr::_GetNextRedln( xub_StrLen nNext ) +{ + nNext = NextExtend( nNext ); + if( !bShow || MSHRT_MAX == nFirst ) + return nNext; + if( MSHRT_MAX == nAct ) + { + nAct = nFirst; + rDoc.GetRedlineTbl()[ nAct ]->CalcStartEnd( nNdIdx, nStart, nEnd ); + } + if( bOn || !nStart ) + { + if( nEnd < nNext ) + nNext = nEnd; + } + else if( nStart < nNext ) + nNext = nStart; + return nNext; +} + +sal_Bool SwRedlineItr::_ChkSpecialUnderline() const +{ + // Wenn die Unterstreichung oder das Escapement vom Redling kommt, + // wenden wir immer das SpecialUnderlining, d.h. die Unterstreichung + // unter der Grundlinie an. + for( MSHORT i = 0; i < aHints.Count(); ++i ) + { + MSHORT nWhich = aHints[i]->Which(); + if( RES_CHRATR_UNDERLINE == nWhich || + RES_CHRATR_ESCAPEMENT == nWhich ) + return sal_True; + } + return sal_False; +} + +sal_Bool SwRedlineItr::CheckLine( xub_StrLen nChkStart, xub_StrLen nChkEnd ) +{ + if( nFirst == MSHRT_MAX ) + return sal_False; + if( nChkEnd == nChkStart ) // Leerzeilen gucken ein Zeichen weiter. + ++nChkEnd; + xub_StrLen nOldStart = nStart; + xub_StrLen nOldEnd = nEnd; + xub_StrLen nOldAct = nAct; + sal_Bool bRet = sal_False; + + for( nAct = nFirst; nAct < rDoc.GetRedlineTbl().Count() ; ++nAct ) + { + rDoc.GetRedlineTbl()[ nAct ]->CalcStartEnd( nNdIdx, nStart, nEnd ); + if( nChkEnd < nStart ) + break; + if( nChkStart <= nEnd && ( nChkEnd > nStart || STRING_LEN == nEnd ) ) + { + bRet = sal_True; + break; + } + } + + nStart = nOldStart; + nEnd = nOldEnd; + nAct = nOldAct; + return bRet; +} + +void SwExtend::ActualizeFont( SwFont &rFnt, MSHORT nAttr ) +{ + if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE ) + rFnt.SetUnderline( UNDERLINE_SINGLE ); + else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE ) + rFnt.SetUnderline( UNDERLINE_BOLD ); + else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE ) + rFnt.SetUnderline( UNDERLINE_DOTTED ); + else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE ) + rFnt.SetUnderline( UNDERLINE_DOTTED ); + + if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT ) + rFnt.SetColor( Color( COL_RED ) ); + + if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT ) + { + const StyleSettings& rStyleSettings = GetpApp()->GetSettings().GetStyleSettings(); + rFnt.SetColor( rStyleSettings.GetHighlightTextColor() ); + rFnt.SetBackColor( new Color( rStyleSettings.GetHighlightColor() ) ); + } + if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE ) + rFnt.SetGreyWave( sal_True ); +} + +short SwExtend::Enter( SwFont& rFnt, xub_StrLen nNew ) +{ + ASSERT( !Inside(), "SwExtend: Enter without Leave" ); + ASSERT( !pFnt, "SwExtend: Enter with Font" ); + nPos = nNew; + if( Inside() ) + { + pFnt = new SwFont( rFnt ); + ActualizeFont( rFnt, rArr[ nPos - nStart ] ); + return 1; + } + return 0; +} + +sal_Bool SwExtend::_Leave( SwFont& rFnt, xub_StrLen nNew ) +{ + ASSERT( Inside(), "SwExtend: Leave without Enter" ); + MSHORT nOldAttr = rArr[ nPos - nStart ]; + nPos = nNew; + if( Inside() ) + { // Wir sind innerhalb des ExtendText-Bereichs geblieben + MSHORT nAttr = rArr[ nPos - nStart ]; + if( nOldAttr != nAttr ) // Gibt es einen (inneren) Attributwechsel? + { + rFnt = *pFnt; + ActualizeFont( rFnt, nAttr ); + } + } + else + { + rFnt = *pFnt; + delete pFnt; + pFnt = NULL; + return sal_True; + } + return sal_False; +} + +xub_StrLen SwExtend::Next( xub_StrLen nNext ) +{ + if( nPos < nStart ) + { + if( nNext > nStart ) + nNext = nStart; + } + else if( nPos < nEnd ) + { + MSHORT nIdx = nPos - nStart; + MSHORT nAttr = rArr[ nIdx ]; + while( ++nIdx < rArr.Count() && nAttr == rArr[ nIdx ] ) + ; //nothing + nIdx = nIdx + nStart; + if( nNext > nIdx ) + nNext = nIdx; + } + return nNext; +} diff --git a/sw/source/core/text/redlnitr.hxx b/sw/source/core/text/redlnitr.hxx new file mode 100644 index 000000000000..67588e976ee3 --- /dev/null +++ b/sw/source/core/text/redlnitr.hxx @@ -0,0 +1,126 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: redlnitr.hxx,v $ + * $Revision: 1.11 $ + * + * 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. + * + ************************************************************************/ +#ifndef _REDLNITR_HXX +#define _REDLNITR_HXX + +#include "ndhints.hxx" + +#ifndef IDOCUMENTREDLINEACCESS_HXX_INCLUDED +#include <IDocumentRedlineAccess.hxx> +#endif + +#include "swfont.hxx" +#ifndef _SVSTDARR_USHORTS +#define _SVSTDARR_USHORTS +#include <svtools/svstdarr.hxx> +#endif + +class SwTxtNode; +class SwDoc; +class SfxItemSet; +class SwAttrHandler; + +class SwExtend +{ + SwFont *pFnt; + const SvUShorts ⇒ // XAMA: Array of xub_StrLen + xub_StrLen nStart; + xub_StrLen nPos; + xub_StrLen nEnd; + sal_Bool _Leave( SwFont& rFnt, xub_StrLen nNew ); + sal_Bool Inside() const { return ( nPos >= nStart && nPos < nEnd ); } + void ActualizeFont( SwFont &rFnt, xub_StrLen nAttr ); +public: + SwExtend( const SvUShorts &rA, xub_StrLen nSt ) : pFnt(0), rArr( rA ), + nStart( nSt ), nPos( STRING_LEN ), nEnd( nStart + rA.Count() ) {} + ~SwExtend() { delete pFnt; } + sal_Bool IsOn() const { return pFnt != 0; } + void Reset() { if( pFnt ) { delete pFnt; pFnt = NULL; } nPos = STRING_LEN; } + sal_Bool Leave( SwFont& rFnt, xub_StrLen nNew ) + { if( pFnt ) return _Leave( rFnt, nNew ); return sal_False; } + short Enter( SwFont& rFnt, xub_StrLen nNew ); + xub_StrLen Next( xub_StrLen nNext ); + SwFont* GetFont() { return pFnt; } + void UpdateFont( SwFont &rFnt ) { ActualizeFont( rFnt, rArr[ nPos - nStart ] ); } +}; + +class SwRedlineItr +{ + SwpHtStart_SAR aHints; + const SwDoc& rDoc; + const SwTxtNode& rNd; + SwAttrHandler& rAttrHandler; + SfxItemSet *pSet; + SwExtend *pExt; + ULONG nNdIdx; + xub_StrLen nFirst; + xub_StrLen nAct; + xub_StrLen nStart; + xub_StrLen nEnd; + sal_Bool bOn; + sal_Bool bShow; + + void _Clear( SwFont* pFnt ); + sal_Bool _ChkSpecialUnderline() const; + void FillHints( MSHORT nAuthor, RedlineType_t eType ); + short _Seek( SwFont& rFnt, xub_StrLen nNew, xub_StrLen nOld ); + xub_StrLen _GetNextRedln( xub_StrLen nNext ); + inline short EnterExtend( SwFont& rFnt, xub_StrLen nNew ) + { if( pExt ) return pExt->Enter( rFnt, nNew ); return 0; } + inline xub_StrLen NextExtend( xub_StrLen nNext ) + { if( pExt ) return pExt->Next( nNext ); return nNext; } +public: + SwRedlineItr( const SwTxtNode& rTxtNd, SwFont& rFnt, SwAttrHandler& rAH, + xub_StrLen nRedlPos, sal_Bool bShw, const SvUShorts *pArr = 0, + xub_StrLen nStart = STRING_LEN ); + ~SwRedlineItr(); + inline sal_Bool IsOn() const { return bOn || ( pExt && pExt->IsOn() ); } + inline void Clear( SwFont* pFnt ) { if( bOn ) _Clear( pFnt ); } + void ChangeTxtAttr( SwFont* pFnt, SwTxtAttr &rHt, sal_Bool bChg ); + inline short Seek( SwFont& rFnt, xub_StrLen nNew, xub_StrLen nOld ) + { if( bShow || pExt ) return _Seek( rFnt, nNew, nOld ); return 0; } + inline void Reset() { if( nAct != nFirst ) nAct = STRING_LEN; + if( pExt ) pExt->Reset(); } + inline xub_StrLen GetNextRedln( xub_StrLen nNext ) + { if( bShow || pExt ) return _GetNextRedln( nNext ); return nNext; } + inline sal_Bool ChkSpecialUnderline() const + { if ( IsOn() ) return _ChkSpecialUnderline(); return sal_False; } + sal_Bool CheckLine( xub_StrLen nChkStart, xub_StrLen nChkEnd ); + inline sal_Bool LeaveExtend( SwFont& rFnt, xub_StrLen nNew ) + { return pExt->Leave(rFnt, nNew ); } + inline sal_Bool ExtOn() { if( pExt ) return pExt->IsOn(); return sal_False; } + inline void UpdateExtFont( SwFont &rFnt ) { + ASSERT( ExtOn(), "UpdateExtFont without ExtOn" ) + pExt->UpdateFont( rFnt ); } +}; + + +#endif + diff --git a/sw/source/core/text/txtcache.cxx b/sw/source/core/text/txtcache.cxx new file mode 100644 index 000000000000..4f5fc86e6adb --- /dev/null +++ b/sw/source/core/text/txtcache.cxx @@ -0,0 +1,244 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtcache.cxx,v $ + * $Revision: 1.9 $ + * + * 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 "errhdl.hxx" + +#include "txtcache.hxx" +#include "txtfrm.hxx" +#include "porlay.hxx" + +/************************************************************************* +|* +|* SwTxtLine::SwTxtLine(), ~SwTxtLine() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 16. Mar. 94 +|* +|*************************************************************************/ + +SwTxtLine::SwTxtLine( SwTxtFrm *pFrm, SwParaPortion *pNew ) : + SwCacheObj( (void*)pFrm ), + pLine( pNew ) +{ +} + +SwTxtLine::~SwTxtLine() +{ + delete pLine; +} + +/************************************************************************* +|* +|* SwTxtLineAccess::NewObj() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 16. Mar. 94 +|* +|*************************************************************************/ + +SwCacheObj *SwTxtLineAccess::NewObj() +{ + return new SwTxtLine( (SwTxtFrm*)pOwner ); +} + +/************************************************************************* +|* +|* SwTxtLineAccess::GetPara() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 16. Mar. 94 +|* +|*************************************************************************/ + +SwParaPortion *SwTxtLineAccess::GetPara() +{ + SwTxtLine *pRet; + if ( pObj ) + pRet = (SwTxtLine*)pObj; + else + { + pRet = (SwTxtLine*)Get(); + ((SwTxtFrm*)pOwner)->SetCacheIdx( pRet->GetCachePos() ); + } + if ( !pRet->GetPara() ) + pRet->SetPara( new SwParaPortion ); + return pRet->GetPara(); +} + + +/************************************************************************* +|* +|* SwTxtLineAccess::SwTxtLineAccess() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 16. Mar. 94 +|* +|*************************************************************************/ + +SwTxtLineAccess::SwTxtLineAccess( const SwTxtFrm *pOwn ) : + SwCacheAccess( *SwTxtFrm::GetTxtCache(), pOwn, pOwn->GetCacheIdx() ) +{ +} + +/************************************************************************* +|* +|* SwTxtLineAccess::IsAvailable +|* +|* Ersterstellung MA 23. Mar. 94 +|* Letzte Aenderung MA 23. Mar. 94 +|* +|*************************************************************************/ + +sal_Bool SwTxtLineAccess::IsAvailable() const +{ + if ( pObj ) + return ((SwTxtLine*)pObj)->GetPara() != 0; + return sal_False; +} + +/************************************************************************* +|* +|* SwTxtFrm::HasPara() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 22. Aug. 94 +|* +|*************************************************************************/ + +sal_Bool SwTxtFrm::_HasPara() const +{ + SwTxtLine *pTxtLine = (SwTxtLine*)SwTxtFrm::GetTxtCache()-> + Get( this, GetCacheIdx(), sal_False ); + if ( pTxtLine ) + { + if ( pTxtLine->GetPara() ) + return sal_True; + } + else + ((SwTxtFrm*)this)->nCacheIdx = MSHRT_MAX; + + return sal_False; +} + +/************************************************************************* +|* +|* SwTxtFrm::GetPara() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 22. Aug. 94 +|* +|*************************************************************************/ + +SwParaPortion *SwTxtFrm::GetPara() +{ + if ( GetCacheIdx() != MSHRT_MAX ) + { SwTxtLine *pLine = (SwTxtLine*)SwTxtFrm::GetTxtCache()-> + Get( this, GetCacheIdx(), sal_False ); + if ( pLine ) + return pLine->GetPara(); + else + nCacheIdx = MSHRT_MAX; + } + return 0; +} + + +/************************************************************************* +|* +|* SwTxtFrm::ClearPara() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 22. Aug. 94 +|* +|*************************************************************************/ + +void SwTxtFrm::ClearPara() +{ + ASSERT( !IsLocked(), "+SwTxtFrm::ClearPara: this is locked." ); + if ( !IsLocked() && GetCacheIdx() != MSHRT_MAX ) + { + SwTxtLine *pTxtLine = (SwTxtLine*)SwTxtFrm::GetTxtCache()-> + Get( this, GetCacheIdx(), sal_False ); + if ( pTxtLine ) + { + delete pTxtLine->GetPara(); + pTxtLine->SetPara( 0 ); + } + else + nCacheIdx = MSHRT_MAX; + } +} + +/************************************************************************* +|* +|* SwTxtFrm::SetPara() +|* +|* Ersterstellung MA 16. Mar. 94 +|* Letzte Aenderung MA 22. Aug. 94 +|* +|*************************************************************************/ + +void SwTxtFrm::SetPara( SwParaPortion *pNew, sal_Bool bDelete ) +{ + if ( GetCacheIdx() != MSHRT_MAX ) + { + //Nur die Information Auswechseln, das CacheObj bleibt stehen. + SwTxtLine *pTxtLine = (SwTxtLine*)SwTxtFrm::GetTxtCache()-> + Get( this, GetCacheIdx(), sal_False ); + if ( pTxtLine ) + { + if( bDelete ) + delete pTxtLine->GetPara(); + pTxtLine->SetPara( pNew ); + } + else + { + ASSERT( !pNew, "+SetPara: Losing SwParaPortion" ); + nCacheIdx = MSHRT_MAX; + } + } + else if ( pNew ) + { //Einen neuen einfuegen. + SwTxtLine *pTxtLine = new SwTxtLine( this, pNew ); + if ( SwTxtFrm::GetTxtCache()->Insert( pTxtLine ) ) + nCacheIdx = pTxtLine->GetCachePos(); + else + { + ASSERT( sal_False, "+SetPara: InsertCache failed." ); + } + } +} + + diff --git a/sw/source/core/text/txtcache.hxx b/sw/source/core/text/txtcache.hxx new file mode 100644 index 000000000000..402f01d01dcd --- /dev/null +++ b/sw/source/core/text/txtcache.hxx @@ -0,0 +1,79 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtcache.hxx,v $ + * $Revision: 1.3 $ + * + * 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. + * + ************************************************************************/ +#ifndef _TXTCACHE_HXX +#define _TXTCACHE_HXX + +#include <sal/types.h> +#include <tools/mempool.hxx> +#include "swcache.hxx" + +class SwParaPortion; +class SwTxtFrm; + +class SwTxtLine : public SwCacheObj +{ + SwParaPortion *pLine; + +public: + DECL_FIXEDMEMPOOL_NEWDEL(SwTxtLine) + + SwTxtLine( SwTxtFrm *pFrm, SwParaPortion *pNew = 0 ); + virtual ~SwTxtLine(); + + inline SwParaPortion *GetPara() { return pLine; } + inline const SwParaPortion *GetPara() const { return pLine; } + + inline void SetPara( SwParaPortion *pNew ) { pLine = pNew; } +}; + + +class SwTxtLineAccess : public SwCacheAccess +{ + +protected: + virtual SwCacheObj *NewObj(); + +public: + SwTxtLineAccess( const SwTxtFrm *pOwner ); + + SwParaPortion *GetPara(); + + inline SwTxtLine &GetTxtLine(); + + virtual sal_Bool IsAvailable() const; +}; + + +inline SwTxtLine &SwTxtLineAccess::GetTxtLine() +{ + return *((SwTxtLine*)Get()); +} + +#endif diff --git a/sw/source/core/text/txtcfg.hxx b/sw/source/core/text/txtcfg.hxx new file mode 100644 index 000000000000..cd6d668b5d7a --- /dev/null +++ b/sw/source/core/text/txtcfg.hxx @@ -0,0 +1,57 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtcfg.hxx,v $ + * $Revision: 1.5 $ + * + * 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. + * + ************************************************************************/ + +#ifndef _TXTCFG_HXX +#define _TXTCFG_HXX + +#if OSL_DEBUG_LEVEL > 1 +#include "dbgloop.hxx" // DBG_LOOP +#else +#ifdef DBG_LOOP //kann per precompiled hereinkommen +#undef DBG_LOOP +#undef DBG_LOOP_RESET +#endif +#define DBG_LOOP +#define DBG_LOOP_RESET +#endif + +// Toleranzwert in der Formatierung und Textausgabe. +#define SLOPPY_TWIPS 5 + +#define CONSTCHAR( name, string ) static const sal_Char __FAR_DATA name[] = string + +// Allgemeines ... + +#ifndef CONST +#define CONST const +#endif + + +#endif diff --git a/sw/source/core/text/txtdrop.cxx b/sw/source/core/text/txtdrop.cxx new file mode 100644 index 000000000000..f77a252a1e72 --- /dev/null +++ b/sw/source/core/text/txtdrop.cxx @@ -0,0 +1,1108 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtdrop.cxx,v $ + * $Revision: 1.27 $ + * + * 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 <vcl/metric.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <paratr.hxx> +#include <txtfrm.hxx> // Format() +#include <charfmt.hxx> +#include <viewopt.hxx> // SwViewOption +#include <viewsh.hxx> // ViewShell +#include <pordrop.hxx> +#include <itrform2.hxx> +#include <txtpaint.hxx> // SwSaveClip +#include <blink.hxx> // pBlink +#include <breakit.hxx> +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <com/sun/star/i18n/WordType.hpp> +#include <svx/langitem.hxx> +#include <charatr.hxx> +#include <svx/fhgtitem.hxx> + +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star; + +/************************************************************************* + * lcl_IsDropFlyInter + * + * Calculates if a drop caps portion intersects with a fly + * The width and height of the drop caps portion are passed as arguments, + * the position is calculated from the values in rInf + *************************************************************************/ + +sal_Bool lcl_IsDropFlyInter( const SwTxtFormatInfo &rInf, + USHORT nWidth, USHORT nHeight ) +{ + const SwTxtFly *pTxtFly = rInf.GetTxtFly(); + if( pTxtFly && pTxtFly->IsOn() ) + { + SwRect aRect( rInf.GetTxtFrm()->Frm().Pos(), Size( nWidth, nHeight) ); + aRect.Pos() += rInf.GetTxtFrm()->Prt().Pos(); + aRect.Pos().X() += rInf.X(); + aRect.Pos().Y() = rInf.Y(); + aRect = pTxtFly->GetFrm( aRect ); + return aRect.HasArea(); + } + + return sal_False; +} + +/************************************************************************* + * class SwDropSave + *************************************************************************/ + +class SwDropSave +{ + SwTxtPaintInfo* pInf; + xub_StrLen nIdx; + xub_StrLen nLen; + long nX; + long nY; + +public: + SwDropSave( const SwTxtPaintInfo &rInf ); + ~SwDropSave(); +}; + +SwDropSave::SwDropSave( const SwTxtPaintInfo &rInf ) : + pInf( ((SwTxtPaintInfo*)&rInf) ), nIdx( rInf.GetIdx() ), + nLen( rInf.GetLen() ), nX( rInf.X() ), nY( rInf.Y() ) +{ +} + +SwDropSave::~SwDropSave() +{ + pInf->SetIdx( nIdx ); + pInf->SetLen( nLen ); + pInf->X( nX ); + pInf->Y( nY ); +} + +/************************************************************************* + * SwDropPortionPart DTor + *************************************************************************/ + +SwDropPortionPart::~SwDropPortionPart() +{ + if ( pFollow ) + delete pFollow; + delete pFnt; +} + +/************************************************************************* + * SwDropPortion CTor, DTor + *************************************************************************/ + +SwDropPortion::SwDropPortion( const MSHORT nLineCnt, + const KSHORT nDrpHeight, + const KSHORT nDrpDescent, + const KSHORT nDist ) + : pPart( 0 ), + nLines( nLineCnt ), + nDropHeight(nDrpHeight), + nDropDescent(nDrpDescent), + nDistance(nDist), + nFix(0), + nX(0) +{ + SetWhichPor( POR_DROP ); +} + +SwDropPortion::~SwDropPortion() +{ + delete pPart; + if( pBlink ) + pBlink->Delete( this ); +} + +sal_Bool SwTxtSizeInfo::_HasHint( const SwTxtNode* pTxtNode, xub_StrLen nPos ) +{ + return 0 != pTxtNode->GetTxtAttrForCharAt(nPos); +} + +/************************************************************************* + * SwTxtNode::GetDropLen() + * + * nWishLen = 0 indicates that we want a whole word + *************************************************************************/ + +MSHORT SwTxtNode::GetDropLen( MSHORT nWishLen ) const +{ + xub_StrLen nEnd = GetTxt().Len(); + if( nWishLen && nWishLen < nEnd ) + nEnd = nWishLen; + + if ( ! nWishLen && pBreakIt->GetBreakIter().is() ) + { + // find first word + const SwAttrSet& rAttrSet = GetSwAttrSet(); + const USHORT nTxtScript = pBreakIt->GetRealScriptOfText( GetTxt(), 0 ); + + LanguageType eLanguage; + + switch ( nTxtScript ) + { + case i18n::ScriptType::ASIAN : + eLanguage = rAttrSet.GetCJKLanguage().GetLanguage(); + break; + case i18n::ScriptType::COMPLEX : + eLanguage = rAttrSet.GetCTLLanguage().GetLanguage(); + break; + default : + eLanguage = rAttrSet.GetLanguage().GetLanguage(); + break; + } + + Boundary aBound = + pBreakIt->GetBreakIter()->getWordBoundary( GetTxt(), 0, + pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, sal_True ); + + nEnd = (xub_StrLen)aBound.endPos; + } + + xub_StrLen i = 0; + for( ; i < nEnd; ++i ) + { + xub_Unicode cChar = GetTxt().GetChar( i ); + if( CH_TAB == cChar || CH_BREAK == cChar || + (( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar ) + && SwTxtSizeInfo::_HasHint( this, i ) ) ) + break; + } + return i; +} + +/************************************************************************* + * SwTxtNode::GetDropSize() + * + * If a dropcap is found the return value is true otherwise false. The + * drop cap sizes passed back by reference are font height, drop height + * and drop descent. + *************************************************************************/ +bool SwTxtNode::GetDropSize(int& rFontHeight, int& rDropHeight, int& rDropDescent) const +{ + rFontHeight = 0; + rDropHeight = 0; + rDropDescent =0; + + const SwAttrSet& rSet = GetSwAttrSet(); + const SwFmtDrop& rDrop = rSet.GetDrop(); + + // Return (0,0) if there is no drop cap at this paragraph + if( 1 >= rDrop.GetLines() || + ( !rDrop.GetChars() && !rDrop.GetWholeWord() ) ) + { + return false; + } + + // get text frame + SwClientIter aClientIter( (SwTxtNode&)*this ); + SwClient* pLastFrm = aClientIter.GoStart(); + + while( pLastFrm ) + { + // Only (master-) text frames can have a drop cap. + if ( pLastFrm->ISA( SwTxtFrm ) && !((SwTxtFrm*)pLastFrm)->IsFollow() ) + { + + if( !((SwTxtFrm*)pLastFrm)->HasPara() ) + ((SwTxtFrm*)pLastFrm)->GetFormatted(); + + if ( !((SwTxtFrm*)pLastFrm)->IsEmpty() ) + { + const SwParaPortion* pPara = ((SwTxtFrm*)pLastFrm)->GetPara(); + ASSERT( pPara, "GetDropSize could not find the ParaPortion, I'll guess the drop cap size" ) + + if ( pPara ) + { + const SwLinePortion* pFirstPor = pPara->GetFirstPortion(); + if (pFirstPor && pFirstPor->IsDropPortion()) + { + const SwDropPortion* pDrop = (const SwDropPortion*)pFirstPor; + rDropHeight = pDrop->GetDropHeight(); + rDropDescent = pDrop->GetDropDescent(); + if (const SwFont *pFont = pDrop->GetFnt()) + rFontHeight = pFont->GetSize(pFont->GetActual()).Height(); + else + { + const SvxFontHeightItem& rItem = (SvxFontHeightItem&)rSet.Get(RES_CHRATR_FONTSIZE); + rFontHeight = rItem.GetHeight(); + } + } + } + } + break; + } + pLastFrm = ++aClientIter; + } + + if (rFontHeight==0 && rDropHeight==0 && rDropDescent==0) + { + const USHORT nLines = rDrop.GetLines(); + + const SvxFontHeightItem& rItem = (SvxFontHeightItem&)rSet.Get( RES_CHRATR_FONTSIZE ); + rFontHeight = rItem.GetHeight(); + rDropHeight = nLines * rFontHeight; + rDropDescent = rFontHeight / 5; + return false; + } + + return true; +} + +/************************************************************************* + * SwDropPortion::PaintTxt() + *************************************************************************/ + +// Die Breite manipulieren, sonst werden die Buchstaben gestretcht + +void SwDropPortion::PaintTxt( const SwTxtPaintInfo &rInf ) const +{ + if ( rInf.OnWin() && + !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() ) + rInf.DrawBackground( *this ); + + ASSERT( nDropHeight && pPart && nLines != 1, "Drop Portion painted twice" ); + + const SwDropPortionPart* pCurrPart = GetPart(); + const xub_StrLen nOldLen = GetLen(); + + const SwTwips nBasePosY = rInf.Y(); + ((SwTxtPaintInfo&)rInf).Y( nBasePosY + nY ); + SwDropSave aSave( rInf ); + // for text inside drop portions we let vcl handle the text directions + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + aLayoutModeModifier.SetAuto(); + + while ( pCurrPart ) + { + ((SwDropPortion*)this)->SetLen( pCurrPart->GetLen() ); + ((SwTxtPaintInfo&)rInf).SetLen( pCurrPart->GetLen() ); + SwFontSave aFontSave( rInf, &pCurrPart->GetFont() ); + + SwTxtPortion::Paint( rInf ); + + ((SwTxtPaintInfo&)rInf).SetIdx( rInf.GetIdx() + pCurrPart->GetLen() ); + ((SwTxtPaintInfo&)rInf).X( rInf.X() + pCurrPart->GetWidth() ); + pCurrPart = pCurrPart->GetFollow(); + } + + ((SwTxtPaintInfo&)rInf).Y( nBasePosY ); + ((SwDropPortion*)this)->SetLen( nOldLen ); +} + +/************************************************************************* + * SwDropPortion::Paint() + *************************************************************************/ + +void SwDropPortion::PaintDrop( const SwTxtPaintInfo &rInf ) const +{ + // ganz normale Ausgabe wird w?hrend des normalen Paints erledigt + if( ! nDropHeight || ! pPart || nLines == 1 ) + return; + + // Luegenwerte einstellen! + const KSHORT nOldHeight = Height(); + const KSHORT nOldWidth = Width(); + const KSHORT nOldAscent = GetAscent(); + const SwTwips nOldPosY = rInf.Y(); + const KSHORT nOldPosX = (KSHORT)rInf.X(); + const SwParaPortion *pPara = rInf.GetParaPortion(); + const Point aOutPos( nOldPosX + nX, nOldPosY - pPara->GetAscent() + - pPara->GetRealHeight() + pPara->Height() ); + // Retusche nachholen. + + // Set baseline + ((SwTxtPaintInfo&)rInf).Y( aOutPos.Y() + nDropHeight ); + + // for background + ((SwDropPortion*)this)->Height( nDropHeight + nDropDescent ); + ((SwDropPortion*)this)->Width( Width() - nX ); + ((SwDropPortion*)this)->SetAscent( nDropHeight ); + + // Clipregion auf uns einstellen! + // Und zwar immer, und nie mit dem bestehenden ClipRect + // verrechnen, weil dies auf die Zeile eingestellt sein koennte. + + SwRect aClipRect; + if ( rInf.OnWin() ) + { + aClipRect = SwRect( aOutPos, SvLSize() ); + aClipRect.Intersection( rInf.GetPaintRect() ); + } + SwSaveClip aClip( (OutputDevice*)rInf.GetOut() ); + aClip.ChgClip( aClipRect, rInf.GetTxtFrm() ); + // Das machen, was man sonst nur macht ... + PaintTxt( rInf ); + + // Alte Werte sichern + ((SwDropPortion*)this)->Height( nOldHeight ); + ((SwDropPortion*)this)->Width( nOldWidth ); + ((SwDropPortion*)this)->SetAscent( nOldAscent ); + ((SwTxtPaintInfo&)rInf).Y( nOldPosY ); +} + +/************************************************************************* + * virtual SwDropPortion::Paint() + *************************************************************************/ + +void SwDropPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + // ganz normale Ausgabe wird hier erledigt. + if( ! nDropHeight || ! pPart || 1 == nLines ) + { + if ( rInf.OnWin() && + !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() ) + rInf.DrawBackground( *this ); + + // make sure that font is not rotated + SwFont* pTmpFont = 0; + if ( rInf.GetFont()->GetOrientation( rInf.GetTxtFrm()->IsVertical() ) ) + { + pTmpFont = new SwFont( *rInf.GetFont() ); + pTmpFont->SetVertical( 0, rInf.GetTxtFrm()->IsVertical() ); + } + + SwFontSave aFontSave( rInf, pTmpFont ); + // for text inside drop portions we let vcl handle the text directions + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + aLayoutModeModifier.SetAuto(); + + SwTxtPortion::Paint( rInf ); + delete pTmpFont; + } +} + +/************************************************************************* + * virtual Format() + *************************************************************************/ + + +sal_Bool SwDropPortion::FormatTxt( SwTxtFormatInfo &rInf ) +{ + const xub_StrLen nOldLen = GetLen(); + const xub_StrLen nOldInfLen = rInf.GetLen(); + const sal_Bool bFull = SwTxtPortion::Format( rInf ); + if( bFull ) + { + // sieht zwar Scheisse aus, aber was soll man schon machen? + rInf.SetUnderFlow( 0 ); + Truncate(); + SetLen( nOldLen ); + rInf.SetLen( nOldInfLen ); + } + return bFull; +} + +/************************************************************************* + * virtual GetTxtSize() + *************************************************************************/ + + +SwPosSize SwDropPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const +{ + USHORT nMyX = 0; + xub_StrLen nIdx = 0; + + const SwDropPortionPart* pCurrPart = GetPart(); + + // skip parts + while ( pCurrPart && nIdx + pCurrPart->GetLen() < rInf.GetLen() ) + { + nMyX = nMyX + pCurrPart->GetWidth(); + nIdx = nIdx + pCurrPart->GetLen(); + pCurrPart = pCurrPart->GetFollow(); + } + + xub_StrLen nOldIdx = rInf.GetIdx(); + xub_StrLen nOldLen = rInf.GetLen(); + + ((SwTxtSizeInfo&)rInf).SetIdx( nIdx ); + ((SwTxtSizeInfo&)rInf).SetLen( rInf.GetLen() - nIdx ); + + // robust + SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : 0 ); + SwPosSize aPosSize( SwTxtPortion::GetTxtSize( rInf ) ); + aPosSize.Width( aPosSize.Width() + nMyX ); + + ((SwTxtSizeInfo&)rInf).SetIdx( nOldIdx ); + ((SwTxtSizeInfo&)rInf).SetLen( nOldLen ); + + return aPosSize; +} + +/************************************************************************* + * virtual GetCrsrOfst() + *************************************************************************/ + +xub_StrLen SwDropPortion::GetCrsrOfst( const KSHORT ) const +{ + return 0; +} + +/************************************************************************* + * SwTxtFormatter::CalcDropHeight() + *************************************************************************/ + +void SwTxtFormatter::CalcDropHeight( const MSHORT nLines ) +{ + const SwLinePortion *const pOldCurr = GetCurr(); + KSHORT nDropHght = 0; + KSHORT nAscent = 0; + KSHORT nHeight = 0; + KSHORT nDropLns = 0; + sal_Bool bRegisterOld = IsRegisterOn(); + bRegisterOn = sal_False; + + Top(); + + while( GetCurr()->IsDummy() ) + { + if ( !Next() ) + break; + } + + // Wenn wir nur eine Zeile haben returnen wir 0 + if( GetNext() || GetDropLines() == 1 ) + { + for( ; nDropLns < nLines; nDropLns++ ) + { + if ( GetCurr()->IsDummy() ) + break; + else + { + CalcAscentAndHeight( nAscent, nHeight ); + nDropHght = nDropHght + nHeight; + bRegisterOn = bRegisterOld; + } + if ( !Next() ) + { + nDropLns++; // Fix: 11356 + break; + } + } + + // In der letzten Zeile plumpsen wir auf den Zeilenascent! + nDropHght = nDropHght - nHeight; + nDropHght = nDropHght + nAscent; + Top(); + } + bRegisterOn = bRegisterOld; + SetDropDescent( nHeight - nAscent ); + SetDropHeight( nDropHght ); + SetDropLines( nDropLns ); + // Alte Stelle wiederfinden! + while( pOldCurr != GetCurr() ) + { + if( !Next() ) + { + ASSERT( !this, "SwTxtFormatter::_CalcDropHeight: left Toulouse" ); + break; + } + } +} + +/************************************************************************* + * SwTxtFormatter::GuessDropHeight() + * + * Wir schaetzen mal, dass die Fonthoehe sich nicht aendert und dass + * erst mindestens soviele Zeilen gibt, wie die DropCap-Einstellung angibt. + * + *************************************************************************/ + + + +void SwTxtFormatter::GuessDropHeight( const MSHORT nLines ) +{ + ASSERT( nLines, "GuessDropHeight: Give me more Lines!" ); + KSHORT nAscent = 0; + KSHORT nHeight = 0; + SetDropLines( nLines ); + if ( GetDropLines() > 1 ) + { + CalcRealHeight(); + CalcAscentAndHeight( nAscent, nHeight ); + } + SetDropDescent( nHeight - nAscent ); + SetDropHeight( nHeight * nLines - GetDropDescent() ); +} + +/************************************************************************* + * SwTxtFormatter::NewDropPortion + *************************************************************************/ + +SwDropPortion *SwTxtFormatter::NewDropPortion( SwTxtFormatInfo &rInf ) +{ + if( !pDropFmt ) + return 0; + + xub_StrLen nPorLen = pDropFmt->GetWholeWord() ? 0 : pDropFmt->GetChars(); + nPorLen = pFrm->GetTxtNode()->GetDropLen( nPorLen ); + if( !nPorLen ) + { + ((SwTxtFormatter*)this)->ClearDropFmt(); + return 0; + } + + SwDropPortion *pDropPor = 0; + + // erste oder zweite Runde? + if ( !( GetDropHeight() || IsOnceMore() ) ) + { + if ( GetNext() ) + CalcDropHeight( pDropFmt->GetLines() ); + else + GuessDropHeight( pDropFmt->GetLines() ); + } + + // the DropPortion + if( GetDropHeight() ) + pDropPor = new SwDropPortion( GetDropLines(), GetDropHeight(), + GetDropDescent(), pDropFmt->GetDistance() ); + else + pDropPor = new SwDropPortion( 0,0,0,pDropFmt->GetDistance() ); + + pDropPor->SetLen( nPorLen ); + + // If it was not possible to create a proper drop cap portion + // due to avoiding endless loops. We return a drop cap portion + // with an empty SwDropCapPart. For these portions the current + // font is used. + if ( GetDropLines() < 2 ) + { + ((SwTxtFormatter*)this)->SetPaintDrop( sal_True ); + return pDropPor; + } + + // build DropPortionParts: + ASSERT( ! rInf.GetIdx(), "Drop Portion not at 0 position!" ); + xub_StrLen nNextChg = 0; + const SwCharFmt* pFmt = pDropFmt->GetCharFmt(); + SwDropPortionPart* pCurrPart = 0; + + while ( nNextChg < nPorLen ) + { + // check for attribute changes and if the portion has to split: + Seek( nNextChg ); + + // the font is deleted in the destructor of the drop portion part + SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); + if ( pFmt ) + { + const SwAttrSet& rSet = pFmt->GetAttrSet(); + pTmpFnt->SetDiffFnt( &rSet, pFrm->GetTxtNode()->getIDocumentSettingAccess() ); + } + + // we do not allow a vertical font for the drop portion + pTmpFnt->SetVertical( 0, rInf.GetTxtFrm()->IsVertical() ); + + // find next attribute change / script change + const xub_StrLen nTmpIdx = nNextChg; + xub_StrLen nNextAttr = Min( GetNextAttr(), rInf.GetTxt().Len() ); + nNextChg = pScriptInfo->NextScriptChg( nTmpIdx ); + if( nNextChg > nNextAttr ) + nNextChg = nNextAttr; + if ( nNextChg > nPorLen ) + nNextChg = nPorLen; + + SwDropPortionPart* pPart = + new SwDropPortionPart( *pTmpFnt, nNextChg - nTmpIdx ); + + if ( ! pCurrPart ) + pDropPor->SetPart( pPart ); + else + pCurrPart->SetFollow( pPart ); + + pCurrPart = pPart; + } + + ((SwTxtFormatter*)this)->SetPaintDrop( sal_True ); + return pDropPor; +} + +/************************************************************************* + * SwTxtPainter::PaintDropPortion() + *************************************************************************/ + + + +void SwTxtPainter::PaintDropPortion() +{ + const SwDropPortion *pDrop = GetInfo().GetParaPortion()->FindDropPortion(); + ASSERT( pDrop, "DrapCop-Portion not available." ); + if( !pDrop ) + return; + + const SwTwips nOldY = GetInfo().Y(); + + Top(); + + GetInfo().SetpSpaceAdd( pCurr->GetpLLSpaceAdd() ); + GetInfo().ResetSpaceIdx(); + GetInfo().SetKanaComp( pCurr->GetpKanaComp() ); + GetInfo().ResetKanaIdx(); + + // 8047: Drops und Dummies + while( !pCurr->GetLen() && Next() ) + ; + + // MarginPortion und Adjustment! + const SwLinePortion *pPor = pCurr->GetFirstPortion(); + KSHORT nX = 0; + while( pPor && !pPor->IsDropPortion() ) + { + nX = nX + pPor->Width(); + pPor = pPor->GetPortion(); + } + Point aLineOrigin( GetTopLeft() ); + +#ifdef NIE + // Retusche nachholen... + if( nX ) + { + const Point aPoint( Left(), Y() ); + const Size aSize( nX - 1, GetDropHeight()+GetDropDescent() ); + SwRect aRetouche( aPoint, aSize ); + GetInfo().DrawRect( aRetouche ); + } +#endif + + aLineOrigin.X() += nX; + KSHORT nTmpAscent, nTmpHeight; + CalcAscentAndHeight( nTmpAscent, nTmpHeight ); + aLineOrigin.Y() += nTmpAscent; + GetInfo().SetIdx( GetStart() ); + GetInfo().SetPos( aLineOrigin ); + GetInfo().SetLen( pDrop->GetLen() ); + + pDrop->PaintDrop( GetInfo() ); + + GetInfo().Y( nOldY ); +} + +/************************************************************************* + * clas SwDropCapCache + * + * Da die Berechnung der Fontgroesse der Initialen ein teures Geschaeft ist, + * wird dies durch einen DropCapCache geschleust. + *************************************************************************/ + +#define DROP_CACHE_SIZE 10 + +class SwDropCapCache +{ + long aMagicNo[ DROP_CACHE_SIZE ]; + XubString aTxt[ DROP_CACHE_SIZE ]; + USHORT aFactor[ DROP_CACHE_SIZE ]; + KSHORT aWishedHeight[ DROP_CACHE_SIZE ]; + short aDescent[ DROP_CACHE_SIZE ]; + MSHORT nIndex; +public: + SwDropCapCache(); + ~SwDropCapCache(){} + void CalcFontSize( SwDropPortion* pDrop, SwTxtFormatInfo &rInf ); +}; + +/************************************************************************* + * SwDropCapCache Ctor / Dtor + *************************************************************************/ + +SwDropCapCache::SwDropCapCache() : nIndex( 0 ) +{ + memset( &aMagicNo, 0, sizeof(aMagicNo) ); + memset( &aWishedHeight, 0, sizeof(aWishedHeight) ); +} + +void SwDropPortion::DeleteDropCapCache() +{ + delete pDropCapCache; +} + +/************************************************************************* + * SwDropCapCache::CalcFontSize + *************************************************************************/ + +void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTxtFormatInfo &rInf ) +{ + const void* pFntNo = 0; + MSHORT nTmpIdx = 0; + + ASSERT( pDrop->GetPart(),"DropPortion without part during font calculation"); + + SwDropPortionPart* pCurrPart = pDrop->GetPart(); + const sal_Bool bUseCache = ! pCurrPart->GetFollow(); + xub_StrLen nIdx = rInf.GetIdx(); + XubString aStr( rInf.GetTxt(), nIdx, pCurrPart->GetLen() ); + + long nAscent = 0; + long nDescent = 0; + long nFactor = -1; + + if ( bUseCache ) + { + SwFont& rFnt = pCurrPart->GetFont(); + rFnt.ChkMagic( rInf.GetVsh(), rFnt.GetActual() ); + rFnt.GetMagic( pFntNo, nTmpIdx, rFnt.GetActual() ); + + nTmpIdx = 0; + + while( nTmpIdx < DROP_CACHE_SIZE && + ( aTxt[ nTmpIdx ] != aStr || aMagicNo[ nTmpIdx ] != long(pFntNo) || + aWishedHeight[ nTmpIdx ] != pDrop->GetDropHeight() ) ) + ++nTmpIdx; + } + + // we have to calculate a new font scaling factor if + // 1. we did not find a scaling factor in the cache or + // 2. we are not allowed to use the cache because the drop portion + // consists of more than one part + if( nTmpIdx >= DROP_CACHE_SIZE || ! bUseCache ) + { + ++nIndex; + nIndex %= DROP_CACHE_SIZE; + nTmpIdx = nIndex; + + long nWishedHeight = pDrop->GetDropHeight(); + + // find out biggest font size for initial scaling factor + long nMaxFontHeight = 0; + while ( pCurrPart ) + { + const SwFont& rFnt = pCurrPart->GetFont(); + const long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() ); + if ( nCurrHeight > nMaxFontHeight ) + nMaxFontHeight = nCurrHeight; + + pCurrPart = pCurrPart->GetFollow(); + } + + nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight; + + if ( bUseCache ) + { + // save keys for cache + aMagicNo[ nTmpIdx ] = long(pFntNo); + aTxt[ nTmpIdx ] = aStr; + aWishedHeight[ nTmpIdx ] = KSHORT(nWishedHeight); + // save initial scaling factor + aFactor[ nTmpIdx ] = (USHORT)nFactor; + } + + sal_Bool bGrow = ( pDrop->GetLen() != 0 ); + + // for growing controll + long nMax = KSHRT_MAX; + long nMin = nFactor / 2; +#if OSL_DEBUG_LEVEL > 1 + long nGrow = 0; +#endif + + sal_Bool bWinUsed = sal_False; + Font aOldFnt; + MapMode aOldMap( MAP_TWIP ); + OutputDevice* pOut = rInf.GetOut(); + OutputDevice* pWin; + if( rInf.GetVsh() && rInf.GetVsh()->GetWin() ) + pWin = rInf.GetVsh()->GetWin(); + else + pWin = GetpApp()->GetDefaultDevice(); + + while( bGrow ) + { + // reset pCurrPart to first part + pCurrPart = pDrop->GetPart(); + sal_Bool bFirstGlyphRect = sal_True; + sal_Bool bHaveGlyphRect = sal_False; + Rectangle aCommonRect, aRect; + + while ( pCurrPart ) + { + // current font + SwFont& rFnt = pCurrPart->GetFont(); + + // Get height including proportion + const USHORT nCurrHeight = + (USHORT)rFnt.GetHeight( rFnt.GetActual() ); + + // Get without proportion + const BYTE nOldProp = rFnt.GetPropr(); + rFnt.SetProportion( 100 ); + Size aOldSize = Size( 0, rFnt.GetHeight( rFnt.GetActual() ) ); + + Size aNewSize( 0, ( nFactor * nCurrHeight ) / 1000 ); + rFnt.SetSize( aNewSize, rFnt.GetActual() ); + rFnt.ChgPhysFnt( rInf.GetVsh(), *pOut ); + + nAscent = rFnt.GetAscent( rInf.GetVsh(), *pOut ); + + // Wir besorgen uns das alle Buchstaben umfassende Rechteck: + bHaveGlyphRect = pOut->GetTextBoundRect( aRect, rInf.GetTxt(), 0, + nIdx, pCurrPart->GetLen() ) && + ! aRect.IsEmpty(); + + if ( ! bHaveGlyphRect ) + { + // getting glyph boundaries failed for some reason, + // we take the window for calculating sizes + if ( pWin ) + { + if ( ! bWinUsed ) + { + bWinUsed = sal_True; + aOldMap = pWin->GetMapMode( ); + pWin->SetMapMode( MapMode( MAP_TWIP ) ); + aOldFnt = pWin->GetFont(); + } + pWin->SetFont( rFnt.GetActualFont() ); + + bHaveGlyphRect = pWin->GetTextBoundRect( aRect, rInf.GetTxt(), 0, + nIdx, pCurrPart->GetLen() ) && + ! aRect.IsEmpty(); + } + if ( bHaveGlyphRect ) + { + FontMetric aWinMet( pWin->GetFontMetric() ); + nAscent = (KSHORT) aWinMet.GetAscent(); + } + else + // We do not have a window or our window could not + // give us glyph boundaries. + aRect = Rectangle( Point( 0, 0 ), Size( 0, nAscent ) ); + } + + // Now we (hopefully) have a bounding rectangle for the + // glyphs of the current portion and the ascent of the current + // font + + // reset font size and proportion + rFnt.SetSize( aOldSize, rFnt.GetActual() ); + rFnt.SetProportion( nOldProp ); + + if ( bFirstGlyphRect ) + { + aCommonRect = aRect; + bFirstGlyphRect = sal_False; + } + else + aCommonRect.Union( aRect ); + + nIdx = nIdx + pCurrPart->GetLen(); + pCurrPart = pCurrPart->GetFollow(); + } + + // now we have a union ( aCommonRect ) of all glyphs with + // respect to a common baseline : 0 + + // get descent and ascent from union + if ( rInf.GetTxtFrm()->IsVertical() ) + { + nDescent = aCommonRect.Left(); + nAscent = aCommonRect.Right(); + + if ( nDescent < 0 ) + nDescent = -nDescent; + } + else + { + nDescent = aCommonRect.Bottom(); + nAscent = aCommonRect.Top(); + } + if ( nAscent < 0 ) + nAscent = -nAscent; + + const long nHght = nAscent + nDescent; + if ( nHght ) + { + if ( nHght > nWishedHeight ) + nMax = nFactor; + else + { + if ( bUseCache ) + aFactor[ nTmpIdx ] = (USHORT)nFactor; + nMin = nFactor; + } + + nFactor = ( nFactor * nWishedHeight ) / nHght; + bGrow = ( nFactor > nMin ) && ( nFactor < nMax ); +#if OSL_DEBUG_LEVEL > 1 + if ( bGrow ) + nGrow++; +#endif + nIdx = rInf.GetIdx(); + } + else + bGrow = sal_False; + } + + if ( bWinUsed ) + { + // reset window if it has been used + pWin->SetMapMode( aOldMap ); + pWin->SetFont( aOldFnt ); + } + + if ( bUseCache ) + aDescent[ nTmpIdx ] = -short( nDescent ); + } + + pCurrPart = pDrop->GetPart(); + + // did made any new calculations or did we use the cache? + if ( -1 == nFactor ) + { + nFactor = aFactor[ nTmpIdx ]; + nDescent = aDescent[ nTmpIdx ]; + } + else + nDescent = -nDescent; + + while ( pCurrPart ) + { + // scale current font + SwFont& rFnt = pCurrPart->GetFont(); + Size aNewSize( 0, ( nFactor * rFnt.GetHeight( rFnt.GetActual() ) ) / 1000 ); + + const BYTE nOldProp = rFnt.GetPropr(); + rFnt.SetProportion( 100 ); + rFnt.SetSize( aNewSize, rFnt.GetActual() ); + rFnt.SetProportion( nOldProp ); + + pCurrPart = pCurrPart->GetFollow(); + } + pDrop->SetY( (short)nDescent ); +} + +/************************************************************************* + * virtual Format() + *************************************************************************/ + +sal_Bool SwDropPortion::Format( SwTxtFormatInfo &rInf ) +{ + sal_Bool bFull = sal_False; + Fix( (USHORT)rInf.X() ); + + SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() ); + aLayoutModeModifier.SetAuto(); + + if( nDropHeight && pPart && nLines!=1 ) + { + if( !pDropCapCache ) + pDropCapCache = new SwDropCapCache(); + + // adjust font sizes to fit into the rectangle + pDropCapCache->CalcFontSize( this, rInf ); + + const long nOldX = rInf.X(); + { + SwDropSave aSave( rInf ); + SwDropPortionPart* pCurrPart = pPart; + + while ( pCurrPart ) + { + rInf.SetLen( pCurrPart->GetLen() ); + SwFont& rFnt = pCurrPart->GetFont(); + { + SwFontSave aFontSave( rInf, &rFnt ); + bFull = FormatTxt( rInf ); + + if ( bFull ) + break; + } + + const SwTwips nTmpWidth = + ( InSpaceGrp() && rInf.GetSpaceAdd() ) ? + Width() + CalcSpacing( rInf.GetSpaceAdd(), rInf ) : + Width(); + + // set values + pCurrPart->SetWidth( (USHORT)nTmpWidth ); + + // Move + rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() ); + rInf.X( rInf.X() + nTmpWidth ); + pCurrPart = pCurrPart->GetFollow(); + } + + Width( (USHORT)(rInf.X() - nOldX) ); + } + + // reset my length + SetLen( rInf.GetLen() ); + + // 7631, 7633: bei Ueberlappungen mit Flys ist Schluss. + if( ! bFull ) + bFull = lcl_IsDropFlyInter( rInf, Width(), nDropHeight ); + + if( bFull ) + { + // Durch FormatTxt kann nHeight auf 0 gesetzt worden sein + if ( !Height() ) + Height( rInf.GetTxtHeight() ); + + // Jetzt noch einmal der ganze Spass + nDropHeight = nLines = 0; + delete pPart; + pPart = NULL; + + // meanwhile use normal formatting + bFull = SwTxtPortion::Format( rInf ); + } + else + rInf.SetDropInit( sal_True ); + + Height( rInf.GetTxtHeight() ); + SetAscent( rInf.GetAscent() ); + } + else + bFull = SwTxtPortion::Format( rInf ); + + if( bFull ) + nDistance = 0; + else + { + const KSHORT nWant = Width() + GetDistance(); + const KSHORT nRest = (USHORT)(rInf.Width() - rInf.X()); + if( ( nWant > nRest ) || + lcl_IsDropFlyInter( rInf, Width() + GetDistance(), nDropHeight ) ) + nDistance = 0; + + Width( Width() + nDistance ); + } + return bFull; +} + diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx new file mode 100644 index 000000000000..cc549ae4ef00 --- /dev/null +++ b/sw/source/core/text/txtfld.cxx @@ -0,0 +1,552 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtfld.cxx,v $ + * $Revision: 1.30.136.1 $ + * + * 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 <fmtfld.hxx> +#include <txtfld.hxx> +#include <charfmt.hxx> + +#include "viewsh.hxx" // NewFldPortion, GetDoc() +#include "doc.hxx" // NewFldPortion, GetSysFldType() +#include "rootfrm.hxx" // Info ueber virt. PageNumber +#include "pagefrm.hxx" // NewFldPortion, GetVirtPageNum() +#include "ndtxt.hxx" // NewNumberPortion, pHints->GetNum() +#include "fldbas.hxx" // SwField +#include "viewopt.hxx" // SwViewOptions +#include "flyfrm.hxx" //IsInBody() +#include "viewimp.hxx" +#include "txtatr.hxx" // SwTxtFld +#include "txtcfg.hxx" +#include "swfont.hxx" // NewFldPortion, new SwFont +#include "fntcache.hxx" // NewFldPortion, SwFntAccess +#include "porfld.hxx" +#include "porftn.hxx" // NewExtraPortion +#include "porref.hxx" // NewExtraPortion +#include "portox.hxx" // NewExtraPortion +#include "porhyph.hxx" // NewExtraPortion +#include "porfly.hxx" // NewExtraPortion +#include "itrform2.hxx" // SwTxtFormatter +#include "chpfld.hxx" +#include "dbfld.hxx" +#include "expfld.hxx" +#include "docufld.hxx" +#include "pagedesc.hxx" // NewFldPortion, GetNum() +#include <pormulti.hxx> // SwMultiPortion +#include "fmtmeta.hxx" // lcl_NewMetaPortion + + +/************************************************************************* + * SwTxtFormatter::NewFldPortion() + *************************************************************************/ + + +sal_Bool lcl_IsInBody( SwFrm *pFrm ) +{ + if ( pFrm->IsInDocBody() ) + return sal_True; + else + { + const SwFrm *pTmp = pFrm; + const SwFlyFrm *pFly; + while ( 0 != (pFly = pTmp->FindFlyFrm()) ) + pTmp = pFly->GetAnchorFrm(); + return pTmp->IsInDocBody(); + } +} + + +SwExpandPortion *SwTxtFormatter::NewFldPortion( SwTxtFormatInfo &rInf, + const SwTxtAttr *pHint ) const +{ + SwExpandPortion *pRet = 0; + SwFrm *pFrame = (SwFrm*)pFrm; + SwField *pFld = (SwField*)pHint->GetFld().GetFld(); + const sal_Bool bName = rInf.GetOpt().IsFldName(); + + SwCharFmt* pChFmt = 0; + sal_Bool bNewFlyPor = sal_False, + bINet = sal_False; + + // set language + ((SwTxtFormatter*)this)->SeekAndChg( rInf ); + if (pFld->GetLanguage() != GetFnt()->GetLanguage()) + { + pFld->SetLanguage( GetFnt()->GetLanguage() ); + // let the visual note know about its new language + if (pFld->GetTyp()->Which()==RES_POSTITFLD) + const_cast<SwFmtFld*> (&pHint->GetFld())->Broadcast( SwFmtFldHint( &pHint->GetFld(), SWFMTFLD_LANGUAGE ) ); + } + + ViewShell *pSh = rInf.GetVsh(); + sal_Bool bPlaceHolder = sal_False; + + switch( pFld->GetTyp()->Which() ) + { + case RES_SCRIPTFLD: + case RES_POSTITFLD: + pRet = new SwPostItsPortion( RES_SCRIPTFLD == pFld->GetTyp()->Which() ); + break; + + case RES_COMBINED_CHARS: + { + String sStr( pFld->GetCntnt( bName )); + if( bName ) + pRet = new SwFldPortion( sStr ); + else + pRet = new SwCombinedPortion( sStr ); + } + break; + + case RES_HIDDENTXTFLD: + pRet = new SwHiddenPortion(pFld->GetCntnt( bName )); + break; + + case RES_CHAPTERFLD: + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFlds() ) + { + ((SwChapterField*)pFld)->ChangeExpansion( pFrame, + &((SwTxtFld*)pHint)->GetTxtNode() ); + } + pRet = new SwFldPortion( pFld->GetCntnt( bName ) ); + break; + + case RES_DOCSTATFLD: + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFlds() ) + ((SwDocStatField*)pFld)->ChangeExpansion( pFrame ); + pRet = new SwFldPortion( pFld->GetCntnt( bName ) ); + break; + + case RES_PAGENUMBERFLD: + { + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFlds() ) + { + SwPageNumberFieldType *pPageNr = (SwPageNumberFieldType *)pFld->GetTyp(); + + const SwRootFrm* pTmpRootFrm = pSh->GetLayout(); + const sal_Bool bVirt = pTmpRootFrm->IsVirtPageNum(); + + SwDoc* pDoc = pSh->GetDoc(); + MSHORT nVirtNum = pFrame->GetVirtPageNum(); + MSHORT nNumPages = pTmpRootFrm->GetPageNum(); + sal_Int16 nNumFmt = -1; + if(SVX_NUM_PAGEDESC == pFld->GetFormat()) + nNumFmt = pFrame->FindPageFrm()->GetPageDesc()->GetNumType().GetNumberingType(); + + pPageNr->ChangeExpansion( pDoc, nVirtNum, nNumPages, + bVirt, nNumFmt > -1 ? &nNumFmt : 0); + } + pRet = new SwFldPortion( pFld->GetCntnt( bName ) ); + break; + } + case RES_GETEXPFLD: + { + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFlds() ) + { + SwGetExpField* pExpFld = (SwGetExpField*)pFld; + if( !::lcl_IsInBody( pFrame ) ) + { + pExpFld->ChgBodyTxtFlag( sal_False ); + pExpFld->ChangeExpansion( *pFrame, *((SwTxtFld*)pHint) ); + } + else if( !pExpFld->IsInBodyTxt() ) + { + // war vorher anders, also erst expandieren, dann umsetzen!! + pExpFld->ChangeExpansion( *pFrame, *((SwTxtFld*)pHint) ); + pExpFld->ChgBodyTxtFlag( sal_True ); + } + } + pRet = new SwFldPortion( pFld->GetCntnt( bName ) ); + break; + } + case RES_DBFLD: + { + if( !bName ) + { + SwDBField* pDBFld = (SwDBField*)pFld; + pDBFld->ChgBodyTxtFlag( ::lcl_IsInBody( pFrame ) ); +/* Solange das ChangeExpansion auskommentiert ist. + * Aktualisieren in Kopf/Fuszeilen geht aktuell nicht. + if( !::lcl_IsInBody( pFrame ) ) + { + pDBFld->ChgBodyTxtFlag( sal_False ); + pDBFld->ChangeExpansion( pFrame, (SwTxtFld*)pHint ); + } + else if( !pDBFld->IsInBodyTxt() ) + { + // war vorher anders, also erst expandieren, dann umsetzen!! + pDBFld->ChangeExpansion( pFrame, (SwTxtFld*)pHint ); + pDBFld->ChgBodyTxtFlag( sal_True ); + } +*/ + } + pRet = new SwFldPortion( pFld->GetCntnt( bName ) ); + break; + } + case RES_REFPAGEGETFLD: + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFlds() ) + ((SwRefPageGetField*)pFld)->ChangeExpansion( pFrame, (SwTxtFld*)pHint ); + pRet = new SwFldPortion( pFld->GetCntnt( bName ) ); + break; + + case RES_JUMPEDITFLD: + if( !bName ) + pChFmt = ((SwJumpEditField*)pFld)->GetCharFmt(); + bNewFlyPor = sal_True; + bPlaceHolder = sal_True; + break; + + default: + { + pRet = new SwFldPortion(pFld->GetCntnt( bName ) ); + } + } + + if( bNewFlyPor ) + { + SwFont *pTmpFnt = 0; + if( !bName ) + { + pTmpFnt = new SwFont( *pFnt ); + if( bINet ) + { + SwAttrPool* pPool = pChFmt->GetAttrSet().GetPool(); + SfxItemSet aSet( *pPool, RES_CHRATR_BEGIN, RES_CHRATR_END ); + SfxItemSet aTmpSet( aSet ); + pFrm->GetTxtNode()->GetAttr(aSet,rInf.GetIdx(),rInf.GetIdx()+1); + aTmpSet.Set( pChFmt->GetAttrSet() ); + aTmpSet.Differentiate( aSet ); + if( aTmpSet.Count() ) + pTmpFnt->SetDiffFnt( &aTmpSet, pFrm->GetTxtNode()->getIDocumentSettingAccess() ); + } + else + pTmpFnt->SetDiffFnt( &pChFmt->GetAttrSet(), pFrm->GetTxtNode()->getIDocumentSettingAccess() ); + } + pRet = new SwFldPortion( pFld->GetCntnt( bName ), pTmpFnt, bPlaceHolder ); + } + + return pRet; +} + +/************************************************************************* + * SwTxtFormatter::TryNewNoLengthPortion() + *************************************************************************/ + +SwFldPortion * lcl_NewMetaPortion(SwTxtAttr & rHint, const bool bPrefix) +{ + ::sw::Meta *const pMeta( + static_cast<SwFmtMeta &>(rHint.GetAttr()).GetMeta() ); + ::rtl::OUString fix; + ::sw::MetaField *const pField( dynamic_cast< ::sw::MetaField * >(pMeta) ); + OSL_ENSURE(pField, "lcl_NewMetaPortion: no meta field?"); + if (pField) + { + pField->GetPrefixAndSuffix((bPrefix) ? &fix : 0, (bPrefix) ? 0 : &fix); + } + return new SwFldPortion( fix ); +} + +/** Try to create a new portion with zero length, for an end of a hint + (where there is no CH_TXTATR). Because there may be multiple hint ends at a + given index, m_nHintEndIndex is used to keep track of the already created + portions. But the portions created here may actually be deleted again, + due to UnderFlow. In that case, m_nHintEndIndex must be decremented, + so the portion will be created again on the next line. + */ +SwExpandPortion * +SwTxtFormatter::TryNewNoLengthPortion(SwTxtFormatInfo & rInfo) +{ + if (pHints) + { + const xub_StrLen nIdx(rInfo.GetIdx()); + while (m_nHintEndIndex < pHints->GetEndCount()) + { + SwTxtAttr & rHint( *pHints->GetEnd(m_nHintEndIndex) ); + xub_StrLen const nEnd( *rHint.GetAnyEnd() ); + if (nEnd > nIdx) + { + break; + } + ++m_nHintEndIndex; + if (nEnd == nIdx) + { + if (RES_TXTATR_METAFIELD == rHint.Which()) + { + SwFldPortion *const pPortion( + lcl_NewMetaPortion(rHint, false)); + pPortion->SetNoLength(); // no CH_TXTATR at hint end! + return pPortion; + } + } + } + } + return 0; +} + +/************************************************************************* + * SwTxtFormatter::NewExtraPortion() + *************************************************************************/ + +SwLinePortion *SwTxtFormatter::NewExtraPortion( SwTxtFormatInfo &rInf ) +{ + SwTxtAttr *pHint = GetAttr( rInf.GetIdx() ); + SwLinePortion *pRet = 0; + if( !pHint ) + { +#if OSL_DEBUG_LEVEL > 1 +// aDbstream << "NewExtraPortion: hint not found?" << endl; +#endif + pRet = new SwTxtPortion; + pRet->SetLen( 1 ); + rInf.SetLen( 1 ); + return pRet; + } + + switch( pHint->Which() ) + { + case RES_TXTATR_FLYCNT : + { + pRet = NewFlyCntPortion( rInf, pHint ); + break; + } + case RES_TXTATR_FTN : + { + pRet = NewFtnPortion( rInf, pHint ); + break; + } + case RES_TXTATR_FIELD : + { + pRet = NewFldPortion( rInf, pHint ); + break; + } + case RES_TXTATR_REFMARK : + { + pRet = new SwIsoRefPortion; + break; + } + case RES_TXTATR_TOXMARK : + { + pRet = new SwIsoToxPortion; + break; + } + case RES_TXTATR_METAFIELD: + { + pRet = lcl_NewMetaPortion( *pHint, true ); + break; + } + default: ; + } + if( !pRet ) + { +#if OSL_DEBUG_LEVEL > 1 +// aDbstream << "NewExtraPortion: unknown hint" << endl; +#endif + const XubString aNothing; + pRet = new SwFldPortion( aNothing ); + rInf.SetLen( 1 ); + } + return pRet; +} + +/************************************************************************* + * SwTxtFormatter::NewNumberPortion() + *************************************************************************/ + + +SwNumberPortion *SwTxtFormatter::NewNumberPortion( SwTxtFormatInfo &rInf ) const +{ + if( rInf.IsNumDone() || rInf.GetTxtStart() != nStart + || rInf.GetTxtStart() != rInf.GetIdx() ) + return 0; + + SwNumberPortion *pRet = 0; + const SwTxtNode* pTxtNd = GetTxtFrm()->GetTxtNode(); + const SwNumRule* pNumRule = pTxtNd->GetNumRule(); + + // hat ein "gueltige" Nummer ? + if( pTxtNd->IsNumbered() && pTxtNd->IsCountedInList()) + { + const SwNumFmt &rNumFmt = pNumRule->Get( static_cast<USHORT>(pTxtNd->GetActualListLevel()) ); + const sal_Bool bLeft = SVX_ADJUST_LEFT == rNumFmt.GetNumAdjust(); + const sal_Bool bCenter = SVX_ADJUST_CENTER == rNumFmt.GetNumAdjust(); + // --> OD 2008-01-23 #newlistlevelattrs# + const bool bLabelAlignmentPosAndSpaceModeActive( + rNumFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ); + const KSHORT nMinDist = bLabelAlignmentPosAndSpaceModeActive + ? 0 : rNumFmt.GetCharTextDistance(); + // <-- + + if( SVX_NUM_BITMAP == rNumFmt.GetNumberingType() ) + { + // --> OD 2008-01-23 #newlistlevelattrs# + pRet = new SwGrfNumPortion( (SwFrm*)GetTxtFrm(), + pTxtNd->GetLabelFollowedBy(), + rNumFmt.GetBrush(), + rNumFmt.GetGraphicOrientation(), + rNumFmt.GetGraphicSize(), + bLeft, bCenter, nMinDist, + bLabelAlignmentPosAndSpaceModeActive ); + // <-- + long nTmpA = rInf.GetLast()->GetAscent(); + long nTmpD = rInf.GetLast()->Height() - nTmpA; + if( !rInf.IsTest() ) + ((SwGrfNumPortion*)pRet)->SetBase( nTmpA, nTmpD, nTmpA, nTmpD ); + } + else + { + // Der SwFont wird dynamisch angelegt und im CTOR uebergeben, + // weil das CharFmt nur einen SV-Font zurueckliefert. + // Im Dtor vom SwNumberPortion wird der SwFont deletet. + SwFont *pNumFnt = 0; + const SwAttrSet* pFmt = rNumFmt.GetCharFmt() ? + &rNumFmt.GetCharFmt()->GetAttrSet() : + NULL; + const IDocumentSettingAccess* pIDSA = pTxtNd->getIDocumentSettingAccess(); + + if( SVX_NUM_CHAR_SPECIAL == rNumFmt.GetNumberingType() ) + { + const Font *pFmtFnt = rNumFmt.GetBulletFont(); + + // + // Build a new bullet font basing on the current paragraph font: + // + pNumFnt = new SwFont( &rInf.GetCharAttr(), pIDSA ); + + // --> FME 2005-08-11 #i53199# + if ( !pIDSA->get(IDocumentSettingAccess::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) + { + // i18463: + // Underline style of paragraph font should not be considered + // Overline style of paragraph font should not be considered + // Weight style of paragraph font should not be considered + // Posture style of paragraph font should not be considered + pNumFnt->SetUnderline( UNDERLINE_NONE ); + pNumFnt->SetOverline( UNDERLINE_NONE ); + pNumFnt->SetItalic( ITALIC_NONE, SW_LATIN ); + pNumFnt->SetItalic( ITALIC_NONE, SW_CJK ); + pNumFnt->SetItalic( ITALIC_NONE, SW_CTL ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SW_LATIN ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CJK ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CTL ); + } + + // + // Apply the explicit attributes from the character style + // associated with the numering to the new bullet font. + // + if( pFmt ) + pNumFnt->SetDiffFnt( pFmt, pIDSA ); + + if ( pFmtFnt ) + { + const BYTE nAct = pNumFnt->GetActual(); + pNumFnt->SetFamily( pFmtFnt->GetFamily(), nAct ); + pNumFnt->SetName( pFmtFnt->GetName(), nAct ); + pNumFnt->SetStyleName( pFmtFnt->GetStyleName(), nAct ); + pNumFnt->SetCharSet( pFmtFnt->GetCharSet(), nAct ); + pNumFnt->SetPitch( pFmtFnt->GetPitch(), nAct ); + } + + // we do not allow a vertical font + pNumFnt->SetVertical( pNumFnt->GetOrientation(), + pFrm->IsVertical() ); + + // --> OD 2008-01-23 #newlistelevelattrs# + pRet = new SwBulletPortion( rNumFmt.GetBulletChar(), + pTxtNd->GetLabelFollowedBy(), + pNumFnt, + bLeft, bCenter, nMinDist, + bLabelAlignmentPosAndSpaceModeActive ); + // <-- + } + else + { + // --> OD 2006-06-02 #b6432095# + // use method <SwNumRule::MakeNumString(..)> instead of + // method <SwTxtNode::GetNumString()>, because for levels with + // numbering none the prefix and the suffix strings have to be provided. +// XubString aTxt( pTxtNd->GetNumString() ); + XubString aTxt( pNumRule->MakeNumString( *(pTxtNd->GetNum()) ) ); + // <-- + // --> OD 2008-01-23 #newlistlevelattrs# + if ( aTxt.Len() > 0 ) + { + aTxt.Insert( pTxtNd->GetLabelFollowedBy() ); + } + // <-- + + // 7974: Nicht nur eine Optimierung... + // Eine Numberportion ohne Text wird die Breite von 0 + // erhalten. Die nachfolgende Textportion wird im BreakLine + // in das BreakCut laufen, obwohl rInf.GetLast()->GetFlyPortion() + // vorliegt! + if( aTxt.Len() ) + { + // + // Build a new numbering font basing on the current paragraph font: + // + pNumFnt = new SwFont( &rInf.GetCharAttr(), pIDSA ); + + // --> FME 2005-08-11 #i53199# + if ( !pIDSA->get(IDocumentSettingAccess::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) + { + // i18463: + // Underline style of paragraph font should not be considered + pNumFnt->SetUnderline( UNDERLINE_NONE ); + // Overline style of paragraph font should not be considered + pNumFnt->SetOverline( UNDERLINE_NONE ); + } + + + // + // Apply the explicit attributes from the character style + // associated with the numering to the new bullet font. + // + if( pFmt ) + pNumFnt->SetDiffFnt( pFmt, pIDSA ); + + // we do not allow a vertical font + pNumFnt->SetVertical( pNumFnt->GetOrientation(), pFrm->IsVertical() ); + + // --> OD 2008-01-23 #newlistlevelattrs# + pRet = new SwNumberPortion( aTxt, pNumFnt, + bLeft, bCenter, nMinDist, + bLabelAlignmentPosAndSpaceModeActive ); + // <-- + } + } + } + } + return pRet; +} + diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx new file mode 100644 index 000000000000..706eca2ec52e --- /dev/null +++ b/sw/source/core/text/txtfly.cxx @@ -0,0 +1,2520 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtfly.cxx,v $ + * $Revision: 1.65.22.1 $ + * + * 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 _VIRDEV_HXX //autogen +#include <vcl/virdev.hxx> +#endif + +#include "viewsh.hxx" +#include "pagefrm.hxx" +#include "rootfrm.hxx" +#include "viewimp.hxx" // SwViewImp +#include "pam.hxx" // SwPosition +#include "swregion.hxx" // SwRegionRects +#include "dcontact.hxx" // SwContact +#include "dflyobj.hxx" // SdrObject +#include "flyfrm.hxx" // SwFlyFrm +#include "frmtool.hxx" // ::DrawGraphic +#include "porfld.hxx" // SwGrfNumPortion +#include "txtfrm.hxx" // SwTxtFrm +#include "itrform2.hxx" // SwTxtFormatter +#include "porfly.hxx" // NewFlyCntPortion +#include "porfld.hxx" // SwGrfNumPortion +#include "txtfly.hxx" // SwTxtFly +#include "txtpaint.hxx" // SwSaveClip +#include "txtatr.hxx" // SwTxtFlyCnt +#include "txtcfg.hxx" +#include "notxtfrm.hxx" +#include "flyfrms.hxx" +#include "fmtcnct.hxx" // SwFmtChain +#include <pormulti.hxx> // SwMultiPortion +#ifdef VERT_DISTANCE +#include <math.h> +#endif +#include <svx/obj3d.hxx> + +#ifndef _TXTRANGE_HXX //autogen +#include <svx/txtrange.hxx> +#endif +#include <svx/lrspitem.hxx> +#include <svx/ulspitem.hxx> +// --> OD 2004-06-16 #i28701# +#include <svx/lspcitem.hxx> +// <-- +#include <txtflcnt.hxx> +#include <fmtsrnd.hxx> +#include <fmtanchr.hxx> +#include <fmtflcnt.hxx> +#include <frmfmt.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <sortedobjs.hxx> +#include <layouter.hxx> +#ifndef IDOCUMENTDRAWMODELACCESS_HXX_INCLUDED +#include <IDocumentDrawModelAccess.hxx> +#endif +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <svx/obj3d.hxx> +#ifndef _TXTRANGE_HXX //autogen +#include <svx/txtrange.hxx> +#endif +#include <svx/lrspitem.hxx> +#include <svx/ulspitem.hxx> +#include <svx/lspcitem.hxx> +#include <svx/svdoedge.hxx> + + +#ifndef PRODUCT +#include "viewopt.hxx" // SwViewOptions, nur zum Testen (Test2) +#endif +#include "doc.hxx" + +#ifdef VERT_DISTANCE +#include <math.h> +#endif + +using namespace ::com::sun::star; + +/***************************************************************************** + * Beschreibung: + * Die Klasse SwTxtFly soll die Universalschnittstelle zwischen der + * Formatierung/Textausgabe und den u.U. ueberlappenden freifliegenden + * Frames sein. + * Waehrend der Formatierung erkundigt sich der Formatierer beim SwTxtFly, + * ob ein bestimmter Bereich durch die Attribute eines ueberlappenden + * Frames vorliegt. Solche Bereiche werden in Form von Dummy-Portions + * abgebildet. + * Die gesamte Textausgabe und Retusche wird ebenfalls an ein SwTxtFly + * weitergeleitet. Dieser entscheidet, ob Textteile geclippt werden muessen + * und zerteilt z.B. die Bereiche bei einem DrawRect. + * Zu beachten ist, dass alle freifliegenden Frames in einem nach TopLeft + * sortiertem PtrArray an der Seite zu finden sind. Intern wird immer nur + * in dokumentglobalen Werten gerechnet. Die IN- und OUT-Parameter sind + * jedoch in den meisten Faellen an die Beduerfnisse des LineIters + * zugeschnitten, d.h. sie werden in frame- oder windowlokalen Koordinaten + * konvertiert. + * Wenn mehrere Frames mit Umlaufattributen in einer Zeile liegen, + * ergeben sich unterschiedliche Auswirkungen fuer den Textfluss: + * + * L/R P L R K + * P -P-P- -P-L -P R- -P K + * L -L P- -L L -L R- -L K + * R R-P- R-L R R- R K + * K K P- K L K R- K K + * + * (P=parallel, L=links, R=rechts, K=kein Umlauf) + * + * Das Verhalten so beschreiben: + * Jeder Rahmen kann Text verdraengen, wobei der Einfluss allerdings nur + * bis zum naechsten Rahmen reicht. + *****************************************************************************/ + +void SwTxtFormatter::CalcUnclipped( SwTwips& rTop, SwTwips& rBottom ) +{ + ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), + "SwTxtFormatter::CalcUnclipped with unswapped frame" ) + + long nFlyAsc, nFlyDesc; + // OD 08.01.2004 #i11859# - use new method <SwLineLayout::MaxAscentDescent(..)> + //lcl_MaxAscDescent( pCurr, rTop, rBottom, nFlyAsc, nFlyDesc ); + pCurr->MaxAscentDescent( rTop, rBottom, nFlyAsc, nFlyDesc ); + rTop = Y() + GetCurr()->GetAscent(); + rBottom = rTop + nFlyDesc; + rTop -= nFlyAsc; +} + +/************************************************************************* + * SwTxtFormatter::UpdatePos() aktualisiert die Referenzpunkte der zeichengeb. + * Objekte, z. B. nach Adjustierung ( rechtsbuendig, Blocksatz etc. ) + * ( hauptsaechlich Korrrektur der X-Position ) + *************************************************************************/ + +void SwTxtFormatter::UpdatePos( SwLineLayout *pCurrent, Point aStart, + xub_StrLen nStartIdx, sal_Bool bAllWays ) const +{ + ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), + "SwTxtFormatter::UpdatePos with unswapped frame" ) + + if( GetInfo().IsTest() ) + return; + SwLinePortion *pFirst = pCurrent->GetFirstPortion(); + SwLinePortion *pPos = pFirst; + SwTxtPaintInfo aTmpInf( GetInfo() ); + aTmpInf.SetpSpaceAdd( pCurrent->GetpLLSpaceAdd() ); + aTmpInf.ResetSpaceIdx(); + aTmpInf.SetKanaComp( pCurrent->GetpKanaComp() ); + aTmpInf.ResetKanaIdx(); + + // Die Groesse des Frames + aTmpInf.SetIdx( nStartIdx ); + aTmpInf.SetPos( aStart ); + + long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; + // OD 08.01.2004 #i11859# - use new method <SwLineLayout::MaxAscentDescent(..)> + //lcl_MaxAscDescent( pPos, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); + pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); + + KSHORT nTmpHeight = pCurrent->GetRealHeight(); + KSHORT nAscent = pCurrent->GetAscent() + nTmpHeight - pCurrent->Height(); + objectpositioning::AsCharFlags nFlags = AS_CHAR_ULSPACE; + if( GetMulti() ) + { + aTmpInf.SetDirection( GetMulti()->GetDirection() ); + if( GetMulti()->HasRotation() ) + { + nFlags |= AS_CHAR_ROTATE; + if( GetMulti()->IsRevers() ) + { + nFlags |= AS_CHAR_REVERSE; + aTmpInf.X( aTmpInf.X() - nAscent ); + } + else + aTmpInf.X( aTmpInf.X() + nAscent ); + } + else + { + if ( GetMulti()->IsBidi() ) + nFlags |= AS_CHAR_BIDI; + aTmpInf.Y( aTmpInf.Y() + nAscent ); + } + } + else + aTmpInf.Y( aTmpInf.Y() + nAscent ); + + while( pPos ) + { + // bislang ist mir nur ein Fall bekannt, wo die Positionsaenderung + // (verursacht durch das Adjustment) fuer eine Portion wichtig + // sein koennte: Bei FlyCntPortions muss ein SetRefPoint erfolgen. + if( ( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() ) + && ( bAllWays || !IsQuick() ) ) + { + // OD 08.01.2004 #i11859# - use new method <SwLineLayout::MaxAscentDescent(..)> + //lcl_MaxAscDescent( pFirst, nTmpAscent, nTmpDescent, + // nFlyAsc, nFlyDesc, pPos ); + pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos ); + + if( pPos->IsGrfNumPortion() ) + { + if( !nFlyAsc && !nFlyDesc ) + { + nTmpAscent = nAscent; + nFlyAsc = nAscent; + nTmpDescent = nTmpHeight - nAscent; + nFlyDesc = nTmpDescent; + } + ((SwGrfNumPortion*)pPos)->SetBase( nTmpAscent, nTmpDescent, + nFlyAsc, nFlyDesc ); + } + else + { + Point aBase( aTmpInf.GetPos() ); + if ( GetInfo().GetTxtFrm()->IsVertical() ) + GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aBase ); + + ((SwFlyCntPortion*)pPos)->SetBase( *aTmpInf.GetTxtFrm(), + aBase, nTmpAscent, nTmpDescent, nFlyAsc, + nFlyDesc, nFlags ); + } + } + if( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasFlyInCntnt() ) + { + ASSERT( !GetMulti(), "Too much multi" ); + ((SwTxtFormatter*)this)->pMulti = (SwMultiPortion*)pPos; + SwLineLayout *pLay = &GetMulti()->GetRoot(); + Point aSt( aTmpInf.X(), aStart.Y() ); + + if ( GetMulti()->HasBrackets() ) + { + ASSERT( GetMulti()->IsDouble(), "Brackets only for doubles"); + aSt.X() += ((SwDoubleLinePortion*)GetMulti())->PreWidth(); + } + else if( GetMulti()->HasRotation() ) + { + aSt.Y() += pCurrent->GetAscent() - GetMulti()->GetAscent(); + if( GetMulti()->IsRevers() ) + aSt.X() += GetMulti()->Width(); + else + aSt.Y() += GetMulti()->Height(); + } + else if ( GetMulti()->IsBidi() ) + // jump to end of the bidi portion + aSt.X() += pLay->Width(); + + xub_StrLen nStIdx = aTmpInf.GetIdx(); + do + { + UpdatePos( pLay, aSt, nStIdx, bAllWays ); + nStIdx = nStIdx + pLay->GetLen(); + aSt.Y() += pLay->Height(); + pLay = pLay->GetNext(); + } while ( pLay ); + ((SwTxtFormatter*)this)->pMulti = NULL; + } + pPos->Move( aTmpInf ); + pPos = pPos->GetPortion(); + } +} + +/************************************************************************* + * SwTxtFormatter::AlignFlyInCntBase() + * richtet die zeichengeb. Objekte in Y-Richtung ggf. neu aus. + *************************************************************************/ + +void SwTxtFormatter::AlignFlyInCntBase( long nBaseLine ) const +{ + ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), + "SwTxtFormatter::AlignFlyInCntBase with unswapped frame" ) + + if( GetInfo().IsTest() ) + return; + SwLinePortion *pFirst = pCurr->GetFirstPortion(); + SwLinePortion *pPos = pFirst; + objectpositioning::AsCharFlags nFlags = AS_CHAR_NOFLAG; + if( GetMulti() && GetMulti()->HasRotation() ) + { + nFlags |= AS_CHAR_ROTATE; + if( GetMulti()->IsRevers() ) + nFlags |= AS_CHAR_REVERSE; + } + + long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; + + while( pPos ) + { + if( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() ) + { + // OD 08.01.2004 #i11859# - use new method <SwLineLayout::MaxAscentDescent(..)> + //lcl_MaxAscDescent( pFirst, nTmpAscent, nTmpDescent, + // nFlyAsc, nFlyDesc, pPos ); + pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos ); + + if( pPos->IsGrfNumPortion() ) + ((SwGrfNumPortion*)pPos)->SetBase( nTmpAscent, nTmpDescent, + nFlyAsc, nFlyDesc ); + else + { + Point aBase; + if ( GetInfo().GetTxtFrm()->IsVertical() ) + { + nBaseLine = GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( nBaseLine ); + aBase = Point( nBaseLine, ((SwFlyCntPortion*)pPos)->GetRefPoint().Y() ); + } + else + aBase = Point( ((SwFlyCntPortion*)pPos)->GetRefPoint().X(), nBaseLine ); + + ((SwFlyCntPortion*)pPos)->SetBase( *GetInfo().GetTxtFrm(), aBase, nTmpAscent, nTmpDescent, + nFlyAsc, nFlyDesc, nFlags ); + } + } + pPos = pPos->GetPortion(); + } +} + +/************************************************************************* + * SwTxtFly::ChkFlyUnderflow() + * This is called after the real height of the line has been calculated + * Therefore it is possible, that more flys from below intersect with the + * line, or that flys from above do not intersect with the line anymore + * We check this and return true if so, meaning that the line has to be + * formatted again + *************************************************************************/ + +sal_Bool SwTxtFormatter::ChkFlyUnderflow( SwTxtFormatInfo &rInf ) const +{ + ASSERT( rInf.GetTxtFly()->IsOn(), "SwTxtFormatter::ChkFlyUnderflow: why?" ); + if( GetCurr() ) + { + // Erst pruefen wir, ob ueberhaupt ein Fly mit der Zeile ueberlappt. + // = GetLineHeight() + const long nHeight = GetCurr()->GetRealHeight(); + SwRect aLine( GetLeftMargin(), Y(), rInf.RealWidth(), nHeight ); + + SwRect aLineVert( aLine ); + if ( pFrm->IsVertical() ) + pFrm->SwitchHorizontalToVertical( aLineVert ); + SwRect aInter( rInf.GetTxtFly()->GetFrm( aLineVert ) ); + if ( pFrm->IsVertical() ) + pFrm->SwitchVerticalToHorizontal( aInter ); + + if( !aInter.HasArea() ) + return sal_False; + + // Nun ueberpruefen wir jede Portion, die sich haette senken koennen, + // ob sie mit dem Fly ueberlappt. + const SwLinePortion *pPos = GetCurr()->GetFirstPortion(); + aLine.Pos().Y() = Y() + GetCurr()->GetRealHeight() - GetCurr()->Height(); + aLine.Height( GetCurr()->Height() ); + + while( pPos ) + { + aLine.Width( pPos->Width() ); + + aLineVert = aLine; + if ( pFrm->IsVertical() ) + pFrm->SwitchHorizontalToVertical( aLineVert ); + aInter = rInf.GetTxtFly()->GetFrm( aLineVert ); + if ( pFrm->IsVertical() ) + pFrm->SwitchVerticalToHorizontal( aInter ); + + // new flys from below? + if( !pPos->IsFlyPortion() ) + { + if( aInter.IsOver( aLine ) ) + { + aInter._Intersection( aLine ); + if( aInter.HasArea() ) + { + // to be evaluated during reformat of this line: + // RealHeight including spacing + rInf.SetLineHeight( KSHORT(nHeight) ); + // Height without extra spacing + rInf.SetLineNettoHeight( KSHORT( pCurr->Height() ) ); + return sal_True; + } + } + } + else + { + // the fly portion is not anylonger intersected by a fly + if ( ! aInter.IsOver( aLine ) ) + { + rInf.SetLineHeight( KSHORT(nHeight) ); + rInf.SetLineNettoHeight( KSHORT( pCurr->Height() ) ); + return sal_True; + } + else + { + aInter._Intersection( aLine ); + + // no area means a fly has become invalid because of + // lowering the line => reformat the line + // we also have to reformat the line, if the fly size + // differs from the intersection intervals size + if( ! aInter.HasArea() || + ((SwFlyPortion*)pPos)->GetFixWidth() != aInter.Width() ) + { + rInf.SetLineHeight( KSHORT(nHeight) ); + rInf.SetLineNettoHeight( KSHORT( pCurr->Height() ) ); + return sal_True; + } + } + } + + aLine.Left( aLine.Left() + pPos->Width() ); + pPos = pPos->GetPortion(); + } + } + return sal_False; +} + +/************************************************************************* + * SwTxtFormatter::CalcFlyWidth() + * ermittelt das naechste Objekt, das in die restliche Zeile ragt und + * konstruiert die zugehoerige FlyPortion. + * Dazu wird SwTxtFly.GetFrm(..) benutzt. + *************************************************************************/ + +// Durch Flys kann sich der rechte Rand verkuerzen. + +void SwTxtFormatter::CalcFlyWidth( SwTxtFormatInfo &rInf ) +{ + if( GetMulti() || rInf.GetFly() ) + return; + + SwTxtFly *pTxtFly = rInf.GetTxtFly(); + if( !pTxtFly->IsOn() || rInf.IsIgnoreFly() ) + return; + + const SwLinePortion *pLast = rInf.GetLast(); + + long nAscent; + long nTop = Y(); + long nHeight; + + if( rInf.GetLineHeight() ) + { + // real line height has already been calculated, we only have to + // search for intersections in the lower part of the strip + nAscent = pCurr->GetAscent(); + nHeight = rInf.GetLineNettoHeight(); + nTop += rInf.GetLineHeight() - nHeight; + } + else + { + nAscent = pLast->GetAscent(); + nHeight = pLast->Height(); + + // we make a first guess for the lines real height + if ( ! pCurr->GetRealHeight() ) + CalcRealHeight(); + + if ( pCurr->GetRealHeight() > nHeight ) + nTop += pCurr->GetRealHeight() - nHeight; + else + // important for fixed space between lines + nHeight = pCurr->GetRealHeight(); + } + + const long nLeftMar = GetLeftMargin(); + const long nLeftMin = (rInf.X() || GetDropLeft()) ? nLeftMar : GetLeftMin(); + + SwRect aLine( rInf.X() + nLeftMin, nTop, rInf.RealWidth() - rInf.X() + + nLeftMar - nLeftMin , nHeight ); + + SwRect aLineVert( aLine ); + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( aLineVert ); + + if ( pFrm->IsVertical() ) + pFrm->SwitchHorizontalToVertical( aLineVert ); + SwRect aInter( pTxtFly->GetFrm( aLineVert ) ); + + if ( pFrm->IsRightToLeft() ) + pFrm->SwitchRTLtoLTR( aInter ); + + if ( pFrm->IsVertical() ) + pFrm->SwitchVerticalToHorizontal( aInter ); + + if( aInter.IsOver( aLine ) ) + { + aLine.Left( rInf.X() + nLeftMar ); + sal_Bool bForced = sal_False; + if( aInter.Left() <= nLeftMin ) + { + SwTwips nFrmLeft = GetTxtFrm()->Frm().Left(); + if( GetTxtFrm()->Prt().Left() < 0 ) + nFrmLeft += GetTxtFrm()->Prt().Left(); + if( aInter.Left() < nFrmLeft ) + aInter.Left( nFrmLeft ); + + long nAddMar = 0; + if ( pFrm->IsRightToLeft() ) + { + nAddMar = pFrm->Frm().Right() - Right(); + if ( nAddMar < 0 ) + nAddMar = 0; + } + else + nAddMar = nLeftMar - nFrmLeft; + + aInter.Width( aInter.Width() + nAddMar ); + // Bei negativem Erstzeileneinzug setzen wir das Flag, + // um anzuzeigen, dass der Einzug/Rand verschoben wurde + // Dies muss beim DefaultTab an der Nullposition beruecksichtigt + // werden. + if( IsFirstTxtLine() && HasNegFirst() ) + bForced = sal_True; + } + aInter.Intersection( aLine ); + if( !aInter.HasArea() ) + return; + + const sal_Bool bFullLine = aLine.Left() == aInter.Left() && + aLine.Right() == aInter.Right(); + + // Obwohl kein Text mehr da ist, muss eine weitere Zeile + // formatiert werden, weil auch leere Zeilen einem Fly + // ohne Umlauf ausweichen muessen. + if( bFullLine && rInf.GetIdx() == rInf.GetTxt().Len() ) + { + rInf.SetNewLine( sal_True ); + // 8221: Dummies erkennt man an Ascent == Height + pCurr->SetDummy(sal_True); + } + + // aInter wird framelokal + aInter.Pos().X() -= nLeftMar; + SwFlyPortion *pFly = new SwFlyPortion( aInter ); + if( bForced ) + { + pCurr->SetForcedLeftMargin( sal_True ); + rInf.ForcedLeftMargin( (USHORT)aInter.Width() ); + } + + if( bFullLine ) + { + // 8110: wir muessen um Einheiten von Zeilenhoehen anwachsen, + // um nebeneinanderliegende Flys mit unterschiedlichen + // Umlaufattributen angemessen zu umfliessen. + // Die letzte ausweichende Zeile, sollte in der Hoehe angepasst + // sein, damit nicht der Eindruck von "Rahmenabstaenden" aufkommt. + // 8221: Wichtig ist, dass Ascent == Height ist, weil die FlyPortionWerte + // im CalcLine in pCurr uebertragen werden und IsDummy() darauf + // angewiesen ist. + // Es gibt meines Wissens nur zwei Stellen, in denen DummyLines + // entstehen koennen: hier und in MakeFlyDummies. + // Ausgewertet wird IsDummy() in IsFirstTxtLine() und + // beim Zeilenwandern und im Zusammenhang mit DropCaps. + pFly->Height( KSHORT(aInter.Height()) ); + + // In nNextTop steckt jetzt die Unterkante des Rahmens, dem wir + // ausweichen oder die Oberkante des naechsten Rahmens, den wir + // beachten muessen. Wir koennen also jetzt getrost bis zu diesem + // Wert anwachsen, so sparen wir einige Leerzeilen. + long nNextTop = pTxtFly->GetNextTop(); + if ( pFrm->IsVertical() ) + nNextTop = pFrm->SwitchVerticalToHorizontal( nNextTop ); + if( nNextTop > aInter.Bottom() ) + { + SwTwips nH = nNextTop - aInter.Top(); + if( nH < KSHRT_MAX ) + pFly->Height( KSHORT( nH ) ); + } + if( nAscent < pFly->Height() ) + pFly->SetAscent( KSHORT(nAscent) ); + else + pFly->SetAscent( pFly->Height() ); + } + else + { + if( rInf.GetIdx() == rInf.GetTxt().Len() ) + { + // Nicht nHeight nehmen, sonst haben wir einen Riesendescent + pFly->Height( pLast->Height() ); + pFly->SetAscent( pLast->GetAscent() ); + } + else + { + pFly->Height( KSHORT(aInter.Height()) ); + if( nAscent < pFly->Height() ) + pFly->SetAscent( KSHORT(nAscent) ); + else + pFly->SetAscent( pFly->Height() ); + } + } + + rInf.SetFly( pFly ); + + if( pFly->Fix() < rInf.Width() ) + rInf.Width( pFly->Fix() ); + + GETGRID( pFrm->FindPageFrm() ) + if ( pGrid ) + { + const SwPageFrm* pPageFrm = pFrm->FindPageFrm(); + const SwLayoutFrm* pBody = pPageFrm->FindBodyCont(); + + SWRECTFN( pPageFrm ) + + const long nGridOrigin = pBody ? + (pBody->*fnRect->fnGetPrtLeft)() : + (pPageFrm->*fnRect->fnGetPrtLeft)(); + + const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); + const USHORT nGridWidth = GETGRIDWIDTH( pGrid, pDoc); //for textgrid refactor + + SwTwips nStartX = GetLeftMargin(); + if ( bVert ) + { + Point aPoint( nStartX, 0 ); + pFrm->SwitchHorizontalToVertical( aPoint ); + nStartX = aPoint.Y(); + } + + const SwTwips nOfst = nStartX - nGridOrigin; + const SwTwips nTmpWidth = rInf.Width() + nOfst; + + const ULONG i = nTmpWidth / nGridWidth + 1; + + const long nNewWidth = ( i - 1 ) * nGridWidth - nOfst; + if ( nNewWidth > 0 ) + rInf.Width( (USHORT)nNewWidth ); + else + rInf.Width( 0 ); + } + } +} + +/***************************************************************************** + * SwTxtFormatter::NewFlyCntPortion + * legt eine neue Portion fuer ein zeichengebundenes Objekt an. + *****************************************************************************/ + +SwFlyCntPortion *SwTxtFormatter::NewFlyCntPortion( SwTxtFormatInfo &rInf, + SwTxtAttr *pHint ) const +{ + SwFlyCntPortion *pRet = 0; + const SwFrm *pFrame = (SwFrm*)pFrm; + + SwFlyInCntFrm *pFly; + SwFrmFmt* pFrmFmt = ((SwTxtFlyCnt*)pHint)->GetFlyCnt().GetFrmFmt(); + if( RES_FLYFRMFMT == pFrmFmt->Which() ) + pFly = ((SwTxtFlyCnt*)pHint)->GetFlyFrm(pFrame); + else + pFly = NULL; + // aBase bezeichnet die dokumentglobale Position, + // ab der die neue Extraportion plaziert wird. + // aBase.X() = Offset in der Zeile, + // hinter der aktuellen Portion + // aBase.Y() = LineIter.Y() + Ascent der aktuellen Portion + + long nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc; + // OD 08.01.2004 #i11859# - use new method <SwLineLayout::MaxAscentDescent(..)> + //SwLinePortion *pPos = pCurr->GetFirstPortion(); + //lcl_MaxAscDescent( pPos, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); + pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc ); + + // Wenn der Ascent des Rahmens groesser als der Ascent der akt. Portion + // ist, wird dieser bei der Base-Berechnung verwendet, sonst wuerde + // der Rahmen zunaechst zu weit nach oben gesetzt, um dann doch wieder + // nach unten zu rutschen und dabei ein Repaint in einem Bereich ausloesen, + // indem er niemals wirklich war. + KSHORT nAscent = 0; + + const bool bTxtFrmVertical = GetInfo().GetTxtFrm()->IsVertical(); + + const bool bUseFlyAscent = pFly && pFly->GetValidPosFlag() && + 0 != ( bTxtFrmVertical ? + pFly->GetRefPoint().X() : + pFly->GetRefPoint().Y() ); + + if ( bUseFlyAscent ) + nAscent = static_cast<USHORT>( Abs( int( bTxtFrmVertical ? + pFly->GetRelPos().X() : + pFly->GetRelPos().Y() ) ) ); + + // check if be prefer to use the ascent of the last portion: + if ( IsQuick() || + !bUseFlyAscent || + nAscent < rInf.GetLast()->GetAscent() ) + { + nAscent = rInf.GetLast()->GetAscent(); + } + else if( nAscent > nFlyAsc ) + nFlyAsc = nAscent; + + Point aBase( GetLeftMargin() + rInf.X(), Y() + nAscent ); + objectpositioning::AsCharFlags nMode = IsQuick() ? AS_CHAR_QUICK : 0; + if( GetMulti() && GetMulti()->HasRotation() ) + { + nMode |= AS_CHAR_ROTATE; + if( GetMulti()->IsRevers() ) + nMode |= AS_CHAR_REVERSE; + } + + Point aTmpBase( aBase ); + if ( GetInfo().GetTxtFrm()->IsVertical() ) + GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aTmpBase ); + + if( pFly ) + { + pRet = new SwFlyCntPortion( *GetInfo().GetTxtFrm(), pFly, aTmpBase, + nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode ); + // Wir muessen sicherstellen, dass unser Font wieder im OutputDevice + // steht. Es koennte sein, dass der FlyInCnt frisch eingefuegt wurde, + // dann hat GetFlyFrm dazu gefuehrt, dass er neu angelegt wird. + // Dessen Frames werden sofort formatiert, die verstellen den Font + // und schon haben wir den Salat (3322). + rInf.SelectFont(); + if( pRet->GetAscent() > nAscent ) + { + aBase.Y() = Y() + pRet->GetAscent(); + nMode |= AS_CHAR_ULSPACE; + if( !rInf.IsTest() ) + aTmpBase = aBase; + if ( GetInfo().GetTxtFrm()->IsVertical() ) + GetInfo().GetTxtFrm()->SwitchHorizontalToVertical( aTmpBase ); + + pRet->SetBase( *rInf.GetTxtFrm(), aTmpBase, nTmpAscent, + nTmpDescent, nFlyAsc, nFlyDesc, nMode ); + } + } + else + { + pRet = new SwFlyCntPortion( *rInf.GetTxtFrm(), (SwDrawContact*)pFrmFmt->FindContactObj(), + aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode ); + } + return pRet; +} + + + +/************************************************************************* + * SwTxtFly::SwTxtFly() + *************************************************************************/ + +SwTxtFly::SwTxtFly( const SwTxtFly& rTxtFly ) +{ + pPage = rTxtFly.pPage; + // --> OD 2006-08-15 #i68520# + mpCurrAnchoredObj = rTxtFly.mpCurrAnchoredObj; + // <-- + pCurrFrm = rTxtFly.pCurrFrm; + pMaster = rTxtFly.pMaster; + // --> OD 2006-08-15 #i68520# + if( rTxtFly.mpAnchoredObjList ) + { + mpAnchoredObjList = new SwAnchoredObjList( *(rTxtFly.mpAnchoredObjList) ); + } + else + { + mpAnchoredObjList = NULL; + } + // <-- + + bOn = rTxtFly.bOn; + bLeftSide = rTxtFly.bLeftSide; + bTopRule = rTxtFly.bTopRule; +} + +void SwTxtFly::CtorInitTxtFly( const SwTxtFrm *pFrm ) +{ + mbIgnoreCurrentFrame = sal_False; + mbIgnoreContour = sal_False; + // --> OD 2004-12-17 #118809# + mbIgnoreObjsInHeaderFooter = sal_False; + // <-- + pPage = pFrm->FindPageFrm(); + const SwFlyFrm* pTmp = pFrm->FindFlyFrm(); + // --> OD 2006-08-15 #i68520# + mpCurrAnchoredObj = pTmp; + // <-- + pCurrFrm = pFrm; + pMaster = pCurrFrm->IsFollow() ? NULL : pCurrFrm; + // --> OD 2006-08-15 #i68520# + mpAnchoredObjList = NULL; + // <-- + // Wenn wir nicht von einem Frame ueberlappt werden, oder wenn + // es gar keine FlyCollection gibt, dann schaltet wir uns fuer immer ab. + // Aber es koennte sein, dass waehrend der Formatierung eine Zeile + // hinzukommt, die in einen Frame hineinragt. Deswegen keine Optimierung + // per bOn = pSortedFlys && IsAnyFrm(); + bOn = pPage->GetSortedObjs() != 0; + bTopRule = sal_True; + bLeftSide = sal_False; + nMinBottom = 0; + nIndex = ULONG_MAX; +} + +/************************************************************************* + * SwTxtFly::_GetFrm() + * + * IN: dokumentglobal (rRect) + * OUT: framelokal (return-Wert) + * Diese Methode wird waehrend der Formatierung vom LineIter gerufen. + * 1. um die naechste FlyPortion vorzubereiten + * 2. um nach Aenderung der Zeilenhoehe neue Ueberlappungen festzustellen + *************************************************************************/ + +SwRect SwTxtFly::_GetFrm( const SwRect &rRect, sal_Bool bTop ) const +{ + SwRect aRet; + if( ForEach( rRect, &aRet, sal_True ) ) + { + SWRECTFN( pCurrFrm ) + if( bTop ) + (aRet.*fnRect->fnSetTop)( (rRect.*fnRect->fnGetTop)() ); + + // 8110: Bottom nicht immer anpassen. + const SwTwips nRetBottom = (aRet.*fnRect->fnGetBottom)(); + const SwTwips nRectBottom = (rRect.*fnRect->fnGetBottom)(); + if ( (*fnRect->fnYDiff)( nRetBottom, nRectBottom ) > 0 || + (aRet.*fnRect->fnGetHeight)() < 0 ) + (aRet.*fnRect->fnSetBottom)( nRectBottom ); + } + return aRet; +} + +/************************************************************************* + * SwTxtFly::IsAnyFrm() + * + * IN: dokumentglobal + * fuer die Printarea des aktuellen Frame + * + * dient zum Abschalten des SwTxtFly, wenn keine Objekte ueberlappen (Relax) + * + *************************************************************************/ + +sal_Bool SwTxtFly::IsAnyFrm() const +{ + SWAP_IF_SWAPPED( pCurrFrm ) + + ASSERT( bOn, "IsAnyFrm: Why?" ); + SwRect aRect( pCurrFrm->Frm().Pos() + pCurrFrm->Prt().Pos(), + pCurrFrm->Prt().SSize() ); + + const sal_Bool bRet = ForEach( aRect, NULL, sal_False ); + UNDO_SWAP( pCurrFrm ) + return bRet; +} + +/************************************************************************* + * SwTxtFly::IsAnyObj() + * + * IN: dokumentglobal + * OUT: sal_True Wenn ein Rahmen oder DrawObj beruecksichtigt werden muss + * Nur wenn IsAnyObj sal_False liefert, koennen Optimierungen benutzt werden + * wie Paint/FormatEmpty fuer leere Absaetze + * und auch das virtuelle Outputdevice. + *************************************************************************/ + +sal_Bool SwTxtFly::IsAnyObj( const SwRect &rRect ) const +{ + ASSERT ( bOn, "SwTxtFly::IsAnyObj: Who's knocking?" ); + + SwRect aRect( rRect ); + if ( aRect.IsEmpty() ) + aRect = SwRect( pCurrFrm->Frm().Pos() + pCurrFrm->Prt().Pos(), + pCurrFrm->Prt().SSize() ); + + const SwSortedObjs *pSorted = pPage->GetSortedObjs(); + if( pSorted ) // Eigentlich ist durch bOn sichergestellt, dass es an der + // Seite Objekte gibt, aber wer weiss, wer inzwischen etwas geloescht hat. + { + for ( MSHORT i = 0; i < pSorted->Count(); ++i ) + { + const SwAnchoredObject* pObj = (*pSorted)[i]; + + const SwRect aBound( pObj->GetObjRectWithSpaces() ); + + // Optimierung + if( pObj->GetObjRect().Left() > aRect.Right() ) + continue; + + // --> OD 2006-08-15 #i68520# + if( mpCurrAnchoredObj != pObj && aBound.IsOver( aRect ) ) + // <-- + return sal_True; + } + } + return sal_False; +} + +const SwCntntFrm* SwTxtFly::_GetMaster() +{ + pMaster = pCurrFrm; + while( pMaster->IsFollow() ) + pMaster = (SwCntntFrm*)pMaster->FindMaster(); + return pMaster; +} + +/************************************************************************* + * SwTxtFly::DrawTextOpaque() + * + * IN: dokumentglobal + * DrawTextOpaque() wird von DrawText() gerufen. + * Die Clipregions werden so gesetzt, dass nur die Teile ausgegeben werden, + * die nicht in den Bereichen von FlyFrms liegen, die undurchsichtig und + * ueber dem aktuellen Frame liegen. + * Die On-Optimierung uebernimmt DrawText()! + *************************************************************************/ + +sal_Bool SwTxtFly::DrawTextOpaque( SwDrawTextInfo &rInf ) +{ + SwSaveClip aClipSave( rInf.GetpOut() ); + SwRect aRect( rInf.GetPos(), rInf.GetSize() ); + if( rInf.GetSpace() ) + { + xub_StrLen nTmpLen = STRING_LEN == rInf.GetLen() ? rInf.GetText().Len() : + rInf.GetLen(); + if( rInf.GetSpace() > 0 ) + { + xub_StrLen nSpaceCnt = 0; + const xub_StrLen nEndPos = rInf.GetIdx() + nTmpLen; + for( xub_StrLen nPos = rInf.GetIdx(); nPos < nEndPos; ++nPos ) + { + if( CH_BLANK == rInf.GetText().GetChar( nPos ) ) + ++nSpaceCnt; + } + if( nSpaceCnt ) + aRect.Width( aRect.Width() + nSpaceCnt * rInf.GetSpace() ); + } + else + aRect.Width( aRect.Width() - nTmpLen * rInf.GetSpace() ); + } + + if( aClipSave.IsOn() && rInf.GetOut().IsClipRegion() ) + { + SwRect aClipRect( rInf.GetOut().GetClipRegion().GetBoundRect() ); + aRect.Intersection( aClipRect ); + } + + SwRegionRects aRegion( aRect ); + + sal_Bool bOpaque = sal_False; + // --> OD 2006-08-15 #i68520# + const UINT32 nCurrOrd = mpCurrAnchoredObj + ? mpCurrAnchoredObj->GetDrawObj()->GetOrdNum() + : SAL_MAX_UINT32; + // <-- + ASSERT( !bTopRule, "DrawTextOpaque: Wrong TopRule" ); + + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); + if ( bOn && nCount > 0 ) + // <-- + { + MSHORT nHellId = pPage->GetShell()->getIDocumentDrawModelAccess()->GetHellId(); + for( MSHORT i = 0; i < nCount; ++i ) + { + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i]; + if( dynamic_cast<const SwFlyFrm*>(pTmpAnchoredObj) && + mpCurrAnchoredObj != pTmpAnchoredObj ) + // <-- + { + // --> OD 2006-08-15 #i68520# + const SwFlyFrm* pFly = dynamic_cast<const SwFlyFrm*>(pTmpAnchoredObj); + // <-- + if( aRegion.GetOrigin().IsOver( pFly->Frm() ) ) + { + const SwFrmFmt *pFmt = pFly->GetFmt(); + const SwFmtSurround &rSur = pFmt->GetSurround(); + const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); + //Nur undurchsichtige und weiter oben liegende. + /// OD 08.10.2002 #103898# - add condition + /// <!(pFly->IsBackgroundTransparent() || pFly->IsShadowTransparent())> + if( !( pFly->IsBackgroundTransparent() + || pFly->IsShadowTransparent() ) && + SURROUND_THROUGHT == rSur.GetSurround() && + ( !rSur.IsAnchorOnly() || + // --> OD 2006-08-15 #i68520# + GetMaster() == pFly->GetAnchorFrm() || + // <-- + ( FLY_AT_CNTNT != rAnchor.GetAnchorId() && + FLY_AUTO_CNTNT != rAnchor.GetAnchorId() + ) + ) && + // --> OD 2006-08-15 #i68520# + pTmpAnchoredObj->GetDrawObj()->GetLayer() != nHellId && + nCurrOrd < pTmpAnchoredObj->GetDrawObj()->GetOrdNum() + // <-- + ) + { + //Ausser der Inhalt ist Transparent + const SwNoTxtFrm *pNoTxt = + pFly->Lower() && pFly->Lower()->IsNoTxtFrm() + ? (SwNoTxtFrm*)pFly->Lower() + : 0; + if ( !pNoTxt || + (!pNoTxt->IsTransparent() && !rSur.IsContour()) ) + { + bOpaque = sal_True; + aRegion -= pFly->Frm(); + } + } + } + } + } + } + + Point aPos( rInf.GetPos().X(), rInf.GetPos().Y() + rInf.GetAscent() ); + const Point &rOld = rInf.GetPos(); + rInf.SetPos( aPos ); + + if( !bOpaque ) + { + if( rInf.GetKern() ) + rInf.GetFont()->_DrawStretchText( rInf ); + else + rInf.GetFont()->_DrawText( rInf ); + rInf.SetPos( rOld ); + return sal_False; + } + else if( aRegion.Count() ) + { + // Was fuer ein Aufwand ... + SwSaveClip aClipVout( rInf.GetpOut() ); + for( MSHORT i = 0; i < aRegion.Count(); ++i ) + { + SwRect &rRect = aRegion[i]; + if( rRect != aRegion.GetOrigin() ) + aClipVout.ChgClip( rRect ); + if( rInf.GetKern() ) + rInf.GetFont()->_DrawStretchText( rInf ); + else + rInf.GetFont()->_DrawText( rInf ); + } + } + rInf.SetPos( rOld ); + return sal_True; +} + +/************************************************************************* + * SwTxtFly::DrawFlyRect() + * + * IN: windowlokal + * Zwei Feinheiten gilt es zu beachten: + * 1) DrawRect() oberhalb des ClipRects sind erlaubt ! + * 2) FlyToRect() liefert groessere Werte als die Framedaten ! + *************************************************************************/ + +void SwTxtFly::DrawFlyRect( OutputDevice* pOut, const SwRect &rRect, + const SwTxtPaintInfo &rInf, sal_Bool bNoGraphic ) +{ + SwRegionRects aRegion( rRect ); + ASSERT( !bTopRule, "DrawFlyRect: Wrong TopRule" ); + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); + if ( bOn && nCount > 0 ) + // <-- + { + MSHORT nHellId = pPage->GetShell()->getIDocumentDrawModelAccess()->GetHellId(); + for( MSHORT i = 0; i < nCount; ++i ) + { + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* pAnchoredObjTmp = (*mpAnchoredObjList)[i]; + if( mpCurrAnchoredObj != pAnchoredObjTmp && + dynamic_cast<const SwFlyFrm*>(pAnchoredObjTmp) ) + // <-- + { + // --> OD 2006-08-15 #i68520# + const SwFmtSurround& rSur = pAnchoredObjTmp->GetFrmFmt().GetSurround(); + // <-- + + // OD 24.01.2003 #106593# - correct clipping of fly frame area. + // Consider that fly frame background/shadow can be transparent + // and <SwAlignRect(..)> fly frame area + // --> OD 2006-08-15 #i68520# + const SwFlyFrm* pFly = dynamic_cast<const SwFlyFrm*>(pAnchoredObjTmp); + // <-- + // --> OD 2005-06-08 #i47804# - consider transparent graphics + // and OLE objects. + bool bClipFlyArea = + ( ( SURROUND_THROUGHT == rSur.GetSurround() ) + // --> OD 2006-08-15 #i68520# + ? (pAnchoredObjTmp->GetDrawObj()->GetLayer() != nHellId) + // <-- + : !rSur.IsContour() ) && + !pFly->IsBackgroundTransparent() && + !pFly->IsShadowTransparent() && + ( !pFly->Lower() || + !pFly->Lower()->IsNoTxtFrm() || + !static_cast<const SwNoTxtFrm*>(pFly->Lower())->IsTransparent() ); + // <-- + if ( bClipFlyArea ) + { + // --> OD 2006-08-15 #i68520# + SwRect aFly( pAnchoredObjTmp->GetObjRect() ); + // <-- + // OD 24.01.2003 #106593# + ::SwAlignRect( aFly, pPage->GetShell() ); + if( aFly.Width() > 0 && aFly.Height() > 0 ) + aRegion -= aFly; + } + } + } + } + + for( MSHORT i = 0; i < aRegion.Count(); ++i ) + { + if ( bNoGraphic ) + pOut->DrawRect( aRegion[i].SVRect() ); + else + { + ASSERT( ((SvxBrushItem*)-1) != rInf.GetBrushItem(), + "DrawRect: Uninitialized BrushItem!" ); + ::DrawGraphic( rInf.GetBrushItem(), pOut, rInf.GetBrushRect(), + aRegion[i] ); + } + } +} + +// --> OD 2004-10-06 #i26945# - change first parameter: +// Now it's the <SwAnchoredObject> instance of the floating screen object +sal_Bool SwTxtFly::GetTop( const SwAnchoredObject* _pAnchoredObj, + const sal_Bool bInFtn, + const sal_Bool bInFooterOrHeader ) +// <-- +{ + // --> OD 2006-08-15 #i68520# + // <mpCurrAnchoredObj> is set, if <pCurrFrm> is inside a fly frame + if( _pAnchoredObj != mpCurrAnchoredObj ) + // <-- + { + // --> OD 2004-10-06 #i26945# + const SdrObject* pNew = _pAnchoredObj->GetDrawObj(); + // <-- + // #102344# Ignore connectors which have one or more connections + if(pNew && pNew->ISA(SdrEdgeObj)) + { + if(((SdrEdgeObj*)pNew)->GetConnectedNode(TRUE) + || ((SdrEdgeObj*)pNew)->GetConnectedNode(FALSE)) + { + return sal_False; + } + } + + if( ( bInFtn || bInFooterOrHeader ) && bTopRule ) + { + // --> OD 2004-10-06 #i26945# + const SwFrmFmt& rFrmFmt = _pAnchoredObj->GetFrmFmt(); + const SwFmtAnchor& rNewA = rFrmFmt.GetAnchor(); + // <-- + if ( FLY_PAGE == rNewA.GetAnchorId() ) + { + if ( bInFtn ) + return sal_False; + + if ( bInFooterOrHeader ) + { + SwFmtVertOrient aVert( rFrmFmt.GetVertOrient() ); + BOOL bVertPrt = aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA || + aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA; + if( bVertPrt ) + return sal_False; + } + } + } + + // --> OD 2006-08-15 #i68520# + // bEvade: consider pNew, if we are not inside a fly + // consider pNew, if pNew is lower of <mpCurrAnchoredObj> + sal_Bool bEvade = !mpCurrAnchoredObj || + Is_Lower_Of( dynamic_cast<const SwFlyFrm*>(mpCurrAnchoredObj), pNew); + + if ( !bEvade ) + { + // We are currently inside a fly frame and pNew is not + // inside this fly frame. We can do some more checks if + // we have to consider pNew. + + // If bTopRule is not set, we ignore the frame types. + // We directly check the z-order + if ( !bTopRule ) + bEvade = sal_True; + else + { + // innerhalb von verketteten Flys wird nur Lowern ausgewichen + // --> OD 2006-08-15 #i68520# + const SwFmtChain &rChain = mpCurrAnchoredObj->GetFrmFmt().GetChain(); + // <-- + if ( !rChain.GetPrev() && !rChain.GetNext() ) + { + // --> OD 2004-10-06 #i26945# + const SwFmtAnchor& rNewA = _pAnchoredObj->GetFrmFmt().GetAnchor(); + // <-- + // --> OD 2006-08-15 #i68520# + const SwFmtAnchor& rCurrA = mpCurrAnchoredObj->GetFrmFmt().GetAnchor(); + // <-- + + // If <mpCurrAnchoredObj> is anchored as character, its content + // does not wrap around pNew + if( FLY_IN_CNTNT == rCurrA.GetAnchorId() ) + return sal_False; + + // If pNew is anchored to page and <mpCurrAnchoredObj is not anchored + // to page, the content of <mpCurrAnchoredObj> does not wrap around pNew + // If both pNew and <mpCurrAnchoredObj> are anchored to page, we can do + // some more checks + if( FLY_PAGE == rNewA.GetAnchorId() ) + { + if( FLY_PAGE == rCurrA.GetAnchorId() ) + bEvade = sal_True; + else + return sal_False; + } + else if( FLY_PAGE == rCurrA.GetAnchorId() ) + return sal_False; // Seitengebundene weichen nur seitengeb. aus + else if( FLY_AT_FLY == rNewA.GetAnchorId() ) + bEvade = sal_True; // Nicht seitengeb. weichen Rahmengeb. aus + else if( FLY_AT_FLY == rCurrA.GetAnchorId() ) + return sal_False; // Rahmengebundene weichen abs.geb. nicht aus + // --> OD 2006-01-30 #i57062# + // In order to avoid loop situation, it's decided to adjust + // the wrapping behaviour of content of at-paragraph/at-character + // anchored objects to one in the page header/footer and + // the document body --> content of at-paragraph/at-character + // anchored objects doesn't wrap around each other. +// else if( bInFooterOrHeader ) +// return sal_False; // In header or footer no wrapping +// // if both bounded at paragraph +// else // Zwei Flies mit (auto-)absatzgebunder Verankerung ... +// // ... entscheiden nach der Reihenfolge ihrer Anker im Dok. +// bEvade = rNewA.GetCntntAnchor()->nNode.GetIndex() <= +// rCurrA.GetCntntAnchor()->nNode.GetIndex(); + else + return sal_False; + // <-- + } + } + + // aber: es wird niemals einem hierarchisch untergeordnetem + // ausgewichen und ausserdem braucht nur bei Ueberlappung + // ausgewichen werden. + // --> OD 2006-08-15 #i68520# + bEvade &= ( mpCurrAnchoredObj->GetDrawObj()->GetOrdNum() < pNew->GetOrdNum() ); + // <-- + if( bEvade ) + { + // --> OD 2006-08-15 #i68520# + SwRect aTmp( _pAnchoredObj->GetObjRectWithSpaces() ); + if ( !aTmp.IsOver( mpCurrAnchoredObj->GetObjRectWithSpaces() ) ) + bEvade = sal_False; + // <-- + } + } + + if ( bEvade ) + { + // --> OD 2004-10-06 #i26945# + const SwFmtAnchor& rNewA = _pAnchoredObj->GetFrmFmt().GetAnchor(); + // <-- + ASSERT( FLY_IN_CNTNT != rNewA.GetAnchorId(), "Don't call GetTop with a FlyInCntFrm" ); + if( FLY_PAGE == rNewA.GetAnchorId() ) + return sal_True; // Seitengebundenen wird immer ausgewichen. + + // Wenn absatzgebundene Flys in einem FlyCnt gefangen sind, so + // endet deren Einflussbereich an den Grenzen des FlyCnt! + // Wenn wir aber gerade den Text des FlyCnt formatieren, dann + // muss er natuerlich dem absatzgebundenen Frm ausweichen! + // pCurrFrm ist der Anker von pNew? + // --> OD 2004-10-06 #i26945# + const SwFrm* pTmp = _pAnchoredObj->GetAnchorFrm(); + // <-- + if( pTmp == pCurrFrm ) + return sal_True; + if( pTmp->IsTxtFrm() && ( pTmp->IsInFly() || pTmp->IsInFtn() ) ) + { + // --> OD 2004-10-06 #i26945# + Point aPos = _pAnchoredObj->GetObjRect().Pos(); + // <-- + pTmp = GetVirtualUpper( pTmp, aPos ); + } + // --> OD 2004-10-06 #i26945# + // --> OD 2004-11-29 #115759# + // If <pTmp> is a text frame inside a table, take the upper + // of the anchor frame, which contains the anchor position. + else if ( pTmp->IsTxtFrm() && pTmp->IsInTab() ) + { + pTmp = const_cast<SwAnchoredObject*>(_pAnchoredObj) + ->GetAnchorFrmContainingAnchPos()->GetUpper(); + } + // <-- + // --> OD 2004-05-13 #i28701# - consider all objects in same context, + // if wrapping style is considered on object positioning. + // Thus, text will wrap around negative positioned objects. + // --> OD 2004-08-25 #i3317# - remove condition on checking, + // if wrappings style is considered on object postioning. + // Thus, text is wrapping around negative positioned objects. + // --> OD 2004-10-20 #i35640# - no consideration of negative + // positioned objects, if wrapping style isn't considered on + // object position and former text wrapping is applied. + // This condition is typically for documents imported from the + // OpenOffice.org file format. + const IDocumentSettingAccess* pIDSA = pCurrFrm->GetTxtNode()->getIDocumentSettingAccess(); + if ( ( pIDSA->get(IDocumentSettingAccess::CONSIDER_WRAP_ON_OBJECT_POSITION) || + !pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) ) && + ::FindKontext( pTmp, 0 ) == ::FindKontext( pCurrFrm, 0 ) ) + { + return sal_True; + } + // <-- + + const SwFrm* pHeader = 0; + if ( pCurrFrm->GetNext() != pTmp && + ( IsFrmInSameKontext( pTmp, pCurrFrm ) || + // --> #i13832#, #i24135# wrap around objects in page header + ( !pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) && + 0 != ( pHeader = pTmp->FindFooterOrHeader() ) && + !pHeader->IsFooterFrm() && + pCurrFrm->IsInDocBody() ) ) ) + // <-- + { + if( pHeader || FLY_AT_FLY == rNewA.GetAnchorId() ) + return sal_True; + + // Compare indices: + // Den Index des anderen erhalten wir immer ueber das Ankerattr. + ULONG nTmpIndex = rNewA.GetCntntAnchor()->nNode.GetIndex(); + // Jetzt wird noch ueberprueft, ob der aktuelle Absatz vor dem + // Anker des verdraengenden Objekts im Text steht, dann wird + // nicht ausgewichen. + // Der Index wird moeglichst ueber einen SwFmtAnchor ermittelt, + // da sonst recht teuer. + if( ULONG_MAX == nIndex ) + nIndex = pCurrFrm->GetNode()->GetIndex(); + + if( nIndex >= nTmpIndex ) + return sal_True; + } + } + } + return sal_False; +} +// --> OD 2006-08-15 #i68520# +struct AnchoredObjOrder +{ + sal_Bool mbR2L; + SwRectFn mfnRect; + + AnchoredObjOrder( const sal_Bool bR2L, + SwRectFn fnRect ) + : mbR2L( bR2L ), + mfnRect( fnRect ) + {} + + bool operator()( const SwAnchoredObject* pListedAnchoredObj, + const SwAnchoredObject* pNewAnchoredObj ) + { + const SwRect aBoundRectOfListedObj( pListedAnchoredObj->GetObjRectWithSpaces() ); + const SwRect aBoundRectOfNewObj( pNewAnchoredObj->GetObjRectWithSpaces() ); + if ( ( mbR2L && + ( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() == + (aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) || + ( !mbR2L && + ( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() == + (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) ) + { + SwTwips nTopDiff = + (*mfnRect->fnYDiff)( (aBoundRectOfNewObj.*mfnRect->fnGetTop)(), + (aBoundRectOfListedObj.*mfnRect->fnGetTop)() ); + if ( nTopDiff == 0 && + ( ( mbR2L && + ( (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() > + (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() ) ) || + ( !mbR2L && + ( (aBoundRectOfNewObj.*mfnRect->fnGetRight)() < + (aBoundRectOfListedObj.*mfnRect->fnGetRight)() ) ) ) ) + { + return true; + } + else if ( nTopDiff > 0 ) + { + return true; + } + } + else if ( ( mbR2L && + ( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() > + (aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) || + ( !mbR2L && + ( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() < + (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) ) + { + return true; + } + + return false; + } +}; + +// --> OD 2006-08-15 #i68520# +SwAnchoredObjList* SwTxtFly::InitAnchoredObjList() +{ + ASSERT( pCurrFrm, "InitFlyList: No Frame, no FlyList" ); + // --> OD 2006-08-15 #i68520# + ASSERT( !mpAnchoredObjList, "InitFlyList: FlyList already initialized" ); + // <-- + + SWAP_IF_SWAPPED( pCurrFrm ) + + const SwSortedObjs *pSorted = pPage->GetSortedObjs(); + const sal_uInt32 nCount = pSorted ? pSorted->Count() : 0; + // --> #108724# Page header/footer content doesn't have to wrap around + // floating screen objects + const bool bFooterHeader = 0 != pCurrFrm->FindFooterOrHeader(); + const IDocumentSettingAccess* pIDSA = pCurrFrm->GetTxtNode()->getIDocumentSettingAccess(); + // --> OD 2005-01-12 #i40155# - check, if frame is marked not to wrap + const sal_Bool bWrapAllowed = ( pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) || + ( !pCurrFrm->IsInFtn() && !bFooterHeader ) ) && + !SwLayouter::FrmNotToWrap( *pCurrFrm->GetTxtNode()->getIDocumentLayoutAccess(), *pCurrFrm ); + // <-- + + bOn = sal_False; + + if( nCount && bWrapAllowed ) + { + // --> OD 2006-08-15 #i68520# + mpAnchoredObjList = new SwAnchoredObjList(); + // <-- + + // --> OD 2004-06-18 #i28701# - consider complete frame area for new + // text wrapping + SwRect aRect; + if ( pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) ) + { + aRect = pCurrFrm->Prt(); + aRect += pCurrFrm->Frm().Pos(); + } + else + { + aRect = pCurrFrm->Frm(); + } + // Wir machen uns etwas kleiner als wir sind, + // damit Ein-Twip-Ueberlappungen ignoriert werden. (#49532) + SWRECTFN( pCurrFrm ) + const long nRight = (aRect.*fnRect->fnGetRight)() - 1; + const long nLeft = (aRect.*fnRect->fnGetLeft)() + 1; + const sal_Bool bR2L = pCurrFrm->IsRightToLeft(); + + const IDocumentDrawModelAccess* pIDDMA = pCurrFrm->GetTxtNode()->getIDocumentDrawModelAccess(); + + for( sal_uInt32 i = 0; i < nCount; i++ ) + { + // --> OD 2006-08-15 #i68520# +// SwAnchoredObject* pAnchoredObj = (*pSorted)[ i ]; +// const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() ); + +// // OD 2004-01-15 #110582# - do not consider hidden objects +// // OD 2004-05-13 #i28701# - check, if object has to be considered +// // for text wrap. +// if ( !pDoc->IsVisibleLayerId( pAnchoredObj->GetDrawObj()->GetLayer() ) || +// !pAnchoredObj->ConsiderForTextWrap() || +// nRight < (aBound.*fnRect->fnGetLeft)() || +// (*fnRect->fnYDiff)( (aRect.*fnRect->fnGetTop)(), +// (aBound.*fnRect->fnGetBottom)() ) > 0 || +// nLeft > (aBound.*fnRect->fnGetRight)() || +// // --> OD 2004-12-17 #118809# - If requested, do not consider +// // objects in page header|footer for text frames not in page +// // header|footer. This is requested for the calculation of +// // the base offset for objects <SwTxtFrm::CalcBaseOfstForFly()> +// ( mbIgnoreObjsInHeaderFooter && !bFooterHeader && +// pAnchoredObj->GetAnchorFrm()->FindFooterOrHeader() ) || +// // <-- +// // --> FME 2004-07-14 #i20505# Do not consider oversized objects +// (aBound.*fnRect->fnGetHeight)() > +// 2 * (pPage->Frm().*fnRect->fnGetHeight)() ) +// // <-- +// { +// continue; +// } + SwAnchoredObject* pAnchoredObj = (*pSorted)[ i ]; + if ( !pIDDMA->IsVisibleLayerId( pAnchoredObj->GetDrawObj()->GetLayer() ) || + !pAnchoredObj->ConsiderForTextWrap() || + ( mbIgnoreObjsInHeaderFooter && !bFooterHeader && + pAnchoredObj->GetAnchorFrm()->FindFooterOrHeader() ) ) + { + continue; + } + + const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() ); + if ( nRight < (aBound.*fnRect->fnGetLeft)() || + (*fnRect->fnYDiff)( (aRect.*fnRect->fnGetTop)(), + (aBound.*fnRect->fnGetBottom)() ) > 0 || + nLeft > (aBound.*fnRect->fnGetRight)() || + (aBound.*fnRect->fnGetHeight)() > + 2 * (pPage->Frm().*fnRect->fnGetHeight)() ) + { + continue; + } + // <-- + + // --> OD 2004-10-06 #i26945# - pass <pAnchoredObj> to method + // <GetTop(..)> instead of only the <SdrObject> instance of the + // anchored object + if ( GetTop( pAnchoredObj, pCurrFrm->IsInFtn(), bFooterHeader ) ) + // <-- + { + // OD 11.03.2003 #107862# - adjust insert position: + // overlapping objects should be sorted from left to right and + // inside left to right sorting from top to bottom. + // If objects on the same position are found, they are sorted + // on its width. + // --> OD 2006-08-15 #i68520# +// sal_uInt16 nPos = pFlyList->Count(); +// while ( nPos ) +// { +// SdrObject* pTmpObj = (*pFlyList)[ --nPos ]; +// const SwRect aBoundRectOfTmpObj( GetBoundRect( pTmpObj ) ); +// if ( ( bR2L && +// ( (aBoundRectOfTmpObj.*fnRect->fnGetRight)() == +// (aBound.*fnRect->fnGetRight)() ) ) || +// ( !bR2L && +// ( (aBoundRectOfTmpObj.*fnRect->fnGetLeft)() == +// (aBound.*fnRect->fnGetLeft)() ) ) ) +// { +// SwTwips nTopDiff = +// (*fnRect->fnYDiff)( (aBound.*fnRect->fnGetTop)(), +// (aBoundRectOfTmpObj.*fnRect->fnGetTop)() ); +// if ( nTopDiff == 0 && +// ( ( bR2L && +// ( (aBound.*fnRect->fnGetLeft)() > +// (aBoundRectOfTmpObj.*fnRect->fnGetLeft)() ) ) || +// ( !bR2L && +// ( (aBound.*fnRect->fnGetRight)() < +// (aBoundRectOfTmpObj.*fnRect->fnGetRight)() ) ) ) ) +// { +// ++nPos; +// break; +// } +// else if ( nTopDiff > 0 ) +// { +// ++nPos; +// break; +// } +// } +// else if ( ( bR2L && +// ( (aBoundRectOfTmpObj.*fnRect->fnGetRight)() > +// (aBound.*fnRect->fnGetRight)() ) ) || +// ( !bR2L && +// ( (aBoundRectOfTmpObj.*fnRect->fnGetLeft)() < +// (aBound.*fnRect->fnGetLeft)() ) ) ) +// { +// ++nPos; +// break; +// } +// } +// SdrObject* pSdrObj = pAnchoredObj->DrawObj(); +// pFlyList->C40_INSERT( SdrObject, pSdrObj, nPos ); + { + SwAnchoredObjList::iterator aInsPosIter = + std::lower_bound( mpAnchoredObjList->begin(), + mpAnchoredObjList->end(), + pAnchoredObj, + AnchoredObjOrder( bR2L, fnRect ) ); + + mpAnchoredObjList->insert( aInsPosIter, pAnchoredObj ); + } + // <-- + + const SwFmtSurround &rFlyFmt = pAnchoredObj->GetFrmFmt().GetSurround(); + // --> OD 2006-08-15 #i68520# + if ( rFlyFmt.IsAnchorOnly() && + pAnchoredObj->GetAnchorFrm() == GetMaster() ) + // <-- + { + const SwFmtVertOrient &rTmpFmt = + pAnchoredObj->GetFrmFmt().GetVertOrient(); + if( text::VertOrientation::BOTTOM != rTmpFmt.GetVertOrient() ) + nMinBottom = ( bVert && nMinBottom ) ? + Min( nMinBottom, aBound.Left() ) : + Max( nMinBottom, (aBound.*fnRect->fnGetBottom)() ); + } + + bOn = sal_True; + } + } + if( nMinBottom ) + { + SwTwips nMax = (pCurrFrm->GetUpper()->*fnRect->fnGetPrtBottom)(); + if( (*fnRect->fnYDiff)( nMinBottom, nMax ) > 0 ) + nMinBottom = nMax; + } + } + else + { + // --> OD 2006-08-15 #i68520# + mpAnchoredObjList = new SwAnchoredObjList(); + // <-- + } + + UNDO_SWAP( pCurrFrm ) + + // --> OD 2006-08-15 #i68520# + return mpAnchoredObjList; + // <-- +} +// <-- + +SwTwips SwTxtFly::CalcMinBottom() const +{ + SwTwips nRet = 0; + const SwSortedObjs *pDrawObj = GetMaster()->GetDrawObjs(); + const sal_uInt32 nCount = pDrawObj ? pDrawObj->Count() : 0; + if( nCount ) + { + SwTwips nEndOfFrm = pCurrFrm->Frm().Bottom(); + for( sal_uInt32 i = 0; i < nCount; i++ ) + { + SwAnchoredObject* pAnchoredObj = (*pDrawObj)[ i ]; + const SwFmtSurround &rFlyFmt = pAnchoredObj->GetFrmFmt().GetSurround(); + if( rFlyFmt.IsAnchorOnly() ) + { + const SwFmtVertOrient &rTmpFmt = + pAnchoredObj->GetFrmFmt().GetVertOrient(); + if( text::VertOrientation::BOTTOM != rTmpFmt.GetVertOrient() ) + { + const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() ); + if( aBound.Top() < nEndOfFrm ) + nRet = Max( nRet, aBound.Bottom() ); + } + } + } + SwTwips nMax = pCurrFrm->GetUpper()->Frm().Top() + + pCurrFrm->GetUpper()->Prt().Bottom(); + if( nRet > nMax ) + nRet = nMax; + } + return nRet; +} + +/************************************************************************* + * Hier erfolgt die Berechnung der Kontur ... + * CalcBoundRect(..) und andere + *************************************************************************/ + +/************************************************************************* + * class SwContourCache + *************************************************************************/ + +SwContourCache::SwContourCache() : + nPntCnt( 0 ), nObjCnt( 0 ) +{ + memset( (SdrObject**)pSdrObj, 0, sizeof(pSdrObj) ); + memset( pTextRanger, 0, sizeof(pTextRanger) ); +} + +SwContourCache::~SwContourCache() +{ + for( MSHORT i = 0; i < nObjCnt; delete pTextRanger[ i++ ] ) + ; +} + +void SwContourCache::ClrObject( MSHORT nPos ) +{ + ASSERT( pTextRanger[ nPos ], "ClrObject: Allready cleared. Good Bye!" ); + nPntCnt -= pTextRanger[ nPos ]->GetPointCount(); + delete pTextRanger[ nPos ]; + --nObjCnt; + memmove( (SdrObject**)pSdrObj + nPos, pSdrObj + nPos + 1, + ( nObjCnt - nPos ) * sizeof( SdrObject* ) ); + memmove( pTextRanger + nPos, pTextRanger + nPos + 1, + ( nObjCnt - nPos ) * sizeof( TextRanger* ) ); +} + +void ClrContourCache( const SdrObject *pObj ) +{ + if( pContourCache && pObj ) + for( MSHORT i = 0; i < pContourCache->GetCount(); ++i ) + if( pObj == pContourCache->GetObject( i ) ) + { + pContourCache->ClrObject( i ); + break; + } +} + +void ClrContourCache() +{ + if( pContourCache ) + { + for( MSHORT i = 0; i < pContourCache->GetCount(); + delete pContourCache->pTextRanger[ i++ ] ) + ; + pContourCache->nObjCnt = 0; + pContourCache->nPntCnt = 0; + } +} + +/************************************************************************* + * SwContourCache::CalcBoundRect + * berechnet das Rechteck, welches vom Objekt in der angegebenen Zeile + * ueberdeckt wird. + * Bei _nicht_ konturumflossenen Objekten ist dies einfach die Ueber- + * lappung von BoundRect (inkl. Abstand!) und Zeile, + * bei Konturumfluss wird das Polypolygon des Objekts abgeklappert + *************************************************************************/ +// --> OD 2006-08-15 #i68520# +const SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj, + const SwRect &rLine, + const SwTxtFrm* pFrm, + const long nXPos, + const sal_Bool bRight ) +{ + SwRect aRet; + const SwFrmFmt* pFmt = &(pAnchoredObj->GetFrmFmt()); + if( pFmt->GetSurround().IsContour() && + ( !pAnchoredObj->ISA(SwFlyFrm) || + ( static_cast<const SwFlyFrm*>(pAnchoredObj)->Lower() && + static_cast<const SwFlyFrm*>(pAnchoredObj)->Lower()->IsNoTxtFrm() ) ) ) + { + aRet = pAnchoredObj->GetObjRectWithSpaces(); + if( aRet.IsOver( rLine ) ) + { + if( !pContourCache ) + pContourCache = new SwContourCache; + + aRet = pContourCache->ContourRect( + pFmt, pAnchoredObj->GetDrawObj(), pFrm, rLine, nXPos, bRight ); + } + else + aRet.Width( 0 ); + } + else + { + aRet = pAnchoredObj->GetObjRectWithSpaces(); + } + + return aRet; +} +// <-- + +const SwRect SwContourCache::ContourRect( const SwFmt* pFmt, + const SdrObject* pObj, const SwTxtFrm* pFrm, const SwRect &rLine, + const long nXPos, const sal_Bool bRight ) +{ + SwRect aRet; + MSHORT nPos = 0; // Suche im Cache ... + while( nPos < GetCount() && pObj != pSdrObj[ nPos ] ) + ++nPos; + if( GetCount() == nPos ) // nicht gefunden + { + if( nObjCnt == POLY_CNT ) + { + nPntCnt -= pTextRanger[ --nObjCnt ]->GetPointCount(); + delete pTextRanger[ nObjCnt ]; + } + ::basegfx::B2DPolyPolygon aPolyPolygon; + ::basegfx::B2DPolyPolygon* pPolyPolygon = 0L; + + if ( pObj->ISA(SwVirtFlyDrawObj) ) + { + // Vorsicht #37347: Das GetContour() fuehrt zum Laden der Grafik, + // diese aendert dadurch ggf. ihre Groesse, ruft deshalb ein + // ClrObject() auf. + PolyPolygon aPoly; + if( !((SwVirtFlyDrawObj*)pObj)->GetFlyFrm()->GetContour( aPoly ) ) + aPoly = PolyPolygon( ((SwVirtFlyDrawObj*)pObj)-> + GetFlyFrm()->Frm().SVRect() ); + aPolyPolygon.clear(); + aPolyPolygon.append(aPoly.getB2DPolyPolygon()); + } + else + { + if( !pObj->ISA( E3dObject ) ) + { + aPolyPolygon = pObj->TakeXorPoly(); + } + + ::basegfx::B2DPolyPolygon aContourPoly(pObj->TakeContour()); + pPolyPolygon = new ::basegfx::B2DPolyPolygon(aContourPoly); + } + const SvxLRSpaceItem &rLRSpace = pFmt->GetLRSpace(); + const SvxULSpaceItem &rULSpace = pFmt->GetULSpace(); + memmove( pTextRanger + 1, pTextRanger, nObjCnt * sizeof( TextRanger* ) ); + memmove( (SdrObject**)pSdrObj + 1, pSdrObj, nObjCnt++ * sizeof( SdrObject* ) ); + pSdrObj[ 0 ] = pObj; // Wg. #37347 darf das Object erst nach dem + // GetContour() eingetragen werden. + pTextRanger[ 0 ] = new TextRanger( aPolyPolygon, pPolyPolygon, 20, + (USHORT)rLRSpace.GetLeft(), (USHORT)rLRSpace.GetRight(), + pFmt->GetSurround().IsOutside(), sal_False, pFrm->IsVertical() ); + pTextRanger[ 0 ]->SetUpper( rULSpace.GetUpper() ); + pTextRanger[ 0 ]->SetLower( rULSpace.GetLower() ); + + delete pPolyPolygon; + // UPPER_LOWER_TEST +#ifndef PRODUCT + const SwRootFrm* pTmpRootFrm = pFmt->getIDocumentLayoutAccess()->GetRootFrm(); + if( pTmpRootFrm->GetCurrShell() ) + { + sal_Bool bT2 = pTmpRootFrm->GetCurrShell()->GetViewOptions()->IsTest2(); + sal_Bool bT6 = pTmpRootFrm->GetCurrShell()->GetViewOptions()->IsTest6(); + if( bT2 || bT6 ) + { + if( bT2 ) + pTextRanger[ 0 ]->SetFlag7( sal_True ); + else + pTextRanger[ 0 ]->SetFlag6( sal_True ); + } + } +#endif + nPntCnt += pTextRanger[ 0 ]->GetPointCount(); + while( nPntCnt > POLY_MAX && nObjCnt > POLY_MIN ) + { + nPntCnt -= pTextRanger[ --nObjCnt ]->GetPointCount(); + delete pTextRanger[ nObjCnt ]; + } + } + else if( nPos ) + { + const SdrObject* pTmpObj = pSdrObj[ nPos ]; + TextRanger* pTmpRanger = pTextRanger[ nPos ]; + memmove( (SdrObject**)pSdrObj + 1, pSdrObj, nPos * sizeof( SdrObject* ) ); + memmove( pTextRanger + 1, pTextRanger, nPos * sizeof( TextRanger* ) ); + pSdrObj[ 0 ] = pTmpObj; + pTextRanger[ 0 ] = pTmpRanger; + } + SWRECTFN( pFrm ) + long nTmpTop = (rLine.*fnRect->fnGetTop)(); + // fnGetBottom is top + height + long nTmpBottom = (rLine.*fnRect->fnGetBottom)(); + + Range aRange( Min( nTmpTop, nTmpBottom ), Max( nTmpTop, nTmpBottom ) ); + + SvLongs *pTmp = pTextRanger[ 0 ]->GetTextRanges( aRange ); + + MSHORT nCount; + if( 0 != ( nCount = pTmp->Count() ) ) + { + MSHORT nIdx = 0; + while( nIdx < nCount && (*pTmp)[ nIdx ] < nXPos ) + ++nIdx; + sal_Bool bOdd = nIdx % 2 ? sal_True : sal_False; + sal_Bool bSet = sal_True; + if( bOdd ) + --nIdx; // innerhalb eines Intervalls + else if( ! bRight && ( nIdx >= nCount || (*pTmp)[ nIdx ] != nXPos ) ) + { + if( nIdx ) + nIdx -= 2; // ein Intervall nach links gehen + else + bSet = sal_False; // vor dem erstem Intervall + } + + if( bSet && nIdx < nCount ) + { + (aRet.*fnRect->fnSetTopAndHeight)( (rLine.*fnRect->fnGetTop)(), + (rLine.*fnRect->fnGetHeight)() ); + (aRet.*fnRect->fnSetLeft)( (*pTmp)[ nIdx ] ); + (aRet.*fnRect->fnSetRight)( (*pTmp)[ nIdx + 1 ] + 1 ); + } + } + return aRet; +} + +/************************************************************************* + * SwContourCache::ShowContour() + * zeichnet die PolyPolygone des Caches zu Debugzwecken. + *************************************************************************/ +#ifndef PRODUCT + +void SwContourCache::ShowContour( OutputDevice* pOut, const SdrObject* pObj, + const Color& rClosedColor, const Color& rOpenColor ) +{ + MSHORT nPos = 0; // Suche im Cache ... + while( nPos < POLY_CNT && pObj != pSdrObj[ nPos ] ) + ++nPos; + if( POLY_CNT != nPos ) + { + const PolyPolygon* pPol = pTextRanger[ nPos ]->GetLinePolygon(); + if( !pPol ) + pPol = &(pTextRanger[ nPos ]->GetPolyPolygon()); + for( MSHORT i = 0; i < pPol->Count(); ++i ) + { + pOut->SetLineColor( rOpenColor ); + const Polygon& rPol = (*pPol)[ i ]; + MSHORT nCount = rPol.GetSize(); + if( nCount > 1 && rPol[ 0 ] == rPol[ nCount - 1 ] ) + pOut->SetLineColor( rClosedColor ); + pOut->DrawPolygon( rPol ); + } +#if OSL_DEBUG_LEVEL > 1 + static KSHORT nRadius = 0; + if( nRadius ) + { + KSHORT nHalf = nRadius / 2; + Size aSz( nRadius, nRadius ); + for( MSHORT i = 0; i < pPol->Count(); ++i ) + { + const Polygon& rPol = (*pPol)[ i ]; + MSHORT nCount = rPol.GetSize(); + for( MSHORT k = 0; k < nCount; ++k ) + { + Point aPt( rPol[ k ] ); + aPt.X() -= nHalf; + aPt.Y() -= nHalf; + Rectangle aTmp( aPt, aSz ); + pOut->DrawEllipse( aTmp ); + } + } + } +#endif + } +} +#endif + +/************************************************************************* + * SwTxtFly::ShowContour() + * zeichnet die PolyPolygone des Caches zu Debugzwecken. + *************************************************************************/ +#ifndef PRODUCT + +void SwTxtFly::ShowContour( OutputDevice* pOut ) +{ + MSHORT nFlyCount; + if( bOn && ( 0 != ( nFlyCount = static_cast<USHORT>(GetAnchoredObjList()->size() ) ) ) ) + { + Color aRedColor( COL_LIGHTRED ); + Color aGreenColor( COL_LIGHTGREEN ); + Color aSaveColor( pOut->GetLineColor() ); + for( MSHORT j = 0; j < nFlyCount; ++j ) + { + const SwAnchoredObject* pObj = (*mpAnchoredObjList)[ j ]; + if( !pObj->GetFrmFmt().GetSurround().IsContour() ) + { + Rectangle aRect = pObj->GetObjRectWithSpaces().SVRect(); + pOut->DrawRect( aRect ); + continue; + } + pContourCache->ShowContour( pOut, pObj->GetDrawObj(), aRedColor, aGreenColor ); + } + pOut->SetLineColor( aSaveColor ); + } +} +#endif + +/************************************************************************* + * SwTxtFly::ForEach() + * + * sucht nach dem ersten Objekt, welches mit dem Rechteck ueberlappt + * + *************************************************************************/ + +sal_Bool SwTxtFly::ForEach( const SwRect &rRect, SwRect* pRect, sal_Bool bAvoid ) const +{ + SWAP_IF_SWAPPED( pCurrFrm ) + + sal_Bool bRet = sal_False; + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList::size_type nCount( bOn ? GetAnchoredObjList()->size() : 0 ); + if ( bOn && nCount > 0 ) + // <-- + { + for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i ) + { + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i]; + + SwRect aRect( pAnchoredObj->GetObjRectWithSpaces() ); + // <-- + + // Optimierung + SWRECTFN( pCurrFrm ) + if( (aRect.*fnRect->fnGetLeft)() > (rRect.*fnRect->fnGetRight)() ) + break; + // --> OD 2006-08-15 #i68520# + if ( mpCurrAnchoredObj != pAnchoredObj && aRect.IsOver( rRect ) ) + // <-- + { + // --> OD 2006-08-15 #i68520# + const SwFmt* pFmt( &(pAnchoredObj->GetFrmFmt()) ); + const SwFmtSurround &rSur = pFmt->GetSurround(); + // <-- + if( bAvoid ) + { + // Wenn der Text drunter durchlaeuft, bleibt die + // Formatierung unbeeinflusst. Im LineIter::DrawText() + // muessen "nur" geschickt die ClippingRegions gesetzt werden ... + const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); + if( ( SURROUND_THROUGHT == rSur.GetSurround() && + ( !rSur.IsAnchorOnly() || + // --> OD 2006-08-15 #i68520# + GetMaster() == pAnchoredObj->GetAnchorFrm() || + // <-- + ( FLY_AT_CNTNT != rAnchor.GetAnchorId() && + FLY_AUTO_CNTNT != rAnchor.GetAnchorId() ) ) ) + || aRect.Top() == WEIT_WECH ) + continue; + } + + // --> OD 2006-01-20 #i58642# + // Compare <GetMaster()> instead of <pCurrFrm> with the anchor + // frame of the anchored object, because a follow frame have + // to ignore the anchored objects of its master frame. + // Note: Anchored objects are always registered at the master + // frame, exception are as-character anchored objects, + // but these aren't handled here. + // --> OD 2006-08-15 #i68520# + if ( mbIgnoreCurrentFrame && + GetMaster() == pAnchoredObj->GetAnchorFrm() ) + continue; + // <-- + + if( pRect ) + { + // --> OD 2006-08-15 #i68520# + SwRect aFly = AnchoredObjToRect( pAnchoredObj, rRect ); + // <-- + if( aFly.IsEmpty() || !aFly.IsOver( rRect ) ) + continue; + if( !bRet || + ( !pCurrFrm->IsRightToLeft() && + ( (aFly.*fnRect->fnGetLeft)() < + (pRect->*fnRect->fnGetLeft)() ) || + ( pCurrFrm->IsRightToLeft() && + ( (aFly.*fnRect->fnGetRight)() > + (pRect->*fnRect->fnGetRight)() ) ) ) ) + *pRect = aFly; + if( rSur.IsContour() ) + { + bRet = sal_True; + continue; + } + } + bRet = sal_True; + break; + } + } + } + + UNDO_SWAP( pCurrFrm ) + + return bRet; +} + +/************************************************************************* + * SwTxtFly::GetPos() + * + * liefert die Position im sorted Array zurueck + *************************************************************************/ + +// --> OD 2006-08-15 #i68520# +SwAnchoredObjList::size_type SwTxtFly::GetPos( const SwAnchoredObject* pAnchoredObj ) const +{ + SwAnchoredObjList::size_type nCount = GetAnchoredObjList()->size(); + SwAnchoredObjList::size_type nRet = 0; + while ( nRet < nCount && pAnchoredObj != (*mpAnchoredObjList)[ nRet ] ) + ++nRet; + return nRet; +} +// <-- + +/************************************************************************* + * SwTxtFly::CalcRightMargin() + * + * pObj ist das Object, der uns gerade ueberlappt. + * pCurrFrm ist der aktuelle Textframe, der ueberlappt wird. + * Der rechte Rand ist der rechte Rand oder + * er wird durch das naechste Object, welches in die Zeile ragt, bestimmt. + *************************************************************************/ +// --> OD 2006-08-15 #i68520# +void SwTxtFly::CalcRightMargin( SwRect &rFly, + SwAnchoredObjList::size_type nFlyPos, + const SwRect &rLine ) const +{ + // Normalerweise ist der rechte Rand der rechte Rand der Printarea. + ASSERT( ! pCurrFrm->IsVertical() || ! pCurrFrm->IsSwapped(), + "SwTxtFly::CalcRightMargin with swapped frame" ) + SWRECTFN( pCurrFrm ) + // --> OD 2004-12-14 #118796# - correct determination of right of printing area + SwTwips nRight = (pCurrFrm->*fnRect->fnGetPrtRight)(); + // <-- + SwTwips nFlyRight = (rFly.*fnRect->fnGetRight)(); + SwRect aLine( rLine ); + (aLine.*fnRect->fnSetRight)( nRight ); + (aLine.*fnRect->fnSetLeft)( (rFly.*fnRect->fnGetLeft)() ); + + // Es koennte aber sein, dass in die gleiche Zeile noch ein anderes + // Object hineinragt, welches _ueber_ uns liegt. + // Wunder der Technik: Flys mit Durchlauf sind fuer die darunterliegenden + // unsichtbar, das heisst, dass sie bei der Berechnung der Raender + // anderer Flys ebenfalls nicht auffallen. + // 3301: pNext->Frm().IsOver( rLine ) ist noetig + // --> OD 2006-08-15 #i68520# + SwSurround eSurroundForTextWrap; + // <-- + + sal_Bool bStop = sal_False; + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList::size_type nPos = 0; + // <-- + + // --> OD 2006-08-15 #i68520# + while( nPos < mpAnchoredObjList->size() && !bStop ) + // <-- + { + if( nPos == nFlyPos ) + { + ++nPos; + continue; + } + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nPos++ ]; + if ( pNext == mpCurrAnchoredObj ) + continue; + eSurroundForTextWrap = _GetSurroundForTextWrap( pNext ); + if( SURROUND_THROUGHT == eSurroundForTextWrap ) + continue; + // <-- + + const SwRect aTmp( SwContourCache::CalcBoundRect + ( pNext, aLine, pCurrFrm, nFlyRight, sal_True ) ); + SwTwips nTmpRight = (aTmp.*fnRect->fnGetRight)(); + + // Optimierung: + // In nNextTop wird notiert, an welcher Y-Positon mit Aenderung der + // Rahmenverhaeltnisse gerechnet werden muss. Dies dient dazu, dass, + // obwohl nur die Rahmen in der aktuellen Zeilenhoehe betrachtet werden, + // bei Rahmen ohne Umlauf die Zeilenhoehe so erhoeht wird, dass mit einer + // einzigen Zeile die Unterkante das Rahmens oder ggf. die Oberkante des + // naechsten Rahmen erreicht wird. + // Insbesondere bei HTML-Dokumenten kommen oft (Dummy-)Absaetze in einer + // 2-Pt.-Schrift vor, bis diese einem groesseren Rahmen ausgewichen sind, + // erforderte es frueher Unmengen von Leerzeilen. + const long nTmpTop = (aTmp.*fnRect->fnGetTop)(); + if( (*fnRect->fnYDiff)( nTmpTop, (aLine.*fnRect->fnGetTop)() ) > 0 ) + { + if( (*fnRect->fnYDiff)( nNextTop, nTmpTop ) > 0 ) + SetNextTop( nTmpTop ); // Die Oberkante des "naechsten" Rahmens + } + else if( ! (aTmp.*fnRect->fnGetWidth)() ) // Typisch fuer Objekte mit Konturumlauf + { // Bei Objekten mit Konturumlauf, die vor der aktuellen Zeile beginnen + // und hinter ihr enden, trotzdem aber nicht mit ihr ueberlappen, + // muss die Optimierung ausgeschaltet werden, denn bereits in der + // naechsten Zeile kann sich dies aendern. + if( ! (aTmp.*fnRect->fnGetHeight)() || + (*fnRect->fnYDiff)( (aTmp.*fnRect->fnGetBottom)(), + (aLine.*fnRect->fnGetTop)() ) > 0 ) + SetNextTop( 0 ); + } + if( aTmp.IsOver( aLine ) && nTmpRight > nFlyRight ) + { + nFlyRight = nTmpRight; + if( SURROUND_RIGHT == eSurroundForTextWrap || + SURROUND_PARALLEL == eSurroundForTextWrap ) + { + // der FlyFrm wird ueberstimmt. + if( nRight > nFlyRight ) + nRight = nFlyRight; + bStop = sal_True; + } + } + } + (rFly.*fnRect->fnSetRight)( nRight ); +} +// <-- + +/************************************************************************* + * SwTxtFly::CalcLeftMargin() + * + * pFly ist der FlyFrm, der uns gerade ueberlappt. + * pCurrFrm ist der aktuelle Textframe, der ueberlappt wird. + * Der linke Rand ist der linke Rand der aktuellen PrintArea oder + * er wird durch den vorigen FlyFrm, der in die Zeile ragt, bestimmt. + *************************************************************************/ +// --> OD 2006-08-15 #i68520# +void SwTxtFly::CalcLeftMargin( SwRect &rFly, + SwAnchoredObjList::size_type nFlyPos, + const SwRect &rLine ) const +{ + ASSERT( ! pCurrFrm->IsVertical() || ! pCurrFrm->IsSwapped(), + "SwTxtFly::CalcLeftMargin with swapped frame" ) + SWRECTFN( pCurrFrm ) + // --> OD 2004-12-14 #118796# - correct determination of left of printing area + SwTwips nLeft = (pCurrFrm->*fnRect->fnGetPrtLeft)(); + // <-- + const SwTwips nFlyLeft = (rFly.*fnRect->fnGetLeft)(); + + if( nLeft > nFlyLeft ) + nLeft = rFly.Left(); + + SwRect aLine( rLine ); + (aLine.*fnRect->fnSetLeft)( nLeft ); + + // Es koennte aber sein, dass in die gleiche Zeile noch ein anderes + // Object hineinragt, welches _ueber_ uns liegt. + // Wunder der Technik: Flys mit Durchlauf sind fuer die darunterliegenden + // unsichtbar, das heisst, dass sie bei der Berechnung der Raender + // anderer Flys ebenfalls nicht auffallen. + // 3301: pNext->Frm().IsOver( rLine ) ist noetig + + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList::size_type nMyPos = nFlyPos; + while( ++nFlyPos < mpAnchoredObjList->size() ) + // <-- + { + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ]; + const SwRect aTmp( pNext->GetObjRectWithSpaces() ); + // <-- + if( (aTmp.*fnRect->fnGetLeft)() >= nFlyLeft ) + break; + } + + while( nFlyPos ) + { + if( --nFlyPos == nMyPos ) + continue; + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ]; + if( pNext == mpCurrAnchoredObj ) + continue; + SwSurround eSurroundForTextWrap = _GetSurroundForTextWrap( pNext ); + if( SURROUND_THROUGHT == eSurroundForTextWrap ) + continue; + // <-- + + const SwRect aTmp( SwContourCache::CalcBoundRect + ( pNext, aLine, pCurrFrm, nFlyLeft, sal_False ) ); + + if( (aTmp.*fnRect->fnGetLeft)() < nFlyLeft && aTmp.IsOver( aLine ) ) + { + // --> OD 2004-12-14 #118796# - no '+1', because <..fnGetRight> + // returns the correct value. + SwTwips nTmpRight = (aTmp.*fnRect->fnGetRight)(); + if ( nLeft <= nTmpRight ) + nLeft = nTmpRight; + // <-- + + break; + } + } + (rFly.*fnRect->fnSetLeft)( nLeft ); +} +// <-- + +/************************************************************************* + * SwTxtFly::FlyToRect() + * + * IN: dokumentglobal (rRect) + * OUT: dokumentglobal (return-Wert) + * Liefert zu einem SwFlyFrm das von ihm in Anspruch genommene Rechteck + * unter Beruecksichtigung der eingestellten Attribute fuer den Abstand + * zum Text zurueck. + *************************************************************************/ +// --> OD 2006-08-15 #i68520# +SwRect SwTxtFly::AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj, + const SwRect &rLine ) const +{ + SWRECTFN( pCurrFrm ) + + const long nXPos = pCurrFrm->IsRightToLeft() ? + rLine.Right() : + (rLine.*fnRect->fnGetLeft)(); + + SwRect aFly = mbIgnoreContour ? + pAnchoredObj->GetObjRectWithSpaces() : + SwContourCache::CalcBoundRect( pAnchoredObj, rLine, pCurrFrm, + nXPos, ! pCurrFrm->IsRightToLeft() ); + + if( !aFly.Width() ) + return aFly; + + SetNextTop( (aFly.*fnRect->fnGetBottom)() ); // Damit die Zeile ggf. bis zur Unterkante + // des Rahmens waechst. + SwAnchoredObjList::size_type nFlyPos = GetPos( pAnchoredObj ); + + // Bei LEFT und RIGHT vergroessern wir das Rechteck. + // Hier gibt es einige Probleme, wenn mehrere Frames zu sehen sind. + // Zur Zeit wird nur der einfachste Fall angenommen: + // LEFT bedeutet, dass der Text links vom Frame fliessen soll, + // d.h. der Frame blaeht sich bis zum rechten Rand der Printarea + // oder bis zum naechsten Frame auf. + // Bei RIGHT ist es umgekehrt. + // Ansonsten wird immer der eingestellte Abstand zwischen Text + // und Frame aufaddiert. + switch( _GetSurroundForTextWrap( pAnchoredObj ) ) + { + case SURROUND_LEFT : + { + CalcRightMargin( aFly, nFlyPos, rLine ); + break; + } + case SURROUND_RIGHT : + { + CalcLeftMargin( aFly, nFlyPos, rLine ); + break; + } + case SURROUND_NONE : + { + CalcRightMargin( aFly, nFlyPos, rLine ); + CalcLeftMargin( aFly, nFlyPos, rLine ); + break; + } + default: + break; + } + return aFly; +} + +// --> OD 2006-08-15 #i68520# +// new method <_GetSurroundForTextWrap(..)> replaces methods +// <CalcSmart(..)> and <GetOrder(..)> +/************************************************************************* + * SwTxtFly::CalcSmart() + * + * CalcSmart() liefert die Umlaufform zurueck. + * + * Auf beiden Seiten ist weniger als 2 cm Platz fuer den Text + * => kein Umlauf ( SURROUND_NONE ) + * Auf genau einer Seite ist mehr als 2 cm Platz + * => Umlauf auf dieser Seite ( SURROUND_LEFT / SURROUND_RIGHT ) + * Auf beiden Seiten ist mehr als 2 cm Platz, das Objekt ist breiter als 1,5 cm + * => Umlauf auf der breiteren Seite ( SURROUND_LEFT / SURROUND_RIGHT ) + * Auf beiden Seiten ist mehr als 2 cm Platz, das Objekt ist schmaler als 1,5 cm + * => beidseitiger Umlauf ( SURROUND_PARALLEL ) + * + *************************************************************************/ + +// Umfluss nur auf Seiten mit mindestens 2 cm Platz fuer den Text +#define TEXT_MIN 1134 +// Beidseitiger Umfluss bis zu einer Rahmenbreite von maximal 1,5 cm +#define FRAME_MAX 850 + +//_FlyCntnt SwTxtFly::CalcSmart( const SdrObject *pObj ) const +//{ +// _FlyCntnt eOrder; + +// // 11839: Nur die X-Positionen sind interessant, die Y-Positionen des +// // CurrentFrames koennen sich noch aendern (wachsen). + +// SWRECTFN( pCurrFrm ) +// const long nCurrLeft = (pCurrFrm->*fnRect->fnGetPrtLeft)(); +// const long nCurrRight = (pCurrFrm->*fnRect->fnGetPrtRight)(); +// const SwRect aRect( GetBoundRect( pObj ) ); +// long nFlyLeft = (aRect.*fnRect->fnGetLeft)(); +// long nFlyRight = (aRect.*fnRect->fnGetRight)(); + +// if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight ) +// eOrder = SURROUND_PARALLEL; +// else +// { +// long nLeft = nFlyLeft - nCurrLeft; +// long nRight = nCurrRight - nFlyRight; +// if( nFlyRight - nFlyLeft > FRAME_MAX ) +// { +// if( nLeft < nRight ) +// nLeft = 0; +// else +// nRight = 0; +// } +// if( nLeft < TEXT_MIN ) +// nLeft = 0; +// if( nRight < TEXT_MIN ) +// nRight = 0; +// if( nLeft ) +// eOrder = nRight ? SURROUND_PARALLEL : SURROUND_LEFT; +// else +// eOrder = nRight ? SURROUND_RIGHT: SURROUND_NONE; +// } + +// return eOrder; +//} + +/************************************************************************* + * SwTxtFly::GetOrder() + *************************************************************************/ + +//_FlyCntnt SwTxtFly::GetOrder( const SdrObject *pObj ) const +//{ +// const SwFrmFmt *pFmt = ((SwContact*)GetUserCall(pObj))->GetFmt(); +// const SwFmtSurround &rFlyFmt = pFmt->GetSurround(); +// _FlyCntnt eOrder = rFlyFmt.GetSurround(); + +// if( rFlyFmt.IsAnchorOnly() && &lcl_TheAnchor( pObj ) != GetMaster() ) +// { +// const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); +// if( FLY_AT_CNTNT == rAnchor.GetAnchorId() || +// FLY_AUTO_CNTNT == rAnchor.GetAnchorId() ) +// return SURROUND_NONE; +// } + +// Beim Durchlauf und Nowrap wird smart ignoriert. +// if( SURROUND_THROUGHT == eOrder || SURROUND_NONE == eOrder ) +// return eOrder; + +// left is left and right is right +// if ( pCurrFrm->IsRightToLeft() ) +// { +// if ( SURROUND_LEFT == eOrder ) +// eOrder = SURROUND_RIGHT; +// else if ( SURROUND_RIGHT == eOrder ) +// eOrder = SURROUND_LEFT; +// } + +// "idealer Seitenumlauf": +// if( SURROUND_IDEAL == eOrder ) +// eOrder = CalcSmart( pObj ); //Bei SMART wird die Order automatisch berechnet: + +// return eOrder; +//} + +SwSurround SwTxtFly::_GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const +{ + const SwFrmFmt* pFmt = &(pAnchoredObj->GetFrmFmt()); + const SwFmtSurround &rFlyFmt = pFmt->GetSurround(); + SwSurround eSurroundForTextWrap = rFlyFmt.GetSurround(); + + if( rFlyFmt.IsAnchorOnly() && pAnchoredObj->GetAnchorFrm() != GetMaster() ) + { + const SwFmtAnchor& rAnchor = pFmt->GetAnchor(); + if ( FLY_AT_CNTNT == rAnchor.GetAnchorId() || + FLY_AUTO_CNTNT == rAnchor.GetAnchorId() ) + return SURROUND_NONE; + } + + // Beim Durchlauf und Nowrap wird smart ignoriert. + if( SURROUND_THROUGHT == eSurroundForTextWrap || + SURROUND_NONE == eSurroundForTextWrap ) + return eSurroundForTextWrap; + + // left is left and right is right + if ( pCurrFrm->IsRightToLeft() ) + { + if ( SURROUND_LEFT == eSurroundForTextWrap ) + eSurroundForTextWrap = SURROUND_RIGHT; + else if ( SURROUND_RIGHT == eSurroundForTextWrap ) + eSurroundForTextWrap = SURROUND_LEFT; + } + + // "idealer Seitenumlauf": + if ( SURROUND_IDEAL == eSurroundForTextWrap ) + { + SWRECTFN( pCurrFrm ) + const long nCurrLeft = (pCurrFrm->*fnRect->fnGetPrtLeft)(); + const long nCurrRight = (pCurrFrm->*fnRect->fnGetPrtRight)(); + const SwRect aRect( pAnchoredObj->GetObjRectWithSpaces() ); + long nFlyLeft = (aRect.*fnRect->fnGetLeft)(); + long nFlyRight = (aRect.*fnRect->fnGetRight)(); + + if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight ) + eSurroundForTextWrap = SURROUND_PARALLEL; + else + { + long nLeft = nFlyLeft - nCurrLeft; + long nRight = nCurrRight - nFlyRight; + if( nFlyRight - nFlyLeft > FRAME_MAX ) + { + if( nLeft < nRight ) + nLeft = 0; + else + nRight = 0; + } + if( nLeft < TEXT_MIN ) + nLeft = 0; + if( nRight < TEXT_MIN ) + nRight = 0; + if( nLeft ) + eSurroundForTextWrap = nRight ? SURROUND_PARALLEL : SURROUND_LEFT; + else + eSurroundForTextWrap = nRight ? SURROUND_RIGHT: SURROUND_NONE; + } + } + + return eSurroundForTextWrap; +} + +/************************************************************************* + * SwTxtFly::IsAnyFrm( SwRect ) + * + * IN: dokumentglobal + * + * dient zum Abschalten des SwTxtFly, wenn keine Objekte ueberlappen (Relax) + * + *************************************************************************/ + +sal_Bool SwTxtFly::IsAnyFrm( const SwRect &rLine ) const +{ + + SWAP_IF_SWAPPED( pCurrFrm ) + + ASSERT( bOn, "IsAnyFrm: Why?" ); + + const sal_Bool bRet = ForEach( rLine, NULL, sal_False ); + UNDO_SWAP( pCurrFrm ) + return bRet; +} diff --git a/sw/source/core/text/txtfly.hxx b/sw/source/core/text/txtfly.hxx new file mode 100644 index 000000000000..f7dd3e1c592e --- /dev/null +++ b/sw/source/core/text/txtfly.hxx @@ -0,0 +1,262 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtfly.hxx,v $ + * $Revision: 1.15 $ + * + * 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. + * + ************************************************************************/ +#ifndef _TXTFLY_HXX +#define _TXTFLY_HXX +#include <svtools/svarray.hxx> + +#include "swtypes.hxx" +#include "swrect.hxx" + +class OutputDevice; +class SwCntntFrm; +class SwPageFrm; +class SwTxtFly; +class SdrObject; +class SwTxtPaintInfo; +class SwFmt; +class TextRanger; +class Color; +// --> OD 2004-10-06 #i26945# +class SwAnchoredObject; +// <-- + +// --> OD 2006-08-15 #i68520# - refactoring +//typedef MSHORT _FlyCntnt; +#include <fmtsrndenum.hxx> +// <-- + +// --> OD 2006-08-15 #i68520# +//SV_DECL_PTRARR( SwFlyList, SdrObject*, 10, 10 ) +#include <vector> +typedef std::vector< SwAnchoredObject* > SwAnchoredObjList; +// <-- + +/************************************************************************* + * class SwFlyIter + *************************************************************************/ +enum PAGESIDE { LEFT_SIDE, RIGHT_SIDE, DONTKNOW_SIDE }; + +/************************************************************************* + * class SwContourCache + *************************************************************************/ + +class SwDrawTextInfo; +// Contour-Cache, globale Variable, in txtinit.cxx initialisiert/zerstoert +// und in txtfly.cxx benutzt bei Konturumfluss +class SwContourCache; +extern SwContourCache *pContourCache; +class SwTxtFrm; + +#define POLY_CNT 20 +#define POLY_MIN 5 +#define POLY_MAX 4000 + +class SwContourCache +{ + friend void ClrContourCache(); + const SdrObject *pSdrObj[ POLY_CNT ]; + TextRanger *pTextRanger[ POLY_CNT ]; + long nPntCnt; + MSHORT nObjCnt; + const SwRect ContourRect( const SwFmt* pFmt, const SdrObject* pObj, + const SwTxtFrm* pFrm, const SwRect &rLine, const long nXPos, + const sal_Bool bRight ); + +public: + SwContourCache(); + ~SwContourCache(); + const SdrObject* GetObject( MSHORT nPos ){ return pSdrObj[ nPos ]; } + MSHORT GetCount() const { return nObjCnt; } + void ClrObject( MSHORT nPos ); + // --> OD 2006-08-15 #i68520# + static const SwRect CalcBoundRect( const SwAnchoredObject* pAnchoredObj, + const SwRect &rLine, + const SwTxtFrm* pFrm, + const long nXPos, + const sal_Bool bRight ); + // <-- +#ifndef PRODUCT + void ShowContour( OutputDevice* pOut, const SdrObject* pObj, + const Color& rClosedColor, const Color& rOpenColor ); +#endif +}; + +/************************************************************************* + * class SwTxtFly + *************************************************************************/ + +class SwTxtFly +{ + const SwPageFrm *pPage; + // --> OD 2006-08-15 #i68520# + const SwAnchoredObject* mpCurrAnchoredObj; + // <-- + + const SwTxtFrm *pCurrFrm; + + const SwCntntFrm *pMaster; + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList* mpAnchoredObjList; + // <-- + + long nMinBottom; + long nNextTop; // Hier wird die Oberkante des "naechsten" Rahmens gespeichert + ULONG nIndex; + sal_Bool bOn : 1; + sal_Bool bLeftSide : 1; + sal_Bool bTopRule: 1; + sal_Bool mbIgnoreCurrentFrame: 1; + sal_Bool mbIgnoreContour: 1; + // --> OD 2004-12-17 #118809# - boolean, indicating if objects in page + // header|footer are considered for text frames not in page header|footer. + sal_Bool mbIgnoreObjsInHeaderFooter: 1; + // <-- + SwRect _GetFrm( const SwRect &rPortion, sal_Bool bTop ) const; + // --> OD 2006-08-15 #i68520# + SwAnchoredObjList* InitAnchoredObjList(); + inline SwAnchoredObjList* GetAnchoredObjList() const + { + return mpAnchoredObjList + ? mpAnchoredObjList + : const_cast<SwTxtFly*>(this)->InitAnchoredObjList(); + } + // iterates over the anchored object list <mpAnchoredObjList> + sal_Bool ForEach( const SwRect &rRect, SwRect* pRect, sal_Bool bAvoid ) const; + SwSurround _GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const; + void CalcRightMargin( SwRect &rFly, + SwAnchoredObjList::size_type nPos, + const SwRect &rLine ) const; + void CalcLeftMargin( SwRect &rFly, + SwAnchoredObjList::size_type nPos, + const SwRect &rLine ) const; + SwAnchoredObjList::size_type GetPos( const SwAnchoredObject* pAnchoredObj ) const; + // <-- + // --> OD 2004-10-06 #i26945# - change first parameter: + // Now it's the <SwAnchoredObject> instance of the floating screen object + sal_Bool GetTop( const SwAnchoredObject* _pAnchoredObj, + const sal_Bool bInFtn, + const sal_Bool bInFooterOrHeader ); + // <-- + SwTwips CalcMinBottom() const; + const SwCntntFrm* _GetMaster(); + +public: + inline SwTxtFly() + { + mbIgnoreCurrentFrame = sal_False; + mbIgnoreCurrentFrame = sal_False; + // --> OD 2004-12-17 #118809# + mbIgnoreObjsInHeaderFooter = sal_False; + // <-- + // --> OD 2006-08-15 #i68520# + mpCurrAnchoredObj = 0; + mpAnchoredObjList = 0; + // <-- + pMaster = 0; + } + inline SwTxtFly( const SwTxtFrm *pFrm ) + { CtorInitTxtFly( pFrm ); } + + SwTxtFly( const SwTxtFly& rTxtFly ); + // --> OD 2006-08-15 #i68520# + inline ~SwTxtFly() { delete mpAnchoredObjList; } + // <-- + void CtorInitTxtFly( const SwTxtFrm *pFrm ); + void SetTopRule(){ bTopRule = sal_False; } + + inline SwRect GetFrm( const SwRect &rPortion, sal_Bool bTop = sal_True ) const; + inline sal_Bool IsOn() const { return bOn; } + inline sal_Bool Relax( const SwRect &rRect ); + inline sal_Bool Relax(); + inline SwTwips GetMinBottom() const + // --> OD 2006-08-15 #i68520# + { return mpAnchoredObjList ? nMinBottom : CalcMinBottom(); } + // <-- + inline const SwCntntFrm* GetMaster() const + { return pMaster ? pMaster : ((SwTxtFly*)this)->_GetMaster(); } + inline long GetNextTop() const { return nNextTop; } + // Diese temporaere Variable darf auch in const-Methoden manipuliert werden + inline void SetNextTop( long nNew ) const + { ((SwTxtFly*)this)->nNextTop = nNew; } + + // --> OD 2006-08-15 #i68520# + // determines the demanded rectangle for an anchored object, + // considering its surround for text wrapping. + SwRect AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj, + const SwRect& rRect ) const; + // <-- + + // Die Drawmethoden stellen sicher, dass ueberlappende Frames + // (ausser bei transparenten Frames) nicht uebergepinselt werden. + sal_Bool DrawTextOpaque( SwDrawTextInfo &rInf ); + + void DrawFlyRect( OutputDevice* pOut, const SwRect &rRect, + const SwTxtPaintInfo &rInf, sal_Bool bNoGraphic = sal_False ); + + // Liefert zurueck, ob die Zeile von einem Frame ueberlappt wird. + sal_Bool IsAnyFrm( const SwRect &rLine ) const; + sal_Bool IsAnyFrm() const; + //Das Rechteck kann leer sein, es gilt dann der Frm. + sal_Bool IsAnyObj( const SwRect& ) const; + + void SetIgnoreCurrentFrame( sal_Bool bNew ) { mbIgnoreCurrentFrame = bNew; } + void SetIgnoreContour( sal_Bool bNew ) { mbIgnoreContour = bNew; } + // --> OD 2004-12-17 #118809# + inline void SetIgnoreObjsInHeaderFooter( const sal_Bool _bNew ) + { + mbIgnoreObjsInHeaderFooter = _bNew; + } + // <-- + +#ifndef PRODUCT + void ShowContour( OutputDevice* pOut ); +#endif +}; + +// Wenn in das rRect (meist die aktuelle Zeile) kein freifliegender +// Frame ragt, dann schalten wir uns ab. +// rRect ist dokumentglobal ! +inline sal_Bool SwTxtFly::Relax( const SwRect &rRect ) +{ + return 0 != (bOn = bOn && IsAnyFrm( rRect )); +} + +inline sal_Bool SwTxtFly::Relax() +{ + return 0 != (bOn = bOn && IsAnyFrm()); +} + +inline SwRect SwTxtFly::GetFrm( const SwRect &rRect, sal_Bool bTop ) const +{ + return bOn ? _GetFrm( rRect, bTop ) : SwRect(); +} + + +#endif diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx new file mode 100644 index 000000000000..14944264d39e --- /dev/null +++ b/sw/source/core/text/txtfrm.cxx @@ -0,0 +1,2751 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtfrm.cxx,v $ + * $Revision: 1.108.30.1 $ + * + * 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 <svtools/ctloptions.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/sfxuno.hxx> +#include <svx/langitem.hxx> +#include <svx/lspcitem.hxx> +#include <svx/lrspitem.hxx> +#include <svx/ulspitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/pgrditem.hxx> +#include <swmodule.hxx> +#include <SwSmartTagMgr.hxx> +#include <doc.hxx> // GetDoc() +#include <pagefrm.hxx> // InvalidateSpelling +#include <rootfrm.hxx> +#include <viewsh.hxx> // ViewShell +#include <pam.hxx> // SwPosition +#include <ndtxt.hxx> // SwTxtNode +#include <txtatr.hxx> +#include <paratr.hxx> +#include <viewopt.hxx> +#include <dflyobj.hxx> +#include <flyfrm.hxx> +#include <tabfrm.hxx> +#include <frmtool.hxx> +#include <pagedesc.hxx> // SwPageDesc +#include <tgrditem.hxx> +#include <dbg_lay.hxx> +#include <fmtfld.hxx> +#include <fmtftn.hxx> +#include <txtfld.hxx> +#include <txtftn.hxx> +#include <charatr.hxx> +#include <ftninfo.hxx> +#ifndef _FMTLINE_HXX +#include <fmtline.hxx> +#endif +#include <txtfrm.hxx> // SwTxtFrm +#include <sectfrm.hxx> // SwSectFrm +#include <txtcfg.hxx> // DBG_LOOP +#include <itrform2.hxx> // Iteratoren +#include <widorp.hxx> // SwFrmBreak +#include <txtcache.hxx> +#include <fntcache.hxx> // GetLineSpace benutzt pLastFont +#include <SwGrammarMarkUp.hxx> +#ifndef _LINEINFO_HXX +#include <lineinfo.hxx> +#endif +#include <SwPortionHandler.hxx> +// OD 2004-01-15 #110582# +#include <dcontact.hxx> +// OD 2004-05-24 #i28701# +#include <sortedobjs.hxx> +// --> OD 2005-03-30 #???# +#include <txtflcnt.hxx> // SwTxtFlyCnt +#include <fmtflcnt.hxx> // SwFmtFlyCnt +#include <fmtcntnt.hxx> // SwFmtCntnt +// <-- +// --> OD 2008-01-31 #newlistlevelattrs# +#include <numrule.hxx> +// <-- +#include <swtable.hxx> +#include <fldupde.hxx> +#include <IGrammarContact.hxx> + +#if OSL_DEBUG_LEVEL > 1 +#include <txtpaint.hxx> // DbgRect +extern const sal_Char *GetPrepName( const enum PrepareHint ePrep ); +#endif + +TYPEINIT1( SwTxtFrm, SwCntntFrm ); + +// Switches width and height of the text frame +void SwTxtFrm::SwapWidthAndHeight() +{ + if ( ! bIsSwapped ) + { + const long nPrtOfstX = Prt().Pos().X(); + Prt().Pos().X() = Prt().Pos().Y(); + Prt().Pos().Y() = Frm().Width() - ( nPrtOfstX + Prt().Width() ); + } + else + { + const long nPrtOfstY = Prt().Pos().Y(); + Prt().Pos().Y() = Prt().Pos().X(); + Prt().Pos().X() = Frm().Height() - ( nPrtOfstY + Prt().Height() ); + } + + const long nFrmWidth = Frm().Width(); + Frm().Width( Frm().Height() ); + Frm().Height( nFrmWidth ); + const long nPrtWidth = Prt().Width(); + Prt().Width( Prt().Height() ); + Prt().Height( nPrtWidth ); + + bIsSwapped = ! bIsSwapped; +} + +// Calculates the coordinates of a rectangle when switching from +// horizontal to vertical layout. +void SwTxtFrm::SwitchHorizontalToVertical( SwRect& rRect ) const +{ + // calc offset inside frame + const long nOfstX = rRect.Left() - Frm().Left(); + const long nOfstY = rRect.Top() + rRect.Height() - Frm().Top(); + const long nWidth = rRect.Width(); + const long nHeight = rRect.Height(); + + if ( bIsSwapped ) + rRect.Left( Frm().Left() + Frm().Height() - nOfstY ); + else + // frame is rotated + rRect.Left( Frm().Left() + Frm().Width() - nOfstY ); + + rRect.Top( Frm().Top() + nOfstX ); + rRect.Width( nHeight ); + rRect.Height( nWidth ); +} + +// Calculates the coordinates of a point when switching from +// horizontal to vertical layout. +void SwTxtFrm::SwitchHorizontalToVertical( Point& rPoint ) const +{ + // calc offset inside frame + const long nOfstX = rPoint.X() - Frm().Left(); + const long nOfstY = rPoint.Y() - Frm().Top(); + + if ( bIsSwapped ) + rPoint.X() = Frm().Left() + Frm().Height() - nOfstY; + else + // calc rotated coords + rPoint.X() = Frm().Left() + Frm().Width() - nOfstY; + + rPoint.Y() = Frm().Top() + nOfstX; +} + +// Calculates the a limit value when switching from +// horizontal to vertical layout. +long SwTxtFrm::SwitchHorizontalToVertical( long nLimit ) const +{ + Point aTmp( 0, nLimit ); + SwitchHorizontalToVertical( aTmp ); + return aTmp.X(); +} + +// Calculates the coordinates of a rectangle when switching from +// vertical to horizontal layout. +void SwTxtFrm::SwitchVerticalToHorizontal( SwRect& rRect ) const +{ + long nOfstX; + + // calc offset inside frame + if ( bIsSwapped ) + nOfstX = Frm().Left() + Frm().Height() - ( rRect.Left() + rRect.Width() ); + else + nOfstX = Frm().Left() + Frm().Width() - ( rRect.Left() + rRect.Width() ); + + const long nOfstY = rRect.Top() - Frm().Top(); + const long nWidth = rRect.Height(); + const long nHeight = rRect.Width(); + + // calc rotated coords + rRect.Left( Frm().Left() + nOfstY ); + rRect.Top( Frm().Top() + nOfstX ); + rRect.Width( nWidth ); + rRect.Height( nHeight ); +} + +// Calculates the coordinates of a point when switching from +// vertical to horizontal layout. +void SwTxtFrm::SwitchVerticalToHorizontal( Point& rPoint ) const +{ + long nOfstX; + + // calc offset inside frame + if ( bIsSwapped ) + nOfstX = Frm().Left() + Frm().Height() - rPoint.X(); + else + nOfstX = Frm().Left() + Frm().Width() - rPoint.X(); + + const long nOfstY = rPoint.Y() - Frm().Top(); + + // calc rotated coords + rPoint.X() = Frm().Left() + nOfstY; + rPoint.Y() = Frm().Top() + nOfstX; +} + +// Calculates the a limit value when switching from +// vertical to horizontal layout. +long SwTxtFrm::SwitchVerticalToHorizontal( long nLimit ) const +{ + Point aTmp( nLimit, 0 ); + SwitchVerticalToHorizontal( aTmp ); + return aTmp.Y(); +} + +SwFrmSwapper::SwFrmSwapper( const SwTxtFrm* pTxtFrm, sal_Bool bSwapIfNotSwapped ) + : pFrm( pTxtFrm ), bUndo( sal_False ) +{ + if ( pFrm->IsVertical() && + ( ( bSwapIfNotSwapped && ! pFrm->IsSwapped() ) || + ( ! bSwapIfNotSwapped && pFrm->IsSwapped() ) ) ) + { + bUndo = sal_True; + ((SwTxtFrm*)pFrm)->SwapWidthAndHeight(); + } +} + +SwFrmSwapper::~SwFrmSwapper() +{ + if ( bUndo ) + ((SwTxtFrm*)pFrm)->SwapWidthAndHeight(); +} + +void SwTxtFrm::SwitchLTRtoRTL( SwRect& rRect ) const +{ + SWAP_IF_NOT_SWAPPED( this ) + + long nWidth = rRect.Width(); + rRect.Left( 2 * ( Frm().Left() + Prt().Left() ) + + Prt().Width() - rRect.Right() - 1 ); + + rRect.Width( nWidth ); + + UNDO_SWAP( this ) +} + +void SwTxtFrm::SwitchLTRtoRTL( Point& rPoint ) const +{ + SWAP_IF_NOT_SWAPPED( this ) + + rPoint.X() = 2 * ( Frm().Left() + Prt().Left() ) + Prt().Width() - rPoint.X() - 1; + + UNDO_SWAP( this ) +} + +SwLayoutModeModifier::SwLayoutModeModifier( const OutputDevice& rOutp ) : + rOut( rOutp ), nOldLayoutMode( rOutp.GetLayoutMode() ) +{ +} + +SwLayoutModeModifier::~SwLayoutModeModifier() +{ + ((OutputDevice&)rOut).SetLayoutMode( nOldLayoutMode ); +} + +void SwLayoutModeModifier::Modify( sal_Bool bChgToRTL ) +{ + ((OutputDevice&)rOut).SetLayoutMode( bChgToRTL ? + TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_BIDI_RTL : + TEXT_LAYOUT_BIDI_STRONG ); +} + +void SwLayoutModeModifier::SetAuto() +{ + const ULONG nNewLayoutMode = nOldLayoutMode & ~TEXT_LAYOUT_BIDI_STRONG; + ((OutputDevice&)rOut).SetLayoutMode( nNewLayoutMode ); +} + +SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang ) : + rOut( rOutp ), nOldLanguageType( rOutp.GetDigitLanguage() ) +{ + LanguageType eLang = eCurLang; + const SvtCTLOptions::TextNumerals nTextNumerals = SW_MOD()->GetCTLOptions().GetCTLTextNumerals(); + + if ( SvtCTLOptions::NUMERALS_HINDI == nTextNumerals ) + eLang = LANGUAGE_ARABIC_SAUDI_ARABIA; + else if ( SvtCTLOptions::NUMERALS_ARABIC == nTextNumerals ) + eLang = LANGUAGE_ENGLISH; + else if ( SvtCTLOptions::NUMERALS_SYSTEM == nTextNumerals ) + eLang = (LanguageType)::GetAppLanguage(); + + ((OutputDevice&)rOut).SetDigitLanguage( eLang ); +} + +SwDigitModeModifier::~SwDigitModeModifier() +{ + ((OutputDevice&)rOut).SetDigitLanguage( nOldLanguageType ); +} + +/************************************************************************* + * SwTxtFrm::Init() + *************************************************************************/ + +void SwTxtFrm::Init() +{ + ASSERT( !IsLocked(), "+SwTxtFrm::Init: this ist locked." ); + if( !IsLocked() ) + { + ClearPara(); + ResetBlinkPor(); + //Die Flags direkt setzen um ResetPreps und damit ein unnuetzes GetPara + //einzusparen. + // Nicht bOrphan, bLocked oder bWait auf sal_False setzen ! + // bOrphan = bFlag7 = bFlag8 = sal_False; + } +} + +/************************************************************************* +|* SwTxtFrm::CTORen/DTOR +|*************************************************************************/ + +void SwTxtFrm::InitCtor() +{ + nCacheIdx = MSHRT_MAX; + nOfst = 0; + nAllLines = 0; + nThisLines = 0; + mnFlyAnchorOfst = 0; + mnFlyAnchorOfstNoWrap = 0; + mnFtnLine = 0; + // OD 2004-03-17 #i11860# + mnHeightOfLastLine = 0; + // --> OD 2008-01-31 #newlistlevelattrs# + mnAdditionalFirstLineOffset = 0; + // <-- + + nType = FRMC_TXT; + bLocked = bFormatted = bWidow = bUndersized = bJustWidow = + bEmpty = bInFtnConnect = bFtn = bRepaint = bBlinkPor = + bFieldFollow = bHasAnimation = bIsSwapped = sal_False; + // OD 14.03.2003 #i11760# + mbFollowFormatAllowed = sal_True; +} + +/************************************************************************* + * SwTxtFrm::SwTxtFrm() + *************************************************************************/ +SwTxtFrm::SwTxtFrm(SwTxtNode * const pNode) + : SwCntntFrm(pNode) +{ + InitCtor(); +} + +/************************************************************************* + * SwTxtFrm::~SwTxtFrm() + *************************************************************************/ +SwTxtFrm::~SwTxtFrm() +{ + // Remove associated SwParaPortion from pTxtCache + ClearPara(); +} + +const XubString& SwTxtFrm::GetTxt() const +{ + return GetTxtNode()->GetTxt(); +} + +void SwTxtFrm::ResetPreps() +{ + if ( GetCacheIdx() != MSHRT_MAX ) + { + SwParaPortion *pPara; + if( 0 != (pPara = GetPara()) ) + pPara->ResetPreps(); + } +} + +/************************************************************************* + * SwTxtFrm::IsHiddenNow() + *************************************************************************/ +sal_Bool SwTxtFrm::IsHiddenNow() const +{ + SwFrmSwapper aSwapper( this, sal_True ); + + if( !Frm().Width() && IsValid() && GetUpper()->IsValid() ) + //bei Stackueberlauf (StackHack) invalid! + { +// ASSERT( false, "SwTxtFrm::IsHiddenNow: thin frame" ); + return sal_True; + } + + const bool bHiddenCharsHidePara = GetTxtNode()->HasHiddenCharAttribute( true ); + const bool bHiddenParaField = GetTxtNode()->HasHiddenParaField(); + const ViewShell* pVsh = GetShell(); + + if ( pVsh && ( bHiddenCharsHidePara || bHiddenParaField ) ) + { + if ( + ( bHiddenParaField && + ( !pVsh->GetViewOptions()->IsShowHiddenPara() && + !pVsh->GetViewOptions()->IsFldName() ) ) || + ( bHiddenCharsHidePara && + !pVsh->GetViewOptions()->IsShowHiddenChar() ) ) + { + return sal_True; + } + } + + return sal_False; +} + + +/************************************************************************* + * SwTxtFrm::HideHidden() + *************************************************************************/ +// Entfernt die Anhaengsel des Textfrms wenn dieser hidden ist + +void SwTxtFrm::HideHidden() +{ + ASSERT( !GetFollow() && IsHiddenNow(), + "HideHidden on visible frame of hidden frame has follow" ); + + const xub_StrLen nEnd = STRING_LEN; + HideFootnotes( GetOfst(), nEnd ); + // OD 2004-01-15 #110582# + HideAndShowObjects(); + + //Die Formatinfos sind jetzt obsolete + ClearPara(); +} + +/************************************************************************* + * SwTxtFrm::HideFootnotes() + *************************************************************************/ +void SwTxtFrm::HideFootnotes( xub_StrLen nStart, xub_StrLen nEnd ) +{ + const SwpHints *pHints = GetTxtNode()->GetpSwpHints(); + if( pHints ) + { + const USHORT nSize = pHints->Count(); + SwPageFrm *pPage = 0; + for ( USHORT i = 0; i < nSize; ++i ) + { + const SwTxtAttr *pHt = (*pHints)[i]; + if ( pHt->Which() == RES_TXTATR_FTN ) + { + const xub_StrLen nIdx = *pHt->GetStart(); + if ( nEnd < nIdx ) + break; + if( nStart <= nIdx ) + { + if( !pPage ) + pPage = FindPageFrm(); + pPage->RemoveFtn( this, (SwTxtFtn*)pHt ); + } + } + } + } +} + +// --> OD 2005-03-30 #120729# - hotfix: WW8 documents contain at its end hidden, +// as-character anchored graphics, which are used for a graphic bullet list. +// As long as these graphic bullet list aren't imported, do not hide a +// at-character anchored object, if +// (a) the document is an imported WW8 document - +// checked by checking certain compatibility options -, +// (b) the paragraph is the last content in the document and +// (c) the anchor character is an as-character anchored graphic. +bool lcl_HideObj( const SwTxtFrm& _rFrm, + const RndStdIds _eAnchorType, + const xub_StrLen _nObjAnchorPos, + SwAnchoredObject* _pAnchoredObj ) +{ + bool bRet( true ); + + if ( _eAnchorType == FLY_AUTO_CNTNT ) + { + const IDocumentSettingAccess* pIDSA = _rFrm.GetTxtNode()->getIDocumentSettingAccess(); + if ( !pIDSA->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING) && + !pIDSA->get(IDocumentSettingAccess::OLD_LINE_SPACING) && + !pIDSA->get(IDocumentSettingAccess::USE_FORMER_OBJECT_POS) && + pIDSA->get(IDocumentSettingAccess::CONSIDER_WRAP_ON_OBJECT_POSITION) && + _rFrm.IsInDocBody() && !_rFrm.FindNextCnt() ) + { + const xub_Unicode cAnchorChar = + _rFrm.GetTxtNode()->GetTxt().GetChar( _nObjAnchorPos ); + if ( cAnchorChar == CH_TXTATR_BREAKWORD ) + { + const SwTxtAttr* const pHint( + _rFrm.GetTxtNode()->GetTxtAttrForCharAt(_nObjAnchorPos, + RES_TXTATR_FLYCNT) ); + if ( pHint ) + { + const SwFrmFmt* pFrmFmt = + static_cast<const SwTxtFlyCnt*>(pHint)->GetFlyCnt().GetFrmFmt(); + if ( pFrmFmt->Which() == RES_FLYFRMFMT ) + { + SwNodeIndex nCntntIndex = *(pFrmFmt->GetCntnt().GetCntntIdx()); + nCntntIndex++; + if ( nCntntIndex.GetNode().IsNoTxtNode() ) + { + bRet = false; + // set needed data structure values for object positioning + SWRECTFN( (&_rFrm) ); + SwRect aLastCharRect( _rFrm.Frm() ); + (aLastCharRect.*fnRect->fnSetWidth)( 1 ); + _pAnchoredObj->maLastCharRect = aLastCharRect; + _pAnchoredObj->mnLastTopOfLine = (aLastCharRect.*fnRect->fnGetTop)(); + } + } + } + } + } + } + + return bRet; +} +// <-- +/************************************************************************* + * SwTxtFrm::HideAndShowObjects() + *************************************************************************/ +/** method to hide/show objects + + OD 2004-01-15 #110582# + method hides respectively shows objects, which are anchored at paragraph, + at/as a character of the paragraph, corresponding to the paragraph and + paragraph portion visibility. + + - is called from HideHidden() - should hide objects in hidden paragraphs and + - from _Format() - should hide/show objects in partly visible paragraphs + + @author OD +*/ +void SwTxtFrm::HideAndShowObjects() +{ + if ( GetDrawObjs() ) + { + if ( IsHiddenNow() ) + { + // complete paragraph is hidden. Thus, hide all objects + for ( sal_uInt32 i = 0; i < GetDrawObjs()->Count(); ++i ) + { + SdrObject* pObj = (*GetDrawObjs())[i]->DrawObj(); + SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); + // --> OD 2005-03-30 #120729# - hotfix: do not hide object + // under certain conditions + const RndStdIds eAnchorType( pContact->GetAnchorId() ); + const xub_StrLen nObjAnchorPos = pContact->GetCntntAnchorIndex().GetIndex(); + if ( eAnchorType != FLY_AUTO_CNTNT || + lcl_HideObj( *this, eAnchorType, nObjAnchorPos, (*GetDrawObjs())[i] ) ) + { + pContact->MoveObjToInvisibleLayer( pObj ); + } + // <-- + } + } + else + { + // paragraph is visible, but can contain hidden text portion. + // first we check if objects are allowed to be hidden: + const SwTxtNode& rNode = *GetTxtNode(); + const ViewShell* pVsh = GetShell(); + const bool bShouldBeHidden = !pVsh || !pVsh->GetWin() || + !pVsh->GetViewOptions()->IsShowHiddenChar(); + + // Thus, show all objects, which are anchored at paragraph and + // hide/show objects, which are anchored at/as character, according + // to the visibility of the anchor character. + for ( sal_uInt32 i = 0; i < GetDrawObjs()->Count(); ++i ) + { + SdrObject* pObj = (*GetDrawObjs())[i]->DrawObj(); + SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); + // --> OD 2005-03-30 #120729# - determine anchor type only once + const RndStdIds eAnchorType( pContact->GetAnchorId() ); + // <-- + + if ( eAnchorType == FLY_AT_CNTNT ) + { + pContact->MoveObjToVisibleLayer( pObj ); + } + else if ( eAnchorType == FLY_AUTO_CNTNT || + eAnchorType == FLY_IN_CNTNT ) + { + xub_StrLen nHiddenStart; + xub_StrLen nHiddenEnd; + xub_StrLen nObjAnchorPos = pContact->GetCntntAnchorIndex().GetIndex(); + SwScriptInfo::GetBoundsOfHiddenRange( rNode, nObjAnchorPos, nHiddenStart, nHiddenEnd, 0 ); + // --> OD 2005-03-30 #120729# - hotfix: do not hide object + // under certain conditions + if ( nHiddenStart != STRING_LEN && bShouldBeHidden && + lcl_HideObj( *this, eAnchorType, nObjAnchorPos, (*GetDrawObjs())[i] ) ) + // <-- + pContact->MoveObjToInvisibleLayer( pObj ); + else + pContact->MoveObjToVisibleLayer( pObj ); + } + else + { + ASSERT( false, + "<SwTxtFrm::HideAndShowObjects()> - object not anchored at/inside paragraph!?" ); + } + } + } + } + + if ( IsFollow() ) + { + FindMaster()->HideAndShowObjects(); + } +} + +/************************************************************************* + * SwTxtFrm::FindBrk() + * + * Liefert die erste Trennmoeglichkeit in der aktuellen Zeile zurueck. + * Die Methode wird in SwTxtFrm::Format() benutzt, um festzustellen, ob + * die Vorgaengerzeile mitformatiert werden muss. + * nFound ist <= nEndLine. + *************************************************************************/ + +xub_StrLen SwTxtFrm::FindBrk( const XubString &rTxt, + const xub_StrLen nStart, const xub_StrLen nEnd ) const +{ + xub_StrLen nFound = nStart; + const xub_StrLen nEndLine = Min( nEnd, rTxt.Len() ); + + // Wir ueberlesen erst alle Blanks am Anfang der Zeile (vgl. Bug 2235). + while( nFound <= nEndLine && ' ' == rTxt.GetChar( nFound ) ) + ++nFound; + + // Eine knifflige Sache mit den TxtAttr-Dummy-Zeichen (hier "$"): + // "Dr.$Meyer" am Anfang der zweiten Zeile. Dahinter ein Blank eingegeben + // und das Wort rutscht nicht in die erste Zeile, obwohl es ginge. + // Aus diesem Grund nehmen wir das Dummy-Zeichen noch mit. + while( nFound <= nEndLine && ' ' != rTxt.GetChar( nFound ) ) + ++nFound; + + return nFound; +} + +/************************************************************************* + * SwTxtFrm::IsIdxInside() + *************************************************************************/ + +sal_Bool SwTxtFrm::IsIdxInside( const xub_StrLen nPos, const xub_StrLen nLen ) const +{ + if( GetOfst() > nPos + nLen ) // d.h., der Bereich liegt komplett vor uns. + return sal_False; + + if( !GetFollow() ) // der Bereich liegt nicht komplett vor uns, + return sal_True; // nach uns kommt niemand mehr. + + const xub_StrLen nMax = GetFollow()->GetOfst(); + + // der Bereich liegt nicht komplett hinter uns bzw. + // unser Text ist geloescht worden. + if( nMax > nPos || nMax > GetTxt().Len() ) + return sal_True; + + // changes made in the first line of a follow can modify the master + const SwParaPortion* pPara = GetFollow()->GetPara(); + return pPara && ( nPos <= nMax + pPara->GetLen() ); +} + +/************************************************************************* + * SwTxtFrm::InvalidateRange() + *************************************************************************/ +inline void SwTxtFrm::InvalidateRange(const SwCharRange &aRange, const long nD) +{ + if ( IsIdxInside( aRange.Start(), aRange.Len() ) ) + _InvalidateRange( aRange, nD ); +} + +/************************************************************************* + * SwTxtFrm::_InvalidateRange() + *************************************************************************/ + +void SwTxtFrm::_InvalidateRange( const SwCharRange &aRange, const long nD) +{ + if ( !HasPara() ) + { InvalidateSize(); + return; + } + + SetWidow( sal_False ); + SwParaPortion *pPara = GetPara(); + + sal_Bool bInv = sal_False; + if( 0 != nD ) + { + //Auf nDelta werden die Differenzen zwischen alter und + //neuer Zeilenlaenge aufaddiert, deshalb ist es negativ, + //wenn Zeichen eingefuegt wurden, positiv, wenn Zeichen + //geloescht wurden. + *(pPara->GetDelta()) += nD; + bInv = sal_True; + } + SwCharRange &rReformat = *(pPara->GetReformat()); + if(aRange != rReformat) { + if( STRING_LEN == rReformat.Len() ) + rReformat = aRange; + else + rReformat += aRange; + bInv = sal_True; + } + if(bInv) + { +// 90365: nD is passed to a follow two times +// if( GetFollow() ) +// ((SwTxtFrm*)GetFollow())->InvalidateRange( aRange, nD ); + InvalidateSize(); + } +} + +/************************************************************************* + * SwTxtFrm::CalcLineSpace() + *************************************************************************/ + +void SwTxtFrm::CalcLineSpace() +{ + ASSERT( ! IsVertical() || ! IsSwapped(), + "SwTxtFrm::CalcLineSpace with swapped frame!" ) + + if( IsLocked() || !HasPara() ) + return; + + SwParaPortion *pPara; + if( GetDrawObjs() || + GetTxtNode()->GetSwAttrSet().GetLRSpace().IsAutoFirst() || + ( pPara = GetPara() )->IsFixLineHeight() ) + { + Init(); + return; + } + + Size aNewSize( Prt().SSize() ); + + SwTxtFormatInfo aInf( this ); + SwTxtFormatter aLine( this, &aInf ); + if( aLine.GetDropLines() ) + { + Init(); + return; + } + + aLine.Top(); + aLine.RecalcRealHeight(); + + aNewSize.Height() = (aLine.Y() - Frm().Top()) + aLine.GetLineHeight(); + + SwTwips nDelta = aNewSize.Height() - Prt().Height(); + // 4291: Unterlauf bei Flys + if( aInf.GetTxtFly()->IsOn() ) + { + SwRect aTmpFrm( Frm() ); + if( nDelta < 0 ) + aTmpFrm.Height( Prt().Height() ); + else + aTmpFrm.Height( aNewSize.Height() ); + if( aInf.GetTxtFly()->Relax( aTmpFrm ) ) + { + Init(); + return; + } + } + + if( nDelta ) + { + SwTxtFrmBreak aBreak( this ); + if( GetFollow() || aBreak.IsBreakNow( aLine ) ) + { + // Wenn es einen Follow() gibt, oder wenn wir an dieser + // Stelle aufbrechen muessen, so wird neu formatiert. + Init(); + } + else + { + // Alles nimmt seinen gewohnten Gang ... + pPara->SetPrepAdjust(); + pPara->SetPrep(); + } + } +} + +// +// SET_WRONG( nPos, nCnt, bMove ) +// +#define SET_WRONG( nPos, nCnt, bMove ) \ +{ \ + lcl_SetWrong( *this, nPos, nCnt, bMove ); \ +} + +void lcl_SetWrong( SwTxtFrm& rFrm, xub_StrLen nPos, long nCnt, bool bMove ) +{ + if ( !rFrm.IsFollow() ) + { + SwTxtNode* pTxtNode = rFrm.GetTxtNode(); + IGrammarContact* pGrammarContact = getGrammarContact( *pTxtNode ); + SwGrammarMarkUp* pWrongGrammar = pGrammarContact ? + pGrammarContact->getGrammarCheck( *pTxtNode, false ) : + pTxtNode->GetGrammarCheck(); + bool bGrammarProxy = pWrongGrammar != pTxtNode->GetGrammarCheck(); + if( bMove ) + { + if( pTxtNode->GetWrong() ) + pTxtNode->GetWrong()->Move( nPos, nCnt ); + if( pWrongGrammar ) + pWrongGrammar->MoveGrammar( nPos, nCnt ); + if( bGrammarProxy && pTxtNode->GetGrammarCheck() ) + pTxtNode->GetGrammarCheck()->MoveGrammar( nPos, nCnt ); + if( pTxtNode->GetSmartTags() ) + pTxtNode->GetSmartTags()->Move( nPos, nCnt ); + } + else + { + xub_StrLen nLen = (xub_StrLen)nCnt; + if( pTxtNode->GetWrong() ) + pTxtNode->GetWrong()->Invalidate( nPos, nLen ); + if( pWrongGrammar ) + pWrongGrammar->Invalidate( nPos, nLen ); + if( pTxtNode->GetSmartTags() ) + pTxtNode->GetSmartTags()->Invalidate( nPos, nLen ); + } + if ( !pTxtNode->GetWrong() && !pTxtNode->IsWrongDirty() ) + { + pTxtNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) ); + pTxtNode->GetWrong()->SetInvalid( nPos, nPos + (USHORT)( nCnt > 0 ? nCnt : 1 ) ); + } + if ( !pTxtNode->GetSmartTags() && !pTxtNode->IsSmartTagDirty() ) + { + // SMARTTAGS + pTxtNode->SetSmartTags( new SwWrongList( WRONGLIST_SMARTTAG ) ); + pTxtNode->GetSmartTags()->SetInvalid( nPos, nPos + (USHORT)( nCnt > 0 ? nCnt : 1 ) ); + } + pTxtNode->SetWrongDirty( true ); + pTxtNode->SetGrammarCheckDirty( true ); + pTxtNode->SetWordCountDirty( true ); + pTxtNode->SetAutoCompleteWordDirty( true ); + // SMARTTAGS + pTxtNode->SetSmartTagDirty( true ); + } + + SwRootFrm *pRootFrm = rFrm.FindRootFrm(); + if (pRootFrm) + { + pRootFrm->SetNeedGrammarCheck( TRUE ); + } + + SwPageFrm *pPage = rFrm.FindPageFrm(); + if( pPage ) + { + pPage->InvalidateSpelling(); + pPage->InvalidateAutoCompleteWords(); + pPage->InvalidateWordCount(); + pPage->InvalidateSmartTags(); + } +} + +// +// SET_SCRIPT_INVAL( nPos ) +// + +#define SET_SCRIPT_INVAL( nPos )\ + lcl_SetScriptInval( *this, nPos ); + +void lcl_SetScriptInval( SwTxtFrm& rFrm, xub_StrLen nPos ) +{ + if( rFrm.GetPara() ) + rFrm.GetPara()->GetScriptInfo().SetInvalidity( nPos ); +} + +void lcl_ModifyOfst( SwTxtFrm* pFrm, xub_StrLen nPos, xub_StrLen nLen ) +{ + while( pFrm && pFrm->GetOfst() <= nPos ) + pFrm = pFrm->GetFollow(); + while( pFrm ) + { + pFrm->ManipOfst( pFrm->GetOfst() + nLen ); + pFrm = pFrm->GetFollow(); + } +} + +/************************************************************************* + * SwTxtFrm::Modify() + *************************************************************************/ + +void SwTxtFrm::Modify( SfxPoolItem *pOld, SfxPoolItem *pNew ) +{ + const MSHORT nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0; + + //Wuensche die FrmAttribute betreffen werden von der Basisklasse + //verarbeitet. + if( IsInRange( aFrmFmtSetRange, nWhich ) || RES_FMT_CHG == nWhich ) + { + SwCntntFrm::Modify( pOld, pNew ); + if( nWhich == RES_FMT_CHG && GetShell() ) + { + // Collection hat sich geaendert + Prepare( PREP_CLEAR ); + _InvalidatePrt(); + SET_WRONG( 0, STRING_LEN, false ); + SetDerivedR2L( sal_False ); + CheckDirChange(); + // OD 09.12.2002 #105576# - Force complete paint due to existing + // indents. + SetCompletePaint(); + InvalidateLineNum(); + } + return; + } + + // Im gelockten Zustand werden keine Bestellungen angenommen. + if( IsLocked() ) + return; + + // Dies spart Stack, man muss nur aufpassen, + // dass sie Variablen gesetzt werden. + xub_StrLen nPos, nLen; + sal_Bool bSetFldsDirty = sal_False; + sal_Bool bRecalcFtnFlag = sal_False; + + switch( nWhich ) + { + case RES_LINENUMBER: + { + InvalidateLineNum(); + } + break; + case RES_INS_CHR: + { + nPos = ((SwInsChr*)pNew)->nPos; + InvalidateRange( SwCharRange( nPos, 1 ), 1 ); + SET_WRONG( nPos, 1, true ) + SET_SCRIPT_INVAL( nPos ) + bSetFldsDirty = sal_True; + if( HasFollow() ) + lcl_ModifyOfst( this, nPos, 1 ); + } + break; + case RES_INS_TXT: + { + nPos = ((SwInsTxt*)pNew)->nPos; + nLen = ((SwInsTxt*)pNew)->nLen; + if( IsIdxInside( nPos, nLen ) ) + { + if( !nLen ) + { + // 6969: Aktualisierung der NumPortions auch bei leeren Zeilen! + if( nPos ) + InvalidateSize(); + else + Prepare( PREP_CLEAR ); + } + else + _InvalidateRange( SwCharRange( nPos, nLen ), nLen ); + } + SET_WRONG( nPos, nLen, true ) + SET_SCRIPT_INVAL( nPos ) + bSetFldsDirty = sal_True; + if( HasFollow() ) + lcl_ModifyOfst( this, nPos, nLen ); + } + break; + case RES_DEL_CHR: + { + nPos = ((SwDelChr*)pNew)->nPos; + InvalidateRange( SwCharRange( nPos, 1 ), -1 ); + SET_WRONG( nPos, -1, true ) + SET_SCRIPT_INVAL( nPos ) + bSetFldsDirty = bRecalcFtnFlag = sal_True; + if( HasFollow() ) + lcl_ModifyOfst( this, nPos, STRING_LEN ); + } + break; + case RES_DEL_TXT: + { + nPos = ((SwDelTxt*)pNew)->nStart; + nLen = ((SwDelTxt*)pNew)->nLen; + long m = nLen; + m *= -1; + if( IsIdxInside( nPos, nLen ) ) + { + if( !nLen ) + InvalidateSize(); + else + InvalidateRange( SwCharRange( nPos, 1 ), m ); + } + SET_WRONG( nPos, m, true ) + SET_SCRIPT_INVAL( nPos ) + bSetFldsDirty = bRecalcFtnFlag = sal_True; + if( HasFollow() ) + lcl_ModifyOfst( this, nPos, nLen ); + } + break; + case RES_UPDATE_ATTR: + { + nPos = ((SwUpdateAttr*)pNew)->nStart; + nLen = ((SwUpdateAttr*)pNew)->nEnd - nPos; + if( IsIdxInside( nPos, nLen ) ) + { + // Es muss in jedem Fall neu formatiert werden, + // auch wenn der invalidierte Bereich null ist. + // Beispiel: leere Zeile, 14Pt einstellen ! + // if( !nLen ) nLen = 1; + + // 6680: FtnNummern muessen formatiert werden. + if( !nLen ) + nLen = 1; + + _InvalidateRange( SwCharRange( nPos, nLen) ); + MSHORT nTmp = ((SwUpdateAttr*)pNew)->nWhichAttr; + + if( ! nTmp || RES_TXTATR_CHARFMT == nTmp || RES_TXTATR_AUTOFMT == nTmp || + RES_FMT_CHG == nTmp || RES_ATTRSET_CHG == nTmp ) + { + SET_WRONG( nPos, nPos + nLen, false ) + SET_SCRIPT_INVAL( nPos ) + } + } + } + break; + case RES_OBJECTDYING: + break; + + case RES_PARATR_LINESPACING: + { + CalcLineSpace(); + InvalidateSize(); + _InvalidatePrt(); + if( IsInSct() && !GetPrev() ) + { + SwSectionFrm *pSect = FindSctFrm(); + if( pSect->ContainsAny() == this ) + pSect->InvalidatePrt(); + } + + // OD 09.01.2004 #i11859# - correction: + // (1) Also invalidate next frame on next page/column. + // (2) Skip empty sections and hidden paragraphs + // Thus, use method <InvalidateNextPrtArea()> + InvalidateNextPrtArea(); + + SetCompletePaint(); + } + break; + case RES_TXTATR_FIELD: + { + nPos = *((SwFmtFld*)pNew)->GetTxtFld()->GetStart(); + if( IsIdxInside( nPos, 1 ) ) + { + if( pNew == pOld ) + { + // Nur repainten + // opt: invalidate aufs Window ? + InvalidatePage(); + SetCompletePaint(); + } + else + _InvalidateRange( SwCharRange( nPos, 1 ) ); + } + bSetFldsDirty = sal_True; + // ST2 + if ( SwSmartTagMgr::Get().IsSmartTagsEnabled() ) + SET_WRONG( nPos, nPos + 1, false ) + } + break; + case RES_TXTATR_FTN : + { + nPos = *((SwFmtFtn*)pNew)->GetTxtFtn()->GetStart(); + if( IsInFtn() || IsIdxInside( nPos, 1 ) ) + Prepare( PREP_FTN, ((SwFmtFtn*)pNew)->GetTxtFtn() ); + break; + } + + case RES_ATTRSET_CHG: + { + InvalidateLineNum(); + + SwAttrSet& rNewSet = *((SwAttrSetChg*)pNew)->GetChgSet(); + const SfxPoolItem* pItem; + int nClear = 0; + MSHORT nCount = rNewSet.Count(); + + if( SFX_ITEM_SET == rNewSet.GetItemState( RES_TXTATR_FTN, + sal_False, &pItem )) + { + nPos = *((SwFmtFtn*)pItem)->GetTxtFtn()->GetStart(); + if( IsIdxInside( nPos, 1 ) ) + Prepare( PREP_FTN, pNew ); + nClear = 0x01; + --nCount; + } + + if( SFX_ITEM_SET == rNewSet.GetItemState( RES_TXTATR_FIELD, + sal_False, &pItem )) + { + nPos = *((SwFmtFld*)pItem)->GetTxtFld()->GetStart(); + if( IsIdxInside( nPos, 1 ) ) + { + const SfxPoolItem& rOldItem = ((SwAttrSetChg*)pOld)-> + GetChgSet()->Get( RES_TXTATR_FIELD ); + if( pItem == &rOldItem ) + { + // Nur repainten + // opt: invalidate aufs Window ? + InvalidatePage(); + SetCompletePaint(); + } + else + _InvalidateRange( SwCharRange( nPos, 1 ) ); + } + nClear |= 0x02; + --nCount; + } + sal_Bool bLineSpace = SFX_ITEM_SET == rNewSet.GetItemState( + RES_PARATR_LINESPACING, sal_False ), + bRegister = SFX_ITEM_SET == rNewSet.GetItemState( + RES_PARATR_REGISTER, sal_False ); + if ( bLineSpace || bRegister ) + { + Prepare( bRegister ? PREP_REGISTER : PREP_ADJUST_FRM ); + CalcLineSpace(); + InvalidateSize(); + _InvalidatePrt(); + + // OD 09.01.2004 #i11859# - correction: + // (1) Also invalidate next frame on next page/column. + // (2) Skip empty sections and hidden paragraphs + // Thus, use method <InvalidateNextPrtArea()> + InvalidateNextPrtArea(); + + SetCompletePaint(); + nClear |= 0x04; + if ( bLineSpace ) + { + --nCount; + if( IsInSct() && !GetPrev() ) + { + SwSectionFrm *pSect = FindSctFrm(); + if( pSect->ContainsAny() == this ) + pSect->InvalidatePrt(); + } + } + if ( bRegister ) + --nCount; + } + if ( SFX_ITEM_SET == rNewSet.GetItemState( RES_PARATR_SPLIT, + sal_False )) + { + if ( GetPrev() ) + CheckKeep(); + Prepare( PREP_CLEAR ); + InvalidateSize(); + nClear |= 0x08; + --nCount; + } + + if( SFX_ITEM_SET == rNewSet.GetItemState( RES_BACKGROUND, sal_False) + && !IsFollow() && GetDrawObjs() ) + { + SwSortedObjs *pObjs = GetDrawObjs(); + for ( int i = 0; GetDrawObjs() && i < int(pObjs->Count()); ++i ) + { + SwAnchoredObject* pAnchoredObj = (*pObjs)[MSHORT(i)]; + if ( pAnchoredObj->ISA(SwFlyFrm) ) + { + SwFlyFrm *pFly = static_cast<SwFlyFrm*>(pAnchoredObj); + if( !pFly->IsFlyInCntFrm() ) + { + const SvxBrushItem &rBack = + pFly->GetAttrSet()->GetBackground(); + // OD 20.08.2002 #99657# #GetTransChg# + // following condition determines, if the fly frame + // "inherites" the background color of text frame. + // This is the case, if fly frame background + // color is "no fill"/"auto fill" and if the fly frame + // has no background graphic. + // Thus, check complete fly frame background + // color and *not* only its transparency value + if ( (rBack.GetColor() == COL_TRANSPARENT) && + //if( rBack.GetColor().GetTransparency() && + rBack.GetGraphicPos() == GPOS_NONE ) + { + pFly->SetCompletePaint(); + pFly->InvalidatePage(); + } + } + } + } + } + + if ( SFX_ITEM_SET == + rNewSet.GetItemState( RES_TXTATR_CHARFMT, sal_False ) ) + { + SET_WRONG( 0, STRING_LEN, false ) + SET_SCRIPT_INVAL( 0 ) + } + else if ( SFX_ITEM_SET == + rNewSet.GetItemState( RES_CHRATR_LANGUAGE, sal_False ) || + SFX_ITEM_SET == + rNewSet.GetItemState( RES_CHRATR_CJK_LANGUAGE, sal_False ) || + SFX_ITEM_SET == + rNewSet.GetItemState( RES_CHRATR_CTL_LANGUAGE, sal_False ) ) + SET_WRONG( 0, STRING_LEN, false ) + else if ( SFX_ITEM_SET == + rNewSet.GetItemState( RES_CHRATR_FONT, sal_False ) || + SFX_ITEM_SET == + rNewSet.GetItemState( RES_CHRATR_CJK_FONT, sal_False ) || + SFX_ITEM_SET == + rNewSet.GetItemState( RES_CHRATR_CTL_FONT, sal_False ) ) + SET_SCRIPT_INVAL( 0 ) + else if ( SFX_ITEM_SET == + rNewSet.GetItemState( RES_FRAMEDIR, sal_False ) ) + { + SetDerivedR2L( sal_False ); + CheckDirChange(); + // OD 09.12.2002 #105576# - Force complete paint due to existing + // indents. + SetCompletePaint(); + } + + + if( nCount ) + { + if( GetShell() ) + { + Prepare( PREP_CLEAR ); + _InvalidatePrt(); + } + + if( nClear ) + { + SwAttrSetChg aOldSet( *(SwAttrSetChg*)pOld ); + SwAttrSetChg aNewSet( *(SwAttrSetChg*)pNew ); + + if( 0x01 & nClear ) + { + aOldSet.ClearItem( RES_TXTATR_FTN ); + aNewSet.ClearItem( RES_TXTATR_FTN ); + } + if( 0x02 & nClear ) + { + aOldSet.ClearItem( RES_TXTATR_FIELD ); + aNewSet.ClearItem( RES_TXTATR_FIELD ); + } + if ( 0x04 & nClear ) + { + if ( bLineSpace ) + { + aOldSet.ClearItem( RES_PARATR_LINESPACING ); + aNewSet.ClearItem( RES_PARATR_LINESPACING ); + } + if ( bRegister ) + { + aOldSet.ClearItem( RES_PARATR_REGISTER ); + aNewSet.ClearItem( RES_PARATR_REGISTER ); + } + } + if ( 0x08 & nClear ) + { + aOldSet.ClearItem( RES_PARATR_SPLIT ); + aNewSet.ClearItem( RES_PARATR_SPLIT ); + } + SwCntntFrm::Modify( &aOldSet, &aNewSet ); + } + else + SwCntntFrm::Modify( pOld, pNew ); + } + + // --> OD 2009-01-06 #i88069# + if ( GetShell() ) + { + GetShell()->InvalidateAccessibleParaAttrs( *this ); + } + // <-- + } + break; + +/* Seit dem neuen Blocksatz muessen wir immer neu formatieren: + case RES_PARATR_ADJUST: + { + if( GetShell() ) + { + Prepare( PREP_CLEAR ); + } + break; + } +*/ + // 6870: SwDocPosUpdate auswerten. + case RES_DOCPOS_UPDATE: + { + if( pOld && pNew ) + { + const SwDocPosUpdate *pDocPos = (const SwDocPosUpdate*)pOld; + if( pDocPos->nDocPos <= aFrm.Top() ) + { + const SwFmtFld *pFld = (const SwFmtFld *)pNew; + InvalidateRange( + SwCharRange( *pFld->GetTxtFld()->GetStart(), 1 ) ); + } + } + break; + } + case RES_PARATR_SPLIT: + if ( GetPrev() ) + CheckKeep(); + Prepare( PREP_CLEAR ); + bSetFldsDirty = sal_True; + break; + case RES_FRAMEDIR : + SetDerivedR2L( sal_False ); + CheckDirChange(); + break; + default: + { + Prepare( PREP_CLEAR ); + _InvalidatePrt(); + if ( !nWhich ) + { + //Wird z.B. bei HiddenPara mit 0 gerufen. + SwFrm *pNxt; + if ( 0 != (pNxt = FindNext()) ) + pNxt->InvalidatePrt(); + } + } + } // switch + + if( bSetFldsDirty ) + GetNode()->getIDocumentFieldsAccess()->SetFieldsDirty( sal_True, GetNode(), 1 ); + + if ( bRecalcFtnFlag ) + CalcFtnFlag(); +} + +sal_Bool SwTxtFrm::GetInfo( SfxPoolItem &rHnt ) const +{ + if ( RES_VIRTPAGENUM_INFO == rHnt.Which() && IsInDocBody() && ! IsFollow() ) + { + SwVirtPageNumInfo &rInfo = (SwVirtPageNumInfo&)rHnt; + const SwPageFrm *pPage = FindPageFrm(); + if ( pPage ) + { + if ( pPage == rInfo.GetOrigPage() && !GetPrev() ) + { + //Das sollte er sein (kann allenfalls temporaer anders sein, + // sollte uns das beunruhigen?) + rInfo.SetInfo( pPage, this ); + return sal_False; + } + if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() && + (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum())) + { + //Das koennte er sein. + rInfo.SetInfo( pPage, this ); + } + } + } + return sal_True; +} + +/************************************************************************* + * SwTxtFrm::PrepWidows() + *************************************************************************/ + +void SwTxtFrm::PrepWidows( const MSHORT nNeed, sal_Bool bNotify ) +{ + ASSERT(GetFollow() && nNeed, "+SwTxtFrm::Prepare: lost all friends"); + + SwParaPortion *pPara = GetPara(); + if ( !pPara ) + return; + pPara->SetPrepWidows( sal_True ); + + // These two lines of code have been deleted for #102340#. + // Obviously the widow control does not work if we have a + // pMaster->pFollow->pFollow situation: + + // returnen oder nicht ist hier die Frage. + // Ohne IsLocked() ist 5156 gefaehrlich, + // ohne IsFollow() werden die Orphans unterdrueckt: 6968. + // Abfrage auf IsLocked erst hier, weil das Flag gesetzt werden soll. +// if( IsLocked() && IsFollow() ) +// return; + + MSHORT nHave = nNeed; + + // Wir geben ein paar Zeilen ab und schrumpfen im CalcPreps() + SWAP_IF_NOT_SWAPPED( this ) + + SwTxtSizeInfo aInf( this ); + SwTxtMargin aLine( this, &aInf ); + aLine.Bottom(); + xub_StrLen nTmpLen = aLine.GetCurr()->GetLen(); + while( nHave && aLine.PrevLine() ) + { + if( nTmpLen ) + --nHave; + nTmpLen = aLine.GetCurr()->GetLen(); + } + // In dieser Ecke tummelten sich einige Bugs: 7513, 7606. + // Wenn feststeht, dass Zeilen abgegeben werden koennen, + // muss der Master darueber hinaus die Widow-Regel ueberpruefen. + if( !nHave ) + { + sal_Bool bSplit; + if( !IsFollow() ) //Nur ein Master entscheidet ueber Orphans + { + const WidowsAndOrphans aWidOrp( this ); + bSplit = ( aLine.GetLineNr() >= aWidOrp.GetOrphansLines() && + aLine.GetLineNr() >= aLine.GetDropLines() ); + } + else + bSplit = sal_True; + + if( bSplit ) + { + GetFollow()->SetOfst( aLine.GetEnd() ); + aLine.TruncLines( sal_True ); + if( pPara->IsFollowField() ) + GetFollow()->SetFieldFollow( sal_True ); + } + } + if ( bNotify ) + { + _InvalidateSize(); + InvalidatePage(); + } + + UNDO_SWAP( this ) +} + +/************************************************************************* + * SwTxtFrm::Prepare + *************************************************************************/ + +sal_Bool lcl_ErgoVadis( SwTxtFrm* pFrm, xub_StrLen &rPos, const PrepareHint ePrep ) +{ + const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo(); + if( ePrep == PREP_ERGOSUM ) + { + if( !rFtnInfo.aErgoSum.Len() ) + return sal_False;; + rPos = pFrm->GetOfst(); + } + else + { + if( !rFtnInfo.aQuoVadis.Len() ) + return sal_False; + if( pFrm->HasFollow() ) + rPos = pFrm->GetFollow()->GetOfst(); + else + rPos = pFrm->GetTxt().Len(); + if( rPos ) + --rPos; // unser letztes Zeichen + } + return sal_True; +} + +void SwTxtFrm::Prepare( const PrepareHint ePrep, const void* pVoid, + sal_Bool bNotify ) +{ + SwFrmSwapper aSwapper( this, sal_False ); + +#if OSL_DEBUG_LEVEL > 1 + const SwTwips nDbgY = Frm().Top(); + (void)nDbgY; +#endif + + if ( IsEmpty() ) + { + switch ( ePrep ) + { + case PREP_BOSS_CHGD: + SetInvalidVert( TRUE ); // Test + case PREP_WIDOWS_ORPHANS: + case PREP_WIDOWS: + case PREP_FTN_GONE : return; + + case PREP_POS_CHGD : + { + // Auch in (spaltigen) Bereichen ist ein InvalidateSize notwendig, + // damit formatiert wird und ggf. das bUndersized gesetzt wird. + if( IsInFly() || IsInSct() ) + { + SwTwips nTmpBottom = GetUpper()->Frm().Top() + + GetUpper()->Prt().Bottom(); + if( nTmpBottom < Frm().Bottom() ) + break; + } + // Gibt es ueberhaupt Flys auf der Seite ? + SwTxtFly aTxtFly( this ); + if( aTxtFly.IsOn() ) + { + // Ueberlappt irgendein Fly ? + aTxtFly.Relax(); + if ( aTxtFly.IsOn() || IsUndersized() ) + break; + } + if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue()) + break; + + GETGRID( FindPageFrm() ) + if ( pGrid && GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() ) + break; + + // --> OD 2004-07-16 #i28701# - consider anchored objects + if ( GetDrawObjs() ) + break; + // <-- + + return; + } + default: + break; + } + } + + if( !HasPara() && PREP_MUST_FIT != ePrep ) + { + SetInvalidVert( TRUE ); // Test + ASSERT( !IsLocked(), "SwTxtFrm::Prepare: three of a perfect pair" ); + if ( bNotify ) + InvalidateSize(); + else + _InvalidateSize(); + return; + } + + //Objekt mit Locking aus dem Cache holen. + SwTxtLineAccess aAccess( this ); + SwParaPortion *pPara = aAccess.GetPara(); + + switch( ePrep ) + { + case PREP_MOVEFTN : Frm().Height(0); + Prt().Height(0); + _InvalidatePrt(); + _InvalidateSize(); + // KEIN break + case PREP_ADJUST_FRM : pPara->SetPrepAdjust( sal_True ); + if( IsFtnNumFrm() != pPara->IsFtnNum() || + IsUndersized() ) + { + InvalidateRange( SwCharRange( 0, 1 ), 1); + if( GetOfst() && !IsFollow() ) + _SetOfst( 0 ); + } + break; + case PREP_MUST_FIT : pPara->SetPrepMustFit( sal_True ); + /* no break here */ + case PREP_WIDOWS_ORPHANS : pPara->SetPrepAdjust( sal_True ); + break; + + case PREP_WIDOWS : + // MustFit ist staerker als alles anderes + if( pPara->IsPrepMustFit() ) + return; + // Siehe Kommentar in WidowsAndOrphans::FindOrphans und CalcPreps() + PrepWidows( *(const MSHORT *)pVoid, bNotify ); + break; + + case PREP_FTN : + { + SwTxtFtn *pFtn = (SwTxtFtn *)pVoid; + if( IsInFtn() ) + { + // Bin ich der erste TxtFrm einer Fussnote ? + if( !GetPrev() ) + // Wir sind also ein TxtFrm der Fussnote, die + // die Fussnotenzahl zur Anzeige bringen muss. + // Oder den ErgoSum-Text... + InvalidateRange( SwCharRange( 0, 1 ), 1); + + if( !GetNext() ) + { + // Wir sind der letzte Ftn, jetzt muessten die + // QuoVadis-Texte geupdated werden. + const SwFtnInfo &rFtnInfo = GetNode()->GetDoc()->GetFtnInfo(); + if( !pPara->UpdateQuoVadis( rFtnInfo.aQuoVadis ) ) + { + xub_StrLen nPos = pPara->GetParLen(); + if( nPos ) + --nPos; + InvalidateRange( SwCharRange( nPos, 1 ), 1); + } + } + } + else + { + // Wir sind also der TxtFrm _mit_ der Fussnote + const xub_StrLen nPos = *pFtn->GetStart(); + InvalidateRange( SwCharRange( nPos, 1 ), 1); + } + break; + } + case PREP_BOSS_CHGD : + { + // Test + { + SetInvalidVert( FALSE ); + BOOL bOld = IsVertical(); + SetInvalidVert( TRUE ); + if( bOld != IsVertical() ) + InvalidateRange( SwCharRange( GetOfst(), STRING_LEN ) ); + } + + if( HasFollow() ) + { + xub_StrLen nNxtOfst = GetFollow()->GetOfst(); + if( nNxtOfst ) + --nNxtOfst; + InvalidateRange( SwCharRange( nNxtOfst, 1 ), 1); + } + if( IsInFtn() ) + { + xub_StrLen nPos; + if( lcl_ErgoVadis( this, nPos, PREP_QUOVADIS ) ) + InvalidateRange( SwCharRange( nPos, 1 ), 0 ); + if( lcl_ErgoVadis( this, nPos, PREP_ERGOSUM ) ) + InvalidateRange( SwCharRange( nPos, 1 ), 0 ); + } + // 4739: Wenn wir ein Seitennummernfeld besitzen, muessen wir + // die Stellen invalidieren. + SwpHints *pHints = GetTxtNode()->GetpSwpHints(); + if( pHints ) + { + const USHORT nSize = pHints->Count(); + const xub_StrLen nEnd = GetFollow() ? + GetFollow()->GetOfst() : STRING_LEN; + for ( USHORT i = 0; i < nSize; ++i ) + { + const SwTxtAttr *pHt = (*pHints)[i]; + const xub_StrLen nStart = *pHt->GetStart(); + if( nStart >= GetOfst() ) + { + if( nStart >= nEnd ) + i = nSize; // fuehrt das Ende herbei + else + { + // 4029: wenn wir zurueckfliessen und eine Ftn besitzen, so + // fliesst die Ftn in jedem Fall auch mit. Damit sie nicht im + // Weg steht, schicken wir uns ein ADJUST_FRM. + // pVoid != 0 bedeutet MoveBwd() + const MSHORT nWhich = pHt->Which(); + if( RES_TXTATR_FIELD == nWhich || + (HasFtn() && pVoid && RES_TXTATR_FTN == nWhich)) + InvalidateRange( SwCharRange( nStart, 1 ), 1 ); + } + } + } + } + // A new boss, a new chance for growing + if( IsUndersized() ) + { + _InvalidateSize(); + InvalidateRange( SwCharRange( GetOfst(), 1 ), 1); + } + break; + } + + case PREP_POS_CHGD : + { + if ( GetValidPrtAreaFlag() ) + { + GETGRID( FindPageFrm() ) + if ( pGrid && GetTxtNode()->GetSwAttrSet().GetParaGrid().GetValue() ) + InvalidatePrt(); + } + + // Falls wir mit niemandem ueberlappen: + // Ueberlappte irgendein Fly _vor_ der Positionsaenderung ? + sal_Bool bFormat = pPara->HasFly(); + if( !bFormat ) + { + if( IsInFly() ) + { + SwTwips nTmpBottom = GetUpper()->Frm().Top() + + GetUpper()->Prt().Bottom(); + if( nTmpBottom < Frm().Bottom() ) + bFormat = sal_True; + } + if( !bFormat ) + { + if ( GetDrawObjs() ) + { + const sal_uInt32 nCnt = GetDrawObjs()->Count(); + for ( MSHORT i = 0; i < nCnt; ++i ) + { + SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i]; + // --> OD 2004-07-16 #i28701# - consider all + // to-character anchored objects + if ( pAnchoredObj->GetFrmFmt().GetAnchor().GetAnchorId() + == FLY_AUTO_CNTNT ) + { + bFormat = sal_True; + break; + } + } + } + if( !bFormat ) + { + // Gibt es ueberhaupt Flys auf der Seite ? + SwTxtFly aTxtFly( this ); + if( aTxtFly.IsOn() ) + { + // Ueberlappt irgendein Fly ? + aTxtFly.Relax(); + bFormat = aTxtFly.IsOn() || IsUndersized(); + } + } + } + } + + if( bFormat ) + { + if( !IsLocked() ) + { + if( pPara->GetRepaint()->HasArea() ) + SetCompletePaint(); + Init(); + pPara = 0; + _InvalidateSize(); + } + } + else + { + if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue() ) + Prepare( PREP_REGISTER, 0, bNotify ); + // Durch Positionsverschiebungen mit Ftns muessen die + // Frames neu adjustiert werden. + else if( HasFtn() ) + { + Prepare( PREP_ADJUST_FRM, 0, bNotify ); + _InvalidateSize(); + } + else + return; // damit kein SetPrep() erfolgt. + } + break; + } + case PREP_REGISTER: + if( GetTxtNode()->GetSwAttrSet().GetRegister().GetValue() ) + { + pPara->SetPrepAdjust( sal_True ); + CalcLineSpace(); + InvalidateSize(); + _InvalidatePrt(); + SwFrm* pNxt; + if ( 0 != ( pNxt = GetIndNext() ) ) + { + pNxt->_InvalidatePrt(); + if ( pNxt->IsLayoutFrm() ) + pNxt->InvalidatePage(); + } + SetCompletePaint(); + } + break; + case PREP_FTN_GONE : + { + // Wenn ein Follow uns ruft, weil eine Fussnote geloescht wird, muss unsere + // letzte Zeile formatiert werden, damit ggf. die erste Zeile des Follows + // hochrutschen kann, die extra auf die naechste Seite gerutscht war, um mit + // der Fussnote zusammen zu sein, insbesondere bei spaltigen Bereichen. + ASSERT( GetFollow(), "PREP_FTN_GONE darf nur vom Follow gerufen werden" ); + xub_StrLen nPos = GetFollow()->GetOfst(); + if( IsFollow() && GetOfst() == nPos ) // falls wir gar keine Textmasse besitzen, + FindMaster()->Prepare( PREP_FTN_GONE ); // rufen wir das Prepare unseres Masters + if( nPos ) + --nPos; // das Zeichen vor unserem Follow + InvalidateRange( SwCharRange( nPos, 1 ), 0 ); + return; + } + case PREP_ERGOSUM: + case PREP_QUOVADIS: + { + xub_StrLen nPos; + if( lcl_ErgoVadis( this, nPos, ePrep ) ) + InvalidateRange( SwCharRange( nPos, 1 ), 0 ); + } + break; + case PREP_FLY_ATTR_CHG: + { + if( pVoid ) + { + xub_StrLen nWhere = CalcFlyPos( (SwFrmFmt*)pVoid ); + ASSERT( STRING_LEN != nWhere, "Prepare: Why me?" ); + InvalidateRange( SwCharRange( nWhere, 1 ) ); + return; + } + // else ... Laufe in den Default-Switch + } + case PREP_CLEAR: + default: + { + if( IsLocked() ) + { + if( PREP_FLY_ARRIVE == ePrep || PREP_FLY_LEAVE == ePrep ) + { + xub_StrLen nLen = ( GetFollow() ? GetFollow()->GetOfst() : + STRING_LEN ) - GetOfst(); + InvalidateRange( SwCharRange( GetOfst(), nLen ), 0 ); + } + } + else + { + if( pPara->GetRepaint()->HasArea() ) + SetCompletePaint(); + Init(); + pPara = 0; + if( GetOfst() && !IsFollow() ) + _SetOfst( 0 ); + if ( bNotify ) + InvalidateSize(); + else + _InvalidateSize(); + } + return; // damit kein SetPrep() erfolgt. + } + } + if( pPara ) + pPara->SetPrep( sal_True ); +} + +/* -----------------11.02.99 17:56------------------- + * Kleine Hilfsklasse mit folgender Funktion: + * Sie soll eine Probeformatierung vorbereiten. + * Der Frame wird in Groesse und Position angepasst, sein SwParaPortion zur Seite + * gestellt und eine neue erzeugt, dazu wird formatiert mit gesetztem bTestFormat. + * Im Dtor wird der TxtFrm wieder in seinen alten Zustand zurueckversetzt. + * + * --------------------------------------------------*/ + +class SwTestFormat +{ + SwTxtFrm *pFrm; + SwParaPortion *pOldPara; + SwRect aOldFrm, aOldPrt; +public: + SwTestFormat( SwTxtFrm* pTxtFrm, const SwFrm* pPrv, SwTwips nMaxHeight ); + ~SwTestFormat(); +}; + +SwTestFormat::SwTestFormat( SwTxtFrm* pTxtFrm, const SwFrm* pPre, SwTwips nMaxHeight ) + : pFrm( pTxtFrm ) +{ + aOldFrm = pFrm->Frm(); + aOldPrt = pFrm->Prt(); + + SWRECTFN( pFrm ) + SwTwips nLower = (pFrm->*fnRect->fnGetBottomMargin)(); + + pFrm->Frm() = pFrm->GetUpper()->Prt(); + pFrm->Frm() += pFrm->GetUpper()->Frm().Pos(); + + (pFrm->Frm().*fnRect->fnSetHeight)( nMaxHeight ); + if( pFrm->GetPrev() ) + (pFrm->Frm().*fnRect->fnSetPosY)( + (pFrm->GetPrev()->Frm().*fnRect->fnGetBottom)() - + ( bVert ? nMaxHeight + 1 : 0 ) ); + + SwBorderAttrAccess aAccess( SwFrm::GetCache(), pFrm ); + const SwBorderAttrs &rAttrs = *aAccess.Get(); + (pFrm->Prt().*fnRect->fnSetPosX)( rAttrs.CalcLeft( pFrm ) ); + + if( pPre ) + { + SwTwips nUpper = pFrm->CalcUpperSpace( &rAttrs, pPre ); + (pFrm->Prt().*fnRect->fnSetPosY)( nUpper ); + } + (pFrm->Prt().*fnRect->fnSetHeight)( + Max( 0L , (pFrm->Frm().*fnRect->fnGetHeight)() - + (pFrm->Prt().*fnRect->fnGetTop)() - nLower ) ); + (pFrm->Prt().*fnRect->fnSetWidth)( + (pFrm->Frm().*fnRect->fnGetWidth)() - + // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)> + ( rAttrs.CalcLeft( pFrm ) + rAttrs.CalcRight( pFrm ) ) ); + pOldPara = pFrm->HasPara() ? pFrm->GetPara() : NULL; + pFrm->SetPara( new SwParaPortion(), sal_False ); + + ASSERT( ! pFrm->IsSwapped(), "A frame is swapped before _Format" ); + + if ( pFrm->IsVertical() ) + pFrm->SwapWidthAndHeight(); + + SwTxtFormatInfo aInf( pFrm, sal_False, sal_True, sal_True ); + SwTxtFormatter aLine( pFrm, &aInf ); + + pFrm->_Format( aLine, aInf ); + + if ( pFrm->IsVertical() ) + pFrm->SwapWidthAndHeight(); + + ASSERT( ! pFrm->IsSwapped(), "A frame is swapped after _Format" ); +} + +SwTestFormat::~SwTestFormat() +{ + pFrm->Frm() = aOldFrm; + pFrm->Prt() = aOldPrt; + pFrm->SetPara( pOldPara ); +} + +sal_Bool SwTxtFrm::TestFormat( const SwFrm* pPrv, SwTwips &rMaxHeight, sal_Bool &bSplit ) +{ + PROTOCOL_ENTER( this, PROT_TESTFORMAT, 0, 0 ) + + if( IsLocked() && GetUpper()->Prt().Width() <= 0 ) + return sal_False; + + SwTestFormat aSave( this, pPrv, rMaxHeight ); + + return SwTxtFrm::WouldFit( rMaxHeight, bSplit, sal_True ); +} + + +/************************************************************************* + * SwTxtFrm::WouldFit() + *************************************************************************/ + +/* SwTxtFrm::WouldFit() + * sal_True: wenn ich aufspalten kann. + * Es soll und braucht nicht neu formatiert werden. + * Wir gehen davon aus, dass bereits formatiert wurde und dass + * die Formatierungsdaten noch aktuell sind. + * Wir gehen davon aus, dass die Framebreiten des evtl. Masters und + * Follows gleich sind. Deswegen wird kein FindBreak() mit FindOrphans() + * gerufen. + * Die benoetigte Hoehe wird von nMaxHeight abgezogen! + */ + +sal_Bool SwTxtFrm::WouldFit( SwTwips &rMaxHeight, sal_Bool &bSplit, sal_Bool bTst ) +{ + ASSERT( ! IsVertical() || ! IsSwapped(), + "SwTxtFrm::WouldFit with swapped frame" ); + SWRECTFN( this ); + + if( IsLocked() ) + return sal_False; + + //Kann gut sein, dass mir der IdleCollector mir die gecachten + //Informationen entzogen hat. + if( !IsEmpty() ) + GetFormatted(); + + // OD 2004-05-24 #i27801# - correction: 'short cut' for empty paragraph + // can *not* be applied, if test format is in progress. The test format doesn't + // adjust the frame and the printing area - see method <SwTxtFrm::_Format(..)>, + // which is called in <SwTxtFrm::TestFormat(..)> + if ( IsEmpty() && !bTst ) + { + bSplit = sal_False; + SwTwips nHeight = bVert ? Prt().SSize().Width() : Prt().SSize().Height(); + if( rMaxHeight < nHeight ) + return sal_False; + else + { + rMaxHeight -= nHeight; + return sal_True; + } + } + + // In sehr unguenstigen Faellen kann GetPara immer noch 0 sein. + // Dann returnen wir sal_True, um auf der neuen Seite noch einmal + // anformatiert zu werden. + ASSERT( HasPara() || IsHiddenNow(), "WouldFit: GetFormatted() and then !HasPara()" ); + if( !HasPara() || ( !(Frm().*fnRect->fnGetHeight)() && IsHiddenNow() ) ) + return sal_True; + + // Da das Orphan-Flag nur sehr fluechtig existiert, wird als zweite + // Bedingung ueberprueft, ob die Rahmengroesse durch CalcPreps + // auf riesengross gesetzt wird, um ein MoveFwd zu erzwingen. + if( IsWidow() || ( bVert ? + ( 0 == Frm().Left() ) : + ( LONG_MAX - 20000 < Frm().Bottom() ) ) ) + { + SetWidow(sal_False); + if ( GetFollow() ) + { + // Wenn wir hier durch eine Widow-Anforderung unseres Follows gelandet + // sind, wird ueberprueft, ob es ueberhaupt einen Follow mit einer + // echten Hoehe gibt, andernfalls (z.B. in neu angelegten SctFrms) + // ignorieren wir das IsWidow() und pruefen doch noch, ob wir + // genung Platz finden. + if( ( ( ! bVert && LONG_MAX - 20000 >= Frm().Bottom() ) || + ( bVert && 0 < Frm().Left() ) ) && + ( GetFollow()->IsVertical() ? + !GetFollow()->Frm().Width() : + !GetFollow()->Frm().Height() ) ) + { + SwTxtFrm* pFoll = GetFollow()->GetFollow(); + while( pFoll && + ( pFoll->IsVertical() ? + !pFoll->Frm().Width() : + !pFoll->Frm().Height() ) ) + pFoll = pFoll->GetFollow(); + if( pFoll ) + return sal_False; + } + else + return sal_False; + } + } + + SWAP_IF_NOT_SWAPPED( this ); + + SwTxtSizeInfo aInf( this ); + SwTxtMargin aLine( this, &aInf ); + + WidowsAndOrphans aFrmBreak( this, rMaxHeight, bSplit ); + + sal_Bool bRet = sal_True; + + aLine.Bottom(); + // Ist Aufspalten ueberhaupt notwendig? + if ( 0 != ( bSplit = !aFrmBreak.IsInside( aLine ) ) ) + bRet = !aFrmBreak.IsKeepAlways() && aFrmBreak.WouldFit( aLine, rMaxHeight, bTst ); + else + { + //Wir brauchen die Gesamthoehe inklusive der aktuellen Zeile + aLine.Top(); + do + { + rMaxHeight -= aLine.GetLineHeight(); + } while ( aLine.Next() ); + } + + UNDO_SWAP( this ) + + return bRet; +} + + +/************************************************************************* + * SwTxtFrm::GetParHeight() + *************************************************************************/ + +KSHORT SwTxtFrm::GetParHeight() const +{ + ASSERT( ! IsVertical() || ! IsSwapped(), + "SwTxtFrm::GetParHeight with swapped frame" ) + + if( !HasPara() ) + { // Fuer nichtleere Absaetze ist dies ein Sonderfall, da koennen wir + // bei UnderSized ruhig nur 1 Twip mehr anfordern. + KSHORT nRet = (KSHORT)Prt().SSize().Height(); + if( IsUndersized() ) + { + if( IsEmpty() ) + nRet = (KSHORT)EmptyHeight(); + else + ++nRet; + } + return nRet; + } + + // FME, OD 08.01.2004 #i11859# - refactoring and improve code + const SwLineLayout* pLineLayout = GetPara(); + KSHORT nHeight = pLineLayout->GetRealHeight(); + if( GetOfst() && !IsFollow() ) // Ist dieser Absatz gescrollt? Dann ist unsere + nHeight *= 2; // bisherige Hoehe mind. eine Zeilenhoehe zu gering + // OD 2004-03-04 #115793# + while ( pLineLayout && pLineLayout->GetNext() ) + { + pLineLayout = pLineLayout->GetNext(); + nHeight = nHeight + pLineLayout->GetRealHeight(); + } + + return nHeight; +} + + +/************************************************************************* + * SwTxtFrm::GetFormatted() + *************************************************************************/ + +// returnt this _immer_ im formatierten Zustand! +SwTxtFrm* SwTxtFrm::GetFormatted( bool bForceQuickFormat ) +{ + SWAP_IF_SWAPPED( this ) + + //Kann gut sein, dass mir der IdleCollector mir die gecachten + //Informationen entzogen hat. Calc() ruft unser Format. + //Nicht bei leeren Absaetzen! + if( !HasPara() && !(IsValid() && IsEmpty()) ) + { + // Calc() muss gerufen werden, weil unsere Frameposition + // nicht stimmen muss. + const sal_Bool bFormat = GetValidSizeFlag(); + Calc(); + // Es kann durchaus sein, dass Calc() das Format() + // nicht anstiess (weil wir einst vom Idle-Zerstoerer + // aufgefordert wurden unsere Formatinformationen wegzuschmeissen). + // 6995: Optimierung mit FormatQuick() + if( bFormat && !FormatQuick( bForceQuickFormat ) ) + Format(); + } + + UNDO_SWAP( this ) + + return this; +} + +/************************************************************************* + * SwTxtFrm::CalcFitToContent() + *************************************************************************/ + +SwTwips SwTxtFrm::CalcFitToContent() +{ + // --> FME 2004-07-16 #i31490# + // If we are currently locked, we better return with a + // fairly reasonable value: + if ( IsLocked() ) + return Prt().Width(); + // <-- + + SwParaPortion* pOldPara = GetPara(); + SwParaPortion *pDummy = new SwParaPortion(); + SetPara( pDummy, false ); + const SwPageFrm* pPage = FindPageFrm(); + + const Point aOldFrmPos = Frm().Pos(); + const SwTwips nOldFrmWidth = Frm().Width(); + const SwTwips nOldPrtWidth = Prt().Width(); + const SwTwips nPageWidth = GetUpper()->IsVertical() ? + pPage->Prt().Height() : + pPage->Prt().Width(); + + Frm().Width( nPageWidth ); + Prt().Width( nPageWidth ); + + // --> FME 2004-07-19 #i25422# objects anchored as character in RTL + if ( IsRightToLeft() ) + Frm().Pos().X() += nOldFrmWidth - nPageWidth; + + // --> FME 2004-07-16 #i31490# + SwTxtFrmLocker aLock( this ); + // <-- + + SwTxtFormatInfo aInf( this, sal_False, sal_True, sal_True ); + aInf.SetIgnoreFly( sal_True ); + SwTxtFormatter aLine( this, &aInf ); + SwHookOut aHook( aInf ); + + // --> OD 2005-09-06 #i54031# - assure mininum of MINLAY twips. + const SwTwips nMax = Max( (SwTwips)MINLAY, + aLine._CalcFitToContent() + 1 ); + // <-- + + Frm().Width( nOldFrmWidth ); + Prt().Width( nOldPrtWidth ); + + // --> FME 2004-07-19 #i25422# objects anchored as character in RTL + if ( IsRightToLeft() ) + Frm().Pos() = aOldFrmPos; + + + SetPara( pOldPara ); + + return nMax; +} + +/** simulate format for a list item paragraph, whose list level attributes + are in LABEL_ALIGNMENT mode, in order to determine additional first + line offset for the real text formatting due to the value of label + adjustment attribute of the list level. + + OD 2008-01-31 #newlistlevelattrs# + + @author OD +*/ +void SwTxtFrm::CalcAdditionalFirstLineOffset() +{ + if ( IsLocked() ) + return; + + // reset additional first line offset + mnAdditionalFirstLineOffset = 0; + + const SwTxtNode* pTxtNode( GetTxtNode() ); + if ( pTxtNode && pTxtNode->IsNumbered() && pTxtNode->IsCountedInList() && + pTxtNode->GetNumRule() ) + { + const SwNumFmt& rNumFmt = + pTxtNode->GetNumRule()->Get( static_cast<USHORT>(pTxtNode->GetActualListLevel()) ); + if ( rNumFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) + { + // keep current paragraph portion and apply dummy paragraph portion + SwParaPortion* pOldPara = GetPara(); + SwParaPortion *pDummy = new SwParaPortion(); + SetPara( pDummy, false ); + + // lock paragraph + SwTxtFrmLocker aLock( this ); + + // simulate text formatting + SwTxtFormatInfo aInf( this, sal_False, sal_True, sal_True ); + aInf.SetIgnoreFly( sal_True ); + SwTxtFormatter aLine( this, &aInf ); + SwHookOut aHook( aInf ); + aLine._CalcFitToContent(); + + // determine additional first line offset + const SwLinePortion* pFirstPortion = aLine.GetCurr()->GetFirstPortion(); + if ( pFirstPortion->InNumberGrp() && !pFirstPortion->IsFtnNumPortion() ) + { + SwTwips nNumberPortionWidth( pFirstPortion->Width() ); + + const SwLinePortion* pPortion = pFirstPortion->GetPortion(); + while ( pPortion && + pPortion->InNumberGrp() && !pPortion->IsFtnNumPortion()) + { + nNumberPortionWidth += pPortion->Width(); + pPortion = pPortion->GetPortion(); + } + + if ( ( IsRightToLeft() && + rNumFmt.GetNumAdjust() == SVX_ADJUST_LEFT ) || + ( !IsRightToLeft() && + rNumFmt.GetNumAdjust() == SVX_ADJUST_RIGHT ) ) + { + mnAdditionalFirstLineOffset = -nNumberPortionWidth; + } + else if ( rNumFmt.GetNumAdjust() == SVX_ADJUST_CENTER ) + { + mnAdditionalFirstLineOffset = -(nNumberPortionWidth/2); + } + } + + // restore paragraph portion + SetPara( pOldPara ); + } + } +} + +/** determine height of last line for the calculation of the proportional line + spacing + + OD 08.01.2004 #i11859# + OD 2004-03-17 #i11860# - method <GetHeightOfLastLineForPropLineSpacing()> + replace by method <_CalcHeightOfLastLine()>. Height of last line will be + stored in new member <mnHeightOfLastLine> and can be accessed via method + <GetHeightOfLastLine()> + OD 2005-05-20 #i47162# - introduce new optional parameter <_bUseFont> + in order to force the usage of the former algorithm to determine the + height of the last line, which uses the font. + + @author OD +*/ +void SwTxtFrm::_CalcHeightOfLastLine( const bool _bUseFont ) +{ + // --> OD 2006-11-13 #i71281# + // invalidate printing area, if height of last line changes + const SwTwips mnOldHeightOfLastLine( mnHeightOfLastLine ); + // <-- + // determine output device + ViewShell* pVsh = GetShell(); + ASSERT( pVsh, "<SwTxtFrm::_GetHeightOfLastLineForPropLineSpacing()> - no ViewShell" ); + // --> OD 2007-07-02 #i78921# - make code robust, according to provided patch + // There could be no <ViewShell> instance in the case of loading a binary + // StarOffice file format containing an embedded Writer document. + if ( !pVsh ) + { + return; + } + OutputDevice* pOut = pVsh->GetOut(); + const IDocumentSettingAccess* pIDSA = GetTxtNode()->getIDocumentSettingAccess(); + if ( !pIDSA->get(IDocumentSettingAccess::BROWSE_MODE) || + pVsh->GetViewOptions()->IsPrtFormat() ) + { + pOut = GetTxtNode()->getIDocumentDeviceAccess()->getReferenceDevice( true ); + } + ASSERT( pOut, "<SwTxtFrm::_GetHeightOfLastLineForPropLineSpacing()> - no OutputDevice" ); + // --> OD 2007-07-02 #i78921# - make code robust, according to provided patch + if ( !pOut ) + { + return; + } + // <-- + + // determine height of last line + + if ( _bUseFont || pIDSA->get(IDocumentSettingAccess::OLD_LINE_SPACING ) ) + { + // former determination of last line height for proprotional line + // spacing - take height of font set at the paragraph + SwFont aFont( GetAttrSet(), pIDSA ); + + // Wir muessen dafuer sorgen, dass am OutputDevice der Font + // korrekt restauriert wird, sonst droht ein Last!=Owner. + if ( pLastFont ) + { + SwFntObj *pOldFont = pLastFont; + pLastFont = NULL; + aFont.SetFntChg( sal_True ); + aFont.ChgPhysFnt( pVsh, *pOut ); + mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut ); + pLastFont->Unlock(); + pLastFont = pOldFont; + pLastFont->SetDevFont( pVsh, *pOut ); + } + else + { + Font aOldFont = pOut->GetFont(); + aFont.SetFntChg( sal_True ); + aFont.ChgPhysFnt( pVsh, *pOut ); + mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut ); + pLastFont->Unlock(); + pLastFont = NULL; + pOut->SetFont( aOldFont ); + } + } + else + { + // new determination of last line height - take actually height of last line + // --> OD 2008-05-06 #i89000# + // assure same results, if paragraph is undersized + if ( IsUndersized() ) + { + mnHeightOfLastLine = 0; + } + else + { + bool bCalcHeightOfLastLine = true; + if ( !HasPara() ) + { + if ( IsEmpty() ) + { + mnHeightOfLastLine = EmptyHeight(); + bCalcHeightOfLastLine = false; + } + } + + if ( bCalcHeightOfLastLine ) + { + ASSERT( HasPara(), + "<SwTxtFrm::_CalcHeightOfLastLine()> - missing paragraph portions." ); + const SwLineLayout* pLineLayout = GetPara(); + while ( pLineLayout && pLineLayout->GetNext() ) + { + // iteration to last line + pLineLayout = pLineLayout->GetNext(); + } + if ( pLineLayout ) + { + SwTwips nAscent, nDescent, nDummy1, nDummy2; + // --> OD 2005-05-20 #i47162# - suppress consideration of + // fly content portions and the line portion. + pLineLayout->MaxAscentDescent( nAscent, nDescent, + nDummy1, nDummy2, + 0, true ); + // <-- + // --> OD 2006-11-22 #i71281# + // Suppress wrong invalidation of printing area, if method is + // called recursive. + // Thus, member <mnHeightOfLastLine> is only set directly, if + // no recursive call is needed. + // mnHeightOfLastLine = nAscent + nDescent; + const SwTwips nNewHeightOfLastLine = nAscent + nDescent; + // --> OD 2005-05-20 #i47162# - if last line only contains + // fly content portions, <mnHeightOfLastLine> is zero. + // In this case determine height of last line by the font + if ( nNewHeightOfLastLine == 0 ) + { + _CalcHeightOfLastLine( true ); + } + else + { + mnHeightOfLastLine = nNewHeightOfLastLine; + } + // <-- + // <-- + } + } + } + // <-- + } + // --> OD 2006-11-13 #i71281# + // invalidate printing area, if height of last line changes + if ( mnHeightOfLastLine != mnOldHeightOfLastLine ) + { + InvalidatePrt(); + } + // <-- +} + +/************************************************************************* + * SwTxtFrm::GetLineSpace() + *************************************************************************/ +// OD 07.01.2004 #i11859# - change return data type +// add default parameter <_bNoPropLineSpacing> to control, if the +// value of a proportional line spacing is returned or not +// OD 07.01.2004 - trying to describe purpose of method: +// Method returns the value of the inter line spacing for a text frame. +// Such a value exists for proportional line spacings ("1,5 Lines", +// "Double", "Proportional" and for leading line spacing ("Leading"). +// By parameter <_bNoPropLineSpace> (default value false) it can be +// controlled, if the value of a proportional line spacing is returned. +long SwTxtFrm::GetLineSpace( const bool _bNoPropLineSpace ) const +{ + long nRet = 0; + + const SwAttrSet* pSet = GetAttrSet(); + const SvxLineSpacingItem &rSpace = pSet->GetLineSpacing(); + + switch( rSpace.GetInterLineSpaceRule() ) + { + case SVX_INTER_LINE_SPACE_PROP: + { + // OD 07.01.2004 #i11859# + if ( _bNoPropLineSpace ) + { + break; + } + + // OD 2004-03-17 #i11860# - use method <GetHeightOfLastLine()> + nRet = GetHeightOfLastLine(); + + long nTmp = nRet; + nTmp *= rSpace.GetPropLineSpace(); + nTmp /= 100; + nTmp -= nRet; + if ( nTmp > 0 ) + nRet = nTmp; + else + nRet = 0; + } + break; + case SVX_INTER_LINE_SPACE_FIX: + { + if ( rSpace.GetInterLineSpace() > 0 ) + nRet = rSpace.GetInterLineSpace(); + } + break; + default: + break; + } + return nRet; +} + +/************************************************************************* + * SwTxtFrm::FirstLineHeight() + *************************************************************************/ + +KSHORT SwTxtFrm::FirstLineHeight() const +{ + if ( !HasPara() ) + { + if( IsEmpty() && IsValid() ) + return IsVertical() ? (KSHORT)Prt().Width() : (KSHORT)Prt().Height(); + return KSHRT_MAX; + } + const SwParaPortion *pPara = GetPara(); + if ( !pPara ) + return KSHRT_MAX; + + return pPara->Height(); +} + +MSHORT SwTxtFrm::GetLineCount( xub_StrLen nPos ) +{ + MSHORT nRet = 0; + SwTxtFrm *pFrm = this; + do + { + pFrm->GetFormatted(); + if( !pFrm->HasPara() ) + break; + SwTxtSizeInfo aInf( pFrm ); + SwTxtMargin aLine( pFrm, &aInf ); + if( STRING_LEN == nPos ) + aLine.Bottom(); + else + aLine.CharToLine( nPos ); + nRet = nRet + aLine.GetLineNr(); + pFrm = pFrm->GetFollow(); + } while ( pFrm && pFrm->GetOfst() <= nPos ); + return nRet; +} + +void SwTxtFrm::ChgThisLines() +{ + //not necassary to format here (GerFormatted etc.), because we have to come from there! + + ULONG nNew = 0; + const SwLineNumberInfo &rInf = GetNode()->getIDocumentLineNumberAccess()->GetLineNumberInfo(); + if ( GetTxt().Len() && HasPara() ) + { + SwTxtSizeInfo aInf( this ); + SwTxtMargin aLine( this, &aInf ); + if ( rInf.IsCountBlankLines() ) + { + aLine.Bottom(); + nNew = (ULONG)aLine.GetLineNr(); + } + else + { + do + { + if( aLine.GetCurr()->HasCntnt() ) + ++nNew; + } while ( aLine.NextLine() ); + } + } + else if ( rInf.IsCountBlankLines() ) + nNew = 1; + + if ( nNew != nThisLines ) + { + if ( !IsInTab() && GetAttrSet()->GetLineNumber().IsCount() ) + { + nAllLines -= nThisLines; + nThisLines = nNew; + nAllLines += nThisLines; + SwFrm *pNxt = GetNextCntntFrm(); + while( pNxt && pNxt->IsInTab() ) + { + if( 0 != (pNxt = pNxt->FindTabFrm()) ) + pNxt = pNxt->FindNextCnt(); + } + if( pNxt ) + pNxt->InvalidateLineNum(); + + //Extend repaint to the bottom. + if ( HasPara() ) + { + SwRepaint *pRepaint = GetPara()->GetRepaint(); + pRepaint->Bottom( Max( pRepaint->Bottom(), + Frm().Top()+Prt().Bottom())); + } + } + else //Paragraphs which are not counted should not manipulate the AllLines. + nThisLines = nNew; + } +} + + +void SwTxtFrm::RecalcAllLines() +{ + ValidateLineNum(); + + const SwAttrSet *pAttrSet = GetAttrSet(); + + if ( !IsInTab() ) + { + const ULONG nOld = GetAllLines(); + const SwFmtLineNumber &rLineNum = pAttrSet->GetLineNumber(); + ULONG nNewNum; + const bool bRestart = GetTxtNode()->getIDocumentLineNumberAccess()->GetLineNumberInfo().IsRestartEachPage(); + + if ( !IsFollow() && rLineNum.GetStartValue() && rLineNum.IsCount() ) + nNewNum = rLineNum.GetStartValue() - 1; + //If it is a follow or not has not be considered if it is a restart at each page; the + //restart should also take affekt at follows. + else if ( bRestart && FindPageFrm()->FindFirstBodyCntnt() == this ) + { + nNewNum = 0; + } + else + { + SwCntntFrm *pPrv = GetPrevCntntFrm(); + while ( pPrv && + (pPrv->IsInTab() || pPrv->IsInDocBody() != IsInDocBody()) ) + pPrv = pPrv->GetPrevCntntFrm(); + + // --> FME 2007-06-22 #i78254# Restart line numbering at page change: + // First body content may be in table! + if ( bRestart && pPrv && pPrv->FindPageFrm() != FindPageFrm() ) + pPrv = 0; + // <-- + + nNewNum = pPrv ? ((SwTxtFrm*)pPrv)->GetAllLines() : 0; + } + if ( rLineNum.IsCount() ) + nNewNum += GetThisLines(); + + if ( nOld != nNewNum ) + { + nAllLines = nNewNum; + SwCntntFrm *pNxt = GetNextCntntFrm(); + while ( pNxt && + (pNxt->IsInTab() || pNxt->IsInDocBody() != IsInDocBody()) ) + pNxt = pNxt->GetNextCntntFrm(); + if ( pNxt ) + { + if ( pNxt->GetUpper() != GetUpper() ) + pNxt->InvalidateLineNum(); + else + pNxt->_InvalidateLineNum(); + } + } + } +} + +void SwTxtFrm::VisitPortions( SwPortionHandler& rPH ) const +{ + const SwParaPortion* pPara = GetPara(); + + if( pPara ) + { + if ( IsFollow() ) + rPH.Skip( GetOfst() ); + + const SwLineLayout* pLine = pPara; + while ( pLine ) + { + const SwLinePortion* pPor = pLine->GetFirstPortion(); + while ( pPor ) + { + pPor->HandlePortion( rPH ); + pPor = pPor->GetPortion(); + } + + rPH.LineBreak(); + pLine = pLine->GetNext(); + } + } + + rPH.Finish(); +} + + +/************************************************************************* + * SwTxtFrm::GetScriptInfo() + *************************************************************************/ + +const SwScriptInfo* SwTxtFrm::GetScriptInfo() const +{ + const SwParaPortion* pPara = GetPara(); + return pPara ? &pPara->GetScriptInfo() : 0; +} + +/************************************************************************* + * lcl_CalcFlyBasePos() + * Helper function for SwTxtFrm::CalcBasePosForFly() + *************************************************************************/ + +SwTwips lcl_CalcFlyBasePos( const SwTxtFrm& rFrm, SwRect aFlyRect, + SwTxtFly& rTxtFly ) +{ + SWRECTFN( (&rFrm) ) + SwTwips nRet = rFrm.IsRightToLeft() ? + (rFrm.Frm().*fnRect->fnGetRight)() : + (rFrm.Frm().*fnRect->fnGetLeft)(); + + do + { + SwRect aRect = rTxtFly.GetFrm( aFlyRect ); + if ( 0 != (aRect.*fnRect->fnGetWidth)() ) + { + if ( rFrm.IsRightToLeft() ) + { + if ( (aRect.*fnRect->fnGetRight)() - + (aFlyRect.*fnRect->fnGetRight)() >= 0 ) + { + (aFlyRect.*fnRect->fnSetRight)( + (aRect.*fnRect->fnGetLeft)() ); + nRet = (aRect.*fnRect->fnGetLeft)(); + } + else + break; + } + else + { + if ( (aFlyRect.*fnRect->fnGetLeft)() - + (aRect.*fnRect->fnGetLeft)() >= 0 ) + { + (aFlyRect.*fnRect->fnSetLeft)( + (aRect.*fnRect->fnGetRight)() + 1 ); + nRet = (aRect.*fnRect->fnGetRight)(); + } + else + break; + } + } + else + break; + } + while ( (aFlyRect.*fnRect->fnGetWidth)() > 0 ); + + return nRet; +} + +/************************************************************************* + * SwTxtFrm::CalcBasePosForFly() + *************************************************************************/ + +void SwTxtFrm::CalcBaseOfstForFly() +{ + ASSERT( !IsVertical() || !IsSwapped(), + "SwTxtFrm::CalcBasePosForFly with swapped frame!" ) + + const SwNode* pNode = GetTxtNode(); + if ( !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::ADD_FLY_OFFSETS) ) + return; + + SWRECTFN( this ) + + SwRect aFlyRect( Frm().Pos() + Prt().Pos(), Prt().SSize() ); + + // Get first 'real' line and adjust position and height of line rectangle + // OD 08.09.2003 #110978#, #108749#, #110354# - correct behaviour, + // if no 'real' line exists (empty paragraph with and without a dummy portion) + { + SwTwips nTop = (aFlyRect.*fnRect->fnGetTop)(); + const SwLineLayout* pLay = GetPara(); + SwTwips nLineHeight = 200; + while( pLay && pLay->IsDummy() && pLay->GetNext() ) + { + nTop += pLay->Height(); + pLay = pLay->GetNext(); + } + if ( pLay ) + { + nLineHeight = pLay->Height(); + } + (aFlyRect.*fnRect->fnSetTopAndHeight)( nTop, nLineHeight ); + } + + SwTxtFly aTxtFly( this ); + aTxtFly.SetIgnoreCurrentFrame( sal_True ); + aTxtFly.SetIgnoreContour( sal_True ); + // --> OD 2004-12-17 #118809# - ignore objects in page header|footer for + // text frames not in page header|footer + aTxtFly.SetIgnoreObjsInHeaderFooter( sal_True ); + // <-- + SwTwips nRet1 = lcl_CalcFlyBasePos( *this, aFlyRect, aTxtFly ); + aTxtFly.SetIgnoreCurrentFrame( sal_False ); + SwTwips nRet2 = lcl_CalcFlyBasePos( *this, aFlyRect, aTxtFly ); + + // make values relative to frame start position + SwTwips nLeft = IsRightToLeft() ? + (Frm().*fnRect->fnGetRight)() : + (Frm().*fnRect->fnGetLeft)(); + + mnFlyAnchorOfst = nRet1 - nLeft; + mnFlyAnchorOfstNoWrap = nRet2 - nLeft; +} diff --git a/sw/source/core/text/txtftn.cxx b/sw/source/core/text/txtftn.cxx new file mode 100644 index 000000000000..6981ca472419 --- /dev/null +++ b/sw/source/core/text/txtftn.cxx @@ -0,0 +1,1702 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtftn.cxx,v $ + * $Revision: 1.51.208.1 $ + * + * 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 "doc.hxx" +#include "pagefrm.hxx" +#include "ndtxt.hxx" +#include "txtatr.hxx" +#include <SwPortionHandler.hxx> +#include <txtftn.hxx> +#include <flyfrm.hxx> +#include <fmtftn.hxx> +#include <ftninfo.hxx> +#include <charfmt.hxx> +#include <dflyobj.hxx> +#include <rowfrm.hxx> +#include <svx/brshitem.hxx> +#include <svx/charrotateitem.hxx> +#include <breakit.hxx> +#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ +#include <com/sun/star/i18n/ScriptType.hdl> +#endif +#include <tabfrm.hxx> +// OD 2004-05-24 #i28701# +#include <sortedobjs.hxx> + +#include "txtcfg.hxx" +#include "swfont.hxx" // new SwFont +#include "porftn.hxx" +#include "porfly.hxx" +#include "porlay.hxx" +#include "txtfrm.hxx" +#include "itrform2.hxx" +#include "ftnfrm.hxx" // FindQuoVadisFrm(), +#include "pagedesc.hxx" +#include "redlnitr.hxx" // SwRedlnItr +#include "sectfrm.hxx" // SwSectionFrm +#include "layouter.hxx" // Endnote-Collection +#include "frmtool.hxx" +#include "ndindex.hxx" + +using namespace ::com::sun::star; + +/************************************************************************* + * _IsFtnNumFrm() + *************************************************************************/ + +sal_Bool SwTxtFrm::_IsFtnNumFrm() const +{ + const SwFtnFrm* pFtn = FindFtnFrm()->GetMaster(); + while( pFtn && !pFtn->ContainsCntnt() ) + pFtn = pFtn->GetMaster(); + return !pFtn; +} + +/************************************************************************* + * FindFtn() + *************************************************************************/ + +// Sucht innerhalb einer Master-Follow-Kette den richtigen TxtFrm zum SwTxtFtn + +SwTxtFrm *SwTxtFrm::FindFtnRef( const SwTxtFtn *pFtn ) +{ + SwTxtFrm *pFrm = this; + const sal_Bool bFwd = *pFtn->GetStart() >= GetOfst(); + while( pFrm ) + { + if( SwFtnBossFrm::FindFtn( pFrm, pFtn ) ) + return pFrm; + pFrm = bFwd ? pFrm->GetFollow() : + pFrm->IsFollow() ? pFrm->FindMaster() : 0; + } + return pFrm; +} + +/************************************************************************* + * CalcFtnFlag() + *************************************************************************/ + +#ifdef PRODUCT +void SwTxtFrm::CalcFtnFlag() +#else +void SwTxtFrm::CalcFtnFlag( xub_StrLen nStop )//Fuer den Test von SplitFrm +#endif +{ + bFtn = sal_False; + + const SwpHints *pHints = GetTxtNode()->GetpSwpHints(); + if( !pHints ) + return; + + const USHORT nSize = pHints->Count(); + +#ifdef PRODUCT + const xub_StrLen nEnd = GetFollow() ? GetFollow()->GetOfst() : STRING_LEN; +#else + const xub_StrLen nEnd = nStop != STRING_LEN ? nStop + : GetFollow() ? GetFollow()->GetOfst() : STRING_LEN; +#endif + + for ( USHORT i = 0; i < nSize; ++i ) + { + const SwTxtAttr *pHt = (*pHints)[i]; + if ( pHt->Which() == RES_TXTATR_FTN ) + { + const xub_StrLen nIdx = *pHt->GetStart(); + if ( nEnd < nIdx ) + break; + if( GetOfst() <= nIdx ) + { + bFtn = sal_True; + break; + } + } + } +} + +/************************************************************************* + * CalcPrepFtnAdjust() + *************************************************************************/ + +sal_Bool SwTxtFrm::CalcPrepFtnAdjust() +{ + ASSERT( HasFtn(), "Wer ruft mich da?" ); + SwFtnBossFrm *pBoss = FindFtnBossFrm( sal_True ); + const SwFtnFrm *pFtn = pBoss->FindFirstFtn( this ); + if( pFtn && FTNPOS_CHAPTER != GetNode()->GetDoc()->GetFtnInfo().ePos && + ( !pBoss->GetUpper()->IsSctFrm() || + !((SwSectionFrm*)pBoss->GetUpper())->IsFtnAtEnd() ) ) + { + const SwFtnContFrm *pCont = pBoss->FindFtnCont(); + sal_Bool bReArrange = sal_True; + + SWRECTFN( this ) + if ( pCont && (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), + (Frm().*fnRect->fnGetBottom)() ) > 0 ) + { + pBoss->RearrangeFtns( (Frm().*fnRect->fnGetBottom)(), sal_False, + pFtn->GetAttr() ); + ValidateBodyFrm(); + ValidateFrm(); + pFtn = pBoss->FindFirstFtn( this ); + } + else + bReArrange = sal_False; + if( !pCont || !pFtn || bReArrange != (pFtn->FindFtnBossFrm() == pBoss) ) + { + SwTxtFormatInfo aInf( this ); + SwTxtFormatter aLine( this, &aInf ); + aLine.TruncLines(); + SetPara( 0 ); //Wird ggf. geloescht! + ResetPreps(); + return sal_False; + } + } + return sal_True; +} + + +/************************************************************************* + * lcl_GetFtnLower() + * + * Local helper function. Checks if nLower should be taken as the boundary + * for the footnote. + *************************************************************************/ + +SwTwips lcl_GetFtnLower( const SwTxtFrm* pFrm, SwTwips nLower ) +{ + // nLower is an absolute value. It denotes the bottom of the line + // containing the footnote. + SWRECTFN( pFrm ) + + ASSERT( !pFrm->IsVertical() || !pFrm->IsSwapped(), + "lcl_GetFtnLower with swapped frame" ); + + SwTwips nAdd; + SwTwips nRet = nLower; + + // + // Check if text is inside a table. + // + if ( pFrm->IsInTab() ) + { + // + // If pFrm is inside a table, we have to check if + // a) The table is not allowed to split or + // b) The table row is not allowed to split + // + // Inside a table, there are no footnotes, + // see SwFrm::FindFtnBossFrm. So we don't have to check + // the case that pFrm is inside a (footnote collecting) section + // within the table. + // + const SwFrm* pRow = pFrm; + while( !pRow->IsRowFrm() || !pRow->GetUpper()->IsTabFrm() ) + pRow = pRow->GetUpper(); + const SwTabFrm* pTabFrm = (SwTabFrm*)pRow->GetUpper(); + + ASSERT( pTabFrm && pRow && + pRow->GetUpper()->IsTabFrm(), "Upper of row should be tab" ) + + const BOOL bDontSplit = !pTabFrm->IsFollow() && + !pTabFrm->IsLayoutSplitAllowed(); + + SwTwips nMin = 0; + if ( bDontSplit ) + nMin = (pTabFrm->Frm().*fnRect->fnGetBottom)(); + else if ( !((SwRowFrm*)pRow)->IsRowSplitAllowed() ) + nMin = (pRow->Frm().*fnRect->fnGetBottom)(); + + if ( nMin && (*fnRect->fnYDiff)( nMin, nLower ) > 0 ) + nRet = nMin; + + nAdd = (pRow->GetUpper()->*fnRect->fnGetBottomMargin)(); + } + else + nAdd = (pFrm->*fnRect->fnGetBottomMargin)(); + + if( nAdd > 0 ) + { + if ( bVert ) + nRet -= nAdd; + else + nRet += nAdd; + } + + // #i10770#: If there are fly frames anchored at previous paragraphs, + // the deadline should consider their lower borders. + const SwFrm* pStartFrm = pFrm->GetUpper()->GetLower(); + ASSERT( pStartFrm, "Upper has no lower" ) + SwTwips nFlyLower = bVert ? LONG_MAX : 0; + while ( pStartFrm != pFrm ) + { + ASSERT( pStartFrm, "Frame chain is broken" ) + if ( pStartFrm->GetDrawObjs() ) + { + const SwSortedObjs &rObjs = *pStartFrm->GetDrawObjs(); + for ( USHORT i = 0; i < rObjs.Count(); ++i ) + { + SwAnchoredObject* pAnchoredObj = rObjs[i]; + SwRect aRect( pAnchoredObj->GetObjRect() ); + + if ( !pAnchoredObj->ISA(SwFlyFrm) || + static_cast<SwFlyFrm*>(pAnchoredObj)->IsValid() ) + { + const SwTwips nBottom = (aRect.*fnRect->fnGetBottom)(); + if ( (*fnRect->fnYDiff)( nBottom, nFlyLower ) > 0 ) + nFlyLower = nBottom; + } + } + } + + pStartFrm = pStartFrm->GetNext(); + } + + if ( bVert ) + nRet = Min( nRet, nFlyLower ); + else + nRet = Max( nRet, nFlyLower ); + + return nRet; +} + + +/************************************************************************* + * SwTxtFrm::GetFtnLine() + *************************************************************************/ + +SwTwips SwTxtFrm::GetFtnLine( const SwTxtFtn *pFtn ) const +{ + ASSERT( ! IsVertical() || ! IsSwapped(), + "SwTxtFrm::GetFtnLine with swapped frame" ) + + SwTxtFrm *pThis = (SwTxtFrm*)this; + + if( !HasPara() ) + { + // #109071# GetFormatted() does not work here, bacause most probably + // the frame is currently locked. We return the previous value. + return pThis->mnFtnLine > 0 ? + pThis->mnFtnLine : + IsVertical() ? Frm().Left() : Frm().Bottom(); + } + + SWAP_IF_NOT_SWAPPED( this ) + + SwTxtInfo aInf( pThis ); + SwTxtIter aLine( pThis, &aInf ); + const xub_StrLen nPos = *pFtn->GetStart(); + aLine.CharToLine( nPos ); + + SwTwips nRet = aLine.Y() + SwTwips(aLine.GetLineHeight()); + if( IsVertical() ) + nRet = SwitchHorizontalToVertical( nRet ); + + UNDO_SWAP( this ) + + nRet = lcl_GetFtnLower( pThis, nRet ); + + pThis->mnFtnLine = nRet; + return nRet; +} + +/************************************************************************* + * SwTxtFrm::GetFtnRstHeight() + *************************************************************************/ + +// Ermittelt die max. erreichbare Hoehe des TxtFrm im Ftn-Bereich. +// Sie wird eingeschraenkt durch den unteren Rand der Zeile mit +// der Ftn-Referenz. + +SwTwips SwTxtFrm::_GetFtnFrmHeight() const +{ + ASSERT( !IsFollow() && IsInFtn(), "SwTxtFrm::SetFtnLine: moon walk" ); + + const SwFtnFrm *pFtnFrm = FindFtnFrm(); + const SwTxtFrm *pRef = (const SwTxtFrm *)pFtnFrm->GetRef(); + const SwFtnBossFrm *pBoss = FindFtnBossFrm(); + if( pBoss != pRef->FindFtnBossFrm( !pFtnFrm->GetAttr()-> + GetFtn().IsEndNote() ) ) + return 0; + + SWAP_IF_SWAPPED( this ) + + SwTwips nHeight = pRef->IsInFtnConnect() ? + 1 : pRef->GetFtnLine( pFtnFrm->GetAttr() ); + if( nHeight ) + { + // So komisch es aussehen mag: Die erste Ftn auf der Seite darf sich + // nicht mit der Ftn-Referenz beruehren, wenn wir im Ftn-Bereich Text + // eingeben. + const SwFrm *pCont = pFtnFrm->GetUpper(); + //Hoehe innerhalb des Cont, die ich mir 'eh noch genehmigen darf. + SWRECTFN( pCont ) + SwTwips nTmp = (*fnRect->fnYDiff)( (pCont->*fnRect->fnGetPrtBottom)(), + (Frm().*fnRect->fnGetTop)() ); + +#ifndef PRODUCT + if( nTmp < 0 ) + { + sal_Bool bInvalidPos = sal_False; + const SwLayoutFrm* pTmp = GetUpper(); + while( !bInvalidPos && pTmp ) + { + bInvalidPos = !pTmp->GetValidPosFlag() || + !pTmp->Lower()->GetValidPosFlag(); + if( pTmp == pCont ) + break; + pTmp = pTmp->GetUpper(); + } + ASSERT( bInvalidPos, "Hanging below FtnCont" ); + } +#endif + + if ( (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), nHeight) > 0 ) + { + //Wachstumspotential den Containers. + if ( !pRef->IsInFtnConnect() ) + { + SwSaveFtnHeight aSave( (SwFtnBossFrm*)pBoss, nHeight ); + nHeight = ((SwFtnContFrm*)pCont)->Grow( LONG_MAX, sal_True ); + } + else + nHeight = ((SwFtnContFrm*)pCont)->Grow( LONG_MAX, sal_True ); + + nHeight += nTmp; + if( nHeight < 0 ) + nHeight = 0; + } + else + { // The container has to shrink + nTmp += (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), nHeight); + if( nTmp > 0 ) + nHeight = nTmp; + else + nHeight = 0; + } + } + + UNDO_SWAP( this ) + + return nHeight; +} + +/************************************************************************* + * SwTxtFrm::FindQuoVadisFrm() + *************************************************************************/ + +SwTxtFrm *SwTxtFrm::FindQuoVadisFrm() +{ + // Erstmal feststellen, ob wir in einem FtnFrm stehen: + if( GetIndPrev() || !IsInFtn() ) + return 0; + + // Zum Vorgaenger-FtnFrm + SwFtnFrm *pFtnFrm = FindFtnFrm()->GetMaster(); + if( !pFtnFrm ) + return 0; + + // Nun den letzten Cntnt: + const SwCntntFrm *pCnt = pFtnFrm->ContainsCntnt(); + if( !pCnt ) + return NULL; + const SwCntntFrm *pLast; + do + { pLast = pCnt; + pCnt = pCnt->GetNextCntntFrm(); + } while( pCnt && pFtnFrm->IsAnLower( pCnt ) ); + return (SwTxtFrm*)pLast; +} + +/************************************************************************* + * SwTxtFrm::RemoveFtn() + *************************************************************************/ + +void SwTxtFrm::RemoveFtn( const xub_StrLen nStart, const xub_StrLen nLen ) +{ + if ( !IsFtnAllowed() ) + return; + + SwpHints *pHints = GetTxtNode()->GetpSwpHints(); + if( !pHints ) + return; + + sal_Bool bRollBack = nLen != STRING_LEN; + USHORT nSize = pHints->Count(); + xub_StrLen nEnd; + SwTxtFrm* pSource; + if( bRollBack ) + { + nEnd = nStart + nLen; + pSource = GetFollow(); + if( !pSource ) + return; + } + else + { + nEnd = STRING_LEN; + pSource = this; + } + + if( nSize ) + { + SwPageFrm* pUpdate = NULL; + sal_Bool bRemove = sal_False; + SwFtnBossFrm *pFtnBoss = 0; + SwFtnBossFrm *pEndBoss = 0; + sal_Bool bFtnEndDoc + = FTNPOS_CHAPTER == GetNode()->GetDoc()->GetFtnInfo().ePos; + for ( USHORT i = nSize; i; ) + { + SwTxtAttr *pHt = pHints->GetTextHint(--i); + if ( RES_TXTATR_FTN != pHt->Which() ) + continue; + + const xub_StrLen nIdx = *pHt->GetStart(); + if( nStart > nIdx ) + break; + + if( nEnd >= nIdx ) + { + SwTxtFtn *pFtn = (SwTxtFtn*)pHt; + sal_Bool bEndn = pFtn->GetFtn().IsEndNote(); + + if( bEndn ) + { + if( !pEndBoss ) + pEndBoss = pSource->FindFtnBossFrm(); + } + else + { + if( !pFtnBoss ) + { + pFtnBoss = pSource->FindFtnBossFrm( sal_True ); + if( pFtnBoss->GetUpper()->IsSctFrm() ) + { + SwSectionFrm* pSect = (SwSectionFrm*) + pFtnBoss->GetUpper(); + if( pSect->IsFtnAtEnd() ) + bFtnEndDoc = sal_False; + } + } + } + + // Wir loeschen nicht, sondern wollen die Ftn verschieben. + // Drei Faelle koennen auftreten: + // 1) Es gibt weder Follow noch PrevFollow + // -> RemoveFtn() (vielleicht sogar ein ASSERT wert) + // 2) nStart > GetOfst, ich habe einen Follow + // -> Ftn wandert in den Follow + // 3) nStart < GetOfst, ich bin ein Follow + // -> Ftn wandert in den PrevFollow + // beide muessen auf einer Seite/in einer Spalte stehen. + + SwFtnFrm *pFtnFrm = bEndn ? pEndBoss->FindFtn( pSource, pFtn ) : + pFtnBoss->FindFtn( pSource, pFtn ); + + if( pFtnFrm ) + { + const sal_Bool bEndDoc = bEndn ? sal_True : bFtnEndDoc; + if( bRollBack ) + { + while ( pFtnFrm ) + { + pFtnFrm->SetRef( this ); + pFtnFrm = pFtnFrm->GetFollow(); + SetFtn( sal_True ); + } + } + else if( GetFollow() ) + { + SwCntntFrm *pDest = GetFollow(); + while( pDest->GetFollow() && ((SwTxtFrm*)pDest-> + GetFollow())->GetOfst() <= nIdx ) + pDest = pDest->GetFollow(); + ASSERT( !pDest->FindFtnBossFrm( !bEndn )->FindFtn( + pDest,pFtn),"SwTxtFrm::RemoveFtn: footnote exists"); + + //Nicht ummelden sondern immer Moven. + // OD 08.11.2002 #104840# - use <SwlayoutFrm::IsBefore(::)> + if ( bEndDoc || + !pFtnFrm->FindFtnBossFrm()->IsBefore( pDest->FindFtnBossFrm( !bEndn ) ) + ) + { + SwPageFrm* pTmp = pFtnFrm->FindPageFrm(); + if( pUpdate && pUpdate != pTmp ) + pUpdate->UpdateFtnNum(); + pUpdate = pTmp; + while ( pFtnFrm ) + { + pFtnFrm->SetRef( pDest ); + pFtnFrm = pFtnFrm->GetFollow(); + } + } + else + { + if( bEndn ) + pEndBoss->MoveFtns( this, pDest, pFtn ); + else + pFtnBoss->MoveFtns( this, pDest, pFtn ); + bRemove = sal_True; + } + ((SwTxtFrm*)pDest)->SetFtn( sal_True ); + + ASSERT( pDest->FindFtnBossFrm( !bEndn )->FindFtn( pDest, + pFtn),"SwTxtFrm::RemoveFtn: footnote ChgRef failed"); + } + else + { + if( !bEndDoc || ( bEndn && pEndBoss->IsInSct() && + !SwLayouter::Collecting( GetNode()->GetDoc(), + pEndBoss->FindSctFrm(), NULL ) ) ) + { + if( bEndn ) + pEndBoss->RemoveFtn( this, pFtn ); + else + pFtnBoss->RemoveFtn( this, pFtn ); + bRemove = bRemove || !bEndDoc; + ASSERT( bEndn ? !pEndBoss->FindFtn( this, pFtn ) : + !pFtnBoss->FindFtn( this, pFtn ), + "SwTxtFrm::RemoveFtn: can't get off that footnote" ); + } + } + } + } + } + if( pUpdate ) + pUpdate->UpdateFtnNum(); + // Wir bringen die Oszillation zum stehen: + if( bRemove && !bFtnEndDoc && HasPara() ) + { + ValidateBodyFrm(); + ValidateFrm(); + } + } + // Folgendes Problem: Aus dem FindBreak heraus wird das RemoveFtn aufgerufen, + // weil die letzte Zeile an den Follow abgegeben werden soll. Der Offset + // des Follows ist aber veraltet, er wird demnaechst gesetzt. CalcFntFlag ist + // auf einen richtigen Follow-Offset angewiesen. Deshalb wird hier kurzfristig + // der Follow-Offset manipuliert. + xub_StrLen nOldOfst = STRING_LEN; + if( HasFollow() && nStart > GetOfst() ) + { + nOldOfst = GetFollow()->GetOfst(); + GetFollow()->ManipOfst( nStart + ( bRollBack ? nLen : 0 ) ); + } + pSource->CalcFtnFlag(); + if( nOldOfst < STRING_LEN ) + GetFollow()->ManipOfst( nOldOfst ); +} + +/************************************************************************* + * SwTxtFormatter::ConnectFtn() + *************************************************************************/ +// sal_False, wenn irgendetwas schief gegangen ist. +// Es gibt eigentlich nur zwei Moeglichkeiten: +// a) Die Ftn ist bereits vorhanden +// => dann wird sie gemoved, wenn ein anderer pSrcFrm gefunden wurde +// b) Die Ftn ist nicht vorhanden +// => dann wird sie fuer uns angelegt. +// Ob die Ftn schliesslich auf unserer Spalte/Seite landet oder nicht, +// spielt in diesem Zusammenhang keine Rolle. +// Optimierungen bei Endnoten. +// Noch ein Problem: wenn die Deadline im Ftn-Bereich liegt, muss die +// Ftn verschoben werden. + +void SwTxtFrm::ConnectFtn( SwTxtFtn *pFtn, const SwTwips nDeadLine ) +{ + ASSERT( !IsVertical() || !IsSwapped(), + "SwTxtFrm::ConnectFtn with swapped frame" ); + + bFtn = sal_True; + bInFtnConnect = sal_True; //Bloss zuruecksetzen! + sal_Bool bEnd = pFtn->GetFtn().IsEndNote(); + + // + // We want to store this value, because it is needed as a fallback + // in GetFtnLine(), if there is no paragraph information available + // + mnFtnLine = nDeadLine; + + // Wir brauchen immer einen Boss (Spalte/Seite) + SwSectionFrm *pSect; + SwCntntFrm *pCntnt = this; + if( bEnd && IsInSct() ) + { + pSect = FindSctFrm(); + if( pSect->IsEndnAtEnd() ) + pCntnt = pSect->FindLastCntnt( FINDMODE_ENDNOTE ); + if( !pCntnt ) + pCntnt = this; + } + + SwFtnBossFrm *pBoss = pCntnt->FindFtnBossFrm( !bEnd ); + +#if OSL_DEBUG_LEVEL > 1 + SwTwips nRstHeight = GetRstHeight(); +#endif + + pSect = pBoss->FindSctFrm(); + sal_Bool bDocEnd = bEnd ? !( pSect && pSect->IsEndnAtEnd() ) : + ( !( pSect && pSect->IsFtnAtEnd() ) && + FTNPOS_CHAPTER == GetNode()->GetDoc()->GetFtnInfo().ePos ); + //Ftn kann beim Follow angemeldet sein. + SwCntntFrm *pSrcFrm = FindFtnRef( pFtn ); + + if( bDocEnd ) + { + if( pSect && pSrcFrm ) + { + SwFtnFrm *pFtnFrm = pBoss->FindFtn( pSrcFrm, pFtn ); + if( pFtnFrm && pFtnFrm->IsInSct() ) + { + pBoss->RemoveFtn( pSrcFrm, pFtn ); + pSrcFrm = 0; + } + } + } + else if( bEnd && pSect ) + { + SwFtnFrm *pFtnFrm = pSrcFrm ? pBoss->FindFtn( pSrcFrm, pFtn ) : NULL; + if( pFtnFrm && !pFtnFrm->GetUpper() ) + pFtnFrm = NULL; + SwDoc *pDoc = GetNode()->GetDoc(); + if( SwLayouter::Collecting( pDoc, pSect, pFtnFrm ) ) + { + if( !pSrcFrm ) + { + SwFtnFrm *pNew = new SwFtnFrm(pDoc->GetDfltFrmFmt(),this,pFtn); + SwNodeIndex aIdx( *pFtn->GetStartNode(), 1 ); + ::_InsertCnt( pNew, pDoc, aIdx.GetIndex() ); + GetNode()->getIDocumentLayoutAccess()->GetLayouter()->CollectEndnote( pNew ); + } + else if( pSrcFrm != this ) + pBoss->ChangeFtnRef( pSrcFrm, pFtn, this ); + bInFtnConnect = sal_False; + return; + } + else if( pSrcFrm ) + { + SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm(); + if( !pFtnBoss->IsInSct() || + pFtnBoss->ImplFindSctFrm()->GetSection()!=pSect->GetSection() ) + { + pBoss->RemoveFtn( pSrcFrm, pFtn ); + pSrcFrm = 0; + } + } + } + + if( bDocEnd || bEnd ) + { + if( !pSrcFrm ) + pBoss->AppendFtn( this, pFtn ); + else if( pSrcFrm != this ) + pBoss->ChangeFtnRef( pSrcFrm, pFtn, this ); + bInFtnConnect = sal_False; + return; + } + + SwSaveFtnHeight aHeight( pBoss, nDeadLine ); + + if( !pSrcFrm ) // Es wurde ueberhaupt keine Ftn gefunden. + pBoss->AppendFtn( this, pFtn ); + else + { + SwFtnFrm *pFtnFrm = pBoss->FindFtn( pSrcFrm, pFtn ); + SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm(); + + sal_Bool bBrutal = sal_False; + + if( pFtnBoss == pBoss ) // Ref und Ftn sind auf der selben Seite/Spalte. + { + SwFrm *pCont = pFtnFrm->GetUpper(); + + SWRECTFN ( pCont ) + long nDiff = (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), + nDeadLine ); + + if( nDiff >= 0 ) + { + //Wenn die Fussnote bei einem Follow angemeldet ist, so ist + //es jetzt an der Zeit sie umzumelden. + if ( pSrcFrm != this ) + pBoss->ChangeFtnRef( pSrcFrm, pFtn, this ); + //Es steht Platz zur Verfuegung, also kann die Fussnote evtl. + //wachsen. + if ( pFtnFrm->GetFollow() && nDiff > 0 ) + { + SwTwips nHeight = (pCont->Frm().*fnRect->fnGetHeight)(); + pBoss->RearrangeFtns( nDeadLine, sal_False, pFtn ); + ValidateBodyFrm(); + ValidateFrm(); + ViewShell *pSh = GetShell(); + if ( pSh && nHeight == (pCont->Frm().*fnRect->fnGetHeight)() ) + //Damit uns nix durch die Lappen geht. + pSh->InvalidateWindows( pCont->Frm() ); + } + bInFtnConnect = sal_False; + return; + } + else + bBrutal = sal_True; + } + else + { + // Ref und Ftn sind nicht auf einer Seite, Move-Versuch ist noetig. + SwFrm* pTmp = this; + while( pTmp->GetNext() && pSrcFrm != pTmp ) + pTmp = pTmp->GetNext(); + if( pSrcFrm == pTmp ) + bBrutal = sal_True; + else + { // Wenn unser Boss in einem spaltigen Bereich sitzt, es aber auf + // der Seite schon einen FtnContainer gibt, hilft nur die brutale + // Methode + if( pSect && pSect->FindFtnBossFrm( !bEnd )->FindFtnCont() ) + bBrutal = sal_True; + // OD 08.11.2002 #104840# - use <SwLayoutFrm::IsBefore(..)> + else if ( !pFtnFrm->GetPrev() || + pFtnBoss->IsBefore( pBoss ) + ) + { + SwFtnBossFrm *pSrcBoss = pSrcFrm->FindFtnBossFrm( !bEnd ); + pSrcBoss->MoveFtns( pSrcFrm, this, pFtn ); + } + else + pBoss->ChangeFtnRef( pSrcFrm, pFtn, this ); + } + } + + // Die brutale Loesung: Fussnote entfernen und appenden. + // Es muss SetFtnDeadLine() gerufen werden, weil nach + // RemoveFtn die nMaxFtnHeight evtl. besser auf unsere Wuensche + // eingestellt werden kann. + if( bBrutal ) + { + pBoss->RemoveFtn( pSrcFrm, pFtn, sal_False ); + SwSaveFtnHeight *pHeight = bEnd ? NULL : + new SwSaveFtnHeight( pBoss, nDeadLine ); + pBoss->AppendFtn( this, pFtn ); + delete pHeight; + } + } + + // In spaltigen Bereichen, die noch nicht bis zum Seitenrand gehen, + // ist kein RearrangeFtns sinnvoll, da der Fussnotencontainer noch + // nicht kalkuliert worden ist. + if( !pSect || !pSect->Growable() ) + { + // Umgebung validieren, um Oszillationen zu verhindern. + SwSaveFtnHeight aNochmal( pBoss, nDeadLine ); + ValidateBodyFrm(); + pBoss->RearrangeFtns( nDeadLine, sal_True ); + ValidateFrm(); + } + else if( pSect->IsFtnAtEnd() ) + { + ValidateBodyFrm(); + ValidateFrm(); + } + +#if OSL_DEBUG_LEVEL > 1 + // pFtnFrm kann sich durch Calc veraendert haben ... + SwFtnFrm *pFtnFrm = pBoss->FindFtn( this, pFtn ); + if( pFtnFrm && pBoss != pFtnFrm->FindFtnBossFrm( !bEnd ) ) + { + int bla = 5; + (void)bla; + } + nRstHeight = GetRstHeight(); +#endif + bInFtnConnect = sal_False; + return; +} + + + +/************************************************************************* + * SwTxtFormatter::NewFtnPortion() + *************************************************************************/ + +// Die Portion fuer die Ftn-Referenz im Text +SwFtnPortion *SwTxtFormatter::NewFtnPortion( SwTxtFormatInfo &rInf, + SwTxtAttr *pHint ) +{ + ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), + "NewFtnPortion with unswapped frame" ); + + if( !pFrm->IsFtnAllowed() ) + return 0; + + SwTxtFtn *pFtn = (SwTxtFtn*)pHint; + SwFmtFtn& rFtn = (SwFmtFtn&)pFtn->GetFtn(); + SwDoc *pDoc = pFrm->GetNode()->GetDoc(); + + if( rInf.IsTest() ) + return new SwFtnPortion( rFtn.GetViewNumStr( *pDoc ), pFrm, pFtn ); + + SWAP_IF_SWAPPED( pFrm ) + + KSHORT nReal; + { + KSHORT nOldReal = pCurr->GetRealHeight(); + KSHORT nOldAscent = pCurr->GetAscent(); + KSHORT nOldHeight = pCurr->Height(); + ((SwTxtFormatter*)this)->CalcRealHeight(); + nReal = pCurr->GetRealHeight(); + if( nReal < nOldReal ) + nReal = nOldReal; + pCurr->SetRealHeight( nOldReal ); + pCurr->Height( nOldHeight ); + pCurr->SetAscent( nOldAscent ); + } + + SwTwips nLower = Y() + nReal; + + const bool bVertical = pFrm->IsVertical(); + if( bVertical ) + nLower = pFrm->SwitchHorizontalToVertical( nLower ); + + nLower = lcl_GetFtnLower( pFrm, nLower ); + + //6995: Wir frischen nur auf. Das Connect tut fuer diesen Fall nix + //Brauchbares, sondern wuerde stattdessen fuer diesen Fall meist die + //Ftn wegwerfen und neu erzeugen. + + if( !rInf.IsQuick() ) + pFrm->ConnectFtn( pFtn, nLower ); + + SwTxtFrm *pScrFrm = pFrm->FindFtnRef( pFtn ); + SwFtnBossFrm *pBoss = pFrm->FindFtnBossFrm( !rFtn.IsEndNote() ); + SwFtnFrm *pFtnFrm = NULL; + if( pScrFrm ) + pFtnFrm = pBoss->FindFtn( pScrFrm, pFtn ); + + // Wir erkundigen uns, ob durch unser Append irgendeine + // Fussnote noch auf der Seite/Spalte steht. Wenn nicht verschwindet + // auch unsere Zeile. Dies fuehrt zu folgendem erwuenschten + // Verhalten: Ftn1 pass noch auf die Seite/Spalte, Ftn2 nicht mehr. + // Also bleibt die Ftn2-Referenz auf der Seite/Spalte stehen. Die + // Fussnote selbst folgt aber erst auf der naechsten Seite/Spalte. + // Ausnahme: Wenn keine weitere Zeile auf diese Seite/Spalte passt, + // so sollte die Ftn2-Referenz auch auf die naechste wandern. + if( !rFtn.IsEndNote() ) + { + SwSectionFrm *pSct = pBoss->FindSctFrm(); + sal_Bool bAtSctEnd = pSct && pSct->IsFtnAtEnd(); + if( FTNPOS_CHAPTER != pDoc->GetFtnInfo().ePos || bAtSctEnd ) + { + SwFrm* pFtnCont = pBoss->FindFtnCont(); + // Wenn der Boss in einem Bereich liegt, kann es sich nur um eine + // Spalte dieses Bereichs handeln. Wenn dies nicht die erste Spalte + // ist, duerfen wir ausweichen + if( !pFrm->IsInTab() && ( GetLineNr() > 1 || pFrm->GetPrev() || + ( !bAtSctEnd && pFrm->GetIndPrev() ) || + ( pSct && pBoss->GetPrev() ) ) ) + { + if( !pFtnCont ) + { + rInf.SetStop( sal_True ); + UNDO_SWAP( pFrm ) + return 0; + } + else + { + // Es darf keine Fussnotencontainer in spaltigen Bereichen und + // gleichzeitig auf der Seite/Seitenspalte geben + if( pSct && !bAtSctEnd ) // liegt unser Container in einem (spaltigen) Bereich? + { + SwFtnBossFrm* pTmp = pBoss->FindSctFrm()->FindFtnBossFrm( sal_True ); + SwFtnContFrm* pFtnC = pTmp->FindFtnCont(); + if( pFtnC ) + { + SwFtnFrm* pTmpFrm = (SwFtnFrm*)pFtnC->Lower(); + if( pTmpFrm && *pTmpFrm < pFtn ) + { + rInf.SetStop( sal_True ); + UNDO_SWAP( pFrm ) + return 0; + } + } + } + // Ist dies die letzte passende Zeile? + SwTwips nTmpBot = Y() + nReal * 2; + + if( bVertical ) + nTmpBot = pFrm->SwitchHorizontalToVertical( nTmpBot ); + + SWRECTFN( pFtnCont ) + + const long nDiff = (*fnRect->fnYDiff)( + (pFtnCont->Frm().*fnRect->fnGetTop)(), + nTmpBot ); + + if( pScrFrm && nDiff < 0 ) + { + if( pFtnFrm ) + { + SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm(); + if( pFtnBoss != pBoss ) + { + // Wir sind in der letzte Zeile und die Fussnote + // ist auf eine andere Seite gewandert, dann wollen + // wir mit ... + rInf.SetStop( sal_True ); + UNDO_SWAP( pFrm ) + return 0; + } + } + } + } + } + } + } + // Endlich: FtnPortion anlegen und raus hier... + SwFtnPortion *pRet = new SwFtnPortion( rFtn.GetViewNumStr( *pDoc ), + pFrm, pFtn, nReal ); + rInf.SetFtnInside( sal_True ); + + UNDO_SWAP( pFrm ) + + return pRet; + } + +/************************************************************************* + * SwTxtFormatter::NewFtnNumPortion() + *************************************************************************/ + +// Die Portion fuer die Ftn-Nummerierung im Ftn-Bereich + +SwNumberPortion *SwTxtFormatter::NewFtnNumPortion( SwTxtFormatInfo &rInf ) const +{ + ASSERT( pFrm->IsInFtn() && !pFrm->GetIndPrev() && !rInf.IsFtnDone(), + "This is the wrong place for a ftnnumber" ); + if( rInf.GetTxtStart() != nStart || + rInf.GetTxtStart() != rInf.GetIdx() ) + return 0; + + const SwFtnFrm* pFtnFrm = pFrm->FindFtnFrm(); + const SwTxtFtn* pFtn = pFtnFrm->GetAttr(); + + // Aha, wir sind also im Fussnotenbereich + SwFmtFtn& rFtn = (SwFmtFtn&)pFtn->GetFtn(); + + SwDoc *pDoc = pFrm->GetNode()->GetDoc(); + XubString aFtnTxt( rFtn.GetViewNumStr( *pDoc, sal_True )); + + const SwEndNoteInfo* pInfo; + if( rFtn.IsEndNote() ) + pInfo = &pDoc->GetEndNoteInfo(); + else + pInfo = &pDoc->GetFtnInfo(); + const SwAttrSet& rSet = pInfo->GetCharFmt(*pDoc)->GetAttrSet(); + + const SwAttrSet* pParSet = &rInf.GetCharAttr(); + const IDocumentSettingAccess* pIDSA = pFrm->GetTxtNode()->getIDocumentSettingAccess(); + SwFont *pNumFnt = new SwFont( pParSet, pIDSA ); + + // --> FME 2005-02-17 #i37142# + // Underline style of paragraph font should not be considered + // Overline style of paragraph font should not be considered + // Weight style of paragraph font should not be considered + // Posture style of paragraph font should not be considered + // See also #i18463# and SwTxtFormatter::NewNumberPortion() + pNumFnt->SetUnderline( UNDERLINE_NONE ); + pNumFnt->SetOverline( UNDERLINE_NONE ); + pNumFnt->SetItalic( ITALIC_NONE, SW_LATIN ); + pNumFnt->SetItalic( ITALIC_NONE, SW_CJK ); + pNumFnt->SetItalic( ITALIC_NONE, SW_CTL ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SW_LATIN ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CJK ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CTL ); + // <-- + + pNumFnt->SetDiffFnt(&rSet, pIDSA ); + pNumFnt->SetVertical( pNumFnt->GetOrientation(), pFrm->IsVertical() ); + + SwFtnNumPortion* pNewPor = new SwFtnNumPortion( aFtnTxt, pNumFnt ); + pNewPor->SetLeft( !pFrm->IsRightToLeft() ); + return pNewPor; +} + +/************************************************************************* + * SwTxtFormatter::NewErgoSumPortion() + *************************************************************************/ + +XubString lcl_GetPageNumber( const SwPageFrm* pPage ) +{ + ASSERT( pPage, "GetPageNumber: Homeless TxtFrm" ); + MSHORT nVirtNum = pPage->GetVirtPageNum(); + const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType(); + return rNum.GetNumStr( nVirtNum ); +} + +SwErgoSumPortion *SwTxtFormatter::NewErgoSumPortion( SwTxtFormatInfo &rInf ) const +{ + // Wir koennen nicht davon ausgehen, dass wir ein Follow sind + // 7983: GetIdx() nicht nStart + if( !pFrm->IsInFtn() || pFrm->GetPrev() || + rInf.IsErgoDone() || rInf.GetIdx() != pFrm->GetOfst() || + pFrm->ImplFindFtnFrm()->GetAttr()->GetFtn().IsEndNote() ) + return 0; + + // Aha, wir sind also im Fussnotenbereich + const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo(); + SwTxtFrm *pQuoFrm = pFrm->FindQuoVadisFrm(); + if( !pQuoFrm ) + return 0; + const SwPageFrm* pPage = pFrm->FindPageFrm(); + const SwPageFrm* pQuoPage = pQuoFrm->FindPageFrm(); + if( pPage == pQuoFrm->FindPageFrm() ) + return 0; // Wenn der QuoVadis auf der selben (spaltigen) Seite steht + const XubString aPage = lcl_GetPageNumber( pPage ); + SwParaPortion *pPara = pQuoFrm->GetPara(); + if( pPara ) + pPara->SetErgoSumNum( aPage ); + if( !rFtnInfo.aErgoSum.Len() ) + return 0; + SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFtnInfo.aErgoSum, + lcl_GetPageNumber( pQuoPage ) ); + return pErgo; +} + +/************************************************************************* + * SwTxtFormatter::FormatQuoVadis() + *************************************************************************/ + +xub_StrLen SwTxtFormatter::FormatQuoVadis( const xub_StrLen nOffset ) +{ + ASSERT( ! pFrm->IsVertical() || ! pFrm->IsSwapped(), + "SwTxtFormatter::FormatQuoVadis with swapped frame" ); + + if( !pFrm->IsInFtn() || pFrm->ImplFindFtnFrm()->GetAttr()->GetFtn().IsEndNote() ) + return nOffset; + + const SwFrm* pErgoFrm = pFrm->FindFtnFrm()->GetFollow(); + if( !pErgoFrm && pFrm->HasFollow() ) + pErgoFrm = pFrm->GetFollow(); + if( !pErgoFrm ) + return nOffset; + + if( pErgoFrm == pFrm->GetNext() ) + { + SwFrm *pCol = pFrm->FindColFrm(); + while( pCol && !pCol->GetNext() ) + pCol = pCol->GetUpper()->FindColFrm(); + if( pCol ) + return nOffset; + } + else + { + const SwPageFrm* pPage = pFrm->FindPageFrm(); + const SwPageFrm* pErgoPage = pErgoFrm->FindPageFrm(); + if( pPage == pErgoPage ) + return nOffset; // Wenn der ErgoSum auf der selben Seite steht + } + + SwTxtFormatInfo &rInf = GetInfo(); + const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo(); + if( !rFtnInfo.aQuoVadis.Len() ) + return nOffset; + + // Ein Wort zu QuoVadis/ErgoSum: + // Fuer diese Texte wird der am Absatz eingestellte Font verwendet. + // Wir initialisieren uns also: +// ResetFont(); + FeedInf( rInf ); + SeekStartAndChg( rInf, sal_True ); + if( GetRedln() && pCurr->HasRedline() ) + GetRedln()->Seek( *pFnt, nOffset, 0 ); + + // Ein fieser Sonderfall: Flyfrms reichen in die Zeile und stehen + // natuerlich da, wo wir unseren Quovadis Text reinsetzen wollen. + // Erst mal sehen, ob es so schlimm ist: + SwLinePortion *pPor = pCurr->GetFirstPortion(); + KSHORT nLastLeft = 0; + while( pPor ) + { + if ( pPor->IsFlyPortion() ) + nLastLeft = ( (SwFlyPortion*) pPor)->Fix() + + ( (SwFlyPortion*) pPor)->Width(); + pPor = pPor->GetPortion(); + } + // Das alte Spiel: wir wollen, dass die Zeile an einer bestimmten + // Stelle umbricht, also beeinflussen wir die Width. + // nLastLeft ist jetzt quasi der rechte Rand. + const KSHORT nOldRealWidth = rInf.RealWidth(); + rInf.RealWidth( nOldRealWidth - nLastLeft ); + + XubString aErgo = lcl_GetPageNumber( pErgoFrm->FindPageFrm() ); + SwQuoVadisPortion *pQuo = new SwQuoVadisPortion(rFtnInfo.aQuoVadis, aErgo ); + pQuo->SetAscent( rInf.GetAscent() ); + pQuo->Height( rInf.GetTxtHeight() ); + pQuo->Format( rInf ); + USHORT nQuoWidth = pQuo->Width(); + SwLinePortion* pCurrPor = pQuo; + + while ( rInf.GetRest() ) + { + SwLinePortion* pFollow = rInf.GetRest(); + rInf.SetRest( 0 ); + pCurrPor->Move( rInf ); + + ASSERT( pFollow->IsQuoVadisPortion(), + "Quo Vadis, rest of QuoVadisPortion" ) + + // format the rest and append it to the other QuoVadis parts + pFollow->Format( rInf ); + nQuoWidth = nQuoWidth + pFollow->Width(); + + pCurrPor->Append( pFollow ); + pCurrPor = pFollow; + } + + nLastLeft = nOldRealWidth - nQuoWidth; + Right( Right() - nQuoWidth ); + + SWAP_IF_NOT_SWAPPED( pFrm ) + + const xub_StrLen nRet = FormatLine( nStart ); + + UNDO_SWAP( pFrm ) + + Right( rInf.Left() + nOldRealWidth - 1 ); + + nLastLeft = nOldRealWidth - pCurr->Width(); + FeedInf( rInf ); + + // Es kann durchaus sein, dass am Ende eine Marginportion steht, + // die beim erneuten Aufspannen nur Aerger bereiten wuerde. + pPor = pCurr->FindLastPortion(); + SwGluePortion *pGlue = pPor->IsMarginPortion() ? + (SwMarginPortion*) pPor : 0; + if( pGlue ) + { + pGlue->Height( 0 ); + pGlue->Width( 0 ); + pGlue->SetLen( 0 ); + pGlue->SetAscent( 0 ); + pGlue->SetPortion( NULL ); + pGlue->SetFixWidth(0); + } + + // Luxus: Wir sorgen durch das Aufspannen von Glues dafuer, + // dass der QuoVadis-Text rechts erscheint: + nLastLeft = nLastLeft - nQuoWidth; + if( nLastLeft ) + { + if( nLastLeft > pQuo->GetAscent() ) // Mindestabstand + { + switch( GetAdjust() ) + { + case SVX_ADJUST_BLOCK: + { + if( !pCurr->GetLen() || + CH_BREAK != GetInfo().GetChar(nStart+pCurr->GetLen()-1)) + nLastLeft = pQuo->GetAscent(); + nQuoWidth = nQuoWidth + nLastLeft; + break; + } + case SVX_ADJUST_RIGHT: + { + nLastLeft = pQuo->GetAscent(); + nQuoWidth = nQuoWidth + nLastLeft; + break; + } + case SVX_ADJUST_CENTER: + { + nQuoWidth = nQuoWidth + pQuo->GetAscent(); + long nDiff = nLastLeft - nQuoWidth; + if( nDiff < 0 ) + { + nLastLeft = pQuo->GetAscent(); + nQuoWidth = (USHORT)(-nDiff + nLastLeft); + } + else + { + nQuoWidth = 0; + nLastLeft = USHORT(( pQuo->GetAscent() + nDiff ) / 2); + } + break; + } + default: + nQuoWidth = nQuoWidth + nLastLeft; + } + } + else + nQuoWidth = nQuoWidth + nLastLeft; + if( nLastLeft ) + { + pGlue = new SwGluePortion(0); + pGlue->Width( nLastLeft ); + pPor->Append( pGlue ); + pPor = pPor->GetPortion(); + } + } + + // Jetzt aber: die QuoVadis-Portion wird angedockt: + pCurrPor = pQuo; + while ( pCurrPor ) + { + // pPor->Append deletes the pPortoin pointer of pPor. Therefore + // we have to keep a pointer to the next portion + pQuo = (SwQuoVadisPortion*)pCurrPor->GetPortion(); + pPor->Append( pCurrPor ); + pPor = pPor->GetPortion(); + pCurrPor = pQuo; + } + + pCurr->Width( pCurr->Width() + KSHORT( nQuoWidth ) ); + + // Und noch einmal adjustieren wegen des Adjustment und nicht zu Letzt + // wegen folgendem Sonderfall: In der Zeile hat der DummUser durchgaengig + // einen kleineren Font eingestellt als der vom QuoVadis-Text ... + CalcAdjustLine( pCurr ); + +#if OSL_DEBUG_LEVEL > 1 + if( OPTDBG( rInf ) ) + { +// aDbstream << "FormatQuoVadis:" << endl; +// pCurr->DebugPortions( aDbstream, rInf.GetTxt(), nStart ); + } +#endif + + // Uff... + return nRet; +} + + +/************************************************************************* + * SwTxtFormatter::MakeDummyLine() + *************************************************************************/ + +// MakeDummyLine() erzeugt eine Line, die bis zum unteren Seitenrand +// reicht. DummyLines bzw. DummyPortions sorgen dafuer, dass Oszillationen +// zum stehen kommen, weil Rueckflussmoeglichkeiten genommen werden. +// Sie werden bei absatzgebundenen Frames in Fussnoten und bei Ftn- +// Oszillationen verwendet. + +void SwTxtFormatter::MakeDummyLine() +{ + KSHORT nRstHeight = GetFrmRstHeight(); + if( pCurr && nRstHeight > pCurr->Height() ) + { + SwLineLayout *pLay = new SwLineLayout; + nRstHeight = nRstHeight - pCurr->Height(); + pLay->Height( nRstHeight ); + pLay->SetAscent( nRstHeight ); + Insert( pLay ); + Next(); + } +} + +/************************************************************************* + * class SwFtnSave + *************************************************************************/ +class SwFtnSave +{ + SwTxtSizeInfo *pInf; + SwFont *pFnt; + SwFont *pOld; +public: + SwFtnSave( const SwTxtSizeInfo &rInf, + const SwTxtFtn *pTxtFtn, + const bool bApplyGivenScriptType, + const BYTE nGivenScriptType ); + ~SwFtnSave(); +}; + +/************************************************************************* + * SwFtnSave::SwFtnSave() + *************************************************************************/ + +SwFtnSave::SwFtnSave( const SwTxtSizeInfo &rInf, + const SwTxtFtn* pTxtFtn, + const bool bApplyGivenScriptType, + const BYTE nGivenScriptType ) + : pInf( &((SwTxtSizeInfo&)rInf) ) + , pFnt( 0 ) + , pOld( 0 ) +{ + if( pTxtFtn && rInf.GetTxtFrm() ) + { + pFnt = ((SwTxtSizeInfo&)rInf).GetFont(); + pOld = new SwFont( *pFnt ); + pOld->GetTox() = pFnt->GetTox(); + pFnt->GetTox() = 0; + SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn(); + const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); + + // --> OD 2009-01-29 #i98418# + if ( bApplyGivenScriptType ) + { + pFnt->SetActual( nGivenScriptType ); + } + else + { + // examine text and set script + String aTmpStr( rFtn.GetViewNumStr( *pDoc ) ); + pFnt->SetActual( SwScriptInfo::WhichFont( 0, &aTmpStr, 0 ) ); + } + // <-- + + const SwEndNoteInfo* pInfo; + if( rFtn.IsEndNote() ) + pInfo = &pDoc->GetEndNoteInfo(); + else + pInfo = &pDoc->GetFtnInfo(); + const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet(); + pFnt->SetDiffFnt( &rSet, rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess() ); + + // we reduce footnote size, if we are inside a double line portion + if ( ! pOld->GetEscapement() && 50 == pOld->GetPropr() ) + { + Size aSize = pFnt->GetSize( pFnt->GetActual() ); + pFnt->SetSize( Size( (long)aSize.Width() / 2, + (long)aSize.Height() / 2 ), + pFnt->GetActual() ); + } + + // set the correct rotation at the footnote font + const SfxPoolItem* pItem; + if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE, + sal_True, &pItem )) + pFnt->SetVertical( ((SvxCharRotateItem*)pItem)->GetValue(), + rInf.GetTxtFrm()->IsVertical() ); + + pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() ); + + if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_BACKGROUND, + sal_True, &pItem )) + pFnt->SetBackColor( new Color( ((SvxBrushItem*)pItem)->GetColor() ) ); + } + else + pFnt = NULL; +} + +/************************************************************************* + * SwFtnSave::~SwFtnSave() + *************************************************************************/ + +SwFtnSave::~SwFtnSave() +{ + if( pFnt ) + { + // SwFont zurueckstellen + *pFnt = *pOld; + pFnt->GetTox() = pOld->GetTox(); + pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() ); + delete pOld; + } +} + +/************************************************************************* + * SwFtnPortion::SwFtnPortion() + *************************************************************************/ + +SwFtnPortion::SwFtnPortion( const XubString &rExpand, SwTxtFrm *pFrame, + SwTxtFtn *pFootn, KSHORT nReal ) + : SwFldPortion( rExpand, 0 ) + , pFrm(pFrame) + , pFtn(pFootn) + , nOrigHeight( nReal ) + // --> OD 2009-01-29 #i98418# + , mbPreferredScriptTypeSet( false ) + , mnPreferredScriptType( SW_LATIN ) + // <-- +{ + SetLen(1); + SetWhichPor( POR_FTN ); +} + +/************************************************************************* + * SwFtnPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwFtnPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const +{ + rTxt = aExpand; + return sal_True; +} + +/************************************************************************* + * virtual SwFtnPortion::Format() + *************************************************************************/ + +sal_Bool SwFtnPortion::Format( SwTxtFormatInfo &rInf ) +{ + // --> OD 2009-01-29 #i98418# +// SwFtnSave aFtnSave( rInf, pFtn ); + SwFtnSave aFtnSave( rInf, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType ); + // <-- + // the idx is manipulated in SwExpandPortion::Format + // this flag indicates, that a footnote is allowed to trigger + // an underflow during SwTxtGuess::Guess + rInf.SetFakeLineStart( rInf.GetIdx() > rInf.GetLineStart() ); + sal_Bool bFull = SwFldPortion::Format( rInf ); + rInf.SetFakeLineStart( sal_False ); + SetAscent( rInf.GetAscent() ); + Height( rInf.GetTxtHeight() ); + rInf.SetFtnDone( !bFull ); + if( !bFull ) + rInf.SetParaFtn(); + return bFull; +} + +/************************************************************************* + * virtual SwFtnPortion::Paint() + *************************************************************************/ + +void SwFtnPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + // --> OD 2009-01-29 #i98418# +// SwFtnSave aFtnSave( rInf, pFtn ); + SwFtnSave aFtnSave( rInf, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType ); + // <-- + rInf.DrawViewOpt( *this, POR_FTN ); + SwExpandPortion::Paint( rInf ); +} + +/************************************************************************* + * virtual SwFtnPortion::GetTxtSize() + *************************************************************************/ + +SwPosSize SwFtnPortion::GetTxtSize( const SwTxtSizeInfo &rInfo ) const +{ + // --> OD 2009-01-29 #i98418# +// SwFtnSave aFtnSave( rInfo, pFtn ); + SwFtnSave aFtnSave( rInfo, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType ); + // <-- + return SwExpandPortion::GetTxtSize( rInfo ); +} + +// --> OD 2009-01-29 #i98418# +void SwFtnPortion::SetPreferredScriptType( BYTE nPreferredScriptType ) +{ + mbPreferredScriptTypeSet = true; + mnPreferredScriptType = nPreferredScriptType; +} +// <-- + +/************************************************************************* + * class SwQuoVadisPortion + *************************************************************************/ + +SwFldPortion *SwQuoVadisPortion::Clone( const XubString &rExpand ) const +{ return new SwQuoVadisPortion( rExpand, aErgo ); } + +SwQuoVadisPortion::SwQuoVadisPortion( const XubString &rExp, const XubString& rStr ) + : SwFldPortion( rExp ), aErgo(rStr) +{ + SetLen(0); + SetWhichPor( POR_QUOVADIS ); +} + +/************************************************************************* + * virtual SwQuoVadisPortion::Format() + *************************************************************************/ + +sal_Bool SwQuoVadisPortion::Format( SwTxtFormatInfo &rInf ) +{ + // erster Versuch, vielleicht passt der Text + CheckScript( rInf ); + sal_Bool bFull = SwFldPortion::Format( rInf ); + SetLen( 0 ); + + if( bFull ) + { + // zweiter Versuch, wir kuerzen den String: + aExpand = XubString( "...", RTL_TEXTENCODING_MS_1252 ); + bFull = SwFldPortion::Format( rInf ); + SetLen( 0 ); + if( bFull ) + // dritter Versuch, es langt: jetzt wird gestaucht: + Width( USHORT(rInf.Width() - rInf.X()) ); + + // 8317: keine mehrzeiligen Felder bei QuoVadis und ErgoSum + if( rInf.GetRest() ) + { + delete rInf.GetRest(); + rInf.SetRest( 0 ); + } + } + return bFull; +} + +/************************************************************************* + * virtual SwQuoVadisPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwQuoVadisPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const +{ + rTxt = aExpand; + // if this QuoVadisPortion has a follow, the follow is responsible for + // the ergo text. + if ( ! HasFollow() ) + rTxt += aErgo; + return sal_True; +} + +/************************************************************************* + * virtual SwQuoVadisPortion::HandlePortion() + *************************************************************************/ + +void SwQuoVadisPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString( aExpand ); + aString += aErgo; + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + +/************************************************************************* + * virtual SwQuoVadisPortion::Paint() + *************************************************************************/ + +void SwQuoVadisPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + // Wir wollen _immer_ per DrawStretchText ausgeben, + // weil nErgo schnell mal wechseln kann. + if( PrtWidth() ) + { + rInf.DrawViewOpt( *this, POR_QUOVADIS ); + SwTxtSlot aDiffTxt( &rInf, this, true, false ); + SwFontSave aSave( rInf, pFnt ); + rInf.DrawText( *this, rInf.GetLen(), sal_True ); + } +} + +/************************************************************************* + * class SwErgoSumPortion + *************************************************************************/ + +SwFldPortion *SwErgoSumPortion::Clone( const XubString &rExpand ) const +{ + UniString aTmp; // = UniString::CreateFromInt32( 0 ); + return new SwErgoSumPortion( rExpand, aTmp ); +} + +SwErgoSumPortion::SwErgoSumPortion( const XubString &rExp, const XubString& rStr ) + : SwFldPortion( rExp ) +{ + SetLen(0); + aExpand += rStr; + + // 7773: sinnvolle Massnahme: ein Blank Abstand zum Text + aExpand += ' '; + SetWhichPor( POR_ERGOSUM ); +} + +xub_StrLen SwErgoSumPortion::GetCrsrOfst( const KSHORT ) const +{ + return 0; +} + +/************************************************************************* + * virtual SwErgoSumPortion::Format() + *************************************************************************/ + +sal_Bool SwErgoSumPortion::Format( SwTxtFormatInfo &rInf ) +{ + sal_Bool bFull = SwFldPortion::Format( rInf ); + SetLen( 0 ); + rInf.SetErgoDone( sal_True ); + + // 8317: keine mehrzeiligen Felder bei QuoVadis und ErgoSum + if( bFull && rInf.GetRest() ) + { + delete rInf.GetRest(); + rInf.SetRest( 0 ); + } + + // We return false in order to get some text into the current line, + // even if it's full (better than looping) + return sal_False; +} + + +/************************************************************************* + * SwParaPortion::SetErgoSumNum() + *************************************************************************/ + +void SwParaPortion::SetErgoSumNum( const XubString& rErgo ) +{ + SwLineLayout *pLay = this; + while( pLay->GetNext() ) + { + DBG_LOOP; + pLay = pLay->GetNext(); + } + SwLinePortion *pPor = pLay; + SwQuoVadisPortion *pQuo = 0; + while( pPor && !pQuo ) + { + if ( pPor->IsQuoVadisPortion() ) + pQuo = (SwQuoVadisPortion*)pPor; + pPor = pPor->GetPortion(); + } + if( pQuo ) + pQuo->SetNumber( rErgo ); +} + +/************************************************************************* + * SwParaPortion::UpdateQuoVadis() + * + * Wird im SwTxtFrm::Prepare() gerufen + *************************************************************************/ + +sal_Bool SwParaPortion::UpdateQuoVadis( const XubString &rQuo ) +{ + SwLineLayout *pLay = this; + while( pLay->GetNext() ) + { + DBG_LOOP; + pLay = pLay->GetNext(); + } + SwLinePortion *pPor = pLay; + SwQuoVadisPortion *pQuo = 0; + while( pPor && !pQuo ) + { + if ( pPor->IsQuoVadisPortion() ) + pQuo = (SwQuoVadisPortion*)pPor; + pPor = pPor->GetPortion(); + } + + if( !pQuo ) + return sal_False; + + return pQuo->GetQuoTxt() == rQuo; +} + + + diff --git a/sw/source/core/text/txthyph.cxx b/sw/source/core/text/txthyph.cxx new file mode 100644 index 000000000000..81a5c98e1913 --- /dev/null +++ b/sw/source/core/text/txthyph.cxx @@ -0,0 +1,698 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txthyph.cxx,v $ + * $Revision: 1.26 $ + * + * 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 <svx/unolingu.hxx> +#include <com/sun/star/i18n/WordType.hpp> +#include <EnhancedPDFExportHelper.hxx> +#include <viewopt.hxx> // SwViewOptions +#include <viewsh.hxx> +#include <errhdl.hxx> +#include <txtcfg.hxx> +#include <SwPortionHandler.hxx> +#include <porhyph.hxx> // +#include <inftxt.hxx> +#include <itrform2.hxx> // +#include <guess.hxx> // +#include <splargs.hxx> // SwInterHyphInfo + +#ifndef PRODUCT +extern const sal_Char *GetLangName( const MSHORT nLang ); +#endif + +using ::rtl::OUString; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::i18n; + +/************************************************************************* + * SwTxtFormatInfo::HyphWord() + *************************************************************************/ + +Reference< XHyphenatedWord > SwTxtFormatInfo::HyphWord( + const XubString &rTxt, const MSHORT nMinTrail ) +{ + if( rTxt.Len() < 4 || pFnt->IsSymbol(pVsh) ) + return 0; +// ASSERT( IsHyphenate(), "SwTxtFormatter::HyphWord: why?" ); + Reference< XHyphenator > xHyph = ::GetHyphenator(); + Reference< XHyphenatedWord > xHyphWord; + + if( xHyph.is() ) + xHyphWord = xHyph->hyphenate( OUString(rTxt), + pBreakIt->GetLocale( pFnt->GetLanguage() ), + rTxt.Len() - nMinTrail, GetHyphValues() ); + return xHyphWord; + +} + +/************************************************************************* + * SwTxtFrm::Hyphenate + * + * Wir formatieren eine Zeile fuer die interaktive Trennung + *************************************************************************/ + +sal_Bool SwTxtFrm::Hyphenate( SwInterHyphInfo &rHyphInf ) +{ + ASSERT( ! IsVertical() || ! IsSwapped(),"swapped frame at SwTxtFrm::Hyphenate" ); + + if( !pBreakIt->GetBreakIter().is() ) + return sal_False;; + // Wir machen den Laden erstmal dicht: + ASSERT( !IsLocked(), "SwTxtFrm::Hyphenate: this is locked" ); + // 4935: Der frame::Frame muss eine gueltige SSize haben! + Calc(); + GetFormatted(); + + sal_Bool bRet = sal_False; + if( !IsEmpty() ) + { + // Wir muessen die Trennung immer einschalten. + // Keine Angst, der SwTxtIter sichert im Hyphenate die alte Zeile. + SwTxtFrmLocker aLock( this ); + + if ( IsVertical() ) + SwapWidthAndHeight(); + + SwTxtFormatInfo aInf( this, sal_True ); // sal_True fuer interactive hyph! + SwTxtFormatter aLine( this, &aInf ); + aLine.CharToLine( rHyphInf.nStart ); + // Wenn wir innerhalb des ersten Wortes einer Zeile stehen, so koennte + // dieses in der vorherigen getrennt werden, deshalb gehen wir ein Zeile + // zurueck. + if( aLine.Prev() ) + { + SwLinePortion *pPor = aLine.GetCurr()->GetFirstPortion(); + while( pPor->GetPortion() ) + pPor = pPor->GetPortion(); + if( pPor->GetWhichPor() == POR_SOFTHYPH || + pPor->GetWhichPor() == POR_SOFTHYPHSTR ) + aLine.Next(); + } + + const xub_StrLen nEnd = rHyphInf.GetEnd(); + while( !bRet && aLine.GetStart() < nEnd ) + { + DBG_LOOP; + bRet = aLine.Hyphenate( rHyphInf ); + if( !aLine.Next() ) + break; + } + + if ( IsVertical() ) + SwapWidthAndHeight(); + } + return bRet; +} + +/************************************************************************* + * SwTxtFormatter::Hyphenate + * + * Wir formatieren eine Zeile fuer die interaktive Trennung + *************************************************************************/ +// Wir koennen davon ausgehen, dass bereits formatiert wurde. +// Fuer die CeBIT'93 gehen wir den einfachen, sicheren Weg: +// Die Zeile wird einfach neu formatiert, der Hyphenator wird dann +// so vorbereitet, wie ihn die UI erwartet. +// Hier stehen natuerlich enorme Optimierungsmoeglichkeiten offen. + +void SetParaPortion( SwTxtInfo *pInf, SwParaPortion *pRoot ) +{ + ASSERT( pRoot, "SetParaPortion: no root anymore" ); + pInf->pPara = pRoot; +} + +sal_Bool SwTxtFormatter::Hyphenate( SwInterHyphInfo &rHyphInf ) +{ + SwTxtFormatInfo &rInf = GetInfo(); + sal_Bool bRet = sal_False; + + // In der letzten Zeile gibt es nie etwas zu trennen. + // Es sei denn, es befindet sich eine FlyPortion darin, + // oder es ist die letzte Zeile des Masters + if( !GetNext() && !rInf.GetTxtFly()->IsOn() && !pFrm->GetFollow() ) + return bRet; + + xub_StrLen nWrdStart = nStart; + + // Wir muessen die alte Zeile erhalten. Ein Beispiel: + // Das Attribut fuer Trennung wurde nicht gesetzt, + // in SwTxtFrm::Hyphenate wird es jedoch immer gesetzt, + // weil wir Trennpositionen im Hyphenator einstellen wollen. + SwLineLayout *pOldCurr = pCurr; + + InitCntHyph(); + + // 5298: IsParaLine() (ex.IsFirstLine) fragt auf GetParaPortion() ab. + // wir muessen gleiche Bedingungen schaffen: in der ersten + // Zeile formatieren wir SwParaPortions... + if( pOldCurr->IsParaPortion() ) + { + SwParaPortion *pPara = new SwParaPortion(); + SetParaPortion( &rInf, pPara ); + pCurr = pPara; + ASSERT( IsParaLine(), "SwTxtFormatter::Hyphenate: not the first" ); + } + else + pCurr = new SwLineLayout(); + + nWrdStart = FormatLine( nWrdStart ); + + // Man muss immer im Hinterkopf behalten, dass es z.B. + // Felder gibt, die aufgetrennt werden koennen ... + if( pCurr->PrtWidth() && pCurr->GetLen() ) + { + // Wir muessen uns darauf einstellen, dass in der Zeile + // FlyFrms haengen, an denen auch umgebrochen werden darf. + // Wir suchen also die erste HyphPortion in dem angegebenen + // Bereich. + + SwLinePortion *pPos = pCurr->GetPortion(); + const xub_StrLen nPamStart = rHyphInf.nStart; + nWrdStart = nStart; + const xub_StrLen nEnd = rHyphInf.GetEnd(); + while( pPos ) + { + // Entweder wir liegen drueber oder wir laufen gerade auf eine + // Hyphportion die am Ende der Zeile oder vor einem Flys steht. + if( nWrdStart >= nEnd ) + { + nWrdStart = 0; + break; + } + + if( nWrdStart >= nPamStart && pPos->InHyphGrp() + && ( !pPos->IsSoftHyphPortion() + || ((SwSoftHyphPortion*)pPos)->IsExpand() ) ) + { + nWrdStart = nWrdStart + pPos->GetLen(); + break; + } + + nWrdStart = nWrdStart + pPos->GetLen(); + pPos = pPos->GetPortion(); + } + // Wenn pPos 0 ist, wurde keine Trennstelle ermittelt. + if( !pPos ) + nWrdStart = 0; + } + + // Das alte LineLayout wird wieder eingestellt ... + delete pCurr; + pCurr = pOldCurr; + + if( pOldCurr->IsParaPortion() ) + { + SetParaPortion( &rInf, (SwParaPortion*)pOldCurr ); + ASSERT( IsParaLine(), "SwTxtFormatter::Hyphenate: even not the first" ); + } + + if( nWrdStart ) + { + // nWrdStart bezeichnet nun die Position im String, der + // fuer eine Trennung zur Debatte steht. + // Start() hangelt sich zum End() + rHyphInf.nWordStart = nWrdStart; + + xub_StrLen nLen = 0; + const xub_StrLen nEnd = nWrdStart; + + // Wir suchen vorwaerts + Reference< XHyphenatedWord > xHyphWord; + + Boundary aBound = + pBreakIt->GetBreakIter()->getWordBoundary( rInf.GetTxt(), nWrdStart, + pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ), WordType::DICTIONARY_WORD, sal_True ); + nWrdStart = static_cast<xub_StrLen>(aBound.startPos); + nLen = static_cast<xub_StrLen>(aBound.endPos - nWrdStart); + bRet = 0 != nLen; + if( bRet ) + { + XubString aSelTxt( rInf.GetTxt().Copy(nWrdStart, nLen) ); + xub_StrLen nCnt = 0; + +// these things should be handled by the dialog +// for( xub_StrLen i = 0; i < nLen; ++i ) +// { +// sal_Unicode cCh = aSelTxt.GetChar(i); +// if( (CH_TXTATR_BREAKWORD == cCh || CH_TXTATR_INWORD == cCh ) +// && rInf.HasHint( nWrdStart + i ) ) +// { +// aSelTxt.Erase( i , 1 ); +// nCnt++; +// --nLen; +// if( i ) +// --i; +// } +// } + + { + MSHORT nMinTrail = 0; + if( nWrdStart + nLen > nEnd ) + nMinTrail = nWrdStart + nLen - nEnd - 1; + + //!! rHyphInf.SetHyphWord( ... ) mu??? hier geschehen + xHyphWord = rInf.HyphWord( aSelTxt, nMinTrail ); + bRet = xHyphWord.is(); + if ( !rHyphInf.IsCheck() && sal_False == bRet ) + rHyphInf.SetNoLang( sal_True ); + } + + if( bRet ) + { + rHyphInf.SetHyphWord( xHyphWord ); + rHyphInf.nWordStart = nWrdStart; + rHyphInf.nWordLen = nLen+nCnt; + rHyphInf.SetNoLang( sal_False ); + rHyphInf.SetCheck( sal_True ); + } +#ifdef DEBUGGY + if( OPTDBG( rInf ) ) + { + ASSERT( aSelTxt == aHyphWord, + "!SwTxtFormatter::Hyphenate: different words, different planets" ); + aDbstream << "Diff: \"" << aSelTxt.GetStr() << "\" != \"" + << aHyphWord.GetStr() << "\"" << endl; + ASSERT( bRet, "!SwTxtFormatter::Hyphenate: three of a perfect pair" ); + aDbstream << "Hyphenate: "; + } +#endif + } + } + return bRet; +} + +/************************************************************************* + * SwTxtPortion::CreateHyphen() + *************************************************************************/ + +sal_Bool SwTxtPortion::CreateHyphen( SwTxtFormatInfo &rInf, SwTxtGuess &rGuess ) +{ + Reference< XHyphenatedWord > xHyphWord = rGuess.HyphWord(); + + ASSERT( !pPortion, "SwTxtPortion::CreateHyphen(): another portion, another planet..." ) + ASSERT( xHyphWord.is(), "SwTxtPortion::CreateHyphen(): You are lucky! The code is robust here." ) + + if( rInf.IsHyphForbud() || + pPortion || // robust + !xHyphWord.is() || // more robust + // Mehrzeilige Felder duerfen nicht interaktiv getrennt werden. + ( rInf.IsInterHyph() && InFldGrp() ) ) + return sal_False; + + SwHyphPortion *pHyphPor; + xub_StrLen nPorEnd; + SwTxtSizeInfo aInf( rInf ); + + // first case: hyphenated word has alternative spelling + if ( xHyphWord->isAlternativeSpelling() ) + { + SvxAlternativeSpelling aAltSpell; + aAltSpell = SvxGetAltSpelling( xHyphWord ); + ASSERT( aAltSpell.bIsAltSpelling, "no alternatve spelling" ); + + XubString aAltTxt = aAltSpell.aReplacement; + nPorEnd = aAltSpell.nChangedPos + rGuess.BreakStart() - rGuess.FieldDiff(); + xub_StrLen nTmpLen = 0; + + // soft hyphen at alternative spelling position? + if( rInf.GetTxt().GetChar( rInf.GetSoftHyphPos() ) == CHAR_SOFTHYPHEN ) + { + pHyphPor = new SwSoftHyphStrPortion( aAltTxt ); + nTmpLen = 1; + } + else { + pHyphPor = new SwHyphStrPortion( aAltTxt ); + } + + // length of pHyphPor is adjusted + pHyphPor->SetLen( aAltTxt.Len() + 1 ); + (SwPosSize&)(*pHyphPor) = pHyphPor->GetTxtSize( rInf ); + pHyphPor->SetLen( aAltSpell.nChangedLength + nTmpLen ); + } + else + { + // second case: no alternative spelling + SwHyphPortion aHyphPor; + aHyphPor.SetLen( 1 ); + + static const void* pLastMagicNo = 0; + static KSHORT aMiniCacheH = 0, aMiniCacheW = 0; + const void* pTmpMagic; + MSHORT nFntIdx; + rInf.GetFont()->GetMagic( pTmpMagic, nFntIdx, rInf.GetFont()->GetActual() ); + if( !pLastMagicNo || pLastMagicNo != pTmpMagic ) { + pLastMagicNo = pTmpMagic; + (SwPosSize&)aHyphPor = aHyphPor.GetTxtSize( rInf ); + aMiniCacheH = aHyphPor.Height(), aMiniCacheW = aHyphPor.Width(); + } else { + aHyphPor.Height( aMiniCacheH ), aHyphPor.Width( aMiniCacheW ); + } + aHyphPor.SetLen( 0 ); + pHyphPor = new SwHyphPortion( aHyphPor ); + + pHyphPor->SetWhichPor( POR_HYPH ); + + // values required for this + nPorEnd = xHyphWord->getHyphenPos() + 1 + rGuess.BreakStart() + - rGuess.FieldDiff(); + } + + // portion end must be in front of us + // we do not put hyphens at start of line + if ( nPorEnd > rInf.GetIdx() || + ( nPorEnd == rInf.GetIdx() && rInf.GetLineStart() != rInf.GetIdx() ) ) + { + aInf.SetLen( nPorEnd - rInf.GetIdx() ); + pHyphPor->SetAscent( GetAscent() ); + SetLen( aInf.GetLen() ); + CalcTxtSize( aInf ); + + Insert( pHyphPor ); + + short nKern = rInf.GetFont()->CheckKerning(); + if( nKern ) + new SwKernPortion( *this, nKern ); + + return sal_True; + } + + // last exit for the lost + delete pHyphPor; + BreakCut( rInf, rGuess ); + return sal_False; +} + + +/************************************************************************* + * virtual SwHyphPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwHyphPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const +{ + // --> FME 2004-06-24 #i16816# tagged pdf support + const sal_Unicode cChar = rInf.GetVsh() && + rInf.GetVsh()->GetViewOptions()->IsPDFExport() && + SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut() ) ? + 0xad : + '-'; + // <-- + + rTxt = cChar; + return sal_True; +} + +/************************************************************************* + * virtual SwHyphPortion::HandlePortion() + *************************************************************************/ + +void SwHyphPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + String aString( '-' ); + rPH.Special( GetLen(), aString, GetWhichPor() ); +} + +/************************************************************************* + * virtual SwHyphPortion::Format() + *************************************************************************/ + +sal_Bool SwHyphPortion::Format( SwTxtFormatInfo &rInf ) +{ + const SwLinePortion *pLast = rInf.GetLast(); + Height( pLast->Height() ); + SetAscent( pLast->GetAscent() ); + XubString aTxt; + + if( !GetExpTxt( rInf, aTxt ) ) + return sal_False; + + PrtWidth( rInf.GetTxtSize( aTxt ).Width() ); + const sal_Bool bFull = rInf.Width() <= rInf.X() + PrtWidth(); + if( bFull && !rInf.IsUnderFlow() ) { + Truncate(); + rInf.SetUnderFlow( this ); + } + + return bFull; +} + +/************************************************************************* + * virtual SwHyphStrPortion::GetExpTxt() + *************************************************************************/ + +sal_Bool SwHyphStrPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const +{ + rTxt = aExpand; + return sal_True; +} + +/************************************************************************* + * virtual SwHyphStrPortion::HandlePortion() + *************************************************************************/ + +void SwHyphStrPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Special( GetLen(), aExpand, GetWhichPor() ); +} + +/************************************************************************* + * class SwSoftHyphPortion + *************************************************************************/ + +SwLinePortion *SwSoftHyphPortion::Compress() { return this; } + +SwSoftHyphPortion::SwSoftHyphPortion() : + bExpand(sal_False), nViewWidth(0), nHyphWidth(0) +{ + SetLen(1); + SetWhichPor( POR_SOFTHYPH ); +} + +KSHORT SwSoftHyphPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const +{ + // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten + // Moment errechnet werden: + if( !Width() && rInf.OnWin() && rInf.GetOpt().IsSoftHyph() && !IsExpand() ) + { + if( !nViewWidth ) + ((SwSoftHyphPortion*)this)->nViewWidth + = rInf.GetTxtSize( '-' ).Width(); + } + else + ((SwSoftHyphPortion*)this)->nViewWidth = 0; + return nViewWidth; +} + +/* Faelle: + * 1) SoftHyph steht in der Zeile, ViewOpt aus. + * -> unsichtbar, Nachbarn unveraendert + * 2) SoftHyph steht in der Zeile, ViewOpt an. + * -> sichtbar, Nachbarn veraendert + * 3) SoftHyph steht am Zeilenende, ViewOpt aus/an. + * -> immer sichtbar, Nachbarn unveraendert + */ + +void SwSoftHyphPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + if( Width() ) + { + rInf.DrawViewOpt( *this, POR_SOFTHYPH ); + SwExpandPortion::Paint( rInf ); + } +} + +/************************************************************************* + * virtual SwSoftHyphPortion::Format() + *************************************************************************/ + +/* Die endgueltige Breite erhalten wir im FormatEOL(). + * In der Underflow-Phase stellen wir fest, ob ueberhaupt ein + * alternatives Spelling vorliegt. Wenn ja ... + * + * Fall 1: "Au-to" + * 1) {Au}{-}{to}, {to} passt nicht mehr => Underflow + * 2) {-} ruft Hyphenate => keine Alternative + * 3) FormatEOL() und bFull = sal_True + * + * Fall 2: "Zuc-ker" + * 1) {Zuc}{-}{ker}, {ker} passt nicht mehr => Underflow + * 2) {-} ruft Hyphenate => Alternative! + * 3) Underflow() und bFull = sal_True + * 4) {Zuc} ruft Hyphenate => {Zuk}{-}{ker} + */ + +sal_Bool SwSoftHyphPortion::Format( SwTxtFormatInfo &rInf ) +{ + sal_Bool bFull = sal_True; + + // special case for old german spelling + if( rInf.IsUnderFlow() ) + { + if( rInf.GetSoftHyphPos() ) + return sal_True; + + const sal_Bool bHyph = rInf.ChgHyph( sal_True ); + if( rInf.IsHyphenate() ) + { + rInf.SetSoftHyphPos( rInf.GetIdx() ); + Width(0); + // if the soft hyphend word has an alternative spelling + // when hyphenated (old german spelling), the soft hyphen + // portion has to trigger an underflow + SwTxtGuess aGuess; + bFull = rInf.IsInterHyph() || + !aGuess.AlternativeSpelling( rInf, rInf.GetIdx() - 1 ); + } + rInf.ChgHyph( bHyph ); + + if( bFull && !rInf.IsHyphForbud() ) + { + rInf.SetSoftHyphPos(0); + FormatEOL( rInf ); + if ( rInf.GetFly() ) + rInf.GetRoot()->SetMidHyph( sal_True ); + else + rInf.GetRoot()->SetEndHyph( sal_True ); + } + else + { + rInf.SetSoftHyphPos( rInf.GetIdx() ); + Truncate(); + rInf.SetUnderFlow( this ); + } + return sal_True; + } + + rInf.SetSoftHyphPos(0); + SetExpand( sal_True ); + bFull = SwHyphPortion::Format( rInf ); + SetExpand( sal_False ); + if( !bFull ) + { + // default-maessig besitzen wir keine Breite, aber eine Hoehe + nHyphWidth = Width(); + Width(0); + } + return bFull; +} + +/************************************************************************* + * virtual SwSoftHyphPortion::FormatEOL() + *************************************************************************/ +// Format end of Line + +void SwSoftHyphPortion::FormatEOL( SwTxtFormatInfo &rInf ) +{ + if( !IsExpand() ) + { + SetExpand( sal_True ); + if( rInf.GetLast() == this ) + rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) ); + + // 5964: alte Werte muessen wieder zurueckgesetzt werden. + const SwTwips nOldX = rInf.X(); + const xub_StrLen nOldIdx = rInf.GetIdx(); + rInf.X( rInf.X() - PrtWidth() ); + rInf.SetIdx( rInf.GetIdx() - GetLen() ); + const sal_Bool bFull = SwHyphPortion::Format( rInf ); + nHyphWidth = Width(); + + // 6976: Eine truebe Sache: Wir werden erlaubterweise breiter, + // aber gleich wird noch ein Fly verarbeitet, der eine korrekte + // X-Position braucht. + if( bFull || !rInf.GetFly() ) + rInf.X( nOldX ); + else + rInf.X( nOldX + Width() ); + rInf.SetIdx( nOldIdx ); + } +} + +/************************************************************************* + * virtual SwSoftHyphPortion::GetExpTxt() + * + * Wir expandieren: + * - wenn die Sonderzeichen sichtbar sein sollen + * - wenn wir am Ende der Zeile stehen. + * - wenn wir vor einem (echten/emuliertem) Zeilenumbruch stehen + *************************************************************************/ + +sal_Bool SwSoftHyphPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const +{ + if( IsExpand() || ( rInf.OnWin() && rInf.GetOpt().IsSoftHyph() ) || + ( GetPortion() && ( GetPortion()->InFixGrp() || + GetPortion()->IsDropPortion() || GetPortion()->IsLayPortion() || + GetPortion()->IsParaPortion() || GetPortion()->IsBreakPortion() ) ) ) + { + return SwHyphPortion::GetExpTxt( rInf, rTxt ); + } + return sal_False; +} + +/************************************************************************* + * virtual SwSoftHyphPortion::HandlePortion() + *************************************************************************/ + +void SwSoftHyphPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + const String aString( '-' ); + const USHORT nWhich = ! Width() ? + POR_SOFTHYPH_COMP : + GetWhichPor(); + rPH.Special( GetLen(), aString, nWhich ); +} + +/************************************************************************* + * SwSoftHyphStrPortion::Paint + *************************************************************************/ + +void SwSoftHyphStrPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ + // Bug oder feature?: + // {Zu}{k-}{ker}, {k-} wird grau statt {-} + rInf.DrawViewOpt( *this, POR_SOFTHYPH ); + SwHyphStrPortion::Paint( rInf ); +} + +SwSoftHyphStrPortion::SwSoftHyphStrPortion( const XubString &rStr ) + : SwHyphStrPortion( rStr ) +{ + SetLen( 1 ); + SetWhichPor( POR_SOFTHYPHSTR ); +} + + + diff --git a/sw/source/core/text/txtinit.cxx b/sw/source/core/text/txtinit.cxx new file mode 100644 index 000000000000..5bfad43be0b9 --- /dev/null +++ b/sw/source/core/text/txtinit.cxx @@ -0,0 +1,104 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtinit.cxx,v $ + * $Revision: 1.11 $ + * + * 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 "errhdl.hxx" +#include "txtcfg.hxx" +#include "swcache.hxx" +#include "fntcache.hxx" // pFntCache ( SwFont/ScrFont-PrtFont Cache ) +#include "swfntcch.hxx" // pSwFontCache ( SwAttrSet/SwFont Cache ) +#include "txtfrm.hxx" +#include "txtcache.hxx" +#include "porlay.hxx" +#include "porglue.hxx" +#include "porexp.hxx" +#include "porrst.hxx" +#include "portab.hxx" +#include "porfly.hxx" +#include "portox.hxx" +#include "porref.hxx" +#include "porftn.hxx" +#include "porhyph.hxx" +#include "pordrop.hxx" +#include "blink.hxx" // Blink-Manager +#include "init.hxx" // Deklarationen fuer _TextInit() und _TextFinit() +#include "txtfly.hxx" // SwContourCache +#include "dbg_lay.hxx" // Layout Debug Fileausgabe + +SwCache *SwTxtFrm::pTxtCache = 0; +long SwTxtFrm::nMinPrtLine = 0; +SwContourCache *pContourCache = 0; +SwDropCapCache *pDropCapCache = 0; + +IMPL_FIXEDMEMPOOL_NEWDEL( SwTxtLine, 50, 50 ) +IMPL_FIXEDMEMPOOL_NEWDEL( SwParaPortion, 50, 50 ) //Absaetze +IMPL_FIXEDMEMPOOL_NEWDEL( SwLineLayout, 150, 150 ) //Zeilen +IMPL_FIXEDMEMPOOL_NEWDEL( SwHolePortion, 150, 150 ) //z.B. Blanks am Zeilenende +IMPL_FIXEDMEMPOOL_NEWDEL( SwTxtPortion, 200, 100 ) //Attributwechsel + +/************************************************************************* + * _TextInit(), _TextFinit() + *************************************************************************/ + +// Werden _nur_ in init.cxx verwendet, dort stehen extern void _TextFinit() +// und extern void _TextInit(...) + +void _TextInit() +{ + pFntCache = new SwFntCache; // Cache for SwSubFont -> SwFntObj = { Font aFont, Font* pScrFont, Font* pPrtFont, OutputDevice* pPrinter, ... } + pSwFontCache = new SwFontCache; // Cache for SwTxtFmtColl -> SwFontObj = { SwFont aSwFont, SfxPoolItem* pDefaultArray } + SwCache *pTxtCache = new SwCache( 250, 100 // Cache for SwTxtFrm -> SwTxtLine = { SwParaPortion* pLine } +#ifndef PRODUCT + , "static SwTxtFrm::pTxtCache" +#endif + ); + SwTxtFrm::SetTxtCache( pTxtCache ); + pWaveCol = new Color( COL_GRAY ); + PROTOCOL_INIT +} + +void _TextFinit() +{ + PROTOCOL_STOP + delete SwTxtFrm::GetTxtCache(); + delete pSwFontCache; + delete pFntCache; + delete pBlink; + delete pWaveCol; + delete pContourCache; + SwDropPortion::DeleteDropCapCache(); +} + + + diff --git a/sw/source/core/text/txtio.cxx b/sw/source/core/text/txtio.cxx new file mode 100644 index 000000000000..854d252afed8 --- /dev/null +++ b/sw/source/core/text/txtio.cxx @@ -0,0 +1,952 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtio.cxx,v $ + * $Revision: 1.17.208.1 $ + * + * 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" + + +#ifdef PRODUCT +#error Wer fummelt denn an den makefiles? +#endif + +#include "viewsh.hxx" // IsDbg() +#include "viewopt.hxx" // IsDbg() +#include "txtatr.hxx" +#include "errhdl.hxx" +#include "txtcfg.hxx" +#include "txtfrm.hxx" // IsDbg() +#include "flyfrms.hxx" +#include "inftxt.hxx" +#include "porexp.hxx" +#include "porfld.hxx" +#include "porfly.hxx" +#include "porftn.hxx" +#include "porglue.hxx" +#include "porhyph.hxx" +#include "porlay.hxx" +#include "porlin.hxx" +#include "porref.hxx" +#include "porrst.hxx" +#include "portab.hxx" +#include "portox.hxx" +#include "portxt.hxx" +#include "pordrop.hxx" +#include "pormulti.hxx" +#include "ndhints.hxx" + +// So kann man die Layoutstruktur ausgeben lassen +// #define AMA_LAYOUT +#ifdef AMA_LAYOUT +#include <stdio.h> +#include <stdlib.h> // getenv() +#include <flowfrm.hxx> +#include <pagefrm.hxx> +#include <svx/svdobj.hxx> +#include <dflyobj.hxx> + + +void lcl_OutFollow( XubString &rTmp, const SwFrm* pFrm ) +{ + if( pFrm->IsFlowFrm() ) + { + const SwFlowFrm *pFlow = SwFlowFrm::CastFlowFrm( pFrm ); + if( pFlow->IsFollow() || pFlow->GetFollow() ) + { + rTmp += "("; + if( pFlow->IsFollow() ) + rTmp += "."; + if( pFlow->GetFollow() ) + { + MSHORT nFrmId = pFlow->GetFollow()->GetFrm()->GetFrmId(); + rTmp += nFrmId; + } + rTmp += ")"; + } + } +} + +void lcl_OutFrame( SvFileStream& rStr, const SwFrm* pFrm, ByteString& rSp, sal_Bool bNxt ) +{ + if( !pFrm ) + return; + KSHORT nSpc = 0; + MSHORT nFrmId = pFrm->GetFrmId(); + ByteString aTmp; + if( pFrm->IsLayoutFrm() ) + { + if( pFrm->IsRootFrm() ) + aTmp = "R"; + else if( pFrm->IsPageFrm() ) + aTmp = "P"; + else if( pFrm->IsBodyFrm() ) + aTmp = "B"; + else if( pFrm->IsColumnFrm() ) + aTmp = "C"; + else if( pFrm->IsTabFrm() ) + aTmp = "Tb"; + else if( pFrm->IsRowFrm() ) + aTmp = "Rw"; + else if( pFrm->IsCellFrm() ) + aTmp = "Ce"; + else if( pFrm->IsSctFrm() ) + aTmp = "S"; + else if( pFrm->IsFlyFrm() ) + { + aTmp = "F"; + const SwFlyFrm *pFly = (SwFlyFrm*)pFrm; + if( pFly->IsFlyInCntFrm() ) + aTmp += "in"; + else if( pFly->IsFlyAtCntFrm() ) + { + aTmp += "a"; + if( pFly->IsAutoPos() ) + aTmp += "u"; + else + aTmp += "t"; + } + else + aTmp += "l"; + } + else if( pFrm->IsHeaderFrm() ) + aTmp = "H"; + else if( pFrm->IsFooterFrm() ) + aTmp = "Fz"; + else if( pFrm->IsFtnContFrm() ) + aTmp = "Fc"; + else if( pFrm->IsFtnFrm() ) + aTmp = "Fn"; + else + aTmp = "?L?"; + aTmp += nFrmId; + lcl_OutFollow( aTmp, pFrm ); + aTmp += " "; + rStr << aTmp; + nSpc = aTmp.Len(); + rSp.Expand( nSpc + rSp.Len() ); + lcl_OutFrame( rStr, ((SwLayoutFrm*)pFrm)->Lower(), rSp, sal_True ); + } + else + { + if( pFrm->IsTxtFrm() ) + aTmp = "T"; + else if( pFrm->IsNoTxtFrm() ) + aTmp = "N"; + else + aTmp = "?C?"; + aTmp += nFrmId; + lcl_OutFollow( aTmp, pFrm ); + aTmp += " "; + rStr << aTmp; + nSpc = aTmp.Len(); + rSp.Expand( nSpc + rSp.Len() ); + } + if( pFrm->IsPageFrm() ) + { + const SwPageFrm* pPg = (SwPageFrm*)pFrm; + const SwSortedObjs *pSorted = pPg->GetSortedObjs(); + const MSHORT nCnt = pSorted ? pSorted->Count() : 0; + if( nCnt ) + { + for( MSHORT i=0; i < nCnt; ++i ) + { + // --> OD 2004-07-07 #i28701# - consider changed type of + // <SwSortedObjs> entries + SwAnchoredObject* pAnchoredObj = (*pSorted)[ i ]; + if( pAnchoredObj->ISA(SwFlyFrm) ) + { + SwFlyFrm* pFly = static_cast<SwFlyFrm*>(pAnchoredObj); + lcl_OutFrame( rStr, pFly, rSp, sal_False ); + } + else + { + aTmp = pAnchoredObj->GetDrawObj()->IsUnoObj() ? "UNO" : "Drw"; + rStr << aTmp; + } + // <-- + if( i < nCnt - 1 ) + rStr << endl << rSp; + } + } + } + else if( pFrm->GetDrawObjs() ) + { + MSHORT nCnt = pFrm->GetDrawObjs()->Count(); + if( nCnt ) + { + for( MSHORT i=0; i < nCnt; ++i ) + { + // --> OD 2004-07-07 #i28701# - consider changed type of + // <SwSortedObjs> entries + SwAnchoredObject* pAnchoredObj = (*pFrm->GetDrawObjs())[ i ]; + if( pAnchoredObj->ISA(SwFlyFrm) ) + { + SwFlyFrm* pFly = static_cast<SwFlyFrm*>(pAnchoredObj); + lcl_OutFrame( rStr, pFly, rSp, sal_False ); + } + else + { + aTmp = pAnchoredObj->GetDrawObj()->IsUnoObj() ? "UNO" : "Drw"; + rStr << aTmp; + } + if( i < nCnt - 1 ) + rStr << endl << rSp; + } + } + } + if( nSpc ) + rSp.Erase( rSp.Len() - nSpc ); + if( bNxt && pFrm->GetNext() ) + { + do + { + pFrm = pFrm->GetNext(); + rStr << endl << rSp; + lcl_OutFrame( rStr, pFrm, rSp, sal_False ); + } while ( pFrm->GetNext() ); + } +} + +void LayOutPut( const SwFrm* pFrm ) +{ + static char* pOutName = 0; + const sal_Bool bFirstOpen = pOutName ? sal_False : sal_True; + if( bFirstOpen ) + { + char *pPath = getenv( "TEMP" ); + char *pName = "layout.txt"; + if( !pPath ) + pOutName = pName; + else + { + const int nLen = strlen(pPath); + // fuer dieses new wird es kein delete geben. + pOutName = new char[nLen + strlen(pName) + 3]; + if(nLen && (pPath[nLen-1] == '\\') || (pPath[nLen-1] == '/')) + snprintf( pOutName, sizeof(pOutName), "%s%s", pPath, pName ); + else + snprintf( pOutName, sizeof(pOutName), "%s/%s", pPath, pName ); + } + } + SvFileStream aStream( pOutName, (bFirstOpen + ? STREAM_WRITE | STREAM_TRUNC + : STREAM_WRITE )); + + if( !aStream.GetError() ) + { + if ( bFirstOpen ) + aStream << "Layout-Struktur"; + else + aStream.Seek( STREAM_SEEK_TO_END ); + aStream << endl; + aStream << "---------------------------------------------" << endl; + XubString aSpace; + lcl_OutFrame( aStream, pFrm, aSpace, sal_False ); + } +} + +#endif + +SvStream &operator<<( SvStream &rOs, const SwpHints & ) //$ ostream +{ + rOs << " {HINTS:"; + +// REMOVED + + rOs << '}'; + return rOs; +} + +/************************************************************************* + * IsDbg() + *************************************************************************/ + +sal_Bool IsDbg( const SwTxtFrm *pFrm ) +{ + if( pFrm && pFrm->GetShell() ) + return pFrm->GetShell()->GetViewOptions()->IsTest4(); + else + return sal_False; +} + +#if OSL_DEBUG_LEVEL < 2 + +static void Error() +{ + // wegen PM und BCC + sal_Bool bFalse = sal_False; + ASSERT( bFalse, "txtio: No debug version" ); +} + +#define IMPL_OUTOP(class) \ + SvStream &class::operator<<( SvStream &rOs ) const /*$ostream*/\ + { \ + Error(); \ + return rOs; \ + } + +IMPL_OUTOP( SwTxtPortion ) +IMPL_OUTOP( SwLinePortion ) +IMPL_OUTOP( SwBreakPortion ) +IMPL_OUTOP( SwGluePortion ) +IMPL_OUTOP( SwFldPortion ) +IMPL_OUTOP( SwHiddenPortion ) +IMPL_OUTOP( SwHyphPortion ) +IMPL_OUTOP( SwFixPortion ) +IMPL_OUTOP( SwFlyPortion ) +IMPL_OUTOP( SwFlyCntPortion ) +IMPL_OUTOP( SwMarginPortion ) +IMPL_OUTOP( SwNumberPortion ) +IMPL_OUTOP( SwBulletPortion ) +IMPL_OUTOP( SwGrfNumPortion ) +IMPL_OUTOP( SwLineLayout ) +IMPL_OUTOP( SwParaPortion ) +IMPL_OUTOP( SwFtnPortion ) +IMPL_OUTOP( SwFtnNumPortion ) +IMPL_OUTOP( SwTmpEndPortion ) +IMPL_OUTOP( SwHyphStrPortion ) +IMPL_OUTOP( SwExpandPortion ) +IMPL_OUTOP( SwBlankPortion ) +IMPL_OUTOP( SwToxPortion ) +IMPL_OUTOP( SwRefPortion ) +IMPL_OUTOP( SwIsoToxPortion ) +IMPL_OUTOP( SwIsoRefPortion ) +IMPL_OUTOP( SwSoftHyphPortion ) +IMPL_OUTOP( SwSoftHyphStrPortion ) +IMPL_OUTOP( SwTabPortion ) +IMPL_OUTOP( SwTabLeftPortion ) +IMPL_OUTOP( SwTabRightPortion ) +IMPL_OUTOP( SwTabCenterPortion ) +IMPL_OUTOP( SwTabDecimalPortion ) +IMPL_OUTOP( SwPostItsPortion ) +IMPL_OUTOP( SwQuoVadisPortion ) +IMPL_OUTOP( SwErgoSumPortion ) +IMPL_OUTOP( SwHolePortion ) +IMPL_OUTOP( SwDropPortion ) +IMPL_OUTOP( SwKernPortion ) +IMPL_OUTOP( SwArrowPortion ) +IMPL_OUTOP( SwMultiPortion ) +IMPL_OUTOP( SwCombinedPortion ) + +const char *GetPortionName( const MSHORT ) +{ + return 0; +} + +const char *GetPrepName( const PrepareHint ) +{ + return 0; +} + +void SwLineLayout::DebugPortions( SvStream &, const XubString &, //$ ostream + const xub_StrLen ) +{ +} + +const char *GetLangName( const MSHORT ) +{ + return 0; +} + +#else +# include <limits.h> +# include <stdlib.h> +# include "swtypes.hxx" // ZTCCONST +# include "swfont.hxx" // SwDropPortion + +CONSTCHAR( pClose, "} " ); + +/************************************************************************* + * GetPortionName() + *************************************************************************/ + +CONSTCHAR( pPOR_LIN, "LIN" ); +CONSTCHAR( pPOR_TXT, "TXT" ); +CONSTCHAR( pPOR_SHADOW, "SHADOW" ); +CONSTCHAR( pPOR_TAB, "TAB" ); +CONSTCHAR( pPOR_TABLEFT, "TABLEFT" ); +CONSTCHAR( pPOR_TABRIGHT, "TABRIGHT" ); +CONSTCHAR( pPOR_TABCENTER, "TABCENTER" ); +CONSTCHAR( pPOR_TABDECIMAL, "TABDECIMAL" ); +CONSTCHAR( pPOR_EXP, "EXP" ); +CONSTCHAR( pPOR_HYPH, "HYPH" ); +CONSTCHAR( pPOR_HYPHSTR, "HYPHSTR" ); +CONSTCHAR( pPOR_FLD, "FLD" ); +CONSTCHAR( pPOR_FIX, "FIX" ); +CONSTCHAR( pPOR_FLY, "FLY" ); +CONSTCHAR( pPOR_FLYCNT, "FLYCNT" ); +CONSTCHAR( pPOR_MARGIN, "MARGIN" ); +CONSTCHAR( pPOR_GLUE, "GLUE" ); +CONSTCHAR( pPOR_HOLE, "HOLE" ); +CONSTCHAR( pPOR_END, "END" ); +CONSTCHAR( pPOR_BRK, "BRK" ); +CONSTCHAR( pPOR_LAY, "LAY" ); +CONSTCHAR( pPOR_BLANK, "BLANK" ); +CONSTCHAR( pPOR_FTN, "FTN" ); +CONSTCHAR( pPOR_FTNNUM, "FTNNUM" ); +CONSTCHAR( pPOR_POSTITS, "POSTITS" ); +CONSTCHAR( pPOR_SOFTHYPH, "SOFTHYPH" ); +CONSTCHAR( pPOR_SOFTHYPHSTR, "SOFTHYPHSTR" ); +CONSTCHAR( pPOR_TOX, "TOX" ); +CONSTCHAR( pPOR_REF, "REF" ); + +CONSTCHAR( pPOR_ISOTOX, "ISOTOX" ); +CONSTCHAR( pPOR_ISOREF, "ISOREF" ); +CONSTCHAR( pPOR_HIDDEN, "Hidden" ); +CONSTCHAR( pPOR_QUOVADIS, "QuoVadis" ); +CONSTCHAR( pPOR_ERGOSUM, "ErgoSum" ); +CONSTCHAR( pPOR_NUMBER, "NUMBER" ); +CONSTCHAR( pPOR_BULLET, "BULLET" ); +CONSTCHAR( pPOR_UNKW, "UNKW" ); +CONSTCHAR( pPOR_PAR, "PAR" ); + +const char *GetPortionName( const MSHORT /*nType*/ ) +{ + return 0; +} + +CONSTCHAR( pPREP_CLEAR, "CLEAR" ); +CONSTCHAR( pPREP_WIDOWS_ORPHANS, "WIDOWS_ORPHANS" ); +CONSTCHAR( pPREP_FIXSIZE_CHG, "FIXSIZE_CHG" ); +CONSTCHAR( pPREP_FOLLOW_FOLLOWS, "FOLLOW_FOLLOWS" ); +CONSTCHAR( pPREP_ADJUST_FRM, "ADJUST_FRM" ); +CONSTCHAR( pPREP_FREE_SPACE, "FREE_SPACE" ); +CONSTCHAR( pPREP_FLY_CHGD, "FLY_CHGD" ); +CONSTCHAR( pPREP_FLY_ATTR_CHG, "FLY_ATTR_CHG" ); +CONSTCHAR( pPREP_FLY_ARRIVE, "FLY_ARRIVE" ); +CONSTCHAR( pPREP_FLY_LEAVE, "FLY_LEAVE" ); +CONSTCHAR( pPREP_VIEWOPT, "VIEWOPT" ); +CONSTCHAR( pPREP_FTN, "FTN" ); +CONSTCHAR( pPREP_POS_CHGD, "POS" ); +CONSTCHAR( pPREP_UL_SPACE, "UL_SPACE" ); +CONSTCHAR( pPREP_MUST_FIT, "MUST_FIT" ); +CONSTCHAR( pPREP_WIDOWS, "ORPHANS" ); +CONSTCHAR( pPREP_QUOVADIS, "QUOVADIS" ); +CONSTCHAR( pPREP_PAGE, "PAGE" ); + +const char *GetPrepName( const PrepareHint ePrep ) +{ + // Kurz und schmerzlos: + const char *ppNameArr[PREP_END] = + { + pPREP_CLEAR, pPREP_WIDOWS_ORPHANS, pPREP_FIXSIZE_CHG, + pPREP_FOLLOW_FOLLOWS, pPREP_ADJUST_FRM, pPREP_FREE_SPACE, + pPREP_FLY_CHGD, pPREP_FLY_ATTR_CHG, pPREP_FLY_ARRIVE, + pPREP_FLY_LEAVE, pPREP_VIEWOPT, pPREP_FTN, pPREP_POS_CHGD, + pPREP_UL_SPACE, pPREP_MUST_FIT, pPREP_WIDOWS, pPREP_QUOVADIS, + pPREP_PAGE + }; + ASSERT( ePrep < PREP_END, "GetPrepName: unknown PrepareHint" ); + return( ppNameArr[ePrep] ); +} + +/************************************************************************* + * SwLineLayout::DebugPortions() + * + * DebugPortion() iteriert ueber alle Portions einer Zeile und deckt die + * internen Strukturen auf. + * Im Gegensatz zum Ausgabe-Operator werden auch die Textteile ausgegeben. + *************************************************************************/ + +void SwLineLayout::DebugPortions( SvStream &rOs, const XubString &/*rTxt*/, //$ ostream + const xub_StrLen /*nStart*/ ) +{ + SwLinePortion *pPortion2 = GetPortion(); + + xub_StrLen nPos = 0; + MSHORT nNr = 0; + KSHORT nPrtWidth, nLastPrt; + nPrtWidth = nLastPrt = 0; + + SwLinePortion::operator<<( rOs ); + rOs << '\"' << endl; + + while( pPortion2 ) + { + DBG_LOOP; + SwTxtPortion *pTxtPor = pPortion2->InTxtGrp() ? + (SwTxtPortion *)pPortion2 : NULL ; + (void)pTxtPor; + ++nNr; + nLastPrt = nPrtWidth; + nPrtWidth = nPrtWidth + pPortion2->PrtWidth(); + rOs << "\tNr:" << nNr + << " Pos:" << nPos + << " Org:" << nLastPrt + << endl; + + rOs << "\t"; + pPortion2->operator<<( rOs ); + rOs << endl; + nPos = nPos + pPortion2->GetLen(); + pPortion2 = pPortion2->GetPortion(); + } +} + +const char *GetLangName( const MSHORT /*nLang*/ ) +{ + return "???"; +} + +SvStream &SwLinePortion::operator<<( SvStream &rOs ) const //$ ostream +{ + rOs << " {"; + rOs << "L:" << nLineLength; + rOs << " H:" << Height(); + rOs << " W:" << PrtWidth(); + rOs << " A:" << nAscent; + rOs << pClose; + return rOs; +} + +SvStream &SwTxtPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TXT:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwTmpEndPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {END:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + if( PrtWidth() ) + rOs << "(view)"; + rOs << pClose; + return rOs; +} + +SvStream &SwBreakPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {BREAK:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwKernPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {KERN:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwArrowPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {ARROW:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwMultiPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {MULTI:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwCombinedPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {COMBINED:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwLineLayout::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {LINE:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + SwLinePortion *pPos = GetPortion(); + while( pPos ) + { + DBG_LOOP; + rOs << "\t"; + pPos->operator<<( rOs ); + pPos = pPos->GetPortion(); + } + rOs << pClose; + return rOs; +} + +SvStream &SwGluePortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {GLUE:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << " F:" << GetFixWidth(); + rOs << " G:" << GetPrtGlue(); + rOs << pClose; + return rOs; +} + +SvStream &SwFixPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {FIX:" ); + rOs << pTxt; + SwGluePortion::operator<<( rOs ); + rOs << " Fix:" << nFix; + rOs << pClose; + return rOs; +} + +SvStream &SwFlyPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {FLY:" ); + rOs << pTxt; + SwFixPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwMarginPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {MAR:" ); + rOs << pTxt; + SwGluePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwFlyCntPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {FLYCNT:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + if( bDraw ) + { + CONSTCHAR( pTxt2, " {DRAWINCNT" ); + rOs << pTxt2; + rOs << pClose; + } + else + { + CONSTCHAR( pTxt2, " {FRM:" ); + rOs << pTxt2; + rOs << " {FRM:" << GetFlyFrm()->Frm() << pClose; + rOs << " {PRT:" << GetFlyFrm()->Prt() << pClose; + rOs << pClose; + } + rOs << pClose; + return rOs; +} + +SvStream &SwExpandPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {EXP:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwFtnPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {FTN:" ); + rOs << pTxt; + SwExpandPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwFtnNumPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {FTNNUM:" ); + rOs << pTxt; + SwNumberPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwNumberPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {NUMBER:" ); + rOs << pTxt; + SwExpandPortion::operator<<( rOs ); + rOs << " Exp:\"" << '\"'; + rOs << pClose; + return rOs; +} + +SvStream &SwBulletPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {BULLET:" ); + rOs << pTxt; + SwNumberPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwGrfNumPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {GRFNUM:" ); + rOs << pTxt; + SwNumberPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwHiddenPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {Hidden:" ); + rOs << pTxt; + SwFldPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwToxPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TOX:" ); + rOs << pTxt; + SwTxtPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwRefPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {Ref:" ); + rOs << pTxt; + SwTxtPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwIsoToxPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {ISOTOX:" ); + rOs << pTxt; + SwToxPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwIsoRefPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {ISOREF:" ); + rOs << pTxt; + SwRefPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwHyphPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {HYPH:" ); + rOs << pTxt; + SwExpandPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwHyphStrPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {HYPHSTR:" ); + rOs << pTxt; + SwExpandPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwSoftHyphPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {SOFTHYPH:" ); + rOs << pTxt; + SwHyphPortion::operator<<( rOs ); + rOs << (IsExpand() ? " on" : " off"); + rOs << pClose; + return rOs; +} + +SvStream &SwSoftHyphStrPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {SOFTHYPHSTR:" ); + rOs << pTxt; + SwHyphStrPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwBlankPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {BLANK:" ); + rOs << pTxt; + SwExpandPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwFldPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {FLD:" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + if( IsFollow() ) + rOs << " F!"; + rOs << pClose; + return rOs; +} + +SvStream &SwPostItsPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {POSTITS" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwTabPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TAB" ); + rOs << pTxt; + SwFixPortion::operator<<( rOs ); + rOs << " T:" << nTabPos; + if( IsFilled() ) + rOs << " \"" << cFill << '\"'; + rOs << pClose; + return rOs; +} + +SvStream &SwTabLeftPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TABLEFT" ); + rOs << pTxt; + SwTabPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwTabRightPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TABRIGHT" ); + rOs << pTxt; + SwTabPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwTabCenterPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TABCENTER" ); + rOs << pTxt; + SwTabPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwTabDecimalPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {TABDECIMAL" ); + rOs << pTxt; + SwTabPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwParaPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {PAR" ); + rOs << pTxt; + SwLineLayout::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwHolePortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {HOLE" ); + rOs << pTxt; + SwLinePortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwQuoVadisPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {QUOVADIS" ); + rOs << pTxt; + SwFldPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &SwErgoSumPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {ERGOSUM" ); + rOs << pTxt; + SwFldPortion::operator<<( rOs ); + rOs << pClose; + return rOs; +} + +SvStream &operator<<( SvStream &rOs, const SwTxtSizeInfo &rInf ) //$ ostream +{ + CONSTCHAR( pTxt, " {SIZEINFO:" ); + rOs << pTxt; + rOs << ' ' << (rInf.OnWin() ? "WIN:" : "PRT:" ); + rOs << " Idx:" << rInf.GetIdx(); + rOs << " Len:" << rInf.GetLen(); + rOs << pClose; + return rOs; +} + +SvStream &SwDropPortion::operator<<( SvStream &rOs ) const //$ ostream +{ + CONSTCHAR( pTxt, " {DROP:" ); + rOs << pTxt; + SwTxtPortion::operator<<( rOs ); + if( pPart && nDropHeight ) + { + rOs << " H:" << nDropHeight; + rOs << " L:" << nLines; + rOs <<" Fnt:" << pPart->GetFont().GetHeight(); + if( nX || nY ) + rOs << " [" << nX << '/' << nY << ']'; + } + rOs << pClose; + return rOs; +} + +#endif /* OSL_DEBUG_LEVEL */ + + diff --git a/sw/source/core/text/txtpaint.cxx b/sw/source/core/text/txtpaint.cxx new file mode 100644 index 000000000000..b756567d8106 --- /dev/null +++ b/sw/source/core/text/txtpaint.cxx @@ -0,0 +1,140 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtpaint.cxx,v $ + * $Revision: 1.11 $ + * + * 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 "txtpaint.hxx" +#include "swrect.hxx" +#include "rootfrm.hxx" + +/************************************************************************* + * SwSaveClip::Reset() + *************************************************************************/ + +void SwSaveClip::Reset() +{ + // Der alte Zustand wird wieder hergestellt. + if( pOut && bChg ) + { + if ( pOut->GetConnectMetaFile() ) + pOut->Pop(); + else + { + if( bOn ) + pOut->SetClipRegion( aClip ); + else + pOut->SetClipRegion(); + } + bChg = sal_False; + } +} + +/************************************************************************* + * SwSaveClip::_ChgClip() + *************************************************************************/ + +void SwSaveClip::_ChgClip( const SwRect &rRect, const SwTxtFrm* pFrm, + sal_Bool bEnlargeRect ) +{ + SwRect aOldRect( rRect ); + const sal_Bool bVertical = pFrm && pFrm->IsVertical(); + + if ( pFrm && pFrm->IsRightToLeft() ) + pFrm->SwitchLTRtoRTL( (SwRect&)rRect ); + + if ( bVertical ) + pFrm->SwitchHorizontalToVertical( (SwRect&)rRect ); + + if ( !pOut || (!rRect.HasArea() && !pOut->IsClipRegion()) ) + { + (SwRect&)rRect = aOldRect; + return; + } + + if ( !bChg ) + { + if ( pOut->GetConnectMetaFile() ) + pOut->Push(); + else if ( bOn ) + aClip = pOut->GetClipRegion(); + } + + if ( !rRect.HasArea() ) + pOut->SetClipRegion(); + else + { + Rectangle aRect( rRect.SVRect() ); + + // Having underscores in our line, we enlarged the repaint area + // (see frmform.cxx) because for some fonts it could be too small. + // Consequently, we have to enlarge the clipping rectangle as well. + if ( bEnlargeRect && ! bVertical ) + aRect.Bottom() += 40; + + // Wenn das ClipRect identisch ist, passiert nix. + if( pOut->IsClipRegion() ) // kein && wg Mac + { + if ( aRect == pOut->GetClipRegion().GetBoundRect() ) + { + (SwRect&)rRect = aOldRect; + return; + } + } + + if( SwRootFrm::HasSameRect( rRect ) ) + pOut->SetClipRegion(); + else + { + const Region aClipRegion( aRect ); + pOut->SetClipRegion( aClipRegion ); +#if OSL_DEBUG_LEVEL > 1 + Rectangle aDbgRect = pOut->GetClipRegion().GetBoundRect(); +#endif + } +#if OSL_DEBUG_LEVEL > 1 +#ifndef PRODUCT + static sal_Bool bDbg = sal_False; + if( bDbg ) + { + DbgBackColor aDbg( pOut, bDbg, COL_RED ); + pOut->DrawRect( aRect ); + } +#endif +#endif + } + bChg = sal_True; + + (SwRect&)rRect = aOldRect; +} + + diff --git a/sw/source/core/text/txtpaint.hxx b/sw/source/core/text/txtpaint.hxx new file mode 100644 index 000000000000..da1c14121b4a --- /dev/null +++ b/sw/source/core/text/txtpaint.hxx @@ -0,0 +1,194 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txtpaint.hxx,v $ + * $Revision: 1.9 $ + * + * 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. + * + ************************************************************************/ +#ifndef _TXTPAINT_HXX +#define _TXTPAINT_HXX +#include <vcl/outdev.hxx> + +class SwRect; // SwSaveClip +#include <txtfrm.hxx> + +/************************************************************************* + * class SwSaveClip + *************************************************************************/ + +class SwSaveClip +{ + Region aClip; + const sal_Bool bOn; + sal_Bool bChg; +protected: + OutputDevice* pOut; + void _ChgClip( const SwRect &rRect, const SwTxtFrm* pFrm, + sal_Bool bEnlargeRect ); +public: + inline SwSaveClip( OutputDevice* pOut ); + inline ~SwSaveClip(); + inline void ChgClip( const SwRect &rRect, const SwTxtFrm* pFrm = 0, + sal_Bool bEnlargeRect = sal_False) + { if( pOut ) _ChgClip( rRect, pFrm, bEnlargeRect ); } + void Reset(); + inline sal_Bool IsOn() const { return bOn; } + inline sal_Bool IsChg() const { return bChg; } + inline sal_Bool IsOut() const { return 0 != pOut; } + inline OutputDevice *GetOut() { return pOut; } +}; + +inline SwSaveClip::SwSaveClip( OutputDevice* pOutDev ) : + bOn( pOutDev && pOutDev->IsClipRegion() ), + bChg( sal_False ), + pOut(pOutDev) +{} + +inline SwSaveClip::~SwSaveClip() +{ + Reset(); +} + +#ifndef PRODUCT + +/************************************************************************* + * class SwDbgOut + *************************************************************************/ + +class SwDbgOut +{ +protected: + OutputDevice* pOut; +public: + inline SwDbgOut( OutputDevice* pOutDev, const sal_Bool bOn = sal_True ); +}; + +/************************************************************************* + * class DbgColor + *************************************************************************/ + +class DbgColor +{ + Font *pFnt; + Color aColor; +public: + inline DbgColor( Font *pFont, const sal_Bool bOn = sal_True, + const ColorData eColor = COL_BLUE ); + inline ~DbgColor(); +}; + +/************************************************************************* + * class DbgBrush + *************************************************************************/ + +class DbgBackColor : public SwDbgOut +{ + Color aOldFillColor; +public: + DbgBackColor( OutputDevice* pOut, const sal_Bool bOn = sal_True, + ColorData nColor = COL_YELLOW ); + ~DbgBackColor(); +}; + +/************************************************************************* + * class DbgRect + *************************************************************************/ + +class DbgRect : public SwDbgOut +{ +public: + DbgRect( OutputDevice* pOut, const Rectangle &rRect, + const sal_Bool bOn = sal_True, + ColorData eColor = COL_LIGHTBLUE ); +}; + +/************************************************************************* + * Inline-Implementierung + *************************************************************************/ + +inline SwDbgOut::SwDbgOut( OutputDevice* pOutDev, const sal_Bool bOn ) + :pOut( bOn ? pOutDev : 0 ) +{ } + + +inline DbgColor::DbgColor( Font *pFont, const sal_Bool bOn, + const ColorData eColor ) + :pFnt( bOn ? pFont : 0 ) +{ + if( pFnt ) + { + aColor = pFnt->GetColor(); + pFnt->SetColor( Color( eColor ) ); + } +} + +inline DbgColor::~DbgColor() +{ + if( pFnt ) + pFnt->SetColor( aColor ); +} + +inline DbgBackColor::DbgBackColor( OutputDevice* pOutDev, const sal_Bool bOn, + ColorData eColor ) + :SwDbgOut( pOutDev, bOn ) +{ + if( pOut ) + { + aOldFillColor = pOut->GetFillColor(); + pOut->SetFillColor( Color(eColor) ); + } +} + +inline DbgBackColor::~DbgBackColor() +{ + if( pOut ) + { + pOut->SetFillColor( aOldFillColor ); + } +} + +inline DbgRect::DbgRect( OutputDevice* pOutDev, const Rectangle &rRect, + const sal_Bool bOn, + ColorData eColor ) + : SwDbgOut( pOutDev, bOn ) +{ + if( pOut ) + { + const Color aColor( eColor ); + Color aLineColor = pOut->GetLineColor(); + pOut->SetLineColor( aColor ); + Color aFillColor = pOut->GetFillColor(); + pOut->SetFillColor( Color(COL_TRANSPARENT) ); + pOut->DrawRect( rRect ); + pOut->SetLineColor( aLineColor ); + pOut->SetFillColor( aFillColor ); + } +} + +#endif + + + +#endif diff --git a/sw/source/core/text/txttab.cxx b/sw/source/core/text/txttab.cxx new file mode 100644 index 000000000000..64bb8be5fc12 --- /dev/null +++ b/sw/source/core/text/txttab.cxx @@ -0,0 +1,666 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: txttab.cxx,v $ + * $Revision: 1.33 $ + * + * 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 <svx/lrspitem.hxx> +#ifndef _SVX_TSTPITEM_HXX //autogen +#include <svx/tstpitem.hxx> +#endif +#include <IDocumentSettingAccess.hxx> +#include <frmatr.hxx> +#include <SwPortionHandler.hxx> + +#include "viewopt.hxx" // SwViewOptions +#include "txtcfg.hxx" +#include "portab.hxx" +#include "inftxt.hxx" +#include "itrform2.hxx" +#include "txtfrm.hxx" +#include <numrule.hxx> +// --> OD 2008-06-05 #i89179# +#include <porfld.hxx> +// <-- + + +/************************************************************************* + * SwLineInfo::GetTabStop() + *************************************************************************/ + +//#i24363# tab stops relative to indent +/* Return the first tab stop that is > nSearchPos. + * If the tab stop is outside the print area, we + * return 0 if it is not the first tab stop.*/ +const SvxTabStop *SwLineInfo::GetTabStop( const SwTwips nSearchPos, + const SwTwips nRight ) const +{ + for( MSHORT i = 0; i < pRuler->Count(); ++i ) + { + const SvxTabStop &rTabStop = pRuler->operator[](i); + if( rTabStop.GetTabPos() > SwTwips(nRight) ) + return i ? 0 : &rTabStop; + + if( rTabStop.GetTabPos() > nSearchPos ) + return &rTabStop; + } + return 0; +} + +/************************************************************************* + * SwLineInfo::NumberOfTabStops() + *************************************************************************/ + +USHORT SwLineInfo::NumberOfTabStops() const +{ + return pRuler->Count(); +} + +/************************************************************************* + * SwTxtFormatter::NewTabPortion() + *************************************************************************/ + +SwTabPortion *SwTxtFormatter::NewTabPortion( SwTxtFormatInfo &rInf, bool bAuto ) const +{ + SwTabPortion *pTabPor = 0; + SwTabPortion *pLastTab = rInf.GetLastTab(); + if( pLastTab && ( pLastTab->IsTabCntPortion() || pLastTab->IsTabDecimalPortion() ) ) + if( pLastTab->PostFormat( rInf ) ) + return 0; + + xub_Unicode cFill = 0; + xub_Unicode cDec = 0; + SvxTabAdjust eAdj; + + KSHORT nNewTabPos; + { + const bool bRTL = pFrm->IsRightToLeft(); + // #i24363# tab stops relative to indent + // nTabLeft: The absolute value, the tab stops are relative to: Tabs origin. + // + // --> OD 2008-07-01 #i91133# + const bool bTabsRelativeToIndent = + pFrm->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TABS_RELATIVE_TO_INDENT); + const SwTwips nTabLeft = bRTL + ? pFrm->Frm().Right() - + ( bTabsRelativeToIndent ? GetTabLeft() : 0 ) + : pFrm->Frm().Left() + + ( bTabsRelativeToIndent ? GetTabLeft() : 0 ); + // <-- + + // + // nLinePos: The absolute position, where we started the line formatting. + // + SwTwips nLinePos = GetLeftMargin(); + if ( bRTL ) + { + Point aPoint( nLinePos, 0 ); + pFrm->SwitchLTRtoRTL( aPoint ); + nLinePos = aPoint.X(); + } + + // + // nTabPos: The current position, relative to the line start. + // + SwTwips nTabPos = rInf.GetLastTab() ? rInf.GetLastTab()->GetTabPos() : 0; + if( nTabPos < rInf.X() ) + { + nTabPos = rInf.X(); + } + + // + // nCurrentAbsPos: The current position in absolute coordinates. + // + const SwTwips nCurrentAbsPos = bRTL ? + nLinePos - nTabPos : + nLinePos + nTabPos; + + SwTwips nMyRight = Right(); + + if ( pFrm->IsVertical() ) + { + Point aRightTop( nMyRight, pFrm->Frm().Top() ); + pFrm->SwitchHorizontalToVertical( aRightTop ); + nMyRight = aRightTop.Y(); + } + + SwTwips nNextPos; + + // #i24363# tab stops relative to indent + // nSearchPos: The current position relative to the tabs origin. + // + const SwTwips nSearchPos = bRTL ? + nTabLeft - nCurrentAbsPos : + nCurrentAbsPos - nTabLeft; + + // + // First, we examine the tab stops set at the paragraph style or + // any hard set tab stops: + // Note: If there are no user defined tab stops, there is always a + // default tab stop. + // + const SvxTabStop* pTabStop = + aLineInf.GetTabStop( nSearchPos, nMyRight ); + if( pTabStop ) + { + cFill = ' ' != pTabStop->GetFill() ? pTabStop->GetFill() : 0; + cDec = pTabStop->GetDecimal(); + eAdj = pTabStop->GetAdjustment(); + nNextPos = pTabStop->GetTabPos(); + } + else + { + KSHORT nDefTabDist = aLineInf.GetDefTabStop(); + if( KSHRT_MAX == nDefTabDist ) + { + const SvxTabStopItem& rTab = + (const SvxTabStopItem &)pFrm->GetAttrSet()-> + GetPool()->GetDefaultItem( RES_PARATR_TABSTOP ); + if( rTab.Count() ) + nDefTabDist = (KSHORT)rTab.GetStart()->GetTabPos(); + else + nDefTabDist = SVX_TAB_DEFDIST; + aLineInf.SetDefTabStop( nDefTabDist ); + } + SwTwips nCount = nSearchPos; + + // Bei negativen Werten rundet "/" auf, "%" liefert negative Reste, + // bei positiven Werten rundet "/" ab, "%" liefert positvie Reste! + if ( nCount < 0 ) + nCount = 0; + + nCount /= nDefTabDist; + nNextPos = ( nCount + 1 ) * nDefTabDist ; + // --> FME 2004-09-21 #117919 Minimum tab stop width is 1 or 51 twips: + const SwTwips nMinimumTabWidth = pFrm->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) ? 0 : 50; + // <-- + if( ( bRTL && nTabLeft - nNextPos >= nCurrentAbsPos - nMinimumTabWidth ) || + ( !bRTL && nNextPos + nTabLeft <= nCurrentAbsPos + nMinimumTabWidth ) ) + { + nNextPos += nDefTabDist; + } + cFill = 0; + eAdj = SVX_TAB_ADJUST_LEFT; + } + // --> OD 2008-02-07 #newlistlevelattrs# + long nForced = 0; + if ( !bTabsRelativeToIndent ) + { + if ( bRTL ) + { + Point aPoint( Left(), 0 ); + pFrm->SwitchLTRtoRTL( aPoint ); + nForced = pFrm->Frm().Right() - aPoint.X(); + } + else + { + nForced = Left() - pFrm->Frm().Left(); + } + } + if( pCurr->HasForcedLeftMargin() ) + { + SwLinePortion* pPor = pCurr->GetPortion(); + while( pPor && !pPor->IsFlyPortion() ) + pPor = pPor->GetPortion(); + if( pPor ) + nForced += pPor->Width(); + } + + // <-- + // --> OD 2009-04-03 #i100732# + // correction of condition, when a tab stop at the left margin can + // be applied: + // If the paragraph is not inside a list having a list tab stop following + // the list label or no further tab stop found in such a paragraph or + // the next tab stop position does not equal the list tab stop, + // a tab stop at the left margin can be applied. If this condition is + // not hold, it is overruled by compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST. + const bool bTabAtLeftMargin = + ( !aLineInf.IsListTabStopIncluded() || + !pTabStop || + nNextPos != aLineInf.GetListTabStopPosition() ) || + // compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST: + pFrm->GetTxtNode()->getIDocumentSettingAccess()-> + get(IDocumentSettingAccess::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST); + if ( bTabAtLeftMargin && + // <-- + ( ( bRTL && nCurrentAbsPos > nTabLeft - nForced ) || + ( !bRTL && nCurrentAbsPos < nTabLeft + nForced ) ) && + // --> OD 2009-07-21 #i103685# + // adjust condition: + // - back to pre OOo 3.0 condition, if tab stops are relative to indent + // - further checks needed, if tab stops are not relative to indent + ( nNextPos > 0 && + ( bTabsRelativeToIndent || + ( !pTabStop || nNextPos > nForced ) ) ) ) + // <-- + { + eAdj = SVX_TAB_ADJUST_DEFAULT; + cFill = 0; + nNextPos = nForced; + } + nNextPos += bRTL ? nLinePos - nTabLeft : nTabLeft - nLinePos; + ASSERT( nNextPos >= 0, "GetTabStop: Don't go back!" ); + nNewTabPos = KSHORT(nNextPos); + } + + if ( bAuto ) + { + if ( SVX_TAB_ADJUST_DECIMAL == eAdj && + // --> FME 2005-12-19 #127428# + 1 == aLineInf.NumberOfTabStops() ) + // <-- + pTabPor = new SwAutoTabDecimalPortion( nNewTabPos, cDec, cFill ); + } + else + { + switch( eAdj ) + { + case SVX_TAB_ADJUST_RIGHT : + { + pTabPor = new SwTabRightPortion( nNewTabPos, cFill ); + break; + } + case SVX_TAB_ADJUST_CENTER : + { + pTabPor = new SwTabCenterPortion( nNewTabPos, cFill ); + break; + } + case SVX_TAB_ADJUST_DECIMAL : + { + pTabPor = new SwTabDecimalPortion( nNewTabPos, cDec, cFill ); + break; + } + default: + { + ASSERT( SVX_TAB_ADJUST_LEFT == eAdj || SVX_TAB_ADJUST_DEFAULT == eAdj, + "+SwTxtFormatter::NewTabPortion: unknown adjustment" ); + pTabPor = new SwTabLeftPortion( nNewTabPos, cFill ); + break; + } + } + } + + // Vorhandensein von Tabulatoren anzeigen ... ist nicht mehr noetig + // pCurr->SetTabulation(); + // Aus Sicherheitsgruenden lassen wir uns die Daten errechnen + // pTabPor->Height( pLast->Height() ); + // pTabPor->SetAscent( pLast->GetAscent() ); + return pTabPor; +} + +/************************************************************************* + * SwTabPortion::SwTabPortion() + *************************************************************************/ + +// Die Basisklasse wird erstmal ohne alles initialisiert. + + +SwTabPortion::SwTabPortion( const KSHORT nTabPosition, const xub_Unicode cFillChar ) + : SwFixPortion( 0, 0 ), nTabPos(nTabPosition), cFill(cFillChar) +{ + nLineLength = 1; +#ifndef PRODUCT + if( IsFilled() ) + { + ASSERT( ' ' != cFill, "SwTabPortion::CTOR: blanks ?!" ); + } +#endif + SetWhichPor( POR_TAB ); +} + +/************************************************************************* + * virtual SwTabPortion::Format() + *************************************************************************/ + + + +sal_Bool SwTabPortion::Format( SwTxtFormatInfo &rInf ) +{ + SwTabPortion *pLastTab = rInf.GetLastTab(); + if( pLastTab == this ) + return PostFormat( rInf ); + if( pLastTab ) + pLastTab->PostFormat( rInf ); + return PreFormat( rInf ); +} + +/************************************************************************* + * virtual SwTabPortion::FormatEOL() + *************************************************************************/ + + + +void SwTabPortion::FormatEOL( SwTxtFormatInfo &rInf ) +{ + if( rInf.GetLastTab() == this && !IsTabLeftPortion() ) + PostFormat( rInf ); +} + +/************************************************************************* + * SwTabPortion::PreFormat() + *************************************************************************/ + + + +sal_Bool SwTabPortion::PreFormat( SwTxtFormatInfo &rInf ) +{ + ASSERT( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" ); + + // Hier lassen wir uns nieder... + Fix( static_cast<USHORT>(rInf.X()) ); + + const bool bTabCompat = rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT); + + // Die Mindestbreite eines Tabs ist immer mindestens ein Blank + // --> FME 2004-11-25 #i37686# In compatibility mode, the minimum width + // should be 1, even for non-left tab stops. + USHORT nMinimumTabWidth = 1; + // <-- + if ( !bTabCompat ) + { + // --> OD 2008-06-05 #i89179# + // tab portion representing the list tab of a list label gets the + // same font as the corresponding number portion + std::auto_ptr< SwFontSave > pSave( 0 ); + if ( GetLen() == 0 && + rInf.GetLast() && rInf.GetLast()->InNumberGrp() && + static_cast<SwNumberPortion*>(rInf.GetLast())->HasFont() ) + { + const SwFont* pNumberPortionFont = + static_cast<SwNumberPortion*>(rInf.GetLast())->GetFont(); + pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) ); + } + // <-- + XubString aTmp( ' ' ); + SwTxtSizeInfo aInf( rInf, aTmp ); + nMinimumTabWidth = aInf.GetTxtSize().Width(); + } + PrtWidth( nMinimumTabWidth ); + + // Break tab stop to next line if: + // 1. Minmal width does not fit to line anymore. + // 2. An underflow event was called for the tab portion. + sal_Bool bFull = ( bTabCompat && rInf.IsUnderFlow() ) || + rInf.Width() <= rInf.X() + PrtWidth(); + + // #95477# Rotated tab stops get the width of one blank + const USHORT nDir = rInf.GetFont()->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); + + if( ! bFull && 0 == nDir ) + { + const MSHORT nWhich = GetWhichPor(); + switch( nWhich ) + { + case POR_TABRIGHT: + case POR_TABDECIMAL: + case POR_TABCENTER: + { + if( POR_TABDECIMAL == nWhich ) + rInf.SetTabDecimal( + ((SwTabDecimalPortion*)this)->GetTabDecimal()); + rInf.SetLastTab( this ); + break; + } + case POR_TABLEFT: + { + PrtWidth( static_cast<USHORT>(GetTabPos() - rInf.X()) ); + bFull = rInf.Width() <= rInf.X() + PrtWidth(); + + // In tabulator compatibility mode, we reset the bFull flag + // if the tabulator is at the end of the paragraph and the + // tab stop position is outside the frame: + if ( bFull && bTabCompat && + rInf.GetIdx() + GetLen() == rInf.GetTxt().Len() && + GetTabPos() >= rInf.GetTxtFrm()->Frm().Width() ) + bFull = sal_False; + + break; + } + default: ASSERT( !this, "SwTabPortion::PreFormat: unknown adjustment" ); + } + } + + if( bFull ) + { + // Wir muessen aufpassen, dass wir nicht endlos schleifen, + // wenn die Breite kleiner ist, als ein Blank ... + if( rInf.GetIdx() == rInf.GetLineStart() && + // --> FME 2005-01-19 #119175# TabStop should be forced to current + // line if there is a fly reducing the line width: + !rInf.GetFly() ) + // <-- + { + PrtWidth( static_cast<USHORT>(rInf.Width() - rInf.X()) ); + SetFixWidth( PrtWidth() ); + } + else + { + Height( 0 ); + Width( 0 ); + SetLen( 0 ); + SetAscent( 0 ); + SetPortion( NULL ); //????? + } + return sal_True; + } + else + { + // Ein Kunstgriff mit Effekt: Die neuen Tabportions verhalten sich nun + // so, wie FlyFrms, die in der Zeile stehen - inklusive Adjustment ! + SetFixWidth( PrtWidth() ); + return sal_False; + } +} + +/************************************************************************* + * SwTabPortion::PostFormat() + *************************************************************************/ + + + +sal_Bool SwTabPortion::PostFormat( SwTxtFormatInfo &rInf ) +{ + const KSHORT nRight = Min( GetTabPos(), rInf.Width() ); + const SwLinePortion *pPor = GetPortion(); + + KSHORT nPorWidth = 0; + while( pPor ) + { + DBG_LOOP; + nPorWidth = nPorWidth + pPor->Width(); + pPor = pPor->GetPortion(); + } + + const MSHORT nWhich = GetWhichPor(); + ASSERT( POR_TABLEFT != nWhich, "SwTabPortion::PostFormat: already formatted" ); + const bool bTabCompat = rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT); + + // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full: + if ( bTabCompat && POR_TABDECIMAL == nWhich ) + { + KSHORT nPrePorWidth = static_cast<const SwTabDecimalPortion*>(this)->GetWidthOfPortionsUpToDecimalPosition(); + + // no value was set => no decimal character was found + if ( USHRT_MAX != nPrePorWidth ) + { + if ( nPrePorWidth && nPorWidth - nPrePorWidth > rInf.Width() - nRight ) + { + nPrePorWidth += nPorWidth - nPrePorWidth - ( rInf.Width() - nRight ); + } + + nPorWidth = nPrePorWidth - 1; + } + } + // <-- + + if( POR_TABCENTER == nWhich ) + { + // zentrierte Tabs bereiten Probleme: + // Wir muessen den Anteil herausfinden, der noch auf die Zeile passt. + KSHORT nNewWidth = nPorWidth /2; + if( nNewWidth > rInf.Width() - nRight ) + nNewWidth = nPorWidth - (rInf.Width() - nRight); + nPorWidth = nNewWidth; + } + + const KSHORT nDiffWidth = nRight - Fix(); + + if( nDiffWidth > nPorWidth ) + { + const KSHORT nOldWidth = GetFixWidth(); + const KSHORT nAdjDiff = nDiffWidth - nPorWidth; + if( nAdjDiff > GetFixWidth() ) + PrtWidth( nAdjDiff ); + // Nicht erschrecken: wir muessen rInf weiterschieben. + // Immerhin waren wir als Rechtstab bislang nur ein Blank breit. + // Da wir uns jetzt aufgespannt haben, muss der Differenzbetrag + // auf rInf.X() addiert werden ! + rInf.X( rInf.X() + PrtWidth() - nOldWidth ); + } + SetFixWidth( PrtWidth() ); + // letzte Werte zuruecksetzen + rInf.SetLastTab(0); + if( POR_TABDECIMAL == nWhich ) + rInf.SetTabDecimal(0); + + return rInf.Width() <= rInf.X(); +} + +/************************************************************************* + * virtual SwTabPortion::Paint() + * + * Ex: LineIter::DrawTab() + *************************************************************************/ + +void SwTabPortion::Paint( const SwTxtPaintInfo &rInf ) const +{ +#ifndef PRODUCT + // Wir wollen uns die Fixbreite anzeigen + if( rInf.OnWin() && OPTDBG( rInf ) && + !rInf.GetOpt().IsPagePreview() && \ + !rInf.GetOpt().IsReadonly() && \ + SwViewOption::IsFieldShadings() ) + { + const KSHORT nTmpWidth = PrtWidth(); + ((SwTabPortion*)this)->PrtWidth( GetFixWidth() ); + rInf.DrawViewOpt( *this, POR_TAB ); + ((SwTabPortion*)this)->PrtWidth( nTmpWidth ); + } +#endif + + // --> OD 2008-06-05 #i89179# + // tab portion representing the list tab of a list label gets the + // same font as the corresponding number portion + std::auto_ptr< SwFontSave > pSave( 0 ); + if ( GetLen() == 0 ) + { + const SwLinePortion* pPrevPortion = + const_cast<SwTabPortion*>(this)->FindPrevPortion( rInf.GetParaPortion() ); + if ( pPrevPortion && + pPrevPortion->InNumberGrp() && + static_cast<const SwNumberPortion*>(pPrevPortion)->HasFont() ) + { + const SwFont* pNumberPortionFont = + static_cast<const SwNumberPortion*>(pPrevPortion)->GetFont(); + pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) ); + } + } + // <-- + rInf.DrawBackBrush( *this ); + + // do we have to repaint a post it portion? + if( rInf.OnWin() && pPortion && !pPortion->Width() ) + pPortion->PrePaint( rInf, this ); + + // Darstellung von Sonderzeichen + if( rInf.OnWin() && rInf.GetOpt().IsTab() ) + { + // gefuellte Tabs werden grau hinterlegt. + if( IsFilled() ) + rInf.DrawViewOpt( *this, POR_TAB ); + else + rInf.DrawTab( *this ); + } + + // 6842: Tabs sollen auf einmal wieder unterstrichen werden. + if( rInf.GetFont()->IsPaintBlank() ) + { + // Tabs mit Fuellung + XubString aTxt( ' ' ); + const KSHORT nCharWidth = rInf.GetTxtSize( aTxt ).Width(); + // robust: + if( nCharWidth ) + { + // 6864: immer mit Kerning, auch auf dem Drucker! + KSHORT nChar = Width() / nCharWidth; + rInf.DrawText( aTxt.Fill( nChar, ' ' ), *this, 0, nChar, sal_True ); + } + } + + // Ausgabe von Fuellzeichen + if( IsFilled() ) + { + // Tabs mit Fuellung + XubString aTxt( cFill ); + const KSHORT nCharWidth = rInf.GetTxtSize( aTxt ).Width(); +#if OSL_DEBUG_LEVEL > 1 + ASSERT( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" ); +#endif + // robust: + if( nCharWidth ) + { + // 6864: immer mit Kerning, auch auf dem Drucker! + KSHORT nChar = Width() / nCharWidth; + if ( cFill == '_' ) + ++nChar; // damit keine Luecken entstehen (Bug 13430) + rInf.DrawText( aTxt.Fill( nChar, cFill ), *this, 0, nChar, sal_True ); + } + } +} + +/************************************************************************* + * virtual SwAutoTabDecimalPortion::Paint() + *************************************************************************/ + +void SwAutoTabDecimalPortion::Paint( const SwTxtPaintInfo & ) const +{ +} + +/************************************************************************* + * virtual SwTabPortion::HandlePortion() + *************************************************************************/ + +void SwTabPortion::HandlePortion( SwPortionHandler& rPH ) const +{ + rPH.Text( GetLen(), GetWhichPor() ); +} + diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx new file mode 100644 index 000000000000..3b09e80e5d29 --- /dev/null +++ b/sw/source/core/text/widorp.cxx @@ -0,0 +1,569 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: widorp.cxx,v $ + * $Revision: 1.22.214.1 $ + * + * 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 "layfrm.hxx" +#include "ftnboss.hxx" +#include "ndtxt.hxx" +#include "paratr.hxx" +#include <svx/orphitem.hxx> +#include <svx/widwitem.hxx> +#include <svx/keepitem.hxx> +#include <svx/spltitem.hxx> +#include <frmatr.hxx> +#include <txtftn.hxx> +#include <fmtftn.hxx> +#include <rowfrm.hxx> + +#include "txtcfg.hxx" +#include "widorp.hxx" +#include "txtfrm.hxx" +#include "itrtxt.hxx" +#include "sectfrm.hxx" //SwSectionFrm +#include "ftnfrm.hxx" + +#undef WIDOWTWIPS + + +/************************************************************************* + * inline IsNastyFollow() + *************************************************************************/ +// Ein Follow, der auf der selben Seite steht, wie sein Master ist nasty. +inline sal_Bool IsNastyFollow( const SwTxtFrm *pFrm ) +{ + ASSERT( !pFrm->IsFollow() || !pFrm->GetPrev() || + ((const SwTxtFrm*)pFrm->GetPrev())->GetFollow() == pFrm, + "IsNastyFollow: Was ist denn hier los?" ); + return pFrm->IsFollow() && pFrm->GetPrev(); +} + +/************************************************************************* + * SwTxtFrmBreak::SwTxtFrmBreak() + *************************************************************************/ + +SwTxtFrmBreak::SwTxtFrmBreak( SwTxtFrm *pNewFrm, const SwTwips nRst ) + : nRstHeight(nRst), pFrm(pNewFrm) +{ + SWAP_IF_SWAPPED( pFrm ) + SWRECTFN( pFrm ) + nOrigin = (pFrm->*fnRect->fnGetPrtTop)(); + SwSectionFrm* pSct; + bKeep = !pFrm->IsMoveable() || IsNastyFollow( pFrm ) || + ( pFrm->IsInSct() && (pSct=pFrm->FindSctFrm())->Lower()->IsColumnFrm() + && !pSct->MoveAllowed( pFrm ) ) || + !pFrm->GetTxtNode()->GetSwAttrSet().GetSplit().GetValue() || + pFrm->GetTxtNode()->GetSwAttrSet().GetKeep().GetValue(); + bBreak = sal_False; + + if( !nRstHeight && !pFrm->IsFollow() && pFrm->IsInFtn() && pFrm->HasPara() ) + { + nRstHeight = pFrm->GetFtnFrmHeight(); + nRstHeight += (pFrm->Prt().*fnRect->fnGetHeight)() - + (pFrm->Frm().*fnRect->fnGetHeight)(); + if( nRstHeight < 0 ) + nRstHeight = 0; + } + + UNDO_SWAP( pFrm ) +} + +/* BP 18.6.93: Widows. + * Im Gegensatz zur ersten Implementierung werden die Widows nicht + * mehr vorausschauend berechnet, sondern erst beim Formatieren des + * gesplitteten Follows festgestellt. Im Master faellt die Widows- + * Berechnung also generell weg (nWidows wird manipuliert). + * Wenn der Follow feststellt, dass die Widowsregel zutrifft, + * verschickt er an seinen Vorgaenger ein Prepare. + * Ein besonderes Problem ergibt sich, wenn die Widows zuschlagen, + * aber im Master noch ein paar Zeilen zur Verfuegung stehen. + * + */ + +/************************************************************************* + * SwTxtFrmBreak::IsInside() + *************************************************************************/ + +/* BP(22.07.92): Berechnung von Witwen und Waisen. + * Die Methode liefert sal_True zurueck, wenn eine dieser Regelung zutrifft. + * + * Eine Schwierigkeit gibt es im Zusammenhang mit Widows und + * unterschiedlichen Formaten zwischen Master- und Folgeframes: + * Beispiel: Wenn die erste Spalte 3cm und die zweite 4cm breit ist + * und Widows auf sagen wir 3 gesetzt ist, so ist erst bei der Formatierung + * des Follows entscheidbar, ob die Widowsbedingung einhaltbar ist oder + * nicht. Leider ist davon abhaengig, ob der Absatz als Ganzes auf die + * naechste Seite rutscht. + */ + +sal_Bool SwTxtFrmBreak::IsInside( SwTxtMargin &rLine ) const +{ + sal_Bool bFit = sal_False; + + SWAP_IF_SWAPPED( pFrm ) + SWRECTFN( pFrm ) + // nOrigin is an absolut value, rLine referes to the swapped situation. + + SwTwips nTmpY; + if ( pFrm->IsVertical() ) + nTmpY = pFrm->SwitchHorizontalToVertical( rLine.Y() + rLine.GetLineHeight() ); + else + nTmpY = rLine.Y() + rLine.GetLineHeight(); + + SwTwips nLineHeight = (*fnRect->fnYDiff)( nTmpY , nOrigin ); + + // 7455 und 6114: Raum fuer die Umrandung unten einkalkulieren. + nLineHeight += (pFrm->*fnRect->fnGetBottomMargin)(); + + if( nRstHeight ) + bFit = nRstHeight >= nLineHeight; + else + { + // Der Frm besitzt eine Hoehe, mit der er auf die Seite passt. + SwTwips nHeight = + (*fnRect->fnYDiff)( (pFrm->GetUpper()->*fnRect->fnGetPrtBottom)(), nOrigin ); + + // Wenn sich alles innerhalb des bestehenden Frames abspielt, + // ist das Ergebnis sal_True; + bFit = nHeight >= nLineHeight; + if( !bFit ) + { + // Die LineHeight sprengt die aktuelle Frm-Hoehe. + // Nun rufen wir ein Probe-Grow, um zu ermitteln, ob der + // Frame um den gewuenschten Bereich wachsen wuerde. + nHeight += pFrm->GrowTst( LONG_MAX ); + + // Das Grow() returnt die Hoehe, um die der Upper des TxtFrm + // den TxtFrm wachsen lassen wuerde. + // Der TxtFrm selbst darf wachsen wie er will. + bFit = nHeight >= nLineHeight; + } + } + + UNDO_SWAP( pFrm ); + + return bFit; +} + +/************************************************************************* + * SwTxtFrmBreak::IsBreakNow() + *************************************************************************/ + +sal_Bool SwTxtFrmBreak::IsBreakNow( SwTxtMargin &rLine ) +{ + SWAP_IF_SWAPPED( pFrm ) + + // bKeep ist staerker als IsBreakNow() + // Ist noch genug Platz ? + if( bKeep || IsInside( rLine ) ) + bBreak = sal_False; + else + { + /* Diese Klasse geht davon aus, dass der SwTxtMargin von Top nach Bottom + * durchgearbeitet wird. Aus Performancegruenden wird in folgenden + * Faellen der Laden fuer das weitere Aufspalten dicht gemacht: + * Wenn eine einzige Zeile nicht mehr passt. + * Sonderfall: bei DummyPortions ist LineNr == 1, obwohl wir splitten + * wollen. + */ + // 6010: DropLines mit einbeziehen + + sal_Bool bFirstLine = 1 == rLine.GetLineNr() && !rLine.GetPrev(); + bBreak = sal_True; + if( ( bFirstLine && pFrm->GetIndPrev() ) + || ( rLine.GetLineNr() <= rLine.GetDropLines() ) ) + { + bKeep = sal_True; + bBreak = sal_False; + } + else if(bFirstLine && pFrm->IsInFtn() && !pFrm->FindFtnFrm()->GetPrev()) + { + SwLayoutFrm* pTmp = pFrm->FindFtnBossFrm()->FindBodyCont(); + if( !pTmp || !pTmp->Lower() ) + bBreak = sal_False; + } + } + + UNDO_SWAP( pFrm ) + + return bBreak; +} + +// OD 2004-02-27 #106629# - no longer inline +void SwTxtFrmBreak::SetRstHeight( const SwTxtMargin &rLine ) +{ + // OD, FME 2004-02-27 #106629# - consider bottom margin + SWRECTFN( pFrm ) + nRstHeight = (pFrm->*fnRect->fnGetBottomMargin)(); + if ( bVert ) + nRstHeight += nOrigin - pFrm->SwitchHorizontalToVertical( rLine.Y() ); + else + nRstHeight += rLine.Y() - nOrigin; +} + +/************************************************************************* + * WidowsAndOrphans::WidowsAndOrphans() + *************************************************************************/ + +WidowsAndOrphans::WidowsAndOrphans( SwTxtFrm *pNewFrm, const SwTwips nRst, + sal_Bool bChkKeep ) + : SwTxtFrmBreak( pNewFrm, nRst ), nWidLines( 0 ), nOrphLines( 0 ) +{ + SWAP_IF_SWAPPED( pFrm ) + + if( bKeep ) + { + // 5652: bei Absaetzen, die zusammengehalten werden sollen und + // groesser sind als die Seite wird bKeep aufgehoben. + if( bChkKeep && !pFrm->GetPrev() && !pFrm->IsInFtn() && + pFrm->IsMoveable() && + ( !pFrm->IsInSct() || pFrm->FindSctFrm()->MoveAllowed(pFrm) ) ) + bKeep = sal_False; + //Auch bei gesetztem Keep muessen Orphans beachtet werden, + //z.B. bei verketteten Rahmen erhaelt ein Follow im letzten Rahmen ein Keep, + //da er nicht (vorwaerts) Moveable ist, + //er darf aber trotzdem vom Master Zeilen anfordern wg. der Orphanregel. + if( pFrm->IsFollow() ) + nWidLines = pFrm->GetTxtNode()->GetSwAttrSet().GetWidows().GetValue(); + } + else + { + const SwAttrSet& rSet = pFrm->GetTxtNode()->GetSwAttrSet(); + const SvxOrphansItem &rOrph = rSet.GetOrphans(); + if ( rOrph.GetValue() > 1 ) + nOrphLines = rOrph.GetValue(); + if ( pFrm->IsFollow() ) + nWidLines = rSet.GetWidows().GetValue(); + + } + + if ( bKeep || nWidLines || nOrphLines ) + { + bool bResetFlags = false; + + if ( pFrm->IsInTab() ) + { + // For compatibility reasons, we disable Keep/Widows/Orphans + // inside splittable row frames: + if ( pFrm->GetNextCellLeaf( MAKEPAGE_NONE ) || pFrm->IsInFollowFlowRow() ) + { + const SwFrm* pTmpFrm = pFrm->GetUpper(); + while ( !pTmpFrm->IsRowFrm() ) + pTmpFrm = pTmpFrm->GetUpper(); + if ( static_cast<const SwRowFrm*>(pTmpFrm)->IsRowSplitAllowed() ) + bResetFlags = true; + } + } + + if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) + { + // Innerhalb von Fussnoten gibt es gute Gruende, das Keep-Attribut und + // die Widows/Orphans abzuschalten. + SwFtnFrm *pFtn = pFrm->FindFtnFrm(); + sal_Bool bFt = !pFtn->GetAttr()->GetFtn().IsEndNote(); + if( !pFtn->GetPrev() && + pFtn->FindFtnBossFrm( bFt ) != pFtn->GetRef()->FindFtnBossFrm( bFt ) + && ( !pFrm->IsInSct() || pFrm->FindSctFrm()->MoveAllowed(pFrm) ) ) + { + bResetFlags = true; + } + } + + if ( bResetFlags ) + { + bKeep = sal_False; + nOrphLines = 0; + nWidLines = 0; + } + } + + UNDO_SWAP( pFrm ) +} + +/************************************************************************* + * WidowsAndOrphans::FindBreak() + *************************************************************************/ + +/* Die Find*-Methoden suchen nicht nur, sondern stellen den SwTxtMargin auf + * die Zeile ein, wo der Absatz gebrochen werden soll und kuerzen ihn dort. + * FindBreak() + */ + +sal_Bool WidowsAndOrphans::FindBreak( SwTxtFrm *pFrame, SwTxtMargin &rLine, + sal_Bool bHasToFit ) +{ + // OD 2004-02-25 #i16128# - Why member <pFrm> _*and*_ parameter <pFrame>?? + // Thus, assertion on situation, that these are different to figure out why. + ASSERT( pFrm == pFrame, "<WidowsAndOrphans::FindBreak> - pFrm != pFrame" ); + + SWAP_IF_SWAPPED( pFrm ) + + sal_Bool bRet = sal_True; + MSHORT nOldOrphans = nOrphLines; + if( bHasToFit ) + nOrphLines = 0; + rLine.Bottom(); + // OD 2004-02-25 #i16128# - method renamed + if( !IsBreakNowWidAndOrp( rLine ) ) + bRet = sal_False; + if( !FindWidows( pFrame, rLine ) ) + { + sal_Bool bBack = sal_False; + // OD 2004-02-25 #i16128# - method renamed + while( IsBreakNowWidAndOrp( rLine ) ) + { + if( rLine.PrevLine() ) + bBack = sal_True; + else + break; + } + // Eigentlich werden bei HasToFit Schusterjungen (Orphans) nicht + // beruecksichtigt, wenn allerdings Dummy-Lines im Spiel sind und + // die Orphansregel verletzt wird, machen wir mal eine Ausnahme: + // Wir lassen einfach eine Dummyline zurueck und wandern mit dem Text + // komplett auf die naechste Seite/Spalte. + if( rLine.GetLineNr() <= nOldOrphans && + rLine.GetInfo().GetParaPortion()->IsDummy() && + ( ( bHasToFit && bRet ) || IsBreakNow( rLine ) ) ) + rLine.Top(); + + rLine.TruncLines( sal_True ); + bRet = bBack; + } + nOrphLines = nOldOrphans; + + UNDO_SWAP( pFrm ) + + return bRet; +} + +/************************************************************************* + * WidowsAndOrphans::FindWidows() + *************************************************************************/ + +/* FindWidows positioniert den SwTxtMargin des Masters auf die umzubrechende + * Zeile, indem der Follow formatiert und untersucht wird. + * Liefert sal_True zurueck, wenn die Widows-Regelung in Kraft tritt, + * d.h. der Absatz _zusammengehalten_ werden soll ! + */ + +sal_Bool WidowsAndOrphans::FindWidows( SwTxtFrm *pFrame, SwTxtMargin &rLine ) +{ + ASSERT( ! pFrame->IsVertical() || ! pFrame->IsSwapped(), + "WidowsAndOrphans::FindWidows with swapped frame" ) + + if( !nWidLines || !pFrame->IsFollow() ) + return sal_False; + + rLine.Bottom(); + + // Wir koennen noch was abzwacken + SwTxtFrm *pMaster = pFrame->FindMaster(); + ASSERT(pMaster, "+WidowsAndOrphans::FindWidows: Widows in a master?"); + if( !pMaster ) + return sal_False; + + // 5156: Wenn die erste Zeile des Follows nicht passt, wird der Master + // wohl voll mit Dummies sein. In diesem Fall waere ein PREP_WIDOWS fatal. + if( pMaster->GetOfst() == pFrame->GetOfst() ) + return sal_False; + + // Resthoehe des Masters + SWRECTFN( pFrame ) + + const SwTwips nDocPrtTop = (pFrame->*fnRect->fnGetPrtTop)(); + SwTwips nOldHeight; + SwTwips nTmpY = rLine.Y() + rLine.GetLineHeight(); + + if ( bVert ) + { + nTmpY = pFrame->SwitchHorizontalToVertical( nTmpY ); + nOldHeight = -(pFrame->Prt().*fnRect->fnGetHeight)(); + } + else + nOldHeight = (pFrame->Prt().*fnRect->fnGetHeight)(); + + const SwTwips nChg = (*fnRect->fnYDiff)( nTmpY, nDocPrtTop + nOldHeight ); + + // Unterhalb der Widows-Schwelle... + if( rLine.GetLineNr() >= nWidLines ) + { + // 8575: Follow to Master I + // Wenn der Follow *waechst*, so besteht fuer den Master die Chance, + // Zeilen entgegenzunehmen, die er vor Kurzem gezwungen war an den + // Follow abzugeben: Prepare(Need); diese Abfrage unterhalb von nChg! + // (0W, 2O, 2M, 2F) + 1F = 3M, 2F + if( rLine.GetLineNr() > nWidLines && pFrame->IsJustWidow() ) + { + // Wenn der Master gelockt ist, so hat er vermutlich gerade erst + // eine Zeile an uns abgegeben, diese geben nicht zurueck, nur + // weil bei uns daraus mehrere geworden sind (z.B. durch Rahmen). + if( !pMaster->IsLocked() && pMaster->GetUpper() ) + { + const SwTwips nTmpRstHeight = (pMaster->Frm().*fnRect->fnBottomDist) + ( (pMaster->GetUpper()->*fnRect->fnGetPrtBottom)() ); + if ( nTmpRstHeight >= + SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) ) + { + pMaster->Prepare( PREP_ADJUST_FRM ); + pMaster->_InvalidateSize(); + pMaster->InvalidatePage(); + } + } + + pFrame->SetJustWidow( sal_False ); + } + return sal_False; + } + + // 8575: Follow to Master II + // Wenn der Follow *schrumpft*, so besteht fuer den Master die Chance, + // den kompletten Orphan zu inhalieren. + // (0W, 2O, 2M, 1F) - 1F = 3M, 0F -> PREP_ADJUST_FRM + // (0W, 2O, 3M, 2F) - 1F = 2M, 2F -> PREP_WIDOWS + + if( 0 > nChg && !pMaster->IsLocked() && pMaster->GetUpper() ) + { + SwTwips nTmpRstHeight = (pMaster->Frm().*fnRect->fnBottomDist) + ( (pMaster->GetUpper()->*fnRect->fnGetPrtBottom)() ); + if( nTmpRstHeight >= SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) ) + { + pMaster->Prepare( PREP_ADJUST_FRM ); + pMaster->_InvalidateSize(); + pMaster->InvalidatePage(); + pFrame->SetJustWidow( sal_False ); + return sal_False; + } + } + + // Master to Follow + // Wenn der Follow nach seiner Formatierung weniger Zeilen enthaelt + // als Widows, so besteht noch die Chance, einige Zeilen des Masters + // abzuzwacken. Wenn dadurch die Orphans-Regel des Masters in Kraft + // tritt muss im CalcPrep() des Master-Frame der Frame so vergroessert + // werden, dass er nicht mehr auf seine urspruengliche Seite passt. + // Wenn er noch ein paar Zeilen entbehren kann, dann muss im CalcPrep() + // ein Shrink() erfolgen, der Follow mit dem Widows rutscht dann auf + // die Seite des Masters, haelt sich aber zusammen, so dass er (endlich) + // auf die naechste Seite rutscht. - So die Theorie! + + + // Wir fordern nur noch ein Zeile zur Zeit an, weil eine Zeile des Masters + // bei uns durchaus mehrere Zeilen ergeben koennten. + // Dafuer behaelt CalcFollow solange die Kontrolle, bis der Follow alle + // notwendigen Zeilen bekommen hat. + MSHORT nNeed = 1; // frueher: nWidLines - rLine.GetLineNr(); + + // Special case: Master cannot give lines to follow + // --> FME 2008-09-16 #i91421# + if ( !pMaster->GetIndPrev() ) + { + ULONG nLines = pMaster->GetThisLines(); + if(nLines == 0 && pMaster->HasPara()) + { + const SwParaPortion *pMasterPara = pMaster->GetPara(); + if(pMasterPara && pMasterPara->GetNext()) + nLines = 2; + } + if( nLines <= nNeed ) + return sal_False; + } + + pMaster->Prepare( PREP_WIDOWS, (void*)&nNeed ); + return sal_True; +} + +/************************************************************************* + * WidowsAndOrphans::WouldFit() + *************************************************************************/ + +sal_Bool WidowsAndOrphans::WouldFit( SwTxtMargin &rLine, SwTwips &rMaxHeight, sal_Bool bTst ) +{ + // Here it does not matter, if pFrm is swapped or not. + // IsInside() takes care for itself + + // Wir erwarten, dass rLine auf der letzten Zeile steht!! + ASSERT( !rLine.GetNext(), "WouldFit: aLine::Bottom missed!" ); + MSHORT nLineCnt = rLine.GetLineNr(); + + // Erstmal die Orphansregel und den Initialenwunsch erfuellen ... + const MSHORT nMinLines = Max( GetOrphansLines(), rLine.GetDropLines() ); + if ( nLineCnt < nMinLines ) + return sal_False; + + rLine.Top(); + SwTwips nLineSum = rLine.GetLineHeight(); + + while( nMinLines > rLine.GetLineNr() ) + { + DBG_LOOP; + if( !rLine.NextLine() ) + return sal_False; + nLineSum += rLine.GetLineHeight(); + } + + // Wenn wir jetzt schon nicht mehr passen ... + if( !IsInside( rLine ) ) + return sal_False; + + // Jetzt noch die Widows-Regel ueberpruefen + if( !nWidLines && !pFrm->IsFollow() ) + { + // I.A. brauchen Widows nur ueberprueft werden, wenn wir ein Follow + // sind. Bei WouldFit muss aber auch fuer den Master die Regel ueber- + // prueft werden, weil wir ja gerade erst die Trennstelle ermitteln. + // Im Ctor von WidowsAndOrphans wurde nWidLines aber nur fuer Follows + // aus dem AttrSet ermittelt, deshalb holen wir es hier nach: + const SwAttrSet& rSet = pFrm->GetTxtNode()->GetSwAttrSet(); + nWidLines = rSet.GetWidows().GetValue(); + } + + // Sind nach Orphans/Initialen noch genug Zeilen fuer die Widows uebrig? + // #111937#: If we are currently doing a test formatting, we may not + // consider the widows rule for two reasons: + // 1. The columns may have different widths. + // Widow lines would have wrong width. + // 2. Test formatting is only done up to the given space. + // we do not have any lines for widows at all. + if( bTst || nLineCnt - nMinLines >= GetWidowsLines() ) + { + if( rMaxHeight >= nLineSum ) + { + rMaxHeight -= nLineSum; + return sal_True; + } + } + return sal_False; +} + diff --git a/sw/source/core/text/widorp.hxx b/sw/source/core/text/widorp.hxx new file mode 100644 index 000000000000..3607c5e31eda --- /dev/null +++ b/sw/source/core/text/widorp.hxx @@ -0,0 +1,98 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: widorp.hxx,v $ + * $Revision: 1.8.214.1 $ + * + * 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. + * + ************************************************************************/ +#ifndef _WIDORP_HXX +#define _WIDORP_HXX +class SwTxtFrm; + +#include "swtypes.hxx" +#include "itrtxt.hxx" + +class SwTxtFrmBreak +{ +private: + SwTwips nRstHeight; + SwTwips nOrigin; +protected: + SwTxtFrm *pFrm; + sal_Bool bBreak; + sal_Bool bKeep; +public: + SwTxtFrmBreak( SwTxtFrm *pFrm, const SwTwips nRst = 0 ); + sal_Bool IsBreakNow( SwTxtMargin &rLine ); + + sal_Bool IsBroken() const { return bBreak; } + sal_Bool IsKeepAlways() const { return bKeep; } + void Keep() { bKeep = sal_True; } + void Break() { bKeep = sal_False; bBreak = sal_True; } + + inline sal_Bool GetKeep() const { return bKeep; } + inline void SetKeep( const sal_Bool bNew ) { bKeep = bNew; } + + sal_Bool IsInside( SwTxtMargin &rLine ) const; + + // Um Sonderfaelle mit Ftn behandeln zu koennen. + // Mit SetRstHeight wird dem SwTxtFrmBreak die Resthoehe eingestellt, + // Um TruncLines() rufen zu koennen, ohne dass IsBreakNow() einen + // anderen Wert zurueckliefert. + // Es wird dabei davon ausgegangen, dass rLine auf der letzten Zeile + // steht, die nicht mehr passt. + + // OD 2004-02-27 #106629# - no longer inline + void SetRstHeight( const SwTxtMargin &rLine ); + SwTwips GetRstHeight() const { return nRstHeight; } +}; + +class WidowsAndOrphans : public SwTxtFrmBreak +{ +private: + MSHORT nWidLines, nOrphLines; + +public: + WidowsAndOrphans( SwTxtFrm *pFrm, const SwTwips nRst = 0, + sal_Bool bCheckKeep = sal_True ); + sal_Bool FindWidows( SwTxtFrm *pFrm, SwTxtMargin &rLine ); + MSHORT GetWidowsLines() const + { return nWidLines; } + MSHORT GetOrphansLines() const + { return nOrphLines; } + void ClrOrphLines(){ nOrphLines = 0; } + + sal_Bool FindBreak( SwTxtFrm *pFrm, SwTxtMargin &rLine, sal_Bool bHasToFit ); + sal_Bool WouldFit( SwTxtMargin &rLine, SwTwips &rMaxHeight, sal_Bool bTest ); + // OD 2004-02-25 #i16128# - rename method to avoid confusion with base class + // method <SwTxtFrmBreak::IsBreakNow>, which isn't virtual. + sal_Bool IsBreakNowWidAndOrp( SwTxtMargin &rLine ) + { + return ( rLine.GetLineNr() > nOrphLines ) && IsBreakNow( rLine ); + } +}; + + +#endif diff --git a/sw/source/core/text/wrong.cxx b/sw/source/core/text/wrong.cxx new file mode 100644 index 000000000000..5897b38def4e --- /dev/null +++ b/sw/source/core/text/wrong.cxx @@ -0,0 +1,647 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: wrong.cxx,v $ + * $Revision: 1.16 $ + * + * 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 <tools/string.hxx> +#include <tools/debug.hxx> +#include "errhdl.hxx" +#include "swtypes.hxx" +#include "txttypes.hxx" + +#include "SwGrammarMarkUp.hxx" + + +/************************************************************************* + * SwWrongList::SwWrongList() + *************************************************************************/ +SwWrongList::SwWrongList( WrongListType eType ) : + meType (eType), + nBeginInvalid(STRING_LEN), // everything correct... (the invalid area starts beyond the string) + nEndInvalid (STRING_LEN) +{ + maList.reserve( 5 ); +} + +SwWrongList::~SwWrongList() +{ + ClearList(); +} + +/************************************************************************* + * SwWrongList* SwWrongList::Clone() + *************************************************************************/ + +SwWrongList* SwWrongList::Clone() +{ + SwWrongList* pClone = new SwWrongList( meType ); + pClone->CopyFrom( *this ); + return pClone; +} + +/************************************************************************* + * void SwWrongList::CopyFrom( const SwWrongList& rCopy ) + *************************************************************************/ + +void SwWrongList::CopyFrom( const SwWrongList& rCopy ) +{ + maList = rCopy.maList; + meType = rCopy.meType; + nBeginInvalid = rCopy.nBeginInvalid; + nEndInvalid = rCopy.nEndInvalid; + for( size_t i = 0; i < maList.size(); ++i ) + { + if( maList[i].mpSubList ) + maList[i].mpSubList = maList[i].mpSubList->Clone(); + } +} + +/************************************************************************* + * SwWrongList::ClearList() + *************************************************************************/ +void SwWrongList::ClearList() +{ + for ( size_t i = 0; i < maList.size(); ++i) + { + if (maList[i].mpSubList) + delete maList[i].mpSubList; + maList[i].mpSubList = NULL; + } + maList.clear(); +} + +/************************************************************************* + * sal_Bool SwWrongList::InWrongWord() gibt den Anfang und die Laenge des + * Wortes zurueck, wenn es als falsch markiert ist. + *************************************************************************/ +sal_Bool SwWrongList::InWrongWord( xub_StrLen &rChk, xub_StrLen &rLn ) const +{ + MSHORT nPos = GetWrongPos( rChk ); + xub_StrLen nWrPos; + if( nPos < Count() && ( nWrPos = Pos( nPos ) ) <= rChk ) + { + rLn = Len( nPos ); + if( nWrPos + rLn <= rChk ) + return sal_False; + rChk = nWrPos; + return sal_True; + } + return sal_False; +} + +/************************************************************************* + * sal_Bool SwWrongList::Check() liefert den ersten falschen Bereich + *************************************************************************/ +sal_Bool SwWrongList::Check( xub_StrLen &rChk, xub_StrLen &rLn ) const +{ + MSHORT nPos = GetWrongPos( rChk ); + rLn = rLn + rChk; + xub_StrLen nWrPos; + + if( nPos == Count() ) + return sal_False; + + xub_StrLen nEnd = Len( nPos ); + nEnd = nEnd + ( nWrPos = Pos( nPos ) ); + if( nEnd == rChk ) + { + ++nPos; + if( nPos == Count() ) + return sal_False; + else + { + nEnd = Len( nPos ); + nEnd = nEnd + ( nWrPos = Pos( nPos ) ); + } + } + if( nEnd > rChk && nWrPos < rLn ) + { + if( nWrPos > rChk ) + rChk = nWrPos; + if( nEnd < rLn ) + rLn = nEnd; + rLn = rLn - rChk; + return 0 != rLn; + } + return sal_False; +} + +/************************************************************************* + * xub_StrLen SwWrongList::NextWrong() liefert die naechste Fehlerposition + *************************************************************************/ + +xub_StrLen SwWrongList::NextWrong( xub_StrLen nChk ) const +{ + xub_StrLen nRet; + xub_StrLen nPos = GetWrongPos( nChk ); + if( nPos < Count() ) + { + nRet = Pos( nPos ); + if( nRet < nChk && nRet + Len( nPos ) <= nChk ) + { + if( ++nPos < Count() ) + nRet = Pos( nPos ); + else + nRet = STRING_LEN; + } + } + else + nRet = STRING_LEN; + if( nRet > GetBeginInv() && nChk < GetEndInv() ) + nRet = nChk > GetBeginInv() ? nChk : GetBeginInv(); + return nRet; +} + +/************************************************************************* + * MSHORT SwWrongList::GetWrongPos( xub_StrLen nValue ) + * sucht die erste Position im Array, die groessergleich nValue ist, + * dies kann natuerlich auch hinter dem letzten Element sein! + *************************************************************************/ + +MSHORT SwWrongList::GetWrongPos( xub_StrLen nValue ) const +{ + MSHORT nOben = Count(), nMitte = 0, nUnten = 0; + + if( nOben > 0 ) + { + // For smart tag lists, we may not use a binary search. We return the + // position of the first smart tag which coveres nValue + if ( 0 != maList[0].maType.getLength() || maList[0].mpSubList ) + { + std::vector<SwWrongArea>::const_iterator aIter = maList.begin(); + while ( aIter != maList.end() ) + { + const xub_StrLen nSTPos = (*aIter).mnPos; + const xub_StrLen nSTLen = (*aIter).mnLen; + if ( nSTPos <= nValue && nValue < nSTPos + nSTLen ) + break; + else if ( nSTPos > nValue ) + break; + + ++aIter; + ++nUnten; + } + return nUnten; + } + + --nOben; + while( nUnten <= nOben ) + { + nMitte = nUnten + ( nOben - nUnten ) / 2; + xub_StrLen nTmp = Pos( nMitte ); + if( nTmp == nValue ) + { + nUnten = nMitte; + break; + } + else if( nTmp < nValue ) + { + if( nTmp + Len( nMitte ) >= nValue ) + { + nUnten = nMitte; + break; + } + nUnten = nMitte + 1; + } + else if( nMitte == 0 ) + { + break; + } + else + nOben = nMitte - 1; + } + } + + // nUnten now points to an index i into the wrong list which + // 1. nValue is inside [ Area[i].pos, Area[i].pos + Area[i].len ] (inkl!!!) + // 2. nValue < Area[i].pos + + return nUnten; +} + +/************************************************************************* + * void SwWrongList::_Invalidate() + *************************************************************************/ + +void SwWrongList::_Invalidate( xub_StrLen nBegin, xub_StrLen nEnd ) +{ + if ( nBegin < GetBeginInv() ) + nBeginInvalid = nBegin; + if ( nEnd > GetEndInv() ) + nEndInvalid = nEnd; +} + +void SwWrongList::SetInvalid( xub_StrLen nBegin, xub_StrLen nEnd ) +{ + nBeginInvalid = nBegin; + nEndInvalid = nEnd; +} + + +/************************************************************************* + * SwWrongList::Move( xub_StrLen nPos, long nDiff ) + * veraendert alle Positionen ab nPos um den angegebenen Wert, + * wird nach Einfuegen oder Loeschen von Buchstaben benoetigt. + *************************************************************************/ + +void SwWrongList::Move( xub_StrLen nPos, long nDiff ) +{ + MSHORT i = GetWrongPos( nPos ); + if( nDiff < 0 ) + { + xub_StrLen nEnd = nPos + xub_StrLen( -nDiff ); + MSHORT nLst = i; + xub_StrLen nWrPos; + xub_StrLen nWrLen; + sal_Bool bJump = sal_False; + while( nLst < Count() && Pos( nLst ) < nEnd ) + ++nLst; + if( nLst > i && ( nWrPos = Pos( nLst - 1 ) ) <= nPos ) + { + nWrLen = Len( nLst - 1 ); + // calculate new length of word + nWrLen = ( nEnd > nWrPos + nWrLen ) ? + nPos - nWrPos : + static_cast<xub_StrLen>(nWrLen + nDiff); + if( nWrLen ) + { + maList[--nLst].mnLen = nWrLen; + bJump = sal_True; + } + } + Remove( i, nLst - i ); + + if ( bJump ) + ++i; + if( STRING_LEN == GetBeginInv() ) + SetInvalid( nPos ? nPos - 1 : nPos, nPos + 1 ); + else + { + ShiftLeft( nBeginInvalid, nPos, nEnd ); + ShiftLeft( nEndInvalid, nPos, nEnd ); + _Invalidate( nPos ? nPos - 1 : nPos, nPos + 1 ); + } + } + else + { + xub_StrLen nWrPos; + xub_StrLen nEnd = nPos + xub_StrLen( nDiff ); + if( STRING_LEN != GetBeginInv() ) + { + if( nBeginInvalid > nPos ) + nBeginInvalid = nBeginInvalid + xub_StrLen( nDiff ); + if( nEndInvalid >= nPos ) + nEndInvalid = nEndInvalid + xub_StrLen( nDiff ); + } + // Wenn wir mitten in einem falschen Wort stehen, muss vom Wortanfang + // invalidiert werden. + if( i < Count() && nPos >= ( nWrPos = Pos( i ) ) ) + { + Invalidate( nWrPos, nEnd ); + xub_StrLen nWrLen = Len( i ) + xub_StrLen( nDiff ); + maList[i++].mnLen = nWrLen; + nWrLen = nWrLen + nWrPos; + Invalidate( nWrPos, nWrLen ); + } + else + Invalidate( nPos, nEnd ); + } + while( i < Count() ) + { + const xub_StrLen nTmp = static_cast<xub_StrLen>(nDiff + maList[i].mnPos); + maList[i++].mnPos = nTmp; + } +} + +/************************************************************************* + * SwWrongList::Fresh + * + * For a given range [nPos, nPos + nLen[ and an index nIndex, this function + * basically counts the number of SwWrongArea entries starting with nIndex + * up to nPos + nLen. All these entries are removed. + *************************************************************************/ +sal_Bool SwWrongList::Fresh( xub_StrLen &rStart, xub_StrLen &rEnd, xub_StrLen nPos, + xub_StrLen nLen, MSHORT nIndex, xub_StrLen nCursorPos ) +{ + // length of word must be greater than 0 and cursor position must be outside the word + sal_Bool bRet = nLen && ( nCursorPos > nPos + nLen || nCursorPos < nPos ); + + xub_StrLen nWrPos = 0; + xub_StrLen nWrEnd = rEnd; + MSHORT nCnt = nIndex; + if( nCnt < Count() && ( nWrPos = Pos( nIndex ) ) < nPos ) + { + if( rStart > nWrPos ) + rStart = nWrPos; + } + + while( nCnt < Count() && ( nWrPos = Pos( nCnt ) ) < nPos ) + nWrEnd = nWrPos + Len( nCnt++ ); + + if( nCnt < Count() && nWrPos == nPos && Len( nCnt ) == nLen ) + { + ++nCnt; + bRet = sal_True; + } + else + { + if( bRet ) + { + if( rStart > nPos ) + rStart = nPos; + nWrEnd = nPos + nLen; + } + } + + nPos = nPos + nLen; + + if( nCnt < Count() && ( nWrPos = Pos( nCnt ) ) < nPos ) + { + if( rStart > nWrPos ) + rStart = nWrPos; + } + + while( nCnt < Count() && ( nWrPos = Pos( nCnt ) ) < nPos ) + nWrEnd = nWrPos + Len( nCnt++ ); + + if( rEnd < nWrEnd ) + rEnd = nWrEnd; + + Remove( nIndex, nCnt - nIndex ); + + return bRet; +} + +void SwWrongList::Invalidate( xub_StrLen nBegin, xub_StrLen nEnd ) +{ + if (STRING_LEN == GetBeginInv()) + SetInvalid( nBegin, nEnd ); + else + _Invalidate( nBegin, nEnd ); +} + +sal_Bool SwWrongList::InvalidateWrong( ) +{ + if( Count() ) + { + xub_StrLen nFirst = Pos( 0 ); + xub_StrLen nLast = Pos( Count() - 1 ) + Len( Count() - 1 ); + Invalidate( nFirst, nLast ); + return sal_True; + } + else + return sal_False; +} + +SwWrongList* SwWrongList::SplitList( xub_StrLen nSplitPos ) +{ + SwWrongList *pRet = NULL; + MSHORT nLst = 0; + xub_StrLen nWrPos; + xub_StrLen nWrLen; + while( nLst < Count() && Pos( nLst ) < nSplitPos ) + ++nLst; + if( nLst && ( nWrPos = Pos( nLst - 1 ) ) + + ( nWrLen = Len( nLst - 1 ) ) > nSplitPos ) + { + nWrLen += nWrPos - nSplitPos; + maList[--nLst].mnPos = nSplitPos; + maList[nLst].mnLen = nWrLen; + } + if( nLst ) + { + if( WRONGLIST_GRAMMAR == GetWrongListType() ) + pRet = new SwGrammarMarkUp(); + else + pRet = new SwWrongList( GetWrongListType() ); + pRet->Insert(0, maList.begin(), ( nLst >= maList.size() ? maList.end() : maList.begin() + nLst ) ); + pRet->SetInvalid( GetBeginInv(), GetEndInv() ); + pRet->_Invalidate( nSplitPos ? nSplitPos - 1 : nSplitPos, nSplitPos ); + Remove( 0, nLst ); + } + if( STRING_LEN == GetBeginInv() ) + SetInvalid( 0, 1 ); + else + { + ShiftLeft( nBeginInvalid, 0, nSplitPos ); + ShiftLeft( nEndInvalid, 0, nSplitPos ); + _Invalidate( 0, 1 ); + } + nLst = 0; + while( nLst < Count() ) + { + nWrPos = maList[nLst].mnPos - nSplitPos; + maList[nLst++].mnPos = nWrPos; + } + return pRet; +} + +void SwWrongList::JoinList( SwWrongList* pNext, xub_StrLen nInsertPos ) +{ + if (pNext) + { + DBG_ASSERT( GetWrongListType() == pNext->GetWrongListType(), "type mismatch with next list" ); + } + if( pNext ) + { + USHORT nCnt = Count(); + pNext->Move( 0, nInsertPos ); + Insert(nCnt, pNext->maList.begin(), pNext->maList.end()); + + Invalidate( pNext->GetBeginInv(), pNext->GetEndInv() ); + if( nCnt && Count() > nCnt ) + { + xub_StrLen nWrPos = Pos( nCnt ); + xub_StrLen nWrLen = Len( nCnt ); + if( !nWrPos ) + { + nWrPos = nWrPos + nInsertPos; + nWrLen = nWrLen - nInsertPos; + maList[nCnt].mnPos = nWrPos; + maList[nCnt].mnLen = nWrLen; + } + if( nWrPos == Pos( nCnt - 1 ) + Len( nCnt - 1 ) ) + { + nWrLen = nWrLen + Len( nCnt - 1 ); + maList[nCnt - 1].mnLen = nWrLen; + Remove( nCnt, 1 ); + } + } + } + Invalidate( nInsertPos ? nInsertPos - 1 : nInsertPos, nInsertPos + 1 ); +} + + +void SwWrongList::InsertSubList( xub_StrLen nNewPos, xub_StrLen nNewLen, USHORT nWhere, SwWrongList* pSubList ) +{ + if (pSubList) + { + DBG_ASSERT( GetWrongListType() == pSubList->GetWrongListType(), "type mismatch with sub list" ); + } + std::vector<SwWrongArea>::iterator i = maList.begin(); + if ( nWhere >= maList.size() ) + i = maList.end(); // robust + else + i += nWhere; + maList.insert(i, SwWrongArea( rtl::OUString(), 0, nNewPos, nNewLen, pSubList ) ); +} + + +// New functions: Necessary because SwWrongList has been changed to use std::vector +void SwWrongList::Insert(USHORT nWhere, std::vector<SwWrongArea>::iterator startPos, std::vector<SwWrongArea>::iterator endPos) +{ + std::vector<SwWrongArea>::iterator i = maList.begin(); + if ( nWhere >= maList.size() ) + i = maList.end(); // robust + else + i += nWhere; + maList.insert(i, startPos, endPos); // insert [startPos, endPos[ before i + + // ownership of the sublist is passed to maList, therefore we have to set the + // pSubList-Pointers to 0 + while ( startPos != endPos ) + { + (*startPos).mpSubList = 0; + ++startPos; + } +} + +void SwWrongList::Remove(USHORT nIdx, USHORT nLen ) +{ + if ( nIdx >= maList.size() ) return; + std::vector<SwWrongArea>::iterator i1 = maList.begin(); + i1 += nIdx; + + std::vector<SwWrongArea>::iterator i2 = i1; + if ( nIdx + nLen >= static_cast<USHORT>(maList.size()) ) + i2 = maList.end(); // robust + else + i2 += nLen; + + std::vector<SwWrongArea>::iterator iLoop = i1; + while ( iLoop != i2 ) + { + if ( (*iLoop).mpSubList ) + delete (*iLoop).mpSubList; + ++iLoop; + } + +#if OSL_DEBUG_LEVEL > 1 + const int nOldSize = Count(); + (void) nOldSize; +#endif + + maList.erase(i1, i2); + +#if OSL_DEBUG_LEVEL > 1 + ASSERT( Count() + nLen == nOldSize, "SwWrongList::Remove() trouble" ) +#endif +} + +void SwWrongList::RemoveEntry( xub_StrLen nBegin, xub_StrLen nEnd ) { + USHORT nDelPos = 0; + USHORT nDel = 0; + std::vector<SwWrongArea>::iterator aIter = maList.begin(); + while( aIter != maList.end() && (*aIter).mnPos < nBegin ) + { + ++aIter; + ++nDelPos; + } + if( WRONGLIST_GRAMMAR == GetWrongListType() ) + { + while( aIter != maList.end() && nBegin < nEnd && nEnd > (*aIter).mnPos ) + { + ++aIter; + ++nDel; + } + } + else + { + while( aIter != maList.end() && nBegin == (*aIter).mnPos && nEnd == (*aIter).mnPos +(*aIter).mnLen ) + { + ++aIter; + ++nDel; + } + } + if( nDel ) + Remove( nDelPos, nDel ); +} + +bool SwWrongList::LookForEntry( xub_StrLen nBegin, xub_StrLen nEnd ) { + std::vector<SwWrongArea>::iterator aIter = maList.begin(); + while( aIter != maList.end() && (*aIter).mnPos < nBegin ) + ++aIter; + if( aIter != maList.end() && nBegin == (*aIter).mnPos && nEnd == (*aIter).mnPos +(*aIter).mnLen ) + return true; + return false; +} + +void SwWrongList::Insert( const rtl::OUString& rType, + com::sun::star::uno::Reference< com::sun::star::container::XStringKeyMap > xPropertyBag, + xub_StrLen nNewPos, xub_StrLen nNewLen ) +{ + std::vector<SwWrongArea>::iterator aIter = maList.begin(); + + while ( aIter != maList.end() ) + { + const xub_StrLen nSTPos = (*aIter).mnPos; + + if ( nNewPos < nSTPos ) + { + // insert at current position + break; + } + else if ( nNewPos == nSTPos ) + { + while ( aIter != maList.end() && (*aIter).mnPos == nSTPos ) + { + const xub_StrLen nSTLen = (*aIter).mnLen; + + if ( nNewLen < nSTLen ) + { + // insert at current position + break; + } + + ++aIter; + } + + break; + } + + ++aIter; + } + + maList.insert(aIter, SwWrongArea( rType, xPropertyBag, nNewPos, nNewLen, 0 ) ); +} + + |