/* AbiWord
 * Copyright (C) 1998 AbiSource, Inc.
 * Copyright (C) 2002 Martin Sevior (msevior@physics.unimelb.edu.au>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include <string.h>
#include <stdlib.h>

#include "ut_types.h"
#include "ut_string.h"

#include "ap_Prefs.h"
#include "fl_SectionLayout.h"
#include "fl_FootnoteLayout.h"
#include "fl_Layout.h"
#include "fl_DocLayout.h"
#include "fl_TOCLayout.h"
#include "fl_BlockLayout.h"
#include "fl_TableLayout.h"
#include "fp_TableContainer.h"
#include "fb_LineBreaker.h"
#include "fb_ColumnBreaker.h"
#include "fp_FootnoteContainer.h"
#include "fp_TOCContainer.h"
#include "fp_Page.h"
#include "fp_Line.h"
#include "fp_Column.h"
#include "pd_Document.h"
#include "pp_AttrProp.h"
#include "gr_Graphics.h"
#include "pp_Property.h"
#include "px_ChangeRecord.h"
#include "px_CR_Object.h"
#include "px_CR_ObjectChange.h"
#include "px_CR_Span.h"
#include "px_CR_SpanChange.h"
#include "px_CR_Strux.h"
#include "px_CR_StruxChange.h"
#include "px_CR_Glob.h"
#include "fv_View.h"
#include "fp_Run.h"
#include "ut_debugmsg.h"
#include "ut_assert.h"
#include "ut_units.h"
#include "fg_Graphic.h"
#include "pt_Types.h"
/*
  TODO this file is now really too long.  divide it up
  into smaller ones.
*/

fl_SectionLayout::fl_SectionLayout(FL_DocLayout* pLayout, PL_StruxDocHandle sdh, PT_AttrPropIndex indexAP, SectionType iType, fl_ContainerType iCType, PTStruxType iStrux, fl_ContainerLayout * pMyContainerLayout)
	: fl_ContainerLayout(pMyContainerLayout, sdh, indexAP,iStrux, iCType),
	  m_iType(iType),
	  m_pLayout(pLayout),
	  m_bIsCollapsed(false), // collapsed layouts cannot contain point, and this value never changes
	  m_bNeedsReformat(true),
	  m_bNeedsRedraw(true),	
	  m_pGraphicImage(NULL),
	  m_pImageImage(NULL),
	  m_iGraphicTick(0),
	  m_iDocImageWidth(0),
	  m_iDocImageHeight(0)

{
	UT_ASSERT(pLayout);
	m_pDoc = pLayout->getDocument();
}

fl_SectionLayout::~fl_SectionLayout()
{
	DELETEP(m_pGraphicImage);
	DELETEP(m_pImageImage);
}

FL_DocLayout* fl_SectionLayout::getDocLayout(void) const
{
	if(m_pLayout == NULL)
	{
		return fl_ContainerLayout::getDocLayout();
	}
	return m_pLayout;
}

bool fl_SectionLayout::recalculateFields(UT_uint32 iUpdateCount)
{
	bool bResult = false;

	fl_ContainerLayout*	pL = getFirstLayout();

	while (pL)
	{
		bResult = pL->recalculateFields(iUpdateCount) || bResult;

		pL = pL->getNext();
	}

	return bResult;
}


void fl_SectionLayout::markAllRunsDirty(void)
{
	fl_ContainerLayout*	pL = getFirstLayout();
	while (pL)
	{
		pL->markAllRunsDirty();
		pL = pL->getNext();
	}
}

void fl_SectionLayout::_purgeLayout()
{
	fl_ContainerLayout*	pL = getLastLayout();

	while (pL)
	{
		fl_ContainerLayout* pNuke = pL;

		pL = pL->getPrev();
		pNuke->setNext(NULL);
		delete pNuke;
	}

	return;
}

void fl_SectionLayout::removeFromUpdate(fl_ContainerLayout * pCL)
{
  while((m_vecFormatLayout.getItemCount() > 0) && (m_vecFormatLayout.findItem(pCL) >= 0))
  {
    UT_sint32 i = m_vecFormatLayout.findItem(pCL);
    m_vecFormatLayout.deleteNthItem(i);
  }
}

void fl_SectionLayout::setNeedsReformat(fl_ContainerLayout * pCL, UT_uint32 /*offset*/)
{
        UT_sint32 i = m_vecFormatLayout.findItem(pCL);
	if(i< 0)
	{
	  m_vecFormatLayout.addItem(pCL);
	}
	m_bNeedsReformat = true;
	xxx_UT_DEBUGMSG(("SetNeedsReformat in %s from %s number to format %d\n",getContainerString(),pCL->getContainerString(),m_vecFormatLayout.getItemCount()));
	if(myContainingLayout() != NULL && (static_cast<fl_SectionLayout *>(myContainingLayout()) != this) && (getContainerType() != FL_CONTAINER_SHADOW))
	{
		static_cast<fl_SectionLayout *>(myContainingLayout())->setNeedsReformat(this);
	}
	if(getContainerType() == FL_CONTAINER_SHADOW)
	{
		fl_HdrFtrShadow * pShad = static_cast<fl_HdrFtrShadow *>(this);
		pShad->getHdrFtrSectionLayout()->setNeedsReformat(this,0);
	}
}


void fl_SectionLayout::setNeedsRedraw(void)
{
	m_bNeedsRedraw = true;
	if(myContainingLayout() != NULL  && static_cast<fl_SectionLayout *>(myContainingLayout()) != this)
	{
		static_cast<fl_SectionLayout *>(myContainingLayout())->setNeedsRedraw();
	}
}

bool fl_SectionLayout::bl_doclistener_populateSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_Span * pcrs, PT_BlockOffset blockOffset, UT_uint32 len)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	if(pBL->getPrev()!= NULL && pBL->getPrev()->getLastContainer()==NULL)
	{
		UT_DEBUGMSG(("In bl_doclistner_pop no LastLine \n"));
		UT_DEBUGMSG(("getPrev = %d this = %d \n",pBL->getPrev(),pBL));
		//  UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
	}
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_populateSpan(pBL,pcrs,blockOffset,len);
		}
		else
		{
			return false;
		}
		return bres;
	}

	return static_cast<fl_BlockLayout *>(pBL)->doclistener_populateSpan(pcrs, blockOffset, len);
}

bool fl_SectionLayout::bl_doclistener_populateObject(fl_ContainerLayout* pBL, PT_BlockOffset blockOffset, const PX_ChangeRecord_Object * pcro)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_populateObject(pBL,blockOffset,pcro);
		}
		else
		{
			return false;
		}
		return bres;
	}
	return static_cast<fl_BlockLayout *>(pBL)->doclistener_populateObject(blockOffset, pcro);
}

bool fl_SectionLayout::bl_doclistener_insertSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_Span * pcrs)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_insertSpan(pBL,pcrs);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
    bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertSpan(pcrs);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_deleteSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_Span * pcrs)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_deleteSpan(pBL,pcrs);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteSpan(pcrs);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_changeSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_SpanChange * pcrsc)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_changeSpan(pBL,pcrsc);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeSpan(pcrsc);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_deleteStrux(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_deleteStrux(pBL,pcrx);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteStrux(pcrx);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_changeStrux(fl_ContainerLayout* pBL, const PX_ChangeRecord_StruxChange * pcrxc)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_changeStrux(pBL,pcrxc);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeStrux(pcrxc);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_insertBlock(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx,
												  PL_StruxDocHandle sdh,
												  PL_ListenerId lid,
												  void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																		  PL_ListenerId lid,
																		  PL_StruxFmtHandle sfhNew))
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	FV_View * pView = NULL;
	UT_ASSERT(m_pDoc->getAllowChangeInsPoint());
	if(pHFSL)
	{
		if(pBL)
		{
			pHFSL->bl_doclistener_insertBlock(pBL,pcrx, sdh, lid, pfnBindHandles);
		}
		else
		{
			pView = m_pLayout->getView();
			// Insert the block at the beginning of the section
			fl_BlockLayout*	pNewBL = static_cast<fl_BlockLayout *>(insert(sdh, NULL, pcrx->getIndexAP(),FL_CONTAINER_BLOCK));
			if (!pNewBL)
			{
				UT_DEBUGMSG(("no memory for BlockLayout\n"));
				return false;
			}
			bres = pNewBL->doclistener_insertFirstBlock(pcrx, sdh,
													lid, pfnBindHandles);			// Insert the block at the beginning of the section in the HDrFtr
            // Typically a cell of a table
			bres = pHFSL->bl_doclistener_insertFirstBlock(this,pcrx, sdh, lid);
		}
		pHFSL->checkAndAdjustCellSize(this);
		UT_ASSERT(m_pDoc->getAllowChangeInsPoint());
		return bres;
	}
	if (pBL)
		return static_cast<fl_BlockLayout *>(pBL)->doclistener_insertBlock(pcrx, sdh, lid, pfnBindHandles);
	else
	{
		// Insert the block at the beginning of the section
		fl_BlockLayout*	pNewBL = static_cast<fl_BlockLayout *>(insert(sdh, NULL, pcrx->getIndexAP(),FL_CONTAINER_BLOCK));
		if (!pNewBL)
		{
			UT_DEBUGMSG(("no memory for BlockLayout\n"));
			return false;
		}
		UT_ASSERT(m_pDoc->getAllowChangeInsPoint());
		return pNewBL->doclistener_insertFirstBlock(pcrx, sdh,
													lid, pfnBindHandles);

	}
}

void fl_SectionLayout::checkAndAdjustCellSize(void)
{
	if(getContainerType() != FL_CONTAINER_CELL)
	{
		return;
	}	
	fl_CellLayout * pCell = static_cast<fl_CellLayout *>(this);
	pCell->checkAndAdjustCellSize();
}

bool fl_SectionLayout::bl_doclistener_insertSection(fl_ContainerLayout* pPrevL,
													SectionType iType,
													const PX_ChangeRecord_Strux * pcrx,
													PL_StruxDocHandle sdh,
													PL_ListenerId lid,
													void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																			PL_ListenerId lid,
																			PL_StruxFmtHandle sfhNew))
{
	if(pPrevL->getContainerType() == FL_CONTAINER_BLOCK)
	{
		bool bres = static_cast<fl_BlockLayout *>(pPrevL)->doclistener_insertSection(pcrx, iType, sdh, lid, pfnBindHandles);
		return bres;
	}
	else if(iType == FL_SECTION_TOC)
	{
		PT_AttrPropIndex indexAP = pcrx->getIndexAP();
		fl_SectionLayout * pSL = static_cast<fl_SectionLayout *>(insert(sdh,pPrevL,indexAP, FL_CONTAINER_TOC));

		// Must call the bind function to complete the exchange of handles
		// with the document (piece table) *** before *** anything tries
		// to call down into the document (like all of the view
		// listeners).

		PL_StruxFmtHandle sfhNew = static_cast<PL_StruxFmtHandle>(pSL);
		//
		// Don't bind to shadows
		//
		if(pfnBindHandles)
		{
			pfnBindHandles(sdh,lid,sfhNew);
		}
		//
		// That's all we need to do except update the view pointers I guess..
		//
		FV_View* pView = m_pLayout->getView();
		if (pView && (pView->isActive() || pView->isPreview()))
		{
			pView->setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
		}
		else if(pView && pView->getPoint() > pcrx->getPosition())
		{
			//
			// For EndTOC
			//
			pView->setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
		}
		return true;

	}
	else if(((pPrevL->getContainerType() == FL_CONTAINER_FRAME) ||(pPrevL->getContainerType() == FL_CONTAINER_TABLE)) && (iType == FL_SECTION_HDRFTR))
	{
		fl_SectionLayout * pSL = new fl_HdrFtrSectionLayout(FL_HDRFTR_NONE,m_pLayout,NULL, sdh, pcrx->getIndexAP());
		fl_HdrFtrSectionLayout * pHFSL = static_cast<fl_HdrFtrSectionLayout *>(pSL);
		m_pLayout->addHdrFtrSection(pHFSL);
//
// Need to find the DocSectionLayout associated with this.
//
		const PP_AttrProp* pHFAP = NULL;
		PT_AttrPropIndex indexAP = pcrx->getIndexAP();
		bool bres = (m_pDoc->getAttrProp(indexAP, &pHFAP) && pHFAP);
		UT_ASSERT(bres);
		const XML_Char* pszNewID = NULL;
		pHFAP->getAttribute("id", pszNewID);
//
// pszHFID may not be defined yet. If not we can't do this stuff. If it is defined
// this step is essential
//
		if(pszNewID)
		{
		  // plam mystery code
			// plam, MES here, I need this code for inserting headers/footers.
		  UT_DEBUGMSG(("new id: tell plam if you see this message\n"));
//		  UT_ASSERT(0);
			fl_DocSectionLayout* pDocSL = m_pLayout->findSectionForHdrFtr(static_cast<const char*>(pszNewID));
			UT_ASSERT(pDocSL);
//
// Determine if this is a header or a footer.
//
			const XML_Char* pszSectionType = NULL;
			pHFAP->getAttribute("type", pszSectionType);

			HdrFtrType hfType = FL_HDRFTR_NONE;
			if (pszSectionType && *pszSectionType)
			{
				if(UT_strcmp(pszSectionType,"header") == 0)
					hfType = FL_HDRFTR_HEADER;
				else if (UT_strcmp(pszSectionType,"header-even") == 0)
					hfType = FL_HDRFTR_HEADER_EVEN;
				else if (UT_strcmp(pszSectionType,"header-first") == 0)
					hfType = FL_HDRFTR_HEADER_FIRST;
				else if (UT_strcmp(pszSectionType,"header-last") == 0)
					hfType = FL_HDRFTR_HEADER_LAST;
				else if (UT_strcmp(pszSectionType,"footer") == 0)
					hfType = FL_HDRFTR_FOOTER;
				else if (UT_strcmp(pszSectionType,"footer-even") == 0)
					hfType = FL_HDRFTR_FOOTER_EVEN;
				else if (UT_strcmp(pszSectionType,"footer-first") == 0)
					hfType = FL_HDRFTR_FOOTER_FIRST;
				else if (UT_strcmp(pszSectionType,"footer-last") == 0)
					hfType = FL_HDRFTR_FOOTER_LAST;

				if(hfType != FL_HDRFTR_NONE)
				{
					pHFSL->setDocSectionLayout(pDocSL);
					pHFSL->setHdrFtr(hfType);
					//
					// Set the pointers to this header/footer
					//
					pDocSL->setHdrFtr(hfType, pHFSL);
				}
			}
		}
		else
		{
			UT_DEBUGMSG(("NO ID found with insertSection HdrFtr \n"));
		}

	// Must call the bind function to complete the exchange of handles
	// with the document (piece table) *** before *** anything tries
	// to call down into the document (like all of the view
	// listeners).

		PL_StruxFmtHandle sfhNew = static_cast<PL_StruxFmtHandle>(pSL);
		//
		// Don't bind to shadows
		//
		if(pfnBindHandles)
		{
			pfnBindHandles(sdh,lid,sfhNew);
		}

		fl_SectionLayout* pOldSL = getDocSectionLayout();
//
// Now move all the containers following into the new section
//
		fl_ContainerLayout* pCL = pPrevL->getNext();
	//
	// BUT!!! Don't move the immediate Footnotes or Endnotes
	//
		fl_ContainerLayout * pLastCL = pPrevL;

		while(pCL && (static_cast<fl_SectionLayout *>(pCL) == pSL))
		{
			pCL = pCL->getNext();
		}
		while(pCL && ((pCL->getContainerType() == FL_CONTAINER_FOOTNOTE) ||
					  (pCL->getContainerType() == FL_CONTAINER_ENDNOTE)))
		{
			pLastCL = pCL;
			pCL = pCL->getNext();
		}
		fl_BlockLayout * pBL = NULL;
		while (pCL)
		{
			fl_ContainerLayout* pNext = pCL->getNext();
			pBL = NULL;
			pCL->collapse();
			if(pCL->getContainerType()==FL_CONTAINER_BLOCK)
			{
				pBL = static_cast<fl_BlockLayout *>(pCL);
			} 
			if(pBL && pBL->isHdrFtr())
			{
				fl_HdrFtrSectionLayout * pHF = static_cast<fl_HdrFtrSectionLayout *>(pBL->getSectionLayout());
				pHF->collapseBlock(pBL);
			}
			pOldSL->remove(pCL);
			pSL->add(pCL);
			if(pBL)
			{
				pBL->setSectionLayout( pSL);
				pBL->setNeedsReformat(pBL,0);
			}
			pCL = pNext;
		}

//
// Terminate blocklist here. This Block is the last in this section.
//
		if (pLastCL)
		{
			pLastCL->setNext(NULL);
			pOldSL->setLastLayout(pLastCL);
		}
		if(pszNewID)
		{
			pSL->format();
			pSL->redrawUpdate();
		}
		else
			return true;

		FV_View* pView = m_pLayout->getView();
		if (pView && (pView->isActive() || pView->isPreview()))
		{
			pView->setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
		}
		else if(pView && pView->getPoint() > pcrx->getPosition())
		{
			pView->setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET + fl_BLOCK_STRUX_OFFSET);
		}
		return true;
	}
	return false;
}


fl_SectionLayout * fl_SectionLayout::bl_doclistener_insertTable(fl_ContainerLayout* pBL,
													SectionType iType,
													const PX_ChangeRecord_Strux * pcrx,
													PL_StruxDocHandle sdh,
													PL_ListenerId lid,
													void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																			PL_ListenerId lid,
																			PL_StruxFmtHandle sfhNew))
{
	fl_SectionLayout * pSL = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertTable(pcrx, iType, sdh, lid, pfnBindHandles);
	checkAndAdjustCellSize();
	return pSL;
}



fl_SectionLayout * fl_SectionLayout::bl_doclistener_insertTable(SectionType iType,
													const PX_ChangeRecord_Strux * pcrx,
													PL_StruxDocHandle sdh,
													PL_ListenerId lid,
													void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																			PL_ListenerId lid,
																			PL_StruxFmtHandle sfhNew))
{
	UT_ASSERT(iType == FL_SECTION_TABLE);
	UT_ASSERT(pcrx);
	UT_ASSERT(pcrx->getType() == PX_ChangeRecord::PXT_InsertStrux);
	PT_DocPosition pos1;
//
// This is to clean the fragments
//
	m_pDoc->getBounds(true,pos1);

	fl_SectionLayout* pSL = NULL;
	bool bFrame = (getContainerType() == FL_CONTAINER_FRAME);
	PT_DocPosition pos = getPosition(true)+1;
	bool bTooFar = (pcrx->getPosition() > pos);
	if(bFrame && bTooFar)
	{
	  //
	  // This happens if a table is inserted right after an end frame strux
	  //
	  fl_DocSectionLayout * pDSL = getDocSectionLayout();
	  fl_ContainerLayout * pCL = static_cast<fl_ContainerLayout *>(this);
	  pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(pDSL)->insert(sdh,pCL,pcrx->getIndexAP(), FL_CONTAINER_TABLE));

	}
	else
	{
	  pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(this)->insert(sdh,this,pcrx->getIndexAP(), FL_CONTAINER_TABLE));
	}
	PL_StruxFmtHandle sfhNew = static_cast<PL_StruxFmtHandle>(pSL);
	//
	// Don't bind to shadows
	//
	if(pfnBindHandles)
	{
		pfnBindHandles(sdh,lid,sfhNew);
	}

//
// increment the insertion point in the view.
//
	FV_View* pView = m_pLayout->getView();
	if (pView && (pView->isActive() || pView->isPreview()))
	{
		pView->setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
	}
	else if(pView && pView->getPoint() > pcrx->getPosition())
	{
		pView->setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET);
	}
//
// OK that's it!
//
	checkAndAdjustCellSize();
	return pSL;
}

fl_BlockLayout * fl_SectionLayout::getFirstBlock(void) const
{
  fl_ContainerLayout * pCL = getFirstLayout();
  if(pCL == NULL)
  {
    return NULL;
  }
  if(pCL->getContainerType() == FL_CONTAINER_BLOCK)
  {
    return static_cast<fl_BlockLayout *>(pCL);
  }
  return pCL->getNextBlockInDocument();
}

fl_SectionLayout * fl_SectionLayout::bl_doclistener_insertFrame(fl_ContainerLayout* pBL,
													SectionType iType,
													const PX_ChangeRecord_Strux * pcrx,
													PL_StruxDocHandle sdh,
													PL_ListenerId lid,
													void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																			PL_ListenerId lid,
																			PL_StruxFmtHandle sfhNew))
{
	fl_SectionLayout * pSL = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertFrame(pcrx, iType, sdh, lid, pfnBindHandles);
	return pSL;
}

bool fl_SectionLayout::bl_doclistener_insertObject(fl_ContainerLayout* pBL, const PX_ChangeRecord_Object * pcro)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_insertObject(pBL,pcro);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertObject(pcro);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_deleteObject(fl_ContainerLayout* pBL, const PX_ChangeRecord_Object * pcro)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_deleteObject(pBL,pcro);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteObject(pcro);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_changeObject(fl_ContainerLayout* pBL, const PX_ChangeRecord_ObjectChange * pcroc)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_changeObject(pBL,pcroc);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}

	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeObject(pcroc);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_insertFmtMark(fl_ContainerLayout* pBL, const PX_ChangeRecord_FmtMark * pcrfm)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_insertFmtMark(pBL,pcrfm);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertFmtMark(pcrfm);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_deleteFmtMark(fl_ContainerLayout* pBL, const PX_ChangeRecord_FmtMark * pcrfm)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_deleteFmtMark(pBL,pcrfm);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteFmtMark(pcrfm);
	checkAndAdjustCellSize();
	return bres;
}

bool fl_SectionLayout::bl_doclistener_changeFmtMark(fl_ContainerLayout* pBL, const PX_ChangeRecord_FmtMarkChange * pcrfmc)
{
    fl_HdrFtrSectionLayout * pHFSL = getHdrFtrLayout();
	bool bres = true;
	if(pHFSL)
	{
		if(pBL)
		{
			bres = pHFSL->bl_doclistener_changeFmtMark(pBL,pcrfmc);
		}
		else
		{
			return false;
		}
		pHFSL->checkAndAdjustCellSize(this);
		return bres;
	}
	bres = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeFmtMark(pcrfmc);
	checkAndAdjustCellSize();
	return bres;
}


void fl_SectionLayout::setImageWidth(UT_sint32 iWidth)
{
    m_iDocImageWidth = iWidth;
}

void  fl_SectionLayout::setImageHeight(UT_sint32 iHeight)
{
    m_iDocImageHeight = iHeight;
}

void fl_SectionLayout::checkGraphicTick(GR_Graphics * pG)
{
  if(getDocLayout()->getGraphicTick() != m_iGraphicTick)
    {
      xxx_UT_DEBUGMSG(("Current tick == %d layout Tick == %d \n",m_iGraphicTick,getDocLayout()->getGraphicTick()));
    }
	if(m_pImageImage && (getDocLayout()->getGraphicTick() != m_iGraphicTick))
	{
		DELETEP(m_pImageImage);
		m_pImageImage = m_pGraphicImage->regenerateImage(pG);
		const UT_Rect rec(0,0,m_iDocImageWidth,m_iDocImageHeight);	
		m_pImageImage->scaleImageTo(pG,rec);
		m_iGraphicTick = getDocLayout()->getGraphicTick();
	}
}


//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
#ifdef _MSC_VER	// MSVC++ warns about using 'this' in initializer list.
#pragma warning(disable: 4355)
#endif
fl_DocSectionLayout::fl_DocSectionLayout(FL_DocLayout* pLayout, PL_StruxDocHandle sdh, PT_AttrPropIndex indexAP, SectionType iType)
	: fl_SectionLayout(pLayout, sdh, indexAP, iType, FL_CONTAINER_DOCSECTION,PTX_Section, this),
	  m_pHeaderSL(NULL),
	  m_pFooterSL(NULL),
	  m_pHeaderEvenSL(NULL),
	  m_pFooterEvenSL(NULL),
	  m_pHeaderFirstSL(NULL),
	  m_pFooterFirstSL(NULL),
	  m_pHeaderLastSL(NULL),
	  m_pFooterLastSL(NULL),
	  m_iNumColumns(1),
	  m_iColumnGap(0),
	  m_bColumnLineBetween(false),
	  m_iColumnOrder(0),
	  m_iSpaceAfter(0),
	  m_bRestart(false),
	  m_iRestartValue(0),

	  m_iLeftMargin(0),
	  m_dLeftMarginUserUnits(0.0),
	  m_iRightMargin(0),
	  m_dRightMarginUserUnits(0.0),
	  m_iTopMargin(0),
	  m_dTopMarginUserUnits(0.0),
	  m_iBottomMargin(0),
	  m_dBottomMarginUserUnits(0.0),
	  m_iFooterMargin(0),
	  m_dFooterMarginUserUnits(0.0),
	  m_iHeaderMargin(0),
	  m_dHeaderMarginUserUnits(0.0),
	  m_iMaxSectionColumnHeight(0),
	  m_dMaxSectionColumnHeight(0.0),
	  m_iFootnoteLineThickness(0),
	  m_iFootnoteYoff(0),

	  m_bForceNewPage(false),
	  m_pFirstColumn(NULL),
	  m_pLastColumn(NULL),
	  m_pFirstOwnedPage(NULL),

	  m_bNeedsFormat(false),
	  m_bNeedsRebuild(false),
	  m_bNeedsSectionBreak(true),
	  m_pFirstEndnoteContainer(NULL),
	  m_pLastEndnoteContainer(NULL),
	  m_bDeleteingBrokenContainers(false),
	  m_iNewHdrHeight(0),
	  m_iNewFtrHeight(0),
	  m_pHdrFtrChangeTimer(NULL),
	  m_bDoingCollapse(false)
{
	UT_ASSERT(iType == FL_SECTION_DOC);

	m_pDoc= pLayout->getDocument();
	
	m_sPaperColor.clear();
	m_sScreenColor.clear();
	lookupProperties();
}

fl_DocSectionLayout::~fl_DocSectionLayout()
{
// Remove any background HdrFtr change callbacks

	if(m_pHdrFtrChangeTimer)
	{
		m_pHdrFtrChangeTimer->stop();
		DELETEP(m_pHdrFtrChangeTimer);
	}
	// Don't delete broken tables since their pages have been removed
	
	// NB: be careful about the order of these
	_purgeLayout();

	UT_GenericVector<fl_HdrFtrSectionLayout*> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	fl_HdrFtrSectionLayout * pHdrFtr = NULL;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		pHdrFtr = vecHdrFtr.getNthItem(i);
		delete pHdrFtr;
	}

	fp_Column* pCol = m_pFirstColumn;
	while (pCol)
	{
		fp_Column* pNext = static_cast<fp_Column *>(pCol->getNext());

		delete pCol;

		pCol = pNext;
	}
}

void fl_DocSectionLayout::setFirstEndnoteContainer(fp_EndnoteContainer * pECon)
{
	m_pFirstEndnoteContainer = pECon;
}


void fl_DocSectionLayout::setLastEndnoteContainer(fp_EndnoteContainer * pECon)
{
	m_pLastEndnoteContainer = pECon;
}


fp_Container * fl_DocSectionLayout::getFirstEndnoteContainer(void)
{
	fp_Container * pCon = static_cast<fp_Container *>(m_pFirstEndnoteContainer);
	return pCon;
}

fp_Container * fl_DocSectionLayout::getLastEndnoteContainer(void)
{
	fp_Container * pCon = static_cast<fp_Container *>(m_pLastEndnoteContainer);
	return pCon;
}


fl_FootnoteLayout * fl_DocSectionLayout::getFootnoteLayout(UT_uint32 pid)
{
	fl_ContainerLayout * pCL = getFirstLayout();
	fl_FootnoteLayout * pFL = NULL;
	bool bFound = false;
	while(pCL && !bFound)
	{
		if(pCL->getContainerType() == FL_CONTAINER_FOOTNOTE)
		{
			pFL = static_cast<fl_FootnoteLayout *>(pCL);
			if(pFL->getFootnotePID() == pid)
			{
				bFound = true;
				break;
			}
		}
		pCL = pCL->getNext();
	}
	if(bFound)
	{
		return pFL;
	}
	return NULL;
}

/*!
 * Returns the usuable height of the Column in logical units (after subtracting
 * top and bottom margins)
 */ 
UT_sint32 fl_DocSectionLayout::getActualColumnHeight(void)
{
	UT_sint32 Height = static_cast<UT_sint32>(getDocument()->m_docPageSize.Height(DIM_IN) * UT_LAYOUT_RESOLUTION /getDocument()->m_docPageSize.getScale());
	Height -= (getTopMargin() + getBottomMargin());
	if(m_iMaxSectionColumnHeight > 0)
	{
		Height = m_iMaxSectionColumnHeight;
	}
	return Height;
}


/*!
 * Returns the usuable width of the Column in logical units (after subtracting
 * left and right margins)
 */ 

UT_sint32 fl_DocSectionLayout::getActualColumnWidth(void)
{
	UT_sint32 width = static_cast<UT_sint32>(getDocument()->m_docPageSize.Width(DIM_IN) * UT_LAYOUT_RESOLUTION /getDocument()->m_docPageSize.getScale());
	width -= (getLeftMargin() + getRightMargin());
	if(m_iNumColumns > 1)
	{
		width -= m_iNumColumns*m_iColumnGap;
		width = width/m_iNumColumns;
	}
	return width;
}
			
UT_sint32 fl_DocSectionLayout::getWidth(void)
{
	UT_sint32 ires = m_pLayout->getGraphics()->getResolution();
	UT_sint32 width = static_cast<UT_sint32>(ires * getDocument()->m_docPageSize.Width(DIM_IN));
	return width;
}

void fl_DocSectionLayout::setHdrFtr(HdrFtrType iType, fl_HdrFtrSectionLayout* pHFSL)
{
	if(pHFSL == NULL)
	{
		switch (iType)
		{
		case FL_HDRFTR_HEADER:
			m_pHeaderSL = pHFSL; break;
		case FL_HDRFTR_HEADER_EVEN:
			m_pHeaderEvenSL = pHFSL; break;
		case FL_HDRFTR_HEADER_FIRST:
			m_pHeaderFirstSL = pHFSL; break;
		case FL_HDRFTR_HEADER_LAST:
			m_pHeaderLastSL = pHFSL; break;
		case FL_HDRFTR_FOOTER:
			m_pFooterSL = pHFSL; break;
		case FL_HDRFTR_FOOTER_EVEN:
			m_pFooterEvenSL = pHFSL; break;
		case FL_HDRFTR_FOOTER_FIRST:
			m_pFooterFirstSL = pHFSL; break;
		case FL_HDRFTR_FOOTER_LAST:
			m_pFooterLastSL = pHFSL; break;
		case FL_HDRFTR_NONE:
			UT_ASSERT(UT_SHOULD_NOT_HAPPEN); break;
		}
		checkAndRemovePages();
		return;
	}
	const char* pszID = pHFSL->getAttribute("id");

	const char* pszAtt = NULL;

	pszAtt = getAttribute("header");
	if (pszAtt && (0 == strcmp(pszAtt, pszID)) && (iType == FL_HDRFTR_HEADER) )
	{
		m_pHeaderSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("header-even");
	if (pszAtt && (0 == strcmp(pszAtt, pszID))&& (iType == FL_HDRFTR_HEADER_EVEN) )
	{
		m_pHeaderEvenSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("header-first");
	if (pszAtt && (0 == strcmp(pszAtt, pszID))&& (iType == FL_HDRFTR_HEADER_FIRST) )
	{
		m_pHeaderFirstSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("header-last");
	if (pszAtt && (0 == strcmp(pszAtt, pszID))&& (iType == FL_HDRFTR_HEADER_LAST) )
	{
		m_pHeaderLastSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("footer");
	if (pszAtt && (0 == strcmp(pszAtt, pszID))&& (iType == FL_HDRFTR_FOOTER))
	{
		m_pFooterSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("footer-even");
	if (pszAtt && (0 == strcmp(pszAtt, pszID)) && (iType == FL_HDRFTR_FOOTER_EVEN)	)
	{
		m_pFooterEvenSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("footer-first");
	if (pszAtt && (0 == strcmp(pszAtt, pszID)) && (iType == FL_HDRFTR_FOOTER_FIRST)	)
	{
		m_pFooterFirstSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	pszAtt = getAttribute("footer-last");
	if (pszAtt && (0 == strcmp(pszAtt, pszID)) && (iType == FL_HDRFTR_FOOTER_LAST)	)
	{
		m_pFooterLastSL = pHFSL;
		checkAndRemovePages();
		return;
	}

	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
}

/*!
 * The calback that implements the HdrFtr size change
 */
void fl_DocSectionLayout::_HdrFtrChangeCallback(UT_Worker * pWorker)
{
	UT_ASSERT(pWorker);
	UT_DEBUGMSG(("Doing HdrFtr change callback \n"));
	// Get the docSectionLayout
	fl_DocSectionLayout * pDSL = static_cast<fl_DocSectionLayout *>(pWorker->getInstanceData());
	UT_ASSERT(pDSL);

	// Win32 timers can fire prematurely on asserts (the dialog's
	// message pump releases the timers)
	if (!pDSL->getDocument())
	{
		return;
	}
	// Don't do anything while PT is changing
	PD_Document * pDoc = pDSL->getDocument();
	if(pDoc->isPieceTableChanging())
	{
		return;
	}
	if(pDSL->m_pLayout->isLayoutFilling())
	{
// FIXME:
// Don't resize on load for now. Put this back laters when I can workout
// how to avoid horrible slowdowns and crashes.
//
		pDSL->m_sHdrFtrChangeProps.clear();
		pDSL->m_pHdrFtrChangeTimer->stop();
		DELETEP(pDSL->m_pHdrFtrChangeTimer);
		return;
	}
	// Don't do anything while a redrawupdate is happening either...
	if(pDoc->isRedrawHappenning())
	{
		return;
	}
// Don't do anything until insertion point is allowed to change
	if (!pDoc->getAllowChangeInsPoint())
	{
		return;
	}
	fl_DocSectionLayout * pPrev = static_cast<fl_DocSectionLayout *>(pDSL->getPrev());
	bool bDoit = true;
	while(pPrev && bDoit)
	{
//
// If a timer has been set on a previous docsection don't do this until it's
// cleared.
//
		if(pPrev->m_pHdrFtrChangeTimer != NULL)
		{
			return;
		}
		fl_DocSectionLayout * pPPrev = static_cast<fl_DocSectionLayout *>(pDSL->getPrev());
		if(pPPrev != pPrev)
		{
			pPrev = pPPrev;
		}
		else
		{
			bDoit = false;
			break;
		}
	}
	const char * pProps = pDSL->m_sHdrFtrChangeProps.c_str();
	const XML_Char * pszAtts[4] = {"props",pProps,NULL,NULL};
	pDoc->notifyPieceTableChangeStart();
	FV_View * pView =  pDSL->m_pLayout->getView();
	PL_StruxDocHandle sdh = pDSL->getStruxDocHandle();
    PT_DocPosition insPos = pView->getPoint();
	fl_HdrFtrShadow * pShadow = pView->getEditShadow();
	HdrFtrType hfType = FL_HDRFTR_HEADER;
	if(pShadow)
	{
		hfType = pShadow->getHdrFtrSectionLayout()->getHFType();
	}
	UT_sint32 iPage = -1;
	if(pShadow)
	{
		iPage = pDSL->m_pLayout->findPage(pShadow->getPage());
	}
	pDoc->setMarginChangeOnly( true);
	pDoc->changeStruxFmtNoUndo(PTC_AddFmt,sdh,pszAtts,NULL);
	pDoc->setMarginChangeOnly(false);
//
// Stop the resizer and delete and clear it's pointer. It's job is done now.
//
	pDSL->m_pHdrFtrChangeTimer->stop();
//
// update the screen
//
	pDSL->format();
	pDSL->formatAllHdrFtr();
	pDSL->updateLayout(true);
	pDoc->signalListeners(PD_SIGNAL_UPDATE_LAYOUT);
	pDoc->notifyPieceTableChangeEnd();
	pDSL->m_sHdrFtrChangeProps.clear();
//
// Put the point at the right point in the header/footer on the right page.
//
	if(iPage >= 0)
	{
		fp_Page * pPage = pDSL->m_pLayout->getNthPage(iPage);
		if(pPage)
		{
			fp_ShadowContainer* pShadowC = pPage->getHdrFtrP(hfType);
			pShadow = pShadowC->getShadow();
			pView->setHdrFtrEdit(pShadow);
		}
	}
    pView->setPoint(insPos);
	pView->notifyListeners(AV_CHG_MOTION | AV_CHG_HDRFTR );
    pView->setPoint(insPos);
	pView->ensureInsertionPointOnScreen();
	DELETEP(pDSL->m_pHdrFtrChangeTimer);
}

/*!
 * This method is called if the top and bottom margins of the pages
 * have changed following an auto-resize request from the header.
 * We try to avoid a complete rebuild.
 */
void fl_DocSectionLayout::doMarginChangeOnly(void)
{
	const PP_AttrProp* pAP = NULL;
	getAP(pAP);
	UT_return_if_fail(pAP);

	const XML_Char* pszSectionType = NULL;
	pAP->getAttribute("type", pszSectionType);
	lookupProperties();
	fp_Page * pMyPage = m_pLayout->getFirstPage();
	while(pMyPage && pMyPage->getOwningSection() != this)
	{
		pMyPage = pMyPage->getNext();
	}
	if(pMyPage == NULL)
	{
		return;
	}
//
// Remove broken tables. They need to be rebroken.
//
	deleteBrokenTablesFromHere(NULL);
	while(pMyPage && pMyPage->getOwningSection() == this)
	{
		pMyPage->TopBotMarginChanged();
		pMyPage = pMyPage->getNext();
	}
//
// Rebreak the document. Place containers on their new pages.
//
	fl_DocSectionLayout * pDSL = this;
	while(pDSL)
	{
		pDSL->completeBreakSection();
		pDSL = pDSL->getNextDocSection();
	}
}
/*!
 * Signal a PT change at the next opportunity to change the height of a Hdr
 * (true) or footer (false)
 *
 * newHeight is the value in layout units of the new height of the 
 * header/footer
 *
 * In both caes the header/footers grow "into" the document area.
 */
bool fl_DocSectionLayout::setHdrFtrHeightChange(bool bHdrFtr, UT_sint32 newHeight)
{
//
// Look to see if we've already sent a signal and if we have to adjust
// the height of the HdrFtr
//
	if(bHdrFtr)
	{
		if(newHeight <= m_iNewHdrHeight)
		{
			return false;
		}
		m_iNewHdrHeight = newHeight;
		getDocument()->setNewHdrHeight(newHeight);
		UT_sint32 fullHeight = newHeight + getHeaderMargin();
		UT_String sHeight = m_pLayout->getGraphics()->invertDimension(DIM_IN, static_cast<double>(fullHeight));
		UT_String sProp = "page-margin-top";
		UT_String_setProperty(m_sHdrFtrChangeProps,sProp,sHeight);
	}
	else
	{
		if(newHeight <= m_iNewFtrHeight)
		{
			return false;
		}
		m_iNewFtrHeight = newHeight;
		getDocument()->setNewFtrHeight(newHeight);
		UT_sint32 fullHeight = newHeight + getFooterMargin();
		UT_String sHeight = m_pLayout->getGraphics()->invertDimension(DIM_IN, static_cast<double>(fullHeight));
		UT_String sProp = "page-margin-bottom";
		UT_String_setProperty(m_sHdrFtrChangeProps,sProp,sHeight);
	}
//
// OK the idea is to run the timer in the idle loop until the Piecetable
// is clear and we're not redrawing
//
// This means the resize will happen at the first opportunity after the
// current edit is finished.
//
	if(m_pHdrFtrChangeTimer == NULL)
	{
	    int inMode = UT_WorkerFactory::IDLE | UT_WorkerFactory::TIMER;
	    UT_WorkerFactory::ConstructMode outMode = UT_WorkerFactory::NONE;

	    m_pHdrFtrChangeTimer = UT_WorkerFactory::static_constructor (_HdrFtrChangeCallback, this, inMode, outMode, m_pLayout->getGraphics());

	    UT_ASSERT(m_pHdrFtrChangeTimer);
	    UT_ASSERT(outMode != UT_WorkerFactory::NONE);

		// If the worker is working on a timer instead of in the idle
		// time, set the frequency of the checks.
	    if ( UT_WorkerFactory::TIMER == outMode )
		{
			// this is really a timer, so it's safe to static_cast it
			static_cast<UT_Timer*>(m_pHdrFtrChangeTimer)->set(100);
		}
	    m_pHdrFtrChangeTimer->start();
	}
	return true;
}

fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getHeader(void)
{
	return m_pHeaderSL;
}

fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getFooter(void)
{
	return m_pFooterSL;
}


fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getHeaderEven(void)
{
	return m_pHeaderEvenSL;
}

fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getFooterEven(void)
{
	return m_pFooterEvenSL;
}


fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getHeaderFirst(void)
{
	return m_pHeaderFirstSL;
}

fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getFooterFirst(void)
{
	return m_pFooterFirstSL;
}


fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getHeaderLast(void)
{
	return m_pHeaderLastSL;
}

fl_HdrFtrSectionLayout*   fl_DocSectionLayout::getFooterLast(void)
{
	return m_pFooterLastSL;
}


fp_Container* fl_DocSectionLayout::getFirstContainer() const
{
	return m_pFirstColumn;
}

fp_Container* fl_DocSectionLayout::getLastContainer() const
{
	return m_pLastColumn;
}

void fl_DocSectionLayout::setFirstContainer(fp_Container * pCon)
{
	UT_DEBUGMSG(("docSectionLayout: DocSec %x First container set to %x \n",this,pCon));
	m_pFirstColumn = static_cast<fp_Column *>(pCon);
}


void fl_DocSectionLayout::setLastContainer(fp_Container * pCon)
{
	m_pLastColumn = static_cast<fp_Column *>(pCon);
}

/*!
  Create new container
  \return The newly created container

  This creates a new column or row of same.

*/
fp_Container* fl_DocSectionLayout::getNewContainer(fp_Container * pFirstContainer)
{
	fp_Page* pPage = NULL;
	fp_Column* pLastColumn = static_cast<fp_Column*>(getLastContainer());
	fp_Column* pAfterColumn = NULL;
	UT_sint32 iNextCtrHeight = 0;

	if (pLastColumn)
	{
		fp_Container * prevContainer = NULL;
		fp_Page* pTmpPage = NULL;
		UT_sint32 pageHeight = 0;
		pTmpPage = pLastColumn->getPage();
		iNextCtrHeight = 0;
		if(pFirstContainer != NULL)
		{
			prevContainer = static_cast<fp_Container *>(pFirstContainer->getPrevContainerInSection());
		}
//
// Calculate from the page height up to prevContainer
//
		pageHeight = pTmpPage->getFilledHeight(prevContainer);

		if(pFirstContainer != NULL)
		{
			iNextCtrHeight = pFirstContainer->getHeight();
		}
		else if( pLastColumn->getLastContainer())
		{
			iNextCtrHeight = pLastColumn->getLastContainer()->getHeight();
		}
		else
		{
			iNextCtrHeight =12*14; // approximately one average line
		}
		UT_sint32 avail =  pTmpPage->getAvailableHeight();

		UT_sint32 newHeight = pageHeight+ 3*iNextCtrHeight;
		xxx_UT_DEBUGMSG(("SEVIOR: Pageheight =%d nextlineheight =%d newheight = %d availableheight =%d linepos %d \n",pageHeight,iNextCtrHeight,newHeight,avail));
		if( newHeight  >= avail || pFirstContainer == NULL)
		{
			xxx_UT_DEBUGMSG(("SEVIOR: Container on new page \n"));
			if (pTmpPage->getNext())
			{
				pPage = pTmpPage->getNext();
			}
			else
			{
				bool bIsFilling = m_pLayout->isLayoutFilling();
				pPage = m_pLayout->addNewPage(this,bIsFilling);
			}
		}
		else
		{
			xxx_UT_DEBUGMSG(("SEVIOR: Container on current page \n"));
			pPage = pTmpPage;
			if(prevContainer == NULL)
			{
				pAfterColumn = pPage->getNthColumnLeader(pPage->countColumnLeaders()-1);
			}
			else
			{
				pAfterColumn = static_cast<fp_Column *>(prevContainer->getContainer())->getLeader();
			}

		}
	}
	else
	{
		// We currently have no columns in this section.  Time to
		// create some.  If there is a previous section, then we need
		// to start our section right after that one.  If not, then we
		// start our section on the first page.  If there is no first
		// page, then we need to create one.
		fl_DocSectionLayout* pPrevSL = getPrevDocSection();
		if (pPrevSL)
		{
//
// This should make sure the last column in the previous section  has it's last container
// on a sane page.
//

#if 0
			//
			// Sevior this code should not be needed!
			//
			UT_DEBUGMSG(("code sez: this code should not be needed!\n"));
			UT_ASSERT(0);

			// says not needed, but clearly still is
#endif
			fp_Column * pPrevCol = static_cast<fp_Column *>(pPrevSL->getLastContainer());
			while(pPrevCol == NULL)
			{
				UT_DEBUGMSG(("BUG! BUG! Prev section has no last container! Attempting to fix this \n"));
				pPrevSL->format();
			    pPrevCol = static_cast<fp_Column *>(pPrevSL->getLastContainer());
			}
			fp_Page* pTmpPage = pPrevSL->getLastContainer()->getPage();
			fp_Container * prevContainer = NULL;
			if(pFirstContainer != NULL)
			{
				prevContainer = static_cast<fp_Container *>(pFirstContainer->getPrevContainerInSection());
			}

			UT_sint32 pageHeight = pTmpPage->getFilledHeight(prevContainer);
			if(pFirstContainer != NULL)
			{
				iNextCtrHeight = pFirstContainer->getHeight();
			}
			else if(pPrevCol->getLastContainer())
			{
				iNextCtrHeight = pPrevCol->getLastContainer()->getHeight();
			}
			else
			{
				iNextCtrHeight = 12*14; //average height!
			}
			bool bForce = (pageHeight + 2*iNextCtrHeight) >= pTmpPage->getAvailableHeight();
//
// Note that the HdrFtr Shadows are created BEFORE the columns are placed
// on the page.
//

			if (m_bForceNewPage || bForce)
			{
				if (pTmpPage->getNext())
				{
					pPage = pTmpPage->getNext();
				}
				else
				{
					pPage = m_pLayout->addNewPage(this);
				}
			}
			else
			{
				pPage = pTmpPage;
				if(prevContainer == NULL)
				{
					pAfterColumn = pPage->getNthColumnLeader(pPage->countColumnLeaders()-1);
				}
				else
				{
					pAfterColumn = static_cast<fp_Column *>(prevContainer->getContainer())->getLeader();
				}
			}
		}
		else
		{
			if (m_pLayout->countPages() > 0)
			{
				pPage = m_pLayout->getFirstPage();
			}
			else
			{
				pPage = m_pLayout->addNewPage(this,true);
			}
		}
	}

	UT_ASSERT(pPage);

	// Create row of columns
	fp_Column* pLeaderColumn = NULL;
	fp_Column* pTail = NULL;
	UT_uint32 i = 0;
	for (i=0; i<m_iNumColumns; i++)
	{
		fp_Column* pCol = new fp_Column(this);
		if (pTail)
		{
			pCol->setLeader(pLeaderColumn);
			pTail->setFollower(pCol);
			pTail->setNext(pCol);
			pCol->setPrev(pTail);

			pTail = pCol;
		}
		else
		{
			pLeaderColumn = pTail = pCol;
			pLeaderColumn->setLeader(pLeaderColumn);
		}
	}

	// Append added columns to any previous columns in this section.
	if (m_pLastColumn)
	{
		UT_ASSERT(m_pFirstColumn);

		m_pLastColumn->setNext(pLeaderColumn);
		pLeaderColumn->setPrev(m_pLastColumn);
	}
	else
	{
		UT_ASSERT(!m_pFirstColumn);
		UT_ASSERT(pLeaderColumn);
		m_pFirstColumn = pLeaderColumn;
	}

	// Find last added column and set that as the last in the section.
	fp_Column* pLastNewCol = pLeaderColumn;
	while (pLastNewCol->getFollower())
	{
		pLastNewCol = pLastNewCol->getFollower();
	}
	m_pLastColumn = pLastNewCol;
	UT_ASSERT(!(m_pLastColumn->getNext()));
	UT_ASSERT(!(m_pLastColumn->getFollower()));

	pPage->insertColumnLeader(pLeaderColumn, pAfterColumn);

	fp_Column* pTmpCol = pLeaderColumn;
	i = 0;
 	while (pTmpCol)
	{
		UT_ASSERT(pTmpCol->getPage());

		pTmpCol = pTmpCol->getFollower();
		i++;
	}

	return pLeaderColumn;
}

void fl_DocSectionLayout::format(void)
{
	fl_ContainerLayout*	pBL = getFirstLayout();
	FV_View * pView = m_pLayout->getView();

	bool bShowHidden = pView && pView->getShowPara();
	FPVisibility eHidden;
	bool bHidden;

	while (pBL)
	{
		eHidden  = pBL->isHidden();
		bHidden = ((eHidden == FP_HIDDEN_TEXT && !bShowHidden)
		              || eHidden == FP_HIDDEN_REVISION
		              || eHidden == FP_HIDDEN_REVISION_AND_TEXT);

		if(!bHidden)
		{
			pBL->format();
			UT_sint32 count = 0;
			while(pBL->getLastContainer() == NULL || pBL->getFirstContainer()==NULL)
			{
				UT_DEBUGMSG(("Error formatting a block try again \n"));
				count = count + 1;
				pBL->format();
				if(count > 3)
				{
					UT_DEBUGMSG(("Give up trying to format. Hope for the best :-( \n"));
					break;
				}
			}
		}

		pBL = pBL->getNext();
	}

	m_ColumnBreaker.breakSection(this);
	m_bNeedsFormat = false;
}

void fl_DocSectionLayout::markAllRunsDirty(void)
{
	fl_ContainerLayout*	pBL = getFirstLayout();
	while (pBL)
	{
		pBL->markAllRunsDirty();
		pBL = pBL->getNext();
	}
	if(m_pHeaderSL)
	{
		m_pHeaderSL->markAllRunsDirty();
	}
	if(m_pHeaderEvenSL)
	{
		m_pHeaderEvenSL->markAllRunsDirty();
	}
	if(m_pHeaderFirstSL)
	{
		m_pHeaderFirstSL->markAllRunsDirty();
	}
	if(m_pHeaderLastSL)
	{
		m_pHeaderLastSL->markAllRunsDirty();
	}
	if(m_pFooterSL)
	{
		m_pFooterSL->markAllRunsDirty();
	}
	if(m_pFooterEvenSL)
	{
		m_pFooterEvenSL->markAllRunsDirty();
	}
	if(m_pFooterFirstSL)
	{
		m_pFooterFirstSL->markAllRunsDirty();
	}
	if(m_pFooterLastSL)
	{
		m_pFooterLastSL->markAllRunsDirty();
	}
}

void fl_DocSectionLayout::updateLayout(bool bDoFull)
{
	fl_ContainerLayout*	pBL = getFirstLayout();
	FV_View * pView = m_pLayout->getView();
	bool bShowHidden = pView && pView->getShowPara();
	FPVisibility eHidden;
	bool bHidden;
	bDoFull = true;
	xxx_UT_DEBUGMSG(("Doing DocSection Update layout \n"));
	if (!bDoFull || (m_vecFormatLayout.getItemCount() > 0))
	{
	        UT_sint32 i =0;
		UT_sint32 j = 0;
		UT_sint32 count = static_cast<UT_sint32>(m_vecFormatLayout.getItemCount());
		for(i=0; i<count; i++)
		{  
		        if(j >= m_vecFormatLayout.getItemCount())
			    break;
		        pBL = m_vecFormatLayout.getNthItem(j);
			j++;
		        eHidden  = pBL->isHidden();
			bHidden = ((eHidden == FP_HIDDEN_TEXT && !bShowHidden)
				   || eHidden == FP_HIDDEN_REVISION
				   || eHidden == FP_HIDDEN_REVISION_AND_TEXT);

			if(!bHidden)
			{
			  xxx_UT_DEBUGMSG(("container %x type %s needformat %d \n",pBL,pBL->getContainerString(),pBL->needsReformat()));
			     if (pBL->needsReformat())
			     {
			          if(!(m_pLayout->isLayoutFilling() && pBL->getContainerType() == FL_CONTAINER_TOC))
				  {
				       pBL->format();
				       j--;
				       if(j < m_vecFormatLayout.getItemCount())
					 m_vecFormatLayout.deleteNthItem(j);
				  }
			     }
			     if (pBL->getContainerType() != FL_CONTAINER_BLOCK && !getDocument()->isDontImmediateLayout())
			     {
			          pBL->updateLayout(false);
			     }
			}
			
		}
	}
	else if(bDoFull)
	{
	        while (pBL)
		{
		        eHidden  = pBL->isHidden();
			bHidden = ((eHidden == FP_HIDDEN_TEXT && !bShowHidden)
				   || eHidden == FP_HIDDEN_REVISION
				   || eHidden == FP_HIDDEN_REVISION_AND_TEXT);

			if(!bHidden)
			{
			     if (pBL->needsReformat())
			     {
			          if(!(m_pLayout->isLayoutFilling() && pBL->getContainerType() == FL_CONTAINER_TOC))
				  {
				       pBL->format();
				  }
			     }
			     if (pBL->getContainerType() != FL_CONTAINER_BLOCK && !getDocument()->isDontImmediateLayout())
			     {
			          pBL->updateLayout(false);
			     }
			}

			pBL = pBL->getNext();
		}
	}
	m_vecFormatLayout.clear();
	if(needsSectionBreak() && !getDocument()->isDontImmediateLayout() )
	{
		m_ColumnBreaker.breakSection(this);
//		UT_ASSERT(!needsSectionBreak());
	}
	if(needsRebuild() && !getDocument()->isDontImmediateLayout() )
	{
//		UT_ASSERT(0);
		checkAndRemovePages();
		addValidPages();
	}
}

void fl_DocSectionLayout::setNeedsSectionBreak(bool bSet, fp_Page * pPage)
{
	m_bNeedsSectionBreak = bSet;
	fp_Page * pOldP = m_ColumnBreaker.getStartPage();
    UT_sint32 iOldP = 999999999;
	if(pPage == NULL)
	{
		m_ColumnBreaker.setStartPage(pPage);
		return;
	}	
	if(pPage->getOwningSection() != this)
	{
		m_ColumnBreaker.setStartPage(NULL);
		return;
	}	
	if(pOldP)
	{
		iOldP = getDocLayout()->findPage(pOldP);
	}
	UT_sint32 iNewP = getDocLayout()->findPage(pPage);
	xxx_UT_DEBUGMSG(("setNeedsSectionBreak: new Page %d OldPage %d \n",iNewP,iOldP));	
	if( (iNewP > -1) && (iNewP < iOldP))
	{
		xxx_UT_DEBUGMSG(("setNeedsSectionBreak: Rebuild from Page %x \n",pPage));	
		m_ColumnBreaker.setStartPage(pPage);
	}
}


void fl_DocSectionLayout::completeBreakSection(void)
{
	m_bNeedsSectionBreak = true;
	updateLayout(true);
	m_ColumnBreaker.setStartPage(NULL);
	m_ColumnBreaker.breakSection(this);
	m_bNeedsSectionBreak = false;
}

void fl_DocSectionLayout::redrawUpdate(void)
{
	fl_ContainerLayout*	pBL = getFirstLayout();

	// we only need to break and redo this section if its contents
	// have changed, i.e., if the field values changed
	xxx_UT_DEBUGMSG(("Doing redraw update \n"));
	while (pBL)
	{
		if(pBL->getContainerType() == FL_CONTAINER_BLOCK && static_cast<fl_BlockLayout *>(pBL)->hasUpdatableField())
		{
			bool bReformat = pBL->recalculateFields(getDocLayout()->getRedrawCount());
			if(bReformat)
			{
				pBL->format();
			}
		}
		else
		{
			pBL->recalculateFields(getDocLayout()->getRedrawCount());
		}
		if (pBL->needsRedraw())
		{
			pBL->redrawUpdate();
		}

		pBL = pBL->getNext();
	}
	xxx_UT_DEBUGMSG(("Finished Doing redraw update \n"));
	fp_EndnoteContainer * pECon =  static_cast<fp_EndnoteContainer *>(getFirstEndnoteContainer());
	if(pECon)
	{
		fl_EndnoteLayout * pEL = static_cast<fl_EndnoteLayout *>(pECon->getSectionLayout());
		while(pEL)
		{
			pEL->redrawUpdate();
			pEL = static_cast<fl_EndnoteLayout *>(pEL->getNext());
		}
	}
	if(needsSectionBreak() || needsRebuild())
	{
		m_ColumnBreaker.breakSection(this);
	
		if(needsRebuild())
		{
//			UT_ASSERT(0);
			checkAndRemovePages();
			addValidPages();
		}
	}

}

bool fl_DocSectionLayout::doclistener_changeStrux(const PX_ChangeRecord_StruxChange * pcrxc)
{
	UT_ASSERT(pcrxc->getType()==PX_ChangeRecord::PXT_ChangeStrux);

	PT_AttrPropIndex IndexOld = getAttrPropIndex();
	
	setAttrPropIndex(pcrxc->getIndexAP());

	const PP_AttrProp * pAP1;
	getDocument()->getAttrProp(IndexOld, &pAP1);

	const PP_AttrProp * pAP2;
	getDocument()->getAttrProp(pcrxc->getIndexAP(), &pAP2);

	if(!pAP1 || !pAP2)
	{
		getDocLayout()->rebuildFromHere(this);
	}

	const XML_Char * prop = "dom-dir";
	const XML_Char * val1 = NULL;
	const XML_Char * val2 = NULL;

	pAP1->getProperty(prop, val1);
	pAP2->getProperty(prop, val2);

	if(!val1 || !val2 || strcmp(val1, val2))
	{
		lookupProperties();
		fl_ContainerLayout * pCL = getFirstLayout();
		while(pCL)
		{
			pCL->lookupProperties();
			pCL = pCL->getNext();
		}
		
		getDocLayout()->rebuildFromHere(this);
	}
	
	
	return true;
}

void fl_DocSectionLayout::updateDocSection(void)
{

	const PP_AttrProp* pAP = NULL;
	getAP(pAP);
	UT_return_if_fail(pAP);

	const XML_Char* pszSectionType = NULL;
	pAP->getAttribute("type", pszSectionType);
	lookupProperties();

	// clear all the columns
    // Assume that all columns and formatting have already been removed via a collapseDocSection()
    //

	/*
	  TODO to more closely mirror the architecture we're using for BlockLayout, this code
	  should probably just set a flag, indicating the need to reformat this section.  Then,
	  when it's time to update everything, we'll actually do the format.
	*/

	FV_View * pView = m_pLayout->getView();
	if(pView)
	{
		pView->setScreenUpdateOnGeneralUpdate(false);
	}
	setNeedsSectionBreak(true,NULL);
	format();
	checkAndRemovePages();
	formatAllHdrFtr();
	markAllRunsDirty();

	if(pView)
	{
		pView->setScreenUpdateOnGeneralUpdate(true);
	}

//	if (pView)
//	{
//		pView->updateScreen(false);
//		pView->notifyListeners(AV_CHG_TYPING | AV_CHG_FMTSECTION);
//	}

	return;
}

void fl_DocSectionLayout::_lookupProperties(const PP_AttrProp* pSectionAP)
{
	UT_return_if_fail(pSectionAP);
// Now turn off the HdrFtr size change locks.

	m_iNewHdrHeight = 0;
	m_iNewFtrHeight = 0;
	getDocument()->setNewHdrHeight(0);
	getDocument()->setNewFtrHeight(0);
	m_sHdrFtrChangeProps.clear();


	/*
	  TODO shouldn't we be using PP_evalProperty like
	  the blockLayout does?

	  Yes, since PP_evalProperty does a fallback to the
	  last-chance defaults, whereas the code below is
	  hard-coding its own defaults.  Bad idea.
	*/

	const char* pszNumColumns = NULL;
	pSectionAP->getProperty("columns", (const XML_Char *&)pszNumColumns);
	if (pszNumColumns && pszNumColumns[0])
	{
		m_iNumColumns = atoi(pszNumColumns);
	}
	else
	{
		m_iNumColumns = 1;
	}

	const char* pszColumnGap = NULL;
	pSectionAP->getProperty("column-gap", (const XML_Char *&)pszColumnGap);
	if (pszColumnGap && pszColumnGap[0])
	{
		m_iColumnGap = UT_convertToLogicalUnits(pszColumnGap);
	}
	else
	{
		m_iColumnGap = UT_convertToLogicalUnits("0.25in");
	}
	UT_ASSERT(m_iColumnGap < 2000000);
	const char* pszColumnLineBetween = NULL;
	pSectionAP->getProperty("column-line", (const XML_Char *&)pszColumnLineBetween);
	if (pszColumnLineBetween && pszColumnLineBetween[0])
	{
		m_bColumnLineBetween = (strcmp(pszColumnLineBetween, "on") == 0) ? true : false;
	}
	else
	{
		m_bColumnLineBetween = false;
	}

	/* column-order */
	//we use the mechanism used by BlockLayout, since otherwise we
	//cannot recode the default value
	const PP_AttrProp * pSpanAP = NULL;
	const PP_AttrProp * pBlockAP = NULL;

	const char * pszColumnOrder = PP_evalProperty("dom-dir",pSpanAP,pBlockAP,pSectionAP,m_pDoc,false);
    UT_DEBUGMSG(("column order: %s\n", pszColumnOrder));

	FV_View * pView = m_pLayout->getView();

	if(pView && pView->getBidiOrder() != FV_Order_Visual)
	{
		if(pView->getBidiOrder() == FV_Order_Logical_LTR)
			m_iColumnOrder = 0;
		else
			m_iColumnOrder = 0;
	}
	else if(pszColumnOrder && pszColumnOrder[0])
	{
		m_iColumnOrder = strcmp(pszColumnOrder, "ltr")	? 1 : 0;
	}
	else
	{
		m_iColumnOrder = 0;
	}

	const char* pszSpaceAfter = NULL;
	pSectionAP->getProperty("section-space-after", (const XML_Char *&)pszSpaceAfter);
	if (pszSpaceAfter && pszSpaceAfter[0])
	{
		m_iSpaceAfter = UT_convertToLogicalUnits(pszSpaceAfter);
	}
	else
	{
		m_iSpaceAfter = UT_convertToLogicalUnits("0in");
	}

	const char* pszRestart = NULL;
	pSectionAP->getProperty("section-restart", (const XML_Char *&)pszRestart);
	if (pszRestart && pszRestart[0])
	{
		m_bRestart = (UT_strcmp(pszRestart,"1")==0);
	}
	else
	{
		m_bRestart = false;
	}

	const char* pszRestartValue = NULL;
	pSectionAP->getProperty("section-restart-value", (const XML_Char *&)pszRestartValue);
	if (pszRestartValue && pszRestartValue[0])
	{
		m_iRestartValue = atoi(pszRestartValue);
	}
	else
	{
		m_iRestartValue = 1;
	}

	const char* pszLeftMargin = NULL;
	const char* pszTopMargin = NULL;
	const char* pszRightMargin = NULL;
	const char* pszBottomMargin = NULL;
	const char* pszFooterMargin = NULL;
	const char* pszHeaderMargin = NULL;
	const char* pszMaxColumnHeight = NULL;
	pSectionAP->getProperty("page-margin-left", (const XML_Char *&)pszLeftMargin);
	pSectionAP->getProperty("page-margin-top", (const XML_Char *&)pszTopMargin);
	pSectionAP->getProperty("page-margin-right", (const XML_Char *&)pszRightMargin);
	pSectionAP->getProperty("page-margin-bottom", (const XML_Char *&)pszBottomMargin);
	pSectionAP->getProperty("page-margin-footer", (const XML_Char *&)pszFooterMargin);
	pSectionAP->getProperty("page-margin-header", (const XML_Char *&)pszHeaderMargin);


	const XML_Char * szRulerUnits;
	UT_Dimension dim;
	if (XAP_App::getApp()->getPrefsValue(AP_PREF_KEY_RulerUnits,&szRulerUnits))
		dim = UT_determineDimension(szRulerUnits);
	else
		dim = DIM_IN;

	UT_String defaultMargin;
	switch(dim)
	{
	case DIM_IN:
		defaultMargin = "1.0in";
		break;

	case DIM_CM:
		defaultMargin = "2.54cm";
		break;

	case DIM_PI:
		defaultMargin = "6.0pi";
		break;

	case DIM_PT:
		defaultMargin= "72.0pt";
		break;

	case DIM_MM:
		defaultMargin= "25.4mm";
		break;

		// TODO: PX, and PERCENT
		// let them fall through to the default now
		// and we don't use them anyway
#if 0
	case DIM_PX:
	case DIM_PERCENT:
#endif
	case DIM_none:
	default:
		defaultMargin = "1.0in";	// TODO: what to do with this.
		break;

	}

	if(pszLeftMargin && pszLeftMargin[0])
	{
		m_iLeftMargin = UT_convertToLogicalUnits(pszLeftMargin);
		m_dLeftMarginUserUnits = UT_convertDimensionless(pszLeftMargin);
	}
	else
	{
		m_iLeftMargin = UT_convertToLogicalUnits(defaultMargin.c_str());
		m_dLeftMarginUserUnits = UT_convertDimensionless(defaultMargin.c_str());
	}

	if(pszTopMargin && pszTopMargin[0])
	{
		m_iTopMargin = UT_convertToLogicalUnits(pszTopMargin);
		m_dTopMarginUserUnits = UT_convertDimensionless(pszTopMargin);
	}
	else
	{
		m_iTopMargin = UT_convertToLogicalUnits(defaultMargin.c_str());
		m_dTopMarginUserUnits = UT_convertDimensionless(defaultMargin.c_str());
	}

	if(pszRightMargin && pszRightMargin[0])
	{
		m_iRightMargin = UT_convertToLogicalUnits(pszRightMargin);
		m_dRightMarginUserUnits = UT_convertDimensionless(pszRightMargin);
	}
	else
	{
		m_iRightMargin = UT_convertToLogicalUnits(defaultMargin.c_str());
		m_dRightMarginUserUnits = UT_convertDimensionless(defaultMargin.c_str());
	}

	if(pszBottomMargin && pszBottomMargin[0])
	{
		m_iBottomMargin = UT_convertToLogicalUnits(pszBottomMargin);
		m_dBottomMarginUserUnits = UT_convertDimensionless(pszBottomMargin);
	}
	else
	{
		m_iBottomMargin = UT_convertToLogicalUnits(defaultMargin.c_str());
		m_dBottomMarginUserUnits = UT_convertDimensionless(defaultMargin.c_str());
	}

	if(pszFooterMargin && pszFooterMargin[0])
	{
		m_iFooterMargin = UT_convertToLogicalUnits(pszFooterMargin);
		m_dFooterMarginUserUnits = UT_convertDimensionless(pszFooterMargin);
	}
	else
	{
		m_iFooterMargin = UT_convertToLogicalUnits("0.0in");
		m_dFooterMarginUserUnits = UT_convertDimensionless("0.0in");
	}

	if(pszHeaderMargin && pszHeaderMargin[0])
	{
		m_iHeaderMargin = UT_convertToLogicalUnits(pszHeaderMargin);
		m_dHeaderMarginUserUnits = UT_convertDimensionless(pszHeaderMargin);
	}
	else
	{
		m_iHeaderMargin = UT_convertToLogicalUnits("0.0in");
		m_dHeaderMarginUserUnits = UT_convertDimensionless("0.0in");
	}

	pSectionAP->getProperty("section-max-column-height", (const XML_Char *&)pszMaxColumnHeight);
	if (pszMaxColumnHeight && pszMaxColumnHeight[0])
	{
		m_iMaxSectionColumnHeight = UT_convertToLogicalUnits(pszMaxColumnHeight);
	}
	else
	{
		m_iMaxSectionColumnHeight = UT_convertToLogicalUnits("0in");
	}

	const XML_Char * pszFootnoteLine = NULL;
	pSectionAP->getProperty("section-footnote-line-thickness", (const XML_Char *&)pszFootnoteLine);
	if (pszFootnoteLine && pszFootnoteLine[0])
	{
		m_iFootnoteLineThickness = UT_convertToLogicalUnits(pszFootnoteLine);
	}
	else
	{
		m_iFootnoteLineThickness = UT_convertToLogicalUnits("0.005in");
	}


	const XML_Char * pszFootnoteYoff = NULL;
	pSectionAP->getProperty("section-footnote-yoff", (const XML_Char *&)pszFootnoteYoff);
	if (pszFootnoteYoff && pszFootnoteYoff[0])
	{
		m_iFootnoteYoff = UT_convertToLogicalUnits(pszFootnoteYoff);
	}
	else
	{
		m_iFootnoteYoff = UT_convertToLogicalUnits("0.01in");
	}

	const XML_Char * pszDataID = NULL;
	pSectionAP->getAttribute(PT_STRUX_IMAGE_DATAID, (const XML_Char *&)pszDataID);
	DELETEP(m_pGraphicImage);
	DELETEP(m_pImageImage);
	if(pszDataID && *pszDataID)
	{
		m_pGraphicImage = FG_Graphic::createFromStrux(this);
	}
	setPaperColor();
	m_bForceNewPage = false;
}

UT_sint32 fl_DocSectionLayout::getTopMargin(void) const
{
	return m_iTopMargin;
}

UT_sint32 fl_DocSectionLayout::getBottomMargin(void) const
{
	return m_iBottomMargin;
}

/*!
 * Set the color of the background paper in the following order of precedence
 * 1. If The section level proper "background-color" is present and is
 *    not transparent use that.
 * 2. If this section is being displayed to the screen use the
 *     ColorForTransparency preference item color.
 * 3. Otherwise use white
 */
void fl_DocSectionLayout::setPaperColor(void)
{
	const PP_AttrProp* pSectionAP = NULL;
	getAP(pSectionAP);
	UT_return_if_fail(pSectionAP);

	const char* pszClrPaper = NULL;
	pSectionAP->getProperty("background-color", (const XML_Char *&)pszClrPaper);
	FV_View * pView = m_pLayout->getView();
	if(pszClrPaper && UT_strcmp(pszClrPaper,"transparent") != 0)
	{
		m_sPaperColor = pszClrPaper;
		m_sScreenColor.clear();
	}
	else if( pView && pView->getGraphics()->queryProperties(GR_Graphics::DGP_SCREEN) )
	{
		XAP_App * pApp = pView->getApp();
		XAP_Prefs * pPrefs = pApp->getPrefs();
		const XML_Char * pszTransparentColor = NULL;
		pPrefs->getPrefsValue(static_cast<const XML_Char *>(XAP_PREF_KEY_ColorForTransparent),&pszTransparentColor);
		m_sPaperColor.clear();
		m_sScreenColor = pszTransparentColor;
	}
	else
	{
		m_sPaperColor.clear();
		m_sScreenColor.clear();
	}
}

/*!
 * Delete Empty Column containers in this section.
 */
void fl_DocSectionLayout::deleteEmptyColumns(void)
{
	fp_Column* pCol = m_pFirstColumn;
	while (pCol)
	{
		if (pCol->getLeader() == pCol)
		{
			fp_Column* pCol2 = pCol;
			bool bAllEmpty = true;
			fp_Column* pLastInGroup = NULL;

			while (pCol2)
			{
				if (!pCol2->isEmpty())
				{
					bAllEmpty = false;
				}

				if (!(pCol2->getFollower()))
				{
					pLastInGroup = pCol2;
				}

				pCol2 = pCol2->getFollower();
			}

			if (bAllEmpty)
			{
				UT_ASSERT(pLastInGroup);
				if(pCol->getPage() != NULL)
				{
					pCol->getPage()->removeColumnLeader(pCol);
				}
				if (pCol == m_pFirstColumn)
				{
					m_pFirstColumn = static_cast<fp_Column *>(pLastInGroup->getNext());
					UT_ASSERT(m_pFirstColumn);
				}

				if (pLastInGroup == m_pLastColumn)
				{
					m_pLastColumn = static_cast<fp_Column *>(pCol->getPrev());
				}

				if (pCol->getPrev())
				{
					pCol->getPrev()->setNext(pLastInGroup->getNext());
				}

				if (pLastInGroup->getNext())
				{
					pLastInGroup->getNext()->setPrev(pCol->getPrev());
				}

				fp_Column* pCol3 = pCol;
				pCol = static_cast<fp_Column *>(pLastInGroup->getNext());
				while (pCol3)
				{
					fp_Column* pNext = pCol3->getFollower();

					delete pCol3;

					pCol3 = pNext;
				}
			}
			else
			{
				pCol = static_cast<fp_Column *>(pLastInGroup->getNext());
			}
		}
		else
		{
			pCol = static_cast<fp_Column *>(pCol->getNext());
		}
	}
}

UT_uint32 fl_DocSectionLayout::getNumColumns(void) const
{
	return m_iNumColumns;
}

UT_sint32 fl_DocSectionLayout::getColumnGap(void) const
{
	UT_ASSERT( m_iColumnGap < 200000);
	return m_iColumnGap;
}

UT_uint32 fl_DocSectionLayout::getColumnOrder(void) const
{
	return m_iColumnOrder;
}

void fl_DocSectionLayout::deleteBrokenTablesFromHere(fl_ContainerLayout * pTL)
{
	UT_DEBUGMSG(("Doing delete broken tables from here \n"));
	if(m_bDeleteingBrokenContainers)
	{
		return;
	}
	if(getDocLayout()->isLayoutDeleting())
	{
	        return;
	}
	m_bDeleteingBrokenContainers = true;
	if(pTL == NULL)
	{
		pTL = getFirstLayout();
	}
	fl_ContainerLayout * pCL = pTL->getNext();
	while(pCL != NULL)
	{
		if(pCL->getContainerType() == FL_CONTAINER_TABLE)
		{
			fl_TableLayout * pTabL = static_cast<fl_TableLayout *>(pCL);
			fp_TableContainer * pTabC = static_cast<fp_TableContainer *>(pTabL->getFirstContainer());
			if(pTabC != NULL)
			{
				pTabC->deleteBrokenTables(true);
			}
		}
		else if(pCL->getContainerType() == FL_CONTAINER_TOC)
		{
			fl_TOCLayout * pTOCL = static_cast<fl_TOCLayout *>(pCL);
			fp_TOCContainer * pTOC = static_cast<fp_TOCContainer *>(pTOCL->getFirstContainer());
			if(pTOC != NULL)
			{
				pTOC->deleteBrokenTOCs(true);
			}
		}
		pCL = pCL->getNext();
	}
	m_bDeleteingBrokenContainers = false;
}


fl_DocSectionLayout* fl_DocSectionLayout::getNextDocSection(void) const
{
	fl_DocSectionLayout * pSL = static_cast<fl_DocSectionLayout *>(getNext());
	if(pSL != NULL && pSL->getType()== FL_SECTION_DOC)
		return pSL;
	return NULL;
}

fl_DocSectionLayout* fl_DocSectionLayout::getPrevDocSection(void) const
{
	fl_DocSectionLayout * pSL = static_cast<fl_DocSectionLayout *>(getPrev());
	while(pSL != NULL && pSL->getType()!= FL_SECTION_DOC)
	{
		pSL = static_cast<fl_DocSectionLayout *>(pSL->getPrev());
	}
	return pSL;
}

void fl_DocSectionLayout::collapse(void)
{
	UT_DEBUGMSG(("DocSectionLayout: Collapsing all content in %x \n",this));
	fp_Column* pCol = m_pFirstColumn;
	m_bDoingCollapse = true;
	while (pCol)
	{
		pCol->clearScreen();

		pCol = static_cast<fp_Column *>(pCol->getNext());
	}
	//
	// Clear the header/footers too
	//
	UT_GenericVector<fl_HdrFtrSectionLayout*> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	fl_HdrFtrSectionLayout * pHdrFtr = NULL;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		pHdrFtr = vecHdrFtr.getNthItem(i);
		pHdrFtr->clearScreen();
	}
	//
	// Collapse the header/footers now
	//
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		pHdrFtr = vecHdrFtr.getNthItem(i);
		pHdrFtr->collapse();
	}
	// remove all the columns from their pages
	pCol = m_pFirstColumn;
	while (pCol)
	{
//
// The endnote in a column may originate from a totally different 
// docsection. We must collapse these endnotes first
//
		pCol->collapseEndnotes();
		if (pCol->getLeader() == pCol)
		{
			pCol->getPage()->removeColumnLeader(pCol);
		}

		pCol = static_cast<fp_Column *>(pCol->getNext());
	}

	// get rid of all the layout information for every block
	fl_ContainerLayout*	pBL = getFirstLayout();
	while (pBL)
	{
		if(pBL->getContainerType() == FL_CONTAINER_ENDNOTE)
		{
			fp_Container * pCon = pBL->getFirstContainer();
			UT_ASSERT_HARMLESS( pCon );
			if(pCon)
			{
				fp_Column * pCol = static_cast<fp_Column *>(pCon->getColumn());
				UT_DEBUGMSG(("Got and endnote in this section!! \n"));
				UT_DEBUGMSG(("Remove Endnote con %x from col %x \n",pCon,pCol));
				pCol->removeContainer(pCon);
			}
		}
		pBL->collapse();
		pBL = pBL->getNext();

	}

	// delete all our columns
	pCol = m_pFirstColumn;
	while (pCol)
	{
		if(pCol->countCons() > 0)
		{
			UT_uint32 i = 0;
			for(i=0; i<pCol->countCons();i++)
			{
				fp_ContainerObject * pCon = pCol->getNthCon(i);
				UT_DEBUGMSG(("Still have a pointer to a container %x -removing pointer \n",pCon));
				pCol->justRemoveNthCon(i);
			}
		}
		fp_Column* pNext = static_cast<fp_Column *>(pCol->getNext());
		delete pCol;
		pCol = pNext;	
	}
	m_pFirstColumn = NULL;
	m_pLastColumn = NULL;
	setFirstEndnoteContainer(NULL);
	setLastEndnoteContainer(NULL);
//
// Remove all the empty pages thus created. Don't notify of the deletion though.
//
	fp_Page* pPage = m_ColumnBreaker.getStartPage();
	if (pPage && pPage->isEmpty())
		m_ColumnBreaker.setStartPage(NULL);
	getDocLayout()->deleteEmptyPages(true);
//
// This Doc Section No longer owns pages so this becomes NULL
//
	m_pFirstOwnedPage = NULL;
	m_bDoingCollapse = false;
}

bool fl_DocSectionLayout::doclistener_deleteStrux(const PX_ChangeRecord_Strux * pcrx)
{
	UT_ASSERT(pcrx->getType()==PX_ChangeRecord::PXT_DeleteStrux);
	UT_ASSERT(pcrx->getStruxType()==PTX_Section);
	UT_DEBUGMSG(("Doing Section delete \n"));
	fl_DocSectionLayout* pPrevSL = getPrevDocSection();
	UT_DEBUGMSG(("Deleting DocSec %x Prev DocSec %x \n",this,pPrevSL));
	if (!pPrevSL)
	{
		// TODO shouldn't this just assert?
		UT_DEBUGMSG(("no prior SectionLayout"));
		return false;
	}

//
// Collapse previous section too. We need this so it can be rebuilt properly.
//
	pPrevSL->collapse();

	// clear all the columns
	collapse();

	DELETEP(m_pHeaderSL);
	DELETEP(m_pHeaderEvenSL);
	DELETEP(m_pHeaderFirstSL);
	DELETEP(m_pHeaderLastSL);
	DELETEP(m_pFooterSL);
	DELETEP(m_pFooterEvenSL);
	DELETEP(m_pFooterFirstSL);
	DELETEP(m_pFooterLastSL);

//
// Collapse the subsequent sections too. These will be reformatted in a few lines.
//
	fl_DocSectionLayout * pDSL = getNextDocSection();
	while(pDSL != NULL)
	{
		pDSL->collapse();
		pDSL = pDSL->getNextDocSection();
	}
//
// OK set the links and move all blocks in this section into the previous section.
	if(getFirstLayout())
	{
		fl_ContainerLayout * pBCur = getFirstLayout();
		fl_ContainerLayout * pBPrev = pPrevSL->getLastLayout();
		UT_ASSERT(pBCur && pBPrev);
	
		pBCur->setPrev(pBPrev);
		pBPrev->setNext(pBCur);
		while(pBCur != NULL)
		{
			xxx_UT_DEBUGMSG(("updating block %x \n",pBCur));
			pBCur->setContainingLayout(pPrevSL);
			if(pBCur->getContainerType() == FL_CONTAINER_BLOCK)
			{
				static_cast<fl_BlockLayout *>(pBCur)->
					setSectionLayout(pPrevSL);
			}
			if(pBCur->getContainerType() == FL_CONTAINER_FOOTNOTE)
			{
				static_cast<fl_FootnoteLayout *>(pBCur)->
					setDocSectionLayout(pPrevSL);
			}
			if(pBCur->getContainerType() == FL_CONTAINER_ENDNOTE)
			{
				static_cast<fl_EndnoteLayout *>(pBCur)->
					setDocSectionLayout(pPrevSL);
			}

			pPrevSL->setLastLayout(pBCur);
			pBCur = pBCur->getNext();
		}
	}
	setFirstLayout(NULL);
	setLastLayout(NULL);

//
// Get this before we remove this section from the run list!
//
    pDSL = getNextDocSection();
	m_pLayout->removeSection(this);
	pPrevSL->format();

	FV_View* pView = m_pLayout->getView();
	if (pView)
  	{
  		pView->_setPoint(pcrx->getPosition());
  	}
//
// Update the following sections.
//
	while(pDSL != NULL)
	{
		pDSL->updateDocSection();
		pDSL = pDSL->getNextDocSection();
	}
	delete this;			// TODO whoa!  this construct is VERY dangerous.

	return true;
}

void fl_DocSectionLayout::addOwnedPage(fp_Page* pPage)
{
	// TODO do we really need the vecOwnedPages member? YES!!!

	if(m_pFirstOwnedPage == NULL)
		m_pFirstOwnedPage = pPage;
	fp_Page * pPrev = m_pFirstOwnedPage;
	pPage->getFillType()->setDocLayout(getDocLayout());
	setImageWidth(pPage->getWidth());
	setImageHeight(pPage->getHeight());
	if(m_pGraphicImage)
	{
		if(m_pImageImage == NULL)
		{
			const PP_AttrProp * pAP = NULL;
			getAP(pAP);
			GR_Image * pImage = m_pGraphicImage->generateImage(getDocLayout()->getGraphics(),pAP,pPage->getWidth(),pPage->getHeight());
			m_iGraphicTick = getDocLayout()->getGraphicTick();
			UT_Rect rec(0,0,pPage->getWidth(),pPage->getHeight());
			pImage->scaleImageTo(getDocLayout()->getGraphics(),rec);
			m_pImageImage = pImage;
		}
		pPage->getFillType()->setImagePointer(&m_pGraphicImage,&m_pImageImage);
	}
	else if(m_sPaperColor.size() > 0)
	{
		pPage->getFillType()->setColor(m_sPaperColor.c_str());
	}
	else if(m_sScreenColor.size() > 0)
	{
		pPage->getFillType()->setTransColor(m_sScreenColor.c_str());
		pPage->getFillType()->markTransparentForPrint();
	}		

//
// The addPage methods will add the page to the correct HdrFtrSL.
//
	UT_GenericVector<fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		if(pHdrFtr->getHFType() < FL_HDRFTR_FOOTER)
		{
			if(pPrev && pPrev->getOwningSection() == this && pPrev->getHdrFtrP(FL_HDRFTR_HEADER) == NULL )
				prependOwnedHeaderPage(pPrev);

			pHdrFtr->addPage(pPage);
		}
		else
		{
			if(pPrev && pPrev->getOwningSection() == this && pPrev->getHdrFtrP(FL_HDRFTR_FOOTER) == NULL)
			{
				prependOwnedFooterPage(pPrev);
			}
			pHdrFtr->addPage(pPage);
		}
	}
	fl_DocSectionLayout * pDSL = this;
	while(pDSL != NULL)
	{
		pDSL->checkAndRemovePages();
		pDSL->addValidPages();
		pDSL = pDSL->getNextDocSection();
	}
}

void fl_DocSectionLayout::prependOwnedHeaderPage(fp_Page* pPage)
{
	//
	// Skip back through the pages until the first owned page of this section
	//
	fp_Page * pPrev = pPage->getPrev();
	if(pPrev && pPrev->getOwningSection() == this && pPrev->getHdrFtrP(FL_HDRFTR_HEADER) == NULL)
	{
		prependOwnedHeaderPage(pPrev);
	}
//
// The addPage methods will add the page to the correct HdrFtrSL.
//
	UT_GenericVector<fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		if(pHdrFtr->getHFType() < FL_HDRFTR_FOOTER)
		{
			xxx_UT_DEBUGMSG(("SEVIOR: prepending page %x \n",pPage));
			pHdrFtr->addPage(pPage);
		}
	}
}


void fl_DocSectionLayout::prependOwnedFooterPage(fp_Page* pPage)
{
	//
	// Skip back through the pages until the first owned page of this section
	//
	fp_Page * pPrev = pPage->getPrev();
	if(pPrev && pPrev->getOwningSection() == this && pPrev->getHdrFtrP(FL_HDRFTR_FOOTER) == NULL)
	{
		prependOwnedFooterPage(pPrev);
	}
//
// The addPage methods will add the page to the correct HdrFtrSL.
//
	UT_GenericVector<fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		if(pHdrFtr->getHFType() >= FL_HDRFTR_FOOTER)
		{
			pHdrFtr->addPage(pPage);
		}
	}
}


/*!
 * This fills a vector with all the valid header/footers.
 */
void fl_DocSectionLayout::getVecOfHdrFtrs(UT_GenericVector<fl_HdrFtrSectionLayout *> * vecHdrFtr)
{
	vecHdrFtr->clear();
	if (m_pHeaderFirstSL != NULL)
	{
		vecHdrFtr->addItem(m_pHeaderFirstSL);
	}
	if (m_pHeaderLastSL  != NULL)
	{
		vecHdrFtr->addItem(m_pHeaderLastSL);
	}
	if (m_pHeaderEvenSL  != NULL)
	{
		vecHdrFtr->addItem(m_pHeaderEvenSL);
	}
	if (m_pHeaderSL  != NULL)
	{
		vecHdrFtr->addItem(m_pHeaderSL);
	}
	if (m_pFooterFirstSL != NULL)
	{
		vecHdrFtr->addItem(m_pFooterFirstSL);
	}
	if (m_pFooterLastSL != NULL)
	{
		vecHdrFtr->addItem(m_pFooterLastSL);
	}
	if (m_pFooterEvenSL != NULL)
	{
		vecHdrFtr->addItem(m_pFooterEvenSL);
	}
	if (m_pFooterSL != NULL)
	{
		vecHdrFtr->addItem(m_pFooterSL);
	}
}

/*!
 * This method formats all the header/footers
 */
void fl_DocSectionLayout::formatAllHdrFtr(void)
{
	xxx_UT_DEBUGMSG(("SEVIOR: Doing formatAllHdrFtr \n"));
	UT_GenericVector<fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		xxx_UT_DEBUGMSG(("SEVIOR: Doing formatting %x in formatAllHdrFtr \n",pHdrFtr));
		pHdrFtr->format();
	}
}

/*!
 * This method checks each header for valid pages and removes the page if it's not
 * valid. ie it remove odd pages from even headers etc.
 */
void fl_DocSectionLayout::checkAndRemovePages(void)
{
	UT_GenericVector <fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		pHdrFtr->checkAndRemovePages();
	}
}


/*!
 * This method adds valid pages to every valid header/footer in the docsection if
 * they're not there already.
 */
void fl_DocSectionLayout::addValidPages(void)
{
	UT_GenericVector<fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		pHdrFtr->addValidPages();
	}
}

/*!
 * This method deletes the owned page from the DocSectionLayout and all
 * the header files.
 */
void fl_DocSectionLayout::deleteOwnedPage(fp_Page* pPage, bool bReallyDeleteIt)
{
	UT_GenericVector<fl_HdrFtrSectionLayout *> vecHdrFtr;
	getVecOfHdrFtrs( &vecHdrFtr);
	UT_uint32 i = 0;
	UT_DEBUGMSG(("Delete Owned Page %x \n",pPage));
	for(i = 0; i < vecHdrFtr.getItemCount(); i++)
	{
		fl_HdrFtrSectionLayout * pHdrFtr = vecHdrFtr.getNthItem(i);
		if(pHdrFtr->isPageHere(pPage))
		{
			pHdrFtr->deletePage(pPage);
			UT_DEBUGMSG(("Delete Owned Page %x from HdrFtr %x \n",pPage,pHdrFtr));
		}
	}
//
// Remove this page from the list of owned pages
//
	if(m_pFirstOwnedPage == pPage)
	{
		fp_Page * pNext = pPage->getNext();
		if(pNext && pNext->getOwningSection() == this)
		{
			m_pFirstOwnedPage = pNext;
		}
		else
		{
			m_pFirstOwnedPage = NULL;
		}
	}
	fl_DocSectionLayout * pDSL = this;
	if(!getDocLayout()->isLayoutDeleting() && bReallyDeleteIt)
	{
		UT_sint32 i = m_pLayout->findPage(pPage);
		if(i>0)
		{
			UT_DEBUGMSG(("fl_DocSec: deleting page %x ReallyDeleteIt %d \n",pPage,bReallyDeleteIt));
			m_pLayout->deletePage(pPage,true);
		}
		while(pDSL != NULL)
		{
			pDSL->checkAndRemovePages();
			pDSL->addValidPages();
			pDSL = pDSL->getNextDocSection();
		}
	}
}

/*!
 * This method returns true if the pPage pointer matches the header/footer type
 * given.
\param hfType The type of the header/Footer
\param fp_Page * pThisPage pointer to the page queried.
 */
bool fl_DocSectionLayout::isThisPageValid(HdrFtrType hfType, fp_Page * pThisPage)
{
	if (!m_pFirstOwnedPage)
		return false;

    // No header/footerness assigned yet. Page is invalid.
	if(hfType == FL_HDRFTR_NONE)
		return false;

	if(hfType == FL_HDRFTR_HEADER_FIRST ||
	   hfType == FL_HDRFTR_FOOTER_FIRST)
		return (pThisPage == m_pFirstOwnedPage);

//
// If there is a header page defined and this is a header page bail now!
//
	if ((pThisPage == m_pFirstOwnedPage) &&
		 ((m_pHeaderFirstSL && hfType < FL_HDRFTR_FOOTER) ||
		  (m_pFooterFirstSL && hfType >= FL_HDRFTR_FOOTER)))
		return false;

	fp_Page * pPage = m_pFirstOwnedPage;
	fp_Page * pNext = pPage->getNext();
	while(pNext && (pNext->getOwningSection() == this))
	{
		pPage = pNext;
		pNext = pNext->getNext();
	}

	if(hfType == FL_HDRFTR_HEADER_LAST ||
	   hfType == FL_HDRFTR_FOOTER_LAST)
	{
		return (pPage == pThisPage);
	}
//
// If there is a Last SL defined and this is the last page in the SLpage bail now!
//
	if ((pThisPage == pPage) &&
		 ((m_pHeaderLastSL && hfType < FL_HDRFTR_FOOTER) ||
		  (m_pFooterLastSL && hfType >= FL_HDRFTR_FOOTER)))
		return false;

	UT_uint32 i = 0;
	for(i=0; i < getDocLayout()->countPages(); i++)
	{
		if (getDocLayout()->getNthPage(i) == pThisPage)
			break;
	}

	UT_ASSERT(i < getDocLayout()->countPages());

	if((hfType == FL_HDRFTR_HEADER_EVEN) ||
	   (hfType == FL_HDRFTR_FOOTER_EVEN))
	{
		if(i % 2 == 0)
			return true;
		else
			return false;
	}
//
// If there is an Even SL defined and this is an even page in the SL page bail now!
//
	if ((i % 2 == 0) &&
		((m_pHeaderEvenSL && hfType < FL_HDRFTR_FOOTER) ||
		 (m_pFooterEvenSL && hfType >= FL_HDRFTR_FOOTER)))
		return false;

	return true; //if we're here all pages are valid.
}

void fl_DocSectionLayout::checkAndAdjustColumnGap(UT_sint32 iLayoutWidth)
{
	// Check to make sure column gap is not to wide to fit on the page with the
	// given number of columns.
	UT_sint32 minColumnWidth =0;
	UT_sint32 iColWidth= 0;
	if(m_iNumColumns > 1)
	{
		minColumnWidth = UT_convertToLogicalUnits("0.5in");	//TODO should this dimension be hard coded.
		iColWidth = (iLayoutWidth - static_cast<UT_sint32>(((m_iNumColumns - 1) * m_iColumnGap))) / static_cast<UT_sint32>(m_iNumColumns);

		if(iColWidth < minColumnWidth)
		{
			m_iColumnGap = (iLayoutWidth - minColumnWidth * static_cast<UT_sint32>(m_iNumColumns)) / (static_cast<UT_sint32>(m_iNumColumns) - 1);
		}

	}
	UT_ASSERT(m_iColumnGap < 2000000);
	if(m_iColumnGap < 30 || (m_iColumnGap > 200000))
	{
		m_iColumnGap = 30;
	}
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

class ABI_EXPORT _PageHdrFtrShadowPair
{
public:
	_PageHdrFtrShadowPair(void)
	   {
			m_pPage = NULL;
			m_pShadow = NULL;
		}
	virtual ~_PageHdrFtrShadowPair(void)
		{
			m_pPage = NULL;
			m_pShadow = NULL;
		}
	void setPage (fp_Page * pPage) { m_pPage = pPage;}
	void setShadow (fl_HdrFtrShadow * pShadow) { m_pShadow = pShadow;}
	fp_Page * getPage(void) const {return m_pPage;}
	fl_HdrFtrShadow * getShadow(void) const {return m_pShadow;}
private:
	fp_Page*			m_pPage;
	fl_HdrFtrShadow*	m_pShadow;
};


fl_HdrFtrSectionLayout::fl_HdrFtrSectionLayout(HdrFtrType iHFType, FL_DocLayout* pLayout, fl_DocSectionLayout* pDocSL, PL_StruxDocHandle sdh, PT_AttrPropIndex indexAP)
	: fl_SectionLayout(pLayout, sdh, indexAP, FL_SECTION_HDRFTR,FL_CONTAINER_HDRFTR,PTX_SectionHdrFtr,pDocSL),
	  m_pDocSL(pDocSL),
	  m_iHFType(iHFType),
	  m_pHdrFtrContainer(NULL)
{
	fl_Layout::setType(PTX_SectionHdrFtr); // Set the type of this strux
	UT_DEBUGMSG(("SEVIOR: Creating HFType =%d \n",m_iHFType));
//
// Since we're almost certainly removing blocks at the end of the doc, tell the
// view to remember the current position on the active view.
//
// 	FV_View * pView = m_pLayout->getView();
// 	if(pView && pView->isActive())
// 	{
// 		pView->markSavedPositionAsNeeded();
// 	}
}

fl_HdrFtrSectionLayout::~fl_HdrFtrSectionLayout()
{
	xxx_UT_DEBUGMSG(("SEVIOR: Deleting HFType =%d \n",m_iHFType));
	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = static_cast<_PageHdrFtrShadowPair*>(m_vecPages.getNthItem(i));

		delete pPair->getShadow();
	}
	_purgeLayout();
	DELETEP(m_pHdrFtrContainer);
//
// Take this section layout out of the linked list
//
	m_pLayout->removeHdrFtrSection(static_cast<fl_SectionLayout *>(this));
	m_pDocSL->removeFromUpdate(this);
//
// Null out pointer to this HdrFtrSection in the attached DocLayoutSection
//
	m_pDocSL->setHdrFtr(m_iHFType, NULL);
//
// Since we're almost certainly removing blocks at the end of the doc, tell the
// view to remember the current position on the active view.
//
// 	FV_View * pView = m_pLayout->getView();
// 	if(pView && pView->isActive())
// 	{
// 		pView->markSavedPositionAsNeeded();
// 	}
//
	UT_VECTOR_PURGEALL(_PageHdrFtrShadowPair*, m_vecPages);
}

/*!
 * This method removes all the lines and containers associated with the shadows
 * and the lines associated with this HdrFtrSectionLayout.
 *
 */
void fl_HdrFtrSectionLayout::collapse(void)
{
//
// If a view exists and we're editting a header footer take the pointer out of
// the header/footer. This will also clear the box around the header/footer
//
	FV_View * pView = m_pLayout->getView();
	if(pView && pView->isHdrFtrEdit())
	{
		pView->clearHdrFtrEdit();
		pView->warpInsPtToXY(0,0,false);
		pView->rememberCurrentPosition();
	}

	_localCollapse();
	UT_uint32 iCount = m_vecPages.getItemCount();
	UT_uint32 i;
	for (i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		fp_Page * ppPage = pPair->getPage();
		delete pPair->getShadow();
		ppPage->removeHdrFtr(getHFType());
		delete pPair;
	}
	m_vecPages.clear();
	DELETEP(m_pHdrFtrContainer);
}

/*!
 * This method removes the block pBlock from all the shadowLayouts.
 */
void fl_HdrFtrSectionLayout::collapseBlock(fl_ContainerLayout *pBlock)
{
	UT_uint32 iCount = m_vecPages.getItemCount();
	UT_uint32 i;
	for (i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		fl_ContainerLayout * pShadowBL = pPair->getShadow()->findMatchingContainer(pBlock);
		UT_ASSERT(pShadowBL);
		UT_DEBUGMSG(("Doing collapseBlock %x \n",pBlock));
		if(pShadowBL)
		{
			// In case we've never checked this one
			if(pShadowBL->getContainerType() == FL_CONTAINER_BLOCK)
			{
				m_pLayout->dequeueBlockForBackgroundCheck(static_cast<fl_BlockLayout *>(pShadowBL));
			}
			pPair->getShadow()->remove( pShadowBL);
			delete pShadowBL;
			pPair->getShadow()->format();
		}
	}
}

bool fl_HdrFtrSectionLayout::recalculateFields(UT_uint32 iUpdateCount)
{
	bool bResult = false;

	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		UT_ASSERT(pPair->getShadow());
		bResult = pPair->getShadow()->recalculateFields(iUpdateCount) || bResult;
	}
	return bResult;
}


fl_HdrFtrShadow * fl_HdrFtrSectionLayout::getFirstShadow(void)
{
	UT_uint32 iCount = m_vecPages.getItemCount();
	if(iCount != 0)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(0);
		return pPair->getShadow();
	}
	return NULL;
}

fp_Container* fl_HdrFtrSectionLayout::getFirstContainer() const
{
	return m_pHdrFtrContainer;
}


fp_Container* fl_HdrFtrSectionLayout::getLastContainer() const
{
	return m_pHdrFtrContainer;
}

fp_Container* fl_HdrFtrSectionLayout::getNewContainer(fp_Container * pFirstContainer)
{
	DELETEP(m_pHdrFtrContainer);
	UT_sint32 iWidth = m_pDocSL->getFirstContainer()->getPage()->getWidth(); // why is this different than the next one ?
	m_pHdrFtrContainer = static_cast<fp_Container *>(new fp_HdrFtrContainer(iWidth, static_cast<fl_SectionLayout *>(this)));
	return m_pHdrFtrContainer;
}

bool fl_HdrFtrSectionLayout::isPageHere(fp_Page * pPage)
{
	return (_findShadow(pPage) >=0 );
}

fl_HdrFtrShadow *  fl_HdrFtrSectionLayout::findShadow(fp_Page* pPage)
{
       UT_sint32 iPage = _findShadow(pPage);
       if(iPage < 0)
	        return NULL;
       _PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(iPage);
       return pPair->getShadow();
}

UT_sint32 fl_HdrFtrSectionLayout::_findShadow(fp_Page* pPage)
{
	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		if (pPair->getPage() == pPage)
		{
			return i;
		}
	}

	return -1;
}
/*!
 * This method converts a previously existing section to this header/footer.
 * Code liberally stolen from fl_DocSectionLayout::doclistener_deleteStrux
 \param fl_DocSectionLayout * pSL sectionlayout to be converted to a
 *     HdrFtrSectionLayout
*/
void fl_HdrFtrSectionLayout::changeIntoHdrFtrSection( fl_DocSectionLayout * pSL)
{
	fl_DocSectionLayout* pPrevSL = pSL->getPrevDocSection();
	UT_ASSERT(pPrevSL);
	// clear all the columns
	fp_Column* pCol =NULL;

	pCol = static_cast<fp_Column *>(pSL->getFirstContainer());
	while (pCol)
	{
		pCol->clearScreen();

		pCol = static_cast<fp_Column *>(pCol->getNext());
	}

	// remove all the columns from their pages
	pCol = static_cast<fp_Column *>(pSL->getFirstContainer());
	while (pCol)
	{
		if (pCol->getLeader() == pCol)
		{
			pCol->getPage()->removeColumnLeader(pCol);
		}

		pCol = static_cast<fp_Column *>(pCol->getNext());
	}


	// get rid of all the layout information for every block
	fl_ContainerLayout*	pBL = pSL->getFirstLayout();
	while (pBL)
	{
		pBL->collapse();

		pBL = pBL->getNext();
	}

	//
	// Change the section type
	//

	// transfer the Sections' blocks into this header/footer

	while (pSL->getFirstLayout())
	{
		pBL = pSL->getFirstLayout();
		pSL->remove(pBL);
		add(pBL);
		static_cast<fl_BlockLayout *>(pBL)->setSectionLayout(this);
		static_cast<fl_BlockLayout *>(pBL)->setHdrFtr();
	}
	//
	// Remove old section from the section linked list!!
	//
	m_pLayout->removeSection(pSL);
//
	DELETEP(pSL); // Old Section layout is totally gone
	//
	// Create and Format the shadows
	//
	format();

	// Finished! we now have a header/footer
}

/*!
 * Remove the strux identifing this as a seperate section has been deleted so
 * we have to remove this HdrFtrSectionLayout class and all the shadow sections
 * attached to it. The blocks in this class are moved to the DocSectionLayout
 * associated with this class.
 * I do this because I expect that this will be called as part
 * on an undo "Insert Header" command. The rest of the undo needs blocks to
 * delete so I'm putting them there to keep the rest of the undo code happy
\param pcrx the changerecord identifying this action as necesary.
\returns true
*/
bool fl_HdrFtrSectionLayout::doclistener_deleteStrux(const PX_ChangeRecord * pcr)
{
	UT_ASSERT(pcr->getType()==PX_ChangeRecord::PXT_DeleteStrux ||
			  pcr->getType()==PX_ChangeRecord::PXT_ChangeStrux);
	

	if(pcr->getType()==PX_ChangeRecord::PXT_ChangeStrux)
	{
		PX_ChangeRecord_StruxChange * pcrx  = (PX_ChangeRecord_StruxChange *) pcr;
		UT_ASSERT_HARMLESS( pcrx->isRevisionDelete() );
	}
	else
	{
		PX_ChangeRecord_Strux * pcrx  = (PX_ChangeRecord_Strux *) pcr;
		UT_ASSERT(pcrx->getStruxType()==PTX_SectionHdrFtr);
	}
	
//
// Get last doc section. Move all the blocks from here to there after deleting
// this strux.
//
	fl_DocSectionLayout* pPrevSL = m_pDocSL;
	if (!pPrevSL)
	{
		UT_DEBUGMSG(("no prior SectionLayout"));
		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
	}
//
// Get rid of all the shadows, all the containers, and all the layout
// information for all the blocks.
//
	collapse();
//
// Now copy these line-less blocks into the previous docSectionLayout.
// Note: I expect that these blocks will be deleted by a later delete strux
// on these blocks.
//
	fl_ContainerLayout * pBL = NULL;
	while (getFirstLayout())
	{
		pBL = getFirstLayout();
		remove(pBL);
		pPrevSL->add(pBL);
	}
//
// Remove the pointer to this hdrftr
//
//
// Null out pointer to this HdrFtrSection in the attached DocLayoutSection
// This prevent a new page being created in the format statement that follows.
//
	m_pDocSL->setHdrFtr(m_iHFType, NULL);
//
// Format the new section containing the blocks.
//
	pPrevSL->format();
//
// Finally delete this HdrFtrSectionLayout. This could be done the docListener
// class but here I'm following the convention for the DocSectionLayout. It
// works there so I hope it works here. The HdrFtrSection destructor takes care
// of the details of unlinking the section etc.
//
	delete this;
	return true;
}

void fl_HdrFtrSectionLayout::addPage(fp_Page* pPage)
{
//
//  Sevior:
//  This triggers if we're rebuilding a section before page is defined like in a section change
//  strux. Reinstate if needed to find other bugs.
//	UT_ASSERT(0 > _findShadow(pPage));
//
//
// This might actually be called before we have any content to put in it. If so
// return and hope we get called later.
//
	if(getFirstLayout() == NULL)
	{
		UT_DEBUGMSG(("HdrFtr BUG. No content but we're asking to create a shadow !! \n"));
		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
		return;
	}

	if(_findShadow(pPage) > -1)
		return;
//
// Check this page is valid for this type of hdrftr
//
	if(!getDocSectionLayout()->isThisPageValid(m_iHFType, pPage))
	{
		return;
	}
	//
	// see if this page has a shadow attached already. This can happen
    // is a page goes from being odd to even.
	//
	fp_ShadowContainer* pOldShadow = pPage->getHdrFtrP(m_iHFType);
	//
	// If so remove it.
	//
	if(pOldShadow != NULL)
	{
		pOldShadow->getHdrFtrSectionLayout()->deletePage(pPage);
		pPage->removeHdrFtr(m_iHFType);
	}

	_PageHdrFtrShadowPair* pPair = new _PageHdrFtrShadowPair();
	UT_return_if_fail( pPair );
	
	// TODO outofmem
	UT_DEBUGMSG(("SEVIOR: Add page %x to pair %x \n",pPage,pPair));
	pPair->setPage(pPage);
	pPair->setShadow(new fl_HdrFtrShadow(m_pLayout, pPage, this, getStruxDocHandle(), m_apIndex));
	//
	// Make sure we register the shadow before populating it.
	//
	m_vecPages.addItem(pPair);
	//
	// Populate the shadow
	//
	fl_ShadowListener* pShadowListener = new fl_ShadowListener(this, pPair->getShadow());
	UT_return_if_fail( pShadowListener );
	UT_DEBUGMSG(("shadow listener %x created \n",pShadowListener));
//
// Populate with just this section so find the start and end of it
//
	PT_DocPosition posStart,posEnd,posDocEnd;
	posStart = getFirstLayout()->getPosition(true) - 1;
	posEnd = getLastLayout()->getPosition(false);
	fp_Run * pRun = getLastLayout()->getFirstRun();

	if(pRun)
	{
		while(pRun->getNextRun() != NULL)
		{
			pRun = pRun->getNextRun();
		}
		posEnd += pRun->getBlockOffset();
	}
	else
	{
		UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
	}
	
	PL_StruxDocHandle sdh=NULL;
	bool bres;
	bres = m_pDoc->getStruxOfTypeFromPosition(posEnd, PTX_Block, &sdh);
	m_pDoc->getBounds(true,posDocEnd);
	while(bres && sdh == getLastLayout()->getStruxDocHandle()
		  && posEnd <= posDocEnd)
	{
		posEnd++;
		bres = m_pDoc->getStruxOfTypeFromPosition(posEnd, PTX_Block, &sdh);
	}
	posEnd--;
	UT_ASSERT(posEnd > posStart);
	PD_DocumentRange * docRange = new PD_DocumentRange(m_pDoc,posStart,posEnd);
	m_pDoc->tellListenerSubset(pShadowListener, docRange);
	delete docRange;
	delete pShadowListener;
	markAllRunsDirty();
}

bool fl_HdrFtrSectionLayout::isPointInHere(PT_DocPosition pos)
{
//
// Skip through the Containers in this shadow to find the one containing this
// point.
//
    fl_ContainerLayout*	pBL = getFirstLayout();
	if(pBL == NULL)
		return false;
	if(pos < pBL->getPosition())
	{
//
// This corner case is that pos == position of the HdrFtr strux
//
		if(pos == (pBL->getPosition() - 1))
		{
			return true;
		}
		return false;
	}
//
// OK see if the next hdrftr is ahead of the pos
//
	fl_HdrFtrSectionLayout * pHF = static_cast<fl_HdrFtrSectionLayout *>(getNext());
	if(pHF == NULL)
	{
		PT_DocPosition posEOD;
		m_pDoc->getBounds(true,posEOD);
		if(pos <= posEOD)
		{
			return true;
		}
		// This happens when you're erasing the last character in the document.
		// Not sure if assert should stay or not.
		return false;
	}
	fl_ContainerLayout * ppBL = pHF->getFirstLayout();
	if(ppBL != NULL)
	{
		if(pos < (ppBL->getPosition()-1))
		{
			return true;
		}
		return false;
	}

	fl_ContainerLayout* pNext = pBL->getNext();
	while(pNext != NULL && pNext->getPosition( true) < pos)
	{
		pBL = pNext;
		pNext = pNext->getNext();
	}
	if(pNext != NULL)
	{
		return true;
	}
	else if(pBL && pBL->getPosition() == pos)
	{
		return true;
	}
//
// Now the point MIGHT be in this last block. Use code from pd_Document
// to find out. Have to check whether we're out of docrange first
//
	PL_StruxDocHandle sdh=NULL;
	bool bres;
	bres = m_pDoc->getStruxOfTypeFromPosition(pos, PTX_Block, &sdh);
	if(bres && sdh == pBL->getStruxDocHandle())
	{
		return true;
	}
	return false;
}

/*!
 * Removes the shadow and the corresponding element pointing to the shadow for this
 * Page.
 */
void fl_HdrFtrSectionLayout::deletePage(fp_Page* pPage)
{
	UT_sint32 iShadow = _findShadow(pPage);
//
// This shadow might have already been deleted via the collapse method
//
	if(iShadow <  0)
		return;
	_PageHdrFtrShadowPair* pPair = static_cast<_PageHdrFtrShadowPair*>(m_vecPages.getNthItem(iShadow));
	UT_ASSERT(pPair);

	UT_ASSERT(pPair->getShadow());


	fp_Page * ppPage = pPair->getPage();
	UT_ASSERT(pPage == ppPage);
	delete pPair->getShadow();
	UT_DEBUGMSG(("Doing deletePage %x \n",pPage));
	if(getDocLayout()->findPage(ppPage) >= 0)
	{
			ppPage->removeHdrFtr(getHFType());
	}
	delete pPair;
	m_vecPages.deleteNthItem(iShadow);
}


/*!
 *  Just format the HdrFtrSectionLayout blocks for an insertBlock method.
 *  these blocks will be collapsed afterwards.
 */
void fl_HdrFtrSectionLayout::localFormat(void)
{
	xxx_UT_DEBUGMSG(("Doing a Local Format of the hdrftr section \n"));
	if(!getDocSectionLayout())
		return;
	fl_ContainerLayout*	pBL = getFirstLayout();
	UT_ASSERT(pBL->getContainerType() != FL_CONTAINER_HDRFTR);
	while (pBL)
	{
		if(pBL->getContainerType() == FL_CONTAINER_BLOCK)
		{
			static_cast<fl_BlockLayout *>(pBL)->setHdrFtr();
		}
		pBL->format();
		pBL = pBL->getNext();
	}
}

/*!
 *  Just collapse the HdrFtrSectionLayout blocks for an insertBlock method.
 *  This removes all lines and references to containers but leaves the blocks
 *  and runs intact.
 */
void fl_HdrFtrSectionLayout::_localCollapse(void)
{
	fl_ContainerLayout*	pBL = getFirstLayout();
	while (pBL)
	{
		pBL->collapse();
		pBL = pBL->getNext();
	}
}

/*!
 * This routine returns the matching block within this HdrFtrSectionLayout of the
 * shadow.
 \param fl_ContainerLayout * Pointer to block in shadow
 \returns the pinter to the matching block in the HdrFtr
 */
fl_ContainerLayout* fl_HdrFtrSectionLayout::findMatchingContainer(fl_ContainerLayout* pBL)
{
	// This routine returns the matching block within the
	// hdrftrSectionlayout.
	//
	fl_ContainerLayout* ppBL = getFirstLayout();
	bool bInTable = false;
	while(ppBL && (ppBL->getStruxDocHandle() != pBL->getStruxDocHandle()))
	{
		if(ppBL && (ppBL->getContainerType() == FL_CONTAINER_TABLE))
		{
			ppBL = ppBL->getFirstLayout();
			bInTable = true;
		}
		else if(bInTable && ppBL->getContainerType() == FL_CONTAINER_CELL)
		{
			ppBL = ppBL->getFirstLayout();
		}
		else if(bInTable && (ppBL->getNext() == NULL))
		{
			if(ppBL->myContainingLayout()->getNext() == NULL)
			{
				ppBL = ppBL->myContainingLayout();
				ppBL = ppBL->myContainingLayout()->getNext();
				bInTable = false;
			}
			else
			{
				ppBL = ppBL->myContainingLayout()->getNext();
			}
		}
		else
		{
			ppBL = ppBL->getNext();
		}
	}
	UT_ASSERT(ppBL);
	//xxx_UT_DEBUGMSG(("This header/footer is %x in findmatchingBlock \n",this));
	return ppBL;
}

/*!
 * This method checks that the pages in this header are valid and removes them if
 * they're not.
 */
void fl_HdrFtrSectionLayout::checkAndRemovePages(void)
{
	UT_sint32 iCount = static_cast<UT_sint32>(m_vecPages.getItemCount());
//
// Check that the pages we have are still valid. Delete them if they're not.
//
	UT_sint32 i = 0;
	UT_GenericVector<fp_Page*> pageForDelete;
	for(i =0; i< iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		UT_ASSERT(pPair);
		UT_ASSERT(pPair->getShadow());

		fp_Page * ppPage = pPair->getPage();
		if(getDocLayout()->findPage(ppPage) >= 0)
		{
			if(!getDocSectionLayout()->isThisPageValid(getHFType(),ppPage))
			{
				pageForDelete.addItem(ppPage);
			}
		}
		else
		{
			pageForDelete.addItem(ppPage);
		}
	}
	for(i=0; i< static_cast<UT_sint32>(pageForDelete.getItemCount()); i++)
	{
		fp_Page * pPage = pageForDelete.getNthItem(i);
		deletePage(pPage);
	}
	if( pageForDelete.getItemCount() > 0)
	{
		markAllRunsDirty();
	}
}

/*!
 * This method adds valid pages to the collection of shadows.
 */
void fl_HdrFtrSectionLayout::addValidPages(void)
{
	//
	// Check that all the pages this header/footer should have are
    // in place.
	// We have to extract this information from m_pDocSL
	// Loop through all the columns in m_pDocSl and find the pages owned
	// by m_pDocSL
	//
        fp_Column * pCol = NULL;
	pCol = static_cast<fp_Column *>(m_pDocSL->getFirstContainer());
	fp_Page * pOldPage = NULL;
	fp_Page * pNewPage = NULL;
	while(pCol)
	{
		pNewPage = pCol->getPage();
		if((pNewPage != NULL) && (pNewPage != pOldPage) && (getDocLayout()->findPage(pNewPage) >=0))
		{
			fl_DocSectionLayout* pDocSec = pNewPage->getOwningSection();
			if(pDocSec == m_pDocSL && _findShadow(pNewPage) < 0)
			{
//
// The addPage Method checks that only valid pages are added to this HdrFtr based on
// the HFType
//
				addPage(pNewPage);
			}
		}
		pCol = static_cast<fp_Column *>(pCol->getNext());
	}
}

/*!
 * Format the overall HdrFtrSectionLayout in it's virtual container.
 * Also check that all the correct pages have been found for this HdrFtr. Then
 * format the Shadows.
 */
void fl_HdrFtrSectionLayout::format(void)
{
	if(getFirstLayout() == NULL)
	{
		return;
	}
	UT_sint32 iCount =0;
	UT_sint32 i = 0;
	localFormat();
	//
	// Fail safe code to add pages if we don't have them.
	//
	addValidPages();

	iCount = m_vecPages.getItemCount();

	for (i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		pPair->getShadow()->format();
	}
	layout();
}

void fl_HdrFtrSectionLayout::updateLayout(bool bDoFull)
{
	bool bredraw = false;
	fl_ContainerLayout*	pBL = getFirstLayout();
	if(needsReformat())
	{
		format();
		bredraw = true;
		m_bNeedsReformat = false;
	}
	m_vecFormatLayout.clear();
	while (pBL)
	{
		if (pBL->needsReformat())
		{
			bredraw = true;
			pBL->format();
		}

		pBL = pBL->getNext();
	}
	if(bredraw == true)
	{
		if(m_pHdrFtrContainer)
			static_cast<fp_HdrFtrContainer *>(m_pHdrFtrContainer)->layout();
 	}
	//
	// update Just the  blocks in the shadowlayouts
	//
  	UT_uint32 iCount = m_vecPages.getItemCount();
	if(bredraw)
	{
	      for (UT_uint32 i=0; i<iCount; i++)
	      {
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		pPair->getShadow()->updateLayout(false);
	      }
	}
}

/*!
 * Mark all runs and lines in the all shadows for redraw.
 */
void fl_HdrFtrSectionLayout::markAllRunsDirty(void)
{
  	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		pPair->getShadow()->markAllRunsDirty();
	}
}

/*!
 * Layout the overall HdrFtr and everything underneath it.
 */
void fl_HdrFtrSectionLayout::layout(void)
{
    if(m_pHdrFtrContainer)
	static_cast<fp_HdrFtrContainer *>(m_pHdrFtrContainer)->layout();
	//
	// update the shadowlayouts
	//
  	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		pPair->getShadow()->layout();
	}
}

void fl_HdrFtrSectionLayout::clearScreen(void)
{
	//
	// update Just the  blocks in the shadowlayouts
	//
  	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		pPair->getShadow()->clearScreen();
	}
}

void fl_HdrFtrSectionLayout::redrawUpdate(void)
{
//
// Do another layout but don't redraw.
//
	if(m_pHdrFtrContainer)
		static_cast<fp_HdrFtrContainer *>(m_pHdrFtrContainer)->layout();
	//
	// Don't need to draw here since this is never displayed on the screen?
	//
	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		if(m_pLayout->findPage(pPair->getPage()) >= 0)
		{
			pPair->getShadow()->redrawUpdate();
		}
	}

}

bool fl_HdrFtrSectionLayout::doclistener_changeStrux(const PX_ChangeRecord_StruxChange * pcrxc)
{
	UT_ASSERT(pcrxc->getType()==PX_ChangeRecord::PXT_ChangeStrux);

	setAttrPropIndex(pcrxc->getIndexAP());

	// TODO what happens here?

	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);

	return false;
}

/*!
    this function is only to be called by fl_ContainerLayout::lookupProperties()
    all other code must call lookupProperties() instead
*/
void fl_HdrFtrSectionLayout::_lookupProperties(const PP_AttrProp* pAP)
{
}

bool fl_HdrFtrSectionLayout::bl_doclistener_populateSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_Span * pcrs, PT_BlockOffset blockOffset, UT_uint32 len)
{
//
// We need to populate block in the header/footer but to do that we need the
// header/footer to be fomatted. So do it then unformat after.
//
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();

	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_populateSpan(pcrs,blockOffset,len)
				&& bResult;
		}
		else
		{
			UT_ASSERT(0);
			break;
		}
	}
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
	  bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_populateSpan(pcrs,blockOffset,len)
	    && bResult;
	}
	return bResult;
}

/*!
 * Now for all these methods which manipulate the shadow sections, turn off
 * Insertion Point changes while the shadows are manipulated.
 * Re Enabled insertion point changes for the overall hdrftrsection so it
 * is changed just once.
 */

bool fl_HdrFtrSectionLayout::bl_doclistener_populateObject(fl_ContainerLayout* pBL, PT_BlockOffset blockOffset, const PX_ChangeRecord_Object * pcro)
{
//
// We need to populate block in the header/footer but to do that we need the
// header/footer to be fomatted. So do it then unformat after.
//
  	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_populateObject(blockOffset,pcro)
				&& bResult;
		}
		else
		{
			bResult = false;
		}
	}
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_populateObject(blockOffset,pcro)
			&& bResult;
	}
	else
	{
		bResult = false;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_insertSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_Span * pcrs)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_insertSpan(pcrs)
				&& bResult;
		}
	}
	m_pDoc->allowChangeInsPoint();
	// Update the overall block too.
	fl_BlockLayout * ppBL = static_cast<fl_BlockLayout *>(findMatchingContainer(pBL));
	if(ppBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertSpan(pcrs)
	&& bResult;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_deleteSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_Span * pcrs)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
		  bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_deleteSpan(pcrs)
		    && bResult;
		}
	}
	// Update the overall block too.

	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
        {
	  bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteSpan(pcrs)
	    && bResult;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_changeSpan(fl_ContainerLayout* pBL, const PX_ChangeRecord_SpanChange * pcrsc)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
		  bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_changeSpan(pcrsc) && bResult;
		}
	}
	// Update the overall block too.

	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
	  bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeSpan(pcrsc)
		&& bResult;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_deleteStrux(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_deleteStrux(pcrx)
				&& bResult;
		}
		else
		{
			UT_ASSERT(0);
		}
	}
	// Update the overall block too.

	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteStrux(pcrx) && bResult;
	}
	return bResult;
}

/*!
 * Delete Just the cell struxes from the HdrFtr shadows
 */
bool fl_HdrFtrSectionLayout::bl_doclistener_deleteCellStrux(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx)
{
	bool bResult = true;
	UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_CELL);
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	if(iCount <=0)
	{
		UT_ASSERT(0);
	}
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			UT_ASSERT(pShadowBL->getContainerType() == FL_CONTAINER_CELL);
			bResult = static_cast<fl_CellLayout *>(pShadowBL)->doclistener_deleteStrux(pcrx)
				&& bResult;
		}
		else
		{
			UT_ASSERT(0);
		}
	}
	return bResult;
}


/*!
 * Delete Just the table struxes from the HdrFtr shadows
 */
bool fl_HdrFtrSectionLayout::bl_doclistener_deleteTableStrux(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx)
{
	bool bResult = true;
	UT_ASSERT(pBL->getContainerType() == FL_CONTAINER_TABLE);
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	if(iCount <=0)
	{
		UT_ASSERT(0);
	}
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			UT_ASSERT(pShadowBL->getContainerType() == FL_CONTAINER_TABLE);
			bResult = static_cast<fl_TableLayout *>(pShadowBL)->doclistener_deleteStrux(pcrx)
				&& bResult;
		}
		else
		{
			UT_ASSERT(0);
		}
	}
	return bResult;
}


bool fl_HdrFtrSectionLayout::bl_doclistener_changeFmtMark(fl_ContainerLayout* pBL, const PX_ChangeRecord_FmtMarkChange * pcrfmc)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
		  bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_changeFmtMark(pcrfmc)
		    && bResult;
		}
	}
	// Update the overall block too.

	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
	  bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeFmtMark(pcrfmc) && bResult;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_changeStrux(fl_ContainerLayout* pBL, const PX_ChangeRecord_StruxChange * pcrxc)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			if(pShadowBL->getContainerType() == FL_CONTAINER_BLOCK)
			{
				bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_changeStrux(pcrxc)
					&& bResult;
			}
			else if(pShadowBL->getContainerType() == FL_CONTAINER_TABLE)
			{
				bResult = static_cast<fl_TableLayout *>(pShadowBL)->doclistener_changeStrux(pcrxc)
				&& bResult;
			}
			else if(pShadowBL->getContainerType() == FL_CONTAINER_CELL)
			{
				bResult = static_cast<fl_CellLayout *>(pShadowBL)->doclistener_changeStrux(pcrxc)
				&& bResult;
			}
		}
	}
	// Update the overall block too.

	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL && (pBL->getContainerType() == FL_CONTAINER_BLOCK))
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeStrux(pcrxc)
			&& bResult;
	}
	return bResult;
}

/*!
 * Insert a cell into every table in the HdrFtr
 */
bool fl_HdrFtrSectionLayout::bl_doclistener_insertCell(fl_ContainerLayout* pCell,
											  const PX_ChangeRecord_Strux * pcrx,
											  PL_StruxDocHandle sdh,
											  PL_ListenerId lid,
											  fl_TableLayout * pTL)
{
	UT_uint32 iCount = m_vecPages.getItemCount();
	fl_ContainerLayout * pShadowBL = NULL;
	UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertCells into shadows \n"));
	m_pDoc->setDontChangeInsPoint();
	bool bResult = true;
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching Table in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pTL);
		fl_ContainerLayout * pPrevCell = NULL;
		if(pCell)
		{
			pPrevCell = pPair->getShadow()->findMatchingContainer(pCell);
		}
		UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertCell into Table shadow \n"));
		if(pShadowBL)
		{
			bResult = static_cast<fl_TableLayout *>(pShadowBL)->bl_doclistener_insertCell(pPrevCell,pcrx,sdh,lid,NULL)
				&& bResult;
		}
	}
	m_pDoc->allowChangeInsPoint();
	return true;

}


/*!
 * Insert an endTable cell into every table in the HdrFtr
 */
bool fl_HdrFtrSectionLayout::bl_doclistener_insertEndTable(fl_ContainerLayout* pTab,
											  const PX_ChangeRecord_Strux * pcrx,
											  PL_StruxDocHandle sdh,
											  PL_ListenerId lid)
{
	UT_uint32 iCount = m_vecPages.getItemCount();
	fl_ContainerLayout * pShadowBL = NULL;
	UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertEndTables into shadows \n"));
	m_pDoc->setDontChangeInsPoint();
	bool bResult = true;
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching Table in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pTab);
		UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertEndTable into shadow \n"));
		if(pShadowBL)
		{
			bResult = static_cast<fl_TableLayout *>(pShadowBL)->bl_doclistener_insertEndTable(NULL,pcrx,sdh,lid,NULL)
				&& bResult;
		}
	}
	m_pDoc->allowChangeInsPoint();
	return true;

}

fl_SectionLayout * fl_HdrFtrSectionLayout::bl_doclistener_insertTable(fl_ContainerLayout* pBL,
													SectionType iType,
													const PX_ChangeRecord_Strux * pcrx,
													PL_StruxDocHandle sdh,
													PL_ListenerId lid,
													void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																			PL_ListenerId lid,
																			PL_StruxFmtHandle sfhNew))
{
	fl_SectionLayout * pSL = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertTable(pcrx, iType, sdh, lid, pfnBindHandles);
//	UT_ASSERT(0);
	fl_SectionLayout::checkAndAdjustCellSize();
//
// FIXME: Propagate this to the shadows
//      : Write code to handle populate cases in the shadows
	bool bResult = true;
//
// Now insert it into all the shadows.
//
	UT_uint32 iCount = m_vecPages.getItemCount();
	fl_ContainerLayout * pShadowBL = NULL;
	UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertTable \n"));
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.
		if(pBL)
		{
			pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
			UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertTable into shadow 1 \n"));
			if(pShadowBL)
			{
				bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_insertTable(pcrx,iType,sdh,lid,NULL)
					&& bResult;
			}
			pPair->getShadow()->checkAndAdjustCellSize();
		}
		else
//
// FIXME: Handle case of inserting a table at the start of the HdrFtr
//
		{
			UT_ASSERT(0);
		}
	}
	m_pDoc->allowChangeInsPoint();
	return pSL;
}

/*!
 * Insert a Table at the start of a HdrFtr
 */
fl_SectionLayout * fl_HdrFtrSectionLayout::bl_doclistener_insertTable(SectionType iType,
													const PX_ChangeRecord_Strux * pcrx,
													PL_StruxDocHandle sdh,
													PL_ListenerId lid,
													void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																			PL_ListenerId lid,
																			PL_StruxFmtHandle sfhNew))
{

	fl_SectionLayout * pSL = static_cast<fl_SectionLayout *>(static_cast<fl_ContainerLayout *>(this)->insert(sdh,this,pcrx->getIndexAP(), FL_CONTAINER_TABLE));

	PL_StruxFmtHandle sfhNew = static_cast<PL_StruxFmtHandle>(pSL);
	//
	// Don't bind to shadows
	//
	if(pfnBindHandles)
	{
		pfnBindHandles(sdh,lid,sfhNew);
	}
//
// increment the insertion point in the view.
//
	FV_View* pView = m_pLayout->getView();
	if (pView && (pView->isActive() || pView->isPreview()))
	{
		pView->setPoint(pcrx->getPosition() + fl_BLOCK_STRUX_OFFSET);
	}
	else if(pView && pView->getPoint() > pcrx->getPosition())
	{
		pView->setPoint(pView->getPoint() + fl_BLOCK_STRUX_OFFSET);
	}

	fl_SectionLayout::checkAndAdjustCellSize();
//
// Now insert it into all the shadows.
//
	UT_uint32 iCount = m_vecPages.getItemCount();
	UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertTable At start \n"));
	m_pDoc->setDontChangeInsPoint();
	fl_HdrFtrShadow * pShadowL = NULL;
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		pShadowL = pPair->getShadow();

		UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertTable into start of shadow 1 \n"));
		if(pShadowL)
		{
		     pShadowL->bl_doclistener_insertTable(FL_SECTION_TABLE, pcrx,sdh,lid,NULL);
		     pShadowL->checkAndAdjustCellSize();
		}
	}
	m_pDoc->allowChangeInsPoint();
	return pSL;
}

/*!
 * Insert the first block of the container in HdrFtr. We need to propage this
 * to all the shadows.
 */
bool fl_HdrFtrSectionLayout::bl_doclistener_insertFirstBlock(fl_ContainerLayout* pCL, const PX_ChangeRecord_Strux * pcrx,PL_StruxDocHandle sdh,PL_ListenerId lid)
{
	UT_uint32 iCount = m_vecPages.getItemCount();
	fl_ContainerLayout * pShadowBL = NULL;
	UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insert First block into shadow Cells \n"));
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching Table in this shadow.

		pShadowBL = pPair->getShadow()->findMatchingContainer(pCL);
		UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertFirstBlock into (CELL) shadow 1 \n"));
		if(pShadowBL)
		{
			fl_BlockLayout*	pNewBL = static_cast<fl_BlockLayout *>(pShadowBL->insert(sdh, NULL, pcrx->getIndexAP(),FL_CONTAINER_BLOCK));
			pNewBL->doclistener_insertFirstBlock(pcrx, sdh,	lid, NULL);
		}
	}
	m_pDoc->allowChangeInsPoint();
	return true;

}

bool fl_HdrFtrSectionLayout::bl_doclistener_insertBlock(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx,PL_StruxDocHandle sdh,PL_ListenerId lid,void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,	PL_ListenerId lid, PL_StruxFmtHandle sfhNew))
{
	bool bResult = true;
//
// Now insert it into all the shadows.
//
	UT_uint32 iCount = m_vecPages.getItemCount();
	fl_ContainerLayout * pShadowBL = NULL;
	UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertBlock \n"));
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		// Find matching block in this shadow.
		if(pBL)
		{
			pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
			xxx_UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertBlock into shadow 1 \n"));
			if(pShadowBL)
			{
				bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_insertBlock(pcrx,sdh,lid,NULL)
					&& bResult;
			}
		}
		else
//
// This is the first block in the shadow
//
		{
			fl_ContainerLayout*	pNewBL = pPair->getShadow()->insert(sdh, NULL, pcrx->getIndexAP(),FL_CONTAINER_BLOCK);
			if (!pNewBL)
			{
				UT_DEBUGMSG(("no memory for BlockLayout\n"));
				return false;
			}
			xxx_UT_DEBUGMSG(("fl_HdrFtrSectionLayout: insertBlock into shadow 2 \n"));
			bResult = bResult && static_cast<fl_BlockLayout *>(pNewBL)->doclistener_insertFirstBlock(pcrx, sdh,
													lid, NULL);
		}
	}
//
// Find Matching Block in this HdrFtrSectionLayout!!
//
	m_pDoc->allowChangeInsPoint();
	if(pBL)
	{
		fl_ContainerLayout * ppBL = findMatchingContainer(pBL);
		if(ppBL)
		{
		  static_cast<fl_BlockLayout *>(ppBL)->setHdrFtr();
		  bResult = static_cast<fl_BlockLayout *>(ppBL)->doclistener_insertBlock(pcrx,sdh,lid,pfnBindHandles)
			&& bResult;
//
// Mark the Block as HdrFtr
//
		  static_cast<fl_BlockLayout *>(ppBL->getNext())->setHdrFtr();
		}
		setNeedsReformat(this);
	}
	else
//
// First block in the section
//
	{
		fl_ContainerLayout*	pNewBL = insert(sdh, NULL, pcrx->getIndexAP(),FL_CONTAINER_BLOCK);
		if (!pNewBL)
		{
			UT_DEBUGMSG(("no memory for BlockLayout\n"));
			return false;
		}
		bResult = bResult && static_cast<fl_BlockLayout *>(pNewBL)->doclistener_insertFirstBlock(pcrx, sdh,
													lid, pfnBindHandles);
		static_cast<fl_BlockLayout *>(pNewBL)->setHdrFtr();
		setNeedsReformat(this);
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_insertSection(fl_ContainerLayout* pBL, const PX_ChangeRecord_Strux * pcrx,
														  PL_StruxDocHandle sdh,
														  PL_ListenerId lid,
														  void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																				  PL_ListenerId lid,
																				  PL_StruxFmtHandle sfhNew))
{
	// TODO this should NEVER happen, right?
	UT_DEBUGMSG(("Insert Section is header/footer!!! \n"));
	UT_ASSERT(0);
	bool bResult = true;
	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);

		bResult = pPair->getShadow()->bl_doclistener_insertSection(pBL, FL_SECTION_DOC, pcrx, sdh, lid, pfnBindHandles)
			&& bResult;
	}

	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_insertObject(fl_ContainerLayout* pBL, const PX_ChangeRecord_Object * pcro)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_insertObject(pcro)
				&& bResult;
		}
	}
	// Update the overall block too.
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertObject(pcro) && bResult;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_deleteObject(fl_ContainerLayout* pBL, const PX_ChangeRecord_Object * pcro)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_deleteObject(pcro)
				&& bResult;
		}
	}
	// Update the overall block too.
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteObject(pcro) && bResult;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_changeObject(fl_ContainerLayout* pBL, const PX_ChangeRecord_ObjectChange * pcroc)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_changeObject(pcroc)
				&& bResult;
		}
	}
	// Update the overall block too.
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_changeObject(pcroc) && bResult;
	}

	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_insertFmtMark(fl_ContainerLayout* pBL, const PX_ChangeRecord_FmtMark * pcrfm)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_insertFmtMark(pcrfm)
				&& bResult;
		}
		else
		{
			bResult = false;
		}
	}
	// Update the overall block too.
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_insertFmtMark(pcrfm) && bResult;
	}
	else
	{
		bResult = false;
	}
	return bResult;
}

bool fl_HdrFtrSectionLayout::bl_doclistener_deleteFmtMark(fl_ContainerLayout* pBL, const PX_ChangeRecord_FmtMark * pcrfm)
{
	bool bResult = true;
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	m_pDoc->setDontChangeInsPoint();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pBL);
		if(pShadowBL)
		{
			bResult = static_cast<fl_BlockLayout *>(pShadowBL)->doclistener_deleteFmtMark(pcrfm)
				&& bResult;
		}
		else
		{
			bResult = false;
		}
	}
	// Update the overall block too.
	m_pDoc->allowChangeInsPoint();
	pBL = findMatchingContainer(pBL);
	if(pBL)
	{
		bResult = static_cast<fl_BlockLayout *>(pBL)->doclistener_deleteFmtMark(pcrfm)	&& bResult;
	}
	else
	{
		bResult = false;
	}
	return bResult;
}

void fl_HdrFtrSectionLayout::checkAndAdjustCellSize(fl_ContainerLayout * pCL)
{
	if(pCL->getContainerType() != FL_CONTAINER_CELL)
	{
		return;
	}
	fl_ContainerLayout * pShadowBL = NULL;
	UT_uint32 iCount = m_vecPages.getItemCount();
	for (UT_uint32 i=0; i<iCount; i++)
	{
		_PageHdrFtrShadowPair* pPair = m_vecPages.getNthItem(i);
		// Find matching block in this shadow.
		pShadowBL = pPair->getShadow()->findMatchingContainer(pCL);
		if(pShadowBL)
		{
			static_cast<fl_SectionLayout *>(pShadowBL)->checkAndAdjustCellSize();
		}
	}
	// Update the overall block too.

	fl_ContainerLayout * pBL = findMatchingContainer(pCL);
	if(pBL)
	{
		static_cast<fl_CellLayout *>(pBL)->checkAndAdjustCellSize();
	}
	return;
}

////////////////////////////////////////////////////////////////////////////

bool fl_DocSectionLayout::bl_doclistener_insertFootnote(fl_ContainerLayout* pFootnote,
											  const PX_ChangeRecord_Strux * pcrx,
											  PL_StruxDocHandle sdh,
											  PL_ListenerId lid,
											  void (* pfnBindHandles)(PL_StruxDocHandle sdhNew,
																	  PL_ListenerId lid,
																	  PL_StruxFmtHandle sfhNew))
{
	fl_ContainerLayout * pNewCL = NULL;
	fl_DocSectionLayout * pCol = static_cast<fl_DocSectionLayout *>(myContainingLayout());
	pNewCL = pCol->insert(sdh,pFootnote,pcrx->getIndexAP(), FL_CONTAINER_FOOTNOTE);
	
		// Must call the bind function to complete the exchange of handles
		// with the document (piece table) *** before *** anything tries
		// to call down into the document (like all of the view
		// listeners).
		
	PL_StruxFmtHandle sfhNew = static_cast<PL_StruxFmtHandle>(pNewCL);
	pfnBindHandles(sdh,lid,sfhNew);


//
// increment the insertion point in the view.
//
	FV_View* pView = m_pLayout->getView();
	if (pView && (pView->isActive() || pView->isPreview()))
	{
		pView->setPoint(pcrx->getPosition() +  fl_BLOCK_STRUX_OFFSET);
	}
	else if(pView && pView->getPoint() > pcrx->getPosition())
	{
		pView->setPoint(pView->getPoint() +  fl_BLOCK_STRUX_OFFSET);
	}
	return true;
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

fl_HdrFtrShadow::fl_HdrFtrShadow(FL_DocLayout* pLayout, fp_Page* pPage, fl_HdrFtrSectionLayout* pHdrFtrSL, PL_StruxDocHandle sdh, PT_AttrPropIndex indexAP)
	: fl_SectionLayout(pLayout, sdh, indexAP, FL_SECTION_SHADOW,FL_CONTAINER_SHADOW,PTX_Section,pHdrFtrSL->getDocSectionLayout()),
	  m_pPage(pPage),
	  m_pHdrFtrSL(pHdrFtrSL)
{
	// Force creation of the appropriate container object;
	// throw away return value.
	m_pPage->getHdrFtrContainer(m_pHdrFtrSL);
	UT_DEBUGMSG(("check that m_iType is indeed FL_SECTION_SHADOW \n"));
	UT_ASSERT(m_iType == FL_SECTION_SHADOW);
//	UT_ASSERT(0);
	fl_Layout::setType(PTX_Section); // Set the type of this strux
}

fl_HdrFtrShadow::~fl_HdrFtrShadow()
{
	_purgeLayout();
}

fp_Container* fl_HdrFtrShadow::getFirstContainer() const
{
	return m_pPage->getHdrFtrContainer(m_pHdrFtrSL);
}

fp_Container* fl_HdrFtrShadow::getLastContainer() const
{
	return m_pPage->getHdrFtrContainer(m_pHdrFtrSL);
}

fp_Container* fl_HdrFtrShadow::getNewContainer(fp_Container * pFirstContainer)
{
	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);

	return NULL;
}

fl_ContainerLayout* fl_HdrFtrShadow::findMatchingContainer(fl_ContainerLayout* pBL)
{
	// This routine returns the matching block within this shadow of the
	// hdrftrSectionlayout.
	//
	fl_ContainerLayout* ppBL = getFirstLayout();
	bool bInTable = false;
	while(ppBL && (ppBL->getStruxDocHandle() != pBL->getStruxDocHandle()))
	{
		if(ppBL->getContainerType() == FL_CONTAINER_TABLE)
		{
			ppBL = ppBL->getFirstLayout();
			bInTable = true;
		}
		else if(bInTable && ppBL->getContainerType() == FL_CONTAINER_CELL)
		{
			ppBL = ppBL->getFirstLayout();
		}
		else if(bInTable && (ppBL->getNext() == NULL))
		{
			if(ppBL->myContainingLayout()->getNext() == NULL)
			{
				ppBL = ppBL->myContainingLayout();
				ppBL = ppBL->myContainingLayout()->getNext();
				bInTable = false;
			}
			else
			{
				ppBL = ppBL->myContainingLayout()->getNext();
			}
		}
		else
		{
			ppBL = ppBL->getNext();
		}
	}
	if(ppBL == NULL)
	{
		//UT_ASSERT(0);
		m_pDoc->miniDump(pBL->getStruxDocHandle(),6);
		if(pBL->getContainerType() == FL_CONTAINER_BLOCK)
		{
			ppBL = getFirstLayout();
			while(ppBL && ppBL->getStruxDocHandle() != pBL->getStruxDocHandle())
			{
				ppBL = ppBL->getNextBlockInDocument();
			}
		}
	}
	//UT_ASSERT(ppBL);
	xxx_UT_DEBUGMSG(("Search for block in shadow %x \n",this));
	return ppBL;
}

void fl_HdrFtrShadow::format(void)
{
	fl_ContainerLayout*	pBL = getFirstLayout();
	while (pBL)
	{
		if(pBL->getContainerType() == FL_CONTAINER_TABLE)
		{
			UT_DEBUGMSG(("!!!!Format Shadow Table!!!!!!!!!!\n"));
		}
		pBL->format();
		pBL = pBL->getNext();
	}
}

/*!
 * Scans through the shadow looking for the block at the specified Document
 * Position.
 \param pos the Document position
 \return A pointer to the block containing the point. Returns NULL if no block
         is found
 */
fl_ContainerLayout * fl_HdrFtrShadow::findBlockAtPosition(PT_DocPosition pos)
{
//
// Skip through the blocks in this shadow to find the one containing this
// point.
//
    fl_ContainerLayout*	pBL = getFirstBlock();
    if(pBL == NULL)
		return NULL;
	if(pos < pBL->getPosition(true))
	{
//
// This corner case is that pos == position of the HdrFtr strux
//
		if(pos == (pBL->getPosition(true) - 1))
		{
			if(pBL->getContainerType() != FL_CONTAINER_BLOCK)
			{
				pBL= pBL->getNextBlockInDocument();
			}
			return pBL;
		}
		return NULL;
	}
	fl_ContainerLayout* pNext = NULL;
	pNext = pBL->getNextBlockInDocument();
	bool doNext = false;
	if(pNext != NULL)
	{
		doNext = (pNext->getPosition(true) < pos);
	}
	while(doNext)
	{
		pBL = pNext;
		pNext = pNext->getNextBlockInDocument();
		if(pNext && (pNext->getPosition(true) < pos))
		{
			if(getNext())
			{
				if(getNext()->getPosition(true) > pNext->getPosition(true))
				{
					doNext = true;
				}
				else
				{
					doNext = false;
				}
			}
			else
			{
				doNext = true;
			}
		}
		else
		{
			doNext = false;
		}
	}

	if(pNext != NULL)
	{
		UT_ASSERT(pNext->getPosition(true) >= pos);
		if(pBL->getContainerType() == FL_CONTAINER_BLOCK)
		{
			return pBL;
		}
		else if(pNext->getContainerType() == FL_CONTAINER_BLOCK)
		{
			return pNext;
		}
	}
	else if(pBL && pBL->getPosition() == pos)
	{
		return pBL;
	}
	else
	{
		return NULL;
	}
//
// Next corner case. See if position is inside the edittableBounds of this
// section
//
	PT_DocPosition posEnd;
	FV_View * pView = m_pLayout->getView();
	if(pView)
	{
		pView->getEditableBounds(true,posEnd);
		if(pos <= posEnd)
			return pBL;
	}
//
// Now the point MIGHT be in this last block. Use code from pd_Document
// to find out. Have to check whether we're out of docrange first
//
	m_pDoc->getBounds(true,posEnd);
	if(pos > posEnd)
		return NULL;
	PL_StruxDocHandle sdh=NULL;
	bool bres;
	bres = m_pDoc->getStruxOfTypeFromPosition(pos, PTX_Block, &sdh);
	if(bres && sdh == pBL->getStruxDocHandle())
		return pBL;
//
// Not here!!
//
	return NULL;
}

void fl_HdrFtrShadow::updateLayout(bool bDoAll)
{
	bool bredraw = false;
	xxx_UT_DEBUGMSG(("Doing Update layout in shadow %x \n",this));
	fl_ContainerLayout*	pBL = getFirstLayout();
	m_vecFormatLayout.clear();
	while (pBL)
	{
		if (pBL->needsReformat())
		{
			bredraw = true;
			pBL->format();
		}

		pBL = pBL->getNext();
	}
	if(bredraw)
	{
		//    clearScreen();
		static_cast<fp_ShadowContainer *>(getFirstContainer())->layout();
 	}
}

void fl_HdrFtrShadow::layout(void)
{
	if(needsReformat())
	{
		format();
	}
	static_cast<fp_ShadowContainer *>(getFirstContainer())->layout();
}

void fl_HdrFtrShadow::clearScreen(void)
{
	UT_ASSERT(getFirstContainer());
	if(getFirstContainer())
		static_cast<fp_ShadowContainer *>(getFirstContainer())->clearScreen();
}

void fl_HdrFtrShadow::redrawUpdate(void)
{
	FV_View * pView = m_pLayout->getView();
	fl_ContainerLayout*	pBL = getFirstLayout();
	while (pBL && (pView != NULL))
	{
		if(pBL->getContainerType() == FL_CONTAINER_BLOCK && static_cast<fl_BlockLayout *>(pBL)->hasUpdatableField())
		{
			bool bReformat = pBL->recalculateFields(getDocLayout()->getRedrawCount());
			if(bReformat)
			{
				pBL->format();
			}
		}
		else
		{
			pBL->recalculateFields(getDocLayout()->getRedrawCount());
		}
		
		if(pView && pBL->needsRedraw())
		{
			pBL->redrawUpdate();
		}
		pBL = pBL->getNext();
	}
	static_cast<fp_ShadowContainer *>(getFirstContainer())->layout();
}
bool fl_HdrFtrShadow::doclistener_changeStrux(const PX_ChangeRecord_StruxChange * pcrxc)
{
	UT_ASSERT(pcrxc->getType()==PX_ChangeRecord::PXT_ChangeStrux);

	setAttrPropIndex(pcrxc->getIndexAP());

	// TODO

	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);

	return false;
}

/*!
    this function is only to be called by fl_ContainerLayout::lookupProperties()
    all other code must call lookupProperties() instead
*/
void fl_HdrFtrShadow::_lookupProperties(const PP_AttrProp* pAP)
{
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////

fl_ShadowListener::fl_ShadowListener(fl_HdrFtrSectionLayout* pHFSL, fl_HdrFtrShadow* pShadow): 
	m_pDoc(pHFSL->getDocLayout()->getDocument()),
	m_pShadow(pShadow),
	m_bListening(false),
	m_pCurrentBL(NULL),
	m_pHFSL(pHFSL),
	m_pCurrentTL(NULL),
	m_pCurrentCell(NULL)
{
}

fl_ShadowListener::~fl_ShadowListener()
{
}

bool fl_ShadowListener::populate(PL_StruxFmtHandle sfh,
								 const PX_ChangeRecord * pcr)
{
	if (!m_bListening)
	{
		return true;
	}

	UT_ASSERT(m_pShadow);
//	UT_DEBUGMSG(("fl_ShadowListener::populate shadow %x \n",m_pShadow));

	bool bResult = false;
	FV_View* pView = m_pHFSL->getDocLayout()->getView();
	PT_DocPosition oldPos = 0;
	//
	// We're not printing
	//
	if(pView != NULL)
	{
		oldPos = pView->getPoint();
	}
	switch (pcr->getType())
	{
	case PX_ChangeRecord::PXT_InsertSpan:
	{
		const PX_ChangeRecord_Span * pcrs = static_cast<const PX_ChangeRecord_Span *> (pcr);

		{
			const fl_Layout * pL = static_cast<const fl_Layout *>(sfh);
			UT_ASSERT(pL->getType() == PTX_Block);
			UT_ASSERT(m_pCurrentBL == (static_cast<const fl_ContainerLayout *>(pL)));
		}
		PT_BlockOffset blockOffset = pcrs->getBlockOffset();
		UT_uint32 len = pcrs->getLength();


		bResult = static_cast<fl_BlockLayout *>(m_pCurrentBL)->doclistener_populateSpan(pcrs, blockOffset, len);
		goto finish_up;
	}

	case PX_ChangeRecord::PXT_InsertObject:
	{
		const PX_ChangeRecord_Object * pcro = static_cast<const PX_ChangeRecord_Object *>(pcr);

		{
			const fl_Layout * pL = static_cast<const fl_Layout *>(sfh);
			UT_ASSERT(pL->getType() == PTX_Block);
			UT_ASSERT(m_pCurrentBL == (static_cast<const fl_ContainerLayout *>(pL)));
		}
		PT_BlockOffset blockOffset = pcro->getBlockOffset();

// sterwill -- is this call to getSectionLayout() needed?  pBLSL is not used.

//			fl_SectionLayout* pBLSL = m_pCurrentBL->getSectionLayout();
		bResult = static_cast<fl_BlockLayout *>(m_pCurrentBL)->doclistener_populateObject(blockOffset,pcro);
		goto finish_up;
	}

	case PX_ChangeRecord::PXT_InsertFmtMark:
	{
		//	const PX_ChangeRecord_FmtMark * pcrfm = static_cast<const PX_ChangeRecord_FmtMark *>(pcr);

		{
			const fl_Layout * pL = static_cast<const fl_Layout *>(sfh);
			UT_ASSERT(pL->getType() == PTX_Block);
			UT_ASSERT(m_pCurrentBL == (static_cast<const fl_ContainerLayout *>(pL)));
		}
		bResult = static_cast<fl_BlockLayout *>(m_pCurrentBL)->doclistener_insertFmtMark( reinterpret_cast<const PX_ChangeRecord_FmtMark *>(pcr));
		goto finish_up;
	}

	default:
		UT_DEBUGMSG(("Unknown Change record = %d \n",pcr->getType()));
		UT_ASSERT(0);
		//
		// We're not printing
		//
		if(pView != NULL && m_pDoc->getAllowChangeInsPoint())
		{
			pView->setPoint(oldPos);
		}
		return false;
	}

 finish_up:
	//
	// We're not printing
	//
	if(pView != NULL && m_pDoc->getAllowChangeInsPoint())
	{
		pView->setPoint(oldPos);
	}
	return bResult;
}

bool fl_ShadowListener::populateStrux(PL_StruxDocHandle sdh,
									  const PX_ChangeRecord * pcr,
									  PL_StruxFmtHandle * psfh)
{
	UT_ASSERT(m_pShadow);
	UT_DEBUGMSG(("fl_ShadowListener::populateStrux\n"));

	UT_ASSERT(pcr->getType() == PX_ChangeRecord::PXT_InsertStrux);
	const PX_ChangeRecord_Strux * pcrx = static_cast<const PX_ChangeRecord_Strux *> (pcr);

	switch (pcrx->getStruxType())
	{
	case PTX_Section:
	{
		PT_AttrPropIndex indexAP = pcr->getIndexAP();
		const PP_AttrProp* pAP = NULL;

		// need to explode revision attribute
		m_pDoc->getAttrProp(indexAP, &pAP);
		if(pAP)
		{
			UT_return_val_if_fail(m_pHFSL && m_pHFSL->getDocLayout(), false);
			FV_View* pView = m_pHFSL->getDocLayout()->getView();
			UT_return_val_if_fail(pView, false);

			UT_uint32 iId  = pView->getRevisionLevel();
			bool bShow     = pView->isShowRevisions();

			PP_RevisionAttr * pRevisions = NULL; // must be NULL

			
			if(  pAP->getRevisedIndex() != 0xffffffff
			  && pAP->getRevisionState().isEqual(iId, bShow, m_pDoc->isMarkRevisions()))
			{
				// the revision has a valid index to an inflated AP, so we use it
				PT_AttrPropIndex revAPI = pAP->getRevisedIndex();

				m_pDoc->getAttrProp(revAPI, &pAP);
			}
			else
			{
				bool bHiddenRevision;
				const PP_AttrProp * pNewAP = m_pDoc->explodeRevisions(pRevisions, pAP, bShow,
																	  iId, bHiddenRevision);

				if(pNewAP)
					pAP = pNewAP;
			}

			delete pRevisions;
		}
		
		if (pAP)
		{
			const XML_Char* pszSectionType = NULL;
			pAP->getAttribute("type", pszSectionType);
			if (
				!pszSectionType
				|| (0 == UT_strcmp(pszSectionType, "doc"))
				)
			{
				m_bListening = false;
			}
			else
			{
				if ( (0 == UT_strcmp(pszSectionType, "header"))
					|| (0 == UT_strcmp(pszSectionType, "footer"))
					 || (0 == UT_strcmp(pszSectionType, "header-first"))
					|| (0 == UT_strcmp(pszSectionType, "footer-first"))
					 || (0 == UT_strcmp(pszSectionType, "header-even"))
					|| (0 == UT_strcmp(pszSectionType, "footer-even"))
					 || (0 == UT_strcmp(pszSectionType, "header-last"))
					|| (0 == UT_strcmp(pszSectionType, "footer-last"))
					)
				{
					// TODO verify id match

					m_bListening = true;
				}
				else
				{
					return false;
				}
			}
		}
		else
		{
			// TODO fail?
			return false;
		}
	}
	break;

	case PTX_SectionHdrFtr:
	{
		PT_AttrPropIndex indexAP = pcr->getIndexAP();
		const PP_AttrProp* pAP = NULL;
		// need to explode revision attribute
		m_pDoc->getAttrProp(indexAP, &pAP);
		if(pAP)
		{
			UT_return_val_if_fail(m_pHFSL && m_pHFSL->getDocLayout(), false);
			FV_View* pView = m_pHFSL->getDocLayout()->getView();
			UT_return_val_if_fail(pView, false);

			UT_uint32 iId  = pView->getRevisionLevel();
			bool bShow     = pView->isShowRevisions();

			PP_RevisionAttr * pRevisions = NULL; // must be NULL

			
			if(  pAP->getRevisedIndex() != 0xffffffff
			  && pAP->getRevisionState().isEqual(iId, bShow, m_pDoc->isMarkRevisions()))
			{
				// the revision has a valid index to an inflated AP, so we use it
				PT_AttrPropIndex revAPI = pAP->getRevisedIndex();

				m_pDoc->getAttrProp(revAPI, &pAP);
			}
			else
			{
				bool bHiddenRevision;
				const PP_AttrProp * pNewAP = m_pDoc->explodeRevisions(pRevisions, pAP, bShow,
																	  iId, bHiddenRevision);

				if(pNewAP)
					pAP = pNewAP;
			}

			delete pRevisions;
		}
		
		if (pAP)
		{
			const XML_Char* pszSectionType = NULL;
			pAP->getAttribute("type", pszSectionType);
			if (
				!pszSectionType
				|| (0 == UT_strcmp(pszSectionType, "doc"))
				)
			{
				m_bListening = false;
			}
			else
			{
				if ( (0 == UT_strcmp(pszSectionType, "header"))
					|| (0 == UT_strcmp(pszSectionType, "footer"))
					 || (0 == UT_strcmp(pszSectionType, "header-first"))
					|| (0 == UT_strcmp(pszSectionType, "footer-first"))
					 || (0 == UT_strcmp(pszSectionType, "header-even"))
					|| (0 == UT_strcmp(pszSectionType, "footer-even"))
					 || (0 == UT_strcmp(pszSectionType, "header-last"))
					|| (0 == UT_strcmp(pszSectionType, "footer-last"))
					)
				{
					// TODO verify id match

					m_bListening = true;
				}
				else
				{
					return false;
				}
			}
		}
		else
		{
			// TODO fail?
			return false;
		}
	}
	break;

	case PTX_Block:
	{
		if (m_bListening)
		{
			// append a new BlockLayout to that SectionLayout
			fl_ContainerLayout*	pBL = NULL;
			if(m_pCurrentCell == NULL)
			{
				pBL = m_pShadow->append(sdh, pcr->getIndexAP(),FL_CONTAINER_BLOCK);
			}
			else
			{
				pBL = m_pCurrentCell->append(sdh, pcr->getIndexAP(),FL_CONTAINER_BLOCK);
			}
			UT_DEBUGMSG(("New Shadow block %x created and set as current \n",pBL));
			if (!pBL)
			{
				UT_DEBUGMSG(("no memory for BlockLayout"));
				return false;
			}
			m_pCurrentBL = pBL;
			*psfh = static_cast<PL_StruxFmtHandle>(pBL);
		}

	}
	break;


	case PTX_SectionTable:
	{
		if (m_bListening)
		{
			// append a new BlockLayout to that SectionLayout
			fl_ContainerLayout*	pTL = m_pShadow->append(sdh, pcr->getIndexAP(),FL_CONTAINER_TABLE);
			UT_DEBUGMSG(("New Shadow Table %x created and set as current \n",pTL));
			m_pCurrentTL = static_cast<fl_TableLayout *>(pTL);
			*psfh = static_cast<PL_StruxFmtHandle>(pTL);
		}

	}
	break;


	case PTX_SectionCell:
	{
		if (m_bListening && m_pCurrentTL)
		{

			// append a new BlockLayout to that SectionLayout
			fl_ContainerLayout*	pCell = m_pCurrentTL->append(sdh, pcr->getIndexAP(),FL_CONTAINER_CELL);
			UT_DEBUGMSG(("New Shadow Cell %x created and set as current \n",pCell));
			m_pCurrentCell = static_cast<fl_CellLayout *>(pCell);
			*psfh = static_cast<PL_StruxFmtHandle>(m_pCurrentCell);
		}

	}
	break;

	case PTX_EndTable:
	{
		if(m_pCurrentTL == NULL)
		{
			m_pDoc->miniDump(sdh,6);
		}

		UT_return_val_if_fail(m_pCurrentTL,false);
		UT_DEBUGMSG(("!!!! Append End Table to Shadow \n"));
		if(m_pCurrentTL->getContainerType() != FL_CONTAINER_TABLE)
		{
			UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
			return false;
		}
		*psfh = (PL_StruxFmtHandle)m_pCurrentTL;
		UT_DEBUGMSG(("SEVIOR: End table in  shadow listener \n"));
		m_pCurrentTL->setDirty();
		m_pCurrentTL->setEndTableIn();
		m_pCurrentTL = NULL;
	}
	break;
	case PTX_EndCell:
	{
		UT_ASSERT(m_pCurrentCell);
		UT_DEBUGMSG(("!!!! Append End Cell in Shadow Listener\n"));
		*psfh = (PL_StruxFmtHandle) m_pCurrentCell;
		m_pCurrentCell = NULL;
	}
	break;

	default:
		UT_ASSERT(0);
		return false;
	}
	//
	// We're not printing
	//
	return true;
}

bool fl_ShadowListener::change(PL_StruxFmtHandle /*sfh*/,
							   const PX_ChangeRecord * /*pcr*/)
{
	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);

	return false;
}

bool fl_ShadowListener::insertStrux(PL_StruxFmtHandle /*sfh*/,
									const PX_ChangeRecord * /*pcr*/,
									PL_StruxDocHandle /*sdh*/,
									PL_ListenerId /*lid*/,
									void (* /*pfnBindHandles*/)(PL_StruxDocHandle sdhNew,
																PL_ListenerId lid,
																PL_StruxFmtHandle sfhNew))
{
	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);

	return false;
}

bool fl_ShadowListener::signal(UT_uint32 /*iSignal*/)
{
	UT_ASSERT(UT_SHOULD_NOT_HAPPEN);

	return false;
}


