Milán Major

netscape-revival

1 branch
Code

lib/layout/edtcmd.cpp

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

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

#ifdef EDITOR

#include "editor.h"
#ifdef DEBUG

const char* kCommandNames[kCommandIDMax+1] = {
    "Null",
    "Typing", 
    "AddText", 
    "DeleteText", 
    "Cut", 
    "PasteText", 
    "PasteHTML", 
    "PasteHREF", 
    "ChangeAttributes", 
    "Indent", 
    "ParagraphAlign", 
    "MorphContainer", 
    "InsertHorizRule", 
    "SetHorizRuleData", 
    "InsertImage", 
    "SetImageData", 
    "InsertBreak", 
    "ChangePageData", 
    "SetMetaData", 
    "DeleteMetaData", 
    "InsertTarget", 
    "SetTargetData", 
    "InsertUnknownTag", 
    "SetUnknownTagData", 
    "GroupOfChanges",                                           
    "SetListData",
    "InsertTable",
    "DeleteTable",
    "SetTableData",
    "InsertTableCaption",
    "SetTableCaptionData",
    "DeleteTableCaption",
    "InsertTableRow",
    "SetTableRowData",
    "DeleteTableRow",
    "InsertTableColumn",
    "SetTableCellData",
    "DeleteTableColumn",
    "InsertTableCell",
    "DeleteTableCell",
    "InsertMultiColumn",
    "DeleteMultiColumn",
    "SetMultiColumnData",
    "SetSelection"
};

#endif

CEditText::CEditText() { m_pChars = NULL; m_iLength = 0; m_iCount = 0; }
CEditText::~CEditText() { Clear(); }

void CEditText::Clear() {
    if ( m_pChars )
        XP_HUGE_FREE(m_pChars);  // Tied to implementation of CStreamOutMemory
    m_pChars = 0;
    m_iCount = 0;
    m_iLength = 0;
}

char* CEditText::GetChars() { return m_pChars; }
int32 CEditText::Length() { return m_iLength; }
char** CEditText::GetPChars() { return &m_pChars; }
int32* CEditText::GetPLength() { return &m_iLength; }

// CEditCommand

CEditCommand::CEditCommand(CEditBuffer* editBuffer, intn id)
{
    m_id = id;
    m_editBuffer = editBuffer;
}

CEditCommand::~CEditCommand()
{
}

void CEditCommand::Do()
{
}

void CEditCommand::Undo()
{
}

void CEditCommand::Redo()
{
    Do();
}

intn CEditCommand::GetID()
{
    return m_id;
}

#ifdef DEBUG
void CEditCommand::Print(IStreamOut& stream) {
    const char* name = "Unknown";
    if ( m_id >= 0 && m_id <= kCommandIDMax ){
        name = kCommandNames[m_id];
    }
    stream.Printf("%s(%ld) ", name, (long)m_id);
}
#endif

// CEditCommandLog

// We were having problems where we were entering a command while processing the undo of another command.
// This would cause the command log to be trimmed, which would delete the undo log, including the
// command that was being undone. This helps detect that situation.

#ifdef DEBUG

class CEditCommandLogRecursionCheckEntry {
private:
    XP_Bool m_bOldBusy;
    CEditCommandLog* m_log;
public:
    CEditCommandLogRecursionCheckEntry(CEditCommandLog* log) {
        m_log = log;
        m_bOldBusy = m_log->m_bBusy;
        XP_ASSERT(m_log->m_bBusy == FALSE);
        m_log->m_bBusy = TRUE;
    }
    ~CEditCommandLogRecursionCheckEntry() {
        m_log->m_bBusy = m_bOldBusy;
    }
};

#define DEBUG_RECURSION_CHECK CEditCommandLogRecursionCheckEntry debugRecursionCheckEntry(this);
#else
#define DEBUG_RECURSION_CHECK
#endif

CEditCommandLog::CEditCommandLog()
{
    m_size = m_undoSize = 0;
    m_historyLimit = EDT_CMD_LOG_MAXHISTORY;
    m_commandList.SetSize(m_historyLimit);
#ifdef DEBUG
    m_bBusy = FALSE;
#endif
}

CEditCommandLog::~CEditCommandLog()
{
    Trim();
}

void CEditCommandLog::AdoptAndDo(CEditCommand* pCommand)
{
    DEBUG_RECURSION_CHECK

    if ( m_batch.IsEmpty() ){
        InternalAdoptAndDo(pCommand);
    }
    else
    {
        m_batch.Top()->AdoptAndDo(pCommand);
    }
}

void CEditCommandLog::InternalAdoptAndDo(CEditCommand* command)
{
    command->Do();
    Trim(m_undoSize, m_size);
    if ( m_undoSize >= m_historyLimit ) {
        delete m_commandList[0];
        for ( intn i = 0; i < m_undoSize - 1; i++ ) {
            m_commandList[i] = m_commandList[i + 1];
        }
        m_undoSize--;
    }
    m_commandList[m_undoSize++] = command;
    m_size = m_undoSize;
}

void CEditCommandLog::Undo()
{
    DEBUG_RECURSION_CHECK
    if ( m_undoSize > 0 )
    {
        FinishBatchCommands();
        CEditCommand* command = m_commandList[--m_undoSize];
        command->Undo();
    }
}

void CEditCommandLog::Redo()
{
    DEBUG_RECURSION_CHECK
    if ( m_undoSize < m_size )
    {
        FinishBatchCommands();
        CEditCommand* command = m_commandList[m_undoSize++];
        command->Redo();
    }
}

void CEditCommandLog::FinishBatchCommands()
{
    if ( !m_batch.IsEmpty() ){
        XP_ASSERT(FALSE);
        while ( !m_batch.IsEmpty() ){
            EndBatchChanges();
        }
    }
}

void CEditCommandLog::Trim()
{
    XP_TRACE(("Trimming undo log."));
    Trim(0, m_size);
    m_undoSize = 0;
    m_size = 0;
}

void CEditCommandLog::Trim(intn start, intn end)
{
    for ( intn i = start; i < end; i++ )
    {
        CEditCommand* command = m_commandList[i];
        delete command;
         m_commandList[i] = 0;
    }
}

intn CEditCommandLog::GetCommandHistoryLimit()
{
    return m_historyLimit;
}

void CEditCommandLog::SetCommandHistoryLimit(intn newLimit) {
    if ( newLimit >= 0 ) {
        Trim();
        // To Do: Preserve existing commands, if any.
        m_historyLimit = newLimit;
        m_commandList.SetSize(m_historyLimit);
    }
}

intn CEditCommandLog::GetNumberOfCommandsToUndo()
{
    return m_undoSize;
}

intn CEditCommandLog::GetNumberOfCommandsToRedo()
{
    return m_size - m_undoSize;
}

// Returns NULL if out of range
CEditCommand* CEditCommandLog::GetUndoCommand(intn index)
{
    if ( index < 0 || index >= m_undoSize)
        return NULL;
    return m_commandList[(m_undoSize - 1) - index];
}

CEditCommand* CEditCommandLog::GetRedoCommand(intn index)
{
    if ( index < 0 || index >= GetNumberOfCommandsToRedo() )
        return NULL;
    return m_commandList[m_undoSize + index];
}

#ifdef DEBUG
void CEditCommandLog::Print(IStreamOut& stream) {
    {
        int size = m_batch.StackSize();
        stream.Printf("Batch command stack: %d items\n", size);
        for ( intn i = 0; i < size; i++ ) {
            CEditCommandGroup* pCommand = m_batch[i];
            stream.Printf(" %2d 0x%08x ", i, pCommand);
            pCommand->Print(stream);
            stream.Printf("\n");
        }
    }
    {
        stream.Printf("Undo list: %d commands\n", GetNumberOfCommandsToUndo());
        for ( intn i = 0; i < GetNumberOfCommandsToUndo(); i++ ) {
            CEditCommand* pCommand = GetUndoCommand(i);
            stream.Printf(" %2d 0x%08x ", i, pCommand);
            pCommand->Print(stream);
            stream.Printf("\n");
        }
    }
    {
        stream.Printf("Redo list: %d commands\n", GetNumberOfCommandsToRedo());
        for ( intn i = 0; i < GetNumberOfCommandsToRedo(); i++ ) {
            CEditCommand* pCommand = GetRedoCommand(i);
            stream.Printf(" %2d 0x%08x ", i, pCommand);
            pCommand->Print(stream);
            stream.Printf("\n");
        }
    }
}
#endif

void CEditCommandLog::BeginBatchChanges(CEditCommandGroup* pBatch) {
    m_batch.Push(pBatch);
}

void CEditCommandLog::EndBatchChanges() {
    if(m_batch.IsEmpty()) {
        XP_ASSERT(FALSE);
    }
    else {
        CEditCommandGroup* pBatch = m_batch.Pop();
        if ( pBatch->GetNumberOfCommands() > 0 ){
            if ( ! m_batch.IsEmpty() ) {
                m_batch.Top()->AdoptAndDo(pBatch);
            }
            else {
                InternalAdoptAndDo(pBatch);
            }
        }
        else {
            delete pBatch;
        }
    }
}

// CEditDataSaver

CEditDataSaver::CEditDataSaver(CEditBuffer* pBuffer){
    m_pEditBuffer = pBuffer;
    m_bModifiedTextHasBeenSaved = FALSE;
#ifdef DEBUG
    m_bDoState = 0;
#endif
}

CEditDataSaver::~CEditDataSaver(){
}

void CEditDataSaver::DoBegin(CPersistentEditSelection& original){
#ifdef DEBUG
    XP_ASSERT(m_bDoState == 0);
    m_bDoState++;
#endif
    m_original = original;
    m_pEditBuffer->GetSelection(m_originalDocument);
    CEditSelection selection =
        m_pEditBuffer->PersistentToEphemeral(m_original);
    selection.ExpandToNotCrossStructures();
    m_expandedOriginal = m_pEditBuffer->EphemeralToPersistent(selection);
    m_pEditBuffer->CopyEditText(m_expandedOriginal, m_originalText);
}

void CEditDataSaver::DoEnd(CPersistentEditSelection& modified){
#ifdef DEBUG
    XP_ASSERT(m_bDoState == 1);
    m_bDoState++;
#endif
    m_pEditBuffer->GetSelection(m_modifiedDocument);
    m_expandedModified = m_expandedOriginal;
    m_expandedModified.Map(m_original, modified);
}

void CEditDataSaver::Undo(){
#ifdef DEBUG
    XP_ASSERT(m_bDoState == 2);
    m_bDoState++;
#endif
    m_pEditBuffer->SetSelection(m_expandedModified);
    if ( ! m_bModifiedTextHasBeenSaved ) {
        m_pEditBuffer->CutEditText(m_modifiedText);
        m_bModifiedTextHasBeenSaved = TRUE;
    }
    else {
        m_pEditBuffer->DeleteSelection();
    }
    m_pEditBuffer->PasteEditText(m_originalText);
    m_pEditBuffer->SetSelection(m_originalDocument);
}

void CEditDataSaver::Redo(){
#ifdef DEBUG
    XP_ASSERT(m_bDoState == 3);
    m_bDoState--;
#endif
    m_pEditBuffer->SetSelection(m_expandedOriginal);
    m_pEditBuffer->DeleteSelection();
    m_pEditBuffer->PasteEditText(m_modifiedText);
    m_pEditBuffer->SetSelection(m_modifiedDocument);
}

// CInsertTableCommand
CInsertTableCommand::CInsertTableCommand(CEditBuffer* buffer, CEditTableElement* pTable, intn id)
    : CEditCommand(buffer, id)
{
    GetEditBuffer()->GetSelection(m_originalSelection);
    if( GetEditBuffer()->IsSelected() ){
        GetEditBuffer()->ClearSelection();
    }
    m_helper = new CPasteCommand(buffer, id, FALSE);
    GetEditBuffer()->InsertNonLeaf(pTable);
    m_helper->Do();
    CEditSelection selection = GetEditBuffer()->ComputeCursor(pTable, 0, 0);
    GetEditBuffer()->SetSelection(selection);
}

CInsertTableCommand::~CInsertTableCommand()
{
    delete m_helper;
}

void CInsertTableCommand::Do() {
    // All done in constructor
}

void CInsertTableCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    m_helper->Undo();
    GetEditBuffer()->SetSelection(m_originalSelection);
}

void CInsertTableCommand::Redo() {
    GetEditBuffer()->SetSelection(m_originalSelection);
    m_helper->Redo();
    GetEditBuffer()->SetSelection(m_changedSelection);
}

// CDeleteTableCommand
CDeleteTableCommand::CDeleteTableCommand(CEditBuffer* buffer, intn id)
    : CEditCommand(buffer, id)
{
	m_pTable = NULL;
}

CDeleteTableCommand::~CDeleteTableCommand()
{
    delete m_pTable;
}

void CDeleteTableCommand::Do() {
    GetEditBuffer()->GetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    m_pTable = ip.m_pElement->GetTableIgnoreSubdoc();
	if ( m_pTable ) {
		CEditElement* pRefreshStart = m_pTable->GetFirstMostChild()->PreviousLeaf();
		CEditInsertPoint replacePoint(m_pTable->GetLastMostChild()->NextLeaf(), 0);
		GetEditBuffer()->SetInsertPoint(replacePoint);
		m_pTable->Unlink();
		m_replacePoint = GetEditBuffer()->EphemeralToPersistent(replacePoint);
		GetEditBuffer()->Relayout(pRefreshStart, 0, replacePoint.m_pElement);
	}
}

void CDeleteTableCommand::Undo() {
	if ( m_pTable ) {
		CEditInsertPoint replacePoint = 
			GetEditBuffer()->PersistentToEphemeral(m_replacePoint);
		m_pTable->InsertBefore(replacePoint.m_pElement->GetParent());
		m_pTable = NULL;
		GetEditBuffer()->Relayout(m_pTable, 0, replacePoint.m_pElement, RELAYOUT_NOCARET);
		GetEditBuffer()->SetSelection(m_originalSelection);
	}
}

void CDeleteTableCommand::Redo() {
    CDeleteTableCommand::Do();
}

// CInsertTableCaptionCommand
CInsertTableCaptionCommand::CInsertTableCaptionCommand(CEditBuffer* buffer,
  EDT_TableCaptionData* pData, intn id)
    : CEditCommand(buffer, id)
{
    m_pOldCaption = NULL;
    GetEditBuffer()->GetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ){
        CEditCaptionElement* pCaption = new CEditCaptionElement();
        pCaption->SetData(pData);
        pTable->SetCaption(pCaption);
        pTable->FinishedLoad(GetEditBuffer());
        // CLM: Don't move insert point if we have a selected cell
        //      (We use selection to indicate current cell in property dialogs)
        if( !GetEditBuffer()->IsSelected() &&
            ip.m_pElement->GetTableCellIgnoreSubdoc() != NULL ){
            // Put cursor at end of caption
            ip.m_pElement = pTable->GetCaption()->GetLastMostChild()->Leaf();
            ip.m_iPos = ip.m_pElement->GetLen();
            GetEditBuffer()->SetInsertPoint(ip);
        }
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

CInsertTableCaptionCommand::~CInsertTableCaptionCommand()
{
    delete m_pOldCaption;
}

void CInsertTableCaptionCommand::Do() {
    // All done in constructor
}

void CInsertTableCaptionCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ) {
        m_pOldCaption = pTable->GetCaption();
        m_pOldCaption->Unlink();
        pTable->FinishedLoad(GetEditBuffer());
        GetEditBuffer()->SetSelection(m_originalSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

void CInsertTableCaptionCommand::Redo() {
    GetEditBuffer()->SetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable) {
        pTable->SetCaption(m_pOldCaption);
        m_pOldCaption = NULL;
        pTable->FinishedLoad(GetEditBuffer());
        GetEditBuffer()->Relayout(pTable, 0);
        // Put cursor at end of caption
        ip.m_pElement = pTable->GetCaption()->GetLastMostChild()->Leaf();
        ip.m_iPos = ip.m_pElement->GetLen();
        GetEditBuffer()->SetInsertPoint(ip);
    }
}

// CDeleteTableCaptionCommand
CDeleteTableCaptionCommand::CDeleteTableCaptionCommand(CEditBuffer* buffer, intn id)
    : CEditCommand(buffer, id),
    m_pOldCaption(NULL)
{
}

CDeleteTableCaptionCommand::~CDeleteTableCaptionCommand()
{
    delete m_pOldCaption;
}

void CDeleteTableCaptionCommand::Do() {
    GetEditBuffer()->GetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ){
        m_pOldCaption = pTable->GetCaption();
        if ( m_pOldCaption ) {
            CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
//            intn row = pTableCell ? pTableCell->GetRowIndex() : 0;
//            intn column = pTableCell ? pTableCell->GetColumnIndex() : 0;
            m_pOldCaption->Unlink();
            pTable->FinishedLoad(GetEditBuffer());
            if( !pTableCell ){
                // Set cursor to someplace that still exists
                // CLM: Don't do this if we we were already in a cell
                //GetEditBuffer()->SyncCursor(pTable, column, row);
                GetEditBuffer()->SyncCursor(pTable, 0, 0);
            }
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

void CDeleteTableCaptionCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ) {
        pTable->SetCaption(m_pOldCaption);
        m_pOldCaption = NULL;
        pTable->FinishedLoad(GetEditBuffer());
        GetEditBuffer()->SetSelection(m_originalSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

void CDeleteTableCaptionCommand::Redo() {
    CDeleteTableCaptionCommand::Do();
}

// CInsertTableRowCommand
CInsertTableRowCommand::CInsertTableRowCommand(CEditBuffer* buffer,
  EDT_TableRowData* /* pData */, XP_Bool bAfterCurrentRow, intn number, intn id)
    : CEditCommand(buffer, id)
{
    m_number = number;
    GetEditBuffer()->GetSelection(m_originalSelection);
    if( GetEditBuffer()->IsSelected() ){
        GetEditBuffer()->ClearSelection();
    }
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
    if ( pTableCell) {
        CEditTableElement* pTable = pTableCell->GetTable();
        if ( pTable ){
            intn row = pTableCell->GetRowIndex();
            intn column = pTableCell->GetColumnIndex();
            m_row = row + (bAfterCurrentRow ? 1 : 0);
            pTable->InsertRows(m_row, number);
            pTable->FinishedLoad(GetEditBuffer());
            GetEditBuffer()->SyncCursor(pTable, column, m_row);
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

CInsertTableRowCommand::~CInsertTableRowCommand()
{
}

void CInsertTableRowCommand::Do() {
    // All done in constructor
}

void CInsertTableRowCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ) {
        pTable->DeleteRows(m_row, m_number);
        GetEditBuffer()->SetSelection(m_originalSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

void CInsertTableRowCommand::Redo() {
    GetEditBuffer()->SetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable) {
        pTable->InsertRows(m_row, m_number);
        pTable->FinishedLoad(GetEditBuffer());
        GetEditBuffer()->SetSelection(m_changedSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

// CDeleteTableRowCommand
CDeleteTableRowCommand::CDeleteTableRowCommand(CEditBuffer* buffer, intn rows, intn id)
    : CEditCommand(buffer, id),
    m_table(0,0)
{
	m_rows = rows;
}

CDeleteTableRowCommand::~CDeleteTableRowCommand()
{
}

void CDeleteTableRowCommand::Do() {
    GetEditBuffer()->GetSelection(m_originalSelection);
    Redo();
}

void CDeleteTableRowCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ) {
        pTable->InsertRows(m_row, m_rows, &m_table);
        if ( m_bDeletedWholeTable ) {
            pTable->DeleteRows(pTable->GetRows()-1,1); // Get rid of extra cell
        }
        GetEditBuffer()->Relayout(pTable, 0, 0, RELAYOUT_NOCARET);
        GetEditBuffer()->SetSelection(m_originalSelection);
    }
}

void CDeleteTableRowCommand::Redo() {
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
    if ( pTableCell ){
        CEditTableElement* pTable = pTableCell->GetTable();
        if ( pTable ){
            m_row = pTableCell->GetRowIndex();
            intn iColumn = pTableCell->GetColumnIndex();
            m_bDeletedWholeTable = m_row == 0 && m_rows >= pTable->GetRows();
            pTable->DeleteRows(m_row, m_rows, &m_table);
            pTable->FinishedLoad(GetEditBuffer());
            // Set cursor to someplace that still exists
            GetEditBuffer()->SyncCursor(pTable, iColumn, m_row);
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

// CInsertTableColumnCommand
CInsertTableColumnCommand::CInsertTableColumnCommand(CEditBuffer* buffer,
  EDT_TableCellData* /* pData */, XP_Bool bAfterCurrentCell, intn number, intn id)
    : CEditCommand(buffer, id)
{
    m_number = number;
    GetEditBuffer()->GetSelection(m_originalSelection);
    if( GetEditBuffer()->IsSelected() ){
        GetEditBuffer()->ClearSelection();
    }
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
    if ( pTableCell) {
        CEditTableElement* pTable = pTableCell->GetTable();
        if ( pTable ){
            intn row = pTableCell->GetRowIndex();
            intn column = pTableCell->GetColumnIndex();
            m_column = column + (bAfterCurrentCell ? 1 : 0);
            pTable->InsertColumns(m_column, number);
            pTable->FinishedLoad(GetEditBuffer());
            GetEditBuffer()->SyncCursor(pTable, m_column, row);
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

CInsertTableColumnCommand::~CInsertTableColumnCommand()
{
}

void CInsertTableColumnCommand::Do() {
    // All done in constructor
}

void CInsertTableColumnCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ) {
        pTable->DeleteColumns(m_column, m_number);
        GetEditBuffer()->SetSelection(m_originalSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

void CInsertTableColumnCommand::Redo() {
    GetEditBuffer()->SetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable) {
        pTable->InsertColumns(m_column, m_number);
        pTable->FinishedLoad(GetEditBuffer());
        GetEditBuffer()->SetSelection(m_changedSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

// CDeleteTableColumnCommand
CDeleteTableColumnCommand::CDeleteTableColumnCommand(CEditBuffer* buffer, intn columns, intn id)
    : CEditCommand(buffer, id),
    m_table(0,0)
{
	m_columns = columns;
}

CDeleteTableColumnCommand::~CDeleteTableColumnCommand()
{
}

void CDeleteTableColumnCommand::Do() {
    GetEditBuffer()->GetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
    if ( pTableCell ){
        CEditTableElement* pTable = pTableCell->GetTable();
        if ( pTable ){
            intn iRow = pTableCell->GetRowIndex();
            m_column = pTableCell->GetColumnIndex();
            m_bDeletedWholeTable = m_column == 0 && m_columns >= pTable->GetColumns();
            pTable->DeleteColumns(m_column, m_columns, &m_table);
            pTable->FinishedLoad(GetEditBuffer());
            // Set cursor to someplace that still exists
            GetEditBuffer()->SyncCursor(pTable, m_column, iRow);
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

void CDeleteTableColumnCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable ) {
        pTable->InsertColumns(m_column, m_columns, &m_table);
         if ( m_bDeletedWholeTable ) {
            pTable->DeleteColumns(pTable->GetColumns()-1,1); // Get rid of extra cell
        }
        GetEditBuffer()->Relayout(pTable, 0, 0, RELAYOUT_NOCARET);
        GetEditBuffer()->SetSelection(m_originalSelection);
    }
}

void CDeleteTableColumnCommand::Redo() {
    CDeleteTableColumnCommand::Do();
}

// CInsertTableCellCommand
CInsertTableCellCommand::CInsertTableCellCommand(CEditBuffer* buffer,
  XP_Bool bAfterCurrentCell, intn number, intn id)
    : CEditCommand(buffer, id)
{
    m_number = number;
    GetEditBuffer()->GetSelection(m_originalSelection);
    if( GetEditBuffer()->IsSelected() ){
        GetEditBuffer()->ClearSelection();
    }
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
    if ( pTableCell) {
        CEditTableRowElement* pTableRow = pTableCell->GetTableRow();
        CEditTableElement* pTable = pTableCell->GetTable();
        if ( pTable && pTableRow ){
            intn row = pTableCell->GetRowIndex();
            intn column = pTableCell->GetColumnIndex();
            m_column = column + (bAfterCurrentCell ? 1 : 0);
            pTableRow->InsertCells(m_column, number);
            pTable->FinishedLoad(GetEditBuffer());
            GetEditBuffer()->SyncCursor(pTable, m_column, row);
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

CInsertTableCellCommand::~CInsertTableCellCommand()
{
}

void CInsertTableCellCommand::Do() {
    // All done in constructor
}

void CInsertTableCellCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableRowElement* pTableRow = ip.m_pElement->GetTableRowIgnoreSubdoc();
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable && pTableRow ) {
        pTableRow->DeleteCells(m_column, m_number);
        GetEditBuffer()->SetSelection(m_originalSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

void CInsertTableCellCommand::Redo() {
    GetEditBuffer()->SetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    CEditTableRowElement* pTableRow = ip.m_pElement->GetTableRowIgnoreSubdoc();
    if ( pTable && pTableRow) {
        pTableRow->InsertCells(m_column, m_number);
        pTable->FinishedLoad(GetEditBuffer());
        GetEditBuffer()->SetSelection(m_changedSelection);
        GetEditBuffer()->Relayout(pTable, 0);
    }
}

// CDeleteTableCellCommand
CDeleteTableCellCommand::CDeleteTableCellCommand(CEditBuffer* buffer, intn columns, intn id)
    : CEditCommand(buffer, id)
{
	m_columns = columns;
}

CDeleteTableCellCommand::~CDeleteTableCellCommand()
{
}

void CDeleteTableCellCommand::Do() {
    GetEditBuffer()->GetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
    if ( pTableCell ){
        CEditTableRowElement* pTableRow = pTableCell->GetTableRowIgnoreSubdoc();
        CEditTableElement* pTable = pTableCell->GetTableIgnoreSubdoc();
        if ( pTable && pTableRow ){
            intn iRow = pTableCell->GetRowIndex();
            m_column = pTableCell->GetColumnIndex();
            m_bDeletedWholeTable = m_column == 0 && m_columns >= pTableRow->GetCells();
            pTableRow->DeleteCells(m_column, m_columns, &m_tableRow);
            pTable->FinishedLoad(GetEditBuffer());
            // Set cursor to someplace that still exists
            GetEditBuffer()->SyncCursor(pTable, m_column, iRow);
            GetEditBuffer()->Relayout(pTable, 0);
        }
    }
}

void CDeleteTableCellCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetTableInsertPoint(ip);
    CEditTableRowElement* pTableRow = ip.m_pElement->GetTableRowIgnoreSubdoc();
    CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
    if ( pTable && pTableRow ) {
        pTableRow->InsertCells(m_column, m_columns, &m_tableRow);
         if ( m_bDeletedWholeTable ) {
            pTableRow->DeleteCells(pTableRow->GetCells()-1,1); // Get rid of extra cell
        }
        GetEditBuffer()->Relayout(pTable, 0, 0, RELAYOUT_NOCARET);
        GetEditBuffer()->SetSelection(m_originalSelection);
    }
}

void CDeleteTableCellCommand::Redo() {
    CDeleteTableCellCommand::Do();
}

// CInsertMultiColumnCommand
CInsertMultiColumnCommand::CInsertMultiColumnCommand(CEditBuffer* buffer, CEditMultiColumnElement* pMultiColumn, intn id)
    : CEditCommand(buffer, id)
{
    GetEditBuffer()->GetSelection(m_originalSelection);
    if( GetEditBuffer()->IsSelected() ){
        GetEditBuffer()->ClearSelection();
    }
    m_helper = new CPasteCommand(buffer, id, FALSE);
    GetEditBuffer()->InsertNonLeaf(pMultiColumn);
    m_helper->Do();
    GetEditBuffer()->SyncCursor(pMultiColumn);
}

CInsertMultiColumnCommand::~CInsertMultiColumnCommand()
{
    delete m_helper;
}

void CInsertMultiColumnCommand::Do() {
    // All done in constructor
}

void CInsertMultiColumnCommand::Undo() {
    GetEditBuffer()->GetSelection(m_changedSelection);
    m_helper->Undo();
    GetEditBuffer()->SetSelection(m_originalSelection);
}

void CInsertMultiColumnCommand::Redo() {
    GetEditBuffer()->SetSelection(m_originalSelection);
    m_helper->Redo();
    GetEditBuffer()->SetSelection(m_changedSelection);
}

// CDeleteMultiColumnCommand
CDeleteMultiColumnCommand::CDeleteMultiColumnCommand(CEditBuffer* buffer, intn id)
    : CEditCommand(buffer, id)
{
	m_pMultiColumn = NULL;
}

CDeleteMultiColumnCommand::~CDeleteMultiColumnCommand()
{
    delete m_pMultiColumn;
}

void CDeleteMultiColumnCommand::Do() {
    GetEditBuffer()->GetSelection(m_originalSelection);
    CEditInsertPoint ip;
    GetEditBuffer()->GetInsertPoint(ip);
    m_pMultiColumn = ip.m_pElement->GetMultiColumnIgnoreSubdoc();
	if ( m_pMultiColumn ) {
		CEditElement* pRefreshStart = m_pMultiColumn->GetFirstMostChild()->PreviousLeaf();
		CEditInsertPoint replacePoint(m_pMultiColumn->GetLastMostChild()->NextLeaf(), 0);
		GetEditBuffer()->SetInsertPoint(replacePoint);
		m_pMultiColumn->Unlink();
		m_replacePoint = GetEditBuffer()->EphemeralToPersistent(replacePoint);
		GetEditBuffer()->Relayout(pRefreshStart, 0, replacePoint.m_pElement);
	}
}

void CDeleteMultiColumnCommand::Undo() {
	if ( m_pMultiColumn ) {
		CEditInsertPoint replacePoint = 
			GetEditBuffer()->PersistentToEphemeral(m_replacePoint);
		m_pMultiColumn->InsertBefore(replacePoint.m_pElement->GetParent());
		m_pMultiColumn = NULL;
		GetEditBuffer()->SetSelection(m_originalSelection);
		GetEditBuffer()->Relayout(m_pMultiColumn, 0, replacePoint.m_pElement);
	}
}

void CDeleteMultiColumnCommand::Redo() {
    CDeleteMultiColumnCommand::Do();
}

// CPasteCommand

CPasteCommand::CPasteCommand(CEditBuffer* buffer, intn id, XP_Bool bClearOriginal, XP_Bool bPredictiveEnd)
    : CEditCommand( buffer, id),
     m_saver(buffer),
     m_bClearOriginal(bClearOriginal),
     m_bPredictiveEnd(bPredictiveEnd)
{
    CPersistentEditSelection selection;
    if ( m_bClearOriginal ) {
        selection = GetEditBuffer()->GetEffectiveDeleteSelection();
        m_saver.DoBegin(selection);
        // We have to delete the old text here because
        // otherwise we aren't able to figure out the extent of the
        // paste.
        if ( m_bClearOriginal && ! selection.IsInsertPoint() ) {
            GetEditBuffer()->DeleteSelection();
        }
        // remember what is pasted.
        GetEditBuffer()->GetInsertPoint(m_selection.m_start);
    }
    else {
        GetEditBuffer()->GetInsertPoint(m_selection.m_start);
        m_selection.m_end = m_selection.m_start;
        m_saver.DoBegin(m_selection);
    }
}

CPasteCommand::~CPasteCommand()
{
}

void CPasteCommand::Do()
{
    if ( ! m_bPredictiveEnd ){
        GetEditBuffer()->GetInsertPoint(m_selection.m_end);
    }
    else {
        // Assume the item is one unit long.
        m_selection.m_end.m_index = m_selection.m_start.m_index + 1;
    }
    m_saver.DoEnd(m_selection);
}

void CPasteCommand::Undo()
{
    m_saver.Undo();
}

void CPasteCommand::Redo()
{
    m_saver.Redo();
}

#if 0
// CCutCommand

CCutCommand::CCutCommand(CEditBuffer* buffer)
    : CEditTextCommand( buffer, kCutCommandID)
{
}

CCutCommand::~CCutCommand()
{
}

void CCutCommand::Do()
{
    // The cut happens externally, so nothing to do.
}

void CCutCommand::Undo()
{
    GetEditBuffer()->PasteEditText(m_oldText);
    RestoreSelection();
}

void CCutCommand::Redo()
{
    // Fake cut -- leave clipboard alone.
    RestoreSelection();
    GetEditBuffer()->DeleteSelection();
}
#endif

// CAddTextCommand

CAddTextCommand::CAddTextCommand(CEditBuffer* buffer, int cmdNo)
    : CEditCommand(buffer, cmdNo),
      m_saver(buffer)
{
    m_bNeverUndone = TRUE;
    m_bDeleteSpace = FALSE;
}

CAddTextCommand::~CAddTextCommand(){
}


void CAddTextCommand::Do(){
    GetEditBuffer()->GetSelection(m_addedText);
}

void CAddTextCommand::Undo(){
    // The m_saver is being used to undo, rather than to do.
    // That's why we "do" and "redo" here.
    if ( m_bNeverUndone ){
        m_bNeverUndone = FALSE;
        // Cut all the text we added.
        m_saver.DoBegin(m_addedText);
        GetEditBuffer()->SetSelection(m_addedText);
        GetEditBuffer()->DeleteSelection();
        CPersistentEditSelection selection;
        GetEditBuffer()->GetSelection(selection);
        m_saver.DoEnd(selection);
    }
    else {
        m_saver.Redo();
    }
    if ( m_bDeleteSpace ) {
        CPersistentEditSelection selection;
        GetEditBuffer()->GetSelection(selection);
        GetEditBuffer()->InsertChar(' ', FALSE);
        GetEditBuffer()->SetSelection(selection);
    }
}

void CAddTextCommand::Redo(){
    if ( m_bDeleteSpace ) {
        CPersistentEditSelection selection;
        GetEditBuffer()->GetSelection(selection);
        GetEditBuffer()->DeleteChar(TRUE, FALSE);
        GetEditBuffer()->SetSelection(selection);
    }
    m_saver.Undo();
}

void CAddTextCommand::AddCharStart(){
}

void CAddTextCommand::AddCharDeleteSpace(){
    m_bDeleteSpace = TRUE;
}

void CAddTextCommand::AddCharEnd(){
    CPersistentEditSelection current;
    GetEditBuffer()->GetSelection(current);
    m_addedText.m_end = current.m_end;
}

// CTypingCommand

CTypingCommand::CTypingCommand(CEditBuffer* pEditBuffer, int cmdNo)
	: CEditCommand(pEditBuffer, cmdNo),
      m_commands(pEditBuffer, cmdNo)
{
    m_pAdder = NULL;
    m_pDeleter = NULL;
}

CTypingCommand::~CTypingCommand()
{
	GetEditBuffer()->TypingDeleted(this);
}

void CTypingCommand::Do(){
}

void CTypingCommand::Undo()
{
    m_commands.Undo();
}

void CTypingCommand::Redo()
{
    m_commands.Redo();
}

void CTypingCommand::AddCharStart() {
    if ( m_pDeleter ){
        XP_ASSERT(FALSE);
        DeleteEnd(); // bug
    }
    if ( ! m_pAdder ){
        m_pAdder = new CAddTextCommand(GetEditBuffer());
        m_commands.AdoptAndDo(m_pAdder);
    }
    m_pAdder->AddCharStart();
}


void CTypingCommand::AddCharDeleteSpace() {
    XP_ASSERT(m_pAdder);
    if ( m_pAdder ){
        m_pAdder->AddCharDeleteSpace();
    }
}

void CTypingCommand::AddCharEnd() {
    XP_ASSERT(m_pAdder);
    if ( m_pAdder ){
        m_pAdder->AddCharEnd();
    }
}

#ifdef DEBUG
void CTypingCommand::Print(IStreamOut& stream) {
    CEditCommand::Print(stream);
    stream.Printf(" m_pAdder 0x%08x ", m_pAdder);
    stream.Printf(" m_pDeleter 0x%08x ", m_pDeleter);
    stream.Printf(" m_commands: ");
    m_commands.Print(stream);
}
#endif

void CTypingCommand::DeleteStart(CEditSelection& selection){
    if ( m_pDeleter ){
        XP_ASSERT(FALSE);
        DeleteEnd();
    }
    if ( m_pAdder ){
        m_pAdder = NULL; // Ends adder
    }
    CPersistentEditSelection persistentSelection =
        GetEditBuffer()->EphemeralToPersistent(selection);
    m_pDeleter = new CChangeAttributesCommand(GetEditBuffer(), persistentSelection, kDeleteTextCommandID);
}

void CTypingCommand::DeleteEnd(){
    XP_ASSERT(!m_pAdder);
    XP_ASSERT(m_pDeleter);
    if ( m_pDeleter ){
        m_commands.AdoptAndDo(m_pDeleter);
        m_pDeleter = NULL;
    }
}

// CEditCommandGroup

CEditCommandGroup::CEditCommandGroup(CEditBuffer* pEditBuffer, int id)
    : CEditCommand( pEditBuffer, id){
}

CEditCommandGroup::~CEditCommandGroup(){
    for ( int i = 0; i < m_commands.Size(); i++ ) {
        CEditCommand* item = m_commands[i];
        delete item;
    }
}

void CEditCommandGroup::AdoptAndDo(CEditCommand* pCommand){
    pCommand->Do();
    m_commands.Add(pCommand);
}

void CEditCommandGroup::Undo(){
    for ( int i = m_commands.Size() - 1; i >= 0 ; i-- ) {
        CEditCommand* item = m_commands[i];
        item->Undo();
    }
}

void CEditCommandGroup::Redo(){
    for ( int i = 0; i < m_commands.Size(); i++ ) {
        CEditCommand* item = m_commands[i];
        item->Redo();
    }
}

intn CEditCommandGroup::GetNumberOfCommands(){
    return m_commands.Size();
}

#ifdef DEBUG
void CEditCommandGroup::Print(IStreamOut& stream){
    CEditCommand::Print(stream);
    stream.Printf(" %d commands\n", m_commands.Size());
    for ( int i = 0; i < m_commands.Size(); i++ ) {
        CEditCommand* item = m_commands[i];
        stream.Printf("    [%d] 0x%08x ", i, item);
        item->Print(stream);
        stream.Printf("\n");
    }
}
#endif

// CChangeAttributesCommand

CChangeAttributesCommand::CChangeAttributesCommand(CEditBuffer* pEditBuffer, intn id)
    : CEditCommand(pEditBuffer, id),
      m_saver(pEditBuffer)
{
    CEditSelection selection;
    GetEditBuffer()->GetSelection(selection);
    selection.ExcludeLastDocumentContainerEnd();
    selection.ExpandToIncludeFragileSpaces();
    CPersistentEditSelection persel = GetEditBuffer()->EphemeralToPersistent(selection);
    m_saver.DoBegin(persel);
}

CChangeAttributesCommand::CChangeAttributesCommand(CEditBuffer* pEditBuffer, CPersistentEditSelection& selection, intn id)
    : CEditCommand(pEditBuffer, id),
      m_saver(pEditBuffer)
{
    m_saver.DoBegin(selection);
}

CChangeAttributesCommand::~CChangeAttributesCommand() {
}

void CChangeAttributesCommand::Do(){
    CEditSelection selection;
    GetEditBuffer()->GetSelection(selection);
    selection.ExcludeLastDocumentContainerEnd();
    selection.ExpandToIncludeFragileSpaces();
    CPersistentEditSelection persel = GetEditBuffer()->EphemeralToPersistent(selection);
    m_saver.DoEnd(persel);
}

void CChangeAttributesCommand::Undo(){
    m_saver.Undo();
}

void CChangeAttributesCommand::Redo(){
    m_saver.Redo();
}

// CChangeIndentCommand

CChangeIndentCommand::CChangeIndentCommand(CEditBuffer* pEditBuffer, intn id)
    : CEditCommand(pEditBuffer, id),
      m_saver(pEditBuffer)
{
    CEditSelection selection;
    GetEditBuffer()->GetSelection(selection);
    selection.ExpandToEncloseWholeContainers();
    // That made it not cross any list boundaries, but the result may cross
    // table cell boundaries. So call ExpandToBeCutable to get over any
    // table cell boundaries.
    selection.ExpandToBeCutable();
    XP_ASSERT(!selection.CrossesSubDocBoundary());
    m_selection = GetEditBuffer()->EphemeralToPersistent(selection);
    m_saver.DoBegin(m_selection);
}

CChangeIndentCommand::~CChangeIndentCommand(){
}

void CChangeIndentCommand::Do(){
    m_saver.DoEnd(m_selection);
}

void CChangeIndentCommand::Undo(){
    m_saver.Undo();
}

void CChangeIndentCommand::Redo(){
    m_saver.Redo();
}

// CSetListDataCommand
CSetListDataCommand::CSetListDataCommand(CEditBuffer* pBuffer, EDT_ListData& listData, intn id)
    : CEditCommand(pBuffer, id)
{
    m_newData = listData;
    m_pOldData = NULL;
}

CSetListDataCommand::~CSetListDataCommand(){
    if ( m_pOldData ){
        CEditListElement::FreeData( m_pOldData );
    }
}

void CSetListDataCommand::Do(){
    m_pOldData = GetEditBuffer()->GetListData();
    XP_ASSERT(m_pOldData);
    GetEditBuffer()->SetListData(&m_newData);
}

void CSetListDataCommand::Undo(){
    if ( m_pOldData ){
        GetEditBuffer()->SetListData(m_pOldData);
    }
}

void CSetListDataCommand::Redo(){
    GetEditBuffer()->SetListData(&m_newData);
}


// CChangeParagraphCommand
CChangeParagraphCommand::CChangeParagraphCommand(CEditBuffer* buffer, intn id)
    : CChangeAttributesCommand(buffer, id) {
    CEditContainerElement* container = FindContainer(*GetOriginalDocumentSelection());
    m_tag = container->GetType();
    m_align = container->GetAlignment();
}

CChangeParagraphCommand::~CChangeParagraphCommand() {
}

void CChangeParagraphCommand::Undo() {
    SwapAttributes();
    CChangeAttributesCommand::Undo();
}

void CChangeParagraphCommand::Redo() {
    CChangeAttributesCommand::Redo();
    SwapAttributes();
}

void CChangeParagraphCommand::SwapAttributes() {
    CPersistentEditSelection selection;
    GetEditBuffer()->GetSelection(selection);
    CEditContainerElement* pContainer = FindContainer(selection);
    if ( pContainer ) {
        TagType tempTag = pContainer->GetType();
        pContainer->SetType(m_tag);
        m_tag = tempTag;
        ED_Alignment tempAlign = pContainer->GetAlignment();
        pContainer->SetAlignment(m_align);
        m_align = tempAlign;
        GetEditBuffer()->Relayout( pContainer, 0, 0);
    }
}

CEditContainerElement* CChangeParagraphCommand::FindContainer(CPersistentEditSelection& selection) {
    CEditInsertPoint insertPoint = GetEditBuffer()->PersistentToEphemeral(selection.m_end);
    return insertPoint.m_pElement->FindContainer();
}

// CChangePageDataCommand

CChangePageDataCommand::CChangePageDataCommand(CEditBuffer* buffer, intn id)
    : CEditCommand(buffer, id)
{
    m_oldData = GetEditBuffer()->GetPageData();
    m_newData = NULL;
}

CChangePageDataCommand::~CChangePageDataCommand(){
    if ( m_oldData ) {
        GetEditBuffer()->FreePageData(m_oldData);
    }
    if ( m_newData ) {
        GetEditBuffer()->FreePageData(m_newData);
    }
}

void CChangePageDataCommand::Undo(){
    if ( ! m_newData ) {
        m_newData = GetEditBuffer()->GetPageData();
    }
    GetEditBuffer()->SetPageData(m_oldData);
}

void CChangePageDataCommand::Redo(){
    GetEditBuffer()->SetPageData(m_newData);
}

// CSetMetaDataCommand

CSetMetaDataCommand::CSetMetaDataCommand(CEditBuffer* buffer, EDT_MetaData *pMetaData, XP_Bool bDelete, intn id)
    : CEditCommand(buffer, id){
    m_bDelete = bDelete;
    int existingIndex = GetEditBuffer()->FindMetaData( pMetaData);
    m_bNewItem = existingIndex < 0;
    if ( m_bNewItem ) {
        m_pOldData = 0;
    }
    else {
        m_pOldData = GetEditBuffer()->GetMetaData(existingIndex);
    }
    if ( m_bDelete ) {
        GetEditBuffer()->DeleteMetaData(pMetaData);
        m_pNewData = 0;
    }
    else {
        GetEditBuffer()->SetMetaData(pMetaData);
        m_pNewData = GetEditBuffer()->GetMetaData(GetEditBuffer()->FindMetaData(pMetaData));
    }
}

CSetMetaDataCommand::~CSetMetaDataCommand(){
    if ( m_pOldData ) {
        GetEditBuffer()->FreeMetaData(m_pOldData);
    }
    if ( m_pNewData ) {
        GetEditBuffer()->FreeMetaData(m_pNewData);
    }
}

void CSetMetaDataCommand::Undo(){
    if ( m_bNewItem  ) {
        if ( m_pNewData ) {
            GetEditBuffer()->DeleteMetaData(m_pNewData);
        }
    }
    else {
        if ( m_pOldData ) {
            GetEditBuffer()->SetMetaData(m_pOldData);
        }
    }
}

void CSetMetaDataCommand::Redo(){
    if ( m_bDelete ) {
        if ( m_pOldData ) {
            GetEditBuffer()->DeleteMetaData(m_pOldData);
        }
    }
    else {
        if ( m_pNewData ) {
            GetEditBuffer()->SetMetaData(m_pNewData);
        }
    }
}

// CSetTableDataCommand

CSetTableDataCommand::CSetTableDataCommand(CEditBuffer* buffer, EDT_TableData* pTableData, intn id)
    : CEditCommand(buffer, id){
    m_pOldData = GetEditBuffer()->GetTableData();
    GetEditBuffer()->SetTableData(pTableData);
    m_pNewData = GetEditBuffer()->GetTableData();
}

CSetTableDataCommand::~CSetTableDataCommand(){
    CEditTableElement::FreeData(m_pOldData);
    CEditTableElement::FreeData(m_pNewData);
}

void CSetTableDataCommand::Undo(){
    GetEditBuffer()->SetTableData(m_pOldData);
}

void CSetTableDataCommand::Redo(){
    GetEditBuffer()->SetTableData(m_pNewData);
}

CSetTableCaptionDataCommand::CSetTableCaptionDataCommand(CEditBuffer* buffer, EDT_TableCaptionData* pTableData, intn id)
    : CEditCommand(buffer, id){
    m_pOldData = GetEditBuffer()->GetTableCaptionData();
    GetEditBuffer()->SetTableCaptionData(pTableData);
    m_pNewData = GetEditBuffer()->GetTableCaptionData();
}

CSetTableCaptionDataCommand::~CSetTableCaptionDataCommand(){
    CEditCaptionElement::FreeData(m_pOldData);
    CEditCaptionElement::FreeData(m_pNewData);
}

void CSetTableCaptionDataCommand::Undo(){
    GetEditBuffer()->SetTableCaptionData(m_pOldData);
}

void CSetTableCaptionDataCommand::Redo(){
    GetEditBuffer()->SetTableCaptionData(m_pNewData);
}

CSetTableRowDataCommand::CSetTableRowDataCommand(CEditBuffer* buffer, EDT_TableRowData* pTableData, intn id)
    : CEditCommand(buffer, id){
    m_pOldData = GetEditBuffer()->GetTableRowData();
    GetEditBuffer()->SetTableRowData(pTableData);
    m_pNewData = GetEditBuffer()->GetTableRowData();
}

CSetTableRowDataCommand::~CSetTableRowDataCommand(){
    CEditTableRowElement::FreeData(m_pOldData);
    CEditTableRowElement::FreeData(m_pNewData);
}

void CSetTableRowDataCommand::Undo(){
    GetEditBuffer()->SetTableRowData(m_pOldData);
}

void CSetTableRowDataCommand::Redo(){
    GetEditBuffer()->SetTableRowData(m_pNewData);
}

CSetTableCellDataCommand::CSetTableCellDataCommand(CEditBuffer* buffer, EDT_TableCellData* pTableData, intn id)
    : CEditCommand(buffer, id){
    m_pOldData = GetEditBuffer()->GetTableCellData();
    GetEditBuffer()->SetTableCellData(pTableData);
    m_pNewData = GetEditBuffer()->GetTableCellData();
}

CSetTableCellDataCommand::~CSetTableCellDataCommand(){
    CEditTableCellElement::FreeData(m_pOldData);
    CEditTableCellElement::FreeData(m_pNewData);
}

void CSetTableCellDataCommand::Undo(){
    GetEditBuffer()->SetTableCellData(m_pOldData);
}

void CSetTableCellDataCommand::Redo(){
    GetEditBuffer()->SetTableCellData(m_pNewData);
}

CSetMultiColumnDataCommand::CSetMultiColumnDataCommand(CEditBuffer* buffer, EDT_MultiColumnData* pMultiColumnData, intn id)
    : CEditCommand(buffer, id){
    m_pOldData = GetEditBuffer()->GetMultiColumnData();
    GetEditBuffer()->SetMultiColumnData(pMultiColumnData);
    m_pNewData = GetEditBuffer()->GetMultiColumnData();
}

CSetMultiColumnDataCommand::~CSetMultiColumnDataCommand(){
    CEditMultiColumnElement::FreeData(m_pOldData);
    CEditMultiColumnElement::FreeData(m_pNewData);
}

void CSetMultiColumnDataCommand::Undo(){
    GetEditBuffer()->SetMultiColumnData(m_pOldData);
}

void CSetMultiColumnDataCommand::Redo(){
    GetEditBuffer()->SetMultiColumnData(m_pNewData);
}

// CSetSelectionCommand

CSetSelectionCommand::CSetSelectionCommand(CEditBuffer* buffer, CEditSelection& selection, intn id)
    : CEditCommand(buffer, id){
    m_NewSelection = GetEditBuffer()->EphemeralToPersistent(selection);
}

CSetSelectionCommand::~CSetSelectionCommand(){
}

void CSetSelectionCommand::Do(){
    GetEditBuffer()->GetSelection(m_OldSelection);
    GetEditBuffer()->SetSelection(m_NewSelection);
}

void CSetSelectionCommand::Undo(){
    GetEditBuffer()->SetSelection(m_OldSelection);
}

void CSetSelectionCommand::Redo(){
    GetEditBuffer()->SetSelection(m_NewSelection);
}

#endif