home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / edtcmd.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  40.8 KB  |  1,538 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20. //
  21. //
  22. // Editor save stuff. LWT 06/01/95
  23. // this should be on the branch
  24. //
  25.  
  26. #ifdef EDITOR
  27.  
  28. #include "editor.h"
  29. #ifdef DEBUG
  30.  
  31. const char* kCommandNames[kCommandIDMax+1] = {
  32.     "Null",
  33.     "Typing", 
  34.     "AddText", 
  35.     "DeleteText", 
  36.     "Cut", 
  37.     "PasteText", 
  38.     "PasteHTML", 
  39.     "PasteHREF", 
  40.     "ChangeAttributes", 
  41.     "Indent", 
  42.     "ParagraphAlign", 
  43.     "MorphContainer", 
  44.     "InsertHorizRule", 
  45.     "SetHorizRuleData", 
  46.     "InsertImage", 
  47.     "SetImageData", 
  48.     "InsertBreak", 
  49.     "ChangePageData", 
  50.     "SetMetaData", 
  51.     "DeleteMetaData", 
  52.     "InsertTarget", 
  53.     "SetTargetData", 
  54.     "InsertUnknownTag", 
  55.     "SetUnknownTagData", 
  56.     "GroupOfChanges",                                           
  57.     "SetListData",
  58.     "InsertTable",
  59.     "DeleteTable",
  60.     "SetTableData",
  61.     "InsertTableCaption",
  62.     "SetTableCaptionData",
  63.     "DeleteTableCaption",
  64.     "InsertTableRow",
  65.     "SetTableRowData",
  66.     "DeleteTableRow",
  67.     "InsertTableColumn",
  68.     "SetTableCellData",
  69.     "DeleteTableColumn",
  70.     "InsertTableCell",
  71.     "DeleteTableCell",
  72.     "InsertMultiColumn",
  73.     "DeleteMultiColumn",
  74.     "SetMultiColumnData",
  75.     "SetSelection"
  76. };
  77.  
  78. #endif
  79.  
  80. CEditText::CEditText() { m_pChars = NULL; m_iLength = 0; m_iCount = 0; }
  81. CEditText::~CEditText() { Clear(); }
  82.  
  83. void CEditText::Clear() {
  84.     if ( m_pChars )
  85.         XP_HUGE_FREE(m_pChars);  // Tied to implementation of CStreamOutMemory
  86.     m_pChars = 0;
  87.     m_iCount = 0;
  88.     m_iLength = 0;
  89. }
  90.  
  91. char* CEditText::GetChars() { return m_pChars; }
  92. int32 CEditText::Length() { return m_iLength; }
  93. char** CEditText::GetPChars() { return &m_pChars; }
  94. int32* CEditText::GetPLength() { return &m_iLength; }
  95.  
  96. // CEditCommand
  97.  
  98. CEditCommand::CEditCommand(CEditBuffer* editBuffer, intn id)
  99. {
  100.     m_id = id;
  101.     m_editBuffer = editBuffer;
  102. }
  103.  
  104. CEditCommand::~CEditCommand()
  105. {
  106. }
  107.  
  108. void CEditCommand::Do()
  109. {
  110. }
  111.  
  112. void CEditCommand::Undo()
  113. {
  114. }
  115.  
  116. void CEditCommand::Redo()
  117. {
  118.     Do();
  119. }
  120.  
  121. intn CEditCommand::GetID()
  122. {
  123.     return m_id;
  124. }
  125.  
  126. #ifdef DEBUG
  127. void CEditCommand::Print(IStreamOut& stream) {
  128.     const char* name = "Unknown";
  129.     if ( m_id >= 0 && m_id <= kCommandIDMax ){
  130.         name = kCommandNames[m_id];
  131.     }
  132.     stream.Printf("%s(%ld) ", name, (long)m_id);
  133. }
  134. #endif
  135.  
  136. // CEditCommandLog
  137.  
  138. // We were having problems where we were entering a command while processing the undo of another command.
  139. // This would cause the command log to be trimmed, which would delete the undo log, including the
  140. // command that was being undone. This helps detect that situation.
  141.  
  142. #ifdef DEBUG
  143.  
  144. class CEditCommandLogRecursionCheckEntry {
  145. private:
  146.     XP_Bool m_bOldBusy;
  147.     CEditCommandLog* m_log;
  148. public:
  149.     CEditCommandLogRecursionCheckEntry(CEditCommandLog* log) {
  150.         m_log = log;
  151.         m_bOldBusy = m_log->m_bBusy;
  152.         XP_ASSERT(m_log->m_bBusy == FALSE);
  153.         m_log->m_bBusy = TRUE;
  154.     }
  155.     ~CEditCommandLogRecursionCheckEntry() {
  156.         m_log->m_bBusy = m_bOldBusy;
  157.     }
  158. };
  159.  
  160. #define DEBUG_RECURSION_CHECK CEditCommandLogRecursionCheckEntry debugRecursionCheckEntry(this);
  161. #else
  162. #define DEBUG_RECURSION_CHECK
  163. #endif
  164.  
  165. CGlobalHistoryGroup* CGlobalHistoryGroup::g_pGlobalHistoryGroup;
  166.  
  167. CGlobalHistoryGroup* CGlobalHistoryGroup::GetGlobalHistoryGroup(){
  168.     if ( g_pGlobalHistoryGroup == NULL ) {
  169.         g_pGlobalHistoryGroup = new CGlobalHistoryGroup();
  170.     }
  171.     return g_pGlobalHistoryGroup;
  172. }
  173.  
  174. CGlobalHistoryGroup::CGlobalHistoryGroup(){
  175.     m_pHead = NULL;
  176. }
  177.  
  178. CGlobalHistoryGroup::~CGlobalHistoryGroup(){
  179.     CEditCommandLog* pLog = m_pHead;
  180.     while(pLog){
  181.         CEditCommandLog* pCurrent = pLog;
  182.         pLog = pLog->m_pNext;
  183.         delete pCurrent;
  184.     }
  185.     m_pHead = NULL;
  186. }
  187.  
  188. XP_Bool CGlobalHistoryGroup::IsReload(CEditBuffer* pEditBuffer){
  189.     CEditCommandLog* pLog = m_pHead;
  190.     while(pLog){
  191.         if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
  192.             return TRUE;
  193.         }
  194.         pLog = pLog->m_pNext;
  195.     }
  196.     return FALSE;
  197. }
  198.  
  199. CEditCommandLog* CGlobalHistoryGroup::CreateLog(CEditBuffer* pEditBuffer){
  200.     CEditCommandLog* pLog = m_pHead;
  201.     while(pLog){
  202.         if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
  203.             pLog->m_bIgnoreDelete = FALSE;
  204.             pLog->m_pBuffer = pEditBuffer;
  205.             return pLog;
  206.         }
  207.         pLog = pLog->m_pNext;
  208.     }
  209.     pLog = new CEditCommandLog();
  210.     pLog->m_pContext = pEditBuffer->m_pContext;
  211.     pLog->m_pNext = m_pHead;
  212.     pLog->m_pBuffer = pEditBuffer;
  213.     m_pHead = pLog;
  214.     return pLog;
  215. }
  216.  
  217. CEditCommandLog* CGlobalHistoryGroup::GetLog(CEditBuffer* pEditBuffer){
  218.     CEditCommandLog* pLog = m_pHead;
  219.     while(pLog){
  220.         if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
  221.             return pLog;
  222.         }
  223.         pLog = pLog->m_pNext;
  224.     }
  225.     XP_ASSERT(FALSE);
  226.     return NULL;
  227. }
  228.  
  229. void CGlobalHistoryGroup::DeleteLog(CEditBuffer* pEditBuffer){
  230.     CEditCommandLog* pLog = m_pHead;
  231.     CEditCommandLog* pPrev = NULL;
  232.     while(pLog){
  233.         if ( pEditBuffer->m_pContext == pLog->m_pContext ) {
  234.             pLog->m_pBuffer = NULL;
  235.             if ( pLog->m_bIgnoreDelete ) {
  236.                 pLog->m_bIgnoreDelete = FALSE;
  237.             }
  238.             else {
  239.                 CEditCommandLog* pNext = pLog->m_pNext;
  240.                 delete pLog;
  241.                 if ( pPrev ) {
  242.                     pPrev->m_pNext = pNext;
  243.                 }
  244.                 else {
  245.                     m_pHead = pNext;
  246.                     if ( pNext == NULL ) {
  247.                         // Why yes, that is our this pointer....
  248.                         delete g_pGlobalHistoryGroup;
  249.                         g_pGlobalHistoryGroup = NULL;
  250.                     }
  251.                 }
  252.             }
  253.             return;
  254.         }
  255.         pPrev = pLog;
  256.         pLog = pLog->m_pNext;
  257.     }
  258.     XP_ASSERT(FALSE);
  259. }
  260.  
  261. void CGlobalHistoryGroup::IgnoreNextDeleteOf(CEditBuffer* pEditBuffer){
  262.     CEditCommandLog* pLog = GetLog(pEditBuffer);
  263.     if ( pLog ) {
  264.         XP_ASSERT(pLog->m_bIgnoreDelete == FALSE);
  265.         pLog->m_bIgnoreDelete = TRUE;
  266.     }
  267. }
  268.  
  269. CCommandState::CCommandState(){
  270.     m_commandID = kNullCommandID;
  271.     m_pState = NULL;
  272. }
  273.  
  274. CCommandState::~CCommandState(){
  275.     Flush();
  276. }
  277.  
  278. void CCommandState::SetID(intn commandID){
  279.     m_commandID = commandID;
  280. }
  281.  
  282. intn CCommandState::GetID(){
  283.     return m_commandID;
  284. }
  285.  
  286. void CCommandState::Record(CEditBuffer* pBufferToRecord){
  287.     Flush();
  288.     m_pState = pBufferToRecord->RecordState();
  289. }
  290.  
  291. void CCommandState::Restore(CEditBuffer* pBufferToRestore){
  292.   if (m_pState) {
  293.     pBufferToRestore->RestoreState(m_pState);
  294.   }
  295. }
  296.  
  297. void CCommandState::Flush(){
  298.   delete m_pState;
  299.   m_pState = NULL;
  300. }
  301.  
  302. #ifdef DEBUG
  303. void CCommandState::Print(IStreamOut& stream) {
  304.   if (m_pState) {
  305.     stream.Printf("id: %d, ", m_commandID);
  306.     m_pState->Print(stream);
  307.   }
  308. }
  309. #endif
  310.  
  311.  
  312. CEditCommandLog::CEditCommandLog()
  313. {
  314.     m_pBuffer = NULL;
  315.     m_pUndo = NULL;
  316.     m_pRedo = NULL;
  317.     m_iBatchLevel = 0;
  318.     m_pNext = NULL;
  319.     m_pContext = NULL;
  320.     m_bIgnoreDelete = FALSE;
  321. #ifdef DEBUG
  322.     m_bBusy = FALSE;
  323. #endif
  324.     m_state = 0;
  325.     m_version = 0;
  326.     m_storedVersion = 0;
  327.     m_highestVersion = 0;
  328. #ifdef EDITOR_JAVA
  329.     m_pPlugins = 0;
  330. #endif
  331.     m_pDocTempDir = NULL;
  332.     m_iDocTempFilenameNonce = 1;
  333.  
  334.  
  335. // If this is the first CEditBuffer created, delete any 
  336. // temporary document directories from the last time 
  337. // Communicator 4.0 was run.
  338. #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2)
  339.     // Don't do this for UNIX since multiple instances of Communicator
  340.     // may be running.
  341.  
  342.     // First instance of CEditCommandLog.
  343.     if (m_iDocTempDirNonce == 0) {
  344.       m_iDocTempDirNonce++; // First temp dir will start with "1".
  345.       char *pxpURL = GetAppTempDir();
  346.       if (pxpURL) {
  347.         // recursive delete.
  348.         edt_RemoveDirectoryR(pxpURL);
  349.         XP_FREE(pxpURL);
  350.       }
  351.       else {
  352.         XP_ASSERT(0);
  353.       }
  354.     }
  355. #endif
  356. }
  357.  
  358. CEditCommandLog::~CEditCommandLog()
  359. {
  360.     Trim();
  361. #ifdef EDITOR_JAVA
  362.     EditorPluginManager_delete(m_pPlugins);
  363.     m_pPlugins = 0;
  364. #endif
  365.  
  366.     // Clean up the temp files associated with this doc.
  367.     if (m_pDocTempDir) {
  368.       edt_RemoveDirectoryR(m_pDocTempDir);
  369.       XP_FREE(m_pDocTempDir);
  370.     }
  371.  
  372. }
  373.  
  374. void CEditCommandLog::StartTyping(intn typing){
  375.     InternalDo(typing);
  376. }
  377.  
  378. void CEditCommandLog::EndTyping(){
  379. }
  380.  
  381. void CEditCommandLog::AdoptAndDo(CEditCommand* pCommand)
  382. {
  383.     DEBUG_RECURSION_CHECK
  384.  
  385.     if ( m_iBatchLevel == 0 ){
  386.         InternalAdoptAndDo(pCommand);
  387.     }
  388.     else
  389.     {
  390.         pCommand->Do();
  391.         delete pCommand;
  392.     }
  393. }
  394.  
  395. void CEditCommandLog::InternalAdoptAndDo(CEditCommand* command)
  396. {
  397.     InternalDo(command->GetID());
  398.     command->Do();
  399.     delete command;
  400. }
  401.  
  402. void CEditCommandLog::InternalDo(intn id)
  403. {
  404.     if ( m_state == 1 ) {
  405.         // recovering from an undo/redo
  406.         return;
  407.     }
  408.     delete m_pUndo;
  409.     m_pUndo = new CCommandState();
  410.     m_pUndo->SetID(id);
  411.     m_pUndo->Record(m_pBuffer);
  412.  
  413.     if ( m_pRedo ) {
  414.         delete m_pRedo;
  415.         m_pRedo = NULL;
  416.     }
  417.     m_state = 0;
  418.  
  419.     // Set m_version to a version that has never been seen before.
  420.     m_version = ++m_highestVersion;
  421. }
  422.  
  423. void CEditCommandLog::Undo()
  424. {
  425.     DEBUG_RECURSION_CHECK
  426.     if ( !m_pUndo ) {
  427.         XP_TRACE(("Nothing to undo."));
  428.         return;
  429.     }
  430.     if ( m_pUndo )
  431.     {
  432.         FinishBatchCommands();
  433.         if ( ! m_pRedo ) {
  434.             m_pRedo = new CCommandState();
  435.             m_pRedo->SetID(m_pUndo->GetID());
  436.             m_pRedo->Record(m_pBuffer);
  437.         }
  438.         m_pUndo->Restore(m_pBuffer);
  439.         delete m_pUndo;
  440.         m_pUndo = NULL;
  441.     }
  442.     m_state = 1;
  443.     // CCommandState::Restore() will set m_version.
  444. }
  445.  
  446. void CEditCommandLog::Redo()
  447. {
  448.     DEBUG_RECURSION_CHECK
  449.     if ( !m_pRedo ) {
  450.         XP_TRACE(("Nothing to redo."));
  451.         return;
  452.     }
  453.     if ( m_pRedo )
  454.     {
  455.         FinishBatchCommands();
  456.         if ( ! m_pUndo ) {
  457.             m_pUndo = new CCommandState();
  458.             m_pUndo->SetID(m_pRedo->GetID());
  459.             m_pUndo->Record(m_pBuffer);
  460.         }
  461.         m_pRedo->Restore(m_pBuffer);
  462.         delete m_pRedo;
  463.         m_pRedo = NULL;
  464.     }
  465.     m_state = 1;
  466.     // CCommandState::Restore() will set m_version.
  467. }
  468.  
  469. void CEditCommandLog::FinishBatchCommands()
  470. {
  471.     if ( m_iBatchLevel > 0 ){
  472.         XP_ASSERT(FALSE);
  473.         m_iBatchLevel = 0;
  474.     }
  475. }
  476.  
  477. void CEditCommandLog::Trim()
  478. {
  479.     delete m_pRedo;
  480.     m_pRedo = NULL;
  481.     delete m_pUndo;
  482.     m_pUndo = NULL;
  483. }
  484.  
  485. XP_Bool CEditCommandLog::InReload(){
  486.     return m_state != 0;
  487. }
  488. void CEditCommandLog::SetInReload(XP_Bool bInReload){
  489.     m_state = bInReload;
  490. }
  491.  
  492. XP_Bool CEditCommandLog::IsDirty(){
  493.     return m_version != m_storedVersion;
  494. }
  495.  
  496. DocumentVersion CEditCommandLog::GetVersion(){
  497.     return m_version;
  498. }
  499.  
  500. void CEditCommandLog::SetVersion(DocumentVersion version) {
  501.   m_version = version;
  502. }
  503.  
  504. void CEditCommandLog::DocumentStored(){
  505.     m_storedVersion = m_version;
  506. }
  507.  
  508. DocumentVersion CEditCommandLog::GetStoredVersion(){
  509.     return m_storedVersion;
  510. }
  511.  
  512. intn CEditCommandLog::GetCommandHistoryLimit()
  513. {
  514.     return 1;
  515. }
  516.  
  517. void CEditCommandLog::SetCommandHistoryLimit(intn newLimit) {
  518.     if ( newLimit >= 0 ) {
  519.         Trim();
  520.     }
  521. }
  522.  
  523. intn CEditCommandLog::GetNumberOfCommandsToUndo()
  524. {
  525.     return m_pUndo ? 1 : 0;
  526. }
  527.  
  528. intn CEditCommandLog::GetNumberOfCommandsToRedo()
  529. {
  530.     return m_pRedo ? 1 : 0;
  531. }
  532.  
  533. // Returns NULL if out of range
  534. intn CEditCommandLog::GetUndoCommand(intn index)
  535. {
  536.     if ( m_pUndo == NULL || index < 0 || index >= GetNumberOfCommandsToUndo())
  537.         return kNullCommandID;
  538.     return m_pUndo->GetID();
  539. }
  540.  
  541. intn CEditCommandLog::GetRedoCommand(intn index)
  542. {
  543.     if ( m_pRedo == NULL || index < 0 || index >= GetNumberOfCommandsToRedo() )
  544.         return kNullCommandID;
  545.     return m_pRedo->GetID();
  546. }
  547.  
  548. #ifdef DEBUG
  549. void CEditCommandLog::Print(IStreamOut& stream) {
  550.     stream.Printf("state: %d\n", m_state);
  551.     stream.Printf("Undo list: %d commands\n", GetNumberOfCommandsToUndo());
  552.      if ( m_pUndo ) {
  553.         m_pUndo->Print(stream);
  554.      }
  555.    stream.Printf("Redo list: %d commands\n", GetNumberOfCommandsToRedo());
  556.      if ( m_pRedo ) {
  557.         m_pRedo->Print(stream);
  558.      }
  559. }
  560. #endif
  561.  
  562. void CEditCommandLog::BeginBatchChanges(intn id) {
  563.     if ( m_iBatchLevel < 0 ) {
  564.         XP_ASSERT(FALSE);
  565.         m_iBatchLevel = 0;
  566.     }
  567.     if ( m_iBatchLevel++ == 0 ) {
  568.         InternalDo(id);
  569.     }
  570. }
  571.  
  572. void CEditCommandLog::EndBatchChanges() {
  573.     if(m_iBatchLevel <= 0) {
  574.         XP_ASSERT(FALSE);
  575.         m_iBatchLevel = 0;
  576.     }
  577.     else {
  578.         m_iBatchLevel--;
  579.     }
  580. }
  581.  
  582. #ifdef EDITOR_JAVA
  583. EditorPluginManager CEditCommandLog::GetPlugins(){
  584.     if ( m_pPlugins == NULL ) {
  585.         m_pPlugins = EditorPluginManager_new(m_pContext);
  586.     }
  587.     return m_pPlugins;
  588. }
  589. #endif
  590.  
  591. // Returns xpURL, ends in slash.
  592. char *CEditCommandLog::GetAppTempDir() {
  593.   char *pTempRoot = XP_TempDirName();
  594.   char* pTempURL = XP_PlatformFileToURL(pTempRoot);
  595.   XP_FREEIF(pTempRoot);
  596.   if (!(pTempURL && *pTempURL)) {
  597.     XP_ASSERT(0);
  598.     return NULL;
  599.   }
  600.   // Make sure ends in slash.
  601.   if (pTempURL[XP_STRLEN(pTempURL)-1] != '/') {
  602.     StrAllocCat(pTempURL,"/");
  603.   }
  604.  
  605.   // Will still end in a slash.
  606. #ifdef XP_UNIX
  607.   // NOTE:  on UNIX we need to provide support for multiple users...
  608.   //
  609.   char *pUserName = getenv("USER");
  610.  
  611.   if (pUserName != NULL) {
  612.       StrAllocCat(pTempURL,"nscomm40-");
  613.       StrAllocCat(pTempURL,pUserName);
  614.       StrAllocCat(pTempURL,"/");
  615.   }
  616.   else {
  617.       StrAllocCat(pTempURL,"nscomm40/");
  618.   }
  619. #else
  620.   StrAllocCat(pTempURL,"nscomm40/");
  621. #endif
  622.   // pTempURL is now the application temp dir root.
  623.  
  624.     // EXTREME DANGER! CANNOT USE THIS IF "#" IS IN THE TEMP DIRECTORY NAME!
  625.     // Bug 83166  -- entire C drive deleted if TEMP=C:\#TEMP
  626.  
  627.     //char *pTemp_xpURL = NET_ParseURL(pTempURL,GET_PATH_PART);
  628.  
  629.     // This is (mostly) lifted from NET_ParseURL - just skip over host part
  630.     char *pTemp_xpURL = NULL;
  631.     char * colon = XP_STRCHR(pTempURL, ':'); /* returns a const char */
  632.     if(colon)
  633.     {
  634.         char * slash;
  635.         if(*(colon+1) == '/' && *(colon+2) == '/')
  636.         {
  637.             /* skip host part */
  638.             slash = XP_STRCHR(colon+3, '/');
  639.         }
  640.         else 
  641.         {
  642.             /* path is right after the colon
  643.              */
  644.             slash = colon+1;
  645.         }
  646.         // Copy everything starting with "/" to result string
  647.         pTemp_xpURL = XP_STRDUP(slash);
  648.         XP_FREEIF(pTempURL);
  649.         
  650.     } else {
  651.         // Should never get here, but if no colon,
  652.         //  just return what we started with
  653.         pTemp_xpURL = pTempURL;
  654.     }
  655.  
  656.     return pTemp_xpURL;
  657. }
  658.  
  659. char *CEditCommandLog::GetDocTempDir() {
  660.   // Already created.
  661.   if (m_pDocTempDir) {
  662.     return XP_STRDUP(m_pDocTempDir);
  663.   }
  664.  
  665.   char *pTemp_xpURL = GetAppTempDir();
  666.   if (!pTemp_xpURL) {
  667.     XP_ASSERT(0);
  668.     return NULL;
  669.   }
  670.  
  671.   // Create application temporary directory if necessary
  672.   XP_Dir pDir = edt_OpenDir(pTemp_xpURL,xpURL);
  673.   if (pDir) {
  674.     // Already exists, so ok.
  675.     XP_CloseDir(pDir);
  676.   }
  677.   else {
  678.     edt_MakeDirectory(pTemp_xpURL,xpURL);
  679.     pDir = edt_OpenDir(pTemp_xpURL,xpURL);
  680.     if (pDir) {
  681.       // made successfully, ok
  682.       XP_CloseDir(pDir);
  683.     }
  684.     else {
  685.       // can't make it, so fail.
  686.       XP_FREEIF(pTemp_xpURL);
  687.       return NULL;
  688.     }
  689.   }
  690.  
  691.   // Under application temporary directory, create process-specific
  692.   // temporary directory.  This is only really process-specific for 
  693.   // UNIX.  WIN and MAC has dummy directory name.
  694.   char *pProcessDir = NULL;
  695.   #ifdef XP_UNIX
  696.     pProcessDir = PR_smprintf("%u/",(unsigned)getpid());
  697.   #else
  698.     pProcessDir = XP_STRDUP("tmp/");
  699.   #endif
  700.   StrAllocCat(pTemp_xpURL,pProcessDir);
  701.   if (!pTemp_xpURL || !pProcessDir) {
  702.     XP_ASSERT(0);
  703.     XP_FREEIF(pProcessDir);
  704.     XP_FREEIF(pTemp_xpURL);
  705.     return NULL;
  706.   }
  707.  
  708.  
  709.   // pTemp_xpURL now has process-specific name appended.
  710.   pDir = edt_OpenDir(pTemp_xpURL,xpURL);
  711.   if (pDir) {
  712.     // Already exists, so ok.
  713.     XP_CloseDir(pDir);
  714.   }
  715.   else {
  716.     edt_MakeDirectory(pTemp_xpURL,xpURL);
  717.     pDir = edt_OpenDir(pTemp_xpURL,xpURL);
  718.     if (pDir) {
  719.       // made successfully, ok
  720.       XP_CloseDir(pDir);
  721.     }
  722.     else {
  723.       // can't make it, so fail.
  724.       XP_FREEIF(pTemp_xpURL);
  725.       return NULL;
  726.     }
  727.   }
  728.  
  729.  
  730.   // Create document-specific temp dir.
  731.   char *pFilename = PR_smprintf("tmp%ld",m_iDocTempDirNonce);
  732.   if (!pFilename) {
  733.     XP_ASSERT(0);
  734.     XP_FREE(pTemp_xpURL);
  735.     return NULL;
  736.   }
  737.   if (XP_STRLEN(pFilename) > 8) {
  738.     // truncate at 8 chars.
  739.     pFilename[8] = '\0';
  740.   }
  741.   StrAllocCat(pTemp_xpURL,pFilename);
  742.   XP_FREE(pFilename);    
  743.  
  744.   // end in slash.
  745.   StrAllocCat(pTemp_xpURL,"/");
  746.  
  747.   pDir = edt_OpenDir(pTemp_xpURL,xpURL);
  748.   if (pDir) {
  749.     // Already exists.  Strange, but not really an error.
  750.     XP_CloseDir(pDir);
  751.   }
  752.   else {
  753.     edt_MakeDirectory(pTemp_xpURL,xpURL);
  754.     pDir = edt_OpenDir(pTemp_xpURL,xpURL);
  755.     if (pDir) {
  756.       // made successfully, ok
  757.       XP_CloseDir(pDir);
  758.     }
  759.     else {
  760.       // failure, clear pTemp_xpURL
  761.       XP_FREEIF(pTemp_xpURL);
  762.     }
  763.   }
  764.  
  765.   if (pTemp_xpURL) {
  766.     // success.
  767.     m_iDocTempDirNonce++;
  768.     m_pDocTempDir = pTemp_xpURL;
  769.     return XP_STRDUP(m_pDocTempDir);  
  770.   }
  771.   else {
  772.     return NULL;
  773.   }
  774. }
  775.  
  776. int32 CEditCommandLog::m_iDocTempDirNonce = 0;
  777.  
  778.  
  779. // Always return a new filename in the temporary directory.
  780. char *CEditCommandLog::CreateDocTempFilename(char *pPrefix,char *pExtension) {
  781.   // Add directory.
  782.   char *pTempF = GetDocTempDir();
  783.   if (!pTempF) {
  784.     return NULL;
  785.   }
  786.  
  787.   // Add file prefix.
  788.   if (pPrefix) {
  789.     char *pPreCopy = XP_STRDUP(pPrefix);
  790.     if (!pPreCopy) {
  791.       XP_ASSERT(0);
  792.       return NULL; 
  793.     }
  794.  
  795.     // truncate to 3 chars.
  796.     if (XP_STRLEN(pPreCopy) > 3) {
  797.       pPreCopy[3] = '\0';
  798.     }
  799.  
  800.     StrAllocCat(pTempF,pPreCopy);
  801.     XP_FREE(pPreCopy);
  802.   }
  803.   else {
  804.     // default to "edt" for prefix.
  805.     StrAllocCat(pTempF,"edt");
  806.   }
  807.  
  808.   if (!pTempF) {
  809.     return NULL;
  810.   }
  811.   
  812.   char *pTempFilename = PR_smprintf("%s%d.%s",
  813.                                     pTempF,
  814.                                     (int)m_iDocTempFilenameNonce,
  815.                                     (pExtension ? pExtension : "tmp"));
  816.   XP_FREE(pTempF);
  817.   m_iDocTempFilenameNonce++;
  818.   return pTempFilename;
  819. }
  820.  
  821.  
  822. // CEditDataSaver
  823.  
  824. CEditDataSaver::CEditDataSaver(CEditBuffer* pBuffer){
  825.     m_pEditBuffer = pBuffer;
  826.     m_bModifiedTextHasBeenSaved = FALSE;
  827. #ifdef DEBUG
  828.     m_bDoState = 0;
  829. #endif
  830. }
  831.  
  832. CEditDataSaver::~CEditDataSaver(){
  833. }
  834.  
  835. void CEditDataSaver::DoBegin(CPersistentEditSelection& original){
  836. #ifdef DEBUG
  837.     XP_ASSERT(m_bDoState == 0);
  838.     m_bDoState++;
  839. #endif
  840.     m_original = original;
  841.     m_pEditBuffer->GetSelection(m_originalDocument);
  842.     CEditSelection selection =
  843.         m_pEditBuffer->PersistentToEphemeral(m_original);
  844.     selection.ExpandToNotCrossStructures();
  845.     m_expandedOriginal = m_pEditBuffer->EphemeralToPersistent(selection);
  846.     m_pEditBuffer->CopyEditText(m_expandedOriginal, m_originalText);
  847. }
  848.  
  849. void CEditDataSaver::DoEnd(CPersistentEditSelection& modified){
  850. #ifdef DEBUG
  851.     XP_ASSERT(m_bDoState == 1);
  852.     m_bDoState++;
  853. #endif
  854.     m_pEditBuffer->GetSelection(m_modifiedDocument);
  855.     m_expandedModified = m_expandedOriginal;
  856.     m_expandedModified.Map(m_original, modified);
  857. }
  858.  
  859. void CEditDataSaver::Undo(){
  860. #ifdef DEBUG
  861.     XP_ASSERT(m_bDoState == 2);
  862.     m_bDoState++;
  863. #endif
  864.     m_pEditBuffer->SetSelection(m_expandedModified);
  865.     if ( ! m_bModifiedTextHasBeenSaved ) {
  866.         m_pEditBuffer->CutEditText(m_modifiedText);
  867.         m_bModifiedTextHasBeenSaved = TRUE;
  868.     }
  869.     else {
  870.         m_pEditBuffer->DeleteSelection();
  871.     }
  872.     m_pEditBuffer->PasteEditText(m_originalText);
  873.     m_pEditBuffer->SetSelection(m_originalDocument);
  874. }
  875.  
  876. void CEditDataSaver::Redo(){
  877. #ifdef DEBUG
  878.     XP_ASSERT(m_bDoState == 3);
  879.     m_bDoState--;
  880. #endif
  881.     m_pEditBuffer->SetSelection(m_expandedOriginal);
  882.     m_pEditBuffer->DeleteSelection();
  883.     m_pEditBuffer->PasteEditText(m_modifiedText);
  884.     m_pEditBuffer->SetSelection(m_modifiedDocument);
  885. }
  886.  
  887.  
  888. // CDeleteTableCommand
  889. CDeleteTableCommand::CDeleteTableCommand(CEditBuffer* buffer, intn id)
  890.     : CEditCommand(buffer, id)
  891. {
  892.     m_pTable = NULL;
  893.     GetEditBuffer()->GetSelection(m_originalSelection);
  894.     CEditInsertPoint ip;
  895.     GetEditBuffer()->GetTableInsertPoint(ip);
  896.     GetEditBuffer()->ClearTableAndCellSelection();
  897.     m_pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  898.     if ( m_pTable ) {
  899.         CEditElement* pRefreshStart = m_pTable->GetFirstMostChild()->PreviousLeaf();
  900.         CEditInsertPoint replacePoint(m_pTable->GetLastMostChild()->NextLeaf(), 0);
  901.         GetEditBuffer()->SetInsertPoint(replacePoint);
  902.         m_pTable->Unlink();
  903.         m_replacePoint = GetEditBuffer()->EphemeralToPersistent(replacePoint);
  904.         GetEditBuffer()->Relayout(pRefreshStart, 0, replacePoint.m_pElement);
  905.     }
  906. }
  907.  
  908. CDeleteTableCommand::~CDeleteTableCommand()
  909. {
  910.     delete m_pTable;
  911. }
  912.  
  913. void CDeleteTableCommand::Do() {
  914. }
  915.  
  916. // CInsertTableCaptionCommand
  917. CInsertTableCaptionCommand::CInsertTableCaptionCommand(CEditBuffer* buffer,
  918.   EDT_TableCaptionData* pData, intn id)
  919.     : CEditCommand(buffer, id)
  920. {
  921.     m_pOldCaption = NULL;
  922.     GetEditBuffer()->GetSelection(m_originalSelection);
  923.     CEditInsertPoint ip;
  924.     GetEditBuffer()->GetTableInsertPoint(ip);
  925.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  926.     if ( pTable )
  927.     {
  928.         CEditCaptionElement* pCaption = new CEditCaptionElement();
  929.         pCaption->SetData(pData);
  930.         pTable->SetCaption(pCaption);
  931.         pTable->FinishedLoad(GetEditBuffer());
  932.         // CLM: Don't move insert point if we have a selected cell
  933.         //      (We use selection to indicate current cell in property dialogs)
  934.         if( !GetEditBuffer()->IsSelected() &&
  935.             ip.m_pElement->GetTableCellIgnoreSubdoc() != NULL )
  936.         {
  937.             // Put cursor at end of caption
  938.             ip.m_pElement = pTable->GetCaption()->GetLastMostChild()->Leaf();
  939.             ip.m_iPos = ip.m_pElement->GetLen();
  940.             GetEditBuffer()->SetInsertPoint(ip);
  941.         }
  942.         GetEditBuffer()->Relayout(pTable, 0);
  943.     }
  944. }
  945.  
  946. CInsertTableCaptionCommand::~CInsertTableCaptionCommand()
  947. {
  948.     delete m_pOldCaption;
  949. }
  950.  
  951. void CInsertTableCaptionCommand::Do() {
  952.     // All done in constructor
  953. }
  954.  
  955. // CDeleteTableCaptionCommand
  956. CDeleteTableCaptionCommand::CDeleteTableCaptionCommand(CEditBuffer* buffer, intn id)
  957.     : CEditCommand(buffer, id),
  958.     m_pOldCaption(NULL)
  959. {
  960.     GetEditBuffer()->GetSelection(m_originalSelection);
  961.     CEditInsertPoint ip;
  962.     GetEditBuffer()->GetTableInsertPoint(ip);
  963.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  964.     if ( pTable )
  965.     {
  966.         m_pOldCaption = pTable->GetCaption();
  967.         if ( m_pOldCaption )
  968.         {
  969.             CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  970.             //cmanske - Set cursor only if we are not in the caption being deleted
  971.             CEditCaptionElement *pCaption = ip.m_pElement->GetCaption();
  972.  
  973.             m_pOldCaption->Unlink();
  974.             pTable->FinishedLoad(GetEditBuffer());
  975.             if( pCaption /*!pTableCell*/ )
  976.             {
  977.                 // Set cursor to someplace that still exists
  978.                 // TODO: NEED TO FIX THIS TO PLACE CURSOR CLOSER TO WHERE CAPTION WAS
  979.                 CEditTableCellElement * pCell = pTable->GetFirstCell();
  980.                 int32 X = 0;
  981.                 int32 Y = 0;
  982.                 if( pCell )
  983.                 {
  984.                     X = pCell->GetX();
  985.                     Y = pCell->GetY();
  986.                 }
  987.                 GetEditBuffer()->StartSelection(X,Y);
  988.             }
  989.             GetEditBuffer()->Relayout(pTable, 0);
  990.         }
  991.     }
  992. }
  993.  
  994. CDeleteTableCaptionCommand::~CDeleteTableCaptionCommand()
  995. {
  996.     delete m_pOldCaption;
  997. }
  998.  
  999. void CDeleteTableCaptionCommand::Do()
  1000. {
  1001. }
  1002.  
  1003. // CInsertTableRowCommand
  1004. CInsertTableRowCommand::CInsertTableRowCommand(CEditBuffer* buffer,
  1005.   EDT_TableRowData* /* pData */, XP_Bool bAfterCurrentRow, intn number, intn id)
  1006.     : CEditCommand(buffer, id)
  1007. {
  1008.     m_number = number;
  1009.     GetEditBuffer()->GetSelection(m_originalSelection);
  1010.     if( GetEditBuffer()->IsSelected() )
  1011.     {
  1012.         GetEditBuffer()->ClearSelection();
  1013.     }
  1014.     CEditInsertPoint ip;
  1015.     GetEditBuffer()->GetTableInsertPoint(ip);
  1016.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  1017.     if ( pTableCell)
  1018.     {
  1019.         CEditTableElement* pTable = pTableCell->GetTable();
  1020.         if ( pTable )
  1021.         {
  1022.             int32 X = pTableCell->GetX();
  1023.             int32 Y = pTableCell->GetY();
  1024.             int32 iNewY = Y + (bAfterCurrentRow ? pTableCell->GetHeight() : 0);
  1025.             // Try to locate cursor in new cell
  1026.             int32 iCaretY = iNewY + (bAfterCurrentRow ? pTable->GetInterCellSpace() : 0);
  1027.             pTable->InsertRows(Y, iNewY, number);
  1028.             pTable->FinishedLoad(GetEditBuffer());
  1029.             GetEditBuffer()->Relayout(pTable, 0);
  1030.             GetEditBuffer()->MoveToExistingCell(pTable, X+2, iCaretY+2);
  1031.         }
  1032.     }
  1033. }
  1034.  
  1035. CInsertTableRowCommand::~CInsertTableRowCommand()
  1036. {
  1037. }
  1038.  
  1039. void CInsertTableRowCommand::Do() {
  1040.     // All done in constructor
  1041. }
  1042.  
  1043.  
  1044. // CDeleteTableRowCommand
  1045. CDeleteTableRowCommand::CDeleteTableRowCommand(CEditBuffer* buffer, intn rows, intn id)
  1046.     : CEditCommand(buffer, id),
  1047.     m_table(0,0)
  1048. {
  1049.     GetEditBuffer()->GetSelection(m_originalSelection);
  1050.     //The code from Redo moved here:
  1051.     CEditInsertPoint ip;
  1052.     GetEditBuffer()->GetTableInsertPoint(ip);
  1053.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  1054.     if ( pTableCell )
  1055.     {
  1056.         CEditTableElement* pTable = pTableCell->GetTable();
  1057.         if ( pTable )
  1058.         {
  1059.             int32 X = pTableCell->GetX();
  1060.             int32 Y = pTableCell->GetY();
  1061.             //TODO: FIGURE THIS OUT 
  1062.             m_bDeletedWholeTable = FALSE; //m_row == 0 && m_rows >= pTable->GetRows();
  1063.  
  1064.             pTable->DeleteRows(Y, rows);
  1065.             pTable->FinishedLoad(GetEditBuffer());
  1066.             // Move to a safe location so Relayout() doesn't assert
  1067.             GetEditBuffer()->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
  1068.             // Try to move to whereever we deleted
  1069.             GetEditBuffer()->MoveToExistingCell(pTable, X, Y);
  1070.         }
  1071.     }
  1072. }
  1073.  
  1074. CDeleteTableRowCommand::~CDeleteTableRowCommand()
  1075. {
  1076. }
  1077.  
  1078. void CDeleteTableRowCommand::Do()
  1079. {
  1080.     // All done in constructor
  1081. }
  1082.  
  1083.  
  1084. // CInsertTableColumnCommand
  1085. CInsertTableColumnCommand::CInsertTableColumnCommand(CEditBuffer* buffer,
  1086.   EDT_TableCellData* /* pData */, XP_Bool bAfterCurrentCell, intn number, intn id)
  1087.     : CEditCommand(buffer, id)
  1088. {
  1089. //    m_number = number;
  1090.     GetEditBuffer()->GetSelection(m_originalSelection);
  1091.     if( GetEditBuffer()->IsSelected() ){
  1092.         GetEditBuffer()->ClearSelection();
  1093.     }
  1094.     CEditInsertPoint ip;
  1095.     GetEditBuffer()->GetTableInsertPoint(ip);
  1096.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  1097.     if ( pTableCell)
  1098.     {
  1099.         CEditTableElement* pTable = pTableCell->GetTable();
  1100.         if ( pTable )
  1101.         {
  1102.             int32 X = pTableCell->GetX();
  1103.             int32 Y = pTableCell->GetY();
  1104.             int32 iNewX = X + (bAfterCurrentCell ? pTableCell->GetWidth() : 0);
  1105.             // Try to place cursor in inserted cell
  1106.             int32 iCaretX = iNewX + (bAfterCurrentCell ? pTable->GetInterCellSpace() : 0);
  1107.             pTable->InsertColumns(X, iNewX, number);
  1108.             pTable->FinishedLoad(GetEditBuffer());
  1109.             GetEditBuffer()->Relayout(pTable, 0);
  1110.             GetEditBuffer()->MoveToExistingCell(pTable, iCaretX, Y);
  1111.         }
  1112.     }
  1113. }
  1114.  
  1115. CInsertTableColumnCommand::~CInsertTableColumnCommand()
  1116. {
  1117. }
  1118.  
  1119. void CInsertTableColumnCommand::Do() {
  1120.     // All done in constructor
  1121. }
  1122.  
  1123. // CDeleteTableColumnCommand
  1124. CDeleteTableColumnCommand::CDeleteTableColumnCommand(CEditBuffer* buffer, intn columns, intn id)
  1125.     : CEditCommand(buffer, id),
  1126.     m_table(0,0)
  1127. {
  1128. //    m_columns = columns;
  1129.     GetEditBuffer()->GetSelection(m_originalSelection);
  1130.     CEditInsertPoint ip;
  1131.     GetEditBuffer()->GetTableInsertPoint(ip);
  1132.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  1133.     if ( pTableCell )
  1134.     {
  1135.         CEditTableElement* pTable = pTableCell->GetTable();
  1136.         if ( pTable )
  1137.         {
  1138.             //TODO: FIGURE THIS OUT 
  1139.             m_bDeletedWholeTable = FALSE; //m_column == 0 && m_columns >= pTable->GetColumns();
  1140.             int32 X = pTableCell->GetX();
  1141.             int32 Y = pTableCell->GetY();
  1142.             pTable->DeleteColumns(X, columns, &m_table);
  1143.             pTable->FinishedLoad(GetEditBuffer());
  1144.             // Move to a safe location so Relayout() doesn't assert
  1145.             GetEditBuffer()->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
  1146.             // Try to move to whereever we deleted
  1147.             GetEditBuffer()->MoveToExistingCell(pTable, X, Y);
  1148.         }
  1149.     }
  1150. }
  1151.  
  1152. CDeleteTableColumnCommand::~CDeleteTableColumnCommand()
  1153. {
  1154. }
  1155.  
  1156. void CDeleteTableColumnCommand::Do()
  1157. {
  1158. //#endif
  1159. }
  1160.  
  1161.  
  1162. // CInsertTableCellCommand
  1163. CInsertTableCellCommand::CInsertTableCellCommand(CEditBuffer* buffer,
  1164.   XP_Bool bAfterCurrentCell, intn number, intn id)
  1165.     : CEditCommand(buffer, id)
  1166. {
  1167.     m_number = number;
  1168.     GetEditBuffer()->GetSelection(m_originalSelection);
  1169.     if( GetEditBuffer()->IsSelected() ){
  1170.         GetEditBuffer()->ClearSelection();
  1171.     }
  1172.     CEditInsertPoint ip;
  1173.     GetEditBuffer()->GetTableInsertPoint(ip);
  1174.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  1175.     if ( pTableCell)
  1176.     {
  1177.         CEditTableRowElement* pTableRow = pTableCell->GetTableRow();
  1178.         CEditTableElement* pTable = pTableCell->GetTable();
  1179.         if ( pTable && pTableRow )
  1180.         {
  1181.             int32 X = pTableCell->GetX();
  1182.             int32 Y = pTableCell->GetY();
  1183.             int32 iNewX = X + (bAfterCurrentCell ? pTableCell->GetWidth() : 0);
  1184.             // Try to move cursor to new column
  1185.             int32 iCaretX = bAfterCurrentCell ? (iNewX+pTable->GetInterCellSpace()) : X;
  1186.             pTableRow->InsertCells(X, iNewX, number);
  1187.             pTable->FinishedLoad(GetEditBuffer());
  1188.             GetEditBuffer()->Relayout(pTable, 0);
  1189.             GetEditBuffer()->MoveToExistingCell(pTable, iCaretX, Y);
  1190.         }
  1191.     }
  1192. }
  1193.  
  1194. CInsertTableCellCommand::~CInsertTableCellCommand()
  1195. {
  1196. }
  1197.  
  1198. void CInsertTableCellCommand::Do() {
  1199.     // All done in constructor
  1200. }
  1201.  
  1202.  
  1203. // CDeleteTableCellCommand
  1204. CDeleteTableCellCommand::CDeleteTableCellCommand(CEditBuffer* buffer, intn columns, intn id)
  1205.     : CEditCommand(buffer, id)
  1206. {
  1207. //    m_columns = columns;
  1208.     GetEditBuffer()->GetSelection(m_originalSelection);
  1209.     CEditInsertPoint ip;
  1210.     GetEditBuffer()->GetTableInsertPoint(ip);
  1211.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  1212.     if ( pTableCell )
  1213.     {
  1214.         CEditTableRowElement* pTableRow = pTableCell->GetTableRowIgnoreSubdoc();
  1215.         CEditTableElement* pTable = pTableCell->GetTableIgnoreSubdoc();
  1216.         if ( pTable && pTableRow )
  1217.         {
  1218.             // TODO: FIGURE THIS OUT
  1219.             m_bDeletedWholeTable = FALSE; // m_column == 0 && m_columns >= pTableRow->GetCells();
  1220.  
  1221.             int32 X = pTableCell->GetX();
  1222.             int32 Y = pTableCell->GetY();
  1223.             pTableRow->DeleteCells(X, columns /*, &m_tableRow*/);
  1224.             pTable->FinishedLoad(GetEditBuffer());
  1225.             // Move to a safe location so Relayout() doesn't assert
  1226.             GetEditBuffer()->Relayout(pTable, 0, NULL, RELAYOUT_NOCARET);
  1227.             // Try to move to whereever we deleted
  1228.             GetEditBuffer()->MoveToExistingCell(pTable, X, Y);
  1229.         }
  1230.     }
  1231. }
  1232.  
  1233. CDeleteTableCellCommand::~CDeleteTableCellCommand()
  1234. {
  1235. }
  1236.  
  1237. void CDeleteTableCellCommand::Do()
  1238. {
  1239. }
  1240.  
  1241.  
  1242. // CEditCommandGroup
  1243.  
  1244. CEditCommandGroup::CEditCommandGroup(CEditBuffer* pEditBuffer, int id)
  1245.     : CEditCommand( pEditBuffer, id){
  1246. }
  1247.  
  1248. CEditCommandGroup::~CEditCommandGroup(){
  1249.     for ( int i = 0; i < m_commands.Size(); i++ ) {
  1250.         CEditCommand* item = m_commands[i];
  1251.         delete item;
  1252.     }
  1253. }
  1254.  
  1255. void CEditCommandGroup::AdoptAndDo(CEditCommand* pCommand){
  1256.     pCommand->Do();
  1257.     //TODO: IS THIS EVER FREED???
  1258.     m_commands.Add(pCommand);
  1259. }
  1260.  
  1261. void CEditCommandGroup::Undo(){
  1262.     for ( int i = m_commands.Size() - 1; i >= 0 ; i-- ) {
  1263.         CEditCommand* item = m_commands[i];
  1264.         item->Undo();
  1265.     }
  1266. }
  1267.  
  1268. void CEditCommandGroup::Redo(){
  1269.     for ( int i = 0; i < m_commands.Size(); i++ ) {
  1270.         CEditCommand* item = m_commands[i];
  1271.         item->Redo();
  1272.     }
  1273. }
  1274.  
  1275. intn CEditCommandGroup::GetNumberOfCommands(){
  1276.     return m_commands.Size();
  1277. }
  1278.  
  1279. #ifdef DEBUG
  1280. void CEditCommandGroup::Print(IStreamOut& stream){
  1281.     CEditCommand::Print(stream);
  1282.     stream.Printf(" %d commands\n", m_commands.Size());
  1283.     for ( int i = 0; i < m_commands.Size(); i++ ) {
  1284.         CEditCommand* item = m_commands[i];
  1285.         stream.Printf("    [%d] 0x%08x ", i, item);
  1286.         item->Print(stream);
  1287.         stream.Printf("\n");
  1288.     }
  1289. }
  1290. #endif
  1291.  
  1292. // CSetListDataCommand
  1293. CSetListDataCommand::CSetListDataCommand(CEditBuffer* pBuffer, EDT_ListData& listData, intn id)
  1294.     : CEditCommand(pBuffer, id)
  1295. {
  1296.     m_newData = listData;
  1297.     m_pOldData = NULL;
  1298. }
  1299.  
  1300. CSetListDataCommand::~CSetListDataCommand(){
  1301.     if ( m_pOldData ){
  1302.         CEditListElement::FreeData( m_pOldData );
  1303.     }
  1304. }
  1305.  
  1306. void CSetListDataCommand::Do(){
  1307.     m_pOldData = GetEditBuffer()->GetListData();
  1308.     XP_ASSERT(m_pOldData);
  1309.     GetEditBuffer()->SetListData(&m_newData);
  1310. }
  1311.  
  1312. void CSetListDataCommand::Undo(){
  1313.     if ( m_pOldData ){
  1314.         GetEditBuffer()->SetListData(m_pOldData);
  1315.     }
  1316. }
  1317.  
  1318. void CSetListDataCommand::Redo(){
  1319.     GetEditBuffer()->SetListData(&m_newData);
  1320. }
  1321.  
  1322. // CChangePageDataCommand
  1323.  
  1324. CChangePageDataCommand::CChangePageDataCommand(CEditBuffer* buffer, intn id)
  1325.     : CEditCommand(buffer, id)
  1326. {
  1327.     m_oldData = GetEditBuffer()->GetPageData();
  1328.     m_newData = NULL;
  1329. }
  1330.  
  1331. CChangePageDataCommand::~CChangePageDataCommand(){
  1332.     if ( m_oldData ) {
  1333.         GetEditBuffer()->FreePageData(m_oldData);
  1334.     }
  1335.     if ( m_newData ) {
  1336.         GetEditBuffer()->FreePageData(m_newData);
  1337.     }
  1338. }
  1339.  
  1340. void CChangePageDataCommand::Undo(){
  1341.     if ( ! m_newData ) {
  1342.         m_newData = GetEditBuffer()->GetPageData();
  1343.     }
  1344.     GetEditBuffer()->SetPageData(m_oldData);
  1345. }
  1346.  
  1347. void CChangePageDataCommand::Redo(){
  1348.     GetEditBuffer()->SetPageData(m_newData);
  1349. }
  1350.  
  1351. // CSetMetaDataCommand
  1352.  
  1353. CSetMetaDataCommand::CSetMetaDataCommand(CEditBuffer* buffer, EDT_MetaData *pMetaData, XP_Bool bDelete, intn id)
  1354.     : CEditCommand(buffer, id){
  1355.     m_bDelete = bDelete;
  1356.     int existingIndex = GetEditBuffer()->FindMetaData( pMetaData);
  1357.     m_bNewItem = existingIndex < 0;
  1358.     if ( m_bNewItem ) {
  1359.         m_pOldData = 0;
  1360.     }
  1361.     else {
  1362.         m_pOldData = GetEditBuffer()->GetMetaData(existingIndex);
  1363.     }
  1364.     if ( m_bDelete ) {
  1365.         GetEditBuffer()->DeleteMetaData(pMetaData);
  1366.         m_pNewData = 0;
  1367.     }
  1368.     else {
  1369.         GetEditBuffer()->SetMetaData(pMetaData);
  1370.         m_pNewData = GetEditBuffer()->GetMetaData(GetEditBuffer()->FindMetaData(pMetaData));
  1371.     }
  1372. }
  1373.  
  1374. CSetMetaDataCommand::~CSetMetaDataCommand(){
  1375.     if ( m_pOldData ) {
  1376.         GetEditBuffer()->FreeMetaData(m_pOldData);
  1377.     }
  1378.     if ( m_pNewData ) {
  1379.         GetEditBuffer()->FreeMetaData(m_pNewData);
  1380.     }
  1381. }
  1382.  
  1383. void CSetMetaDataCommand::Undo(){
  1384.     if ( m_bNewItem  ) {
  1385.         if ( m_pNewData ) {
  1386.             GetEditBuffer()->DeleteMetaData(m_pNewData);
  1387.         }
  1388.     }
  1389.     else {
  1390.         if ( m_pOldData ) {
  1391.             GetEditBuffer()->SetMetaData(m_pOldData);
  1392.         }
  1393.     }
  1394. }
  1395.  
  1396. void CSetMetaDataCommand::Redo(){
  1397.     if ( m_bDelete ) {
  1398.         if ( m_pOldData ) {
  1399.             GetEditBuffer()->DeleteMetaData(m_pOldData);
  1400.         }
  1401.     }
  1402.     else {
  1403.         if ( m_pNewData ) {
  1404.             GetEditBuffer()->SetMetaData(m_pNewData);
  1405.         }
  1406.     }
  1407. }
  1408.  
  1409. // CSetTableDataCommand
  1410.  
  1411. CSetTableDataCommand::CSetTableDataCommand(CEditBuffer* buffer, EDT_TableData* pTableData, intn id)
  1412.     : CEditCommand(buffer, id){
  1413.     m_pOldData = GetEditBuffer()->GetTableData();
  1414.     GetEditBuffer()->SetTableData(pTableData);
  1415.     m_pNewData = GetEditBuffer()->GetTableData();
  1416. }
  1417.  
  1418. CSetTableDataCommand::~CSetTableDataCommand(){
  1419.     CEditTableElement::FreeData(m_pOldData);
  1420.     CEditTableElement::FreeData(m_pNewData);
  1421. }
  1422.  
  1423. void CSetTableDataCommand::Undo(){
  1424.     GetEditBuffer()->SetTableData(m_pOldData);
  1425. }
  1426.  
  1427. void CSetTableDataCommand::Redo(){
  1428.     GetEditBuffer()->SetTableData(m_pNewData);
  1429. }
  1430.  
  1431. CSetTableCaptionDataCommand::CSetTableCaptionDataCommand(CEditBuffer* buffer, EDT_TableCaptionData* pTableData, intn id)
  1432.     : CEditCommand(buffer, id){
  1433.     m_pOldData = GetEditBuffer()->GetTableCaptionData();
  1434.     GetEditBuffer()->SetTableCaptionData(pTableData);
  1435.     m_pNewData = GetEditBuffer()->GetTableCaptionData();
  1436. }
  1437.  
  1438. CSetTableCaptionDataCommand::~CSetTableCaptionDataCommand(){
  1439.     CEditCaptionElement::FreeData(m_pOldData);
  1440.     CEditCaptionElement::FreeData(m_pNewData);
  1441. }
  1442.  
  1443. void CSetTableCaptionDataCommand::Undo(){
  1444.     GetEditBuffer()->SetTableCaptionData(m_pOldData);
  1445. }
  1446.  
  1447. void CSetTableCaptionDataCommand::Redo(){
  1448.     GetEditBuffer()->SetTableCaptionData(m_pNewData);
  1449. }
  1450.  
  1451. CSetTableRowDataCommand::CSetTableRowDataCommand(CEditBuffer* buffer, EDT_TableRowData* pTableData, intn id)
  1452.     : CEditCommand(buffer, id){
  1453.     m_pOldData = GetEditBuffer()->GetTableRowData();
  1454.     GetEditBuffer()->SetTableRowData(pTableData);
  1455.     m_pNewData = GetEditBuffer()->GetTableRowData();
  1456. }
  1457.  
  1458. CSetTableRowDataCommand::~CSetTableRowDataCommand(){
  1459.     CEditTableRowElement::FreeData(m_pOldData);
  1460.     CEditTableRowElement::FreeData(m_pNewData);
  1461. }
  1462.  
  1463. void CSetTableRowDataCommand::Undo(){
  1464.     GetEditBuffer()->SetTableRowData(m_pOldData);
  1465. }
  1466.  
  1467. void CSetTableRowDataCommand::Redo(){
  1468.     GetEditBuffer()->SetTableRowData(m_pNewData);
  1469. }
  1470.  
  1471. CSetTableCellDataCommand::CSetTableCellDataCommand(CEditBuffer* buffer, EDT_TableCellData* pTableData, intn id)
  1472.     : CEditCommand(buffer, id){
  1473.     m_pOldData = GetEditBuffer()->GetTableCellData();
  1474.     GetEditBuffer()->SetTableCellData(pTableData);
  1475.     m_pNewData = GetEditBuffer()->GetTableCellData();
  1476. }
  1477.  
  1478. CSetTableCellDataCommand::~CSetTableCellDataCommand(){
  1479.     CEditTableCellElement::FreeData(m_pOldData);
  1480.     CEditTableCellElement::FreeData(m_pNewData);
  1481. }
  1482.  
  1483. void CSetTableCellDataCommand::Undo(){
  1484.     GetEditBuffer()->SetTableCellData(m_pOldData);
  1485. }
  1486.  
  1487. void CSetTableCellDataCommand::Redo(){
  1488.     GetEditBuffer()->SetTableCellData(m_pNewData);
  1489. }
  1490.  
  1491. #ifdef SUPPORT_MULTICOLUMN
  1492.  
  1493. CSetMultiColumnDataCommand::CSetMultiColumnDataCommand(CEditBuffer* buffer, EDT_MultiColumnData* pMultiColumnData, intn id)
  1494.     : CEditCommand(buffer, id){
  1495.     m_pOldData = GetEditBuffer()->GetMultiColumnData();
  1496.     GetEditBuffer()->SetMultiColumnData(pMultiColumnData);
  1497.     m_pNewData = GetEditBuffer()->GetMultiColumnData();
  1498. }
  1499.  
  1500. CSetMultiColumnDataCommand::~CSetMultiColumnDataCommand(){
  1501.     CEditMultiColumnElement::FreeData(m_pOldData);
  1502.     CEditMultiColumnElement::FreeData(m_pNewData);
  1503. }
  1504.  
  1505. void CSetMultiColumnDataCommand::Undo(){
  1506.     GetEditBuffer()->SetMultiColumnData(m_pOldData);
  1507. }
  1508.  
  1509. void CSetMultiColumnDataCommand::Redo(){
  1510.     GetEditBuffer()->SetMultiColumnData(m_pNewData);
  1511. }
  1512. #endif
  1513.  
  1514. // CSetSelectionCommand
  1515.  
  1516. CSetSelectionCommand::CSetSelectionCommand(CEditBuffer* buffer, CEditSelection& selection, intn id)
  1517.     : CEditCommand(buffer, id){
  1518.     m_NewSelection = GetEditBuffer()->EphemeralToPersistent(selection);
  1519. }
  1520.  
  1521. CSetSelectionCommand::~CSetSelectionCommand(){
  1522. }
  1523.  
  1524. void CSetSelectionCommand::Do(){
  1525.     GetEditBuffer()->GetSelection(m_OldSelection);
  1526.     GetEditBuffer()->SetSelection(m_NewSelection);
  1527. }
  1528.  
  1529. void CSetSelectionCommand::Undo(){
  1530.     GetEditBuffer()->SetSelection(m_OldSelection);
  1531. }
  1532.  
  1533. void CSetSelectionCommand::Redo(){
  1534.     GetEditBuffer()->SetSelection(m_NewSelection);
  1535. }
  1536.  
  1537. #endif
  1538.