blob: faa034cf2a4b994cea2d3b7fdb2010f4ceea2851 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <switerator.hxx>
#include <calbck.hxx>
#include <node.hxx>
#include <ndindex.hxx>
#include <swtable.hxx>
#include <ftnfrm.hxx>
#include <sectfrm.hxx>
#include "frmfmt.hxx"
#include "cntfrm.hxx"
#include "tabfrm.hxx"
#include "frmtool.hxx"
#include "section.hxx"
#include "node2lay.hxx"
/* -----------------25.02.99 10:31-------------------
* Die SwNode2LayImpl-Klasse erledigt die eigentliche Arbeit,
* die SwNode2Layout-Klasse ist nur die der Oefffentlichkeit bekannte Schnittstelle
* --------------------------------------------------*/
class SwNode2LayImpl
{
SwIterator<SwFrm,SwModify>* pIter;
SwModify* pMod;
SvPtrarr *pUpperFrms;// Zum Einsammeln der Upper
sal_uLong nIndex; // Der Index des einzufuegenden Nodes
sal_Bool bMaster : 1; // sal_True => nur Master , sal_False => nur Frames ohne Follow
sal_Bool bInit : 1; // Ist am SwClient bereits ein First()-Aufruf erfolgt?
public:
SwNode2LayImpl( const SwNode& rNode, sal_uLong nIdx, sal_Bool bSearch );
~SwNode2LayImpl() { delete pIter; delete pUpperFrms; }
SwFrm* NextFrm(); // liefert den naechsten "sinnvollen" Frame
SwLayoutFrm* UpperFrm( SwFrm* &rpFrm, const SwNode &rNode );
void SaveUpperFrms(); // Speichert (und lockt ggf.) die pUpper
// Fuegt unter jeden pUpper des Arrays einen Frame ein.
void RestoreUpperFrms( SwNodes& rNds, sal_uLong nStt, sal_uLong nEnd );
SwFrm* GetFrm( const Point* pDocPos = 0,
const SwPosition *pPos = 0,
const sal_Bool bCalcFrm = sal_True ) const;
};
/* -----------------25.02.99 10:38-------------------
* Hauptaufgabe des Ctor: Das richtige SwModify zu ermitteln,
* ueber das iteriert wird.
* Uebergibt man bSearch == sal_True, so wird der naechste Cntnt- oder TableNode
* gesucht, der Frames besitzt ( zum Einsammeln der pUpper ), ansonsten wird
* erwartet, das rNode bereits auf einem solchen Cntnt- oder TableNode sitzt,
* vor oder hinter den eingefuegt werden soll.
* --------------------------------------------------*/
SwNode* GoNextWithFrm(const SwNodes& rNodes, SwNodeIndex *pIdx)
{
if( pIdx->GetIndex() >= rNodes.Count() - 1 )
return 0;
SwNodeIndex aTmp(*pIdx, +1);
SwNode* pNd = 0;
while( aTmp < rNodes.Count()-1 )
{
pNd = &aTmp.GetNode();
bool bFound = false;
if ( pNd->IsCntntNode() )
bFound = ( SwIterator<SwFrm,SwCntntNode>::FirstElement(*(SwCntntNode*)pNd) != 0);
else if ( pNd->IsTableNode() )
bFound = ( SwIterator<SwFrm,SwFmt>::FirstElement(*((SwTableNode*)pNd)->GetTable().GetFrmFmt()) != 0 );
else if( pNd->IsEndNode() && !pNd->StartOfSectionNode()->IsSectionNode() )
{
pNd = 0;
break;
}
if ( bFound )
break;
aTmp++;
}
if( aTmp == rNodes.Count()-1 )
pNd = 0;
else if( pNd )
(*pIdx) = aTmp;
return pNd;
}
SwNode* GoPreviousWithFrm(SwNodeIndex *pIdx)
{
if( !pIdx->GetIndex() )
return 0;
SwNodeIndex aTmp( *pIdx, -1 );
SwNode* pNd(0);
while( aTmp.GetIndex() )
{
pNd = &aTmp.GetNode();
bool bFound = false;
if ( pNd->IsCntntNode() )
bFound = ( SwIterator<SwFrm,SwCntntNode>::FirstElement(*(SwCntntNode*)pNd) != 0);
else if ( pNd->IsTableNode() )
bFound = ( SwIterator<SwFrm,SwFmt>::FirstElement(*((SwTableNode*)pNd)->GetTable().GetFrmFmt()) != 0 );
else if( pNd->IsStartNode() && !pNd->IsSectionNode() )
{
pNd = 0;
break;
}
if ( bFound )
break;
aTmp--;
}
if( !aTmp.GetIndex() )
pNd = 0;
else if( pNd )
(*pIdx) = aTmp;
return pNd;
}
SwNode2LayImpl::SwNode2LayImpl( const SwNode& rNode, sal_uLong nIdx, sal_Bool bSearch )
: pUpperFrms( NULL ), nIndex( nIdx ), bInit( sal_False )
{
const SwNode* pNd;
if( bSearch || rNode.IsSectionNode() )
{
// Suche den naechsten Cntnt/TblNode, der einen Frame besitzt,
// damit wir uns vor/hinter ihn haengen koennen
if( !bSearch && rNode.GetIndex() < nIndex )
{
SwNodeIndex aTmp( *rNode.EndOfSectionNode(), +1 );
pNd = GoPreviousWithFrm( &aTmp );
if( !bSearch && pNd && rNode.GetIndex() > pNd->GetIndex() )
pNd = NULL; // Nicht ueber den Bereich hinausschiessen
bMaster = sal_False;
}
else
{
SwNodeIndex aTmp( rNode, -1 );
pNd = GoNextWithFrm( rNode.GetNodes(), &aTmp );
bMaster = sal_True;
if( !bSearch && pNd && rNode.EndOfSectionIndex() < pNd->GetIndex() )
pNd = NULL; // Nicht ueber den Bereich hinausschiessen
}
}
else
{
pNd = &rNode;
bMaster = nIndex < rNode.GetIndex();
}
if( pNd )
{
if( pNd->IsCntntNode() )
pMod = (SwModify*)pNd->GetCntntNode();
else
{
ASSERT( pNd->IsTableNode(), "For Tablenodes only" );
pMod = pNd->GetTableNode()->GetTable().GetFrmFmt();
}
pIter = new SwIterator<SwFrm,SwModify>( *pMod );
}
else
{
pIter = NULL;
pMod = 0;
}
}
/* -----------------25.02.99 10:41-------------------
* SwNode2LayImpl::NextFrm() liefert den naechsten "sinnvollen" Frame,
* beim ersten Aufruf wird am eigentlichen Iterator ein First gerufen,
* danach die Next-Methode. Das Ergebnis wird auf Brauchbarkeit untersucht,
* so werden keine Follows akzeptiert, ein Master wird beim Einsammeln der
* pUpper und beim Einfuegen vor ihm akzeptiert. Beim Einfuegen dahinter
* wird vom Master ausgehend der letzte Follow gesucht und zurueckgegeben.
* Wenn der Frame innerhalb eines SectionFrms liegt, wird noch festgestellt,
* ob statt des Frames der SectionFrm der geeignete Rueckgabewert ist, dies
* ist der Fall, wenn der neu einzufuegende Node ausserhalb des Bereichs liegt.
* --------------------------------------------------*/
SwFrm* SwNode2LayImpl::NextFrm()
{
SwFrm* pRet;
if( !pIter )
return NULL;
if( !bInit )
{
pRet = pIter->First();
bInit = sal_True;
}
else
pRet = pIter->Next();
while( pRet )
{
SwFlowFrm* pFlow = SwFlowFrm::CastFlowFrm( pRet );
ASSERT( pFlow, "Cntnt or Table expected?!" );
// Follows sind fluechtige Gestalten, deshalb werden sie ignoriert.
// Auch wenn wir hinter dem Frame eingefuegt werden sollen, nehmen wir
// zunaechst den Master, hangeln uns dann aber zum letzten Follow durch.
if( !pFlow->IsFollow() )
{
if( !bMaster )
{
while( pFlow->HasFollow() )
pFlow = pFlow->GetFollow();
pRet = pFlow->GetFrm();
}
if( pRet->IsInSct() )
{
SwSectionFrm* pSct = pRet->FindSctFrm();
// Vorsicht: Wenn wir in einer Fussnote sind, so kann diese
// Layoutmaessig in einem spaltigen Bereich liegen, obwohl
// sie nodemaessig ausserhalb liegt. Deshalb muss bei Fussnoten
// ueberprueft werden, ob auch der SectionFrm in der Fussnote
// und nicht ausserhalb liegt.
if( !pRet->IsInFtn() || pSct->IsInFtn() )
{
ASSERT( pSct && pSct->GetSection(), "Where's my section?" );
SwSectionNode* pNd = pSct->GetSection()->GetFmt()->GetSectionNode();
ASSERT( pNd, "Lost SectionNode" );
// Wenn der erhaltene Frame in einem Bereichsframe steht,
// dessen Bereich den Ausgangsnode nicht umfasst, so kehren
// wir mit dem SectionFrm zurueck, sonst mit dem Cntnt/TabFrm
if( bMaster )
{
if( pNd->GetIndex() >= nIndex )
pRet = pSct;
}
else if( pNd->EndOfSectionIndex() < nIndex )
pRet = pSct;
}
}
return pRet;
}
pRet = pIter->Next();
}
return NULL;
}
void SwNode2LayImpl::SaveUpperFrms()
{
pUpperFrms = new SvPtrarr( 0, 20 );
SwFrm* pFrm;
while( 0 != (pFrm = NextFrm()) )
{
SwFrm* pPrv = pFrm->GetPrev();
pFrm = pFrm->GetUpper();
if( pFrm )
{
if( pFrm->IsFtnFrm() )
((SwFtnFrm*)pFrm)->ColLock();
else if( pFrm->IsInSct() )
pFrm->FindSctFrm()->ColLock();
if( pPrv && pPrv->IsSctFrm() )
((SwSectionFrm*)pPrv)->LockJoin();
pUpperFrms->Insert( (void*)pPrv, pUpperFrms->Count() );
pUpperFrms->Insert( (void*)pFrm, pUpperFrms->Count() );
}
}
delete pIter;
pIter = NULL;
pMod = 0;
}
SwLayoutFrm* SwNode2LayImpl::UpperFrm( SwFrm* &rpFrm, const SwNode &rNode )
{
rpFrm = NextFrm();
if( !rpFrm )
return NULL;
SwLayoutFrm* pUpper = rpFrm->GetUpper();
if( rpFrm->IsSctFrm() )
{
const SwNode* pNode = rNode.StartOfSectionNode();
if( pNode->IsSectionNode() )
{
SwFrm* pFrm = bMaster ? rpFrm->FindPrev() : rpFrm->FindNext();
if( pFrm && pFrm->IsSctFrm() )
{
// #137684#: pFrm could be a "dummy"-section
if( ((SwSectionFrm*)pFrm)->GetSection() &&
(&((SwSectionNode*)pNode)->GetSection() ==
((SwSectionFrm*)pFrm)->GetSection()) )
{
// OD 2004-06-02 #i22922# - consider columned sections
// 'Go down' the section frame as long as the layout frame
// is found, which would contain content.
while ( pFrm->IsLayoutFrm() &&
static_cast<SwLayoutFrm*>(pFrm)->Lower() &&
!static_cast<SwLayoutFrm*>(pFrm)->Lower()->IsFlowFrm() &&
static_cast<SwLayoutFrm*>(pFrm)->Lower()->IsLayoutFrm() )
{
pFrm = static_cast<SwLayoutFrm*>(pFrm)->Lower();
}
ASSERT( pFrm->IsLayoutFrm(),
"<SwNode2LayImpl::UpperFrm(..)> - expected upper frame isn't a layout frame." );
rpFrm = bMaster ? NULL
: static_cast<SwLayoutFrm*>(pFrm)->Lower();
ASSERT( !rpFrm || rpFrm->IsFlowFrm(),
"<SwNode2LayImpl::UpperFrm(..)> - expected sibling isn't a flow frame." );
return static_cast<SwLayoutFrm*>(pFrm);
}
pUpper = new SwSectionFrm(((SwSectionNode*)pNode)->GetSection(), rpFrm);
pUpper->Paste( rpFrm->GetUpper(),
bMaster ? rpFrm : rpFrm->GetNext() );
static_cast<SwSectionFrm*>(pUpper)->Init();
rpFrm = NULL;
// 'Go down' the section frame as long as the layout frame
// is found, which would contain content.
while ( pUpper->Lower() &&
!pUpper->Lower()->IsFlowFrm() &&
pUpper->Lower()->IsLayoutFrm() )
{
pUpper = static_cast<SwLayoutFrm*>(pUpper->Lower());
}
return pUpper;
}
}
};
if( !bMaster )
rpFrm = rpFrm->GetNext();
return pUpper;
}
void SwNode2LayImpl::RestoreUpperFrms( SwNodes& rNds, sal_uLong nStt, sal_uLong nEnd )
{
ASSERT( pUpperFrms, "RestoreUpper without SaveUpper?" )
SwNode* pNd;
SwDoc *pDoc = rNds.GetDoc();
sal_Bool bFirst = sal_True;
for( ; nStt < nEnd; ++nStt )
{
SwFrm* pNew = 0;
SwFrm* pNxt;
SwLayoutFrm* pUp;
if( (pNd = rNds[nStt])->IsCntntNode() )
for( sal_uInt16 n = 0; n < pUpperFrms->Count(); )
{
pNxt = (SwFrm*)(*pUpperFrms)[n++];
if( bFirst && pNxt && pNxt->IsSctFrm() )
((SwSectionFrm*)pNxt)->UnlockJoin();
pUp = (SwLayoutFrm*)(*pUpperFrms)[n++];
if( pNxt )
pNxt = pNxt->GetNext();
else
pNxt = pUp->Lower();
pNew = ((SwCntntNode*)pNd)->MakeFrm( pUp );
pNew->Paste( pUp, pNxt );
(*pUpperFrms)[n-2] = pNew;
}
else if( pNd->IsTableNode() )
for( sal_uInt16 x = 0; x < pUpperFrms->Count(); )
{
pNxt = (SwFrm*)(*pUpperFrms)[x++];
if( bFirst && pNxt && pNxt->IsSctFrm() )
((SwSectionFrm*)pNxt)->UnlockJoin();
pUp = (SwLayoutFrm*)(*pUpperFrms)[x++];
if( pNxt )
pNxt = pNxt->GetNext();
else
pNxt = pUp->Lower();
pNew = ((SwTableNode*)pNd)->MakeFrm( pUp );
ASSERT( pNew->IsTabFrm(), "Table exspected" );
pNew->Paste( pUp, pNxt );
((SwTabFrm*)pNew)->RegistFlys();
(*pUpperFrms)[x-2] = pNew;
}
else if( pNd->IsSectionNode() )
{
nStt = pNd->EndOfSectionIndex();
for( sal_uInt16 x = 0; x < pUpperFrms->Count(); )
{
pNxt = (SwFrm*)(*pUpperFrms)[x++];
if( bFirst && pNxt && pNxt->IsSctFrm() )
((SwSectionFrm*)pNxt)->UnlockJoin();
pUp = (SwLayoutFrm*)(*pUpperFrms)[x++];
ASSERT( pUp->GetUpper() || pUp->IsFlyFrm(), "Lost Upper" );
::_InsertCnt( pUp, pDoc, pNd->GetIndex(), sal_False, nStt+1, pNxt );
pNxt = pUp->GetLastLower();
(*pUpperFrms)[x-2] = pNxt;
}
}
bFirst = sal_False;
}
for( sal_uInt16 x = 0; x < pUpperFrms->Count(); ++x )
{
SwFrm* pTmp = (SwFrm*)(*pUpperFrms)[++x];
if( pTmp->IsFtnFrm() )
((SwFtnFrm*)pTmp)->ColUnlock();
else if ( pTmp->IsInSct() )
{
SwSectionFrm* pSctFrm = pTmp->FindSctFrm();
pSctFrm->ColUnlock();
// OD 26.08.2003 #i18103# - invalidate size of section in order to
// assure, that the section is formatted, unless it was 'Collocked'
// from its 'collection' until its 'restoration'.
pSctFrm->_InvalidateSize();
}
}
}
SwFrm* SwNode2LayImpl::GetFrm( const Point* pDocPos,
const SwPosition *pPos,
const sal_Bool bCalcFrm ) const
{
// mba: test if change of member pIter -> pMod broke anything
return pMod ? ::GetFrmOfModify( 0, *pMod, USHRT_MAX, pDocPos, pPos, bCalcFrm ) : 0;
}
SwNode2Layout::SwNode2Layout( const SwNode& rNd, sal_uLong nIdx )
{
pImpl = new SwNode2LayImpl( rNd, nIdx, sal_False );
}
SwNode2Layout::SwNode2Layout( const SwNode& rNd )
{
pImpl = new SwNode2LayImpl( rNd, rNd.GetIndex(), sal_True );
pImpl->SaveUpperFrms();
}
void SwNode2Layout::RestoreUpperFrms( SwNodes& rNds, sal_uLong nStt, sal_uLong nEnd )
{
ASSERT( pImpl, "RestoreUpperFrms without SaveUpperFrms" );
pImpl->RestoreUpperFrms( rNds, nStt, nEnd );
}
SwFrm* SwNode2Layout::NextFrm()
{
return pImpl->NextFrm();
}
SwLayoutFrm* SwNode2Layout::UpperFrm( SwFrm* &rpFrm, const SwNode &rNode )
{
return pImpl->UpperFrm( rpFrm, rNode );
}
SwNode2Layout::~SwNode2Layout()
{
delete pImpl;
}
SwFrm* SwNode2Layout::GetFrm( const Point* pDocPos,
const SwPosition *pPos,
const sal_Bool bCalcFrm ) const
{
return pImpl->GetFrm( pDocPos, pPos, bCalcFrm );
}