/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"

#ifdef DBG_UTIL

/* -----------------08.01.99 14:55-------------------
 * Und hier die Beschreibung:
 *
 * Durch die PROTOCOL-Makros wird es ermoeglicht, Ereignisse im Frame-Methoden zu protokollieren.
 * In protokollwuerdigen Stellen in Frame-Methoden muss entweder ein PROTOCOL(...) oder bei Methoden,
 * bei denen auch das Verlassen der Methode mitprotokolliert werden soll, ein PROTOCOL_ENTER(...)-Makro
 * stehen.
 * Die Parameter der PROTOCOL-Makros sind
 * 1.	Ein Pointer auf einen SwFrm, also meist "this" oder "rThis"
 * 2.	Die Funktionsgruppe z.B. PROT_MAKEALL, hierueber wird (inline) entschieden, ob dies
 * 		zur Zeit protokolliert werden soll oder nicht.
 * 3.	Die Aktion, im Normalfall 0, aber z.B. ein ACT_START bewirkt eine Einrueckung in der
 * 		Ausgabedatei, ein ACT_END nimmt dies wieder zurueck. Auf diese Art wird z.B. durch
 * 		PROTOCOL_ENTER am Anfang einer Methode eingerueckt und beim Verlassen wieder zurueck.
 * 4.	Der vierte Parameter ist ein void-Pointer, damit man irgendetwas uebergeben kann,
 * 		was in das Protokoll einfliessen kann, typesches Beispiel bei PROT_GROW muss man
 * 		einen Pointer auf den Wert, um den gegrowt werden soll, uebergeben.
 *
 *
 * Das Protokoll ist die Datei "dbg_lay.out" im aktuellen (BIN-)Verzeichnis.
 * Es enthaelt Zeilen mit FrmId, Funktionsgruppe sowie weiteren Infos.
 *
 * Was genau protokolliert wird, kann auf folgende Arten eingestellt werden:
 * 1.	Die statische Variable SwProtokoll::nRecord enthaelt die Funktionsgruppen,
 * 		die aufgezeichnet werden sollen.
 * 		Ein Wert von z.B. PROT_GROW bewirkt, das Aufrufe von SwFrm::Grow dokumentiert werden,
 *		PROT_MAKEALL protokolliert Aufrufe von xxx::MakeAll.
 *		Die PROT_XY-Werte koennen oderiert werden.
 * 		Default ist Null, es wird keine Methode aufgezeichnet.
 * 2.	In der SwImplProtocol-Klasse gibt es einen Filter fuer Frame-Typen,
 * 		nur die Methodenaufrufe von Frame-Typen, die dort gesetzt sind, werden protokolliert.
 *		Der Member nTypes kann auf Werte wie FRM_PAGE, FRM_SECTION gesetzt und oderiert werden.
 * 		Default ist 0xFFFF, d.h. alle Frame-Typen.
 * 3.	In der SwImplProtocol-Klasse gibt es einen ArrayPointer auf FrmIds, die zu ueberwachen sind.
 * 		Ist der Pointer Null, so werden alle Frames protokolliert, ansonsten nur Frames,
 * 		die in dem Array vermerkt sind.
 *
 * Eine Aufzeichnung in Gang zu setzen, erfordert entweder Codemanipulation, z.B. in
 * SwProtocol::Init() einen anderen Default fuer nRecord setzen oder Debuggermanipulation.
 * Im Debugger gibt verschiedene, sich anbietende Stellen:
 * 1.	In SwProtocol::Init() einen Breakpoint setzen und dort nRecord manipulieren, ggf.
 *		FrmIds eintragen, dann beginnt die Aufzeichnung bereits beim Programmstart.
 * 2.	Waehrend des Programmlaufs einen Breakpoint vor irgendein PROTOCOL oder PROTOCOL_ENTER-
 * 		Makro setzen, dann am SwProtocol::nRecord das unterste Bit setzen (PROT_INIT). Dies
 * 		bewirkt, dass die Funktionsgruppe des folgenden Makros aktiviert und in Zukunft
 * 		protokolliert wird.
 * 3.	Spezialfall von 2.: Wenn man 2. in der Methode SwRootFrm::Paint(..) anwendet, werden
 * 		die Aufzeichnungseinstellung aus der Datei "dbg_lay.ini" ausgelesen!
 * 		In dieser INI-Datei kann es Kommentarzeilen geben, diese beginnen mit '#', dann
 * 		sind die Sektionen "[frmid]", "[frmtype]" und "[record]" relevant.
 * 		Nach [frmid] koennen die FrameIds der zu protokollierenden Frames folgen. Gibt es
 * 		dort keine Eintraege, werden alle Frames aufgezeichnet.
 * 		Nach [frmtype] koennen FrameTypen folgen, die aufgezeichnet werden sollen, da der
 * 		Default hier allerdings USHRT_MAX ist, werden sowieso alle aufgezeichnet. Man kann
 * 		allerdings auch Typen entfernen, in dem man ein '!' vor den Wert setzt, z.B.
 * 		!0xC000 nimmt die SwCntntFrms aus der Aufzeichnung heraus.
 * 		Nach [record] folgen die Funktionsgruppen, die aufgezeichnet werden sollen, Default
 * 		ist hier 0, also keine. Auch hier kann man mit einem vorgestellten '!' Funktionen
 * 		wieder entfernen.
 * 		Hier mal ein Beispiel fuer eine INI-Datei:
 * 		------------------------------------------
 * 			#Funktionen: Alle, ausser PRTAREA
 *	 		[record] 0xFFFFFFE !0x200
 * 			[frmid]
 * 			#folgende FrmIds:
 *	 		1 2 12 13 14 15
 * 			#keine Layoutframes ausser ColumnFrms
 * 			[frmtype] !0x3FFF 0x4
 * 		------------------------------------------
 *
 * Wenn die Aufzeichnung erstmal laeuft, kann man in SwImplProtocol::_Record(...) mittels
 * Debugger vielfaeltige Manipulationen vornehmen, z.B. bezueglich FrameTypen oder FrmIds.
 *
 * --------------------------------------------------*/

#ifndef DBG_UTIL
#error Wer fummelt denn an den makefiles rum?
#endif



#include "dbg_lay.hxx"
#include <tools/stream.hxx>

#ifndef _SVSTDARR_HXX
#define _SVSTDARR_USHORTS
#define _SVSTDARR_USHORTSSORT
#define _SVSTDARR_LONGS
#include <svl/svstdarr.hxx>
#endif

#include <stdio.h>

#include "frame.hxx"
#include "layfrm.hxx"
#include "flyfrm.hxx"
#include "txtfrm.hxx"
#include "ndtxt.hxx"
#include "dflyobj.hxx"
#include <fntcache.hxx>
// OD 2004-05-24 #i28701#
#include <sortedobjs.hxx>

sal_uLong SwProtocol::nRecord = 0;
SwImplProtocol* SwProtocol::pImpl = NULL;

sal_uLong lcl_GetFrameId( const SwFrm* pFrm )
{
#ifdef DBG_UTIL
    static sal_Bool bFrameId = sal_False;
    if( bFrameId )
        return pFrm->GetFrmId();
#endif
    if( pFrm && pFrm->IsTxtFrm() )
        return ((SwTxtFrm*)pFrm)->GetTxtNode()->GetIndex();
    return 0;
}

class SwImplProtocol
{
	SvFileStream *pStream;		// Ausgabestream
	SvUShortsSort *pFrmIds;		// welche FrmIds sollen aufgezeichnet werden ( NULL == alle )
    SvLongs *pVar;              // Variables
    ByteString aLayer;          // Einrueckung der Ausgabe ("  " pro Start/End)
	sal_uInt16 nTypes;				// welche Typen sollen aufgezeichnet werden
	sal_uInt16 nLineCount;			// Ausgegebene Zeilen
	sal_uInt16 nMaxLines;			// Maximal auszugebende Zeilen
	sal_uInt8 nInitFile;				// Bereich (FrmId,FrmType,Record) beim Einlesen der INI-Datei
	sal_uInt8 nTestMode;				// Special fuer Testformatierung, es wird ggf. nur
								// innerhalb einer Testformatierung aufgezeichnet.
	void _Record( const SwFrm* pFrm, sal_uLong nFunction, sal_uLong nAct, void* pParam );
	sal_Bool NewStream();
	void CheckLine( ByteString& rLine );
	void SectFunc( ByteString &rOut, const SwFrm* pFrm, sal_uLong nAct, void* pParam );
public:
	SwImplProtocol();
	~SwImplProtocol();
	// Aufzeichnen
	void Record( const SwFrm* pFrm, sal_uLong nFunction, sal_uLong nAct, void* pParam )
		{ if( pStream ) _Record( pFrm, nFunction, nAct, pParam ); }
	sal_Bool InsertFrm( sal_uInt16 nFrmId );	// FrmId aufnehmen zum Aufzeichnen
	sal_Bool DeleteFrm( sal_uInt16 nFrmId );	// FrmId entfernen, diesen nicht mehr Aufzeichnen
	void FileInit();					// Auslesen der INI-Datei
	void ChkStream() { if( !pStream ) NewStream(); }
    void SnapShot( const SwFrm* pFrm, sal_uLong nFlags );
    void GetVar( const sal_uInt16 nNo, long& rVar )
        { if( pVar && nNo < pVar->Count() ) rVar = (*pVar)[ nNo ]; }
};

/* -----------------11.01.99 10:43-------------------
 * Durch das PROTOCOL_ENTER-Makro wird ein SwEnterLeave-Objekt erzeugt,
 * wenn die aktuelle Funktion aufgezeichnet werden soll, wird ein
 * SwImplEnterLeave-Objekt angelegt. Der Witz dabei ist, das der Ctor
 * des Impl-Objekt am Anfang der Funktion und automatisch der Dtor beim
 * Verlassen der Funktion gerufen wird. In der Basis-Implementierung ruft
 * der Ctor lediglich ein PROTOCOL(..) mit ACT_START und im Dtor ein
 * PROTOCOL(..) mit ACT_END.
 * Es lassen sich Ableitungen der Klasse bilden, um z.B. beim Verlassen
 * einer Funktion Groessenaenderungen des Frames zu dokumentieren u.v.a.m.
 * Dazu braucht dann nur noch in SwEnterLeave::Ctor(...) die gewuenschte
 * SwImplEnterLeave-Klasse angelegt zu werden.
 *
 * --------------------------------------------------*/

class SwImplEnterLeave
{
protected:
	const SwFrm* pFrm;				// Der Frame,
	sal_uLong nFunction, nAction;		// die Funktion, ggf. die Aktion
	void* pParam;					// und weitere Parameter
public:
	SwImplEnterLeave( const SwFrm* pF, sal_uLong nFunct, sal_uLong nAct, void* pPar )
		: pFrm( pF ), nFunction( nFunct ), nAction( nAct ), pParam( pPar ) {}
	virtual void Enter();			// Ausgabe beim Eintritt
	virtual void Leave();			// Ausgabe beim Verlassen
};

class SwSizeEnterLeave : public SwImplEnterLeave
{
	long nFrmHeight;
public:
	SwSizeEnterLeave( const SwFrm* pF, sal_uLong nFunct, sal_uLong nAct, void* pPar )
		: SwImplEnterLeave( pF, nFunct, nAct, pPar ), nFrmHeight( pF->Frm().Height() ) {}
	virtual void Leave();			// Ausgabe der Groessenaenderung
};

class SwUpperEnterLeave : public SwImplEnterLeave
{
	sal_uInt16 nFrmId;
public:
	SwUpperEnterLeave( const SwFrm* pF, sal_uLong nFunct, sal_uLong nAct, void* pPar )
		: SwImplEnterLeave( pF, nFunct, nAct, pPar ), nFrmId( 0 ) {}
	virtual void Enter();			// Ausgabe
	virtual void Leave();			// Ausgabe der FrmId des Uppers
};

class SwFrmChangesLeave : public SwImplEnterLeave
{
	SwRect aFrm;
public:
	SwFrmChangesLeave( const SwFrm* pF, sal_uLong nFunct, sal_uLong nAct, void* pPar )
		: SwImplEnterLeave( pF, nFunct, nAct, pPar ), aFrm( pF->Frm() ) {}
	virtual void Enter();			// keine Ausgabe
	virtual void Leave();			// Ausgabe bei Aenderung der Frm-Area
};

void SwProtocol::Record( const SwFrm* pFrm, sal_uLong nFunction, sal_uLong nAct, void* pParam )
{
	if( Start() )
	{	// Hier landen wir, wenn im Debugger SwProtocol::nRecord mit PROT_INIT(0x1) oderiert wurde
		sal_Bool bFinit = sal_False; // Dies bietet im Debugger die Moeglichkeit,
		if( bFinit )		 // die Aufzeichnung dieser Action zu beenden
		{
			nRecord &= ~nFunction;	// Diese Funktion nicht mehr aufzeichnen
			nRecord &= ~PROT_INIT;	// PROT_INIT stets zuruecksetzen
			return;
		}
		nRecord |= nFunction;		// Aufzeichnung dieser Funktion freischalten
		nRecord &= ~PROT_INIT;		// PROT_INIT stets zuruecksetzen
		if( pImpl )
			pImpl->ChkStream();
	}
	if( !pImpl )						// Impl-Object anlegen, wenn noetig
		pImpl = new SwImplProtocol();
	pImpl->Record( pFrm, nFunction, nAct, pParam );	// ...und Aufzeichnen
}

// Die folgende Funktion wird beim Anziehen der Writer-DLL durch TxtInit(..) aufgerufen
// und ermoeglicht dem Debuggenden Funktionen und/oder FrmIds freizuschalten

void SwProtocol::Init()
{
	nRecord = 0;
	XubString aName( "dbg_lay.go", RTL_TEXTENCODING_MS_1252 );
	SvFileStream aStream( aName, STREAM_READ );
	if( aStream.IsOpen() )
	{
		pImpl = new SwImplProtocol();
		pImpl->FileInit();
	}
    aStream.Close();
}

// Ende der Aufzeichnung

void SwProtocol::Stop()
{
	 if( pImpl )
	 {
		delete pImpl;
		pImpl = NULL;
        if( pFntCache )
            pFntCache->Flush();
	 }
	 nRecord = 0;
}

// Creates a more or less detailed snapshot of the layout structur

void SwProtocol::SnapShot( const SwFrm* pFrm, sal_uLong nFlags )
{
    if( pImpl )
        pImpl->SnapShot( pFrm, nFlags );
}

void SwProtocol::GetVar( const sal_uInt16 nNo, long& rVar )
{
    if( pImpl )
        pImpl->GetVar( nNo, rVar );
}

SwImplProtocol::SwImplProtocol()
    : pStream( NULL ), pFrmIds( NULL ), pVar( NULL ), nTypes( 0xffff ),
      nLineCount( 0 ), nMaxLines( USHRT_MAX ), nTestMode( 0 )
{
	NewStream();
}

sal_Bool SwImplProtocol::NewStream()
{
	XubString aName( "dbg_lay.out", RTL_TEXTENCODING_MS_1252 );
	nLineCount = 0;
	pStream = new SvFileStream( aName, STREAM_WRITE | STREAM_TRUNC );
	if( pStream->GetError() )
	{
		delete pStream;
		pStream = NULL;
	}
	return 0 != pStream;
}

SwImplProtocol::~SwImplProtocol()
{
    if( pStream )
    {
        pStream->Close();
        delete pStream;
    }
	delete pFrmIds;
    delete pVar;
}

/* -----------------11.01.99 11:03-------------------
 * SwImplProtocol::CheckLine analysiert eine Zeile der INI-Datei
 * --------------------------------------------------*/

void SwImplProtocol::CheckLine( ByteString& rLine )
{
	rLine = rLine.ToLowerAscii(); // Gross/Kleinschreibung ist einerlei
	while( STRING_LEN > rLine.SearchAndReplace( '\t', ' ' ) )
		; //nothing					// Tabs werden durch Blanks ersetzt
	if( '#' == rLine.GetChar(0) )	// Kommentarzeilen beginnen mit '#'
		return;
	if( '[' == rLine.GetChar(0) )	// Bereiche: FrmIds, Typen oder Funktionen
	{
		ByteString aTmp = rLine.GetToken( 0, ']' );
		if( "[frmid" == aTmp )		// Bereich FrmIds
		{
			nInitFile = 1;
			delete pFrmIds;
			pFrmIds = NULL;         // Default: Alle Frames aufzeichnen
		}
		else if( "[frmtype" == aTmp )// Bereich Typen
		{
			nInitFile = 2;
			nTypes = USHRT_MAX;		// Default: Alle FrmaeTypen aufzeichnen
		}
		else if( "[record" == aTmp )// Bereich Funktionen
		{
			nInitFile = 3;
			SwProtocol::SetRecord( 0 );// Default: Keine Funktion wird aufgezeichnet
		}
		else if( "[test" == aTmp )// Bereich Funktionen
		{
			nInitFile = 4; // Default:
			nTestMode = 0; // Ausserhalb der Testformatierung wird aufgezeichnet
		}
		else if( "[max" == aTmp )// maximale Zeilenzahl
		{
			nInitFile = 5; // Default:
			nMaxLines = USHRT_MAX;
		}
        else if( "[var" == aTmp )// variables
		{
            nInitFile = 6;
            if( !pVar )
                pVar = new SvLongs( 5, 5 );
        }
		else
			nInitFile = 0;			// Nanu: Unbekannter Bereich?
		rLine.Erase( 0, aTmp.Len() + 1 );
	}
	sal_uInt16 nToks = rLine.GetTokenCount( ' ' );	// Blanks (oder Tabs) sind die Trenner
	for( sal_uInt16 i=0; i < nToks; ++i )
	{
		ByteString aTok = rLine.GetToken( i, ' ' );
		sal_Bool bNo = sal_False;
		if( '!' == aTok.GetChar(0) )
		{
			bNo = sal_True;             	// Diese(n) Funktion/Typ entfernen
			aTok.Erase( 0, 1 );
		}
		if( aTok.Len() )
		{
			sal_uLong nVal;
			sscanf( aTok.GetBuffer(), "%li", &nVal );
			switch ( nInitFile )
			{
				case 1: InsertFrm( sal_uInt16( nVal ) );	// FrmId aufnehmen
						break;
				case 2: {
							sal_uInt16 nNew = (sal_uInt16)nVal;
							if( bNo )
								nTypes &= ~nNew;	// Typ entfernen
							else
								nTypes |= nNew;		// Typ aufnehmen
						}
						break;
				case 3: {
							sal_uLong nOld = SwProtocol::Record();
							if( bNo )
								nOld &= ~nVal;		// Funktion entfernen
							else
								nOld |= nVal;		// Funktion aufnehmen
							SwProtocol::SetRecord( nOld );
						}
						break;
				case 4: {
							sal_uInt8 nNew = (sal_uInt8)nVal;
							if( bNo )
								nTestMode &= ~nNew;	// TestMode zuruecksetzen
							else
								nTestMode |= nNew;		// TestMode setzen
						}
						break;
				case 5: nMaxLines = (sal_uInt16)nVal;
						break;
                case 6: pVar->Insert( (long)nVal, pVar->Count() );
                        break;
			}
		}
	}
}

/* -----------------11.01.99 11:17-------------------
 * SwImplProtocol::FileInit() liest die Datei "dbg_lay.ini"
 * im aktuellen Verzeichnis und wertet sie aus.
 * --------------------------------------------------*/
void SwImplProtocol::FileInit()
{
	XubString aName( "dbg_lay.ini", RTL_TEXTENCODING_MS_1252 );
	SvFileStream aStream( aName, STREAM_READ );
	if( aStream.IsOpen() )
	{
		ByteString aLine;
		nInitFile = 0;
		while( !aStream.IsEof() )
		{
			sal_Char c;
			aStream >> c;
			if( '\n' == c || '\r' == c )	// Zeilenende
			{
				aLine.EraseLeadingChars();
				aLine.EraseTrailingChars();
				if( aLine.Len() )
					CheckLine( aLine );		// Zeile auswerten
				aLine.Erase();
			}
			else
				aLine += c;
		}
		if( aLine.Len() )
			CheckLine( aLine );		// letzte Zeile auswerten
	}
    aStream.Close();
}

/* -----------------11.01.99 11:20-------------------
 * lcl_Start sorgt fuer Einrueckung um zwei Blanks bei ACT_START
 * und nimmt diese bei ACT_END wieder zurueck.
 * --------------------------------------------------*/
void lcl_Start( ByteString& rOut, ByteString& rLay, sal_uLong nAction )
{
	if( nAction == ACT_START )
	{
		rLay += "  ";
		rOut += " On";
	}
	else if( nAction == ACT_END )
	{
		if( rLay.Len() > 1 )
		{
			rLay.Erase( rLay.Len() - 2 );
			rOut.Erase( 0, 2 );
		}
		rOut += " Off";
	}
}

/* -----------------11.01.99 11:21-------------------
 * lcl_Flags gibt das ValidSize-, ValidPos- und ValidPrtArea-Flag ("Sz","Ps","PA")
 * des Frames aus, "+" fuer valid, "-" fuer invalid.
 * --------------------------------------------------*/

void lcl_Flags( ByteString& rOut, const SwFrm* pFrm )
{
	rOut += " Sz";
	rOut += pFrm->GetValidSizeFlag() ? '+' : '-';
	rOut += " Ps";
	rOut += pFrm->GetValidPosFlag() ? '+' : '-';
	rOut += " PA";
	rOut += pFrm->GetValidPrtAreaFlag() ? '+' : '-';
}

/* -----------------11.01.99 11:23-------------------
 * lcl_FrameType gibt den Typ des Frames in Klartext aus.
 * --------------------------------------------------*/

void lcl_FrameType( ByteString& rOut, const SwFrm* pFrm )
{
	if( pFrm->IsTxtFrm() )
		rOut += "Txt ";
	else if( pFrm->IsLayoutFrm() )
	{
		if( pFrm->IsPageFrm() )
			rOut += "Page ";
		else if( pFrm->IsColumnFrm() )
			rOut += "Col ";
		else if( pFrm->IsBodyFrm() )
		{
			if( pFrm->GetUpper() && pFrm->IsColBodyFrm() )
				rOut += "(Col)";
			rOut += "Body ";
		}
		else if( pFrm->IsRootFrm() )
			rOut += "Root ";
		else if( pFrm->IsCellFrm() )
			rOut += "Cell ";
		else if( pFrm->IsTabFrm() )
			rOut += "Tab ";
		else if( pFrm->IsRowFrm() )
			rOut += "Row ";
		else if( pFrm->IsSctFrm() )
			rOut += "Sect ";
		else if( pFrm->IsHeaderFrm() )
			rOut += "Header ";
		else if( pFrm->IsFooterFrm() )
			rOut += "Footer ";
		else if( pFrm->IsFtnFrm() )
			rOut += "Ftn ";
		else if( pFrm->IsFtnContFrm() )
			rOut += "FtnCont ";
		else if( pFrm->IsFlyFrm() )
			rOut += "Fly ";
		else
			rOut += "Layout ";
	}
	else if( pFrm->IsNoTxtFrm() )
		rOut += "NoTxt ";
	else
		rOut += "Not impl. ";
}

/* -----------------11.01.99 11:25-------------------
 * SwImplProtocol::Record(..) wird nur gerufen, wenn das PROTOCOL-Makro
 * feststellt, dass die Funktion aufgezeichnet werden soll ( SwProtocol::nRecord ).
 * In dieser Methode werden noch die beiden weiteren Einschraenkungen ueberprueft,
 * ob die FrmId und der FrameType zu den aufzuzeichnenden gehoeren.
 * --------------------------------------------------*/

void SwImplProtocol::_Record( const SwFrm* pFrm, sal_uLong nFunction, sal_uLong nAct, void* pParam )
{
	sal_uInt16 nSpecial = 0;
	if( nSpecial )	// Debugger-Manipulationsmoeglichkeit
	{
        sal_uInt16 nId = sal_uInt16(lcl_GetFrameId( pFrm ));
		switch ( nSpecial )
		{
			case 1: InsertFrm( nId ); break;
			case 2: DeleteFrm( nId ); break;
			case 3: delete pFrmIds; pFrmIds = NULL; break;
			case 4: delete pStream; pStream = NULL; break;
		}
		return;
	}
	if( !pStream && !NewStream() )
		return; // Immer noch kein Stream

    if( pFrmIds && !pFrmIds->Seek_Entry( sal_uInt16(lcl_GetFrameId( pFrm )) ) )
        return; // gehoert nicht zu den gewuenschten FrmIds

	if( !(pFrm->GetType() & nTypes) )
		return; // Der Typ ist unerwuenscht

	if( 1 == nTestMode && nFunction != PROT_TESTFORMAT )
		return; // Wir sollen nur innerhalb einer Testformatierung aufzeichnen
	sal_Bool bTmp = sal_False;
	ByteString aOut = aLayer;
    aOut += ByteString::CreateFromInt64( lcl_GetFrameId( pFrm ) );
	aOut += ' ';
	lcl_FrameType( aOut, pFrm );	// dann den FrameType
	switch ( nFunction )			// und die Funktion
	{
        case PROT_SNAPSHOT: lcl_Flags( aOut, pFrm );
                            break;
		case PROT_MAKEALL:	aOut += "MakeAll";
							lcl_Start( aOut, aLayer, nAct );
							if( nAct == ACT_START )
								lcl_Flags( aOut, pFrm );
							break;
		case PROT_MOVE_FWD: bTmp = sal_True; // NoBreak
		case PROT_MOVE_BWD: aOut += ( nFunction == bTmp ) ? "Fwd" : "Bwd";
							lcl_Start( aOut, aLayer, nAct );
							if( pParam )
							{
								aOut += ' ';
                                aOut += ByteString::CreateFromInt32( *((sal_uInt16*)pParam) );
							}
							break;
		case PROT_GROW_TST: if( ACT_START != nAct )
								return;
							aOut += "TestGrow";
							break;
		case PROT_SHRINK_TST: if( ACT_START != nAct )
								return;
							aOut += "TestShrink";
							break;
		case PROT_ADJUSTN :
		case PROT_SHRINK:   bTmp = sal_True; // NoBreak
		case PROT_GROW:		aOut += !bTmp ? "Grow" :
									( nFunction == PROT_SHRINK ? "Shrink" : "AdjustNgbhd" );
							lcl_Start( aOut, aLayer, nAct );
							if( pParam )
							{
								aOut += ' ';
                                aOut += ByteString::CreateFromInt64( *((long*)pParam) );
							}
							break;
		case PROT_POS: 		break;
		case PROT_PRTAREA:	aOut += "PrtArea";
							lcl_Start( aOut, aLayer, nAct );
							break;
		case PROT_SIZE:		aOut += "Size";
							lcl_Start( aOut, aLayer, nAct );
							aOut += ' ';
                            aOut += ByteString::CreateFromInt64( pFrm->Frm().Height() );
							break;
		case PROT_LEAF:		aOut += "Prev/NextLeaf";
							lcl_Start( aOut, aLayer, nAct );
							aOut += ' ';
							if( pParam )
							{
								aOut += ' ';
                                aOut += ByteString::CreateFromInt64( lcl_GetFrameId( (SwFrm*)pParam ) );
							}
							break;
		case PROT_FILE_INIT: FileInit();
							 aOut = "Initialize";
							break;
		case PROT_SECTION:  SectFunc( aOut, pFrm, nAct, pParam );
							break;
		case PROT_CUT:		bTmp = sal_True; // NoBreak
		case PROT_PASTE:	aOut += bTmp ? "Cut from " : "Paste to ";
                            aOut += ByteString::CreateFromInt64( lcl_GetFrameId( (SwFrm*)pParam ) );
							break;
		case PROT_TESTFORMAT: aOut += "Test";
							lcl_Start( aOut, aLayer, nAct );
							if( ACT_START == nAct )
								nTestMode |= 2;
							else
								nTestMode &= ~2;
							break;
		case PROT_FRMCHANGES:
							{
								SwRect& rFrm = *((SwRect*)pParam);
								if( pFrm->Frm().Pos() != rFrm.Pos() )
								{
									aOut += "PosChg: (";
                                    aOut += ByteString::CreateFromInt64(rFrm.Left());
									aOut += ", ";
                                    aOut += ByteString::CreateFromInt64(rFrm.Top());
									aOut += ") (";
                                    aOut += ByteString::CreateFromInt64(pFrm->Frm().Left());
									aOut += ", ";
                                    aOut += ByteString::CreateFromInt64(pFrm->Frm().Top());
									aOut += ") ";
								}
								if( pFrm->Frm().Height() != rFrm.Height() )
								{
									aOut += "Height: ";
                                    aOut += ByteString::CreateFromInt64(rFrm.Height());
									aOut += " -> ";
                                    aOut += ByteString::CreateFromInt64(pFrm->Frm().Height());
									aOut += " ";
								}
								if( pFrm->Frm().Width() != rFrm.Width() )
								{
									aOut += "Width: ";
                                    aOut += ByteString::CreateFromInt64(rFrm.Width());
									aOut += " -> ";
                                    aOut += ByteString::CreateFromInt64(pFrm->Frm().Width());
									aOut += " ";
								}
								break;
							}
	}
	*pStream << aOut.GetBuffer() << endl;	// Ausgabe
	pStream->Flush();	// Gleich auf die Platte, damit man mitlesen kann
	if( ++nLineCount >= nMaxLines )     // Maximale Ausgabe erreicht?
		SwProtocol::SetRecord( 0 );        // => Ende der Aufzeichnung
}

/* -----------------13.01.99 11:39-------------------
 * SwImplProtocol::SectFunc(...) wird von SwImplProtocol::_Record(..) gerufen,
 * hier werden die Ausgaben rund um SectionFrms abgehandelt.
 * --------------------------------------------------*/

void SwImplProtocol::SectFunc( ByteString &rOut, const SwFrm* , sal_uLong nAct, void* pParam )
{
	sal_Bool bTmp = sal_False;
	switch( nAct )
	{
		case ACT_MERGE:			rOut += "Merge Section ";
                                rOut += ByteString::CreateFromInt64( lcl_GetFrameId( (SwFrm*)pParam ) );
								break;
		case ACT_CREATE_MASTER: bTmp = sal_True; // NoBreak
		case ACT_CREATE_FOLLOW: rOut += "Create Section ";
								rOut += bTmp ? "Master to " : "Follow from ";
                                rOut += ByteString::CreateFromInt64( lcl_GetFrameId( (SwFrm*)pParam ) );
								break;
		case ACT_DEL_MASTER: 	bTmp = sal_True; // NoBreak
		case ACT_DEL_FOLLOW: 	rOut += "Delete Section ";
								rOut += bTmp ? "Master to " : "Follow from ";
                                rOut += ByteString::CreateFromInt64( lcl_GetFrameId( (SwFrm*)pParam ) );
								break;
	}
}

/* -----------------11.01.99 11:31-------------------
 * SwImplProtocol::InsertFrm(..) nimmt eine neue FrmId zum Aufzeichnen auf,
 * wenn pFrmIds==NULL, werden alle aufgezeichnet, sobald durch InsertFrm(..)
 * pFrmIds angelegt wird, werden nur noch die enthaltenen FrmIds aufgezeichnet.
 * --------------------------------------------------*/

sal_Bool SwImplProtocol::InsertFrm( sal_uInt16 nId )
{
	if( !pFrmIds )
		pFrmIds = new SvUShortsSort(5,5);
	if( pFrmIds->Seek_Entry( nId ) )
		return sal_False;
	pFrmIds->Insert( nId );
	return sal_True;
}

/* -----------------11.01.99 11:52-------------------
 * SwImplProtocol::DeleteFrm(..) entfernt eine FrmId aus dem pFrmIds-Array,
 * so dass diese Frame nicht mehr aufgezeichnet wird.
 * --------------------------------------------------*/
sal_Bool SwImplProtocol::DeleteFrm( sal_uInt16 nId )
{
	sal_uInt16 nPos;
	if( !pFrmIds || !pFrmIds->Seek_Entry( nId, &nPos ) )
		return sal_False;
	pFrmIds->Remove( nPos );
	return sal_True;
}

/*-----------------20.9.2001 10:29------------------
 * SwProtocol::SnapShot(..)
 * creates a snapshot of the given frame and its content.
 * --------------------------------------------------*/
void SwImplProtocol::SnapShot( const SwFrm* pFrm, sal_uLong nFlags )
{
    while( pFrm )
    {
        _Record( pFrm, PROT_SNAPSHOT, 0, 0);
        if( pFrm->GetDrawObjs() && nFlags & SNAP_FLYFRAMES )
        {
            aLayer += "[ ";
            const SwSortedObjs &rObjs = *pFrm->GetDrawObjs();
			for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
			{
                SwAnchoredObject* pObj = rObjs[i];
                if ( pObj->ISA(SwFlyFrm) )
                    SnapShot( static_cast<SwFlyFrm*>(pObj), nFlags );
            }
            if( aLayer.Len() > 1 )
                aLayer.Erase( aLayer.Len() - 2 );
        }
        if( pFrm->IsLayoutFrm() && nFlags & SNAP_LOWER &&
            ( !pFrm->IsTabFrm() || nFlags & SNAP_TABLECONT ) )
        {
            aLayer += "  ";
            SnapShot( ((SwLayoutFrm*)pFrm)->Lower(), nFlags );
            if( aLayer.Len() > 1 )
                aLayer.Erase( aLayer.Len() - 2 );
        }
        pFrm = pFrm->GetNext();
    }
}

/* -----------------11.01.99 11:53-------------------
 * SwEnterLeave::Ctor(..) wird vom eigentlichen (inline-)Kontruktor gerufen,
 * wenn die Funktion aufgezeichnet werden soll.
 * Die Aufgabe ist es abhaengig von der Funktion das richtige SwImplEnterLeave-Objekt
 * zu erzeugen, alles weitere geschieht dann in dessen Ctor/Dtor.
 * --------------------------------------------------*/
void SwEnterLeave::Ctor( const SwFrm* pFrm, sal_uLong nFunc, sal_uLong nAct, void* pPar )
{
	switch( nFunc )
	{
		case PROT_ADJUSTN :
		case PROT_GROW:
		case PROT_SHRINK : pImpl = new SwSizeEnterLeave( pFrm, nFunc, nAct, pPar ); break;
		case PROT_MOVE_FWD:
		case PROT_MOVE_BWD : pImpl = new SwUpperEnterLeave( pFrm, nFunc, nAct, pPar ); break;
		case PROT_FRMCHANGES : pImpl = new SwFrmChangesLeave( pFrm, nFunc, nAct, pPar ); break;
		default: pImpl = new SwImplEnterLeave( pFrm, nFunc, nAct, pPar ); break;
	}
	pImpl->Enter();
}

/* -----------------11.01.99 11:56-------------------
 * SwEnterLeave::Dtor() ruft lediglich den Destruktor des SwImplEnterLeave-Objekts,
 * ist nur deshalb nicht inline, damit die SwImplEnterLeave-Definition nicht
 * im dbg_lay.hxx zu stehen braucht.
 * --------------------------------------------------*/

void SwEnterLeave::Dtor()
{
	if( pImpl )
	{
		pImpl->Leave();
		delete pImpl;
	}
}

void SwImplEnterLeave::Enter()
{
	SwProtocol::Record( pFrm, nFunction, ACT_START, pParam );
}

void SwImplEnterLeave::Leave()
{
	SwProtocol::Record( pFrm, nFunction, ACT_END, pParam );
}

void SwSizeEnterLeave::Leave()
{
	nFrmHeight = pFrm->Frm().Height() - nFrmHeight;
	SwProtocol::Record( pFrm, nFunction, ACT_END, &nFrmHeight );
}

void SwUpperEnterLeave::Enter()
{
    nFrmId = pFrm->GetUpper() ? sal_uInt16(lcl_GetFrameId( pFrm->GetUpper() )) : 0;
	SwProtocol::Record( pFrm, nFunction, ACT_START, &nFrmId );
}

void SwUpperEnterLeave::Leave()
{
    nFrmId = pFrm->GetUpper() ? sal_uInt16(lcl_GetFrameId( pFrm->GetUpper() )) : 0;
	SwProtocol::Record( pFrm, nFunction, ACT_END, &nFrmId );
}

void SwFrmChangesLeave::Enter()
{
}

void SwFrmChangesLeave::Leave()
{
	if( pFrm->Frm() != aFrm )
		SwProtocol::Record( pFrm, PROT_FRMCHANGES, 0, &aFrm );
}

#endif // DBG_UTIL

