Milán Major

netscape-revival

1 branch
Code

lib/layout/editor.h

/* -*- Mode: C++; tab-width: 4; tabs-indent-mode: nil -*- */

//
// Editor save stuff. LWT 06/01/95
// this should be on the branch
//

#ifdef EDITOR

#ifndef _EDITOR_H
#define _EDITOR_H
#include "edttypep.h"       // include first so types are mapped.

//
//    Many of the system header files have C++ specific stuff in
//    them. If we just widely extern "C" {} the lot, we will lose.
//    So.. pre-include some that must be seen by C++.
//
#if defined(HPUX) || defined(SCO_SV)
#include "math.h"
#endif /*HPUX losing headers */

extern "C" {
#include "net.h"
//#include "glhist.h"
//#include "shist.h"
#include "merrors.h"
#include "xp.h"
#include "il.h"
#include "pa_parse.h"
#include "layout.h"
#include "bits.h"
#include "edt.h"
#include "pa_tags.h"
#include "pa_amp.h"
#include "xpassert.h"
#include "xp_file.h"
#include "libmocha.h"
#include "libi18n.h"
}

#include "garray.h"

// TRY / CATCH / RETHROW macros for minimal error recovery on macs
#if defined(XP_MAC)
#define EDT_TRY try
#define EDT_CATCH_ALL catch(...)
#define EDT_RETHROW throw
#else
#define EDT_TRY if(1)
#define EDT_CATCH_ALL else
#define EDT_RETHROW

#endif

#if defined(XP_MAC) || defined(XP_UNIX)
static void DebugBreak() {}
#endif

//#define NON_BREAKING_SPACE  160
//
#define NON_BREAKING_SPACE_STRING   "\xa0"

#define DEF_FONTSIZE    3
#define MAX_FONTSIZE    7
#define DEFAULT_HR_THICKNESS    2

#define EDT_IS_LIVEWIRE_MACRO(s) ( (s) && (s)[0] == '`' )

/* If SUPPORT_MULTICOLUMN is defined, the <MULTICOL> tag is recognized when
 * parsing HTML. Othewise it's treated as an unknown tag.
 */

/* #define SUPPORT_MULTICOLUMN */

//-----------------------------------------------------------------------------
// Edit Classes
//-----------------------------------------------------------------------------
class CEditElement;
class CEditLeafElement;
class CEditSubDocElement;
class CEditTableElement;
class CEditTableRowElement;
class CEditCaptionElement;
class CEditTableCellElement;
class CEditRootDocElement;
class CEditContainerElement;
class CEditListElement;
class CEditTextElement; 
class CEditImageElement;
class CEditHorizRuleElement;
class CEditIconElement;
class CEditEndElement;
class CEditBreakElement;
class CEditTargetElement;
class CEditMultiColumnElement;

class CParseState;
class CPrintState;
class IStreamOut;
class IStreamIn;
class CEditImageLoader;
class CEditSaveObject;
class CFileSaveObject;
struct ED_Link;
class CEditCommand;
class CEditCommandGroup;
class CPasteCommand;
class CStreamOutMemory;

#ifdef DEBUG
class CEditTestManager;
#endif

// Originally this was a sugared int32, but we ran into
// trouble working with big-endian architectures, like the
// Macintosh. So rather than being even cleverer with
// macros, I decided to make it a regular class.
// If this turns out to be a performance bottleneck
// we can always inline it.... jhp
class ED_Color {
public:
    ED_Color();
    ED_Color(LO_Color& pLoColor);
    ED_Color(int32 rgb);
    ED_Color(int r, int g, int b);
    ED_Color(LO_Color* pLoColor);
    XP_Bool IsDefined();
    XP_Bool operator==(const ED_Color& other) const;
    XP_Bool operator!=(const ED_Color& other) const;
    int Red();
    int Green();
    int Blue();
    LO_Color GetLOColor();
    long GetAsLong(); // 0xRRGGBB
	void SetUndefined();
	static ED_Color GetUndefined();
private:
    LO_Color m_color;
    XP_Bool m_bDefined;
};

// Copies the argument into a static buffer, and then upper-cases the
// buffer, and returns a pointer to the buffer. You don't own the
// result pointer, and you can't call twice in same line of code.

char* EDT_UpperCase(char*);

// Returns a string for the given alignment. Uses a static buffer, so
// don't call twice in the same line of code.

char* EDT_AlignString(ED_Alignment align);

// Backwards-compatable macros. Should clean this up some day.

#define EDT_COLOR_DEFAULT    (ED_Color())
#define EDT_RED(x)      ((x).Red())
#define EDT_GREEN(x)    ((x).Green())
#define EDT_BLUE(x)     ((x).Blue())
#define EDT_RGB(r,g,b)  (ED_Color(r,g,b)) 
#define EDT_LO_COLOR(pLoColor)  ED_Color(pLoColor)

// Editor selections

// template declarations
Declare_GrowableArray(int32,int32)                      // TXP_GrowableArray_int32
Declare_GrowableArray(ED_Link,ED_Link*)                 // TXP_GrowableArray_ED_Link
Declare_GrowableArray(EDT_MetaData,EDT_MetaData*)       // TXP_GrowableArray_EDT_MetaData
Declare_GrowableArray(pChar,char*)                      // TXP_GrowableArray_pChar
Declare_GrowableArray(CEditCommand,CEditCommand*)       // TXP_GrowableArray_CEditCommand

Declare_PtrStack(ED_Alignment,ED_Alignment)             // TXP_PtrStack_ED_Alignment
Declare_PtrStack(TagType,TagType)                       // TXP_PtrStack_TagType
Declare_PtrStack(CEditTextElement,CEditTextElement*)    // TXP_PtrStack_CEditTextElement
Declare_PtrStack(ED_TextFormat,int32)                   // TXP_PtrStack_ED_TextFormat
Declare_PtrStack(CEditCommandGroup,CEditCommandGroup*)  // TXP_PtrStack_CEditCommandGroup
Declare_PtrStack(CParseState,CParseState*)              // TXP_PtrStack_CParseState

#ifdef DEBUG
    #define DEBUG_EDIT_LAYOUT
#endif

typedef int32 ElementIndex; // For persistent insert points, a position from the start of the document.
typedef int32 ElementOffset; // Within one element, an offset into an element. Is 0 or 1, except for text.

class CEditInsertPoint
{
public:
    CEditInsertPoint();
    CEditInsertPoint(CEditElement* pElement, ElementOffset iPos);
    CEditInsertPoint(CEditLeafElement* pElement, ElementOffset iPos);
    CEditInsertPoint(CEditElement* pElement, ElementOffset iPos, XP_Bool bStickyAfter);

    XP_Bool IsStartOfElement();
    XP_Bool IsEndOfElement();
    XP_Bool IsStartOfContainer();
    XP_Bool IsEndOfContainer();
    XP_Bool IsStartOfDocument();
    XP_Bool IsEndOfDocument();

    XP_Bool GapWithBothSidesAllowed();
    XP_Bool IsLineBreak();
    XP_Bool IsSoftLineBreak();
    XP_Bool IsHardLineBreak();
    XP_Bool IsSpace(); // After insert point
    XP_Bool IsSpaceBeforeOrAfter(); // Before or after insert point

    // Move forward and backward. Returns FALSE if can't move
    CEditInsertPoint NextPosition();
    CEditInsertPoint PreviousPosition();

    XP_Bool operator==(const CEditInsertPoint& other );
    XP_Bool operator!=(const CEditInsertPoint& other );
    XP_Bool operator<(const CEditInsertPoint& other );
    XP_Bool operator<=(const CEditInsertPoint& other );
    XP_Bool operator>=(const CEditInsertPoint& other );
    XP_Bool operator>(const CEditInsertPoint& other );
    XP_Bool IsDenormalizedVersionOf(const CEditInsertPoint& other);
    intn Compare(const CEditInsertPoint& other );

#ifdef DEBUG
    void Print(IStreamOut& stream);
#endif

    CEditLeafElement* m_pElement;
    ElementOffset m_iPos;
    XP_Bool m_bStickyAfter;

private:
    CEditElement* FindNonEmptyElement( CEditElement *pStartElement );
};


class CEditSelection
{
public:
    CEditSelection();
    CEditSelection(CEditElement* pStart, intn iStartPos, CEditElement* pEnd, intn iEndPos, XP_Bool fromStart = FALSE);
    CEditSelection(const CEditInsertPoint& start, const CEditInsertPoint& end, XP_Bool fromStart = FALSE);

    XP_Bool operator==(const CEditSelection& other );
    XP_Bool operator!=(const CEditSelection& other );
    XP_Bool IsInsertPoint();
    CEditInsertPoint* GetActiveEdge();
    CEditInsertPoint* GetAnchorEdge();
    CEditInsertPoint* GetEdge(XP_Bool bEnd);

    // Like ==, except ignores bFromStart.
    XP_Bool EqualRange(CEditSelection& other);
    XP_Bool Intersects(CEditSelection& other); // Just a test; doesn't change this.
    XP_Bool Contains(CEditInsertPoint& point);
    XP_Bool Contains(CEditSelection& other);
    XP_Bool ContainsStart(CEditSelection& other);
    XP_Bool ContainsEnd(CEditSelection& other);
    XP_Bool ContainsEdge(CEditSelection& other, XP_Bool bEnd);
    XP_Bool CrossesOver(CEditSelection& other);

    XP_Bool ClipTo(CEditSelection& other); // True if the result is defined. No change to this if it isn't

    CEditElement* GetCommonAncestor();
    XP_Bool CrossesSubDocBoundary();
    CEditSubDocElement* GetEffectiveSubDoc(XP_Bool bEnd);

    XP_Bool ExpandToNotCrossStructures(); // TRUE if this was changed
    void ExpandToBeCutable();
    void ExpandToIncludeFragileSpaces();
    void ExpandToEncloseWholeContainers();
    XP_Bool EndsAtStartOfContainer();
    XP_Bool StartsAtEndOfContainer();
    XP_Bool AnyLeavesSelected(); // FALSE if insert point or container end.
    XP_Bool IsContainerEnd(); // TRUE if just contains the end of a container.
    // Useful for cut & delete, where you don't replace the final container mark
    XP_Bool ContainsLastDocumentContainerEnd();
    void ExcludeLastDocumentContainerEnd();

    CEditContainerElement* GetStartContainer();
    CEditContainerElement* GetClosedEndContainer(); // Not 1/2 open!

#ifdef DEBUG
    void Print(IStreamOut& stream);
#endif

    CEditInsertPoint m_start;
    CEditInsertPoint m_end;
    XP_Bool m_bFromStart;
};


class CPersistentEditInsertPoint
{
public:
    // m_bStickyAfter defaults to TRUE
    CPersistentEditInsertPoint();
    CPersistentEditInsertPoint(ElementIndex index);
    CPersistentEditInsertPoint(ElementIndex index, XP_Bool bStickyAfter);
#ifdef DEBUG
    void Print(IStreamOut& stream);
#endif
    XP_Bool operator==(const CPersistentEditInsertPoint& other );
    XP_Bool operator!=(const CPersistentEditInsertPoint& other );
    XP_Bool operator<(const CPersistentEditInsertPoint& other );
    XP_Bool operator<=(const CPersistentEditInsertPoint& other );
    XP_Bool operator>=(const CPersistentEditInsertPoint& other );
    XP_Bool operator>(const CPersistentEditInsertPoint& other );

    XP_Bool IsEqualUI(const CPersistentEditInsertPoint& other );

    // Used for undo
    // delta = this - other;
    void ComputeDifference(CPersistentEditInsertPoint& other, CPersistentEditInsertPoint& delta);
    // result = this + delta;
    void AddRelative(CPersistentEditInsertPoint& delta, CPersistentEditInsertPoint& result);

    ElementIndex m_index;
    // If this insert point falls between two elements, and
    // m_bStickyAfter is TRUE, then this insert point is attached
    // to the start of the second element. If m_bStickyAfter
    // is false, then this insert point is attached to the end of
    // the first element. It is FALSE by default. It is ignored
    // by all of the comparison operators except IsEqualUI. It
    // is ignored by ComputeDifference and AddRelative

     XP_Bool m_bStickyAfter;
};

#ifdef DEBUG
    #define DUMP_PERSISTENT_EDIT_INSERT_POINT(s, pt) { XP_TRACE(("%s %d", s, pt.m_index));}
#else
    #define DUMP_PERSISTENT_EDIT_INSERT_POINT(s, pt) {}
#endif

class CPersistentEditSelection
{
public:
    CPersistentEditSelection();
    CPersistentEditSelection(const CPersistentEditInsertPoint& start, const CPersistentEditInsertPoint& end);
#ifdef DEBUG
    void Print(IStreamOut& stream);
#endif
    XP_Bool operator==(const CPersistentEditSelection& other );
    XP_Bool operator!=(const CPersistentEditSelection& other );
    XP_Bool SelectedRangeEqual(const CPersistentEditSelection& other ); // Ignores m_bFromStart

    ElementIndex GetCount();
    XP_Bool IsInsertPoint();
    CPersistentEditInsertPoint* GetEdge(XP_Bool bEnd);

    // Used for undo
    // change this by the same way that original was changed into modified.
    void Map(CPersistentEditSelection& original, CPersistentEditSelection& modified);

    CPersistentEditInsertPoint m_start;
    CPersistentEditInsertPoint m_end;
    XP_Bool m_bFromStart;
};

#ifdef DEBUG
    #define DUMP_PERSISTENT_EDIT_SELECTION(s, sel) { XP_TRACE(("%s %d-%d %d", s,\
        sel.m_start.m_index,\
        sel.m_end.m_index,\
        sel.m_bFromStart));}
#else
    #define DUMP_PERSISTENT_EDIT_SELECTION(s, sel) {}
#endif

//
// Element type used during stream constructor
//
enum EEditElementType {
    eElementNone,
    eElement,
    eTextElement,
    eImageElement,
    eHorizRuleElement,
    eBreakElement,
    eContainerElement,
    eIconElement,
    eTargetElement,
    eListElement,
    eEndElement,
    eTableElement,
    eTableRowElement,
    eTableCellElement,
    eCaptionElement,
    eMultiColumnElement
};

//
// CEditElement
//
class CEditElement {
private:
    TagType m_tagType;
    CEditElement* m_pParent;
    CEditElement* m_pNext;      
    CEditElement* m_pChild;
    char *m_pTagData;               // hack! remainder of tag data for regen.

protected:
    char* GetTagData(){ return m_pTagData; };
    void SetTagData(char* tagData);

public:

    // ctor, dtor
    CEditElement(CEditElement *pParent, TagType tagType, char* pData = NULL);
    CEditElement(CEditElement *pParent, PA_Tag *pTag);
    CEditElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);
    virtual ~CEditElement();

    static CEditElement* StreamCtor( IStreamIn *pIn, CEditBuffer *pBuffer );
    static CEditElement* StreamCtorNoChildren( IStreamIn *pIn, CEditBuffer *pBuffer );
    virtual void StreamInChildren(IStreamIn* pIn, CEditBuffer* pBuffer);

    //
    // Accessor functions
    //
    CEditElement* GetParent(){ return m_pParent; }
    CEditElement* GetNextSibling(){ return m_pNext; }
    CEditElement* GetPreviousSibling();
    CEditElement* GetChild(){ return m_pChild; }
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild) {return TRUE;}
    void SetChild(CEditElement *pChild);
    void SetNextSibling(CEditElement* pNext);

    CEditElement* GetLastChild();
    CEditElement* GetLastMostChild();
    CEditElement* GetFirstMostChild();

    CEditCaptionElement* GetCaption(); // Returns containing cell, or NULL if none.
    CEditCaptionElement* GetCaptionIgnoreSubdoc(); // Returns containing cell, or NULL if none.
    CEditTableCellElement* GetTableCell(); // Returns containing cell, or NULL if none.
    CEditTableCellElement* GetTableCellIgnoreSubdoc(); // Returns containing cell, or NULL if none.
    CEditTableRowElement* GetTableRow(); // Returns containing row, or NULL if none.
    CEditTableRowElement* GetTableRowIgnoreSubdoc(); // Returns containing row, or NULL if none.
    CEditTableElement* GetTable(); // Returns containing table, or NULL if none.
    CEditTableElement* GetTableIgnoreSubdoc(); // Returns containing table, or NULL if none.
    CEditMultiColumnElement* GetMultiColumn(); // Returns containing multicolumn, or NULL if none.
    CEditMultiColumnElement* GetMultiColumnIgnoreSubdoc(); // Returns containing multicolumn, or NULL if none.
    CEditSubDocElement* GetSubDoc(); // Returns containing sub-doc, or NULL if none.
    CEditSubDocElement* GetSubDocSkipRoot(); // Returns containing sub-doc, or NULL if none.
    CEditRootDocElement* GetRootDoc(); // Returns containing root, or NULL if none.

    CEditElement* GetTopmostTableOrMultiColumn(); // Returns topmost containing table or MultiColumn, or NULL if none.
    CEditElement* GetTableOrMultiColumnIgnoreSubdoc(); // Returns containing table or MultiColumn, or NULL if none.
    CEditElement* GetSubDocOrMultiColumnSkipRoot();

    CEditBuffer* GetEditBuffer(); // returns the edit buffer if the element is in one.

    virtual ED_Alignment GetDefaultAlignment();
    virtual ED_Alignment GetDefaultVAlignment();

    TagType GetType(){ return m_tagType; }
    void SetType( TagType t ){ m_tagType = t; }

    //
    // Cast routines.
    //
    CEditTextElement* Text(){ 
        XP_ASSERT(IsText());
        return (CEditTextElement*)this;
    }

    virtual XP_Bool IsLeaf() { return FALSE; }
    CEditLeafElement* Leaf(){ XP_ASSERT(IsLeaf());
        return (CEditLeafElement*)this;
    }

    virtual XP_Bool IsRoot() { return FALSE; }
    CEditRootDocElement* Root(){ XP_ASSERT(IsRoot());
        return (CEditRootDocElement*)this;
    }

    virtual XP_Bool IsContainer() { return FALSE; }
    CEditContainerElement* Container(){ XP_ASSERT(IsContainer());
        return (CEditContainerElement*)this;
    }

    virtual XP_Bool IsList() { return FALSE; }
    CEditListElement* List(){ XP_ASSERT(IsList());
        return (CEditListElement*)this;
    }

    virtual XP_Bool IsBreak() { return FALSE; }
    CEditBreakElement* Break(){ XP_ASSERT(IsBreak());
        return (CEditBreakElement*)this;
    }

    virtual XP_Bool CausesBreakBefore() { return FALSE;}
    virtual XP_Bool CausesBreakAfter() { return FALSE;}
    virtual XP_Bool AllowBothSidesOfGap() { return FALSE; }

    virtual XP_Bool IsImage() { return FALSE; }
    CEditImageElement* Image(){
        // Not all P_IMAGE elements are images.  CEditIconElements have P_IMAGE 
        //  as their tagType but are not CEditImageElements.
        XP_ASSERT(m_tagType==P_IMAGE 
            && GetElementType() == eImageElement 
            && IsImage() ) ;
        return (CEditImageElement*)this;
    }

    virtual XP_Bool IsIcon() { return FALSE; }
    CEditIconElement* Icon(){ XP_ASSERT(IsIcon());
        return (CEditIconElement*)this;
    }

    CEditTargetElement* Target(){ XP_ASSERT(GetElementType() == eTargetElement);
        return (CEditTargetElement*)this;
    }

    CEditHorizRuleElement* HorizRule(){ XP_ASSERT(m_tagType==P_HRULE); 
        return (CEditHorizRuleElement*)this;
    }

    virtual XP_Bool IsRootDoc() { return FALSE; }
    CEditRootDocElement* RootDoc() { XP_ASSERT(IsRootDoc()); return (CEditRootDocElement*) this; }
    virtual XP_Bool IsSubDoc() { return FALSE; }
    CEditSubDocElement* SubDoc() { XP_ASSERT(IsSubDoc()); return (CEditSubDocElement*) this; }
    virtual XP_Bool IsTable() { return FALSE; }
    CEditTableElement* Table() { XP_ASSERT(IsTable()); return (CEditTableElement*) this; }
    virtual XP_Bool IsTableRow() { return FALSE; }
    CEditTableRowElement* TableRow() { XP_ASSERT(IsTableRow()); return (CEditTableRowElement*) this; }
    virtual XP_Bool IsTableCell() { return FALSE; }
    CEditTableCellElement* TableCell() { XP_ASSERT(IsTableCell()); return (CEditTableCellElement*) this; }
    virtual XP_Bool IsCaption() { return FALSE; }
    CEditCaptionElement* Caption() { XP_ASSERT(IsCaption()); return (CEditCaptionElement*) this; }
    virtual XP_Bool IsText() { return FALSE; }
    virtual XP_Bool IsMultiColumn() { return FALSE; }
    CEditMultiColumnElement* MultiColumn() { XP_ASSERT(IsMultiColumn()); return (CEditMultiColumnElement*) this; }

    XP_Bool IsEndOfDocument() { return GetElementType() == eEndElement; }
    virtual XP_Bool IsEndContainer() { return FALSE; }

    // Can this element contain a paragraph?
    virtual XP_Bool IsContainerContainer();

    XP_Bool IsA( int tagType ){ return m_tagType == tagType; }
    XP_Bool Within( int tagType );
    XP_Bool InFormattedText();
    CEditContainerElement* FindContainer(); // Returns this if this is a container.
    CEditContainerElement* FindEnclosingContainer(); // Starts search with parent of this.
    CEditElement* FindList();
    void FindList( CEditContainerElement*& pContainer, CEditListElement*& pList );
    CEditElement* FindContainerContainer();

    //
    // Tag Generation functions
    //
    virtual PA_Tag* TagOpen( int iEditOffset );
    virtual PA_Tag* TagEnd( );
    void SetTagData( PA_Tag* pTag, char* pTagData);

    //
    // Output routines.
    //
    virtual void PrintOpen( CPrintState *pPrintState );
    void InternalPrintOpen( CPrintState *pPrintState, char* pTagData );
    virtual void PrintEnd( CPrintState *pPrintState );
    virtual void StreamOut( IStreamOut *pOut );
    virtual void PartialStreamOut( IStreamOut *pOut, CEditSelection& selection );
    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection);

    virtual EEditElementType GetElementType();
    virtual XP_Bool Reduce( CEditBuffer *pBuffer );

    // Selection utilities
    XP_Bool ClipToMe(CEditSelection& selection, CEditSelection& local); // Returns TRUE if selection intersects with "this".
    virtual void GetAll(CEditSelection& selection); // Get selection that selects all of this.

    // iterators
    typedef XP_Bool (CEditElement::*PMF_EditElementTest)(void*);

    CEditElement* FindNextElement( PMF_EditElementTest pmf, void *pData );
    CEditElement* UpRight( PMF_EditElementTest pmf, void *pTestData );
    CEditElement* DownRight( PMF_EditElementTest pmf,void *pTestData, 
            XP_Bool bIgnoreThis = FALSE );

    CEditElement* FindPreviousElement( PMF_EditElementTest pmf, void *pTestData );
    CEditElement* UpLeft( PMF_EditElementTest pmf, void *pTestData );
    CEditElement* DownLeft( PMF_EditElementTest pmf, void *pTestData, 
            XP_Bool bIgnoreThis = FALSE );

    XP_Bool TestIsTrue( PMF_EditElementTest pmf, void *pTestData ){ 
                return (this->*pmf)(pTestData); }

    //
    // Search routines to be applied FindNextElement and FindPreviousElement
    //
    XP_Bool FindText( void* /*pVoid*/ );
    XP_Bool FindTextAll( void* /*pVoid*/ );
    XP_Bool FindLeaf( void* /*pVoid*/ );
    XP_Bool FindLeafAll( void* /*pVoid*/ );
    XP_Bool FindImage( void* /*pvoid*/ );
    XP_Bool FindTarget( void* /*pvoid*/ );
    XP_Bool SplitContainerTest( void* /*pVoid*/ );
    XP_Bool SplitFormattingTest( void* pVoid );

    XP_Bool FindContainer( void* /*pVoid*/ );

    //
    // Split the tree.  You can stop when pmf returns true.
    // 
    virtual CEditElement* Split(CEditElement *pLastChild, 
                                CEditElement* pCloneTree,
                                PMF_EditElementTest pmf,
                                void *pData);

    virtual CEditElement* Clone( CEditElement *pParent = 0 );
    CEditElement* CloneFormatting( TagType endType );
    virtual void Unlink();
    virtual void Merge( CEditElement *pAppendBlock, XP_Bool bCopyAppendAttributes = TRUE );
    

#ifdef DEBUG
    virtual void ValidateTree();
#endif
    CEditElement* InsertAfter( CEditElement *pPrev );
    CEditElement* InsertBefore( CEditElement *pNext );
    void InsertAsFirstChild( CEditElement *pParent );
    void InsertAsLastChild( CEditElement *pParent );

    XP_Bool IsFirstInContainer();
    CEditTextElement* TextInContainerAfter();
    CEditTextElement* PreviousTextInContainer();
    virtual CEditElement* Divide(int iOffset);

    CEditLeafElement* PreviousLeafInContainer();
    CEditLeafElement* LeafInContainerAfter();

    CEditLeafElement* PreviousLeaf() { return (CEditLeafElement*) FindPreviousElement( &CEditElement::FindLeaf, 0 ); }
    CEditLeafElement* NextLeaf() { return (CEditLeafElement*) FindNextElement( &CEditElement::FindLeaf, 0 ); }
    CEditLeafElement* NextLeafAll() { return (CEditLeafElement*) FindNextElement( &CEditElement::FindLeafAll, 0 ); }
    CEditContainerElement* NextContainer() { return (CEditContainerElement*) FindNextElement( &CEditElement::FindContainer, 0 ); }
    CEditContainerElement* PreviousContainer() { return (CEditContainerElement*) FindPreviousElement( &CEditElement::FindContainer, 0 ); }

    CEditElement* GetRoot();
    CEditElement* GetCommonAncestor(CEditElement* pOther); // NULL if not in same tree
    CEditElement* GetChildContaining(CEditElement* pDescendant); // NULL if not a descendant

    XP_Bool DeleteElement( CEditElement *pTellIfKilled );
    void DeleteChildren();

    // should be in Container class
    int GetDefaultFontSize();

    // To and from persistent insert points
    virtual CEditInsertPoint IndexToInsertPoint(ElementIndex index, XP_Bool bStickyAfter);
    virtual CPersistentEditInsertPoint GetPersistentInsertPoint(ElementOffset offset);
    virtual ElementIndex GetElementIndexOf(CEditElement* child); // asserts if argument is not a child.
    virtual ElementIndex GetPersistentCount();
    ElementIndex GetElementIndex();

    virtual void FinishedLoad( CEditBuffer* pBuffer );

protected:
    virtual void Finalize();
    void EnsureSelectableSiblings(CEditBuffer* pBuffer);

private:
    void CommonConstructor();
    // For CEditElement constructor, where the vtable isn't
    // set up enough to check if the item is acceptable.
    void RawSetNextSibling(CEditElement* pNext) {m_pNext = pNext; }
    void RawSetChild(CEditElement* pChild) {m_pChild = pChild; }
};


//
// CEditSubDocElement - base class for the root, captions, and table data cells
//
class CEditSubDocElement: public CEditElement {
public:
    CEditSubDocElement(CEditElement *pParent, int tagType, char* pData = NULL);
    CEditSubDocElement(CEditElement *pParent, PA_Tag *pTag);
    CEditSubDocElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsSubDoc() { return TRUE; }
    static CEditSubDocElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsSubDoc() ? (CEditSubDocElement*) pElement : 0; }

    virtual XP_Bool Reduce( CEditBuffer *pBuffer );
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild){
            return ! pChild.IsLeaf();
        }
    virtual void FinishedLoad( CEditBuffer* pBuffer );
};

class CEditTableElement: public CEditElement {
public:
    CEditTableElement(intn columns, intn rows);
    CEditTableElement(CEditElement *pParent, PA_Tag *pTag, ED_Alignment align);
    CEditTableElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsTable() { return TRUE; }
    static CEditTableElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsTable() ? (CEditTableElement*) pElement : 0; }
    virtual EEditElementType GetElementType() { return eTableElement; }
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild){
            return pChild.IsTableRow() || pChild.IsCaption();
        }
    virtual void FinishedLoad( CEditBuffer* pBuffer );
    void AdjustCaption();
    virtual PA_Tag* TagOpen(int iEditOffset);
    PA_Tag* InternalTagOpen(int iEditOffset, XP_Bool bPrinting);
    virtual PA_Tag* TagEnd();

    intn GetRows();
    intn GetColumns();
    void InsertRows(intn iPosition, intn number, CEditTableElement* pSource = NULL);
    void InsertColumns(intn iPosition, intn number, CEditTableElement* pSource = NULL);
    void DeleteRows(intn iPosition, intn number, CEditTableElement* pUndoContainer = NULL);
    void DeleteColumns(intn iPosition, intn number, CEditTableElement* pUndoContainer = NULL);
    CEditTableRowElement* FindRow(intn number);
    CEditTableCellElement* FindCell(intn column, intn row);
    CEditCaptionElement* GetCaption();
    void SetCaption(CEditCaptionElement*);
    void DeleteCaption();

    void SetData( EDT_TableData *pData );
    EDT_TableData* GetData( );
    EDT_TableData* ParseParams( PA_Tag *pTag );
    static EDT_TableData* NewData();
    static void FreeData( EDT_TableData *pData );

    void SetBackgroundColor( ED_Color iColor );
    ED_Color GetBackgroundColor();

    // Override printing and taging, since we don't allow ALIGN tag when editing.
    virtual void PrintOpen( CPrintState *pPrintState );
    virtual void PrintEnd( CPrintState *pPrintState );
    char* CreateTagData(EDT_TableData *pData, XP_Bool bPrinting);

    virtual void StreamOut( IStreamOut *pOut);

private:
    ED_Color m_backgroundColor;
    ED_Alignment m_align;
    ED_Alignment m_malign;
};

class CEditTableRowElement: public CEditElement {
public:
    CEditTableRowElement();
    CEditTableRowElement(intn cells);
    CEditTableRowElement(CEditElement *pParent, PA_Tag *pTag);
    CEditTableRowElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsTableRow() { return TRUE; }
    static CEditTableRowElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsTableRow() ? (CEditTableRowElement*) pElement : 0; }
    virtual EEditElementType GetElementType() { return eTableRowElement; }
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild){
            return pChild.IsTableCell();
        }
    virtual void FinishedLoad( CEditBuffer* pBuffer );

    intn GetRowIndex();

    intn GetCells();
    void InsertCells(intn iPosition, intn number, CEditTableRowElement* pSource = NULL);
    void DeleteCells(intn iPosition, intn number, CEditTableRowElement* pUndoContainer = NULL);
    CEditTableCellElement* FindCell(intn number);

    void SetData( EDT_TableRowData *pData );
    EDT_TableRowData* GetData( );
    EDT_TableRowData* ParseParams( PA_Tag *pTag );
    static EDT_TableRowData* NewData();
    static void FreeData( EDT_TableRowData *pData );
    void SetBackgroundColor( ED_Color iColor );
    ED_Color GetBackgroundColor();
private:
    ED_Color m_backgroundColor;
};

class CEditCaptionElement: public CEditSubDocElement {
public:
    CEditCaptionElement();
    CEditCaptionElement(CEditElement *pParent, PA_Tag *pTag);
    CEditCaptionElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsCaption() { return TRUE; }
    static CEditCaptionElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsCaption() ? (CEditCaptionElement*) pElement : 0; }
    virtual EEditElementType GetElementType() { return eCaptionElement; }

    void SetData( EDT_TableCaptionData *pData );
    EDT_TableCaptionData* GetData( );
    EDT_TableCaptionData* ParseParams( PA_Tag *pTag );
    static EDT_TableCaptionData* NewData();
    static void FreeData( EDT_TableCaptionData *pData );
};

// Table data and Table header

class CEditTableCellElement: public CEditSubDocElement {
public:
    CEditTableCellElement();
    CEditTableCellElement(XP_Bool bIsHeader);
    CEditTableCellElement(CEditElement *pParent, PA_Tag *pTag);
    CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsTableCell();
    static CEditTableCellElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsTableCell() ? (CEditTableCellElement*) pElement : 0; }
    virtual EEditElementType GetElementType();
    virtual ED_Alignment GetDefaultAlignment();

    XP_Bool IsTableData();

    intn GetRowIndex();
    intn GetColumnIndex();

    void SetData( EDT_TableCellData *pData );
    EDT_TableCellData* GetData( );
    EDT_TableCellData* ParseParams( PA_Tag *pTag );
    static EDT_TableCellData* NewData();
    static void FreeData( EDT_TableCellData *pData );
    void SetBackgroundColor( ED_Color iColor );
    ED_Color GetBackgroundColor();
private:
    ED_Color m_backgroundColor;
};

class CEditMultiColumnElement: public CEditElement {
public:
    CEditMultiColumnElement(intn columns);
    CEditMultiColumnElement(CEditElement *pParent, PA_Tag *pTag);
    CEditMultiColumnElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsMultiColumn() { return TRUE; }
    static CEditMultiColumnElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsMultiColumn() ? (CEditMultiColumnElement*) pElement : 0; }
    virtual EEditElementType GetElementType() { return eMultiColumnElement; }
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild){
            return pChild.IsContainer();
        }
    virtual void FinishedLoad( CEditBuffer* pBuffer );

    void SetData( EDT_MultiColumnData *pData );
    EDT_MultiColumnData* GetData( );
    EDT_MultiColumnData* ParseParams( PA_Tag *pTag );
    static EDT_MultiColumnData* NewData();
    static void FreeData( EDT_MultiColumnData *pData );

private:
};

//
// CEditRootDocElement - Top most element in the tree, tag HTML
//
class CEditRootDocElement: public CEditSubDocElement {
private:
    CEditBuffer* m_pBuffer;
public:
    CEditRootDocElement( CEditBuffer* pBuffer ): 
            CEditSubDocElement(0, P_MAX), m_pBuffer( pBuffer ){}
    virtual XP_Bool Reduce( CEditBuffer *pBuffer ){ return FALSE; }
    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection) { return FALSE; }

    virtual void FinishedLoad( CEditBuffer *pBuffer );

    virtual void PrintOpen( CPrintState *pPrintState );
    virtual void PrintEnd( CPrintState *pPrintState );

    virtual XP_Bool IsRoot() { return TRUE; }
    static CEditRootDocElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsRoot() ? (CEditRootDocElement*) pElement : 0; }
    CEditBuffer* GetBuffer(){ return m_pBuffer; }
#ifdef DEBUG
    virtual void ValidateTree();
#endif
};

class CEditLeafElement: public CEditElement {
private:
    ElementOffset m_iSize;        // fake size.
public:
    // pass through constructors.
    CEditLeafElement(CEditElement *pParent, int tagType):
            CEditElement(pParent,tagType),m_iSize(1){}

    CEditLeafElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer): 
            CEditElement(pStreamIn,pBuffer),m_iSize(1){}

    //
    // CEditElement Stuff
    //
    virtual XP_Bool IsLeaf() { return TRUE; }
    static CEditLeafElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsLeaf() ? (CEditLeafElement*) pElement : 0; }
    virtual PA_Tag* TagOpen( int iEditOffset );

    virtual void GetAll(CEditSelection& selection);
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild) {return FALSE;}
    virtual ElementIndex GetPersistentCount();

    virtual XP_Bool IsContainerContainer() { return FALSE; }

    //
    // Stuff created at this level (Leaf)
    //
    virtual void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement )=0;
    virtual void ResetLayoutElement( intn iEditOffset, 
                LO_Element* pLoElement )=0;
    virtual LO_Element* GetLayoutElement()=0;

    virtual XP_Bool GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter,
                LO_Element*& pRetElement, 
                int& pLayoutOffset )=0;       // if appropriate
    virtual ElementOffset GetLen(){ return m_iSize; }
    virtual void DeleteChar( MWContext *pContext, int iOffset ){ m_iSize = 0; }
    virtual CEditElement* Divide(int iOffset);
    virtual CEditTextElement* CopyEmptyText(CEditElement *pParent = 0);

    // HREF management...
    virtual void SetHREF( ED_LinkId ){}
    virtual ED_LinkId GetHREF(){ return ED_LINK_ID_NONE; }
    //
    // Leaf manipulation functions..
    //
    XP_Bool NextPosition(ElementOffset iOffset, CEditLeafElement*& pNew, ElementOffset& iNewOffset );
    XP_Bool PrevPosition(ElementOffset iOffset, CEditLeafElement*& pNew, ElementOffset& iNewOffset );

    virtual CPersistentEditInsertPoint GetPersistentInsertPoint(ElementOffset offset);

#ifdef DEBUG
    virtual void ValidateTree();
#endif
protected:
    void DisconnectLayoutElements(LO_Element* pElement);
    void SetLayoutElementHelper( intn desiredType, LO_Element** pElementHolder,
          intn iEditOffset, intn lo_type, 
          LO_Element* pLoElement);
    void ResetLayoutElementHelper( LO_Element** pElementHolder, intn iEditOffset, 
            LO_Element* pLoElement );
};

class CEditContainerElement: public CEditElement {
private:
    ED_Alignment m_align;
    ED_Alignment m_defaultAlign;
public:    

    static CEditContainerElement* NewDefaultContainer( CEditElement *pParent, 
            ED_Alignment align );
    CEditContainerElement(CEditElement *pParent, PA_Tag *pTag, 
            ED_Alignment eAlign = ED_ALIGN_DEFAULT);
    CEditContainerElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);
    virtual void StreamInChildren(IStreamIn* pIn, CEditBuffer* pBuffer);

    virtual void StreamOut( IStreamOut *pOut);
    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection) { return TRUE; }
    virtual XP_Bool IsContainer() { return TRUE; }
    static CEditContainerElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsContainer() ? (CEditContainerElement*) pElement : 0; }
    virtual XP_Bool IsContainerContainer() { return FALSE; }

    virtual ElementIndex GetPersistentCount();
    virtual void FinishedLoad( CEditBuffer *pBuffer );
   
    virtual PA_Tag* TagOpen( int iEditOffset );
    virtual PA_Tag* TagEnd( );
    virtual EEditElementType GetElementType();
    virtual void PrintOpen( CPrintState *pPrintState );
    virtual void PrintEnd( CPrintState *pPrintState );

    virtual CEditElement* Clone( CEditElement *pParent = 0);

    static EDT_ContainerData* NewData();
    static void FreeData( EDT_ContainerData *pData );

    virtual XP_Bool IsAcceptableChild(CEditElement& pChild) {return pChild.IsLeaf();}

    //
    // Implementation
    //
    void SetData( EDT_ContainerData *pData );
    EDT_ContainerData* GetData( );
    EDT_ContainerData* ParseParams( PA_Tag *pTag );

    void SetAlignment( ED_Alignment eAlign );
    ED_Alignment GetAlignment( ){ return m_align; }
    void CopyAttributes( CEditContainerElement *pContainer );
    XP_Bool IsEmpty();

    XP_Bool ShouldSkipTags();

#ifdef DEBUG
    virtual void ValidateTree();
#endif

    XP_Bool IsPlainFirstContainer();
    XP_Bool IsFirstContainer();
    XP_Bool SupportsAlign();
    void AlignIfEmpty( ED_Alignment eAlign );
};

class CEditListElement: public CEditElement {
private:
public:    
    CEditListElement( CEditElement *pParent, PA_Tag *pTag );
    CEditListElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    XP_Bool IsList(){ return TRUE; }
    static CEditListElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsList() ? (CEditListElement*) pElement : 0; }
    
    virtual PA_Tag* TagOpen( int iEditOffset );
    virtual CEditElement* Clone( CEditElement *pParent = 0);

    static EDT_ListData* NewData();
    static void FreeData( EDT_ListData *pData );

    // Streaming
    virtual EEditElementType GetElementType();

    //
    // Implementation
    //
    void SetData( EDT_ListData *pData );
    EDT_ListData* GetData( );
    EDT_ListData* ParseParams( PA_Tag *pTag );

    void CopyData(CEditListElement* pOther);

#ifdef DEBUG
    virtual void ValidateTree();
#endif

};

//
// CEditTextElement
//

class CEditTextElement: public CEditLeafElement {
public:
    char* m_pText;                          // pointer to actual string.
    int m_textSize;                         // number of bytes allocated 
    LO_Element* m_pFirstLayoutElement;      
    ED_TextFormat m_tf;
    intn m_iFontSize;
    ED_Color m_color;
    ED_LinkId m_href;
    char* m_pFace;
    char* m_pScriptExtra;   // <SCRIPT> tag parameters

public:
    CEditTextElement(CEditElement *pParent, char *pText);
    CEditTextElement(IStreamIn *pStreamIn, CEditBuffer* pBuffer);
    virtual ~CEditTextElement();

    virtual XP_Bool IsText() { return TRUE; }
    static CEditTextElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsText() ? (CEditTextElement*) pElement : 0; }

    // accessor functions
    char* GetText(){ return m_pText; }
    void SetText( char* pText );
    int GetSize(){ return m_textSize; }
    ElementOffset GetLen(){ return m_pText ? XP_STRLEN( m_pText ) : 0 ; }
    LO_Element* GetLayoutElement(){ return m_pFirstLayoutElement; }
    virtual void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement );
    virtual void ResetLayoutElement( intn iEditOffset, 
                LO_Element* pLoElement );

    void SetColor( ED_Color iColor );
    ED_Color GetColor(){ return m_color; }
    void SetFontSize( int iSize );
    int GetFontSize();
    void SetFontFace(char* face);
    char* GetFontFace();
    void SetScriptExtra(char* );
    char* GetScriptExtra();
    void SetHREF( ED_LinkId );
    ED_LinkId GetHREF(){ return m_href; }
    void ClearFormatting();

    void SetData( EDT_CharacterData *pData );
    EDT_CharacterData *GetData();
    void MaskData( EDT_CharacterData*& ppData );

    //
    // utility functions
    //
    XP_Bool InsertChar( int iOffset, int newChar );
    XP_Bool GetLOTextAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter, LO_TextStruct*& pRetText, 
        int& pLayoutOffset );

    // for leaf implementation.
    void DeleteChar( MWContext *pContext, int iOffset );
    XP_Bool GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter,
                LO_Element*& pRetText, 
                int& pLayoutOffset ){ 
        return GetLOTextAndOffset( iEditOffset, bEditStickyAfter, *(LO_TextStruct**)&pRetText, pLayoutOffset);
    }

    LO_TextStruct* GetLOText( int iEditOffset );

    virtual PA_Tag* TagOpen( int iEditOffset );

    // output funcitons
    virtual void PrintOpen( CPrintState *ps );
    virtual void PrintWithEscapes( CPrintState *ps );
    virtual void PrintLiteral( CPrintState *ps );
    virtual void StreamOut( IStreamOut *pOut );
    virtual void PartialStreamOut( IStreamOut *pOut, CEditSelection& selection );
    virtual EEditElementType GetElementType();
    virtual XP_Bool Reduce( CEditBuffer *pBuffer );

    CEditElement* SplitText( int iOffset );
    void DeleteText();
    XP_Bool IsOffsetEnd( int iOffset ){
        ElementOffset iLen = GetLen();
        return ( iOffset == iLen
                  || ( iOffset == iLen-1  
                        && m_pText[iLen-1] == ' ')
                );
    }
    CEditTextElement* CopyEmptyText( CEditElement *pParent = 0);
    void FormatOpenTags(PA_Tag*& pStartList, PA_Tag*& pEndList);
    void FormatTransitionTags(CEditTextElement *pNext, 
            PA_Tag*& pStart, PA_Tag*& pEndList);
    void FormatCloseTags(PA_Tag*& pStartList, PA_Tag*& pEndList);
    char* DebugFormat();
    
    void PrintTagOpen( CPrintState *pPrintState, TagType t, ED_TextFormat tf, char* pExtra=0 );
    void PrintFormatDifference( CPrintState *ps, ED_TextFormat bitsDifferent );
    void PrintFormat( CPrintState *ps, CEditTextElement *pFirst, ED_TextFormat mask );
    void PrintTagClose( CPrintState *ps, TagType t );
    void PrintPopFormat( CPrintState *ps, int iStackTop );
    ED_TextFormat PrintFormatClose( CPrintState *ps );

    XP_Bool SameAttributes(CEditTextElement *pCompare);
    void ComputeDifference(CEditTextElement *pFirst, 
        ED_TextFormat mask, ED_TextFormat& bitsCommon, ED_TextFormat& bitsDifferent);

#ifdef DEBUG
    virtual void ValidateTree();
#endif

};

//-----------------------------------------------------------------------------
//  CEditImageElement
//-----------------------------------------------------------------------------

class CEditImageElement: public CEditLeafElement {
    LO_ImageStruct *m_pLoImage;
    ED_Alignment m_align;
    char *m_pParams;            // partial parameter string 

    //EDT_ImageData *pData;
    int32 m_iHeight;
    int32 m_iWidth;
    ED_LinkId m_href;
    XP_Bool m_bSizeWasGiven;
    XP_Bool m_bSizeIsBogus;
public:
    intn m_iSaveIndex;
    intn m_iSaveLowIndex;

public:
    // pass through constructors.
    CEditImageElement(CEditElement *pParent, PA_Tag* pTag = 0, 
            ED_LinkId href = ED_LINK_ID_NONE );
    CEditImageElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer); 
    virtual ~CEditImageElement();

    LO_Element* GetLayoutElement();
    virtual void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement );
    virtual void ResetLayoutElement( intn iEditOffset, 
                LO_Element* pLoElement );

    void StreamOut( IStreamOut *pOut );
    virtual EEditElementType GetElementType();


    //
    // CEditElement implementation
    //
    PA_Tag* TagOpen( int iEditOffset );
    void PrintOpen( CPrintState *pPrintState );

    //
    // CEditLeafElement Implementation
    //
    XP_Bool IsImage() { return TRUE; }
    static CEditImageElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsImage() ? (CEditImageElement*) pElement : 0; }
    void FinishedLoad( CEditBuffer *pBuffer );
    LO_ImageStruct* GetLayoutImage(){ return m_pLoImage; }

    XP_Bool GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter, 
                LO_Element*& pRetElement, 
                int& pLayoutOffset ){ 
        pLayoutOffset = 0; 
        pRetElement = (LO_Element*)m_pLoImage;
        pLayoutOffset = iEditOffset;
        return TRUE;
    }

    int32 GetDefaultBorder();
    void SetHREF( ED_LinkId );
    ED_LinkId GetHREF(){ return m_href; }

    //
    // Implementation
    //
    void SetImageData( EDT_ImageData *pData );
    EDT_ImageData* GetImageData( );
    EDT_ImageData* ParseParams( PA_Tag *pTag );
    char* FormatParams(EDT_ImageData* pData, XP_Bool bForPrinting);

    XP_Bool SizeIsKnown();
    
    //CLM: Since HREF is a "character" attribute,
    //  we look at images for this as well
    void MaskData( EDT_CharacterData*& pData );
    EDT_CharacterData* GetCharacterData();

};


//-----------------------------------------------------------------------------
//  CEditHRuleElement
//-----------------------------------------------------------------------------

class CEditHorizRuleElement: public CEditLeafElement {
    LO_HorizRuleStruct *m_pLoHorizRule;
public:
    // pass through constructors.
    CEditHorizRuleElement(CEditElement *pParent, PA_Tag* pTag = 0 );
    CEditHorizRuleElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer); 
    virtual ~CEditHorizRuleElement();

    void StreamOut( IStreamOut *pOut);
    virtual EEditElementType GetElementType();

    //
    // CEditLeafElement Implementation
    //
    virtual XP_Bool CausesBreakBefore() { return TRUE;}
    virtual XP_Bool CausesBreakAfter() { return TRUE;}
    virtual XP_Bool AllowBothSidesOfGap() { return TRUE; }

    virtual void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement );
    virtual void ResetLayoutElement( intn iEditOffset, 
                LO_Element* pLoElement );

    LO_Element* GetLayoutElement();
    LO_HorizRuleStruct* GetLayoutHorizRule(){ return m_pLoHorizRule; }

    XP_Bool GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter, 
                LO_Element*& pRetElement, 
                int& pLayoutOffset ){ 
        pLayoutOffset = 0; 
        pRetElement = (LO_Element*)m_pLoHorizRule;
        pLayoutOffset = iEditOffset;
        return TRUE;
    }

    static EDT_HorizRuleData* NewData();
    static void FreeData( EDT_HorizRuleData *pData );

    //
    // Implementation
    //
    void SetData( EDT_HorizRuleData *pData );
    EDT_HorizRuleData* GetData( );
    static EDT_HorizRuleData* ParseParams( PA_Tag *pTag );
};

//-----------------------------------------------------------------------------
//  CEditIconElement
//-----------------------------------------------------------------------------

#define EDT_ICON_NAMED_ANCHOR           0
#define EDT_ICON_FORM_ELEMENT           1
#define EDT_ICON_UNSUPPORTED_TAG        2
#define EDT_ICON_UNSUPPORTED_END_TAG    3
#define EDT_ICON_JAVA                   4
#define EDT_ICON_PLUGIN                 5

class CEditIconElement: public CEditLeafElement {
protected:
    LO_ImageStruct *m_pLoIcon;
    TagType m_originalTagType;
    int32 m_iconTag;
    XP_Bool m_bEndTag;
    char *m_pSpoofData;

public:
    // pass through constructors.
    CEditIconElement(CEditElement *pParent, int32 iconTag, PA_Tag* pTag = 0 );
    CEditIconElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer); 
    ~CEditIconElement();

    void StreamOut( IStreamOut *pOut);
    virtual EEditElementType GetElementType(){ return eIconElement; }

    static ED_TagValidateResult ValidateTag(  char *Data, XP_Bool bNoBrackets ); 

    //
    // CEditLeafElement Implementation
    //
    XP_Bool IsIcon() { return TRUE; }
    static CEditIconElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsIcon() ? (CEditIconElement*) pElement : 0; }

    virtual void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement );
    virtual void ResetLayoutElement( intn iEditOffset, 
                LO_Element* pLoElement );

    LO_Element* GetLayoutElement(){ return (LO_Element*)m_pLoIcon; }
    LO_ImageStruct* GetLayoutIcon(){ return m_pLoIcon; }

    XP_Bool GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter, 
                LO_Element*& pRetElement, 
                int& pLayoutOffset ){ 
        pLayoutOffset = 0; 
        pRetElement = (LO_Element*)m_pLoIcon;
        pLayoutOffset = iEditOffset;
        return TRUE;
    }

    PA_Tag* TagOpen( int iEditOffset );
    void PrintOpen( CPrintState *pPrintState );
    void PrintEnd( CPrintState *pPrintState );

    char* GetData();

    // Not currently implemented.
    void SetData( char* );

    void MorphTag( PA_Tag *pTag );
    void SetSpoofData( PA_Tag* pTag );
    void SetSpoofData( char* pData );
};

//-----------------------------------------------------------------------------
//  CEditTargetElement
//-----------------------------------------------------------------------------
class CEditTargetElement: public CEditIconElement {
private:
public:
    CEditTargetElement(CEditElement *pParent, PA_Tag* pTag = 0 );
    CEditTargetElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer); 
    void StreamOut( IStreamOut *pOut);
    EEditElementType GetElementType(){ return eTargetElement; }

    PA_Tag* TagOpen( int iEditOffset );

    // Need to be rewriten in terms of m_pTagData 
    char *GetData();
    void SetData( char* pName );
};


//-----------------------------------------------------------------------------
//  CEditBreakElement
//-----------------------------------------------------------------------------

class CEditBreakElement: public CEditLeafElement {
    LO_LinefeedStruct *m_pLoLinefeed;
public:
    // pass through constructors.
    CEditBreakElement(CEditElement *pParent, PA_Tag* pTag = 0 );
    CEditBreakElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer); 
    virtual ~CEditBreakElement();

    void StreamOut( IStreamOut *pOut );
    virtual EEditElementType GetElementType();

    void PrintOpen( CPrintState *ps );

    virtual XP_Bool IsBreak() { return TRUE; }
    virtual XP_Bool CausesBreakAfter() { return TRUE;}
    static CEditBreakElement* Cast(CEditElement* pElement) {
        return pElement && pElement->IsBreak() ? (CEditBreakElement*) pElement : 0; }
    //
    // CEditLeafElement Implementation
    //
    void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement );
    virtual void ResetLayoutElement( intn iEditOffset, 
                LO_Element* pLoElement );

    LO_Element* GetLayoutElement();

    XP_Bool GetLOElementAndOffset( ElementOffset iEditOffset, XP_Bool bEditStickyAfter, 
                LO_Element*& pRetElement, 
                int& pLayoutOffset );
};

class CEditEndElement: public CEditHorizRuleElement {
public:
    CEditEndElement(CEditElement *pParent):
            CEditHorizRuleElement(pParent){
            EDT_HorizRuleData ourParams;
            ourParams.align = ED_ALIGN_LEFT;
            ourParams.size = DEFAULT_HR_THICKNESS;
            ourParams.bNoShade = FALSE;
            ourParams.iWidth = 64;
            ourParams.bWidthPercent = FALSE;
            ourParams.pExtra = 0;
            SetData(&ourParams);
            }

    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection) { return FALSE; }
    virtual void StreamOut( IStreamOut *pOut) { XP_ASSERT(FALSE); }

    void SetLayoutElement( intn iEditOffset, intn lo_type, 
                LO_Element* pLoElement ){
        CEditHorizRuleElement::SetLayoutElement(-1, lo_type, pLoElement);
    }
    
    virtual EEditElementType GetElementType() { return eEndElement; }
    virtual void PrintOpen( CPrintState *pPrintState ) {}
    virtual void PrintEnd( CPrintState *pPrintState ) {}
};

class CEditEndContainerElement: public CEditContainerElement {
public:
    CEditEndContainerElement(CEditElement *pParent) :
        CEditContainerElement(pParent, NULL, ED_ALIGN_LEFT)
        {}
    virtual void StreamOut( IStreamOut *pOut) { XP_ASSERT(FALSE); }
    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection) { return FALSE; }
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild) {return pChild.GetElementType() == eEndElement;}
    virtual void PrintOpen( CPrintState *pPrintState ) {}
    virtual void PrintEnd( CPrintState *pPrintState ) {}
    virtual XP_Bool IsEndContainer() { return TRUE; }
};

//
// Macro to compute the next size of an TextBuffer.
//
#define GROW_TEXT(x)    ((x+0x20) & ~0x1f)   // grow buffer by 32 bytes

//-----------------------------------------------------------------------------
// CEditPosition
//-----------------------------------------------------------------------------

class CEditPosition {
private:
    CEditElement* m_pElement;
    int m_iOffset;
public:
    CEditPosition( CEditElement* pElement, int iOffset = 0 ):
            m_pElement(pElement), m_iOffset( iOffset ){}

    int Offset() { return m_iOffset; }
    CEditElement* Element() { return m_pElement; }
    IsPositioned(){ return m_pElement != 0; }
};


class CEditPositionComparable: public CEditPosition {
private:
    TXP_GrowableArray_int32 m_Array;
    void CalcPosition( TXP_GrowableArray_int32* pA, CEditPosition *pPos );
public:
    CEditPositionComparable( CEditElement* pElement, int iOffset = 0 ):
            CEditPosition( pElement, iOffset ) 
    {
        CalcPosition( &m_Array, this );
    }
    
    // return -1 if passed pos is before
    // return 0 if pos is the same
    // return 1 if pos is greater
    int Compare( CEditPosition *pPos );
};

//-----------------------------------------------------------------------------
// CEditBuffer (and others)..
//-----------------------------------------------------------------------------

class CParseState {
public:
    XP_Bool bLastWasSpace;
    XP_Bool m_bInTitle;
    XP_Bool m_bInHead;
    XP_Bool m_bInJavaScript;
    int m_baseFontSize;
    TXP_PtrStack_ED_Alignment m_formatAlignStack;
    TXP_PtrStack_TagType m_formatTypeStack;
    TXP_PtrStack_CEditTextElement m_formatTextStack;
    CEditTextElement *m_pNextText;
    CStreamOutMemory *m_pJavaScript;

public:
    void Reset();
    CParseState();
    ~CParseState();
};

class CPrintState {
public:
    int m_iLevel;
    int m_iCharPos;
    XP_Bool m_bTextLast;
    IStreamOut* m_pOut;
    TXP_PtrStack_ED_TextFormat m_formatStack;
    TXP_PtrStack_CEditTextElement m_elementStack;
    CEditBuffer *m_pBuffer;
public:
    void Reset( IStreamOut *pStream, CEditBuffer *pBuffer );
};


//
// This is a structure, not a class.  It cannot be instanciated with NEW and
//  should never be derived from.
//
class CEditLinkManager;

struct ED_Link {
private:
    // Should never create on of these with a constructor!  Must 
    //  create it only through CEditLinkManager::Add
    ED_Link(){}
public:
    void AddRef(){ iRefCount++; }
    void Release(){ }
    EDT_HREFData* GetData();
    intn iRefCount;
    CEditLinkManager* pLinkManager;
    int linkOffset;
    char *hrefStr;
    char *pExtra;
};

class CEditLinkManager {
private:
    TXP_GrowableArray_ED_Link m_links;

public:
    CEditLinkManager();
    ED_Link* MakeLink( char* pStr, char* pExtra, intn iRefCount = 1 );
    ED_LinkId Add( char *pHREF, char *pExtra );
    char* GetHREF( ED_LinkId id ){ return id->hrefStr; }
    int GetHREFLen( ED_LinkId id ){ return XP_STRLEN( id->hrefStr ); }
    EDT_HREFData* GetHREFData( ED_LinkId id){ return id->GetData(); } 
    void Free( ED_LinkId id );
    void AdjustAllLinks( char *pOldURL, char* pNewURL );
};

// Relayout Flags
#define RELAYOUT_NOCARET        1       // after relaying out, don't show the caret


// Commands

class CEditBuffer;

class CEditCommand {
public:
    CEditCommand(CEditBuffer*, intn id);
    virtual ~CEditCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();
    intn GetID();
#ifdef DEBUG
    virtual void Print(IStreamOut& stream);
#endif
protected:
    CEditBuffer* GetEditBuffer() { return m_editBuffer; };
private:
    CEditBuffer* m_editBuffer;
    intn m_id;
};

// CEditCommandGroup

class CEditCommandGroup : public CEditCommand {
public:
    CEditCommandGroup(CEditBuffer*, int id);
    virtual ~CEditCommandGroup();
    void AdoptAndDo(CEditCommand* pCommand);
    virtual void Undo();
    virtual void Redo();
#ifdef DEBUG
    virtual void Print(IStreamOut& stream);
#endif
    intn GetNumberOfCommands();
private:
    TXP_GrowableArray_CEditCommand m_commands;
};

/* Default */
#define EDT_CMD_LOG_MAXHISTORY 1

class CEditCommandLog {
public:
    CEditCommandLog();
    ~CEditCommandLog();

    void AdoptAndDo(CEditCommand*);
    void Undo();
    void Redo();
    void Trim();    // Trims undo and redo command lists.

    intn GetCommandHistoryLimit();
    void SetCommandHistoryLimit(intn newLimit);

    intn GetNumberOfCommandsToUndo();
    intn GetNumberOfCommandsToRedo();

    // Returns NULL if out of range
    CEditCommand* GetUndoCommand(intn);
    CEditCommand* GetRedoCommand(intn);

    void BeginBatchChanges(CEditCommandGroup* pBatch);
    void EndBatchChanges();

#ifdef DEBUG
    void Print(IStreamOut& stream);
#endif

private:
    void FinishBatchCommands();
    void InternalAdoptAndDo(CEditCommand*);
    void Trim(intn start, intn end);
    TXP_GrowableArray_CEditCommand m_commandList;
    intn m_undoSize;
    intn m_size;
    intn m_historyLimit;
    TXP_PtrStack_CEditCommandGroup m_batch;    // Aliases

#ifdef DEBUG
    friend class CEditCommandLogRecursionCheckEntry;
    XP_Bool m_bBusy;
#endif

};

// The commands

#define kNullCommandID 0
#define kTypingCommandID 1
#define kAddTextCommandID 2
#define kDeleteTextCommandID 3
#define kCutCommandID 4
#define kPasteTextCommandID 5
#define kPasteHTMLCommandID 6
#define kPasteHREFCommandID 7
#define kChangeAttributesCommandID 8
#define kIndentCommandID 9
#define kParagraphAlignCommandID 10
#define kMorphContainerCommandID 11
#define kInsertHorizRuleCommandID 12
#define kSetHorizRuleDataCommandID 13
#define kInsertImageCommandID 14
#define kSetImageDataCommandID 15
#define kInsertBreakCommandID 16
#define kChangePageDataCommandID 17
#define kSetMetaDataCommandID 18
#define kDeleteMetaDataCommandID 19
#define kInsertTargetCommandID 20
#define kSetTargetDataCommandID 21
#define kInsertUnknownTagCommandID 22
#define kSetUnknownTagDataCommandID 23
#define kGroupOfChangesCommandID 24
#define kSetListDataCommandID 25

#define kInsertTableCommandID 26
#define kDeleteTableCommandID 27
#define kSetTableDataCommandID 28

#define kInsertTableCaptionCommandID 29
#define kSetTableCaptionDataCommandID 30
#define kDeleteTableCaptionCommandID 31

#define kInsertTableRowCommandID 32
#define kSetTableRowDataCommandID 33
#define kDeleteTableRowCommandID 34

#define kInsertTableColumnCommandID 35
#define kSetTableCellDataCommandID 36
#define kDeleteTableColumnCommandID 37

#define kInsertTableCellCommandID 38
#define kDeleteTableCellCommandID 39

#define kInsertMultiColumnCommandID 40
#define kDeleteMultiColumnCommandID 41
#define kSetMultiColumnDataCommandID 42

#define kSetSelectionCommandID 43

#define kCommandIDMax 43

// This class holds a chunk of html text.

class CEditText
{
public:
    CEditText();
    ~CEditText();

    void Clear();

    // The length is the number of chars
    char* GetChars();
    int32 Length();
    char** GetPChars();
    int32* GetPLength();
private:
    char* m_pChars;
    int32 m_iLength;
    ElementIndex m_iCount;
};

// Can save and restore a region of the document. Also saves and restores current
// selection

class CEditDataSaver {
public:
    CEditDataSaver(CEditBuffer* pBuffer);
    ~CEditDataSaver();
    void DoBegin(CPersistentEditSelection& original);
    void DoEnd(CPersistentEditSelection& modified);
    void Undo();
    void Redo();

    CPersistentEditSelection* GetOriginalDocumentSelection() { return &m_originalDocument; }
private:
    CEditBuffer* m_pEditBuffer;
    CPersistentEditSelection m_originalDocument;
    CPersistentEditSelection m_modifiedDocument;
    CPersistentEditSelection m_original;
    CPersistentEditSelection m_expandedOriginal;
    CPersistentEditSelection m_expandedModified;
    CEditText m_originalText;
    CEditText m_modifiedText;
    XP_Bool m_bModifiedTextHasBeenSaved;
#ifdef DEBUG
    int m_bDoState;
#endif
};

class CInsertTableCommand
: public CEditCommand
{
public:
    CInsertTableCommand(CEditBuffer* buffer, CEditTableElement* pTable, intn id = kInsertTableCommandID);
    virtual ~CInsertTableCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    CPasteCommand* m_helper;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CDeleteTableCommand
    : public CEditCommand
{
public:
    CDeleteTableCommand(CEditBuffer* buffer, intn id = kDeleteTableCommandID);
    virtual ~CDeleteTableCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
	CEditTableElement* m_pTable;
    CPersistentEditInsertPoint m_replacePoint;
    CPersistentEditSelection m_originalSelection;
};

class CInsertTableCaptionCommand
    : public CEditCommand
{
public:
    CInsertTableCaptionCommand(CEditBuffer* buffer, EDT_TableCaptionData *pData, intn id = kInsertTableCaptionCommandID);
    virtual ~CInsertTableCaptionCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    CEditCaptionElement* m_pOldCaption;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CDeleteTableCaptionCommand
    : public CEditCommand
{
public:
    CDeleteTableCaptionCommand(CEditBuffer* buffer, intn id = kDeleteTableCaptionCommandID);
    virtual ~CDeleteTableCaptionCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    CEditCaptionElement* m_pOldCaption;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};
class CInsertTableRowCommand
    : public CEditCommand
{
public:
    CInsertTableRowCommand(CEditBuffer* buffer, EDT_TableRowData *pData, XP_Bool bAfterCurrentRow, intn number, intn id = kInsertTableRowCommandID);
    virtual ~CInsertTableRowCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    intn m_row;
    intn m_number;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CDeleteTableRowCommand
    : public CEditCommand
{
public:
    CDeleteTableRowCommand(CEditBuffer* buffer, intn rows, intn id = kDeleteTableRowCommandID);
    virtual ~CDeleteTableRowCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    XP_Bool m_bDeletedWholeTable;
    intn m_row;
    intn m_rows;
	CEditTableElement m_table;    // Holds deleted rows
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CInsertTableColumnCommand
    : public CEditCommand
{
public:
    CInsertTableColumnCommand(CEditBuffer* buffer, EDT_TableCellData *pData, XP_Bool bAfterCurrentCell, intn number, intn id = kInsertTableColumnCommandID);
    virtual ~CInsertTableColumnCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    intn m_column;
    intn m_number;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CDeleteTableColumnCommand
    : public CEditCommand
{
public:
    CDeleteTableColumnCommand(CEditBuffer* buffer, intn rows, intn id = kDeleteTableColumnCommandID);
    virtual ~CDeleteTableColumnCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    XP_Bool m_bDeletedWholeTable;
    intn m_column;
    intn m_columns;
	CEditTableElement m_table;    // Holds deleted columns
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CInsertTableCellCommand
    : public CEditCommand
{
public:
    CInsertTableCellCommand(CEditBuffer* buffer, XP_Bool bAfterCurrentCell, intn number, intn id = kInsertTableCellCommandID);
    virtual ~CInsertTableCellCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    intn m_column;
    intn m_number;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CDeleteTableCellCommand
    : public CEditCommand
{
public:
    CDeleteTableCellCommand(CEditBuffer* buffer, intn rows, intn id = kDeleteTableCellCommandID);
    virtual ~CDeleteTableCellCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    XP_Bool m_bDeletedWholeTable;
    intn m_column;
    intn m_columns;
	CEditTableRowElement m_tableRow;    // Holds deleted cells
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CInsertMultiColumnCommand
: public CEditCommand
{
public:
    CInsertMultiColumnCommand(CEditBuffer* buffer, CEditMultiColumnElement* pMultiColumn, intn id = kInsertMultiColumnCommandID);
    virtual ~CInsertMultiColumnCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
    CPasteCommand* m_helper;
    CPersistentEditSelection m_originalSelection;
    CPersistentEditSelection m_changedSelection;
};

class CDeleteMultiColumnCommand
    : public CEditCommand
{
public:
    CDeleteMultiColumnCommand(CEditBuffer* buffer, intn id = kDeleteMultiColumnCommandID);
    virtual ~CDeleteMultiColumnCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    
private:
	CEditMultiColumnElement* m_pMultiColumn;
    CPersistentEditInsertPoint m_replacePoint;
    CPersistentEditSelection m_originalSelection;
};

class CSetMultiColumnDataCommand
    : public CEditCommand
{
public:
    // Note: Simply creating this command does the action -- this is so we don't have to explicitly copy the
    // data that's passed in.
    CSetMultiColumnDataCommand(CEditBuffer*, EDT_MultiColumnData *pMetaData, intn id = kSetMultiColumnDataCommandID);
    virtual ~CSetMultiColumnDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    EDT_MultiColumnData* m_pOldData;
    EDT_MultiColumnData* m_pNewData;
};

// CChangeAttributesCommand - for commands that changes the currently selected stuff

class CChangeAttributesCommand
    : public CEditCommand
{
public:
    CChangeAttributesCommand(CEditBuffer*, intn id = kChangeAttributesCommandID);
    // For when we know something about the affected range, and it isn't the current selection,
    // but it will be by the time "Do" is called.
    CChangeAttributesCommand(CEditBuffer*, CPersistentEditSelection& selection, intn id = kChangeAttributesCommandID);
    virtual ~CChangeAttributesCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    

protected:
    CPersistentEditSelection* GetOriginalDocumentSelection() { return m_saver.GetOriginalDocumentSelection(); }

private:
    CEditDataSaver m_saver;
};

// For indent & outdent, which affect whole paragraphs at a time.
// Expands the current selection to be at least a whole container.
class CChangeIndentCommand
    : public CEditCommand
{
public:
    CChangeIndentCommand(CEditBuffer*, intn id = kIndentCommandID);
    virtual ~CChangeIndentCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    

private:
    CEditDataSaver m_saver;
    CPersistentEditSelection m_selection;
};

class CSetListDataCommand
    : public CEditCommand
{
public:
    CSetListDataCommand(CEditBuffer*, EDT_ListData& listData, intn id = kSetListDataCommandID);
    virtual ~CSetListDataCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();    

private:
    EDT_ListData m_newData;
    EDT_ListData* m_pOldData;
};


// This is a paste/insert command

class CPasteCommand
    : public CEditCommand
{
public:
    // Aliases "text" until Do is done.
    CPasteCommand(CEditBuffer*, intn id, XP_Bool bClearOriginal, XP_Bool bPredictiveEnd = FALSE);
    virtual ~CPasteCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();
private:
    CEditDataSaver m_saver;
    CPersistentEditSelection m_selection;
    XP_Bool m_bClearOriginal;
    XP_Bool m_bPredictiveEnd;
};

class CAddTextCommand: public CEditCommand {
public:
	CAddTextCommand(CEditBuffer* buffer, int cmdNo = kAddTextCommandID);
	virtual ~CAddTextCommand();
	virtual void Do();
	virtual void Undo();
	virtual void Redo();

    virtual void AddCharStart();
    virtual void AddCharDeleteSpace();
    virtual void AddCharEnd();

private:
    CPersistentEditSelection m_addedText;
    XP_Bool m_bNeverUndone;
    XP_Bool m_bDeleteSpace;
    CEditDataSaver m_saver;
};

class CTypingCommand: public CEditCommand {
public:
	CTypingCommand(CEditBuffer* buffer, int cmdNo);
	virtual ~CTypingCommand();
	virtual void Do();
	virtual void Undo();
	virtual void Redo();
    void DeleteStart(CEditSelection& rangeOfDeletion); // Call before deleting.
    void DeleteEnd(); // Call after deleting.
    void AddCharStart(); // Call before adding.
    void AddCharDeleteSpace(); // Call before deleting a space.
    void AddCharEnd(); // Call after adding.
#ifdef DEBUG
    virtual void Print(IStreamOut& stream);
#endif

private:

    CEditCommandGroup m_commands; // The individual commands that make up the typing command
    CAddTextCommand* m_pAdder; // Alias, Non-NIL if we're currently adding text.
    CChangeAttributesCommand* m_pDeleter; // Alias, Non-NIL if we're currently deleting text.
};

// For commands that change the paragraph attributes of currently selected stuff

class CChangeParagraphCommand
    : public CChangeAttributesCommand
{
public:
    CChangeParagraphCommand(CEditBuffer*, intn id);
    virtual ~CChangeParagraphCommand();
    virtual void Undo();
    virtual void Redo();

private:
    void SwapAttributes();
    CEditContainerElement* FindContainer(CPersistentEditSelection& selection);
    TagType m_tag;
    ED_Alignment m_align;
};

// CChangePageDataCommand - for commands that change the document's page data

class CChangePageDataCommand
    : public CEditCommand
{
public:
    CChangePageDataCommand(CEditBuffer*, intn id = kChangePageDataCommandID);
    virtual ~CChangePageDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    EDT_PageData* m_oldData;
    EDT_PageData* m_newData;
};

// CSetMetaDataCommand - for changes to the document's meta data

class CSetMetaDataCommand
    : public CEditCommand
{
public:
    // Note: Simply creating this command does the action -- this is so we don't have to explicitly copy the
    // meta data that's passed in.
    CSetMetaDataCommand(CEditBuffer*, EDT_MetaData *pMetaData, XP_Bool bDelete, intn id = kSetMetaDataCommandID);
    virtual ~CSetMetaDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    XP_Bool m_bNewItem;    // This command creates an item.
    XP_Bool m_bDelete;     // This command deletes an item.
    EDT_MetaData* m_pOldData;
    EDT_MetaData* m_pNewData;
};

class CSetTableDataCommand
    : public CEditCommand
{
public:
    // Note: Simply creating this command does the action -- this is so we don't have to explicitly copy the
    // data that's passed in.
    CSetTableDataCommand(CEditBuffer*, EDT_TableData *pMetaData, intn id = kSetTableDataCommandID);
    virtual ~CSetTableDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    EDT_TableData* m_pOldData;
    EDT_TableData* m_pNewData;
};

class CSetTableCaptionDataCommand
    : public CEditCommand
{
public:
    // Note: Simply creating this command does the action -- this is so we don't have to explicitly copy the
    // data that's passed in.
    CSetTableCaptionDataCommand(CEditBuffer*, EDT_TableCaptionData *pMetaData, intn id = kSetTableCaptionDataCommandID);
    virtual ~CSetTableCaptionDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    EDT_TableCaptionData* m_pOldData;
    EDT_TableCaptionData* m_pNewData;
};

class CSetTableRowDataCommand
    : public CEditCommand
{
public:
    // Note: Simply creating this command does the action -- this is so we don't have to explicitly copy the
    // data that's passed in.
    CSetTableRowDataCommand(CEditBuffer*, EDT_TableRowData *pMetaData, intn id = kSetTableRowDataCommandID);
    virtual ~CSetTableRowDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    EDT_TableRowData* m_pOldData;
    EDT_TableRowData* m_pNewData;
};

class CSetTableCellDataCommand
    : public CEditCommand
{
public:
    // Note: Simply creating this command does the action -- this is so we don't have to explicitly copy the
    // data that's passed in.
    CSetTableCellDataCommand(CEditBuffer*, EDT_TableCellData *pMetaData, intn id = kSetTableCellDataCommandID);
    virtual ~CSetTableCellDataCommand();
    virtual void Undo();
    virtual void Redo();

private:
    EDT_TableCellData* m_pOldData;
    EDT_TableCellData* m_pNewData;
};

class CSetSelectionCommand
    : public CEditCommand
{
public:
    CSetSelectionCommand(CEditBuffer*, CEditSelection& selection, intn id = kSetSelectionCommandID);
    virtual ~CSetSelectionCommand();
    virtual void Do();
    virtual void Undo();
    virtual void Redo();

private:
    CPersistentEditSelection m_OldSelection;
    CPersistentEditSelection m_NewSelection;
};

// Generic timer callback

extern "C" {
void CEditTimerCallback (void * closure);
}

class CEditTimer {
public:
    CEditTimer();
    virtual ~CEditTimer();
protected:
    void SetTimeout(uint32 msecs);
    void Callback();
    void Cancel();
    XP_Bool IsTimeoutEnabled() { return m_timeoutEnabled; }
    virtual void OnCallback();
private:
    friend void CEditTimerCallback (void * closure);
    XP_Bool m_timeoutEnabled;
    void*   m_timerID;
};

class CFinishLoadTimer : public CEditTimer {
public:
    CFinishLoadTimer();
    void FinishedLoad(CEditBuffer* pBuffer);
protected:
    virtual void OnCallback();
private:
    CEditBuffer* m_pBuffer;
};

class CRelayoutTimer : public CEditTimer {
public:
    CRelayoutTimer();
    void SetEditBuffer(CEditBuffer* pBuffer);
    void Relayout(CEditElement *pStartElement, int iOffset);
    void Flush();
protected:
    virtual void OnCallback();
private:
    CEditBuffer* m_pBuffer;
    CEditElement* m_pStartElement;
    int m_iOffset;
};

class CAutoSaveTimer : public CEditTimer {
public:
    CAutoSaveTimer();
    void SetEditBuffer(CEditBuffer* pBuffer);
    void SetPeriod(int32 minutes);
    int32 GetPeriod();
    void Restart();
protected:
    virtual void OnCallback();
private:
    CEditBuffer* m_pBuffer;
    int32 m_iMinutes;
};

//
// CEditBuffer
//

// Matches LO_NA constants, plus up/down

#define EDT_NA_CHARACTER 0
#define EDT_NA_WORD 1
#define EDT_NA_LINEEDGE 2
#define EDT_NA_DOCUMENT 3
#define EDT_NA_UPDOWN 4
#define EDT_NA_COUNT 5

class CEditBuffer { 
public:
    CEditRootDocElement *m_pRoot;
    // To do: replace m_pCurrent et. al. with a CEditInsertPoint
    CEditLeafElement *m_pCurrent;
    ElementOffset m_iCurrentOffset;
    XP_Bool m_bCurrentStickyAfter;
            
    CEditElement* m_pCreationCursor;
    ED_Color m_colorText;
    ED_Color m_colorBackground;
    ED_Color m_colorLink;
    ED_Color m_colorFollowedLink;
    ED_Color m_colorActiveLink;
    char *m_pTitle;
    char *m_pBackgroundImage;
    char *m_pBodyExtra;
    CEditImageLoader *m_pLoadingImage;
    CFileSaveObject *m_pSaveObject;

    int m_hackFontSize;             // not a real implementation, just for testing
    XP_Bool m_bDirty;                  // dirty flag
    MWContext *m_pContext;

    int32 m_iDesiredX;              
    int32 m_lastTopY;
    XP_Bool m_inScroll;
    XP_Bool m_bBlocked;                // we are doing the initial layout so we 
                                    //  are blocked.
    XP_Bool m_bSelecting;
    XP_Bool m_bNoRelayout;               // maybe should be a counter
    CEditElement *m_pSelectStart;
    int m_iSelectOffset;

    int m_preformatLinePos;
    XP_Bool m_bInPreformat;             // semaphore to keep us from reentering
                                    //  NormalizePreformat


    CPrintState printState;
    CEditLinkManager linkManager;
    TXP_GrowableArray_EDT_MetaData m_metaData;

    // Get time and save in m_FileSaveTime
    // Public so CEditFileSave can call it
    void  GetFileWriteTime();

private:
    CParseState*    GetParseState();
    void            PushParseState();
    void            PopParseState();
    void            ResetParseStateStack();
    TXP_PtrStack_CParseState    m_parseStateStack;

    // CFileFileSaveObject->m_status copied here
    //  to return a result even if save object is deleted
    ED_FileError m_status;

    CEditCommandLog m_commandLog;
    CTypingCommand* m_pTypingCommand;
    XP_Bool m_bLayoutBackpointersDirty; 
    //CLM: Save the current file time to
    //     check if an outside editor changed our source
    int32 m_iFileWriteTime;

#ifdef DEBUG
    friend CEditTestManager;
    CEditTestManager* m_pTestManager;
    XP_Bool m_bSuppressPhantomInsertPointCheck;
    XP_Bool m_bSkipValidation;
#endif

public:
    CEditBuffer( MWContext* pContext );
    ~CEditBuffer();
    CEditElement* CreateElement(PA_Tag* pTag, CEditElement *pParent);
    CEditElement* CreateFontElement(PA_Tag* pTag, CEditElement *pParent);
    intn ParseTag(pa_DocData *data_doc, PA_Tag* pTag, intn status);
    void FinishedLoad();
    void FinishedLoad2();
    void DummyCharacterAddedDuringLoad();

    void PrintTree( CEditElement* m_pRoot );
    void DebugPrintTree( CEditElement* m_pRoot );
    
    void PrintDocumentHead( CPrintState *pPrintState );
    void PrintDocumentEnd( CPrintState *pPrintState );
    void AppendTitle( char* pTitleString );

    void RepairAndSet(CEditSelection& selection);
    XP_Bool Reduce( CEditElement* pRoot );
    void NormalizeTree( );
    CEditElement* FindRelayoutStart( CEditElement *pStartElement );
    void Relayout( CEditElement *pStartElement, int iOffset, 
            CEditElement *pEndElement = 0, intn relayoutFlags = 0 );
    void SetCaret();
    void InternalSetCaret(XP_Bool bRevealPosition);
    LO_Position GetEffectiveCaretLOPosition(CEditElement* pElement, intn iOffset, XP_Bool bStickyAfter);
    void RevealPosition(CEditElement* pElement, int iOffset, XP_Bool bStickyAfter);
    void RevealSelection();
    XP_Bool GetLOCaretPosition(CEditElement* pElement, int iOffset, XP_Bool bStickyAfter,
        int32& targetX, int32& targetYLow, int32& targetYHigh);
    void WindowScrolled();
    ED_ElementType GetCurrentElementType(); /* CM: "Current" is superfluous! */

    // 
    // Doesn't allocate.  Returns the base URL of the document if it has been
    //  saved.  NULL if it hasn't.
    //
    char* GetBaseURL(){ return LO_GetBaseURL( m_pContext); }

    // editor buffer commands
    EDT_ClipboardResult InsertChar( int newChar, XP_Bool bTyping );
    EDT_ClipboardResult DeletePreviousChar();
    EDT_ClipboardResult DeleteNextChar();
    EDT_ClipboardResult DeleteChar(XP_Bool bForward, XP_Bool bTyping = TRUE);
    EDT_ClipboardResult TypingDeleteSelection(CTypingCommand* cmd);
    CPersistentEditSelection GetEffectiveDeleteSelection();

    XP_Bool PreviousChar( XP_Bool bSelect );
    XP_Bool PrevPosition(CEditLeafElement *pEle, ElementOffset iOffset, CEditLeafElement*& pNew, 
            ElementOffset& iNewOffset );
    XP_Bool NextPosition(CEditLeafElement *pEle, ElementOffset iOffset, CEditLeafElement*& pNew, 
            ElementOffset& iNewOffset );
    void SelectNextChar( );
    void SelectPreviousChar( );
    void NextChar( XP_Bool bSelect );
    void UpDown( XP_Bool bSelect, XP_Bool bForward );
    void NavigateChunk( XP_Bool bSelect, intn chunkType, XP_Bool bForward );
    void EndOfLine( XP_Bool bSelect );
    //void BeginOfDocument( XP_Bool bSelect );
    //void EndOfDocument( XP_Bool bSelect );
    EDT_ClipboardResult ReturnKey(XP_Bool bTyping);
    EDT_ClipboardResult InternalReturnKey(XP_Bool bRelayout);
    void Indent();
    void IndentContainer( CEditContainerElement *pContainer, 
            CEditListElement *pList );
    void Outdent();
    void OutdentContainer( CEditContainerElement *pContainer, 
            CEditListElement *pList );
    void MorphContainer( TagType t );
    void SetParagraphAlign( ED_Alignment eAlign );
    ED_Alignment GetParagraphAlign();
    void FormatCharacter( ED_TextFormat tf );

    void SetCharacterData( EDT_CharacterData *pData );
    void SetCharacterDataSelection( EDT_CharacterData *pData );
    EDT_CharacterData* GetCharacterData( );

    void SetDisplayParagraphMarks(XP_Bool bDisplay);
    XP_Bool GetDisplayParagraphMarks();

    void SetDisplayTables(XP_Bool bDisplay);
    XP_Bool GetDisplayTables();

    ED_TextFormat GetCharacterFormatting();
    TagType GetParagraphFormatting();
    int GetFontSize();
    void SetFontSize(int n); 
    void SetFontSizeSelection(int n); 
    ED_Color GetFontColor();
    void SetFontColor(ED_Color n); 
    void SetFontColorSelection(ED_Color n); 
    void InsertLeaf(CEditLeafElement *pLeaf);
    void InsertNonLeaf( CEditElement* pNonLeaf);
    EDT_ImageData* GetImageData();
    void SetImageData( EDT_ImageData* pData, XP_Bool bKeepImagesWithDoc );
    void InsertImage( EDT_ImageData* pData );
    void LoadImage( EDT_ImageData* pData, XP_Bool bKeepImagesWithDoc, XP_Bool bReplaceImage );

    // Tables

    // Like GetInsertPoint, but handles the right edge of the
    // selection differently: If the right edge of a non-empty
    // selection is at the edge of the table cell, the insert point
    // is moved inside the table cell. This gives the
    // correct behavior for table operations.
    void GetTableInsertPoint(CEditInsertPoint& ip);

    XP_Bool IsInsertPointInTable();
    XP_Bool IsInsertPointInNestedTable();
    EDT_TableData* GetTableData();
    void SetTableData(EDT_TableData *pData);
    void InsertTable(EDT_TableData *pData);
    void DeleteTable();

    XP_Bool IsInsertPointInTableCaption();
    EDT_TableCaptionData* GetTableCaptionData();
    void SetTableCaptionData(EDT_TableCaptionData *pData);
    void InsertTableCaption(EDT_TableCaptionData *pData);
    void DeleteTableCaption();

    XP_Bool IsInsertPointInTableRow();
    EDT_TableRowData* GetTableRowData();
    void SetTableRowData(EDT_TableRowData *pData);
    void InsertTableRows(EDT_TableRowData *pData, XP_Bool bAfterCurrentRow, intn number);
    void DeleteTableRows(intn number);

    XP_Bool IsInsertPointInTableCell();
    EDT_TableCellData* GetTableCellData();
    void SetTableCellData(EDT_TableCellData *pData);
    void InsertTableCells(EDT_TableCellData *pData, XP_Bool bAfterCurrentCell, intn number);
    void DeleteTableCells(intn number);
    void InsertTableColumns(EDT_TableCellData *pData, XP_Bool bAfterCurrentColumn, intn number);
    void DeleteTableColumns(intn number);

    XP_Bool IsInsertPointInMultiColumn();
    EDT_MultiColumnData* GetMultiColumnData();
    void SetMultiColumnData(EDT_MultiColumnData *pData);
    void InsertMultiColumn(EDT_MultiColumnData *pData);
    void DeleteMultiColumn();

    EDT_PageData* GetPageData();
    void SetPageData( EDT_PageData* pData );
    static void FreePageData( EDT_PageData* pData );

    intn MetaDataCount( );
    intn FindMetaData( EDT_MetaData *pMetaData );
    intn FindMetaDataIndex( EDT_MetaData *pMetaData );
    EDT_MetaData* MakeMetaData( XP_Bool bHttpEquiv, char *pName, char*pContent );
    EDT_MetaData* GetMetaData( intn n );
    void SetMetaData( EDT_MetaData *pMetaData );
    void DeleteMetaData( EDT_MetaData *pMetaData );
    static void FreeMetaData( EDT_MetaData *pMetaData );
    void ParseMetaTag( PA_Tag *pTag );
    void PrintMetaData( CPrintState *pPrintState );

    EDT_HorizRuleData* GetHorizRuleData();
    void SetHorizRuleData( EDT_HorizRuleData* pData );
    void InsertHorizRule( EDT_HorizRuleData* pData );

    char* GetTargetData();
    void SetTargetData( char* pData );
    void InsertTarget( char* pData );
    char* GetAllDocumentTargets();
    char* GetAllDocumentTargetsInFile(char *pHref);  //CLM
    char* GetAllDocumentFiles();

    char* GetUnknownTagData();
    void SetUnknownTagData( char* pData );
    void InsertUnknownTag( char* pData );

    EDT_ListData* GetListData();
    void SetListData( EDT_ListData* pData );

    void InsertBreak( ED_BreakType eBreak, XP_Bool bTyping = TRUE );

    void SetInsertPoint( CEditLeafElement* pElement, int iOffset, XP_Bool bStickyAfter );
    void FixupInsertPoint();
    void FixupInsertPoint(CEditInsertPoint& ip);
    EDT_ClipboardResult DeleteSelection(XP_Bool bCopyAppendAttributes = TRUE);
    EDT_ClipboardResult DeleteSelection(CEditSelection& selection, XP_Bool bCopyAppendAttributes = TRUE);
    void DeleteBetweenPoints( CEditLeafElement* pBegin, CEditLeafElement* pEnd, XP_Bool bCopyAppendAttributes = TRUE );
    void PositionCaret( int32 x, int32 y);
    void StartSelection( int32 x, int32 y , XP_Bool doubleClick);
    void SelectObject( int32 x, int32 y);
    void ExtendSelection( int32 x, int32 y );
    void ExtendSelectionElement( CEditLeafElement *pElement, int iOffset, XP_Bool bStickyAfter );
    void EndSelection(int32 x, int32 y);
    void EndSelection();
    void SelectAll();
    void SelectTable();
    void SelectTableCell();
 
    void SelectRegion(CEditLeafElement *pBegin, intn iBeginPos, 
            CEditLeafElement* pEnd, intn iEndPos, XP_Bool bFromStart = FALSE,
            XP_Bool bForward = FALSE );
    void SetSelection(CEditSelection& selection);
    void SetInsertPoint(CEditInsertPoint& insertPoint);
    void SetInsertPoint(CPersistentEditInsertPoint& insertPoint);

    void GetInsertPoint( CEditLeafElement** ppLeaf, ElementOffset *pOffset, XP_Bool * pbStickyAfter);
    XP_Bool GetPropertyPoint( CEditLeafElement** ppLeaf, ElementOffset *pOffset);
    CEditElement *GetSelectedElement();
    void SelectCurrentElement();
    void ClearSelection( XP_Bool bResyncInsertPoint = TRUE, XP_Bool bKeepLeft = FALSE );
    void BeginSelection( XP_Bool bExtend = FALSE, XP_Bool bFromStart = FALSE );
    void MakeSelectionEndPoints( CEditLeafElement*& pBegin, CEditLeafElement*& pEnd );
    void MakeSelectionEndPoints( CEditSelection& selection, CEditLeafElement*& pBegin, CEditLeafElement*& pEnd );
    void GetSelection( CEditLeafElement*& pStartElement, ElementOffset& iStartOffset,
                CEditLeafElement*& pEndElement, ElementOffset& iEndOffset, XP_Bool& bFromStart );
    void FormatCharacterSelection( ED_TextFormat tf );

    int Compare( CEditElement *p1, int i1Offset, CEditElement *p2, int i2Offset );

    void FileFetchComplete(ED_FileError m_status = ED_ERROR_NONE);

    void AutoSaveCallback();
    void SetAutoSavePeriod(int32 minutes);
    int32 GetAutoSavePeriod();

    void ClearMove(XP_Bool bFlushRelayout = TRUE);
    XP_Bool IsSelected(){ return LO_IsSelected( m_pContext ); }
    XP_Bool IsSelecting(){ return m_bSelecting; }
    XP_Bool IsBlocked(){ return this == 0 || m_bBlocked; }
    XP_Bool IsPhantomInsertPoint();
    XP_Bool IsPhantomInsertPoint(CEditInsertPoint& ip);
    void ClearPhantomInsertPoint();
    XP_Bool GetDirtyFlag(){ return m_bDirty; }
    void SetDirtyFlag( XP_Bool bValue ){ m_bDirty = bValue; }

    EDT_ClipboardResult CanCut(XP_Bool bStrictChecking);
    EDT_ClipboardResult CanCut(CEditSelection& selection, XP_Bool bStrictChecking);
    EDT_ClipboardResult CanCopy(XP_Bool bStrictChecking);
    EDT_ClipboardResult CanCopy(CEditSelection& selection, XP_Bool bStrictChecking);
    EDT_ClipboardResult CanPaste(XP_Bool bStrictChecking);
    EDT_ClipboardResult CanPaste(CEditSelection& selection, XP_Bool bStrictChecking);

    XP_Bool CanSetHREF();
    char *GetHREF();
    char *GetHREFText();
    void SetHREF( char *pHREF, char *pExtra );
    void SetHREFSelection( ED_LinkId eId ); 

    // Accessor functions

    ED_FileError SaveFile(char * pSourceURL,
                          char * pDestURL,
                          XP_Bool   bSaveAs,
                          XP_Bool   bKeepImagesWithDoc,
                          XP_Bool   bAutoAdjustLinks );

    ED_FileError WriteBufferToFile( char* pFileName );

    //CLM: Get current time and return TRUE if it changed
    XP_Bool IsFileModified();

    // saves the edit buffer to a file.
    //CM: was char *pFileName, added return value 0 for success, -1 if fail
    int  WriteToFile( XP_File hFile ); 
    void WriteToBuffer( char **PpBuffer );
#ifdef DEBUG
    void DebugPrintState(IStreamOut& stream);
    void ValidateTree();
    void SuppressPhantomInsertPointCheck(XP_Bool bSuppress);
#endif
    void DisplaySource();
    char* NormalizeText( char* pSrc );
    intn NormalizePreformatText( pa_DocData *pData, PA_Tag *pTag, intn status );
    MWContext *GetContext(){ return m_pContext; }
    EDT_ClipboardResult PasteText(char *pText);
    EDT_ClipboardResult PasteHTML(char *pText, XP_Bool bUndoRedo);
    EDT_ClipboardResult PasteExternalHTML(char *pText);
    EDT_ClipboardResult PasteHTML(IStreamIn& stream, XP_Bool bUndoRedo);
    EDT_ClipboardResult PasteHREF( char **ppHref, char **ppTitle, int iCount);
    EDT_ClipboardResult CopySelection( char **ppText, int32* pTextLen,
            char **ppHtml, int32* pHtmlLen);

    XP_Bool CopyBetweenPoints( CEditElement *pBegin,
                    CEditElement *pEnd, char **ppText, int32* pTextLen,
                    char **ppHtml, int32* pHtmlLen );
    XP_Bool CopySelectionContents( CEditSelection& selection,
                    char **ppHtml, int32* pHtmlLen );
    XP_Bool CopySelectionContents( CEditSelection& selection,
                    IStreamOut& stream );
    EDT_ClipboardResult CutSelection( char **ppText, int32* pTextLen,
            char **ppHtml, int32* pHtmlLen);
    XP_Bool CutSelectionContents( CEditSelection& selection,
                    char **ppHtml, int32* pHtmlLen );

    void RefreshLayout();
    void FixupSpace( XP_Bool bTyping = FALSE);
    ED_Alignment GetCurrentAlignment();
    int32 GetDesiredX(CEditElement* pEle, intn iOffset, XP_Bool bStickyAfter);

    // Selection utility methods
    void GetInsertPoint(CEditInsertPoint& insertPoint);
    void GetInsertPoint(CPersistentEditInsertPoint& insertPoint);
    void GetSelection(CEditSelection& selection);
    void GetSelection(CPersistentEditSelection& persistentSelection);
    void SetSelection(CPersistentEditSelection& persistentSelection);

    // Navigation
    XP_Bool Move(CEditInsertPoint& pt, XP_Bool forward);
    XP_Bool Move(CPersistentEditInsertPoint& pt, XP_Bool forward);
    XP_Bool CanMove(CEditInsertPoint& pt, XP_Bool forward);
    XP_Bool CanMove(CPersistentEditInsertPoint& pt, XP_Bool forward);

    void CopyEditText(CPersistentEditSelection& selection, CEditText& text);
    void CopyEditText(CEditText& text);
    void CutEditText(CEditText& text);
    void PasteEditText(CEditText& text);

    // Persistent to regular selection conversion routines
    CEditInsertPoint PersistentToEphemeral(CPersistentEditInsertPoint& persistentInsertPoint);
    CPersistentEditInsertPoint EphemeralToPersistent(CEditInsertPoint& insertPoint);
    CEditSelection PersistentToEphemeral(CPersistentEditSelection& persistentInsertPoint);
    CPersistentEditSelection EphemeralToPersistent(CEditSelection& insertPoint);

    // Command methods
    void AdoptAndDo(CEditCommand*);
    void Undo();
    void Redo();
    void Trim();

    // History limits
    intn GetCommandHistoryLimit();
    void SetCommandHistoryLimit(intn newLimit);

    // Returns NULL if out of range
    CEditCommand* GetUndoCommand(intn);
    CEditCommand* GetRedoCommand(intn);

    void BeginBatchChanges(intn commandID);
    void EndBatchChanges();

    // Typing command methods.
    void DoneTyping();
    CTypingCommand* GetTypingCommand(); // Creates it if needed
    // Hook for typing command to inform editor it's been deleted.
    void TypingDeleted(CTypingCommand* command);

    void SyncCursor(CEditTableElement* pTable, intn iColumn, intn iRow);
    CEditSelection ComputeCursor(CEditTableElement* pTable, intn iColumn, intn iRow);
    void SyncCursor(CEditMultiColumnElement* pMultiColumn);

#ifdef FIND_REPLACE
    XP_Bool FindAndReplace( EDT_FindAndReplaceData *pData );
#endif

private:
    void ParseBodyTag(PA_Tag *pTag);
    CFinishLoadTimer m_finishLoadTimer;
    CRelayoutTimer m_relayoutTimer;
    CAutoSaveTimer m_autoSaveTimer;
    XP_Bool m_bDisplayTables;
    XP_Bool m_bDummyCharacterAddedDuringLoad;
    static XP_Bool m_bAutoSaving;
};


//
// CEditTagCursor - 
//
class CEditTagCursor {
private: // types
    enum TagPosition { tagOpen, tagEnd };

private: // data

    CEditBuffer *m_pEditBuffer;
    CEditElement *m_pCurrentElement;
    CEditPositionComparable m_endPos;
    TagPosition m_tagPosition;
    int m_stateDepth;
    int m_currentStateDepth;
    int m_iEditOffset;
    XP_Bool m_bClearRelayoutState;
    MWContext *m_pContext;
    PA_Tag* m_pStateTags;


public:  // routines
    CEditTagCursor( CEditBuffer* pEditBuffer, CEditElement *pElement, 
            int iEditOffset, CEditElement* pEndElement );
    ~CEditTagCursor();

    PA_Tag* GetNextTag();
    PA_Tag* GetNextTagState();
    XP_Bool AtBreak(XP_Bool* pEndTag);
    int32 CurrentLine();
    XP_Bool ClearRelayoutState() { return m_bClearRelayoutState; }
    CEditTagCursor* Clone();

};

//-----------------------------------------------------------------------------
//  CEditImageLoader
//-----------------------------------------------------------------------------
class CEditImageLoader {
private:
    CEditBuffer *m_pBuffer;
    EDT_ImageData *m_pImageData;
    LO_ImageStruct *m_pLoImage;
    XP_Bool m_bReplaceImage;

public:
    CEditImageLoader( CEditBuffer *pBuffer, EDT_ImageData *pImageData, 
            XP_Bool bReplaceImage ); 
    ~CEditImageLoader();

    void LoadImage();
    void SetImageInfo(int32 ele_id, int32 width, int32 height);
};


//-----------------------------------------------------------------------------
//  CEditFileBackup
//
// File backup and restore Object
//  Object can be reinititialized with a Reset.
//
//-----------------------------------------------------------------------------
class CEditFileBackup {
private:
    XP_Bool m_bInitOk;
    char *m_pBackupName;
    char *m_pFileName;

public:
    CEditFileBackup(): m_bInitOk(FALSE), m_pBackupName(0), m_pFileName(0){}

    ~CEditFileBackup(){
        Reset();
    }

    void Reset();
    XP_Bool InTransaction(){ return m_bInitOk; }
    ED_FileError BeginTransaction( char *pDestURL );
    char* FileName(){ return m_pFileName; }
    void Commit();
    void Rollback();
};


//-----------------------------------------------------------------------------
//  CFileSaveObject
//-----------------------------------------------------------------------------
class CFileSaveObject {
private:
    XP_Bool m_bOverwriteAll;
    XP_Bool m_bDontOverwriteAll;
    XP_Bool m_bDontOverwrite;
    int m_iCurFile;
    XP_File m_outFile;
    CEditFileBackup m_fileBackup;

    TXP_GrowableArray_pChar m_srcImageURLs;
    TXP_GrowableArray_pChar m_destImageFileBaseName;

protected:
    MWContext *m_pContext;
    char *m_pDestPathURL;       
    int m_iExtraFiles;
    XP_Bool m_bDeleteMe;
    ED_FileError m_status;

public:
    //
    // inherited interface.
    //
    CFileSaveObject( MWContext *pContext ) ;
    virtual ~CFileSaveObject();

    // can be a full file path.  Grabs the path part automatically.
    void SetDestPathURL( char* pDestURL );


    intn AddFile( char *pSrcURL, char *pDestURL );
    
    ED_FileError SaveFiles();

    char *GetDestName( intn index );
    char *GetSrcName( intn index );

    // Inherit from CFileSaveObject and implement FileFetchComplete.
    //
    virtual ED_FileError FileFetchComplete();

    void Cancel();

private:
    ED_FileError FetchNextFile();
/*    intn OpenOutputFile( int iFile ); */

// Netlib interface
public:
    // Made Public so we can access from file save stream
    //  (We don't open file until 
    //   libnet succeeds in finding source data)
    intn OpenOutputFile(/*int iFile*/);
    // Net Callbacks
    int NetStreamWrite( const char *block, int32 length );
    void NetFetchDone( URL_Struct *pUrl, int status, MWContext *pContext );

};


//-----------------------------------------------------------------------------
//  CEditSaveObject
//-----------------------------------------------------------------------------
class CEditSaveObject: public CFileSaveObject{
private:
    CEditBuffer *m_pBuffer;
    char *m_pSrcURL;
    char *m_pDestFileURL;
    XP_Bool m_bSaveAs;
    XP_Bool m_bKeepImagesWithDoc;
    XP_Bool m_bAutoAdjustLinks;
    XP_Bool m_bOverwriteAll;
    XP_Bool m_bDontOverwriteAll;
    XP_Bool m_bFixupImageLinks;
    intn m_backgroundIndex;

public:
    CEditSaveObject( CEditBuffer *pBuffer, char *pSrcURL, char* pDestFileURL,
                            XP_Bool bSaveAs, XP_Bool bKeepImagesWithDoc, 
                            XP_Bool bAutoAdjustLinks ); 
    ~CEditSaveObject();

    XP_Bool MakeImagesAbsolute(){ return !m_bKeepImagesWithDoc && m_bAutoAdjustLinks; }

    XP_Bool FindAllDocumentImages();
    intn AddImage( char *pSrc );
    XP_Bool IsSameURL( char* pSrcURL, char *pLocalName );

    virtual ED_FileError FileFetchComplete();
    void FixupLinks();
};


//-----------------------------------------------------------------------------
//  CEditImageSaveObject
//-----------------------------------------------------------------------------
class CEditImageSaveObject: public CFileSaveObject{
private:
    CEditBuffer *m_pBuffer;
    EDT_ImageData *m_pData;
    XP_Bool m_bReplaceImage;

public:
    intn m_srcIndex;
    intn m_lowSrcIndex;

    CEditImageSaveObject( CEditBuffer *pBuffer, EDT_ImageData *pData, 
                        XP_Bool bReplaceImage );
    ~CEditImageSaveObject();

    virtual ED_FileError FileFetchComplete();
};

//-----------------------------------------------------------------------------
//  CEditBackgroundImageSaveObject
//-----------------------------------------------------------------------------
class CEditBackgroundImageSaveObject: public CFileSaveObject{
private:
    CEditBuffer *m_pBuffer;

public:
    CEditBackgroundImageSaveObject( CEditBuffer *pBuffer );
    ~CEditBackgroundImageSaveObject();

    virtual ED_FileError FileFetchComplete();
};



//-----------------------------------------------------------------------------
//  Streams
//-----------------------------------------------------------------------------

#define STREAM_PRINTF_MAX       1024



//
// Abstract output file stream.
//
class IStreamOut {
public:
    virtual void Write( char *pBuffer, int32 iCount )=0;

    // the use of this printf function is limited to 1024 bytes and is not
    //  checked.  Default implementation is implemented in term of 'Write'
    virtual int Printf( char * pFormat, ... );

    enum EOutStreamStatus {
        EOS_NoError,
        EOS_DeviceFull
    };

    virtual EOutStreamStatus Status(){ return EOS_NoError; }

    // implemented in terms of the interface.
    void WriteInt( int32 i ){ Write( (char*)&i, sizeof( int32 ) ); }
    void WriteZString( char* pString);
    void WritePartialZString( char* pString, ElementIndex start, ElementIndex end);
};

class IStreamIn {
public:
    virtual void Read( char *pBuffer, int32 iCount )=0;
    int32 ReadInt(){ int32 i; Read((char*)&i, sizeof(int32)); return i; }
    char *ReadZString();
};


//
// Output Stream as file.
//
class CStreamOutFile: public IStreamOut {
private:
    EOutStreamStatus m_status;
    XP_File m_outputFile;  
    XP_Bool m_bBinary;
public:
    CStreamOutFile(  XP_File hFile, XP_Bool bBinary  );
    ~CStreamOutFile();
    virtual void Write( char *pBuffer, int32 iCount );
    virtual EOutStreamStatus Status(){ return m_status; }
};

//
// Stream out to net.
//
class CStreamOutNet: public IStreamOut {
private:
    NET_StreamClass * m_pStream;

public:
    CStreamOutNet( MWContext* pContext );
    ~CStreamOutNet();
    virtual void Write( char *pBuffer, int32 iCount );
};

//
// Output Stream as memory
//
class CStreamOutMemory: public IStreamOut {
private:
    int32 m_bufferSize;
    int32 m_bufferEnd;
    XP_HUGE_CHAR_PTR m_pBuffer;
public:
    CStreamOutMemory();
    ~CStreamOutMemory();
    virtual void Write( char *pBuffer, int32 iCount );
    XP_HUGE_CHAR_PTR GetText(){ return m_pBuffer; }
    int32 GetLen(){ return m_bufferEnd; }
};


class CStreamInMemory: public IStreamIn {
private:
    XP_HUGE_CHAR_PTR m_pBuffer;
    int32 m_iCurrentPos;
public:
    virtual void Read( char *pBuffer, int32 iCount );
    CStreamInMemory(char *pMem): m_pBuffer(pMem), m_iCurrentPos(0){}
};

extern CBitArray *edt_setNoEndTag;
extern CBitArray *edt_setWriteEndTag;
extern CBitArray *edt_setHeadTags;
extern CBitArray *edt_setSoloTags;
extern CBitArray *edt_setBlockFormat;
extern CBitArray *edt_setCharFormat;
extern CBitArray *edt_setList;
extern CBitArray *edt_setUnsupported;
extern CBitArray *edt_setTextContainer;
extern CBitArray *edt_setListContainer;
extern CBitArray *edt_setParagraphBreak;
extern CBitArray *edt_setFormattedText;
extern CBitArray *edt_setContainerSupportsAlign;
extern CBitArray *edt_setIgnoreWhiteSpace;
extern CBitArray *edt_setSuppressNewlineBefore;
extern CBitArray *edt_setRequireNewlineAfter;

inline XP_Bool BitSet( CBitArray* pBitArray, int i ){ if ( i < 0 ) return FALSE;
    else return (*pBitArray)[i]; }

inline int IsListContainer( TagType i ){ return BitSet( edt_setListContainer, i ); }

inline int TagHasClose( TagType i ){ return !BitSet( edt_setNoEndTag, i ); }
inline int WriteTagClose( TagType i ){ return (BitSet( edt_setWriteEndTag, i ) ||  
                TagHasClose(i)); }

//
// Utility functions.
//
char *EDT_TagString(int32 tagType);
ED_ETextFormat edt_TagType2TextFormat( TagType t );
char *edt_WorkBuf( int iSize );
char *edt_QuoteString( char* pString );
char *edt_MakeParamString( char* pString );

char *edt_FetchParamString( PA_Tag *pTag, char* param );
XP_Bool edt_FetchParamBoolExist( PA_Tag *pTag, char* param );
XP_Bool edt_FetchDimension( PA_Tag *pTag, char* param, 
                         int32 *pValue, XP_Bool *pPercent,
                         int32 nDefaultValue, XP_Bool bDefaultPercent );
ED_Alignment edt_FetchParamAlignment( PA_Tag* pTag, 
                ED_Alignment eDefault=ED_ALIGN_BASELINE, XP_Bool bVAlign = FALSE );
int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue );
ED_Color edt_FetchParamColor( PA_Tag *pTag, char* param );

// Preserves the old value of data if there's no parameter.
void edt_FetchParamString2(PA_Tag* pTag, char* param, char*& data);
void edt_FetchParamColor2( PA_Tag *pTag, char* param, ED_Color& data );

// Concatenates onto the old value
void edt_FetchParamExtras2( PA_Tag *pTag, char**ppKnownParams, char*& data);

LO_Color* edt_MakeLoColor( ED_Color c );
void edt_SetLoColor( ED_Color c, LO_Color *pColor );

void edt_InitEscapes(MWContext *pContext);
void edt_PrintWithEscapes( CPrintState *ps, char *p, XP_Bool bBreakLines );
char *edt_LocalName( char *pURL );
PA_Block PA_strdup( char* s );

inline char *edt_StrDup( char *pStr );
EDT_ImageData* edt_NewImageData();
EDT_ImageData* edt_DupImageData( EDT_ImageData *pOldData );
void edt_FreeImageData( EDT_ImageData *pData );

void edt_SetTagData( PA_Tag* pTag, char* pTagData);
void edt_AddTag( PA_Tag*& pStart, PA_Tag*& pEnd, TagType t, XP_Bool bIsEnd,
        char *pTagData = 0 );

void edt_InitBitArrays();

//
// Retreive extra parameters and return them in the form:
//
//  ' foo=bar zoo mombo="fds ifds ifds">'
//
char* edt_FetchParamExtras( PA_Tag *pTag, char**ppKnownParams );

// 
// Parse return values.
//      
#define OK_IGNORE       2
#define OK_CONTINUE     1
#define OK_STOP         0
#define NOT_OK          -1



#endif  // _EDITOR_H
#endif  // EDITOR