/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */


//
// Editor save stuff. LWT 06/01/95
//

#ifdef EDITOR

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

// 
// Turn on new DT mapping code
//
#define EDT_DDT 1

#if defined(JAVA)
#define EDITOR_JAVA
#endif

//
//    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 "pa_parse.h"
#include "layout.h"
#include "edt.h"
#include "pa_tags.h"
#include "xpassert.h"
#include "xp_file.h"
#include "libmocha.h"
#include "libi18n.h"
}

#include "bits.h"
#include "garray.h"
#include "streams.h"


#include "gui.h"

// For textFE test, hardts
#include "xlate.h"

#include "fsfile.h"
#include "shist.h"

#ifdef XP_WIN32
#include	<crtdbg.h>
#endif

// XP Strings
extern "C" {
#include "xpgetstr.h"
#define WANT_ENUM_STRING_IDS
#include "allxpstr.h"
#undef WANT_ENUM_STRING_IDS
}

// Defining DEBUG_AUTO_SAVE will make the document AutoSave every six seconds.
//#define DEBUG_AUTO_SAVE

// Test if ele
#ifdef USE_SCRIPT
#define  ED_IS_NOT_SCRIPT(pEle) (0 == (pEle->Text()->m_tf & (TF_SERVER | TF_SCRIPT | TF_STYLE)))
#else
#define ED_IS_NOT_SCRIPT(pEle) (TRUE)
#endif

#define EDT_SELECTION_START_COMMENT "selection start"
#define EDT_SELECTION_END_COMMENT "selection end"

// 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

#ifdef XP_MAC
// taken from pa_amp.h
#define NON_BREAKING_SPACE  ((char)007)
#define NON_BREAKING_SPACE_STRING   "\07"
#else
#define NON_BREAKING_SPACE  ((char)160)
#define NON_BREAKING_SPACE_STRING   "\xa0"
#endif

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

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


// Macros for the public entry points.
// usage:
// GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) ; // For no return value
// GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer) return-value; // to return a return value

#define GET_WRITABLE_EDIT_BUF_OR_RETURN(CONTEXT, BUFFER) CEditBuffer* BUFFER = LO_GetEDBuffer( CONTEXT );\
    if (!CEditBuffer::IsAlive(BUFFER) || !BUFFER->IsReady() || !BUFFER->IsWritable() ) return

#define GET_EDIT_BUF_OR_RETURN(CONTEXT, BUFFER) CEditBuffer* BUFFER = LO_GetEDBuffer( CONTEXT );\
    if (!CEditBuffer::IsAlive(BUFFER) || !BUFFER->IsReady() ) return

#define GET_EDIT_BUF_OR_RETURN_READY_OR_NOT(CONTEXT, BUFFER) CEditBuffer* BUFFER = LO_GetEDBuffer( CONTEXT );\
    if (!CEditBuffer::IsAlive(BUFFER) ) return

#define GET_WRITABLE_EDIT_BUF_OR_RETURN_READY_OR_NOT(CONTEXT, BUFFER) CEditBuffer* BUFFER = LO_GetEDBuffer( CONTEXT );\
    if (!CEditBuffer::IsAlive(BUFFER) || !BUFFER->IsWritable() ) return


//-----------------------------------------------------------------------------
// 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 CEditLayerElement;
class CEditDivisionElement;
class CParseState;
class CPrintState;
class CEditImageLoader;
class CEditSaveObject;
class CFileSaveObject;
struct ED_Link;
class CEditCommand;
class CEditCommandGroup;

class CEditSaveData;
class CEditSaveToTempData;

#ifdef DEBUG
class CEditTestManager;
#endif
#ifdef EDITOR_JAVA
// Opaque interface to Editor Plugin support classes.
// (It's too expensive to pull jri in in this header file.
// If we do, bsdi runs out of VM while compiling.

typedef void* EditorPluginManager;
EditorPluginManager EditorPluginManager_new(MWContext* pContext);
void EditorPluginManager_delete(EditorPluginManager a);
XP_Bool EditorPluginManager_IsPluginActive(EditorPluginManager a);
XP_Bool EditorPluginManager_PluginsExist();
class CEditorPluginInterface;
#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
#ifdef EDITOR_JAVA
Declare_GrowableArray(CEditorPluginInterface,CEditorPluginInterface*)       // TXP_GrowableArray_CEditorPluginInterface
#endif
//cmanske - store lists of selected cell elements
Declare_GrowableArray(CEditTableCellElement,CEditTableCellElement*)  // TXP_GrowableArray_CEditTableCellElement
Declare_GrowableArray(LO_CellStruct,LO_CellStruct*)                  // TXP_GrowableArray_LO_CellStruct
//List of tables encountered during Relayout()
Declare_GrowableArray(LO_TableStruct,LO_TableStruct*)                // TXP_GrowableArray_LO_TableStruct

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


/* Gives us quick access to first cell in each column or row 
 *  with location and index data
*/
struct _EDT_CellLayoutData {
    int32                 X;                 /* X value of cell */
    int32                 Y;                 /* Y value of cell */
    int32                 iColumn;           /* Geometric column index */
    int32                 iRow;              /* Geometric row index */
    int32                 iCellsInColumn;    /* Total number of cells in each column */
    int32                 iCellsInRow;       /*  and row */
    LO_Element            *pLoCell;          /* Layout element pointer */
    CEditTableCellElement *pEdCell;          /* Corresponding edit element object */
};

typedef struct _EDT_TableRowData EDT_TableRowData;

/* iTableMode param for CEditTableElement::SetSizeMode() */
#define ED_MODE_TABLE_PERCENT     0x0001   /* Convert table to use % of parent width */
#define ED_MODE_TABLE_PIXELS      0x0002   /* Convert table to use absolute pixels */
#define ED_MODE_CELL_PERCENT      0x0004   /* Convert all cells to use % of parent width */
#define ED_MODE_CELL_PIXELS       0x0008   /* Convert all cells to use absolute pixels */
#define ED_MODE_USE_TABLE_WIDTH   0x0010   /* Set WIDTH param for table */
#define ED_MODE_NO_TABLE_WIDTH    0x0020   /* Remove WIDTH param for table (allows cell resizing) */
#define ED_MODE_USE_TABLE_HEIGHT  0x0040   /* Set HEIGHT param for table */
#define ED_MODE_NO_TABLE_HEIGHT   0x0080   /* Remove HEIGHT param for table (allows cell resizing) */
#define ED_MODE_USE_CELL_WIDTH    0x0100   /* Set WIDTH param for all cells (allows column resizing) */
#define ED_MODE_NO_CELL_WIDTH     0x0200   /* Remove WIDTH param for all cells (allows table resizing)*/
#define ED_MODE_USE_CELL_HEIGHT   0x0400   /* Set HEIGHT param for all cells (allows row resizing) */
#define ED_MODE_NO_CELL_HEIGHT    0x0800   /* Remove HEIGHT param for all cells (allows table resizing)*/
#define ED_MODE_USE_COLS          0x1000   /* Set COLS param for table (use only 1st row for column widths) */
#define ED_MODE_NO_COLS           0x2000   /* Remove COLS param for table (all cell widths used) */

#ifdef DEBUG
    #define DEBUG_EDIT_LAYOUT
#endif

typedef int32 DocumentVersion;

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;
    
    //cmanske: Needed to differentiate between an empty selection 
    //  (indicating we should get the current selection)
    //  and a selection obtained from a table cell
    XP_Bool IsEmpty() { return (m_start.m_pElement == 0); }
};


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

#ifdef DEBUG
void TraceLargeString(char* b);
#endif

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

//
// Copy type written to stream
//    Used to decide how to paste into existing table
//
enum EEditCopyType {
    eCopyNormal,        // Default = 0. Normal HTML copying, not a table
    eCopyTable,         // Entire table - paste normally or embeded into existing table
    eCopyRows,          // Entire rows - inserts/overlays into existing table
    eCopyColumns,       // Entire columns   "
    eCopyCells          // Arbitrary cells - overlay selected target cells in table
};

//
// 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, int16 csid);
    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);
    void SetParent(CEditElement* pParent){m_pParent = pParent;}
    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.
    CEditLayerElement* GetLayer(); // Returns containing Layer, or NULL if none.
    CEditLayerElement* GetLayerIgnoreSubdoc(); // Returns containing Layer, 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.
    XP_Bool InMungableMailQuote(); // Returns true if this element is within a mungable mail quote.
    XP_Bool InMailQuote(); // Returns true if this element is within a mail quote.
    CEditListElement* GetMailQuote(); // Returns containing mail quote or NULL.

    //cmanske A bit more readable
    XP_Bool IsInTableCell() { return NULL != GetTableCellIgnoreSubdoc(); }
    XP_Bool IsInTable() { return NULL != GetTableIgnoreSubdoc(); }

    CEditElement* GetTopmostTableOrLayer(); // Returns topmost containing table or Layer, or NULL if none.
    CEditElement* GetTableOrLayerIgnoreSubdoc(); // Returns containing table or Layer, or NULL if none.
    CEditElement* GetSubDocOrLayerSkipRoot();

    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; }

    // Returns TRUE only if tag has WIDTH or HEIGHT already defined
    // Use NULL for params if you don't need those values
    XP_Bool GetWidth(XP_Bool * pPercent = NULL, int32 * pWidth = NULL);
    XP_Bool GetHeight(XP_Bool * pPercent = NULL, int32 * pHeight = NULL);
    
    virtual void SetSize(XP_Bool bWidthPercent, int32 iWidth,
                         XP_Bool bHeightPercent, int32 iHeight);
	
	virtual XP_Bool CanReflow();
	
    //
    // Cast routines.
    //
    CEditTextElement* Text();

    virtual XP_Bool IsLeaf();
    CEditLeafElement* Leaf();

    virtual XP_Bool IsRoot();
    CEditRootDocElement* Root();

    virtual XP_Bool IsContainer();
    CEditContainerElement* Container();

    //get previous non empty container to querry for preopen conditions ect
    CEditContainerElement*  GetPreviousNonEmptyContainer();
    //get next non empty container to querry for preclose conditions ect
    CEditContainerElement*  GetNextNonEmptyContainer();

    virtual XP_Bool IsList();
    CEditListElement* List();

    virtual XP_Bool IsBreak();
    CEditBreakElement* Break();

    virtual XP_Bool CausesBreakBefore();
    virtual XP_Bool CausesBreakAfter();
    virtual XP_Bool AllowBothSidesOfGap();

    virtual XP_Bool IsImage();
    CEditImageElement* Image();
    virtual XP_Bool IsIcon();
    CEditIconElement* Icon();

    CEditTargetElement* Target();

    CEditHorizRuleElement* HorizRule();
    virtual XP_Bool IsRootDoc();
    CEditRootDocElement* RootDoc();
    virtual XP_Bool IsSubDoc();
    CEditSubDocElement* SubDoc();
    virtual XP_Bool IsTable();
    CEditTableElement* Table();
    virtual XP_Bool IsTableRow();
    CEditTableRowElement* TableRow();
    virtual XP_Bool IsTableCell();
    CEditTableCellElement* TableCell();
    virtual XP_Bool IsCaption();
    CEditCaptionElement* Caption();
    virtual XP_Bool IsText();
    virtual XP_Bool IsLayer();
    CEditLayerElement* Layer();
    virtual XP_Bool IsDivision();
    CEditDivisionElement* Division();

    XP_Bool IsEndOfDocument();
    virtual XP_Bool IsEndContainer();

    // 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 void StreamToPositionalText( IStreamOut * /*pOut*/, XP_Bool /*bEnd*/ ){}


    virtual EEditElementType GetElementType();

    // Get parent table of any element
    CEditTableElement* GetParentTable();

    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.
    
    //cmanske: Used only by CEditTableElement and CEditTableCellElement
    virtual XP_Bool IsSelected() {return FALSE; }

    // 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 FindUnknownHTML( void* /*pvoid*/ );
    XP_Bool SplitContainerTest( void* /*pVoid*/ );
    XP_Bool SplitFormattingTest( void* pVoid );
    XP_Bool FindTable( void* /*pvoid*/ );
    XP_Bool FindTableRow( void* /*pvoid*/ );
    XP_Bool FindTableCell( 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 );

    virtual 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 ); }
    CEditLeafElement* NextLeafAll(XP_Bool bForward);
    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 );
    virtual void AdjustContainers( CEditBuffer* pBuffer );

	int16 GetWinCSID(); // International Character set ID

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, int16 csid);
    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, int16 csid, ED_Alignment align);
    CEditTableElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);
    virtual ~CEditTableElement();

    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();

    void InsertRows(int32 Y, int32 newY, intn number, CEditTableElement* pSource = NULL);
    void InsertColumns(int32 X, int32 newX, intn number, CEditTableElement* pSource = NULL);
    void DeleteRows(int32 Y, intn number, CEditTableElement* pUndoContainer = NULL);
    void DeleteColumns(int32 X, intn number, CEditTableElement* pUndoContainer = NULL);
    
    // Not used much - index based
    CEditTableRowElement* FindRow(intn number);

    // Find first row containing a cell that has Y as its top
    // Optionally return the "logical" row index
    CEditTableRowElement* GetRow(int32 Y, intn *pRow = NULL);

    // Find first geometric cell that is at or spans given Y value
    // The logical row is written to m_iGetRow
    CEditTableCellElement* GetFirstCellInRow(int32 Y, XP_Bool bSpan = FALSE);
    CEditTableCellElement* GetFirstCellInRow(CEditTableCellElement *pCell, XP_Bool bSpan = FALSE);
    // Find next cell using the supplied cell's Y value 
    //  (bSpan is FALSE, so Y must match exactly)
    // If pCell is NULL, use the same Y and bSpan as used for GetFirstCellInRow()
    // The logical row is written to the element's m_iGetRow
    CEditTableCellElement* GetNextCellInRow(CEditTableCellElement *pCell = NULL);
    
    // Find previous and cell using supplied cell's Y value 
    //  (does not depend on data from GetFirstCellInRow)
    CEditTableCellElement* GetPreviousCellInRow(CEditTableCellElement *pCell);
    CEditTableCellElement* GetLastCellInRow(CEditTableCellElement *pCell);

    // Same as above, but finds columns
    CEditTableCellElement* GetFirstCellInColumn(int32 X, XP_Bool bSpan = FALSE);
    CEditTableCellElement* GetFirstCellInColumn(CEditTableCellElement *pCell, XP_Bool bSpan = FALSE);
    CEditTableCellElement* GetNextCellInColumn(CEditTableCellElement *pCell = NULL);
    
    // Find previous and last cell using supplied cell's X value 
    //  (does not depend on data from GetFirstCellInRow)
    CEditTableCellElement* GetPreviousCellInColumn(CEditTableCellElement *pCell);
    CEditTableCellElement* GetLastCellInColumn(CEditTableCellElement *pCell);

    // Find first cell in column or row exactly at the specified col or row index grid
    CEditTableCellElement *GetFirstCellAtColumnIndex(int32 iIndex);
    CEditTableCellElement *GetFirstCellAtRowIndex(int32 iIndex);

    CEditCaptionElement* GetCaption();
    void SetCaption(CEditCaptionElement*);
    void DeleteCaption();

    void SetData( EDT_TableData *pData );
    EDT_TableData* GetData();
    int32 GetCellPadding();
    EDT_TableData* ParseParams( PA_Tag *pTag, int16 csid );
    static EDT_TableData* NewData();
    static void FreeData( EDT_TableData *pData );

    void SetBackgroundColor( ED_Color iColor );
    ED_Color GetBackgroundColor();
    void SetBackgroundImage( char* pImage );
    char* GetBackgroundImage();

    // 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);

    //cmanske:
    // Get corresponding Layout table element
    LO_TableStruct* GetLoTable();

    // Get width of window view or parent cell of a table
    // Supply pLoTable if known, else find it
    void GetParentSize(MWContext *pContext, int32 *pWidth, int32 *pHeight = NULL, LO_TableStruct *pLoTable = NULL);

    // See edttypes.h for dConvert table and all rows and cells to Percent mode or not
    // Current bWidthPercent, bHeightPercent, bWidthDefined, and bHeightDefined are saved...
    void SetSizeMode(MWContext *pContext, int iTableMode);
    // ... to be restored by this after relayout after table, col, or row resizing
    void RestoreSizeMode(MWContext *pContext);

    int32 GetInterCellSpace() {return m_iInterCellSpace; }
    int32 GetWidth() {return m_iWidthPixels; }
    int32 GetHeight() {return m_iHeightPixels; }
    int32 GetWidthOrPercent() { return m_iWidth; }
    int32 GetHeightOrPercent() { return m_iHeight; }

    // Get first cell in table. 
    //  Use GetNextCellInTable() or resulting pCell->GetNextCellInTable() 
    //     to walk through the table, wrapping around rows,
    //  or resultCell->pGetNextCellInLogicalRow() to walk through cells in the row
    CEditTableCellElement* GetFirstCell();

    // Result from above is stored in m_pCurrentCell    
    CEditTableCellElement* GetNextCellInTable(intn *pRowCounter = NULL);

    // Analogous routine for rows
    CEditTableRowElement* GetFirstRow();
    
    // This just counts rows - return m_iRows if it is set
    intn GetRows();
    
    // Max of number of columns in all rows
    intn GetColumns();
    // See comment below for m_iMaxColumns
    intn GetMaxColumns() {return m_iMaxColumns; }

    // Get number of columns between given values    
    intn GetColumnsSpanned(int32 iStartX, int32 iEndX);

    // Test if any cell in the first row has COLSPAN > 1
    XP_Bool FirstRowHasColSpan();

    // Also figure these out during FixupTableData
    // Used to tell when we are in first row or column of table
    int32 GetFirstColumnX() {return m_iFirstColumnX;}
    void SetFirstColumnX(int32 X) {m_iFirstColumnX = X;}
    int32 GetFirstRowY() {return m_iFirstRowY;}
    void SetFirstRowY(int32 Y) { m_iFirstRowY = Y;}

private:
    ED_Color m_backgroundColor;
    char* m_pBackgroundImage;
    ED_Alignment m_align;
    ED_Alignment m_malign;

    //Actual width, as determined from layout
    // See comments for similar params in CEditTableCellElement
    int32   m_iWidthPixels;       
    // This is % of parent (when bWidthPercent = TRUE) or absolute (in pixels)
    XP_Bool m_iWidth;
    XP_Bool m_bWidthPercent;       

    int32   m_iHeightPixels;       
    int32   m_iHeight;
    XP_Bool m_bHeightPercent;       

    // Pixels between cells
    int32   m_iInterCellSpace;

    int32   m_iFirstColumnX;
    int32   m_iFirstRowY;

    // Save last value of m_bWidthPercent, m_bWidthDefined,
    //  bHeightPercent, and bHeightDefined here
    //   during table, col, and row resizing.
    // Call RestoreTableSizeMode to reset back to these values
    XP_Bool m_bSaveWidthPercent;       
    XP_Bool m_bSaveHeightPercent;
    XP_Bool m_bSaveWidthDefined;       
    XP_Bool m_bSaveHeightDefined;

    // Total number in table
    // Columns are tricky - max number of possible columns,
    //    which is sensitive to ROWSPAN and COLSPAN
    //    calculated in CEditBuffer::FixupTableData()
    // Thus, this may be more than GetColumns(), which doesn't include RowSpan effect
    int32   m_iMaxColumns;
    
    // Rows are straight forward. This should == result of GetRows();
    int32   m_iRows;

    // Used by GetFirstCell() and GetNextCellInTable()
    CEditTableCellElement *m_pCurrentCell;

    // For use with GetFirstCellInRow/Column and GetNextCellInRow/Column
    CEditTableCellElement *m_pFirstCellInColumnOrRow;
    CEditTableCellElement *m_pNextCellInColumnOrRow;
    int32                  m_iColX;
    int32                  m_iRowY;
    XP_Bool                m_bSpan;
    intn                   m_iGetRow;

public:
    intn m_iBackgroundSaveIndex;

};

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

    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 GetCells();
    // Use actual cell X and Y for Insert and Delete logic
    void InsertCells(int32 X, int32 newX, intn number, CEditTableRowElement* pSource = NULL);
    // Append (move) cells in supplied row to this row and delete supplied row if requested
    // (Note: Unlike most other "insert" methods, "this" is the existing row, not row to be inserted)
    // Returns the number of cells "positions" appended (sum of COLSPAN of appended cells)
    intn AppendRow( CEditTableRowElement *pAppendRow, XP_Bool bDeleteRow = TRUE );
    // Add extra empty cells to fill out a row to specified amount
    //   or use current table's max columns if iColumns is 0
    void PadRowWithEmptyCells(intn iColumns = 0);

    void DeleteCells(int32 X, intn number, CEditTableRowElement* pUndoContainer = NULL);
    // Find cell at specific location in column
    CEditTableCellElement* FindCell(int32 X, XP_Bool bIncludeRightEdge = FALSE);

    void SetData( EDT_TableRowData *pData );
    EDT_TableRowData* GetData( );
    static EDT_TableRowData* ParseParams( PA_Tag *pTag, int16 csid );
    static EDT_TableRowData* NewData();
    static void FreeData( EDT_TableRowData *pData );
    void SetBackgroundColor( ED_Color iColor );
    ED_Color GetBackgroundColor();
    void SetBackgroundImage( char* pImage );
    char* GetBackgroundImage();
    // Not sure if we need this, but it will be useful
    //   for padding tables with ragged right edges
    intn GetColumns() {return m_iColumns; }
    void SetColumns(intn iColumns) {m_iColumns = iColumns; }

    CEditTableRowElement *GetNextRow();
    // Use resultCell->pGetNextCellInLogicalRow() to walk through cells in row
    CEditTableCellElement *GetFirstCell();

private:
    ED_Color m_backgroundColor;
    char* m_pBackgroundImage;
    // Total number of "physical" columns in row
    //  (i.e., includes effect of COLSPAN and ROWSPAN)
    intn m_iColumns;

public:
    intn m_iBackgroundSaveIndex;

};

class CEditCaptionElement: public CEditSubDocElement {
public:
    CEditCaptionElement();
    CEditCaptionElement(CEditElement *pParent, PA_Tag *pTag, int16 csid);
    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* GetData( int16 csid );
    static EDT_TableCaptionData* ParseParams( PA_Tag *pTag, int16 csid );
    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, int16 csid);
    CEditTableCellElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);
    virtual ~CEditTableCellElement();

    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();

    void SetData( EDT_TableCellData *pData );
    // Supply the csid if getting data for table not
    //  yet part of doc, as when pasting from stream
    EDT_TableCellData* GetData(int16 csid = 0);

    // Clear mask bits for attributes different from supplied data
    void MaskData( EDT_TableCellData *pData );
    
    EDT_TableCellData* ParseParams( PA_Tag *pTag, int16 csid );
    static EDT_TableCellData* NewData();
    static void FreeData( EDT_TableCellData *pData );
    void SetBackgroundColor( ED_Color iColor );
    ED_Color GetBackgroundColor();
    void SetBackgroundImage( char* pImage );
    char* GetBackgroundImage();

    //cmanske:
    // Get corresponding Layout struct
    LO_CellStruct* GetLoCell();

    // Next cell in table - wraps around end of each
    //  row until end of table is found (returns null)
    // If pRowCount is supplied, value it is incremented
    //   when cell returned is 1st one in a next row
    CEditTableCellElement* GetNextCellInTable(intn *pRowCounter = NULL);
    // Similar to above, but finds previous cell until beginning is found (returns null)
    // If pRowCount is supplied, value it is decremented
    //   when cell returned is 1st one in a previous row
    CEditTableCellElement* GetPreviousCellInTable(intn *pRowCounter = NULL);
    
    // This gets next cell in the same logical row
    CEditTableCellElement* GetNextCellInLogicalRow();

    // Find first geometric cell that is at 
    //  given X (for column) or Y (for row)value
    // GetNext... continue using same X or Y
    // (These just call same functions in CEditTableElement)
    CEditTableCellElement* GetFirstCellInRow(int32 Y, XP_Bool bSpan = FALSE);
    CEditTableCellElement* GetFirstCellInColumn(int32 X, XP_Bool bSpan = FALSE);
    CEditTableCellElement* GetNextCellInRow();
    CEditTableCellElement* GetNextCellInColumn();

    // Test if cell contains only 1 container with 
    //  just the empty text field every new cell has
    XP_Bool IsEmpty();

    // Move contents of supplied cell into this cell
    void MergeCells(CEditTableCellElement* pCell);

    // Delete all contents, leaving just the minimum empty text element
    void DeleteContents();

    // Get all text from a cell. If bJoinParagrphs is FALSE,
    //  appropriate CR/LF is added between containers,
    //  else they are appended without CR/LF so cell contents
    //  can be pasted into Excell spreadsheet
    char * GetText(XP_Bool bJoinParagraphs = FALSE);

    // Returns sum of widths of all cells in first row in table
    int32 GetParentWidth();
    // Sum of heights of all cells in first column of table
    int32 GetParentHeight();

    int32 GetX() { return m_X; }
    int32 GetY() { return m_Y; }
    intn  GetRow() { return m_iRow; }
    int32 GetWidth() { return m_iWidthPixels; }
    int32 GetHeight() { return m_iHeightPixels; }
    int32 GetWidthOrPercent() { return m_iWidth; }
    int32 GetHeightOrPercent() { return m_iHeight; }
    int32 GetColSpan() { return m_iColSpan; }
    int32 GetRowSpan() { return m_iRowSpan; }
    XP_Bool GetWidthPercent() { return m_bWidthPercent; }
    XP_Bool GetHeightPercent() { return m_bHeightPercent; }

    // These will do appropriate action using SetData
    void IncreaseColSpan(int32 iIncrease);
    void IncreaseRowSpan(int32 iIncrease);
    void DecreaseColSpan(int32 iDecrease);
    void DecreaseRowSpan(int32 iDecrease);

    // Insert as last child of supplied parent 
    //   but first save current parent and next pointers
    void SwitchLinkage(CEditTableRowElement *pParentRow);
    // This switches parent and next pointers
    //   to those saved during SwitchLinkage
    void RestoreLinkage();

    // Save current percent and "defined" values -- do this before resizing, which may need
    //  to change the mode for better control of resizing
    // Since this is always called when we know the cell's data,
    //  supply the bWidthDefined and bHeightDefined values
    //  (percent mode params are held in class member variables)
    void SaveSizeMode(XP_Bool bWidthDefined, XP_Bool bHeightDefined);
    
    // Restore saved width and height percent modes,
    //   readjust m_bWidth an m_bHeight, and call SetData to set tag data
    void RestoreSizeMode(int32 iParentWidth, int32 iParentHeight);

    // Calculate the Percent iWidth or iHeight from the 
    //  iWidthPixels and iHeightPixels in pData...
    void CalcPercentWidth(EDT_TableCellData *pData);
    void CalcPercentHeight(EDT_TableCellData *pData);

    // ...vice versa
    void CalcPixelWidth(EDT_TableCellData *pData);
    void CalcPixelHeight(EDT_TableCellData *pData);

    void SetWidth(XP_Bool bWidthDefined, XP_Bool bWidthPercent, int32 iWidthPixels);
    void SetHeight(XP_Bool bHeightDefined, XP_Bool bHeightPercent, int32 iHeightPixels);

    // Next two are used when dragging the right border
    // Set all cells in a column to the width supplied
    void SetColumnWidthRight(CEditTableElement *pTable, LO_Element *pLoCell, EDT_TableCellData *pData);
    // Set all cells in a row to the width params supplied in pData
    void SetRowHeightBottom(CEditTableElement *pTable, LO_Element *pLoCell, EDT_TableCellData *pData);

    // Next two are use when resizing inside table cell property dialog
    // Supplying bWidthDefined or bHeightDefined allows clearing this for entire col or row
    // Set all cells in a column to the width supplied
    void SetColumnWidthLeft(CEditTableElement *pTable, CEditTableCellElement *pEdCell, EDT_TableCellData *pData);
    // Set all cells in a row to the width supplied
    void SetRowHeightTop(CEditTableElement *pTable, CEditTableCellElement *pEdCell, EDT_TableCellData *pData);

private:
    ED_Color m_backgroundColor;
    char* m_pBackgroundImage;

    // Cache this for efficiency
    XP_Bool m_bWidthPercent;       
    XP_Bool m_bHeightPercent;
    int32   m_iColSpan;
    int32   m_iRowSpan;

    // These are set only by FixupTableData(),
    //FIGURING OUT INDEXES IS TOO COMPLICATED!
    //  USE REAL LOCATIONS INSTEAD
    int32 m_X;
    int32 m_Y;
    intn m_iRow;    // Current logical and actual row index

    //Actual width, as determined from layout
    //Set by CEditBuffer::FixupTableData() after a table is layed out
    // We need this because tag data is not recorded
    // if m_bWidthDefined is FALSE, but we want to be able
    // to have the actual width available as default when
    // user edits the table or cell data
    int32   m_iWidthPixels;       
    int32   m_iHeightPixels;       

    // This is actual width or height in pixels, or Percent if b[Width|Height]Percent = TRUE;
    int32   m_iWidth;       
    int32   m_iHeight;       

    // Save last value of m_bWidthPercent, m_bWidthDefined,
    //  bHeightPercent, and bHeightDefined here
    //   during table, col, and row resizing.
    // Call RestoreTableSizeMode to reset back to these values
    XP_Bool m_bSaveWidthPercent;       
    XP_Bool m_bSaveHeightPercent;
    XP_Bool m_bSaveWidthDefined;       
    XP_Bool m_bSaveHeightDefined;

    // This is used to temporarily switch cell to 
    //  a table created for copying contents
    // These are used by SwitchLinkage() and RestoreLinkage()
    CEditElement *m_pSaveParent;
    CEditElement *m_pSaveNext;

public:
    intn m_iBackgroundSaveIndex;
};

class CEditLayerElement: public CEditElement {
public:
    CEditLayerElement();
    CEditLayerElement(CEditElement *pParent, PA_Tag *pTag, int16 csid);
    CEditLayerElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

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

    void SetData( EDT_LayerData *pData );
    EDT_LayerData* GetData( );
    static EDT_LayerData* ParseParams( PA_Tag *pTag, int16 csid );
    static EDT_LayerData* NewData();
    static void FreeData( EDT_LayerData *pData );

    // Temporary methods. Remove when we can display layers.
    virtual void PrintOpen( CPrintState *pPrintState );
    virtual void PrintEnd( CPrintState *pPrintState );


private:
};

class CEditDivisionElement: public CEditElement {
public:
    CEditDivisionElement();
    CEditDivisionElement(CEditElement *pParent, PA_Tag *pTag, int16 csid);
    CEditDivisionElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);

    virtual XP_Bool IsDivision();
    static CEditDivisionElement* Cast(CEditElement* pElement);
    virtual EEditElementType GetElementType();
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild);

    ED_Alignment GetAlignment();
    void ClearAlignment();
    XP_Bool HasData();

    void SetData( EDT_DivisionData *pData );
    EDT_DivisionData* GetData( );
    static EDT_DivisionData* ParseParams( PA_Tag *pTag, int16 csid );
    static EDT_DivisionData* NewData();
    static void FreeData( EDT_DivisionData *pData );

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

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 );
    virtual ElementIndex GetPersistentCount();
    virtual void StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd );

    virtual XP_Bool IsContainerContainer();
    virtual XP_Bool IsFirstInContainer();

    //
    // 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);

    // True if this is a bucket for totally unknown html
    virtual XP_Bool IsUnknownHTML();
    // True if this element is an HTML comment
    virtual XP_Bool IsComment();
    // True if this element is an HTML comment that matches a given prefix.
    // IsComment("FOO") will match "<!-- FOOBAR -->"
    virtual XP_Bool IsComment(char* pPrefix);

    XP_Bool IsMisspelled();

#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;
    XP_Bool m_hackPreformat;
public:
    XP_Bool m_bHasEndTag; // Only important for parsing.

    static CEditContainerElement* NewDefaultContainer( CEditElement *pParent,
            ED_Alignment align );
    CEditContainerElement(CEditElement *pParent, PA_Tag *pTag, int16 csid,
            ED_Alignment eAlign);
    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 void AdjustContainers( 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);
    virtual void StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd );

    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, int16 csid );

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

    XP_Bool ShouldSkipTags();
    XP_Bool IsImplicitBreak();
    // 0..2, where 0 = not in pseudo paragraph.
    // 1 = first container of pseudo paragraph.
    // 2 = second container of pseudo paragraph.
    int16 GetPseudoParagraphState(); 
    XP_Bool ForcesDoubleSpacedNextLine();

    //cmanske: Get single string with all of container's text (no embeded CR/LF)
    // Caller must XP_FREE the result
    char *GetText();

#ifdef DEBUG
    virtual void ValidateTree();
#endif

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

class CEditListElement: public CEditElement {
private:
    char *m_pBaseURL; /* If an ED_LIST_TYPE_CITE, this is the URL from the enclosing <BASE> tag. */

public:
    CEditListElement( CEditElement *pParent, PA_Tag *pTag, int16 csid );
    CEditListElement(IStreamIn *pStreamIn, CEditBuffer *pBuffer);
    virtual ~CEditListElement();

    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( );
    static EDT_ListData* ParseParams( PA_Tag *pTag, int16 csid );

    void CopyData(CEditListElement* pOther);

    XP_Bool IsMailQuote();
#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;
    LO_TextBlock* m_pTextBlock;
    ED_TextFormat m_tf;
    intn m_iFontSize;
    ED_Color m_color;
    ED_LinkId m_href;
    char* m_pFace;
    int16  m_iWeight;                 /* font weight range = 100-900, 400=Normal, 700=Bold*/
    int16  m_iPointSize;              /* not sure what default is! Use 0 to mean "default" */
    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; }
    char* GetTextWithConvertedSpaces();
    // If bConvertSpaces is FALSE, then csid is not used.
    void SetText(char* pText, XP_Bool bConvertSpaces=FALSE, int16 csid=0 );
    int GetSize(){ return m_textSize; }
    ElementOffset GetLen(){ return m_pText ? XP_STRLEN( m_pText ) : 0 ; }
    LO_Element* GetLayoutElement();
    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 SetFontWeight(int16 weight);
    int16 GetFontWeight();
    void SetFontPointSize(int16 pointSize);
    int16 GetFontPointSize();
    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 );
    // returns number of bytes inserted.
    int32 InsertChars( int iOffset, char* pNewChars );
    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 );

    LO_TextStruct* GetLOText( int iEditOffset );
	LO_TextBlock* GetTextBlock();
	
	virtual XP_Bool CanReflow();

    virtual PA_Tag* TagOpen( int iEditOffset );

    // output funcitons
    virtual void PrintOpen( CPrintState *ps );
    virtual void PrintWithEscapes( CPrintState *ps, XP_Bool bTrimTrailingSpaces );
    virtual void PrintLiteral( CPrintState *ps );
    virtual void StreamOut( IStreamOut *pOut );
    // Why is "selection" param commented out?
    virtual void PartialStreamOut( IStreamOut *pOut, CEditSelection& /*selection*/ );
    virtual EEditElementType GetElementType();
    virtual XP_Bool Reduce( CEditBuffer *pBuffer );
    virtual void StreamToPositionalText( IStreamOut *pOut, XP_Bool bEnd );

    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

private:
    void PrintRange(CPrintState *ps, int32 start, int32 end);
    void PrintOpen2(CPrintState* ps, XP_Bool bTrimTrailingSpaces);
};

//-----------------------------------------------------------------------------
//  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;
    XP_Bool m_bWidthPercent;
    XP_Bool m_bHeightPercent;
public:
    intn m_iSaveIndex;
    intn m_iSaveLowIndex;

public:
    // pass through constructors.
    CEditImageElement(CEditElement *pParent, PA_Tag* pTag = 0, int16 csid = 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 );

    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, int16 csid );
    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, int16 csid = 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 );

    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, int16 csid );
};

//-----------------------------------------------------------------------------
//  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
#define EDT_ICON_OBJECT                 6
#define EDT_ICON_LAYER                  7

class CEditIconElement: public CEditLeafElement {
private:
    // States for parsing LOCALDATA tag, see ParseLocalData().
    enum {BeforeMIME,InMIME,BeforeURL,InURL,AfterURL};

    // States for ReplaceParamValue().
    enum {OutsideValue,BeforeValue,InsideValue,InsideValueQuote};

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, int16 csid = 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 );

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

    char* GetData();

    // Creates list of the mimeType/URL pairs from the LOCALDATA parameter.
    // List of Strings.
    // mimeTypes and URLs must be freed with FreeLocalDataLists() when done.
    // Returns the length of the lists.
    int ParseLocalData(char ***mimeTypes,char ***urls);
    static void FreeLocalDataLists(char **mimeTypes,char **urls,int count);

    // Replace all occurances of pOld in parameter values with pNew.
    // Used by LOCALDATA to update tags when saved/published/mailed.
    void ReplaceParamValues(char *pOld,char *pNew);

    // Call this to set both Width and Height at same time,
    //  to avoid relayout twice if Width and Height are set separetely
    // Set either value to 0 to ignore 
    //  Do we ever need to set to 0? (change to -1 to "ignore" if we need 0)
    void SetSize(XP_Bool bWidthPercent, int32 iWidth,
                 XP_Bool bHeightPercent, int32 iHeight);

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

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

    virtual XP_Bool IsUnknownHTML();
    virtual XP_Bool IsComment();
    virtual XP_Bool IsComment(char* pPrefix);

public:
    // Used by CEditSaveObject.
    intn *m_piSaveIndices;
};

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

    PA_Tag* TagOpen( int iEditOffset );

    char *GetName();
    void SetName( char* pName, int16 csid );

    void SetData( EDT_TargetData *pData );
    EDT_TargetData* GetData();
    EDT_TargetData* GetData(int16 csid);
    static EDT_TargetData* ParseParams( PA_Tag *pTag, int16 csid );
    static EDT_TargetData* NewTargetData();
    static void FreeTargetData(EDT_TargetData* pData);
};


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

class CEditBreakElement: public CEditLeafElement {
    LO_LinefeedStruct *m_pLoLinefeed;
public:
    // pass through constructors.
    CEditBreakElement(CEditElement *pParent, PA_Tag* pTag = 0, int16 csid = 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);
    virtual void StreamOut( IStreamOut * pOut);
    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection );
    virtual XP_Bool IsAcceptableChild(CEditElement& pChild);
    virtual void PrintOpen( CPrintState * pPrintState );
    virtual void PrintEnd( CPrintState * pPrintState );
    virtual XP_Bool IsEndContainer();
    virtual void AdjustContainers( CEditBuffer* pBuffer );
};

class CEditInternalAnchorElement: public CEditLeafElement {
public:
    CEditInternalAnchorElement(CEditElement *pParent);
    virtual ~CEditInternalAnchorElement();
    virtual XP_Bool Reduce( CEditBuffer* );

    virtual XP_Bool ShouldStreamSelf( CEditSelection& local, CEditSelection& selection);
    virtual void StreamOut( IStreamOut *pOut);

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

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

    virtual EEditElementType GetElementType();
    virtual void PrintOpen( CPrintState *pPrintState );
    virtual void PrintEnd( CPrintState *pPrintState );
};


//
// 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;
private:
    int m_iDocPart;
public:
    XP_Bool InBody();
    void StartBody();
    void EndBody();

    TagType m_inJavaScript; // 0 when not in any script, otherwise the tag of the kind of script
    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;
    CStreamOutMemory* m_pPostBody;
    CStreamOutMemory* GetStream(); // Gets either m_pJavaScript or m_postBody, depending upon m_iDocPart

public:
    void Reset();
    CParseState();
    ~CParseState();
private:
    void Free(CStreamOutMemory*& pStream);
};

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;
    XP_Bool m_bEncodeSelectionAsComment;
    CEditSelection m_selection;
public:
    void Reset( IStreamOut *pStream, CEditBuffer *pBuffer );
    XP_Bool ShouldPrintSelectionComments(CEditLeafElement* pElement);
    XP_Bool ShouldPrintSelectionComment(CEditLeafElement* pElement, XP_Bool bEnd);
    void PrintSelectionComment(XP_Bool bEnd, XP_Bool bStickyAfter);
};


//
// 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() {} 
    // A bug, probably should call pLinkManager::Free(), not worth the work to fix 
    // it and make sure ref counting works all the time.  hardts

    EDT_HREFData* GetData();
    intn iRefCount;
    CEditLinkManager* pLinkManager;
    int linkOffset;
    char *hrefStr;
    char *pExtra;
    XP_Bool bAdjusted;
};


typedef TXP_GrowableArray_pChar ED_HREFList;
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, ED_HREFList *badLinks );

    // Wrapper for other AdjustLink function.
    void AdjustLink( ED_LinkId id, char *pOldURL, char* pNewURL, ED_HREFList *badLinks ) {
      if (id && id->hrefStr) AdjustLink(&id->hrefStr,pOldURL,pNewURL,badLinks);
    }

    // Used for adjusting links, images, etc.
    static void AdjustLink(char **pURL,char *pOldBase, char *pNewBase, ED_HREFList *badLinks);

    static void AddHREFUnique(ED_HREFList *badLinks,char *pURL);

    // Since we don't trust the reference counting, CEditSaveObject::FixupLinks() marks each link
    // as it adjusts it, so it doesn't adjust links twice.  HARDTS
    static void SetAdjusted(ED_LinkId id,XP_Bool bVal) { if (id) id->bAdjusted = bVal;}
    static XP_Bool GetAdjusted(ED_LinkId id) { if (id) return id->bAdjusted; else return TRUE;}
};

// 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;

class CGlobalHistoryGroup {
public:
    static CGlobalHistoryGroup* GetGlobalHistoryGroup();
    CGlobalHistoryGroup();
    ~CGlobalHistoryGroup();
    XP_Bool IsReload(CEditBuffer* pContext);
    void ReloadFinished(CEditBuffer* pContext);
    CEditCommandLog* CreateLog(CEditBuffer* pContext);
    CEditCommandLog* GetLog(CEditBuffer* pContext);
    void DeleteLog(CEditBuffer* pContext);
    void IgnoreNextDeleteOf(CEditBuffer* pContext);

private:
    CEditCommandLog* m_pHead; // First log
    static CGlobalHistoryGroup* g_pGlobalHistoryGroup;
};

//
// CEditDocState
//
class CEditDocState {
public:
  // Only created by CEditBuffer::RecordState().
  friend class CEditBuffer;
  virtual ~CEditDocState();

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

private:
  CEditDocState();

  XP_HUGE_CHAR_PTR m_pBuffer;
  DocumentVersion m_version;
};


class CCommandState {
public:
    CCommandState();
    ~CCommandState();
    void SetID(intn commandID);
    intn GetID();
    void Record(CEditBuffer* pBufferToRecord);
    void Restore(CEditBuffer* pBufferToRestore);
#ifdef DEBUG
    void Print(IStreamOut& stream);
#endif

private:
    void Flush();

    intn m_commandID;
    CEditDocState *m_pState;
};

class CEditCommandLog {
public:
    void StartTyping(intn typing);
    void EndTyping();
    void AdoptAndDo(CEditCommand*);
    void Undo();
    void Redo();
    void Trim();    // Trims undo and redo command lists.
    XP_Bool InReload();
    void SetInReload(XP_Bool bInReload);
    DocumentVersion GetVersion();
    void SetVersion(DocumentVersion); // For CEditBuffer::RestoreState().
    DocumentVersion GetStoredVersion();
    void DocumentStored();
    XP_Bool IsDirty();

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

    intn GetNumberOfCommandsToUndo();
    intn GetNumberOfCommandsToRedo();

    // Returns kNullCommandID if out of range
    intn GetUndoCommand(intn index);
    intn GetRedoCommand(intn index);

    void BeginBatchChanges(intn batchID);
    void EndBatchChanges();

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

#ifdef EDITOR_JAVA
    EditorPluginManager GetPlugins();
#endif

    // Temporary directory specific to the edit buffer.  In xpURL format.
    char *GetDocTempDir();
    char *CreateDocTempFilename(char *pPrefix,char *pExtension);

protected:
    // Use the global history to get the command log for a given context.
    CEditCommandLog();
    ~CEditCommandLog();

private:
    void FinishBatchCommands();
    void InternalAdoptAndDo(CEditCommand*);
    void InternalDo(intn id);
    void Trim(intn start, intn end);
    static char *GetAppTempDir();

    CEditBuffer* m_pBuffer;
    CCommandState* m_pUndo;
    CCommandState* m_pRedo;
    int32 m_iBatchLevel;
    int16 m_state;

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

    friend CGlobalHistoryGroup;
    CEditCommandLog* m_pNext;
    MWContext* m_pContext;
    XP_Bool m_bIgnoreDelete;

    DocumentVersion m_version;
    DocumentVersion m_storedVersion;
    DocumentVersion m_highestVersion;

#ifdef EDITOR_JAVA
    EditorPluginManager m_pPlugins;
#endif


    ///// All the temp directory variables are stored in CEditCommandLog
    ///// because they must persist when the CEditBuffer is deleted 
    ///// during undo/redo.    
    // The temp directory for this document.
    char *m_pDocTempDir; 
    // Used to create unique filenames in the temp directory.
    int m_iDocTempFilenameNonce;
    // used to create unique temp dir filenames.
    static int32 m_iDocTempDirNonce; 
};

// 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 kInsertLayerCommandID 40
#define kDeleteLayerCommandID 41
#define kSetLayerDataCommandID 42

#define kSetSelectionCommandID 43

#define kPerformPluginCommandID 44

#define kCommandIDMax 44

// 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 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;
    intn m_new_row;
    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_number;
    //cmanske: Changed to retain actual column and new insert column
    // This is needed for proper insert given COLSPAN effects
    intn m_column;
    intn m_new_column;
    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_new_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 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;
};

// 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();

    //CLM: Call before and after operations
    //     that are upset by autosave relayout
    void Suspend();
    void Resume();

protected:
    virtual void OnCallback();
private:
    CEditBuffer* m_pBuffer;
    int32 m_iMinutes;
    XP_Bool m_bSuspended;
    XP_Bool m_bCalledWhileSuspended;
};


//
// CSizingObject
//

class CSizingObject {
public:
    CSizingObject();
    ~CSizingObject();
    XP_Bool Create(CEditBuffer *pBuffer,
                   LO_Element *pLoElement,
                   int iSizingStyle,
                   int32 xVal, int32 yVal,
                   XP_Bool bLockAspect, XP_Rect *pRect);


    XP_Bool GetSizingRect(int32 xVal, int32 yVal, XP_Bool bLockAspect, XP_Rect *pRect);
    void ResizeObject();
    void EraseAddRowsOrCols();

private:
    CEditBuffer   *m_pBuffer;
    LO_Element    *m_pLoElement;
    XP_Rect        m_Rect;
    int            m_iStyle;
    int32          m_iXOrigin;
    int32          m_iYOrigin;
    int32          m_iStartWidth;
    int32          m_iStartHeight;
    int32          m_iParentWidth;
    int32          m_iViewWidth;
    int32          m_iViewHeight;
    int32          m_iAddCols;
    int32          m_iAddRows;
    XP_Bool        m_bWidthPercent;
    XP_Bool        m_bHeightPercent;
    XP_Bool        m_bPercentOriginal;
    XP_Bool        m_bFirstTime;
    int            m_iWidthMsgID;
};



//
// 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:
    long m_lifeFlag; /* Used to detect accesses to deleted buffer. */
    MWContext *m_pContext;
    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;
    XP_Bool m_bBackgroundNoSave;
    char *m_pFontDefURL;
    XP_Bool m_bFontDefNoSave;
    char *m_pBaseTarget;
    char *m_pBodyExtra;
    CEditImageLoader *m_pLoadingImage;
    CFileSaveObject *m_pSaveObject;
    XP_Bool m_bMultiSpaceMode;
    
    // Save copied style here
    // By making it static, we can paste attributes
    //   into a different window
    static EDT_CharacterData *m_pCopyStyleCharacterData;

    int m_hackFontSize;             // not a real implementation, just for testing

    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
    TagType m_iLastTagType; // The previous tag's type, comments are ignored.
    XP_Bool m_bLastTagIsEnd; // The previous tag's open vs. close state, comments are ignored.


    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();

    // Used only by CEditBuffer::Relayout()
    TXP_GrowableArray_LO_TableStruct m_RelayoutTables;

    PRBool m_bEncrypt;

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;

    XP_Bool IsSelectionComment(PA_Tag* pTag);
    XP_Bool HandleSelectionComment(PA_Tag* pTag, CEditElement*& pElement, intn& retVal);
    CEditCommandLog* GetCommandLog(){ XP_ASSERT(m_pCommandLog); return m_pCommandLog; };
    CEditCommandLog* m_pCommandLog;
    XP_Bool m_bTyping;
    CEditInternalAnchorElement* m_pStartSelectionAnchor;
    CEditInternalAnchorElement* m_pEndSelectionAnchor;
    XP_Bool m_bStartSelectionStickyAfter;
    XP_Bool m_bEndSelectionStickyAfter;

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

    // For maintaining the selected Table, row, columns, or individual cell
    // Only 1 table can be selected, but multiple cells are selected for rows, columns
    // Array of SelectedCell structs matches LO_Elements to CEditElements
    TXP_GrowableArray_CEditTableCellElement m_SelectedEdCells;
    TXP_GrowableArray_LO_CellStruct         m_SelectedLoCells;
    CEditTableElement                      *m_pSelectedEdTable;
    LO_TableStruct                         *m_pSelectedLoTable;
    
    // Index to the next selected cell from the m_SelectedEdCells list.
    // This and m_CurrentInsertPoint are used by GetFirstCellSelection() and SelectNextSelectedCell()
    intn m_iNextSelectedCell;

    // Save the current insert point before mucking with selected cells
    // Don't need this as long as we suppress relayout for individual
    //   cell selection format changes. Just relayout entire table when done
//    CEditInsertPoint m_CurrentInsertPoint;

    // This is either m_pSelectedLoTable or last selected cell 
    //   (which will be a member of m_SelectedLoCells list)
    LO_Element     *m_pSelectedTableElement;
    ED_HitType      m_TableHitType;

    // Used with ExtentTableCellSelection:
    //  save previous cell we used to extend selection so we change
    //  selection only when we move into a different cell
    LO_Element     *m_pPrevExtendSelectionCell;

#ifdef DEBUG
    friend CEditTestManager;
    CEditTestManager* m_pTestManager;
    int16 m_iSuppressPhantomInsertPointCheck;
    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);

private:
    XP_Bool ShouldAutoStartBody(PA_Tag* pTag, int16 csid);
    intn ParseOpenTag(pa_DocData *data_doc, PA_Tag* pTag, intn status);
    void ParseUnsupportedTag(PA_Tag* pTag, CEditElement*& pElement, intn& retVal);
    void ParseLink(PA_Tag* pTag, CEditElement*& pElement, intn& retVal);
    void ParseLinkFontDef(PA_Tag* pTag, CEditElement*& pElement, intn& retVal);
    intn ParseEndTag(PA_Tag* pTag);
    void WriteClosingScriptTag();
public:
    void FinishedLoad();
    void FinishedLoad2();
    void DummyCharacterAddedDuringLoad();

    XP_Bool IsComposeWindow(){ return m_pContext->bIsComposeWindow; }

    void PrintTree( CEditElement* m_pRoot );
    void DebugPrintTree( CEditElement* m_pRoot );
    XP_HUGE_CHAR_PTR GetPositionalText();
    void StreamToPositionalText( CEditElement* pElement, IStreamOut* pOut );

    void PrintDocumentHead( CPrintState *pPrintState );
    void PrintDocumentEnd( CPrintState *pPrintState );
    XP_Bool IsBodyTagRequired();
    static void Write(CStreamOutMemory *pSource, IStreamOut* pDest);
    void AppendTitle( char* pTitleString );

    void RepairAndSet(CEditSelection& selection);
    XP_Bool Reduce( CEditElement* pRoot );
    void NormalizeTree( );
    
    // Change Table and alls cell width data to match
    //  sizes calculated by Layout. Must do for all tables during Relayout()
    //  else generated HTML is very misleading!
    void FixupTableData();
    CEditElement* FindRelayoutStart( CEditElement *pStartElement );
    void Relayout( CEditElement *pStartElement, int iOffset,
            CEditElement *pEndElement = 0, intn relayoutFlags = 0 );

	void Reflow( CEditElement *pStartElement, int iOffset,
            CEditElement *pEndElement = 0, intn relayoutFlags = 0 );

    // Call these instead of Relayout( pTable )
    //  when we want to relayout entire table because we are changing
    //  either the table size or cell size(s)
    // bChangeWidth and bChangeHeight tell us which dimension is changing
    void ResizeTable(CEditTableElement *pTable, XP_Bool bChangeWidth = FALSE, XP_Bool bChangeHeight = FALSE);
    void ResizeTableCell(CEditTableElement *pTable, XP_Bool bChangeWidth = FALSE, XP_Bool bChangeHeight = FALSE);

    // Relayout current selected table (or parent of selected cells)
    void RelayoutSelectedTable();

    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();
    void NavigateDocument(XP_Bool bSelect, XP_Bool bForward );
    /* Begin of document check preference and if move curosr is enabled, 
        we will move the cursor if not, we leave the cursor alone.
        REQUIRES: (Boolean to select or not), Valid editbuffer
        RETURNS: Nothing
        NOTE: uses
        NavigateChunk( bSelect, LO_NA_DOCUMENT, FALSE ); 
            if we move the cursor.
        and
        FE_ScrollDocTo (MWContext *context, int iLocation, int32 x,int32 y);
            if we do not
    */
    void PageUpDown(XP_Bool select, XP_Bool pagedown);
    /*PageUpDown requires:  Boolean to select the scrolled text if cursor should move
                            Boolean , true if page down, false if page up.
                  effects:  moves cursor or not depending on preferences.*/

    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 InsertChars( char* pNewChars, XP_Bool bTyping , XP_Bool bReduce);
    EDT_ClipboardResult DeletePreviousChar();
    EDT_ClipboardResult DeleteNextChar();
    EDT_ClipboardResult DeleteChar(XP_Bool bForward, XP_Bool bTyping = TRUE);

    // Delete key clears contents, but doesn't delete cells
    EDT_ClipboardResult ClearSelectedCells();

    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 );
    // Return FALSE if at the 1st or last cell
    // Option pRowCounter: if supplied, it is incremented when we move to a new row
    XP_Bool NextTableCell( XP_Bool bSelect, XP_Bool bForward, intn* pRowCounter = NULL );
    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 );
    void ClearMailQuote();
    EDT_ClipboardResult ReturnKey(XP_Bool bTyping);
    EDT_ClipboardResult TabKey(XP_Bool bForward, XP_Bool bForceTabChar);
    EDT_ClipboardResult InternalReturnKey(XP_Bool bRelayout);
    void Indent();
    void IndentSelection(CEditSelection& selection);
    void IndentContainer( CEditContainerElement *pContainer,
            CEditListElement *pList );
    XP_Bool Outdent(); /* Returns TRUE if Outdent succeeded */
    XP_Bool OutdentSelection(CEditSelection& selection);
    void OutdentContainer( CEditContainerElement *pContainer,
            CEditListElement *pList );
    void MorphContainer( TagType t );
    void MorphListContainer( TagType t );

    // Added to use for selected regions and table cells
    void MorphContainerSelection( TagType t, CEditSelection& selection  );
    void MorphListContainer2( TagType t, CEditSelection& selection );
    void ToggleList(intn iTagType);
    void ToggleList2(intn iTagType, CEditSelection& selection);

    void SetParagraphAlign( ED_Alignment eAlign );
    void SetTableAlign( ED_Alignment eAlign );
    ED_Alignment GetParagraphAlign();
    void FormatCharacter( ED_TextFormat tf );

    // Format Character was modified to handle selected cells
    enum {
        ED_GET_FORMAT,
        ED_SET_FORMAT,
        ED_CLEAR_FORMAT
    };
    // This does similar scan of elements in selection, but only to find out if we should set
    //   or clear an attribute. Call this for a set of non-contiguous selected cells,
    //   then use result to set the iSetState for FormatCharacterSelection
    XP_Bool GetSetStateForCharacterSelection( ED_TextFormat tf, CEditSelection& selection );

    // iSetState default will get the set state in one pass,
    //   else it will use supplied state
    void FormatCharacterSelection( ED_TextFormat tf, CEditSelection& selection, XP_Bool bRelayout, intn iSetState = 0 );

    void SetCharacterData( EDT_CharacterData *pData );
    void SetCharacterDataSelection( EDT_CharacterData *pData, CEditSelection& selection, XP_Bool bRelayout );
    void SetCharacterDataAtOffset( EDT_CharacterData *pData,
            ED_BufferOffset iBufOffset, int32 iLen );

    EDT_CharacterData* GetCharacterData();
    // Pass in existing data to combine with previous data,
    //   used primarily for selected table cells
    EDT_CharacterData* GetCharacterDataSelection(EDT_CharacterData *pData, CEditSelection& selection);

    // Format all text contents into tab-delimeted cells,
    //  with CR/LF (or appropriate end-line for platform) at end of each row.
    // Use result to paste into spreadsheets like Excel
    char *GetTabDelimitedTextFromSelectedCells();

    // Convert Selected text into a table (put each paragraph in separate cell)
    // Number of rows is automatic - creates as many as needed
    void ConvertTextToTable(intn iColumns);

    // Convert the table into text - unravel existing paragraphs in cells
    void ConvertTableToText();

    // Save the character and paragraph style of selection or at caret
    void CopyStyle();
    
    // This is TRUE after EDT_CopyStyle is called, until the next left mouse up call
    XP_Bool CanPasteStyle() { return (m_pCopyStyleCharacterData != NULL); }

    // Apply the style to selection or at caret. Use bApplyStyle = FALSE to cancel
    void PasteStyle(XP_Bool bApplyStyle);

    // Get the index of the current font (at caret or selection)
    // Returns 0 (ED_FONT_VARIABLE), 1 (ED_FONT_FIXED), or 2 (ED_FONT_LOCAL)
    int GetFontFaceIndex();
    
    // Get the current font face for current selection or insert point
    // If the current font matches an XP font 'group',
    //    else it is the font face string from EDT_CharacterData.
    //    Use this to search your local font list
    //    in menu or listbox. DO NOT FREE RETURN VALUE (its a static string)
    char * GetFontFace();

    void ReplaceTextAtOffset( char *pText, EDT_CharacterData *pData,
            ED_BufferOffset iBufOffset, int32 iLen );

    void SetRefresh( XP_Bool bRefreshOn );

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

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

    ED_TextFormat GetCharacterFormatting();
    TagType GetParagraphFormatting();
    TagType GetParagraphFormattingSelection(CEditSelection& selection);
    int GetFontSize();
    void SetFontSize(int n);
    void SetFontSizeSelection(int n, CEditSelection& selection, XP_Bool bRelayout);
    int GetFontPointSize();
    void SetFontPointSize(int pointSize);
    ED_Color GetFontColor();
    void SetFontColor(ED_Color n);
    void SetFontColorSelection(ED_Color n, CEditSelection& selection, XP_Bool bRelayout);
    void InsertLeaf(CEditLeafElement *pLeaf);
    void InsertNonLeaf( CEditElement* pNonLeaf);
    EDT_ImageData* GetImageData();
    void SetImageData( EDT_ImageData* pData, XP_Bool bKeepImagesWithDoc );
    void InsertImage( EDT_ImageData* pData );
    int32 GetDefaultBorderWidth();
    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);
    
    // Set insert point to beginning or end of cell contents
    // Returns TRUE if we found an appropriate 
    //   paragraph + text element inside cell
    XP_Bool SetTableInsertPoint(CEditTableCellElement *pCell, XP_Bool bStartOfCell = FALSE);

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

    //cmanske: new:
    ED_MergeType GetMergeTableCellsType();
    XP_Bool CanSplitTableCell();
    void MergeTableCells();
    void SplitTableCell();

    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 ChangeTableSelection(ED_HitType iHitType, ED_MoveSelType iMoveType, EDT_TableCellData *pData = NULL);

    void SelectAndMoveToNextTableCell(XP_Bool bForward, EDT_TableCellData *pData = NULL);
    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 IsInsertPointInLayer();
    EDT_LayerData* GetLayerData();
    void SetLayerData(EDT_LayerData *pData);
    void InsertLayer(EDT_LayerData *pData);
    void DeleteLayer();

    EDT_PageData* GetPageData();
    void SetPageData( EDT_PageData* pData );
    static void FreePageData( EDT_PageData* pData );
    void SetImageAsBackground();
    
    intn MetaDataCount( );
    intn FindMetaData( EDT_MetaData *pMetaData );
    intn FindMetaData( XP_Bool bHttpEquiv, char* pName );
    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 );
    void PrintMetaData( CPrintState *pPrintState, int index );

    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(XP_Bool **, XP_Bool);

    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);

    // Show where we can drop during  dragNdrop. Handles tables as well
    XP_Bool PositionDropCaret(int32 x, int32 y);
    void DeleteSelectionAndPositionCaret(int32 x, int32 y);

    // Data for table currently being dragged.  Accessible to all edit windows
    static EDT_DragTableData* m_pDragTableData;

    // Setup data for dragging table cells
    XP_Bool StartDragTable(int32 x, int32 y);
    static XP_Bool IsDraggingTable() { return m_pDragTableData != NULL; }
    static void StopDragTable();

    /*StartSelection:
        used when mouse is clicked or double clicked.  calls clearmove then done typing before 
        going on to LO_Click/LO_DoubleClick.
    */
    void StartSelection( int32 x, int32 y , XP_Bool doubleClick = FALSE);
    void MoveAndHideCaretInTable(LO_Element *pLoElement); //cmanske: Move caret without scrolling or showing caret
    // Moves caret to an existing cell -- use after deleting a cell or row, but before Relayout();
    void MoveToExistingCell(CEditTableElement *pTable, int32 X, int32 Y);

    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();         //See below for new overloaded version

    void SelectTableCell();     //Now selects the cell border, not contents
    // Similar to the old version of SelectTableCell: selects a cells contents
    void SelectCellContents(CEditTableCellElement *pCell);

    // Very important for new Table/Cell selection strategy:
    // 1. Check if IsTableOrCellSelected() is TRUE,
    // 2. Call GetFirstCellSelection to contents of
    //     the "selected" table cell (from m_pSelectedEdTable or m_SelectedEdCells)
    // 3. Do whatever formating on returned selection data
    // 4. Call GetNextCellSelection() and repeat formatting
    //     until return value is FALSE.
    XP_Bool GetFirstCellSelection(CEditSelection& selection);
    XP_Bool GetNextCellSelection(CEditSelection& selection);

    // Similar to above, but just returns the cell pointer
    CEditTableCellElement *GetFirstSelectedCell();
    // *pRowCounter is incremented when 1st cell of next row is returned
    // Use these to build a selected table for cut/copy/paste of cells
    CEditTableCellElement *GetNextSelectedCell(intn *pRowCounter = NULL);


    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 );
    
    // Use data from supplied selection, or get from current selection if supplied is empty
    // Used were above used to be used -- allows using "selected" cell data
    void GetSelection( CEditSelection& selection, CEditLeafElement*& pStartElement, ElementOffset& iStartOffset,
                CEditLeafElement*& pEndElement, ElementOffset& iEndOffset, XP_Bool& bFromStart );

    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 SuspendAutoSave(); //CLM
    void ResumeAutoSave();

    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();
    void DocumentStored();

    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();
    ED_LinkId GetHREFLinkID();
    char *GetHREFText();
    void GetHREFData( EDT_HREFData *pData );
    void SetHREFData( EDT_HREFData *pData );
    void SetHREF( char *pHREF, char *pExtra );
    void SetHREFSelection( ED_LinkId eId );

    // Accessor functions

    ED_FileError SaveFile( ED_SaveFinishedOption finishedOpt,
                            char * pSourceURL, // used to resolve links in editor document.
                            ITapeFileSystem *tapeFS,  /* char *pDestURL, */
                            XP_Bool   bSaveAs,
                            XP_Bool   bKeepImagesWithDoc,
                            XP_Bool   bAutoAdjustLinks,
                            XP_Bool   bAutoSave,
                            char **ppIncludedFiles,
                            CEditSaveToTempData *pData = NULL);
    // Does actual save, from plugin callback.
    void SaveFileReal( CEditSaveData *pData);

    // Returns the desired on-disk, published output character set.
    int16 GetDocCharSetID();

    // Returns the current in-memory character set ID.
    // Will be the same as the wincsid, except without the AUTODETECT bits,
    // and except when the user has just changed the view encoding.
    int16 GetRAMCharSetID();

    void ForceDocCharSetID(int16 csid);

    ED_FileError PublishFile( ED_SaveFinishedOption finishedOpt,
                           char *pSourceURL,
                           char **ppIncludedFiles,
                           char *pDestURL, /* Must have trailing slash, ie after HEAD call */
                           char *pUsername,
                           char *pPassword,
                           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 );
    // returns length of pBuffer. (You can't use XP_STRLEN on a XP_HUGE_CHAR_PTR.)
    int32 WriteToBuffer( XP_HUGE_CHAR_PTR* pBuffer, XP_Bool bEncodeSelectionAsComment );

    // Warning! This will delete the CEditBuffer.
    void ReadFromBuffer(XP_HUGE_CHAR_PTR pBuffer);

    void WriteToStream( IStreamOut *stream );

    // Delegates to WriteToBuffer() and ReadFromBuffer().
    CEditDocState *RecordState();
    void RestoreState(CEditDocState *);

#ifdef DEBUG
    void DebugPrintState(IStreamOut& stream);
    void ValidateTree();
    void SuppressPhantomInsertPointCheck(XP_Bool bSuppress);
#endif
    void DisplaySource();
    void InitEscapes();
    char* NormalizeText( char* pSrc );
    intn NormalizePreformatText( pa_DocData *pData, PA_Tag *pTag, intn status );
    MWContext *GetContext(){ return m_pContext; }
    EDT_ClipboardResult PasteQuoteBegin(XP_Bool bHTML);
    EDT_ClipboardResult PasteQuote(char *pText);
    EDT_ClipboardResult PasteQuoteINTL(char *pText, int16 csid);
    EDT_ClipboardResult PasteQuoteEnd();
    void PasteHTMLHook(CPrintState* pPrintState); // For CPrintState.

    EDT_ClipboardResult PasteText(char *pText, XP_Bool bMailQuote, XP_Bool bIsContinueTyping, XP_Bool bRelayout, XP_Bool bReduce); /* Deprecated. */
    EDT_ClipboardResult PasteText(char *pText, XP_Bool bMailQuote, XP_Bool bIsContinueTyping, int16 csid,  XP_Bool bRelayout, XP_Bool bReduce);
    EDT_ClipboardResult PasteHTML(char *pText, XP_Bool bUndoRedo);
    EDT_ClipboardResult PasteHTML(IStreamIn& stream, XP_Bool bUndoRedo);
    EDT_ClipboardResult PasteCellsIntoTable(IStreamIn& stream, EEditCopyType iCopyType);
    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, EEditCopyType iCopyType = eCopyNormal );
    //cmanske: Added flags param to pass info about copied table elements
    XP_Bool CopySelectionContents( CEditSelection& selection,
                    IStreamOut& stream, EEditCopyType iCopyType = eCopyNormal );
    int32 GetClipboardSignature();
    int32 GetClipboardVersion();
    EDT_ClipboardResult CutSelection( char **ppText, int32* pTextLen,
            char **ppHtml, int32* pHtmlLen);
    XP_Bool CutSelectionContents( CEditSelection& selection,
                    char **ppHtml, int32* pHtmlLen );

    // cmanske: New Table-related cut/paste functions
    // Returns FALSE if number of cells in each row are not all the same
    XP_Bool CountRowsAndColsInPasteText(char *pText, intn* pRows, intn* pCols);
    // Supply Rows, Cols if known, else they will be calculated from text 
    EDT_ClipboardResult PasteTextAsNewTable(char *pText, intn iRows = 0, intn iCols = 0);
    EDT_ClipboardResult PasteTextIntoTable(char *pText, XP_Bool bReplace = FALSE, intn iRows = 0, intn iCols = 0);

    void RefreshLayout();
    void FixupSpace( XP_Bool bTyping = FALSE);
    ED_Alignment GetCurrentAlignment();
    int32 GetDesiredX(CEditLeafElement* 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
    intn GetUndoCommand(intn);
    intn GetRedoCommand(intn);
    void BeginBatchChanges(intn commandID);
    void EndBatchChanges();

    XP_Bool IsWritable(); // FALSE while a plugin is executing.

    // Typing command methods.
    void DoneTyping();
    void StartTyping(XP_Bool bTyping);

//cmanske - no longer using column/row indexes
//    void SyncCursor(CEditTableElement* pTable, intn iColumn, intn iRow);
//    CEditSelection ComputeCursor(CEditTableElement* pTable, intn iColumn, intn iRow);
    void SyncCursor(CEditLayerElement* pLayer);

    void ReplaceText( char *pReplaceText, XP_Bool bReplaceAll,
					  char *pTextToLookFor, XP_Bool bCaseless, XP_Bool bBackward, XP_Bool bDoWrap );

    /* function for finding and replacing text once */
    XP_Bool ReplaceOnce(char *pReplaceText, XP_Bool bRelayout, XP_Bool bReduce);

    void ReplaceLoop(char *pReplaceText, XP_Bool bReplaceAll,
                         char *pTextToLookFor, XP_Bool bCaseless,
                         XP_Bool bBackward, XP_Bool bDoWrap );

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

    XP_Bool IsMultiSpaceMode(){ return m_bMultiSpaceMode; }

private:
    // CLM: Dynamic object sizing
    CSizingObject *m_pSizingObject;
    
public:
    // Table sizing, selection, add row or col interface
    
    // Get the hit type from current mouse location in the doc
    //  (See ED_HitType enum in include/edttypes.h)
    // Optional: Returns the table or cell element clicked on
    // bModifierKeyPressed is applicable if selecting table (upper left corner),
    //  or moving mouse over cell to extend selection.
    //  If TRUE, returns ED_HIT_SEL_ALL_CELLS instead of ED_HIT_SEL_TABLE
    ED_HitType GetTableHitRegion(int32 xVal, int32 yVal, LO_Element **ppElement, XP_Bool bModifierKeyPressed = FALSE);

    // Tells us where to insert or replace cell (cell we are over is in *ppElement)
    ED_DropType GetTableDropRegion(int32 *pX, int32 *pY, int32 *pWidth, int32 *pHeight, LO_Element **ppElement);

    // Used withing editor - use supplied cell's X and Y and get LO_CellStruct from it
    //   to pass to following version.
    // iHitType can be set to just 1 cell, or entire row or column
    XP_Bool SelectTableElement(CEditTableCellElement *pCell, ED_HitType iHitType);

    // This is only table select function exposed to FEs via EDT_SelectTableElement
    // Select a table, cell, row, or column and redraw table to show new selection
    // If selecting cells and bAppendSelection is TRUE, then current selected items will not be cleared
    //  (ignored if selecting a table - cell selection will be cleared)
    // All uses ignore x and y except when ED_HitType = ED_HIT_SEL_COL or ED_HIT_SEL_ROW,
    //   which need those coordinates to find the REAL column or row 
    //   when ROWSPAN or COLSPAN is used.
    // If pLoElement == NULL, then the current caret location is used
    //   to locate element by calling GetCurrentLoTableElement
    XP_Bool SelectTableElement(int32, int32 y, LO_Element *pLoElement, ED_HitType iHitType, 
                               XP_Bool bModifierKeyPressed = FALSE,
                               XP_Bool bExtendSelection = FALSE );

    // Helper for SelectTableElement - find appropriate element at current caret location
    LO_Element* GetCurrentLoTableElement(ED_HitType iHitType, int32 *pX, int32 *pY);

       
    // Called on mouse-move message after selection was started
    // on a row, column, or cell element
    // Returns the hit type: ED_HIT_SEL_ROW, ED_HIT_SEL_COL, ED_HIT_SEL_CELL, or ED_HIT_NONE
    //   reflecting what type of block we are extended,
    //     or ED_HIT_NONE if mouse is outside of the table
    //   Use this to set the type of cursor by the FEs
    ED_HitType ExtendTableCellSelection(int32 x, int32 y);

    // Used by ExtendTableCellSelection
    //  and SelectTableElement when bExtendSelection is TRUE,
    // Select all cells whose top and left are within a rect formed by
    //   left and top last selected cell (m_pSelectedTableElement) and pLastCell
    void SelectBlockOfCells(LO_CellStruct *pLastCell);

    // Get corresponding layout cell struct containing the given edit element
    // Null if not in a table, of course
    LO_CellStruct* GetLoCell(CEditElement *pEdElement);

    // Set or clear the two table pointers (m_pSelectedEdTable, m_pSelectedLoTable)
    // pLoTable must be supplied if bSelect is TRUE. 
    //   If FALSE, existing selected table is unselected
    // Table selection is redisplayed if changed from existing state
    //   pEdTable is optional - it will be found from pLoTable if pEdTable = NULL
    //   and returned
    CEditTableElement* SelectTable(XP_Bool bSelect, LO_TableStruct *pLoTable = NULL, CEditTableElement *pEdTable = NULL);

    // Helper to select a single cell - both Lo and Edit arrays updated
    //   and cells are redisplayed. pLoCell must be supplied, but
    //   pEdCell is optional - it will be found from pLoCell if pEdCell = NULL
    //   and returned
    CEditTableCellElement* SelectCell(XP_Bool bSelect, LO_CellStruct *pLoCell, CEditTableCellElement *pEdCell = NULL);
    
    // Set the special selection attribute used for all currently-selected cells,
    //  except for the supplied "focus" cell.
    // Front ends should always display the "special" attribute
    //  if that bit (LO_ELE_SELECTED_SPECIAL) even if LO_ELE_SELECTED ) is also set
    void DisplaySpecialCellSelection( CEditTableCellElement *pFocusCell = NULL,  EDT_TableCellData *pCellData = NULL );
    
    // Select current cell and change other currently-selected cells
    //  to LO_ELE_SELECTED_SPECIAL so user can tell difference between
    //  the "focus" cell (current, where caret is) and other selected cells
    //  Call just before Table Cell properties dialog is created
    // Supply pCellData to update iSelectionType and iSelectedCount values
    void StartSpecialCellSelection( EDT_TableCellData *pCellData = NULL );

    // Remove LO_ELE_SELECTED_SPECIAL from all currently-selected cells
    // Call after closing the Table Cell properties dialog
    void ClearSpecialCellSelection();

    // Rearrange order of cells in selected cell lists as they
    //   appear from upper left to lower right (rows first) 
    void SortSelectedCells();
    
    // Remove selection from all tables and cells (both Editor and LO_Element elements affected)
    void ClearTableAndCellSelection();
    
    // Clear any existing cells selected if current edit element is not inside selection
    void ClearCellSelectionIfNotInside();

    // If the supplied element is in our Table or Selection list,
    //  clear the selection. Call when deleting an element
    void ClearTableIfContainsElement(CEditElement *pElement);
    
    // Called from front ends before during redrawing of screen
    void ClearLastTableSelection();

    XP_Bool IsTableSelected() {return m_pSelectedEdTable != NULL; }
    XP_Bool IsTableOrCellSelected() { return (XP_Bool)(m_pSelectedEdTable ? TRUE : m_SelectedEdCells.Size()); }
    int     GetSelectedCellCount() { return m_SelectedEdCells.Size(); }


    // For special cell highlighting during drag and drop
    // pLoElement can be the table or any cell that is starting cell,
    //  all cells from starting cell will have attribute removed
    void ClearCellAttrmask(LO_Element *pLoElement, uint16 attrmask );

    // Start Column or Row search from current element
    // This works on geometric location data, not "logical" row or column
    CEditTableCellElement* GetFirstCellInCurrentColumn();
    CEditTableCellElement* GetFirstCellInCurrentRow();

    intn GetNumberOfSelectedColumns();
    intn GetNumberOfSelectedRows();
    void MoveToFirstSelectedCell();
    void MoveToLastSelectedCell();

    // Dynamic object sizing interface
    XP_Bool       IsSizing() { return (m_pSizingObject != 0); }
    ED_SizeStyle  CanSizeObject(LO_Element *pLoElement, int32 xVal, int32 yVal);
    ED_SizeStyle  StartSizing(LO_Element *pLoElement, int32 xVal, int32 yVal, XP_Bool bLockAspect, XP_Rect *pRect);
    XP_Bool       GetSizingRect(int32 xVal, int32 yVal, XP_Bool bLockAspect, XP_Rect *pRect);
    void          EndSizing();
    void          CancelSizing();
    
    XP_Bool       IsReady();

    // Spellcheck api
    XP_Bool FindNextMisspelledWord( XP_Bool bFirst, XP_Bool bSelect,
            CEditLeafElement **ppWordStart );

    // Iterate over mispelled words, replace or ignore them.
    enum EMSW_FUNC { EMSW_IGNORE, EMSW_REPLACE };
    void IterateMisspelledWords( EMSW_FUNC eFunc, char* pOldWord, char* 
            pNewWord, XP_Bool bAll );

    LO_Element* FirstElementOnLine(LO_Element* pTarget, int32* pLineNum);

    void ChangeEncoding(int16 csid);
    void SetEncoding(int16 csid);
    XP_Bool HasEncoding();

    // Used for QA only - Ctrl+Alt+Shift+N accelerator for automated testing
    void SelectNextNonTextObject();
    CEditTableElement *m_pNonTextSelectedTable;

#ifdef EDITOR_JAVA
    EditorPluginManager GetPlugins();
#endif

public:
    static XP_Bool IsAlive(CEditBuffer* pBuffer); // Tells you if the pointer is valid.

private:
    void CheckAndPrintComment(CEditLeafElement* pElement, CEditSelection& selection, XP_Bool bEnd);
    void CheckAndPrintComment2(const CEditInsertPoint& where, CEditSelection& selection, XP_Bool bEnd);

    void SetSelectionInNewDocument();
    void ParseBodyTag(PA_Tag *pTag);
    void RecordTag(PA_Tag* pTag, XP_Bool bWithLinefeed);
    void RecordJavaScriptAsUnknownTag(CStreamOutMemory* pOut);

    void SetBaseTarget(char* pTarget);
    char* GetBaseTarget(); /* NULL if no BASE TARGET */

    CFinishLoadTimer m_finishLoadTimer;
    CRelayoutTimer m_relayoutTimer;
    CAutoSaveTimer m_autoSaveTimer;
    XP_Bool m_bDisplayTables;
    XP_Bool m_bDummyCharacterAddedDuringLoad;
    static XP_Bool m_bAutoSaving;
    XP_Bool m_bReady;
    XP_Bool m_bPasteQuoteMode;
    XP_Bool m_bPasteHTML;
    XP_Bool m_bPasteHTMLWhenSavingDocument;
    CStreamOutMemory* m_pPasteHTMLModeText;
    CConvertCSIDStreamOut* m_pPasteTranscoder;
    XP_Bool m_bAbortPasteQuote;
    // In case the user sets the encoding
    int16 m_originalWinCSID;
    XP_Bool m_bForceDocCSID;
    int16 m_forceDocCSID;

//preference information
    static XP_Bool m_bMoveCursor; //true = move cursor when pageup/down false, just move scrollbar
    static XP_Bool m_bEdtBufPrefInitialized; //are the preferences initialized
    static int PrefCallback(const char *,void *);//callback for preferences
    static void InitializePrefs();//call this function to force initialization of preferences
};


//
// 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);
};

//-----------------------------------------------------------------------------
// CEditSaveToTempData: used to call back to FE after saving document to temp
// file.  Could be expanded to be a general callback mechanism for reporting
// when saving is complete.
//-----------------------------------------------------------------------------
class CEditSaveToTempData {
public:
  CEditSaveToTempData() : doneFn(NULL), hook(NULL), pFileURL(NULL) {}
  ~CEditSaveToTempData() {XP_FREEIF(pFileURL);}

  EDT_SaveToTempCallbackFn doneFn;
  void *hook;
  char *pFileURL;
};

//-----------------------------------------------------------------------------
//  CFileSaveObject
//-----------------------------------------------------------------------------
// Takes care of FE interaction, does not know that there is anything
// special about the first file (the root HTML document for CEditSaveObject).
class CFileSaveObject {
    friend void edt_CFileSaveObjectDone(XP_Bool,void *); // A callback.

private:
    XP_Bool m_bOverwriteAll;
    XP_Bool m_bDontOverwriteAll;
    XP_Bool m_bDontOverwrite;
    int m_iCurFile; // 1-based
    IStreamOut *m_pOutStream;
    XP_Bool m_bOpenOutputHandledError;
    CEditSaveToTempData *m_pSaveToTempData;

protected:
    // Override this if the child of CFileSaveObject needs to do anything special to save the
    // first file.  O.w. it will just be copied from source to dest like everything else.
    // Return TRUE if the child took care of saving the first file.
    virtual XP_Bool SaveFirstFile() {return FALSE;}

    // can be a full file path.  Grabs the path part automatically.
    // Return the path part of pFileURL, alloced with XP_STRDUP.
    char *GetPathURL( char* pFileURL );

    // Check to see if we should call FE_FinishedSave() to tell
    // the front end that a new file has been created.
    void CheckFinishedSave(intn iFileIndex,ED_FileError iError);

    MWContext *m_pContext;
    ITapeFileSystem *m_tapeFS;
    ED_FileError m_status;
    XP_Bool m_bFromAutoSave; // This save was initiated from the auto save callback.

public:
    //
    // inherited interface.
    //
    CFileSaveObject( MWContext *pContext, char *pSrcURL, ITapeFileSystem *tapeFS, XP_Bool bAutoSave,
                     CEditSaveToTempData * ) ;
    virtual ~CFileSaveObject();

    // Return values like ITapeFileSystem::AddFile().
    intn AddFile( char *pSrcURL, char *pMIMEType, int16 iDocCharSetID );

    ED_FileError SaveFiles();

    char *GetDestName( intn index );
    char *GetDestAbsoluteURL( intn index );
    char *GetSrcName( intn index );
    // Return newly allocated string.

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

    void Cancel();

	// Communicate content type and other information to the tape file system.
	// This function is called from edt_MakeFileSaveStream 
	// after OpenOutputFile() and before FileFetchComplete().
	// Necessary so that libmime can convey the mime type of an inferior URL
	// that represents, for example, part of a mail or news message.
	void CopyURLInfo(const URL_Struct *pURL);
	
private:
    ED_FileError FetchNextFile();
/*    intn OpenOutputFile( int iFile ); */

    // Report error given by iError on the current file being processed.
    // Return whether we sould continue.  
    // Replaces FE_SaveErrorContinueDialog().
    XP_Bool SaveErrorContinueDialog(ED_FileError iError);

// 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:
    enum {IgnoreImage = -1, AttemptAdjust = -2};

    // Can return IgnoreImage or AttemptAdjust
    // If ppIncludedFiles is non-NULL, image will only be added if it is in
    // the list.
    // If ppIncludeFiles is NULL, use bNoSave to decide whether to add it.
    intn CheckAddFile( char *pSrc, char *pMIMEType, char **ppIncludedFiles, XP_Bool bNoSave );

    ////XP_Bool IsSameURL( char* pSrcURL, char *pLocalName );

   // ppList is a NULL-terminated list of absolute URLs.
   XP_Bool URLInList (char **ppList, char *pURL);

    // helper for FixupLinks().
    void FixupLink(intn iIndex, // index to tape file system
                   char **ppImageURL,   // Value to fixup.
                   char *pDestPathURL,
                   ED_HREFList *badLinks);

    char *FixupLinks();

    CEditBuffer *m_pBuffer;
    CEditDocState *m_pDocStateSave; // Backup of m_pBuffer before doing anything.
    char *m_pSrcURL;
    //XP_Bool m_bSaveAs;
    ED_SaveFinishedOption m_finishedOpt; // Should we be editing the newly saved doc after saving is done.
    XP_Bool m_bKeepImagesWithDoc;
    XP_Bool m_bAutoAdjustLinks;
    intn m_backgroundIndex;
    intn m_fontDefIndex;

public:
    CEditSaveObject( CEditBuffer *pBuffer, 
                            ED_SaveFinishedOption finishedOpt, char *pSrcURL, ITapeFileSystem *tapeFS,
                            XP_Bool bSaveAs,
                            XP_Bool bKeepImagesWithDoc, 
                            XP_Bool bAutoAdjustLinks,
                            XP_Bool bAutoSave,
                            CEditSaveToTempData *pSaveToTempData ); 
    ~CEditSaveObject();

    // Add HTML document, then find and add all images in document.
    // Returns success or not.
    // If pIncludedFiles in non-NULL, it is a NULL-terminated list of absolute
    // URLs telling which images may be published..  
    // Free all memory pointed to by ppIncludedFiles when done.
    XP_Bool AddAllFiles(char **ppIncludedFiles);

    virtual ED_FileError FileFetchComplete();

    // If list is non-null, free it element-wise, then free the list itself.
    // list is NULL-terminated if it exists.
    static void FreeList( char **list );

    // Save the source URL when we fail to publish
    // Used by EDT_GetDefaultPublishUrl to return the previous failed destination
    static char * m_pFailedPublishUrl;

protected:
    virtual XP_Bool SaveFirstFile();
};


#if 0 //// Now obsolete
//-----------------------------------------------------------------------------
//  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();
};
#endif

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

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

    virtual ED_FileError FileFetchComplete();
};
#endif

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_setAutoStartBody;
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;
extern CBitArray *edt_setContainerBreakConvert;
extern CBitArray *edt_setContainerHasLineAfter;
extern CBitArray *edt_setIgnoreBreakAfterClose;

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_TextFormat 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, int16 win_csid );
XP_Bool edt_FetchParamBoolExist( PA_Tag *pTag, char* param, int16 win_csid );
XP_Bool edt_FetchDimension( PA_Tag *pTag, char* param,
                         int32 *pValue, XP_Bool *pPercent,
                         int32 nDefaultValue, XP_Bool bDefaultPercent, int16 win_csid );
// Old defaults: ED_ALIGN_BASELINE, FALSE
ED_Alignment edt_FetchParamAlignment( PA_Tag* pTag,
                ED_Alignment eDefault, XP_Bool bVAlign, int16 win_csid );
int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue, int16 win_csid );
int32 edt_FetchParamInt( PA_Tag *pTag, char* param, int32 defValue, int32 defValueIfParamButNoValue, int16 win_csid );
ED_Color edt_FetchParamColor( PA_Tag *pTag, char* param, int16 win_csid );

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

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

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

void edt_InitEscapes(int16 csid, XP_Bool bQuoteHiBits);
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();

// Given an absolute URL, return the path portion of it, allocated with XP_STRDUP.
char *edt_GetPathURL(char *);

//
// Convert NBSPs into spaces.
//
void edt_RemoveNBSP( int16 csid, char *pString );

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

// Replace the Value of a Param, rebuilding the Tag data within given tag
// Returns TRUE if param NAME was already in the list. If it wasn't,
//   it is added to end of tag data. If pValue is NULL, pName is removed from tag
XP_Bool edt_ReplaceParamValue( PA_Tag *pTag, char * pName, char * pValue, int16 csid );

// Copies text out of a Huge pointer. Truncates it if it's too large for the platform.
// Returns a copy of the text. Caller owns the copy.
// csid is the character set id of the text. Used to make sure truncation occurs on a
// character boundary.
// pOutLength is the output length in bytes. Pass NULL if you don't care about the new length.
// The result is always zero terminated, so that if the input does not contain nulls,
// XP_STRLEN(result) == *pOutLenth.

char* edt_CopyFromHuge(int16 csid, XP_HUGE_CHAR_PTR text, int32 length, int32* pOutLength);


// Return dup'd string that is the same as pUrl except that any username/password info is removed.
char *edt_StripUsernamePassword(char *pUrl);

// Terminate string in place at "#" or "?"
void edt_StripAtHashOrQuestionMark(char *pUrl);

// Kills directory pName and, recursively, everything under it.
// pName is in xpURL format.
void edt_RemoveDirectoryR(char *pName);

// Just wrappers around the XP_* functions.  The only difference is that
// these functions will work if the directory ends in a trailing slash,
// avoiding obscure bugs on Win16.
XP_Dir edt_OpenDir(const char * name, XP_FileType type);
int edt_MakeDirectory(const char* name, XP_FileType type);
int edt_RemoveDirectory(const char *name, XP_FileType type);

// Returns the URL that should be used as the base for relative URLs in 
// the document.  Similar to just calling LO_GetBaseURL(), except returns
// the document temp directory for untitled documents.
// Must free returned memory.
char *edt_GetDocRelativeBaseURL(MWContext *pContext);

// Platform-specific end-of-line character(s) added to string,
//  returning in possibly-reallocated buffer that caller must free
char *edt_AppendEndOfLine(char *pText);
//
// Parse return values.
//
#define OK_IGNORE       2
#define OK_CONTINUE     1
#define OK_STOP         0
#define NOT_OK          -1

// Inline function implementations

inline char *edt_StrDup( char *pStr ){
    if( pStr ){ 
        return XP_STRDUP( pStr );
    }
    else {
        return 0;
    }
}

#ifdef DEBUG

class CEditTestManager {
public:
    CEditTestManager(CEditBuffer* pBuffer);
    XP_Bool Key(char key);
    XP_Bool Backspace();
    XP_Bool ReturnKey();

protected:

    void PowerOnTest();

    void DumpLoElements();
    void VerifyLoElements();
    void DumpDocumentContents();

    void PasteINTL();
    void PasteINTLText();

    void CopyBufferToDocument();
    void CopyDocumentToBuffer();

    XP_Bool ArrowTest();
    XP_Bool LArrowTest(XP_Bool bSelect);
    XP_Bool RArrowTest(XP_Bool bSelect);
    XP_Bool UArrowTest(XP_Bool bSelect);
    XP_Bool DArrowTest(XP_Bool bSelect);
    XP_Bool EveryNavigationKeyEverywhereTest();
    XP_Bool NavigateChunkCrashTest();
    XP_Bool NavChunkCrashTest(XP_Bool bSelect, int chunk, XP_Bool bDirection);
    XP_Bool DeleteKeyTest();
    XP_Bool BackspaceKeyTest();
    XP_Bool ZeroDocumentCursorTest();
    XP_Bool OneCharDocumentCursorTest();
    XP_Bool OneDayTest(int32 rounds);
    XP_Bool BoldTest(int32 rounds);
    XP_Bool TextFETest(); // Not really a test.

    void GetWholeDocumentSelection(CPersistentEditSelection& selection);
    void    DumpMemoryDelta();
#ifdef EDITOR_JAVA
    void DumpPlugins();
    void PerformFirstPlugin();
    void PerformPluginByName();
    void PerformFirstEncoder();
    void PerformPreOpen();
#endif
    void TestHTMLPaste();
    void SaveToTempFile();
    static void SaveToTempFileCB(char *pFileURL,void *hook);
    void RemoveTempFile();
    // Create the document-specific temporary directory.
    void GetDocTempDir();
private:
    CEditBuffer* m_pBuffer;
    XP_Bool m_bTesting;
    int m_iTriggerIndex;
    static const char* m_kTrigger;

    XP_HUGE_CHAR_PTR m_pSaveBuffer;

#ifdef XP_WIN32
    _CrtMemState m_state;
#endif
    char *m_pTempFileURL; // for SaveToTempFile() and RemoveTempFile().
};

#endif  //DEBUG

// Semantic sugar for debugging. Validates the tree on construction and
// destruction. Place in every method that munges the tree.
//
// VALIDATE_TREE(this);

#ifdef DEBUG
class CTreeValidater {
    CEditBuffer* m_pBuffer;
public:
    CTreeValidater(CEditBuffer* pBuffer)
     : m_pBuffer(pBuffer)
    {
        m_pBuffer->ValidateTree();
    }
    ~CTreeValidater() {
        m_pBuffer->ValidateTree();
    }
};

class CSuppressPhantomInsertPointCheck {
    CEditBuffer* m_pBuffer;
public:
    CSuppressPhantomInsertPointCheck(CEditBuffer* pBuffer)
     : m_pBuffer(pBuffer)
    {
        m_pBuffer->SuppressPhantomInsertPointCheck(TRUE);
    }
    ~CSuppressPhantomInsertPointCheck() {
        m_pBuffer->SuppressPhantomInsertPointCheck(FALSE);
    }
};

// The local variable names will cause problems if you try to
// use these macros twice in the same scope. However,
// they are nescessary to ensure that some compilers (MS VC 2.2)
// keep the object alive for the duration of the scope, instead
// of constructing and destructing it at the point of declaration.
// I think this may be a bug in the compiler -- but I'd have
// to check the ANSI standard to make sure.

#define VALIDATE_TREE(BUFFER) CTreeValidater CTreeValidater_x_zzx1(BUFFER)
#define SUPPRESS_PHANTOMINSERTPOINTCHECK(BUFFER) \
    CSuppressPhantomInsertPointCheck CSuppressPhantomInsertPointCheck_x_zzx2(BUFFER)
#else

#define VALIDATE_TREE(BUFFER)
#define SUPPRESS_PHANTOMINSERTPOINTCHECK(BUFFER)

#endif

// Find the current context's URL in the cached history data and update the
//   corresponding Title. Used by CEditBuffer::SetPageData()
void edt_UpdateEditHistoryTitle(MWContext * pMWContext, char * pTitle);

// Get the corresponding CEditTableElement if LoElementType == LO_TABLE,
//   or CEditTableCellElement if LoElementType = LO_CELL
CEditElement* edt_GetTableElementFromLO_Element(LO_Element *pLoElement, int16 LoElementType);

#endif  // _EDITOR_H
#endif  // EDITOR


