home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / edtbuf.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  494.7 KB  |  14,929 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. #ifdef EDITOR
  21.  
  22. #include "editor.h"
  23. #include "prefapi.h"
  24. #include "intl_csi.h"
  25.  
  26. #define CONTENT_TYPE "Content-Type"
  27.  
  28. // Maximum length of "positional text" - see GetPositionalText(). 
  29. #ifdef XP_WIN16
  30. const int32 MAX_POSITIONAL_TEXT = 64000;
  31. #else
  32. const int32 MAX_POSITIONAL_TEXT =0x7FFFFFF0;
  33. #endif
  34.  
  35. // Maximum length of text that can be pasted.
  36. #ifdef XP_WIN16
  37. #define MAX_PASTE_SIZE 32760
  38. #else
  39. #define MAX_PASTE_SIZE (1<<30)
  40. #endif
  41.  
  42. #include "prefapi.h"
  43.  
  44. #define GET_COL TRUE
  45. #define GET_ROW FALSE
  46.  
  47. #if defined( DEBUG_shannon )
  48. int32    gEditorReflow = TRUE;
  49. #endif
  50.  
  51. EDT_CharacterData *CEditBuffer::m_pCopyStyleCharacterData = 0;
  52.  
  53. //-----------------------------------------------------------------------------
  54. // CEditBuffer
  55. //-----------------------------------------------------------------------------
  56.  
  57. CEditElement* CEditBuffer::CreateElement(PA_Tag* pTag, CEditElement *pParent){
  58.     CEditElement *pElement;
  59.     // this is a HACK.  We don't understand the tag but we want
  60.     //  to make sure that we can re-gurgitate it to layout.
  61.     //  Save the parameter data so we can spew it back out.
  62.     char *locked_buff;
  63.  
  64.     PA_LOCK(locked_buff, char *, pTag->data);
  65.     if( locked_buff && *locked_buff != '>'){
  66.         pElement = new CEditElement(pParent,pTag->type,locked_buff);
  67.     }
  68.     else {
  69.         pElement = new CEditElement(pParent,pTag->type);
  70.     }
  71.     PA_UNLOCK(pTag->data);
  72.     return pElement;
  73. }
  74.  
  75. CParseState* CEditBuffer::GetParseState() {
  76.     if ( m_parseStateStack.IsEmpty() ) {
  77.         PushParseState();
  78.     }
  79.     return m_parseStateStack.Top();
  80. }
  81.  
  82.  
  83. /////////////////////////////////////////////
  84. //EDITBUFFER/////////////////////////////////
  85. /////////////////////////////////////////////
  86.  
  87. XP_Bool CEditBuffer::m_bEdtBufPrefInitialized=FALSE;
  88. XP_Bool CEditBuffer::m_bMoveCursor; //no initial value should be depended on
  89. //these variables are set by the pref_registercallback function "PrefCallback";
  90.  
  91.  
  92. //callback from a change in the preferences. we must then reinitialize these prefs
  93. int CEditBuffer::PrefCallback(const char *, void *)//static
  94. {
  95.   m_bEdtBufPrefInitialized=FALSE;
  96.   CEditBuffer::InitializePrefs();//static
  97.   return TRUE;
  98. }
  99.  
  100.  
  101. void CEditBuffer::InitializePrefs()//static
  102. {
  103.   if (!m_bEdtBufPrefInitialized) //this is ok if this is allready initialized, 
  104.   {                              //this means that someone has changed 
  105.                                  //them and we need to reset them 
  106.     PREF_RegisterCallback("editor", CEditBuffer::PrefCallback, NULL);//no instance data, only setting statics.  we are all of the same mind here
  107.     PREF_GetBoolPref("editor.page_updown_move_cursor",&m_bMoveCursor);
  108.   }
  109.   m_bEdtBufPrefInitialized=TRUE;
  110. }
  111.  
  112.  
  113.  
  114. void CEditBuffer::PushParseState(){
  115.     XP_Bool bInBody = FALSE;
  116.     if ( ! m_parseStateStack.IsEmpty() ) {
  117.         bInBody = m_parseStateStack.Top()->InBody();
  118.     }
  119.     CParseState* state = new CParseState();
  120.     state->Reset();
  121.     if ( bInBody ) {
  122.         state->StartBody(); // If we were in the body before, we're still in the body.
  123.     }
  124.     m_parseStateStack.Push(state);
  125. }
  126.  
  127. void CEditBuffer::PopParseState(){
  128.     if ( m_parseStateStack.IsEmpty() ) {
  129.         XP_ASSERT(FALSE);
  130.     }
  131.     else {
  132.         CParseState* state = m_parseStateStack.Pop();
  133.         delete state;
  134.     }
  135. }
  136.  
  137. void CEditBuffer::ResetParseStateStack(){
  138.     while ( ! m_parseStateStack.IsEmpty() ) {
  139.         PopParseState();
  140.     }
  141. }
  142.  
  143. //
  144. // stolen from layout
  145. //
  146. #define DEF_TAB_WIDTH       8
  147.  
  148. #define REMOVECHAR(p) XP_MEMMOVE( p, p+1, XP_STRLEN(p) );
  149.  
  150. PRIVATE
  151. void edt_ExpandTab(char *p, intn n){
  152.     if( n != 1 ){
  153.         XP_MEMMOVE( p+n, p+1, XP_STRLEN(p));
  154.     }
  155.     XP_MEMSET( p, ' ', n);
  156. }
  157.  
  158.  
  159. //
  160. // Parse the text into a series of tags.  Each tag is at most one line in
  161. //  length.
  162. //
  163. intn CEditBuffer::NormalizePreformatText(pa_DocData *pData, PA_Tag* pTag,
  164.             intn status ){
  165.     char *pText;
  166.     intn retVal = OK_CONTINUE;
  167.     m_bInPreformat = TRUE;
  168.  
  169.     PA_LOCK(pText, char *, pTag->data);
  170.  
  171.  
  172.     //
  173.     // Calculate how big a buffer we are going to need to normalize this
  174.     //  preformatted text.
  175.     //
  176.     int iTabCount = 0;
  177.     int iLen = 0;
  178.     int iNeedLen;
  179.     int iSpace;
  180.  
  181.     while( *pText ){
  182.         if( *pText == TAB ){
  183.             iTabCount++;
  184.         }
  185.         pText++;
  186.         iLen++;
  187.     }
  188.  
  189.     PA_UNLOCK(pTag->data);
  190.     iNeedLen = iLen + iTabCount * DEF_TAB_WIDTH+1;
  191.  
  192.     pTag->data =  (PA_Block) PA_REALLOC( pTag->data, iNeedLen );
  193.     pTag->data_len = iNeedLen;
  194.     PA_LOCK(pText, char *, pTag->data);
  195.  
  196.     // LTNOTE: total hack, but make sure the buffer is 0 terminated...  Probably has
  197.     //  bad internation ramifications.. Oh Well...
  198.     // JHPNOTE: I'll say this is a total hack. We used to write at [data_len], which
  199.     // smashed beyond allocated memory. As far as I can see there's no need to write here,
  200.     // since the string is known to be zero terminated now, and all the operations
  201.     // below keep the string zero terminated. Never the less, in the interest of keeping
  202.     // tradition alive (and because I don't want to take the chance of introducing a
  203.     // bug, I'll leave the correct, but unnescessary, code in place.
  204.  
  205.     pText[iNeedLen-1] = 0;
  206.     pText[iLen] = 0;
  207.  
  208.     char* pBuf = pText;
  209.     //
  210.     // Now actually Normalize the preformatted text
  211.     //
  212.     while( *pText ){
  213.         switch( *pText ){
  214.         // Look for CF LF, if you see it, just treat it as LF
  215.         case CR:
  216.             if( pText[1] == LF ){
  217.                 REMOVECHAR(pText);
  218.                 break;
  219.             }
  220.             else {
  221.                 *pText = LF;
  222.             }
  223.  
  224.         case LF:
  225.             pText++;
  226.             m_preformatLinePos = 0;
  227.             break;
  228.  
  229.         case VTAB:
  230.         case FF:
  231.             REMOVECHAR(pText);
  232.             break;
  233.  
  234.         case TAB:
  235.             iSpace = DEF_TAB_WIDTH - (m_preformatLinePos % DEF_TAB_WIDTH);
  236.  
  237.             edt_ExpandTab( pText, iSpace );
  238.             pText += iSpace;
  239.             m_preformatLinePos += iSpace;
  240.             break;
  241.  
  242.  
  243.         default:
  244.             pText++;
  245.             m_preformatLinePos++;
  246.         }
  247.     }
  248.  
  249.     XP_Bool bBreak = FALSE;
  250.     while( *pBuf && retVal == OK_CONTINUE ){
  251.         pText = pBuf;
  252.         bBreak = FALSE;
  253.  
  254.         // scan for end of line.
  255.         while( *pText && *pText != '\n' ){
  256.             pText++;
  257.         }
  258.  
  259.  
  260.         PA_Tag *pNewTag = XP_NEW( PA_Tag );
  261.         XP_BZERO( pNewTag, sizeof( PA_Tag ) );
  262.         pNewTag->type = P_TEXT;
  263.  
  264.         char save = *pText;
  265.         *pText = 0;
  266.         edt_SetTagData(pNewTag, pBuf );
  267.         *pText = save;
  268.  
  269.         // include the new line
  270.         if( *pText == '\n' ){
  271.             pText++;
  272.             bBreak = TRUE;
  273.         }
  274.  
  275.         pBuf = pText;
  276.         retVal = EDT_ProcessTag( pData, pNewTag, status );
  277.         if( bBreak ){
  278.             pNewTag = XP_NEW( PA_Tag );
  279.             XP_BZERO( pNewTag, sizeof( PA_Tag ) );
  280.             pNewTag->type = P_LINEBREAK;
  281.             retVal = EDT_ProcessTag( pData, pNewTag, status );
  282.         }
  283.     }
  284.  
  285.     PA_UNLOCK(pTag->data);
  286.     m_bInPreformat = FALSE;
  287.     return ( retVal == OK_CONTINUE) ? OK_IGNORE : retVal ;
  288. }
  289.  
  290.  
  291. static char *anchorHrefParams[] = {
  292.     PARAM_HREF,
  293.     0
  294. };
  295.  
  296. static char *bodyParams[] = {
  297.     PARAM_BACKGROUND,
  298.     PARAM_BGCOLOR,
  299.     PARAM_TEXT,
  300.     PARAM_LINK,
  301.     PARAM_ALINK,
  302.     PARAM_VLINK,
  303.     PARAM_NOSAVE,
  304.     0
  305. };
  306.  
  307. PRIVATE
  308. void NormalizeEOLsInString(char* pStr){
  309.     int state = 0;
  310.     char* pOut = pStr;
  311.     for(;; ) {
  312.         char c = *pStr++;
  313.         if ( c == '\0' ) break;
  314.  
  315.         int charClass = 0;
  316.         if ( c == '\r' ) charClass = 1;
  317.         else if ( c == '\n') charClass = 2;
  318.         switch ( state ) {
  319.         case 0: /* Normal */
  320.             switch ( charClass ) {
  321.             case 0: /* Normal char */
  322.                 *pOut++ = c;
  323.                 break;
  324.             case 1: /* CR */
  325.                 state = 1;
  326.                 *pOut++ = '\n';
  327.                 break;
  328.             case 2: /* LF */
  329.                 state = 2;
  330.                 *pOut++ = '\n';
  331.                 break;
  332.             }
  333.             break;
  334.         case 1: /* Just saw a CR */
  335.             switch ( charClass ) {
  336.             case 0: /* Normal char */
  337.                 *pOut++ = c;
  338.                 state = 0;
  339.                 break;
  340.             case 1: /* CR */
  341.                 *pOut++ = '\n';
  342.                 break;
  343.             case 2: /* LF */
  344.                 state = 0;
  345.                 /* Swallow it, back to normal */
  346.                 break;
  347.             }
  348.             break;
  349.         case 2: /* Just saw a LF */
  350.             switch ( charClass ) {
  351.             case 0: /* Normal char */
  352.                 *pOut++ = c;
  353.                 state = 0;
  354.                 break;
  355.             case 1: /* CR */
  356.                 /* Swallow it, back to normal */
  357.                 state = 0;
  358.                 break;
  359.             case 2: /* LF */
  360.                 *pOut++ = '\n';
  361.                 break;
  362.             }
  363.             break;
  364.         }
  365.     }
  366.     *pOut = '\0';
  367. }
  368.  
  369.  
  370. PRIVATE
  371. void NormalizeEOLsInTag(PA_Tag* pTag){
  372.     // Convert the end-of-lines into \n characters.
  373.     if( pTag->data ){
  374.         char* pStr;
  375.         PA_LOCK(pStr, char*, pTag->data);
  376.         NormalizeEOLsInString(pStr);
  377.         pTag->data_len = strlen( pStr );
  378.         PA_UNLOCK( pTag->data );
  379.     }
  380. }
  381.  
  382. PRIVATE
  383. XP_Bool IsComment(PA_Tag* pTag){
  384.     // Is this a comment tag?
  385.     XP_Bool result = FALSE;
  386.     if ( pTag->type == P_UNKNOWN ) {
  387.         if( pTag->data ){
  388.             char* pStr;
  389.             const char* kComment = "!";
  390.             unsigned int commentLen = XP_STRLEN(kComment);
  391.             PA_LOCK(pStr, char*, pTag->data);
  392.             if ( pStr && XP_STRLEN(pStr) >= commentLen && XP_STRNCASECMP(pStr, kComment, commentLen) == 0 ) {
  393.                 result = TRUE;
  394.             }
  395.             PA_UNLOCK( pTag->data );
  396.         }
  397.     }
  398.     return result;
  399. }
  400.  
  401. PRIVATE
  402. XP_Bool IsEmptyText(PA_Tag* pTag){
  403.     // Is this empty white-space text?
  404.     // Takes advantage of white-space being
  405.     // one byte in all character sets,
  406.     // and non-white-space characters always
  407.     // start with a non-white-space byte.
  408.     XP_Bool result = FALSE;
  409.     if ( pTag->type == P_TEXT ) {
  410.         result = TRUE;
  411.         if( pTag->data ){
  412.             char* pStr;
  413.             PA_LOCK(pStr, char*, pTag->data);
  414.             if ( pStr ) {
  415.                 while ( *pStr ) {
  416.                     if ( ! XP_IS_SPACE(*pStr) ) {
  417.                         result = FALSE;
  418.                         break;
  419.                     }
  420.                     pStr++;
  421.                 }
  422.             }
  423.             PA_UNLOCK( pTag->data );
  424.         }
  425.     }
  426.     return result;
  427. }
  428.  
  429.  
  430. PRIVATE
  431. XP_Bool IsDocTypeTag(PA_Tag* pTag){
  432.     // Is this a DocType tag?
  433.     XP_Bool result = FALSE;
  434.     if ( pTag->type == P_UNKNOWN ) {
  435.         if( pTag->data ){
  436.             char* pStr;
  437.             const char* kDocType = "!DOCTYPE";
  438.             unsigned int docTypeLen = XP_STRLEN(kDocType);
  439.             PA_LOCK(pStr, char*, pTag->data);
  440.             if ( XP_STRLEN(pStr) >= docTypeLen && XP_STRNCASECMP(pStr, kDocType, docTypeLen) == 0 ) {
  441.                 result = TRUE;
  442.             }
  443.             PA_UNLOCK( pTag->data );
  444.         }
  445.     }
  446.     return result;
  447. }
  448.  
  449. // To do - combine with HandleSelectionComment
  450.  
  451. XP_Bool CEditBuffer::IsSelectionComment(PA_Tag* pTag){
  452.     XP_Bool result = FALSE;
  453.     if ( pTag && pTag->type == P_UNKNOWN) {
  454.         if ( pTag->data ){
  455.             char* kStart = "!-- " EDT_SELECTION_START_COMMENT;
  456.             char* kEnd = "!-- " EDT_SELECTION_END_COMMENT;
  457.             char* pStr;
  458.             PA_LOCK(pStr, char*, pTag->data);
  459.             XP_Bool bStartComment = XP_STRLEN(pStr) >= XP_STRLEN(kStart) &&
  460.                 XP_STRNCASECMP(pStr, kStart, XP_STRLEN(kStart)) == 0;
  461.             XP_Bool bEndComment = XP_STRLEN(pStr) >= XP_STRLEN(kEnd) &&
  462.                 XP_STRNCASECMP(pStr, kEnd,  XP_STRLEN(kEnd)) == 0;
  463.             if ( bStartComment || bEndComment ) {
  464.                 result = TRUE;
  465.             }
  466.             PA_UNLOCK( pTag->data );
  467.         }
  468.     }
  469.     return result;
  470. }
  471.  
  472. XP_Bool CEditBuffer::HandleSelectionComment(PA_Tag* pTag, CEditElement*& pElement, intn& retval){
  473.     XP_Bool result = FALSE;
  474.     if ( pTag->type == P_UNKNOWN) {
  475.         if ( pTag->data ){
  476.             char* kStart = "!-- " EDT_SELECTION_START_COMMENT;
  477.             char* kEnd = "!-- " EDT_SELECTION_END_COMMENT;
  478.             char* kStartPlus = "!-- " EDT_SELECTION_START_COMMENT "+";
  479.             char* kEndPlus = "!-- " EDT_SELECTION_END_COMMENT "+";
  480.             char* pStr;
  481.             PA_LOCK(pStr, char*, pTag->data);
  482.             XP_Bool bStartComment = XP_STRLEN(pStr) >= XP_STRLEN(kStart) &&
  483.                 XP_STRNCASECMP(pStr, kStart, XP_STRLEN(kStart)) == 0;
  484.             XP_Bool bEndComment = XP_STRLEN(pStr) >= XP_STRLEN(kEnd) &&
  485.                 XP_STRNCASECMP(pStr, kEnd,  XP_STRLEN(kEnd)) == 0;
  486.             if ( bStartComment || bEndComment )
  487.             {
  488.                 result = TRUE;
  489.                 pElement = NULL;
  490.                 if ( bStartComment && ! m_pStartSelectionAnchor ) {
  491.                     pElement = m_pStartSelectionAnchor = new CEditInternalAnchorElement(m_pCreationCursor);
  492.                     m_bStartSelectionStickyAfter = XP_STRLEN(pStr) >= XP_STRLEN(kStartPlus) &&
  493.                         XP_STRNCASECMP(pStr, kStartPlus, XP_STRLEN(kStartPlus)) == 0;
  494.                 }
  495.                 else if ( bEndComment && ! m_pEndSelectionAnchor ) {
  496.                     pElement = m_pEndSelectionAnchor = new CEditInternalAnchorElement(m_pCreationCursor);
  497.                     m_bEndSelectionStickyAfter = XP_STRLEN(pStr) >= XP_STRLEN(kEndPlus) &&
  498.                         XP_STRNCASECMP(pStr, kEndPlus, XP_STRLEN(kEndPlus)) == 0;
  499.                 }
  500.                 else {
  501.                     retval = OK_IGNORE;
  502.                 }
  503.             }
  504.             PA_UNLOCK( pTag->data );
  505.         }
  506.     }
  507.  
  508.     if ( pElement ) {
  509.         m_pCreationCursor = pElement->GetParent();
  510.     }
  511.     return result;
  512. }
  513.  
  514. XP_Bool CEditBuffer::ShouldAutoStartBody(PA_Tag* pTag, int16 csid){
  515.     CParseState* pParseState = GetParseState();
  516.     if ( pParseState->InBody() ) {
  517.         return FALSE;
  518.     }
  519.     if ( pParseState->m_inJavaScript ||
  520.         pParseState->m_bInTitle) {
  521.         return FALSE;
  522.     }
  523.     if (IsSelectionComment(pTag)){
  524.         return TRUE;
  525.     }
  526.     if ( ! BitSet( edt_setAutoStartBody, pTag->type ) ) {
  527.         return FALSE;
  528.     }
  529.     if ( pTag->type != P_TEXT ) {
  530.         return TRUE;
  531.     }
  532.     XP_Bool result = FALSE;
  533.     // Pure white space doesn't start a body.
  534.     // This code takes advantage of the fact that, in all supported
  535.     // character sets, white-space characters are single byte.
  536.     if( pTag->data ){
  537.         char* pStr;
  538.         PA_LOCK(pStr, char*, pTag->data);
  539.         if ( pStr ) {
  540.             while ( *pStr ) {
  541.                 if (! XP_IS_SPACE(*pStr) ) {
  542.                     result = TRUE;
  543.                     break;
  544.                 }
  545.                 pStr = INTL_NextChar(csid, pStr);
  546.             }
  547.         }
  548.         PA_UNLOCK( pTag->data );
  549.     }
  550.     return result;
  551. }
  552.  
  553. intn CEditBuffer::ParseTag(pa_DocData *pData, PA_Tag* pTag, intn status){
  554.     intn retVal = OK_CONTINUE;
  555.  
  556.     // Examine the tag  before it's modified or replaced.
  557.     XP_Bool bRecordTag = ! IsComment(pTag) && ! IsEmptyText(pTag);
  558.     TagType iTagType = pTag->type;
  559.     XP_Bool bTagIsEnd = (XP_Bool) pTag->is_end;
  560.  
  561.     NormalizeEOLsInTag(pTag);
  562.  
  563.     /* P_STRIKE is a synonym for P_STRIKEOUT. Since pre-3.0 versions of
  564.      * Navigator don't recognize P_STRIKE ( "<S>" ), we switch it to
  565.      * P_STRIKEOUT ( "<STRIKE>" ). Also, it would complicate the
  566.      * editor internals to track two versions of the same tag.
  567.      * We test here so that we catch both the beginning and end tag.
  568.      */
  569.     if ( pTag->type == P_STRIKE ) {
  570.         pTag->type = P_STRIKEOUT;
  571.     }
  572.  
  573.     /* Many HTML pages don't include BODY tags. The first appearance of a
  574.      * body-only tag is our signal to start a body.
  575.      */
  576.     if ( ShouldAutoStartBody(pTag, GetRAMCharSetID()) ) {
  577.         GetParseState()->StartBody();
  578.     }
  579.  
  580.     if( pTag->is_end && !BitSet( edt_setUnsupported, pTag->type ) &&
  581.             pTag->type != P_UNKNOWN ){
  582.         retVal = ParseEndTag(pTag);
  583.     }
  584.     else {
  585.         retVal = ParseOpenTag(pData, pTag, status);
  586.     }
  587.  
  588.     // Maintain the m_iLastTagType
  589.     if ( bRecordTag ) {
  590.         m_iLastTagType = iTagType;
  591.         m_bLastTagIsEnd = bTagIsEnd;
  592.     }
  593.  
  594.     return retVal;
  595. }
  596.  
  597. intn CEditBuffer::ParseEndTag(PA_Tag* pTag){
  598.     intn retVal = OK_CONTINUE;
  599.  
  600.     // If this is an end tag, match backwardly to the appropriate tag.
  601.     //
  602.     CEditElement *pPop = m_pCreationCursor;
  603.     if( pTag->type == P_HEAD ){
  604.         WriteClosingScriptTag();
  605.         // RecordTag(pTag, TRUE);
  606.         return retVal;
  607.     }
  608.     if( pTag->type == P_BODY ){
  609.         // We ignore </BODY> in the Mail Compose window because we have
  610.         // to deal with nested HTML documents.
  611.         // We don't ignore it in the Page Compose window because
  612.         // we want to preserve <FRAMESET>s and other tags that appear
  613.         // after the </BODY>.
  614.         if ( !IsComposeWindow() ) {
  615.             GetParseState()->EndBody();
  616.         }
  617.         return retVal;
  618.     }
  619.     if( pTag->type == P_TITLE ){
  620.         GetParseState()->m_bInTitle = FALSE;
  621.         return retVal;
  622.     }
  623.     // libparse is nice enough to only call us with end tags if they
  624.     // match the actual brute tag that we're in. So we
  625.     // don't have to check if the end SCRIPT or STYLE tag matches the
  626.     // corresponding start tag. (Other tags are passed as two text
  627.     // tags, one for the opening '<', and the other for the rest of
  628.     // the tag.)
  629.     if( GetParseState()->m_inJavaScript &&
  630.         pTag->type == GetParseState()->m_inJavaScript ){
  631.         WriteClosingScriptTag();
  632.         if ( GetParseState()->InBody() ){
  633.             PopParseState();
  634.         }
  635.         return retVal;
  636.     }
  637.     if( BitSet( edt_setTextContainer, pTag->type ) || BitSet( edt_setList, pTag->type ) ){
  638.         GetParseState()->bLastWasSpace = TRUE;
  639.     }
  640.  
  641.     while( pPop && pTag->type != pPop->GetType() ){
  642.         pPop = pPop->GetParent();
  643.     }
  644.  
  645.     // if this is character formatting, pop the stack.
  646.     if( pTag->type == P_CENTER || pTag->type == P_DIVISION ){
  647.         if( !GetParseState()->m_formatAlignStack.IsEmpty() ){
  648.             GetParseState()->m_formatAlignStack.Pop();
  649.         }
  650.         // If we are closing alignment, it forces a line break (or new
  651.         //  paragraph)
  652.         if( m_pCreationCursor->IsContainer() ){
  653.             m_pCreationCursor = m_pCreationCursor->GetParent();
  654.         }
  655.         GetParseState()->bLastWasSpace = TRUE;
  656.  
  657.         // And for divisions, pop the division information.
  658.         if( pTag->type == P_DIVISION && pPop && pTag->type == pPop->GetType() && pPop->GetParent() != 0 ) {
  659.             m_pCreationCursor = pPop->GetParent();
  660.         }
  661.     }
  662.     else if( BitSet( edt_setCharFormat, pTag->type ) && !GetParseState()->m_formatTypeStack.IsEmpty()){
  663.         // The navigator is forgiving about the nesting of style end tags, e.g. </b> vs. </i>.
  664.         // So you can say, for example,
  665.         // <b>foo<i>bar</b>baz</i> and it will work as if you had said:
  666.         // <b>foo<i>bar</i>baz</b>.
  667.  
  668.         // LTNOTE: This crashes sometimes??
  669.         //delete GetParseState()->m_pNextText;
  670.         GetParseState()->m_pNextText = 0;
  671.         GetParseState()->m_formatTypeStack.Pop();
  672.         GetParseState()->m_pNextText = GetParseState()->m_formatTextStack.Pop();
  673.  
  674.         // </a> pops all the HREFs. So if you say:
  675.         // <a href="a">foo<a href="b">bar</a>baz, it's as if you said:
  676.         // <a href="a">foo</a><a href="b">bar</a>baz
  677.         // For fun, try this:
  678.         // <a href="a">foo<a name="b">bar</a>baz
  679.         // The 'bar' will look like it is a link, but it won't act like a link if you
  680.         // press on it.
  681.         // Anyway, we emulate this mess by clearing the hrefs out of the format text stack when we
  682.         // close any P_ANCHOR tag
  683.  
  684.         if ( pTag->type == P_ANCHOR ) {
  685.             if ( GetParseState()->m_pNextText ) {
  686.                 GetParseState()->m_pNextText->SetHREF( ED_LINK_ID_NONE );
  687.             }
  688.             TXP_PtrStack_CEditTextElement& textStack = GetParseState()->m_formatTextStack;
  689.             for ( int i = 0; i < textStack.Size(); i++) {
  690.                 if ( textStack[i] ) {
  691.                     textStack[i]->SetHREF( ED_LINK_ID_NONE );
  692.                 }
  693.             }
  694.         }
  695.     }
  696.     else if( pTag->type == P_PARAGRAPH ) {
  697.         if( pPop && pTag->type == pPop->GetType() && pPop->IsContainer() ) {
  698.             CEditContainerElement *pCont = pPop->Container();
  699.             if ( pCont ) {
  700.                 pCont->m_bHasEndTag = TRUE;
  701.             }
  702.         }
  703.  
  704.         if( pPop && pTag->type == pPop->GetType() && pPop->GetParent() != 0 ) {
  705.             m_pCreationCursor = pPop->GetParent();
  706.         }
  707.     }
  708.     else if( pPop && pTag->type == pPop->GetType() && pPop->GetParent() != 0 ) {
  709.         m_pCreationCursor = pPop->GetParent();
  710.     }
  711.     else{
  712.         // We have an error.  Ignore it for now.
  713.         //DebugBreak();
  714.     }
  715.     if ( pPop && (pTag->type == P_TABLE_HEADER || pTag->type == P_TABLE_DATA || pTag->type == P_CAPTION) ) {
  716.         PopParseState();
  717.     }
  718.     return retVal;
  719. }
  720.  
  721. void CEditBuffer::WriteClosingScriptTag()
  722. {
  723.     // Write out the closing Script tag.
  724.     TagType type = GetParseState()->m_inJavaScript;
  725.     if ( ! type ) return;
  726.     char* tagName =  EDT_TagString(type);
  727.     CStreamOutMemory* pOut = GetParseState()->GetStream();
  728.     pOut->Write( "</", 2);
  729.     pOut->Write( tagName, XP_STRLEN(tagName));
  730.     pOut->Write( ">\n", 2);
  731.     GetParseState()->m_inJavaScript = 0;
  732.     if ( GetParseState()->InBody() ){
  733.         RecordJavaScriptAsUnknownTag(pOut);
  734.     }
  735. }
  736.  
  737. PRIVATE void edt_MakeAbsoluteUsingBaseTag(CEditElement *pCreationCursor,char **ppURL) {
  738.   // Don't do anything if NULL.
  739.   if (!(ppURL && *ppURL)) {
  740.     return;
  741.   }
  742.  
  743.   // Find enclosing mail quote.
  744.   CEditListElement *pMQuote = pCreationCursor->GetMailQuote();
  745.   if (pMQuote) {
  746.     EDT_ListData *pListData = pMQuote->GetData();
  747.     if (pListData) {
  748.       // If mail quote has a <BASE HREF=> tag.
  749.       if (pListData->pBaseURL && *pListData->pBaseURL) {
  750.         char *pAbs = NET_MakeAbsoluteURL(pListData->pBaseURL,*ppURL);
  751.         if (pAbs) {
  752.           XP_FREE(*ppURL);
  753.           *ppURL = pAbs;
  754.         }
  755.       }
  756.       CEditListElement::FreeData(pListData);
  757.     }
  758.   }
  759. }
  760.  
  761. intn CEditBuffer::ParseOpenTag(pa_DocData *pData, PA_Tag* pTag, intn status){
  762.     char *pStr;
  763.     CEditElement* pElement = 0;
  764.     CEditElement *pContainer;
  765.     PA_Block buff;
  766.     intn retVal = OK_CONTINUE;
  767.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  768.     int16 win_csid = INTL_GetCSIWinCSID(c);
  769.  
  770.     //
  771.     // make sure all solo tags are placed in a paragraph.
  772.     //
  773.     if( pTag->type != P_TEXT
  774.             && (BitSet( edt_setSoloTags,  pTag->type  )
  775.                     || BitSet( edt_setUnsupported,  pTag->type ) )
  776.             && !BitSet( edt_setTextContainer,  m_pCreationCursor->GetType()  )
  777.             && m_pCreationCursor->FindContainer() == 0 ){
  778.         //m_pCreationCursor = new CEditElement(m_pCreationCursor,P_PARAGRAPH);
  779.         m_pCreationCursor = CEditContainerElement::NewDefaultContainer( m_pCreationCursor,
  780.                     GetCurrentAlignment() );
  781.  
  782.     }
  783.     if ( BitSet( edt_setUnsupported, pTag->type ) ) {
  784.         ParseUnsupportedTag(pTag, pElement, retVal);
  785.     }
  786.     else
  787.         switch(pTag->type){
  788.  
  789.         case P_LINK:
  790.             ParseLink(pTag, pElement, retVal);
  791.             break;
  792.  
  793.         case P_TEXT:
  794.             if( pTag->data ){
  795.                 PA_LOCK(pStr, char*, pTag->data);
  796.  
  797.                 if( GetParseState()->m_bInTitle ){
  798.                     AppendTitle( pStr );
  799.                     PA_UNLOCK( pTag->data );
  800.                     return retVal;
  801.                 }
  802.                 else if( GetParseState()->m_inJavaScript ){
  803.                     GetParseState()->GetStream()->Write( pStr, XP_STRLEN( pStr ) );
  804.                     PA_UNLOCK( pTag->data );
  805.                     return OK_IGNORE;
  806.                 }
  807.                 else if( !BitSet( edt_setFormattedText, m_pCreationCursor->GetType() )
  808. #ifdef USE_SCRIPT
  809.                         && !(GetParseState()->m_pNextText->m_tf & (TF_SERVER|TF_SCRIPT|TF_STYLE))
  810. #endif
  811.                         && !m_pCreationCursor->InFormattedText() ){
  812.                     NormalizeText( pStr );
  813.                 }
  814.                 else {
  815.                     if( !m_bInPreformat ){
  816.                         //
  817.                         // calls EDT_ProcessTag iteratively
  818.                         //
  819.                         return NormalizePreformatText( pData, pTag, status );
  820.                     }
  821.                 }
  822.                 // we probably need to adjust the length in the parse tag.
  823.                 //  it works without it, but I'm not sure why.
  824.  
  825.                 // if the text didn't get reduced away.
  826.                 // Strip white space in tables
  827.                 XP_Bool bGoodText = *pStr && ! (( XP_IS_SPACE(*pStr) && pStr[1] == '\0' ) &&
  828.                     BitSet( edt_setIgnoreWhiteSpace,  m_pCreationCursor->GetType()));
  829.                 if( bGoodText ){
  830.                     // if this text is not within a container, make one.
  831.                     if( !BitSet( edt_setTextContainer,  m_pCreationCursor->GetType()  )
  832.                             && m_pCreationCursor->FindContainer() == 0 ){
  833.                         m_pCreationCursor = CEditContainerElement::NewDefaultContainer( m_pCreationCursor,
  834.                                 GetCurrentAlignment() );
  835.                     }
  836.                     CEditTextElement *pNew = GetParseState()->m_pNextText->CopyEmptyText(m_pCreationCursor);
  837.                     pNew->SetText( pStr, IsMultiSpaceMode(), win_csid );
  838.  
  839.                     // create a new text element.  It adds itself as a child
  840.                     pElement = pNew;
  841.                 }
  842.                 else {
  843.                     retVal =  OK_IGNORE;
  844.                 }
  845.                 PA_UNLOCK( pTag->data );
  846.             }
  847.             break;
  848.  
  849.         //
  850.         // Paragraphs are always inserted at the level of the current
  851.         //  container.
  852.         //
  853.         case P_HEADER_1:
  854.         case P_HEADER_2:
  855.         case P_HEADER_3:
  856.         case P_HEADER_4:
  857.         case P_HEADER_5:
  858.         case P_HEADER_6:
  859.         case P_DESC_TITLE:
  860.         case P_ADDRESS:
  861.         case P_LIST_ITEM:
  862.         case P_DESC_TEXT:
  863.         case P_PARAGRAPH:
  864.         ContainerCase:
  865.             pContainer = m_pCreationCursor->FindContainerContainer();
  866.             if( pContainer ){
  867.                 m_pCreationCursor = pContainer;
  868.             }
  869.             else {
  870.                 m_pCreationCursor = m_pRoot;
  871.             }
  872.             m_pCreationCursor = pElement = new CEditContainerElement( m_pCreationCursor,
  873.                     pTag, GetRAMCharSetID(), GetCurrentAlignment() );
  874.             break;
  875.  
  876.         case P_UNUM_LIST:
  877.         case P_NUM_LIST:
  878.         case P_DESC_LIST:
  879.         case P_MENU:
  880.         case P_DIRECTORY:
  881.         case P_BLOCKQUOTE:
  882.         case P_MQUOTE: // Mailing quotation.
  883.             pContainer = m_pCreationCursor->FindContainerContainer();
  884.             if( pContainer ){
  885.                 m_pCreationCursor = pContainer;
  886.             }
  887.             else {
  888.                 m_pCreationCursor = m_pRoot;
  889.             }
  890.             m_pCreationCursor = pElement = new CEditListElement( m_pCreationCursor, pTag, GetRAMCharSetID());
  891.             break;
  892.  
  893.         case P_BODY:
  894.             ParseBodyTag(pTag);
  895.             break;
  896.  
  897.         case P_HEAD:
  898.             // Silently eat this tag.
  899.             break;
  900.  
  901.     // character formatting
  902.         case P_STYLE:
  903.         case P_SCRIPT:
  904.         case P_SERVER:
  905.             if( GetParseState()->InBody() ){
  906.                 PushParseState();
  907.             }
  908.             RecordTag(pTag, FALSE);
  909.             GetParseState()->m_inJavaScript = pTag->type;
  910.             return OK_IGNORE;
  911.             /*
  912.             // Save tag parameters.
  913.             if ( GetParseState()->m_pNextText ) {
  914.                char *locked_buff;
  915.                PA_LOCK(locked_buff, char *, pTag->data );
  916.                GetParseState()->m_pNextText->SetScriptExtra(locked_buff);
  917.                PA_UNLOCK(pTag->data);
  918.             }
  919.             m_preformatLinePos = 0;
  920.             // intentionally fall through.
  921.             */
  922.         case P_BOLD:
  923.         case P_STRONG:
  924.         case P_ITALIC:
  925.         case P_EMPHASIZED:
  926.         case P_VARIABLE:
  927.         case P_CITATION:
  928.         case P_FIXED:
  929.         case P_CODE:
  930.         case P_KEYBOARD:
  931.         case P_SAMPLE:
  932.         case P_SUPER:
  933.         case P_SUB:
  934.         case P_NOBREAK:
  935.         case P_STRIKEOUT:
  936.         case P_SPELL:
  937.         case P_INLINEINPUT:
  938.         case P_INLINEINPUTTHICK:
  939.         case P_INLINEINPUTDOTTED:
  940.         case P_STRIKE:
  941.         case P_UNDERLINE:
  942.         case P_BLINK:
  943.             GetParseState()->m_formatTextStack.Push(GetParseState()->m_pNextText);
  944.             GetParseState()->m_formatTypeStack.Push(pTag->type);
  945.             GetParseState()->m_pNextText = GetParseState()->m_pNextText->CopyEmptyText();
  946.             GetParseState()->m_pNextText->m_tf |= edt_TagType2TextFormat(pTag->type);
  947.             break;
  948.  
  949.         case P_SMALL:
  950.             GetParseState()->m_formatTextStack.Push(GetParseState()->m_pNextText);
  951.             GetParseState()->m_formatTypeStack.Push(pTag->type);
  952.             GetParseState()->m_pNextText = GetParseState()->m_pNextText->CopyEmptyText();
  953.             if( GetParseState()->m_pNextText->m_tf & TF_FONT_SIZE ){
  954.                 GetParseState()->m_pNextText->SetFontSize(GetParseState()->m_pNextText->GetFontSize()-1);
  955.             }
  956.             else {
  957.                 GetParseState()->m_pNextText->SetFontSize(m_pCreationCursor->GetDefaultFontSize()-1);
  958.             }
  959.             break;
  960.  
  961.         case P_BIG:
  962.             GetParseState()->m_formatTextStack.Push(GetParseState()->m_pNextText);
  963.             GetParseState()->m_formatTypeStack.Push(pTag->type);
  964.             GetParseState()->m_pNextText = GetParseState()->m_pNextText->CopyEmptyText();
  965.             if( GetParseState()->m_pNextText->m_tf & TF_FONT_SIZE ){
  966.                 GetParseState()->m_pNextText->SetFontSize(GetParseState()->m_pNextText->GetFontSize()+1);
  967.             }
  968.             else {
  969.                 GetParseState()->m_pNextText->SetFontSize(m_pCreationCursor->GetDefaultFontSize()+1);
  970.             }
  971.             break;
  972.  
  973.         case P_BASEFONT:
  974.              GetParseState()->m_baseFontSize = edt_FetchParamInt(pTag, PARAM_SIZE, 3, GetRAMCharSetID());
  975.              GetParseState()->m_pNextText->SetFontSize(GetParseState()->m_baseFontSize);
  976.             break;
  977.  
  978.         case P_FONT:
  979.             {
  980.                 GetParseState()->m_formatTextStack.Push(GetParseState()->m_pNextText);
  981.                 GetParseState()->m_formatTypeStack.Push(pTag->type);
  982.                 GetParseState()->m_pNextText = GetParseState()->m_pNextText->CopyEmptyText();
  983.                 buff = PA_FetchParamValue(pTag, PARAM_SIZE, win_csid);
  984.                 if (buff != NULL) {
  985.                     char *size_str;
  986.                     PA_LOCK(size_str, char *, buff);
  987.                     GetParseState()->m_pNextText->SetFontSize(LO_ChangeFontSize(
  988.                             GetParseState()->m_baseFontSize, size_str ));
  989.                     PA_UNLOCK(buff);
  990.                     PA_FREE(buff);
  991.                 }
  992.                 buff = PA_FetchParamValue(pTag, PARAM_FACE, win_csid);
  993.                 if (buff != NULL)
  994.                 {
  995.                     char* str;
  996.                     PA_LOCK(str, char *, buff);
  997.                     GetParseState()->m_pNextText->SetFontFace(str);
  998.                     PA_UNLOCK(buff);
  999.                     PA_FREE(buff);
  1000.                 }
  1001.  
  1002.                 int16 iWeight = (int16) edt_FetchParamInt(pTag, PARAM_FONT_WEIGHT, ED_FONT_WEIGHT_NORMAL, GetRAMCharSetID());
  1003.                 if (iWeight != ED_FONT_WEIGHT_NORMAL)
  1004.                 {
  1005.                     GetParseState()->m_pNextText->SetFontWeight(iWeight);
  1006.                 }
  1007.  
  1008.                 int16 iPointSize = (int16) edt_FetchParamInt(pTag, PARAM_POINT_SIZE, ED_FONT_POINT_SIZE_DEFAULT, GetRAMCharSetID());
  1009.                 if (iPointSize != ED_FONT_POINT_SIZE_DEFAULT)
  1010.                 {
  1011.                     GetParseState()->m_pNextText->SetFontPointSize(iPointSize);
  1012.                 }
  1013.  
  1014.                 ED_Color c = edt_FetchParamColor( pTag, PARAM_COLOR, GetRAMCharSetID() );
  1015.                 if( c.IsDefined() ){
  1016.                     GetParseState()->m_pNextText->SetColor( c );
  1017.                 }
  1018.             }
  1019.             break;
  1020.  
  1021.         case P_ANCHOR:
  1022.             // push the formatting stack in any case so we properly pop
  1023.             //  it in the future.
  1024.             GetParseState()->m_formatTextStack.Push(GetParseState()->m_pNextText);
  1025.             GetParseState()->m_formatTypeStack.Push(pTag->type);
  1026.             GetParseState()->m_pNextText = GetParseState()->m_pNextText->CopyEmptyText();
  1027.  
  1028.             pStr = edt_FetchParamString(pTag, PARAM_HREF, win_csid);
  1029.             if( pStr != NULL ){
  1030.                 // If in a mail quote with a <BASE> tag, make absolute.
  1031.                 edt_MakeAbsoluteUsingBaseTag(m_pCreationCursor,&pStr);
  1032.  
  1033.                 // collect extra stuff..
  1034.                 // LTNOTE:
  1035.                 char* pExtra = edt_FetchParamExtras( pTag, anchorHrefParams, win_csid );
  1036.                 ED_LinkId id = linkManager.Add( pStr, pExtra );
  1037.                 GetParseState()->m_pNextText->SetHREF( id );
  1038.                 linkManager.Free( id );         // reference counted.
  1039.                 XP_FREE( pStr );
  1040.                 XP_FREEIF( pExtra );
  1041.             }
  1042.  
  1043.             pStr = edt_FetchParamString(pTag, PARAM_NAME, win_csid);
  1044.             if( pStr ){
  1045.                 // We don't catch this case above because by default
  1046.                 //  an anchor is a character formatting
  1047.                 if( m_pCreationCursor->FindContainer() == 0 ){
  1048.                     m_pCreationCursor = CEditContainerElement::NewDefaultContainer(
  1049.                             m_pCreationCursor, GetCurrentAlignment() );
  1050.                 }
  1051.                 pElement = new CEditTargetElement(m_pCreationCursor, pTag);
  1052.                 m_pCreationCursor = pElement->GetParent();
  1053.                 XP_FREE( pStr );
  1054.             }
  1055.             break;
  1056.  
  1057.         case P_IMAGE:
  1058.         case P_NEW_IMAGE:
  1059.             {
  1060.                 pElement = new CEditImageElement(m_pCreationCursor, pTag, GetRAMCharSetID(),
  1061.                             GetParseState()->m_pNextText->GetHREF());
  1062.                 
  1063.                 // If in a mail quote with a <BASE> tag, make SRC and LOWSRC absolute.
  1064.                 EDT_ImageData *pImageData = pElement->Image()->GetImageData();
  1065.                 if (pImageData) {
  1066.                   // regular image
  1067.                   edt_MakeAbsoluteUsingBaseTag(m_pCreationCursor,&pImageData->pSrc);
  1068.                   // low resolution
  1069.                   edt_MakeAbsoluteUsingBaseTag(m_pCreationCursor,&pImageData->pLowSrc);
  1070.  
  1071.                   pElement->Image()->SetImageData(pImageData);
  1072.                   EDT_FreeImageData(pImageData);
  1073.                 }
  1074.  
  1075.                 m_pCreationCursor = pElement->GetParent();
  1076.                 GetParseState()->bLastWasSpace = FALSE;
  1077.                 break;
  1078.             }
  1079.  
  1080.         case P_HRULE:
  1081.             {
  1082.             pElement = new CEditHorizRuleElement(m_pCreationCursor, pTag);
  1083.             m_pCreationCursor = pElement->GetParent();
  1084.             GetParseState()->bLastWasSpace = TRUE;
  1085.             break;
  1086.             }
  1087.  
  1088.         case P_PLAIN_TEXT:
  1089.         case P_PLAIN_PIECE:
  1090.         case P_LISTING_TEXT:
  1091.         case P_PREFORMAT:
  1092.             pTag->type = P_PREFORMAT;
  1093.             m_preformatLinePos = 0;
  1094.             goto ContainerCase;
  1095.  
  1096.  
  1097.         //
  1098.         // pass these tags on so the document looks right
  1099.         //
  1100.         case P_CENTER:
  1101.             // Forces a new paragraph unless the current paragraph is empty
  1102.             if( m_pCreationCursor->IsContainer() && m_pCreationCursor->GetChild() ){
  1103.                 m_pCreationCursor = m_pCreationCursor->GetParent();
  1104.             }
  1105.             GetParseState()->m_formatAlignStack.Push(ED_ALIGN_CENTER);
  1106.             pContainer = m_pCreationCursor->FindContainer();
  1107.             if( pContainer ){
  1108.                 pContainer->Container()->AlignIfEmpty( ED_ALIGN_CENTER );
  1109.             }
  1110.             break;
  1111.  
  1112.         case P_DIVISION:
  1113.             // Forces a new paragraph unless the current paragraph is empty
  1114.             if( m_pCreationCursor->IsContainer() && m_pCreationCursor->GetChild() ){
  1115.                 m_pCreationCursor = m_pCreationCursor->GetParent();
  1116.             }
  1117.             {
  1118.                 pContainer = m_pCreationCursor->FindContainerContainer();
  1119.                 if( pContainer ){
  1120.                     m_pCreationCursor = pContainer;
  1121.                 }
  1122.                 else {
  1123.                     m_pCreationCursor = m_pRoot;
  1124.                 }
  1125.                 CEditDivisionElement* pDivision = new CEditDivisionElement( m_pCreationCursor, pTag, GetRAMCharSetID() );
  1126.  
  1127.                 // For historical reasons, we handle alignment in the formatAlignStack.
  1128.                 // It would probably be better to handle it in the DIV tag.
  1129.                 ED_Alignment eAlign = pDivision->GetAlignment();
  1130.                 if ( eAlign == ED_ALIGN_DEFAULT ) {
  1131.                     eAlign = ED_ALIGN_LEFT;
  1132.                 }
  1133.                 else {
  1134.                     pDivision->ClearAlignment(); 
  1135.                 }
  1136.                 GetParseState()->m_formatAlignStack.Push( eAlign );
  1137.                 pContainer = m_pCreationCursor->FindContainer();
  1138.                 if( pContainer ){
  1139.                     pContainer->Container()->AlignIfEmpty( eAlign );
  1140.                 }
  1141.                 // Create a division container.
  1142.                 m_pCreationCursor = pDivision;
  1143.                 break;
  1144.             }
  1145.  
  1146.         case P_LINEBREAK:
  1147.             {
  1148.             // Ignore <BR> after </LI>.
  1149.             if ( m_bLastTagIsEnd && BitSet( edt_setIgnoreBreakAfterClose, m_iLastTagType ) ) {
  1150.             }
  1151.             else {
  1152.                 pElement = new CEditBreakElement(m_pCreationCursor, pTag);
  1153.                 m_pCreationCursor = pElement->GetParent();
  1154.             }
  1155.             GetParseState()->bLastWasSpace = TRUE;
  1156.             break;
  1157.             }
  1158.  
  1159.         case P_TITLE:
  1160.             GetParseState()->m_bInTitle = TRUE;
  1161.             break;
  1162.  
  1163.         case P_META:
  1164.             ParseMetaTag( pTag );
  1165.             break;
  1166.  
  1167.         case P_BASE:
  1168.             // There are two known parameters in a BASE tag:
  1169.             // HREF which sets the base URL
  1170.             // TARGET which sets the default target for links.
  1171.             // We ignore HREF when it's not in a mail quotation.
  1172.             // We preserve TARGET as part of the document properties.
  1173.             {
  1174.                 char *pTarget = edt_FetchParamString(pTag,PARAM_TARGET,m_pCreationCursor->GetWinCSID());
  1175.                 if ( pTarget ) {
  1176.                     SetBaseTarget(pTarget);
  1177.                 }
  1178.                 XP_FREEIF(pTarget);
  1179.             }
  1180.             // We only deal with P_BASE tags for mail quotation.
  1181.             {
  1182.               // Look for enclosing mail quote CEditListElement and set pBaseURL to
  1183.               // be the HREF parameter of the <BASE> tag.
  1184.               CEditListElement *pMQuote = m_pCreationCursor->GetMailQuote();
  1185.               if (pMQuote) {
  1186.                 char *pHref = edt_FetchParamString(pTag,PARAM_HREF,m_pCreationCursor->GetWinCSID());
  1187.                 if (pHref) {
  1188.                   EDT_ListData *pData = pMQuote->GetData();
  1189.                   if (pData) {
  1190.                     XP_FREEIF(pData->pBaseURL);
  1191.                     pData->pBaseURL = pHref;
  1192.                     pMQuote->SetData(pData);
  1193.                     CEditListElement::FreeData(pData);
  1194.                   }
  1195.                   else {
  1196.                     XP_FREE(pHref);
  1197.                   }
  1198.                 }
  1199.               }
  1200.             }
  1201.             break;
  1202.  
  1203.         case P_TABLE:
  1204.             {
  1205.               pContainer = m_pCreationCursor->FindContainerContainer();
  1206.               if( pContainer ){
  1207.                   m_pCreationCursor = pContainer;
  1208.               }
  1209.               else {
  1210.                   m_pCreationCursor = m_pRoot;
  1211.               }
  1212.  
  1213.  
  1214.               // A table following a </P> should have an extra break before the table.  Add a NSDT.
  1215.               CEditElement *pPrev = m_pCreationCursor->GetLastChild();
  1216.               if (pPrev && pPrev->IsContainer()) {
  1217.                   CEditContainerElement *pCont = pPrev->Container();
  1218.                   if (pCont && pCont->GetType() == P_PARAGRAPH && pCont->m_bHasEndTag) {
  1219.                     CEditContainerElement *pNew;
  1220.                     pNew = CEditContainerElement::NewDefaultContainer( m_pCreationCursor, pCont->GetAlignment() );
  1221.                     // g++ thinks the following value is not used, but it's mistaken.
  1222.                     // The constructor auto-inserts the element into the tree.
  1223.                     (void) new CEditTextElement(pNew, 0);
  1224.                   }
  1225.               }
  1226.  
  1227.  
  1228.               m_pCreationCursor = pElement = new CEditTableElement( m_pCreationCursor, pTag, GetRAMCharSetID(), GetCurrentAlignment() );
  1229.  
  1230.               // If in a mail quote with a <BASE> tag, make table background absolute.
  1231.               EDT_TableData *pData;
  1232.               if (pElement && NULL != (pData = CEditTableElement::Cast(pElement)->GetData())) {
  1233.                 edt_MakeAbsoluteUsingBaseTag(m_pCreationCursor,&pData->pBackgroundImage);
  1234.                 CEditTableElement::Cast(pElement)->SetData(pData);
  1235.                 CEditTableElement::FreeData(pData);
  1236.               }
  1237.             }
  1238.  
  1239.             break;
  1240.  
  1241.         case P_TABLE_ROW:
  1242.             {
  1243.                 CEditTableElement* pTable = m_pCreationCursor->GetTable();
  1244.                 if ( ! pTable ){
  1245.                     // We might be in a TD that someone forgot to close.
  1246.                     CEditTableCellElement* pCell = m_pCreationCursor->GetTableCell();
  1247.                     if ( pCell ){
  1248.                         pTable = pCell->GetTable();
  1249.                     }
  1250.                 }
  1251.                 if ( pTable ) {
  1252.                     m_pCreationCursor = pElement = new CEditTableRowElement( pTable, pTag, GetRAMCharSetID() );
  1253.  
  1254.                     // If in a mail quote with a <BASE> tag, make table background absolute.
  1255.                     EDT_TableRowData *pData;
  1256.                     if (pElement && NULL != (pData = CEditTableRowElement::Cast(pElement)->GetData())) {
  1257.                       edt_MakeAbsoluteUsingBaseTag(m_pCreationCursor,&pData->pBackgroundImage);
  1258.                       CEditTableRowElement::Cast(pElement)->SetData(pData);
  1259.                       CEditTableRowElement::FreeData(pData);
  1260.                     }
  1261.                 }
  1262.                 else {
  1263.                     XP_TRACE(("Ignoring table row. Not in table."));
  1264.                 }
  1265.             }
  1266.             break;
  1267.  
  1268.         case P_TABLE_HEADER:
  1269.         case P_TABLE_DATA:
  1270.             {
  1271.                 CEditTableElement* pTable = m_pCreationCursor->GetTable();
  1272.                 if ( ! pTable ){
  1273.                     // We might be in a TD that someone forgot to close.
  1274.                     CEditTableCellElement* pCell = m_pCreationCursor->GetTableCell();
  1275.                     if ( pCell ){
  1276.                         m_pCreationCursor = pCell->GetParent();
  1277.                         pTable = m_pCreationCursor->GetTable();
  1278.                     }
  1279.                 }
  1280.                 if ( ! pTable ) {
  1281.                     XP_TRACE(("Ignoring table cell. Not in table."));
  1282.                 }
  1283.                 else {
  1284.                     CEditTableRowElement* pTableRow = m_pCreationCursor->GetTableRow();
  1285.                     if ( ! pTableRow ){
  1286.                         // They forgot to put in a table row.
  1287.                         // Create one for them.
  1288.  
  1289.                         pTableRow = new CEditTableRowElement();
  1290.                         pTableRow->InsertAsLastChild(pTable);
  1291.                     }
  1292.                     m_pCreationCursor = pElement = new CEditTableCellElement( pTableRow, pTag, GetRAMCharSetID() );
  1293.  
  1294.                     // If in a mail quote with a <BASE> tag, make table background absolute.
  1295.                     EDT_TableCellData *pData;
  1296.                     if (pElement && NULL != (pData = CEditTableCellElement::Cast(pElement)->GetData())) {
  1297.                       edt_MakeAbsoluteUsingBaseTag(m_pCreationCursor,&pData->pBackgroundImage);
  1298.                       CEditTableCellElement::Cast(pElement)->SetData(pData);
  1299.                       CEditTableCellElement::FreeData(pData);
  1300.                     }
  1301.  
  1302.                     PushParseState();
  1303.                 }
  1304.             }
  1305.             break;
  1306.  
  1307.         case P_CAPTION:
  1308.             {
  1309.                 CEditTableElement* pTable = m_pCreationCursor->GetTable();
  1310.                 if ( pTable ) {
  1311.                     m_pCreationCursor = pElement = new CEditCaptionElement( pTable, pTag, GetRAMCharSetID() );
  1312.                     PushParseState();
  1313.                 }
  1314.                 else {
  1315.                     XP_TRACE(("Ignoring caption. Not in a table."));
  1316.                 }
  1317.             }
  1318.             break;
  1319.  
  1320.         case P_LAYER:
  1321.             pContainer = m_pCreationCursor->FindContainerContainer();
  1322.             if( pContainer ){
  1323.                 m_pCreationCursor = pContainer;
  1324.             }
  1325.             else {
  1326.                 m_pCreationCursor = m_pRoot;
  1327.             }
  1328.             m_pCreationCursor = new CEditLayerElement( m_pCreationCursor, pTag, GetRAMCharSetID() );
  1329.             retVal =  OK_IGNORE; // Remove this when we can display layers in the editor
  1330.             break;
  1331.  
  1332.         // unimplemented Tags
  1333.  
  1334.         case P_MAX:
  1335.             m_pCreationCursor = pElement = CreateElement( pTag, m_pCreationCursor );
  1336.             if( BitSet( edt_setSoloTags, m_pCreationCursor->GetType() ) ){
  1337.                 m_pCreationCursor = m_pCreationCursor->GetParent();
  1338.             }
  1339.             break;
  1340.  
  1341.         //
  1342.         // someones added a new tag.  Inspect pTag and look in pa_tags.h
  1343.         // At the least, add it to edt_setUnsupported
  1344.         //
  1345.         default:
  1346.             XP_TRACE(("Someone added a new tag type %d.  Inspect pTag and look in pa_tags.h", pTag->type));
  1347.             XP_ASSERT(FALSE);
  1348.         case P_UNKNOWN:
  1349.             ParseUnsupportedTag(pTag, pElement, retVal);
  1350.             break;
  1351.  
  1352.         // its ok to ignore these tags.
  1353.         case P_NSCP_OPEN:
  1354.         case P_NSCP_CLOSE:
  1355.         case P_NSCP_REBLOCK:
  1356.         case P_HTML:
  1357.             break;
  1358.     }
  1359.     pTag->edit_element = pElement;
  1360.     return retVal;
  1361. }
  1362.  
  1363. void CEditBuffer::ParseLink(PA_Tag* pTag, CEditElement*& pElement, intn& retVal){
  1364.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  1365.     if ( GetParseState()->InBody() ){
  1366.         ParseUnsupportedTag(pTag, pElement, retVal);
  1367.         return;
  1368.     }
  1369.     char* pRel = edt_FetchParamString(pTag, PARAM_REL, INTL_GetCSIWinCSID(c));
  1370.     if ( pRel && XP_STRCASECMP(pRel, "FONTDEF") == 0 ) {
  1371.         ParseLinkFontDef(pTag, pElement, retVal); 
  1372.         return;
  1373.     }
  1374.     ParseUnsupportedTag(pTag, pElement, retVal);
  1375. }
  1376.  
  1377. void CEditBuffer::ParseLinkFontDef(PA_Tag* pTag, CEditElement*& /*pElement*/, intn& /*retVal*/){
  1378.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  1379.     if ( m_pFontDefURL ) {
  1380.         XP_FREE(m_pFontDefURL);
  1381.     }
  1382.     m_pFontDefURL = edt_FetchParamString(pTag, PARAM_SRC, INTL_GetCSIWinCSID(c));
  1383.     m_bFontDefNoSave = edt_FetchParamBoolExist(pTag, PARAM_NOSAVE, GetRAMCharSetID());
  1384. }
  1385.  
  1386.  
  1387. void CEditBuffer::ParseUnsupportedTag(PA_Tag* pTag, CEditElement*& pElement, intn& retVal){
  1388.     if ( IsDocTypeTag(pTag) ) {
  1389.         return; // Ignore document's idea about it's document type.
  1390.     }
  1391.  
  1392.     // If we don't understand it, and it's in the head, then assume that it needs to stay in the head.
  1393.     if ( !GetParseState()->InBody() ){
  1394.         RecordTag(pTag, TRUE);
  1395.         retVal = OK_IGNORE;
  1396.         return;
  1397.     }
  1398.  
  1399.     if( !m_pCreationCursor->IsContainer() && m_pCreationCursor->FindContainer() == 0 ){
  1400.         m_pCreationCursor = CEditContainerElement::NewDefaultContainer(
  1401.                 m_pCreationCursor, GetCurrentAlignment() );
  1402.     }
  1403.     if ( HandleSelectionComment(pTag, pElement, retVal) ){
  1404.         return;
  1405.     }
  1406.     m_pCreationCursor = pElement = new CEditIconElement( m_pCreationCursor,
  1407.         pTag->is_end ?
  1408.             EDT_ICON_UNSUPPORTED_END_TAG
  1409.         :
  1410.             EDT_ICON_UNSUPPORTED_TAG,
  1411.         pTag );
  1412.     m_pCreationCursor->Icon()->MorphTag( pTag );
  1413.     m_pCreationCursor = m_pCreationCursor->GetParent();
  1414.     return;
  1415. }
  1416.  
  1417. void CEditBuffer::RecordTag(PA_Tag* pTag, XP_Bool bWithLinefeed){
  1418.     CStreamOutMemory* pOut = GetParseState()->GetStream();
  1419.  
  1420.     // Save the tag and its parameters
  1421.     char* kScriptTag =  EDT_TagString(pTag->type);
  1422.     pOut->Write("<",1);
  1423.     if ( pTag->is_end ) {
  1424.         pOut->Write("/",1);
  1425.     }
  1426.     pOut->Write(kScriptTag, XP_STRLEN(kScriptTag));
  1427.  
  1428.     char *locked_buff;
  1429.     PA_LOCK(locked_buff, char *, pTag->data );
  1430.     pOut->Write(locked_buff, XP_STRLEN(locked_buff));
  1431.     PA_UNLOCK(pTag->data);
  1432.     if ( bWithLinefeed ) pOut->Write("\n", 1);
  1433. }
  1434.  
  1435. void CEditBuffer::RecordJavaScriptAsUnknownTag(CStreamOutMemory* pOut){
  1436.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  1437.     // Store the script in a Icon tag.
  1438.     PA_Tag dummyTag;
  1439.     XP_BZERO( &dummyTag, sizeof( dummyTag ) );
  1440.     dummyTag.type = P_UNKNOWN; 
  1441.     dummyTag.is_end = FALSE;
  1442.  
  1443.     if( !m_pCreationCursor->IsContainer() && m_pCreationCursor->FindContainer() == 0 ){
  1444.         m_pCreationCursor = CEditContainerElement::NewDefaultContainer(
  1445.                 m_pCreationCursor, GetCurrentAlignment() );
  1446.     }
  1447.     m_pCreationCursor = new CEditIconElement( m_pCreationCursor,
  1448.                 EDT_ICON_UNSUPPORTED_TAG,
  1449.                 &dummyTag );
  1450.     m_pCreationCursor->Icon()->MorphTag( &dummyTag );
  1451.     char* pData = edt_CopyFromHuge(INTL_GetCSIWinCSID(c), pOut->GetText(), pOut->GetLen(), NULL);
  1452.     m_pCreationCursor->Icon()->SetData( pData );
  1453.     m_pCreationCursor = m_pCreationCursor->GetParent();
  1454.     XP_FREE(pData);
  1455. }
  1456.  
  1457. void CEditBuffer::ParseBodyTag(PA_Tag *pTag){
  1458.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  1459.     GetParseState()->StartBody();
  1460.     // Allow multiple body tags -- don't replace parameter a new one isn't found.
  1461.     edt_FetchParamString2(pTag, PARAM_BACKGROUND, m_pBackgroundImage, INTL_GetCSIWinCSID(c));
  1462.     m_bBackgroundNoSave = edt_FetchParamBoolExist( pTag, PARAM_NOSAVE, GetRAMCharSetID() );
  1463.     edt_FetchParamColor2( pTag, PARAM_BGCOLOR, m_colorBackground, GetRAMCharSetID());
  1464.     edt_FetchParamColor2( pTag, PARAM_TEXT, m_colorText, GetRAMCharSetID() );
  1465.     edt_FetchParamColor2( pTag, PARAM_LINK, m_colorLink, GetRAMCharSetID() );
  1466.     edt_FetchParamColor2( pTag, PARAM_ALINK, m_colorActiveLink, GetRAMCharSetID() );
  1467.     edt_FetchParamColor2( pTag, PARAM_VLINK, m_colorFollowedLink, GetRAMCharSetID() );
  1468.     //m_pBody = m_pCreationCursor = pElement = CreateElement( pTag, m_pCreationCursor );
  1469.     edt_FetchParamExtras2( pTag, bodyParams, m_pBodyExtra, GetRAMCharSetID() );
  1470. }
  1471.  
  1472.  
  1473. CEditBuffer::CEditBuffer(MWContext *pContext):
  1474.         m_lifeFlag(0xbab3fac3),
  1475.         m_pContext(pContext),
  1476.         m_pRoot(0),
  1477.         m_pCurrent(0),
  1478.         m_iCurrentOffset(0),
  1479.         m_bCurrentStickyAfter(FALSE),
  1480.         m_pCreationCursor(0),
  1481.         m_colorText(ED_Color::GetUndefined()),
  1482.         m_colorBackground(ED_Color::GetUndefined()),
  1483.         m_colorLink(ED_Color::GetUndefined()),
  1484.         m_colorFollowedLink(ED_Color::GetUndefined()),
  1485.         m_colorActiveLink(ED_Color::GetUndefined()),
  1486.         m_pTitle(0),
  1487.         m_pBackgroundImage(0),
  1488.         m_bBackgroundNoSave(FALSE),
  1489.         m_pFontDefURL(0),
  1490.         m_bFontDefNoSave(FALSE),
  1491.         m_pBodyExtra(0),
  1492.         m_pLoadingImage(0),
  1493.         m_pSaveObject(0),
  1494.         m_bMultiSpaceMode(TRUE),
  1495.         m_hackFontSize(0),
  1496.         m_iDesiredX(-1),
  1497.         m_lastTopY(0),
  1498.         m_inScroll(FALSE),
  1499.         m_bBlocked(TRUE),
  1500.         m_bSelecting(0),
  1501.         m_bNoRelayout(FALSE),
  1502.         m_pSelectStart(0),
  1503.         m_iSelectOffset(0),
  1504.         m_preformatLinePos(0),
  1505.         m_bInPreformat(FALSE),
  1506.         printState(),
  1507.         linkManager(),
  1508.         m_metaData(),
  1509.         m_parseStateStack(),
  1510.         m_status(ED_ERROR_NONE),
  1511.         m_pCommandLog(0),
  1512.         m_bTyping(FALSE),
  1513.         m_pStartSelectionAnchor(0),
  1514.         m_pEndSelectionAnchor(0),
  1515.         m_bStartSelectionStickyAfter(0),
  1516.         m_bEndSelectionStickyAfter(0),
  1517.         m_bLayoutBackpointersDirty(TRUE),
  1518.         m_iFileWriteTime(0),
  1519. #ifdef DEBUG
  1520.         m_pTestManager(0),
  1521.         m_iSuppressPhantomInsertPointCheck(0),
  1522.         m_bSkipValidation(0),
  1523. #endif
  1524.         m_pSizingObject(0),
  1525.         m_finishLoadTimer(),
  1526.         m_relayoutTimer(),
  1527.         m_autoSaveTimer(),
  1528.         m_bDisplayTables(TRUE),
  1529.         m_bDummyCharacterAddedDuringLoad(FALSE),
  1530.         m_bReady(0),
  1531.         m_bPasteQuoteMode(FALSE),
  1532.         m_bPasteHTML(FALSE),
  1533.         m_bPasteHTMLWhenSavingDocument(FALSE),
  1534.         m_pPasteHTMLModeText(0),
  1535.         m_pPasteTranscoder(0),
  1536.         m_bForceDocCSID(FALSE),
  1537.         m_forceDocCSID(0),
  1538.         m_originalWinCSID(0),
  1539.         m_pSelectedLoTable(0),
  1540.         m_pSelectedEdTable(0),
  1541.         m_iNextSelectedCell(0),
  1542.         m_TableHitType(ED_HIT_NONE),
  1543.         m_pSelectedTableElement(0),
  1544.         m_pPrevExtendSelectionCell(0),
  1545.         m_bEncrypt(PR_FALSE),
  1546.         m_pNonTextSelectedTable(0)
  1547. {
  1548.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  1549.     m_originalWinCSID = INTL_GetCSIWinCSID(c);
  1550.     m_pCommandLog = CGlobalHistoryGroup::GetGlobalHistoryGroup()->CreateLog(this);
  1551.     m_pStartSelectionAnchor = NULL;
  1552.     m_pEndSelectionAnchor = NULL;
  1553.     m_bStartSelectionStickyAfter = FALSE;
  1554.     m_bEndSelectionStickyAfter = FALSE;
  1555.     m_iLastTagType = -1;
  1556.     m_bLastTagIsEnd = FALSE;
  1557.     m_pBaseTarget = 0;
  1558.  
  1559.     // create a root element
  1560.     edt_InitBitArrays();
  1561.     m_pCreationCursor = m_pRoot = new CEditRootDocElement( this );
  1562.     ResetParseStateStack();
  1563.  
  1564. #ifdef DEBUG
  1565.     m_pTestManager = new CEditTestManager(this);
  1566.     m_iSuppressPhantomInsertPointCheck = FALSE;
  1567.     m_bSkipValidation = FALSE;
  1568. #endif
  1569.  
  1570.     m_relayoutTimer.SetEditBuffer(this);
  1571.     m_autoSaveTimer.SetEditBuffer(this);
  1572. #ifdef DEBUG_AUTO_SAVE
  1573.     m_autoSaveTimer.SetPeriod(1);
  1574. #endif
  1575.  
  1576.     m_pContext->display_table_borders = TRUE; // On by default.
  1577. }
  1578.  
  1579.  
  1580. CEditBuffer::~CEditBuffer(){
  1581.     m_lifeFlag = 0;
  1582.  
  1583.     if ( m_pContext ) {
  1584.         int32 doc_id = XP_DOCID(m_pContext);
  1585.         lo_TopState* top_state = lo_FetchTopState(doc_id);
  1586.         if (top_state != NULL )
  1587.         {
  1588.             top_state->edit_buffer = 0;
  1589.         }
  1590.     }
  1591. #ifdef DEBUG
  1592.     delete m_pTestManager;
  1593. #endif
  1594.     delete m_pRoot;
  1595.     m_pRoot = NULL;
  1596.     // Delete meta data
  1597.     for(intn i = 0; i < m_metaData.Size(); i++ ) {
  1598.         FreeMetaData(m_metaData[i]);
  1599.     }
  1600.     ResetParseStateStack();
  1601.     CGlobalHistoryGroup::GetGlobalHistoryGroup()->DeleteLog(this);
  1602.     delete m_pPasteHTMLModeText;
  1603.     delete m_pPasteTranscoder;
  1604.     XP_FREEIF(m_pBaseTarget);
  1605. }
  1606.  
  1607. XP_Bool CEditBuffer::IsAlive(CEditBuffer* pBuffer){
  1608.     return pBuffer && pBuffer->m_lifeFlag == 0xbab3fac3;
  1609. }
  1610.  
  1611. XP_Bool CEditBuffer::IsReady(){
  1612.     return m_bReady;
  1613. }
  1614.  
  1615. void CEditBuffer::FixupInsertPoint(){
  1616.     CEditInsertPoint ip(m_pCurrent, m_iCurrentOffset, m_bCurrentStickyAfter);
  1617.     FixupInsertPoint(ip);
  1618.     m_pCurrent = ip.m_pElement;
  1619.     m_iCurrentOffset = ip.m_iPos;
  1620.     m_bCurrentStickyAfter = ip.m_bStickyAfter;
  1621.     XP_ASSERT(m_bCurrentStickyAfter == TRUE || m_bCurrentStickyAfter == FALSE);
  1622. }
  1623.  
  1624. void CEditBuffer::FixupInsertPoint(CEditInsertPoint& ip){
  1625.     if( ip.m_iPos == 0 && ! IsPhantomInsertPoint(ip) ){
  1626.         CEditLeafElement *pPrev = ip.m_pElement->PreviousLeafInContainer();
  1627.         if( pPrev && pPrev->GetLen() != 0 ){
  1628.             ip.m_pElement = pPrev;
  1629.             ip.m_iPos = pPrev->GetLen();
  1630.         }
  1631.     }
  1632.     // Since we fake up spaces at the beginning of paragraph, we might get
  1633.     //  an offset of 1 (after the fake space).
  1634.     else if( ip.m_pElement->GetLen() == 0 ){
  1635.         ip.m_iPos = 0;
  1636.     }
  1637. }
  1638.  
  1639. void CEditBuffer::SetInsertPoint( CEditLeafElement* pElement, int iOffset, XP_Bool bStickyAfter ){
  1640. #ifdef LAYERS
  1641.     LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  1642. #else
  1643.     LO_StartSelectionFromElement( m_pContext, 0, 0);
  1644. #endif
  1645.     m_pCurrent = pElement;
  1646.     m_iCurrentOffset = iOffset;
  1647.     m_bCurrentStickyAfter = bStickyAfter;
  1648.     XP_ASSERT(m_bCurrentStickyAfter == TRUE || m_bCurrentStickyAfter == FALSE);
  1649.     SetCaret();
  1650. }
  1651.  
  1652. //
  1653. // Returns true if the insert point doesn't really exist.
  1654. //
  1655. XP_Bool CEditBuffer::IsPhantomInsertPoint(){
  1656.     if( !IsSelected() ) {
  1657.         CEditInsertPoint ip(m_pCurrent, m_iCurrentOffset, m_bCurrentStickyAfter);
  1658.         return IsPhantomInsertPoint(ip);
  1659.     }
  1660.     else {
  1661.         return FALSE;
  1662.     }
  1663. }
  1664.  
  1665. XP_Bool CEditBuffer::IsPhantomInsertPoint(CEditInsertPoint& ip){
  1666.     if(  ip.m_pElement
  1667.             && ip.m_pElement->IsA( P_TEXT )
  1668.             && ip.m_pElement->Text()->GetLen() == 0
  1669.             && !( ip.m_pElement->IsFirstInContainer()
  1670.                     && ip.m_pElement->LeafInContainerAfter() == 0 )
  1671.                 ){
  1672.         return TRUE;
  1673.     }
  1674.     else {
  1675.         return FALSE;
  1676.     }
  1677. }
  1678.  
  1679. //
  1680. // Force the insertpoint to be a real insert point so we can do something.
  1681. //
  1682. void CEditBuffer::ClearPhantomInsertPoint(){
  1683.     if( IsPhantomInsertPoint() ){
  1684.         CEditLeafElement *pPrev = m_pCurrent->PreviousLeafInContainer();
  1685.         if( pPrev ){
  1686.             m_pCurrent = pPrev;
  1687.             m_iCurrentOffset = m_pCurrent->GetLen();
  1688.             m_bCurrentStickyAfter = FALSE;
  1689.         }
  1690.         else {
  1691.             CEditLeafElement *pNext = m_pCurrent->LeafInContainerAfter();
  1692.             if( pNext ){
  1693.                 m_pCurrent = pNext;
  1694.                 m_iCurrentOffset = 0;
  1695.                 m_bCurrentStickyAfter = FALSE;
  1696.            }
  1697.             else {
  1698.                 XP_ASSERT(FALSE);
  1699.             }
  1700.         }
  1701.         Reduce( m_pCurrent->FindContainer() );
  1702.     }
  1703. }
  1704.  
  1705. XP_Bool CEditBuffer::GetDirtyFlag(){
  1706.     return GetCommandLog()->IsDirty();
  1707. }
  1708.  
  1709. void CEditBuffer::DocumentStored(){
  1710.     DoneTyping();
  1711.     GetCommandLog()->DocumentStored();
  1712. }
  1713.  
  1714. CEditElement* CEditBuffer::FindRelayoutStart( CEditElement *pStartElement ){
  1715.     CEditElement* pOldElement = NULL;
  1716.     while ( pStartElement && pStartElement != pOldElement ) {
  1717.         pOldElement = pStartElement;
  1718.         CEditElement* pTable = pStartElement->GetTopmostTableOrLayer();
  1719.         if ( m_bDisplayTables && pTable ) {
  1720.             // If this is in a table, skip before it.
  1721.             pStartElement = pTable->PreviousLeaf();
  1722.         }
  1723.         else if( !pStartElement->IsLeaf() ){
  1724.             pStartElement = pStartElement->PreviousLeaf();
  1725.         }
  1726.         else if( pStartElement->Leaf()->GetLayoutElement() == 0 ){
  1727.            pStartElement = pStartElement->PreviousLeaf();
  1728.         }
  1729.     }
  1730.     return pStartElement;
  1731. }
  1732.  
  1733. static TXP_GrowableArray_LO_TableStruct edt_RelayoutTables;
  1734.  
  1735. // Add to current list of tables being relayed out
  1736. void EDT_AddToRelayoutTables(MWContext * /*pMWContext*/, LO_TableStruct *pLoTable )
  1737. {
  1738.     if(pLoTable)
  1739.     {
  1740.         edt_RelayoutTables.Add(pLoTable);
  1741.     }
  1742. }
  1743.  
  1744. void EDT_FixupTableData(MWContext *pMWContext)
  1745. {
  1746.     GET_WRITABLE_EDIT_BUF_OR_RETURN(pMWContext, pEditBuffer);
  1747.     pEditBuffer->FixupTableData();
  1748. }
  1749.  
  1750. // Change Table and all cell width data to match
  1751. //  sizes calculated by Layout. Must do for all tables during Relayout()
  1752. //  else generated HTML is very misleading!
  1753. void CEditBuffer::FixupTableData()
  1754. {
  1755.     for( int i = 0; i < edt_RelayoutTables.Size(); i++ )
  1756.     {
  1757.         LO_TableStruct *pLoTable = edt_RelayoutTables[i];
  1758.         LO_Element     *pLoTableElement = (LO_Element*)pLoTable;
  1759.  
  1760.         // Currently, embeded tables will cause reallocation during layout
  1761.         //   that end up reusing pointers, thus some table pointers
  1762.         //   may not be valid. Skip over them.
  1763.         // TODO: Figure out how to remove re-allocated tables from edt_RelayoutTables
  1764.         if( pLoTable->type != LO_TABLE )
  1765.             continue;
  1766.  
  1767.         CEditTableElement *pEdTable =
  1768.             (CEditTableElement*)GetTableElementFromLO_Element(pLoTableElement, LO_TABLE );
  1769.         if(!pEdTable ) 
  1770.             continue;
  1771.  
  1772.         // Fixup Table Data
  1773.         EDT_TableData *pTableData = pEdTable->GetData();
  1774.         if(!pTableData)
  1775.             return;
  1776.  
  1777.         pTableData->iInterCellSpace = pLoTable->inter_cell_space;
  1778.  
  1779. //XP_TRACE(("Fixup Table: Old iWidth = %d, iWidthPixels = %d; New iWidthPixels = %d", pTableData->iWidth, pTableData->iWidthPixels, pLoTable->width));
  1780.  
  1781.         int32 iMaxWidth;
  1782.         int32 iMaxHeight;
  1783.         pEdTable->GetParentSize(m_pContext, &iMaxWidth, &iMaxHeight, pLoTable);
  1784.  
  1785.         // Save correct width even if bWidthDefined is FALSE
  1786.         //   (NOTE: width will NOT be save in m_pTagData if bWidthDefined == FALSE)
  1787.         pTableData->iWidthPixels = pLoTable->width;
  1788.         if( pTableData->bWidthPercent )
  1789.         {
  1790.             pTableData->iWidth = (pTableData->iWidthPixels * 100) / iMaxWidth;
  1791.         } else {
  1792.             pTableData->iWidth = pTableData->iWidthPixels;
  1793.         }
  1794.  
  1795.         pTableData->iHeightPixels = pLoTable->height;
  1796.         if( pTableData->bHeightPercent )
  1797.         {
  1798.             pTableData->iHeight = (pTableData->iHeightPixels * 100) / iMaxHeight;
  1799.         } else {
  1800.             pTableData->iHeight = pTableData->iHeightPixels;
  1801.         }
  1802.         // Get starting layout cell - usually first cell after table element
  1803.         LO_Element *pLoCell = pLoTableElement->lo_any.next;
  1804.         
  1805.         // Skip over non-cells
  1806.         while( pLoCell && pLoCell->type != LO_CELL )
  1807.             pLoCell = pLoCell->lo_any.next;
  1808.         
  1809.         CEditTableCellElement *pEdCell;
  1810.  
  1811.         // This is dependable even if row and cell indexes are not set for cells
  1812.         int32 iRows = pEdTable->GetRows();
  1813.         int32 iArraySize = iRows * sizeof(int32);
  1814.         int32 *ExtraColumns = (int32*)XP_ALLOC(iArraySize);
  1815.         if( !ExtraColumns )
  1816.         {
  1817.             return;
  1818.         }
  1819.         XP_MEMSET( ExtraColumns, 0, iArraySize );
  1820.  
  1821.         // This may actually be a CaptionElement,
  1822.         //  but we will test for that below
  1823.         CEditElement *pRow = pEdTable->GetChild();
  1824.         intn iRow = 0;
  1825.         intn iMaxColumns = 0;
  1826.  
  1827.         // Save coordinates of first cell in table
  1828.         pEdTable->SetFirstColumnX(pLoCell->lo_any.x);
  1829.         pEdTable->SetFirstRowY(pLoCell->lo_any.y);
  1830.  
  1831.         while( pRow )
  1832.         {
  1833.             // A Caption element may be a child, so test type first
  1834.             if( pRow->IsTableRow() )
  1835.             {
  1836.                 pEdCell = (CEditTableCellElement*)(pRow->GetChild());
  1837.  
  1838.                 // We will count number of actual cell locations in each row
  1839.                 // Start with extra columns caused by ROWSPAN in previous rows
  1840.                 int32 iColumnsInRow = ExtraColumns[iRow];
  1841.  
  1842.                 while( pEdCell )
  1843.                 {
  1844.                     intn iColSpan = pEdCell->GetColSpan();
  1845.                     intn iRowSpan = pEdCell->GetRowSpan();
  1846.  
  1847.                     iColumnsInRow += iColSpan;
  1848.  
  1849.     #ifdef DEBUG
  1850.                     // Test if list scan is in sync between layout and editor objects        
  1851.                     LO_Element *pNextCell = (LO_Element*)(pEdCell->GetLoCell());
  1852.                     if( !pLoCell || pLoCell !=  pNextCell)
  1853.                     {
  1854.                         XP_TRACE(("**** pNextCell (%d) is not correct", pLoCell));
  1855.                     }
  1856.                     else
  1857.     #endif                
  1858.                     {
  1859.                         // If current cell has extra ROWSPAN,
  1860.                         //  then it will cause extra columns in following row(s)
  1861.                         if( iRowSpan > 1 )
  1862.                         {
  1863.                             for( intn j = 1; j < iRowSpan; j++ )
  1864.                                 ExtraColumns[iRow+j] += iColSpan;
  1865.                         }
  1866.                         //  Save actual location and size data
  1867.                         EDT_TableCellData *pCellData = pEdCell->GetData();
  1868.                         if( pCellData )
  1869.                         {
  1870.                             pCellData->X = pLoCell->lo_any.x;
  1871.                             pCellData->Y = pLoCell->lo_any.y;
  1872.                             pCellData->iRow = iRow;
  1873.                             pCellData->iWidthPixels = pLoCell->lo_any.width;
  1874.                             pCellData->iHeightPixels = pLoCell->lo_any.height;
  1875.  
  1876.                             if( pCellData->bWidthPercent )
  1877.                             {
  1878.                                 pCellData->iWidth = (pCellData->iWidthPixels * 100) / iMaxWidth;
  1879.                             } else{
  1880.                                 pCellData->iWidth = pCellData->iWidthPixels;
  1881.                             }
  1882.  
  1883.                             if( pCellData->bHeightPercent )
  1884.                             {
  1885.                                 pCellData->iHeight = (pCellData->iHeightPixels * 100) / iMaxHeight;
  1886.                             } else{
  1887.                                 pCellData->iHeight = pCellData->iHeightPixels;
  1888.                             }
  1889.                             pEdCell->SetData(pCellData);
  1890.                             EDT_FreeTableCellData(pCellData);
  1891.                         }
  1892.                     }
  1893.                     CEditTableCellElement *pNextEdCell = (CEditTableCellElement*)(pEdCell->GetNextSibling());
  1894.                 
  1895.                     // Next cell in row 
  1896.                     //  (or signal to get next row if NULL)
  1897.                     pEdCell = pNextEdCell;
  1898.  
  1899.                     // If cell scanning is in sync
  1900.                     //   this should be much quicker than searching for 
  1901.                     //   a LO_Element from each CEditElement or vice versa
  1902.                     if( pLoCell )
  1903.                     {
  1904.                         pLoCell = pLoCell->lo_any.next;
  1905.                         // Skip over non-cells
  1906.                         while( pLoCell && pLoCell->type != LO_CELL )
  1907.                             pLoCell = pLoCell->lo_any.next;
  1908.                     }
  1909.                 }
  1910.                 // Save the column count in Row
  1911.                 pRow->TableRow()->SetColumns(iColumnsInRow);
  1912.  
  1913.                 // Save maximum for all rows
  1914.                 if( iColumnsInRow > iMaxColumns )
  1915.                     iMaxColumns = iColumnsInRow;
  1916.  
  1917.                 // PLEASE!!! I hope this rule holds: "We can never have an empty row"
  1918.                 iRow++;
  1919.             }
  1920.             pRow = pRow->GetNextSibling();
  1921.         }
  1922.         // Now we know maximum number of columns to set in table
  1923.         pTableData->iColumns = iMaxColumns;
  1924.         pTableData->iRows = iRow;
  1925.         pEdTable->SetData(pTableData);
  1926.         EDT_FreeTableData(pTableData);
  1927.  
  1928.         XP_FREE(ExtraColumns);
  1929.     }
  1930.  
  1931.     // We need to do this just once, so clear the list
  1932.     edt_RelayoutTables.Empty();
  1933. }
  1934.  
  1935. //
  1936. // ReflowFromElement
  1937. //
  1938. // This attempts to reflow layout elements from a given edit element. The idea is to not
  1939. // have to return to the tags for basic edit operations. Text typing is currently the main
  1940. // client for this.
  1941. //
  1942. // We do not handle typing inside tables (yet).
  1943. //
  1944. // There will be some duplication of code/functionality with Relayout.
  1945. //
  1946. void CEditBuffer::Reflow( CEditElement* pStartElement,
  1947.                             int iEditOffset,
  1948.                             CEditElement *pEndElement,
  1949.                             intn relayoutFlags ){
  1950.     CEditElement *pEdStart, *pNewStartElement;
  1951.     LO_Element *pLayoutElement;
  1952.     LO_Element *pLoStartLine;
  1953.     int iOffset;
  1954.     int32 iLineNum;
  1955.  
  1956.     if( m_bNoRelayout ){
  1957.         return;
  1958.     }
  1959.  
  1960. #if defined( DEBUG_shannon )
  1961.     // do we want to force calls through to the old relayout?
  1962.     if ( !gEditorReflow ) {
  1963.         Relayout( pStartElement, iEditOffset, pEndElement, relayoutFlags );
  1964.         return;
  1965.     }
  1966. #endif
  1967.  
  1968.     // if the start element can't reflow, then do a relayout
  1969.     if ( ( pStartElement != NULL ) && !pStartElement->CanReflow() ) {
  1970.         Relayout( pStartElement, iEditOffset, pEndElement, relayoutFlags );
  1971.         return;
  1972.     }
  1973.     
  1974.     // Clear the list of tables created during layout
  1975.     // This will be rebuilt as each table is encountered
  1976.     edt_RelayoutTables.Empty();
  1977.  
  1978.     CEditLeafElement *pBegin, *pEnd;
  1979.     ElementOffset iBeginPos, iEndPos;
  1980.     XP_Bool bFromStart;
  1981.     XP_Bool bWasSelected = IsSelected();
  1982.  
  1983.     if( bWasSelected )
  1984.     {
  1985.         GetSelection( pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  1986. #ifdef LAYERS
  1987.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  1988. #else
  1989.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  1990. #endif
  1991.     }
  1992.  
  1993.     pNewStartElement = FindRelayoutStart(pStartElement);
  1994.     if( pNewStartElement && pNewStartElement != pStartElement ){
  1995.         // we had to back up some.  Layout until we pass this point.
  1996.         if( pEndElement == 0 ){
  1997.             pEndElement = pStartElement;
  1998.         }
  1999.         pStartElement = pNewStartElement;
  2000.         iEditOffset = pStartElement->Leaf()->GetLen();
  2001.     }
  2002.  
  2003.     // If the end is in a table, move it outside of the table.
  2004.     if ( pEndElement ) {
  2005.         CEditElement* pTable = pEndElement->GetTopmostTableOrLayer();
  2006.         if ( m_bDisplayTables && pTable) {
  2007.             // If this is in a table, skip after it.
  2008.             pEndElement = pTable->GetLastMostChild()->NextLeaf();
  2009.         }
  2010.     }
  2011.  
  2012.     // laying out from the beginning of the document
  2013.     if( pNewStartElement == 0 ){
  2014.         if( pEndElement == 0 ){
  2015.             pEndElement = pStartElement;
  2016.         }
  2017.         iLineNum = 0;
  2018.         pEdStart = m_pRoot;
  2019.         iOffset = 0;
  2020.     }
  2021.     else {
  2022.         //
  2023.         // normal case
  2024.         //
  2025.         if( pStartElement->IsA(P_TEXT)){
  2026.             pLayoutElement = (LO_Element*)pStartElement->Text()->GetLOText( iEditOffset );
  2027.         }
  2028.         else {
  2029.             pLayoutElement = pStartElement->Leaf()->GetLayoutElement();
  2030.         }
  2031.         if( pLayoutElement == 0 ){
  2032.             // since we can't find the start element, we have to go back to the tags.
  2033.             // BRAIN DAMAGE: Is this really true - shouldn't we just be able to reflow
  2034.             // from here instead?
  2035.             XP_TRACE(("Cannot find start element - relayout from tags"));
  2036.             Relayout( pStartElement->FindContainer(), 0, pEndElement ?
  2037.                         pEndElement : pStartElement, relayoutFlags  );
  2038.             return;
  2039.         }
  2040.         //
  2041.         // Find the first element on this line.
  2042.         pLoStartLine = FirstElementOnLine( pLayoutElement, &iLineNum );
  2043.  
  2044.         //
  2045.         // Position the tag cursor at this position
  2046.         //
  2047.         pEdStart = pLoStartLine->lo_any.edit_element;
  2048.         iOffset = pLoStartLine->lo_any.edit_offset;
  2049.     }
  2050.  
  2051.  
  2052.     // Create a new cursor.
  2053.     CEditTagCursor cursor(this, pEdStart, iOffset, pEndElement);
  2054.     //CEditTagCursor *pCursor = new CEditTagCursor(this, m_pRoot, iOffset);
  2055.  
  2056.     LO_EditorReflow(m_pContext, &cursor, iLineNum, iOffset, m_bDisplayTables);
  2057.  
  2058.  
  2059. #if defined( DEBUG_shannon )
  2060.     XP_TRACE(("\n\nEDITOR REFLOW"));
  2061.     lo_PrintLayout(m_pContext);
  2062. #endif
  2063.  
  2064.     // Search for zero-length text elements, and remove the non-breaking spaces we put
  2065.     // in.
  2066.  
  2067.     CEditElement* pElement= m_pRoot;
  2068.     while ( NULL != (pElement = pElement->FindNextElement(&CEditElement::FindLeafAll,0))){
  2069.         switch ( pElement->GetElementType() ) {
  2070.         case eTextElement:
  2071.             {
  2072.                 /* Am I correct that we don't need to do this? */
  2073.                 CEditTextElement* pText = pElement->Text();
  2074.                 intn iLen = pText->GetLen();
  2075.                 if ( iLen == 0 ) {
  2076.                     LO_Element* pTextStruct = pText->GetLayoutElement();
  2077.                     if ( pTextStruct && pTextStruct->type == LO_TEXT
  2078.                             && pTextStruct->lo_text.text_len == 1
  2079.                             && pTextStruct->lo_text.text ) {
  2080.                         XP_ASSERT( pElement->Leaf()->PreviousLeafInContainer() == 0 );
  2081.                         XP_ASSERT( pElement->Leaf()->LeafInContainerAfter() == 0 );
  2082.                         // Need to strip the allocated text, because lo_bump_position cares.
  2083.                         pTextStruct->lo_text.text_len = 0;
  2084.                     }
  2085.                 }
  2086.             }
  2087.             break;
  2088.         case eBreakElement:
  2089.             {
  2090.                 CEditBreakElement* pBreak = pElement->Break();
  2091.                 LO_Element* pBreakStruct = pBreak->GetLayoutElement();
  2092.                 if ( pBreakStruct && pBreakStruct->type == LO_LINEFEED) {
  2093.                     // Need to strip the allocated text, because lo_bump_position cares.
  2094.                     pBreakStruct->lo_linefeed.break_type = LO_LINEFEED_BREAK_HARD;
  2095.                     pBreakStruct->lo_linefeed.edit_element = pElement;
  2096.                     pBreakStruct->lo_linefeed.edit_offset = 0;
  2097.                 }
  2098.             }
  2099.             break;
  2100.        case eHorizRuleElement:
  2101.              {
  2102.                 // The linefeeds after hrules need to be widened.
  2103.                 // They are zero pixels wide by default.
  2104.                 CEditHorizRuleElement* pHorizRule = (CEditHorizRuleElement*) pElement;
  2105.                 LO_Element* pHRuleElement = pHorizRule->GetLayoutElement();
  2106.                 if ( pHRuleElement ) {
  2107.                     LO_Element* pNext = pHRuleElement->lo_any.next;
  2108.                     if ( pNext && pNext->type == LO_LINEFEED) {
  2109.                         const int32 kMinimumWidth = 7;
  2110.                         if (pNext->lo_linefeed.width < kMinimumWidth ) {
  2111.                              pNext->lo_linefeed.width = kMinimumWidth;
  2112.                         }
  2113.                     }
  2114.                 }
  2115.             }
  2116.             break;
  2117.       default:
  2118.             break;
  2119.         }
  2120.         // Set end-of-paragraph marks
  2121.         if ( pElement->GetNextSibling() == 0 ){
  2122.             // We're the last leaf in the container.
  2123.             CEditLeafElement* pLeaf = pElement->Leaf();
  2124.             LO_Element* pLastElement;
  2125.             int iOffset;
  2126.             if ( pLeaf->GetLOElementAndOffset(pLeaf->GetLen(), TRUE,
  2127.                 pLastElement, iOffset) ){
  2128.                 LO_Element* pNextElement = pLastElement ? pLastElement->lo_any.next : 0;
  2129.                 if ( pNextElement && pNextElement->type == LO_LINEFEED ) {
  2130.                     pNextElement->lo_linefeed.break_type = LO_LINEFEED_BREAK_PARAGRAPH;
  2131.                 }
  2132.             }
  2133.             else {
  2134.                 // Last leaf, but we're empty. Yay. Try the previous leaf.
  2135.                 CEditElement* pPrevious = pElement->GetPreviousSibling();
  2136.                 if ( pPrevious ) {
  2137.                     CEditLeafElement* pLeaf = pPrevious->Leaf();
  2138.                     LO_Element* pLastElement;
  2139.                     int iOffset;
  2140.                     if ( pLeaf->GetLOElementAndOffset(pLeaf->GetLen(), TRUE,
  2141.                         pLastElement, iOffset) ){
  2142.                         LO_Element* pNextElement = pLastElement ? pLastElement->lo_any.next : 0;
  2143.                         if ( pNextElement && pNextElement->type == LO_LINEFEED ) {
  2144.                             pNextElement->lo_linefeed.break_type = LO_LINEFEED_BREAK_PARAGRAPH;
  2145.                         }
  2146.                     }
  2147.                 }
  2148.             }
  2149.         }
  2150.     }
  2151.     if( (relayoutFlags & RELAYOUT_NOCARET) == 0 && !bWasSelected){
  2152.         SetCaret();
  2153.     }
  2154.  
  2155.     if( bWasSelected ){
  2156.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  2157.     }
  2158.  
  2159.     m_bLayoutBackpointersDirty = FALSE;
  2160.     
  2161.     // For each table just layed out, readjust all table and cell width data
  2162.     //   to reflect the complicated Layout algorithm's size data
  2163.     FixupTableData();
  2164.  
  2165. #if defined(XP_WIN) || defined(XP_OS2)
  2166.     // This clears FE pointers to cached layout elements
  2167.     FE_FinishedRelayout(m_pContext);
  2168. #endif
  2169. }
  2170.  
  2171. //
  2172. // Relayout.
  2173. //
  2174. void CEditBuffer::Relayout( CEditElement* pStartElement,
  2175.                             int iEditOffset,
  2176.                             CEditElement *pEndElement,
  2177.                             intn relayoutFlags ){
  2178.     CEditElement *pEdStart, *pNewStartElement;
  2179.     CEditElement *pContainer;
  2180.     LO_Element *pLayoutElement;
  2181.     LO_Element *pLoStartLine;
  2182.     int iOffset;
  2183.     int32 iLineNum;
  2184.  
  2185.     if( m_bNoRelayout ){
  2186.         return;
  2187.     }
  2188.     
  2189.    // These elements will be destroyed during LO_Relayout,
  2190.     //  so remove the list -- reconstructed after laying out
  2191.     m_pSelectedLoTable = NULL;
  2192.     m_SelectedLoCells.Empty();
  2193.         
  2194.     // Clear the list of tables
  2195.     // This will be rebuilt as each table is encountered
  2196.     edt_RelayoutTables.Empty();
  2197.  
  2198.     CEditLeafElement *pBegin, *pEnd;
  2199.     ElementOffset iBeginPos, iEndPos;
  2200.     XP_Bool bFromStart;
  2201.     XP_Bool bWasSelected = IsSelected();
  2202.  
  2203.     if( bWasSelected )
  2204.     {
  2205.         GetSelection( pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  2206. #ifdef LAYERS
  2207.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  2208. #else
  2209.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  2210. #endif
  2211.     }
  2212.     
  2213.     // we actually may need to go back more to the container as we need to layout whole text elements in relayout
  2214.     if ( pStartElement ){
  2215.         pContainer = pStartElement->FindContainer();
  2216.         if ( pContainer ){
  2217.             pStartElement = pContainer;
  2218.             iEditOffset = 0;
  2219.         }
  2220.     }
  2221.  
  2222.     pNewStartElement = FindRelayoutStart(pStartElement);
  2223.     if( pNewStartElement && pNewStartElement != pStartElement ){
  2224.     
  2225.         // we really want to get the first element of this container
  2226.         pContainer = pNewStartElement->FindContainer();
  2227.         if ( pContainer ){
  2228.             pNewStartElement = pContainer->GetChild();
  2229.         }
  2230.         
  2231.         // we had to back up some.  Layout until we pass this point.
  2232.         if( pEndElement == 0 ){
  2233.             pEndElement = pStartElement;
  2234.         }
  2235.         pStartElement = pNewStartElement;
  2236.         iEditOffset = 0;
  2237.     }
  2238.  
  2239.     // If the end is in a table, move it outside of the table.
  2240.     if ( pEndElement ) {
  2241.         CEditElement* pTable = pEndElement->GetTopmostTableOrLayer();
  2242.         if ( m_bDisplayTables && pTable) {
  2243.             // If this is in a table, skip after it.
  2244.             pEndElement = pTable->GetLastMostChild()->NextLeaf();
  2245.         }
  2246.     }
  2247.  
  2248.     // laying out from the beginning of the document
  2249.     if( pNewStartElement == 0 ){
  2250.         if( pEndElement == 0 ){
  2251.             pEndElement = pStartElement;
  2252.         }
  2253.         iLineNum = 0;
  2254.         pEdStart = m_pRoot;
  2255.         iOffset = 0;
  2256.     }
  2257.     else {
  2258.         //
  2259.         // normal case
  2260.         //
  2261.         // we always go to the leaf's layout element in relayout
  2262.         pLayoutElement = pStartElement->Leaf()->GetLayoutElement();
  2263.  
  2264.         if( pLayoutElement == 0 ){
  2265.             // we are sunk! try something different.
  2266.             XP_TRACE(("Yellow Alert! Can't resync, plan B"));
  2267.             Relayout( pStartElement->FindContainer(), 0, pEndElement ?
  2268.                         pEndElement : pStartElement, relayoutFlags  );
  2269.             return;
  2270.         }
  2271.         //
  2272.         // Find the first element on this line.
  2273.         pLoStartLine = FirstElementOnLine( pLayoutElement, &iLineNum );
  2274.  
  2275.         //
  2276.         // Position the tag cursor at this position
  2277.         //
  2278.         pEdStart = pLoStartLine->lo_any.edit_element;
  2279.         iOffset = pLoStartLine->lo_any.edit_offset;
  2280.     }
  2281.  
  2282.  
  2283.     // Create a new cursor.
  2284.     CEditTagCursor cursor(this, pEdStart, iOffset, pEndElement);
  2285.     //CEditTagCursor *pCursor = new CEditTagCursor(this, m_pRoot, iOffset);
  2286.  
  2287.     LO_Relayout(m_pContext, &cursor, iLineNum, iOffset, m_bDisplayTables);
  2288.  
  2289. #if defined( DEBUG_shannon )
  2290.     XP_TRACE(("\n\nEDITOR RELAYOUT"));
  2291.     lo_PrintLayout(m_pContext);
  2292. #endif
  2293.  
  2294.     // Search for zero-length text elements, and remove the non-breaking spaces we put
  2295.     // in.
  2296.  
  2297.     CEditElement* pElement= m_pRoot;
  2298.     while ( NULL != (pElement = pElement->FindNextElement(&CEditElement::FindLeafAll,0))){
  2299.         switch ( pElement->GetElementType() ) {
  2300.         case eTextElement:
  2301.             {
  2302.                 /* Am I correct that we don't need to do this? */
  2303.                 CEditTextElement* pText = pElement->Text();
  2304.                 intn iLen = pText->GetLen();
  2305.                 if ( iLen == 0 ) {
  2306.                     LO_Element* pTextStruct = pText->GetLayoutElement();
  2307.                     if ( pTextStruct && pTextStruct->type == LO_TEXT
  2308.                             && pTextStruct->lo_text.text_len == 1
  2309.                             && pTextStruct->lo_text.text ) {
  2310.                         XP_ASSERT( pElement->Leaf()->PreviousLeafInContainer() == 0 );
  2311.                         XP_ASSERT( pElement->Leaf()->LeafInContainerAfter() == 0 );
  2312.                         // Need to strip the allocated text, because lo_bump_position cares.
  2313.                         pTextStruct->lo_text.text_len = 0;
  2314.                     }
  2315.                 }
  2316. #ifdef OLDWAY
  2317.                 else {
  2318.                     // Layout stripped out all the spaces that crossed soft line breaks.
  2319.                     // Put them back in so they can be selected.
  2320.                     LO_Element* pTextStruct = pText->GetLayoutElement();
  2321.                     while ( pTextStruct && pTextStruct->type == LO_TEXT
  2322.                         && pTextStruct->lo_any.edit_element == pText ) {
  2323.                         intn iOffset = pTextStruct->lo_any.edit_offset + pTextStruct->lo_text.text_len;
  2324.                         if ( iOffset >= iLen )
  2325.                             break;
  2326.                         LO_Element* pNext = pTextStruct->lo_any.next;
  2327.                         while ( pNext && pNext->type != LO_TEXT) {
  2328.                             pNext = pNext->lo_any.next;
  2329.                         }
  2330.                         intn bytesToCopy = iLen - iOffset;
  2331.                         if ( pNext && pNext->lo_any.edit_element == pText ){
  2332.                            bytesToCopy =  pNext->lo_text.edit_offset - iOffset;
  2333.                         }
  2334.                         if ( bytesToCopy > 0 ) {
  2335.                             // Layout has stripped some characters.
  2336.                             // (Probably just a single space character.)
  2337.                             // Put them back.
  2338.                             int16 length = pTextStruct->lo_text.text_len;
  2339.                             int16 newLength = (int16) (length + bytesToCopy + 1); // +1 for '\0'
  2340.                             PA_Block newData;
  2341.                             if (pTextStruct->lo_text.text) {
  2342.                                 newData  = (PA_Block) PA_REALLOC(pTextStruct->lo_text.text, newLength);
  2343.                             }
  2344.                             else {
  2345.                                 newData = (PA_Block) PA_ALLOC(newLength);
  2346.                             }
  2347.                             if ( ! newData ) {
  2348.                                 XP_ASSERT(FALSE); /* Out of memory. Not well tested. */
  2349.                                 break;
  2350.                             }
  2351.                             pTextStruct->lo_text.text = newData;
  2352.                             pTextStruct->lo_text.text_len = (int16) (newLength - 1);
  2353.                             char *locked_buff;
  2354.                             PA_LOCK(locked_buff, char *, pTextStruct->lo_text.text);
  2355.                             if ( locked_buff ) {
  2356.                                 char* source = pText->GetText();
  2357.                                 for ( intn i = 0; i < bytesToCopy; i++ ){
  2358.                                     locked_buff[length + i] = source[iOffset+ i];
  2359.                                 }
  2360.                                 locked_buff[length + bytesToCopy] = '\0';
  2361.                             }
  2362.                             PA_UNLOCK(pNext->lo_text.text);
  2363.                             // The string is now wider. We must adjust the width.
  2364.                             LO_TextInfo text_info;
  2365.                             FE_GetTextInfo(m_pContext, &pTextStruct->lo_text, &text_info);
  2366.                             int32 delta = text_info.max_width - pTextStruct->lo_text.width;
  2367.                             pTextStruct->lo_text.width = text_info.max_width;
  2368.                             // Shrink the linefeed
  2369.                             LO_Element* pNext = pTextStruct->lo_any.next;
  2370.                             if ( pNext && pNext->type == LO_LINEFEED ) {
  2371.                                 if ( pNext->lo_linefeed.width > delta ) {
  2372.                                     pNext->lo_linefeed.width -= delta;
  2373.                                     pNext->lo_linefeed.x += delta;
  2374.                                 }
  2375.                             }
  2376.                         }
  2377.                         pTextStruct = pNext;
  2378.                     }
  2379.                 }
  2380. #endif
  2381.             }
  2382.             break;
  2383.         case eBreakElement:
  2384.             {
  2385.                 CEditBreakElement* pBreak = pElement->Break();
  2386.                 LO_Element* pBreakStruct = pBreak->GetLayoutElement();
  2387.                 if ( pBreakStruct && pBreakStruct->type == LO_LINEFEED) {
  2388.                     // Need to strip the allocated text, because lo_bump_position cares.
  2389.                     pBreakStruct->lo_linefeed.break_type = LO_LINEFEED_BREAK_HARD;
  2390.                     pBreakStruct->lo_linefeed.edit_element = pElement;
  2391.                     pBreakStruct->lo_linefeed.edit_offset = 0;
  2392.                 }
  2393.             }
  2394.             break;
  2395.        case eHorizRuleElement:
  2396.              {
  2397.                 // The linefeeds after hrules need to be widened.
  2398.                 // They are zero pixels wide by default.
  2399.                 CEditHorizRuleElement* pHorizRule = (CEditHorizRuleElement*) pElement;
  2400.                 LO_Element* pHRuleElement = pHorizRule->GetLayoutElement();
  2401.                 if ( pHRuleElement ) {
  2402.                     LO_Element* pNext = pHRuleElement->lo_any.next;
  2403.                     if ( pNext && pNext->type == LO_LINEFEED) {
  2404.                         const int32 kMinimumWidth = 7;
  2405.                         if (pNext->lo_linefeed.width < kMinimumWidth ) {
  2406.                              pNext->lo_linefeed.width = kMinimumWidth;
  2407.                         }
  2408.                     }
  2409.                 }
  2410.             }
  2411.             break;
  2412.       default:
  2413.             break;
  2414.         }
  2415.         // Set end-of-paragraph marks
  2416.         if ( pElement->GetNextSibling() == 0 ){
  2417.             // We're the last leaf in the container.
  2418.             CEditLeafElement* pLeaf = pElement->Leaf();
  2419.             LO_Element* pLastElement;
  2420.             int iOffset;
  2421.             if ( pLeaf->GetLOElementAndOffset(pLeaf->GetLen(), TRUE,
  2422.                 pLastElement, iOffset) ){
  2423.                 LO_Element* pNextElement = pLastElement ? pLastElement->lo_any.next : 0;
  2424.                 if ( pNextElement && pNextElement->type == LO_LINEFEED ) {
  2425.                     pNextElement->lo_linefeed.break_type = LO_LINEFEED_BREAK_PARAGRAPH;
  2426.                 }
  2427.             }
  2428.             else {
  2429.                 // Last leaf, but we're empty. Yay. Try the previous leaf.
  2430.                 CEditElement* pPrevious = pElement->GetPreviousSibling();
  2431.                 if ( pPrevious ) {
  2432.                     CEditLeafElement* pLeaf = pPrevious->Leaf();
  2433.                     LO_Element* pLastElement;
  2434.                     int iOffset;
  2435.                     if ( pLeaf->GetLOElementAndOffset(pLeaf->GetLen(), TRUE,
  2436.                         pLastElement, iOffset) ){
  2437.                         LO_Element* pNextElement = pLastElement ? pLastElement->lo_any.next : 0;
  2438.                         if ( pNextElement && pNextElement->type == LO_LINEFEED ) {
  2439.                             pNextElement->lo_linefeed.break_type = LO_LINEFEED_BREAK_PARAGRAPH;
  2440.                         }
  2441.                     }
  2442.                 }
  2443.             }
  2444.         }
  2445.     }
  2446.     if( (relayoutFlags & RELAYOUT_NOCARET) == 0 && !bWasSelected){
  2447.         SetCaret();
  2448.     }
  2449.  
  2450.     if( bWasSelected ){
  2451.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  2452.     }
  2453.  
  2454.     m_bLayoutBackpointersDirty = FALSE;
  2455.     
  2456.     //XP_TRACE(("*** Resync Table Selection"));
  2457.     // Resynch table selection
  2458.     // TODO -- NOT NEEDED AFTER CHANGE SO LAYOUT ELEMENTS AREN'T DESTROYED
  2459.     if( m_pSelectedEdTable )
  2460.     {
  2461.         m_pSelectedLoTable = m_pSelectedEdTable->GetLoTable();
  2462.         SelectTable(TRUE, m_pSelectedLoTable, m_pSelectedEdTable);
  2463.     } else {
  2464.         for( int i = 0; i < m_SelectedEdCells.Size(); i++ )
  2465.         {
  2466.             // Get the new LO_CellStruct matching the previously-selected edit element
  2467.             LO_CellStruct *pLoCell = m_SelectedEdCells[i]->GetLoCell();
  2468.             // Resync the list and display the cell
  2469.             SelectCell(TRUE, pLoCell, m_SelectedEdCells[i]);
  2470.         }
  2471.     }
  2472.  
  2473.     // For each table just layed out, readjust all table and cell width data
  2474.     //   to reflect the complicated Layout algorithm's size data
  2475.     FixupTableData();
  2476.  
  2477. #if defined(XP_WIN) || defined(XP_OS2)
  2478.     // This clears FE pointers to cached layout elements
  2479.     FE_FinishedRelayout(m_pContext);
  2480. #endif
  2481. }
  2482.  
  2483. // Relayout selected table or parent table if any cells are selected
  2484. void CEditBuffer::RelayoutSelectedTable()
  2485. {
  2486.     // Should be cleared, but set just to be sure
  2487.     m_bNoRelayout = FALSE;
  2488.     CEditTableElement *pTable = NULL;
  2489.     if( m_pSelectedEdTable )
  2490.     {
  2491.         pTable = m_pSelectedEdTable;
  2492.     }
  2493.     else if( m_SelectedEdCells.Size() )
  2494.     {
  2495.         pTable = m_SelectedEdCells[0]->GetParentTable();
  2496.     }
  2497.  
  2498.     if( pTable )
  2499.         Relayout(pTable, 0, pTable);
  2500. }
  2501.  
  2502.  
  2503. //
  2504. // Insert a character at the current edit point
  2505. //
  2506. EDT_ClipboardResult CEditBuffer::InsertChar( int newChar, XP_Bool bTyping ){
  2507.     char buffer[2];
  2508.     buffer[0] = (char) newChar;
  2509.     buffer[1] = '\0';
  2510.     return InsertChars(buffer, bTyping, TRUE);
  2511. }
  2512.  
  2513. EDT_ClipboardResult CEditBuffer::InsertChars( char* pNewChars, XP_Bool bTyping, XP_Bool bReduce){
  2514.  
  2515. #ifdef DEBUG
  2516.     if ( bTyping ) {
  2517.         for(char* pChar = pNewChars; *pChar; pChar++ ) {
  2518.             if ( m_pTestManager->Key(*pChar) )
  2519.                 return EDT_COP_OK;
  2520.         }
  2521.     }
  2522. #endif
  2523.  
  2524.     VALIDATE_TREE(this);
  2525.     EDT_ClipboardResult result = EDT_COP_OK;
  2526.     int iChangeOffset = m_iCurrentOffset;
  2527.     StartTyping(bTyping);
  2528.  
  2529.     if( IsSelected() ){
  2530.         result = DeleteSelection();
  2531.         if ( result != EDT_COP_OK ) return result;
  2532.     }
  2533.     //ClearSelection();
  2534.  
  2535.     ClearMove(FALSE);
  2536.     if(!IsPhantomInsertPoint() ){
  2537.         FixupInsertPoint();
  2538.     }
  2539.     if( m_pCurrent->IsA( P_TEXT )){
  2540.         // If this assert ever fails, it means that we've
  2541.         // got to uncomment this and make it work.
  2542.         XP_ASSERT(IsMultiSpaceMode()); 
  2543. #if 0
  2544.         // Check for space after space case.
  2545.         if ( newChar == ' ' && !m_pCurrent->InFormattedText()
  2546.                 && !IsMultiSpaceMode() ) {
  2547.             CEditInsertPoint p(m_pCurrent, m_iCurrentOffset);
  2548.             // move to the right on spacebar if we are under a space.
  2549.             if( p.IsSpace() ){
  2550.                 NextChar( FALSE );
  2551.                 return result;
  2552.             }
  2553.             if ( p.IsSpaceBeforeOrAfter() ) {
  2554.                 return result;
  2555.             }
  2556.         }
  2557. #endif
  2558.         //
  2559.         // The edit element can choose not to insert a character if it is
  2560.         //  a space and at the insertion point there already is a space.
  2561.         //
  2562.         int32 bytesInserted = m_pCurrent->Text()->InsertChars( m_iCurrentOffset, pNewChars );
  2563.         if( bytesInserted > 0 ){
  2564.  
  2565.             // move past the inserted characters
  2566.             m_iCurrentOffset += bytesInserted;
  2567.             // Reduce or die unless dont reduce
  2568.             if (bReduce)
  2569.             {
  2570.                 Reduce(m_pCurrent->FindContainer());
  2571.                 // relay out the stream
  2572.                 Reflow(m_pCurrent, iChangeOffset);
  2573.             }
  2574.         }
  2575.     }
  2576.     else {
  2577.         if (bReduce)
  2578.         {
  2579.             Reduce( m_pCurrent->FindContainer());
  2580.         }
  2581.         if( m_iCurrentOffset == 0 ){
  2582.             // insert before leaf case.
  2583.             CEditElement *pPrev = m_pCurrent->GetPreviousSibling();
  2584.             if( pPrev == 0 || !pPrev->IsA( P_TEXT ) ){
  2585.                 pPrev = new CEditTextElement((CEditElement*)0,0);
  2586.                 pPrev->InsertBefore( m_pCurrent );
  2587.             }
  2588.             m_pCurrent = pPrev->Leaf();
  2589.             m_iCurrentOffset = m_pCurrent->Text()->GetLen();
  2590.         }
  2591.         else {
  2592.             XP_ASSERT( m_iCurrentOffset == 1 );
  2593.             // insert after leaf case
  2594.             CEditLeafElement *pNext = (CEditLeafElement*) m_pCurrent->GetNextSibling();
  2595.             if( pNext == 0 || !pNext->IsA( P_TEXT ) ){
  2596.                 CEditTextElement* pPrev = m_pCurrent->PreviousTextInContainer();
  2597.                 if( pPrev ){
  2598.                     pNext = pPrev->CopyEmptyText();
  2599.                 }
  2600.                 else {
  2601.                     pNext = new CEditTextElement((CEditElement*)0,0);
  2602.                 }
  2603.                 pNext->InsertAfter( m_pCurrent );
  2604.             }
  2605.             m_pCurrent = pNext;
  2606.             m_iCurrentOffset = 0;
  2607.         }
  2608.         // now we have a text.  Do the actual insert.
  2609.         int32 bytesInserted = m_pCurrent->Text()->InsertChars( m_iCurrentOffset, pNewChars );
  2610.         if ( bytesInserted > 0 ){
  2611.             m_iCurrentOffset += bytesInserted;
  2612.             if ( bTyping ) {
  2613.                 m_relayoutTimer.Relayout(m_pCurrent, iChangeOffset);
  2614.             }
  2615.             else {
  2616.                 Relayout(m_pCurrent, iChangeOffset);
  2617.             }
  2618.         }
  2619.     }
  2620.     return result;
  2621. }
  2622.  
  2623.  
  2624. EDT_ClipboardResult CEditBuffer::DeletePreviousChar(){
  2625. #ifdef DEBUG
  2626.     if ( m_pTestManager->Backspace() )
  2627.         return EDT_COP_OK;
  2628. #endif
  2629.     return DeleteChar(FALSE);
  2630. }
  2631.  
  2632. EDT_ClipboardResult CEditBuffer::DeleteNextChar()
  2633. {
  2634.     return DeleteChar(TRUE);
  2635. }
  2636.  
  2637. EDT_ClipboardResult CEditBuffer::DeleteChar(XP_Bool bForward, XP_Bool bTyping)
  2638. {
  2639.     VALIDATE_TREE(this);
  2640.     EDT_ClipboardResult result = EDT_COP_OK;
  2641.     if( IsTableOrCellSelected() )
  2642.     {
  2643.         // Delete just cell contents of all selected cells
  2644.         ClearSelectedCells();
  2645.     } 
  2646.     else 
  2647.     {
  2648.         ClearTableAndCellSelection();
  2649.         ClearPhantomInsertPoint();
  2650.         ClearMove();
  2651.         StartTyping(bTyping);
  2652.         if( IsSelected() ){
  2653.             result = DeleteSelection();
  2654.         }
  2655.         else {
  2656.             CEditSelection selection;
  2657.             GetSelection(selection);
  2658.             if ( Move(*selection.GetEdge(bForward), bForward ) ) {
  2659.                 result = CanCut(selection, TRUE);
  2660.                 if ( result == EDT_COP_OK ) {
  2661.                     DeleteSelection(selection, FALSE);
  2662.                 }
  2663.             }
  2664.         }
  2665.     }
  2666.     return result;
  2667. }
  2668.  
  2669. EDT_ClipboardResult CEditBuffer::ClearSelectedCells()
  2670. {
  2671.     CEditTableCellElement *pFirstCell = GetFirstSelectedCell();
  2672.     if( pFirstCell )
  2673.     {
  2674.         
  2675.         BeginBatchChanges(kGroupOfChangesCommandID);
  2676.  
  2677.         CEditTableCellElement *pCell = pFirstCell;
  2678.         while(pCell)
  2679.         {
  2680.             pCell->DeleteContents();
  2681.             pCell = GetNextSelectedCell();
  2682.         }
  2683.         // Reset the insert point in the first cell cleared
  2684.         SetTableInsertPoint(pFirstCell);
  2685.  
  2686.         Relayout(pFirstCell->GetParentTable(), 0);
  2687.  
  2688.         EndBatchChanges();
  2689.     }
  2690.     return EDT_COP_OK;
  2691. }
  2692.  
  2693. XP_Bool CEditBuffer::Move(CEditInsertPoint& pt, XP_Bool forward) {
  2694.     CEditLeafElement* dummyNewElement = pt.m_pElement;
  2695.     ElementOffset dummyNewOffset = pt.m_iPos;
  2696.     XP_Bool result = FALSE;
  2697.     if ( forward )
  2698.         result = NextPosition(dummyNewElement, dummyNewOffset,
  2699.             dummyNewElement, dummyNewOffset);
  2700.     else
  2701.         result = PrevPosition(dummyNewElement, dummyNewOffset,
  2702.             dummyNewElement, dummyNewOffset);
  2703.     if ( result ) {
  2704.         pt.m_pElement = dummyNewElement;
  2705.         pt.m_iPos = dummyNewOffset;
  2706.     }
  2707.     return result;
  2708. }
  2709.  
  2710. XP_Bool CEditBuffer::CanMove(CEditInsertPoint& pt, XP_Bool forward) {
  2711.     CEditInsertPoint test(pt);
  2712.     return Move(test, forward);
  2713. }
  2714.  
  2715. XP_Bool CEditBuffer::CanMove(CPersistentEditInsertPoint& pt, XP_Bool forward) {
  2716.     CPersistentEditInsertPoint test(pt);
  2717.     return Move(test, forward);
  2718. }
  2719.  
  2720.  
  2721. XP_Bool CEditBuffer::Move(CPersistentEditInsertPoint& pt, XP_Bool forward) {
  2722.     CEditInsertPoint insertPoint = PersistentToEphemeral(pt);
  2723.     XP_Bool result = Move(insertPoint, forward);
  2724.     if ( result ) {
  2725.         pt = EphemeralToPersistent(insertPoint);
  2726.     }
  2727.     return result;
  2728. }
  2729.  
  2730. void CEditBuffer::SelectNextChar( ){
  2731.     CEditLeafElement *pBegin, *pEnd;
  2732.     ElementOffset iBeginPos, iEndPos;
  2733.     XP_Bool bFromStart;
  2734.     XP_Bool bFound;
  2735.  
  2736.     if( IsSelected() ){
  2737.         GetSelection( pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  2738.         if( bFromStart ){
  2739.             bFound = NextPosition( pBegin, iBeginPos, pBegin, iBeginPos );
  2740.         }
  2741.         else {
  2742.             bFound = NextPosition( pEnd, iEndPos, pEnd, iEndPos );
  2743.         }
  2744.         if( bFound ){
  2745.             SelectRegion( pBegin, iBeginPos, pEnd, iEndPos, bFromStart, TRUE  );
  2746.         }
  2747.     }
  2748.     else {
  2749.         BeginSelection( TRUE, FALSE );
  2750.     }
  2751. }
  2752.  
  2753.  
  2754. void CEditBuffer::SelectPreviousChar( ){
  2755.     if( IsSelected() ){
  2756.         CEditLeafElement *pBegin, *pEnd;
  2757.         ElementOffset iBeginPos, iEndPos;
  2758.         XP_Bool bFound = FALSE;
  2759.         XP_Bool bFromStart;
  2760.         GetSelection( pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  2761.         if( bFromStart ){
  2762.             bFound = PrevPosition( pBegin, iBeginPos, pBegin, iBeginPos );
  2763.         }
  2764.         else {
  2765.             bFound = PrevPosition( pEnd, iEndPos, pEnd, iEndPos );
  2766.         }
  2767.         if( bFound ){
  2768.             SelectRegion( pBegin, iBeginPos, pEnd, iEndPos, bFromStart, FALSE  );
  2769.         }
  2770.     }
  2771.     else {
  2772.          BeginSelection( TRUE, TRUE );
  2773.     }
  2774. }
  2775.  
  2776. XP_Bool CEditBuffer::PrevPosition(CEditLeafElement *pEle, ElementOffset iOffset,
  2777.                 CEditLeafElement*& pNew, ElementOffset& iNewOffset ){
  2778.  
  2779.     LO_Element* pElement;
  2780.     int iLayoutOffset;
  2781.     int iNewLayoutOffset;
  2782.  
  2783.     CEditElement* pNewEditElement;
  2784.     if( pEle->GetLOElementAndOffset( iOffset, FALSE, pElement, iLayoutOffset ) ){
  2785.         XP_Bool result = LO_PreviousPosition( m_pContext, pElement, iLayoutOffset, &pNewEditElement, &iNewLayoutOffset );
  2786.         if ( result ){
  2787.             pNew = pNewEditElement->Leaf();
  2788.             iNewOffset = iNewLayoutOffset;
  2789.         }
  2790.         return result;
  2791.     }
  2792.     else {
  2793.         return pEle->PrevPosition( iOffset, pNew, iNewOffset );
  2794.     }
  2795. }
  2796.  
  2797. XP_Bool CEditBuffer::NextPosition(CEditLeafElement *pEle, ElementOffset iOffset,
  2798.                 CEditLeafElement*& pNew, ElementOffset& iNewOffset ){
  2799.  
  2800.     LO_Element* pElement;
  2801.     int iLayoutOffset;
  2802.     int iNewLayoutOffset;
  2803.     CEditElement* pNewEditElement;
  2804.  
  2805.     if( pEle->GetLOElementAndOffset( iOffset, FALSE, pElement, iLayoutOffset )){
  2806.         XP_Bool result = LO_NextPosition( m_pContext, pElement, iLayoutOffset, &pNewEditElement, &iNewLayoutOffset );
  2807.         // The insert point can't be the end of the document.
  2808.         if ( result && (
  2809.             ! pNewEditElement || pNewEditElement->IsEndOfDocument() ) ) {
  2810.             result = FALSE;
  2811.         }
  2812.         if ( result ){
  2813.             pNew = pNewEditElement->Leaf();
  2814.             iNewOffset = iNewLayoutOffset;
  2815.         }
  2816.         return result;
  2817.     }
  2818.     else {
  2819.         pEle->NextPosition( iOffset, pNew, iNewOffset );
  2820.         return TRUE;
  2821.     }
  2822. }
  2823.  
  2824. void CEditBuffer::NextChar( XP_Bool bSelect ){
  2825.     VALIDATE_TREE(this);
  2826. //    int iSavePos = m_iCurrentOffset;
  2827.  
  2828.     if( bSelect ){
  2829.         SelectNextChar();
  2830.         return;
  2831.     }
  2832.     ClearPhantomInsertPoint();
  2833.     ClearMove();
  2834.     ClearSelection();
  2835.     //FixupInsertPoint();
  2836.     NextPosition( m_pCurrent, m_iCurrentOffset,
  2837.                     m_pCurrent, m_iCurrentOffset );
  2838.     SetCaret();
  2839. }
  2840.  
  2841.  
  2842.  
  2843. XP_Bool CEditBuffer::PreviousChar( XP_Bool bSelect ){
  2844.     VALIDATE_TREE(this);
  2845.  
  2846.     if( bSelect ){
  2847.         SelectPreviousChar();
  2848.         return FALSE;       // no return value on select previous..
  2849.     }
  2850.  
  2851.     ClearPhantomInsertPoint();
  2852.     ClearMove();
  2853.     ClearSelection();
  2854.     //FixupInsertPoint();
  2855.     if( PrevPosition( m_pCurrent, m_iCurrentOffset,
  2856.                     m_pCurrent, m_iCurrentOffset )){
  2857.         SetCaret();
  2858.         return TRUE;
  2859.     }
  2860.     else {
  2861.         return FALSE;
  2862.     }
  2863. }
  2864.  
  2865.  
  2866. //
  2867. // Find out our current layout position and move up or down from here.
  2868. //
  2869. void CEditBuffer::UpDown( XP_Bool bSelect, XP_Bool bForward ){
  2870.     VALIDATE_TREE(this);
  2871.     CEditLeafElement *pEle;
  2872.     ElementOffset iOffset;
  2873.     XP_Bool bStickyAfter;
  2874.     LO_Element* pElement;
  2875.     int iLayoutOffset;
  2876.  
  2877.     DoneTyping();
  2878.  
  2879.     BeginSelection();
  2880.  
  2881.     if( bSelect ){
  2882.         GetInsertPoint( &pEle, &iOffset, &bStickyAfter );
  2883.     }
  2884.     else {
  2885.         // do the right thing depending on insertion point change of direction
  2886.         ClearSelection( TRUE, !bForward );
  2887.         ClearPhantomInsertPoint();
  2888.         pEle = m_pCurrent;
  2889.         iOffset = m_iCurrentOffset;
  2890.         bStickyAfter = m_bCurrentStickyAfter;
  2891.     }
  2892.  
  2893.     XP_ASSERT(!pEle->IsEndOfDocument());
  2894.     pEle->GetLOElementAndOffset( iOffset, bStickyAfter, pElement, iLayoutOffset );
  2895.  
  2896.     LO_UpDown( m_pContext, pElement, iLayoutOffset, GetDesiredX(pEle, iOffset, bStickyAfter), bSelect, bForward );
  2897.  
  2898.     EndSelection();
  2899. }
  2900.  
  2901. XP_Bool CEditBuffer::NextTableCell( XP_Bool /*bSelect*/, XP_Bool bForward, intn* pRowCounter )
  2902. {
  2903.     if( !  IsInsertPointInTable() )
  2904.         return FALSE;
  2905.  
  2906.     CEditTableCellElement* pTableCell = m_pCurrent->GetTableCellIgnoreSubdoc();
  2907.     if( !pTableCell )
  2908.         return FALSE;
  2909.  
  2910.     CEditElement *pNext;
  2911.     if(bForward)
  2912.     {
  2913.         pNext = pTableCell->GetNextSibling();
  2914.     } else {
  2915.         pNext = pTableCell->GetPreviousSibling();
  2916.     }
  2917.     if( !pNext )
  2918.     {
  2919.         // No sibling found, but we may need to check for more rows
  2920.         CEditTableRowElement* pRow = pTableCell->GetTableRowIgnoreSubdoc();
  2921.         CEditElement* pNextRow;
  2922.         if( pRow )
  2923.         {
  2924.             if(bForward)
  2925.             {
  2926.                 pNextRow = pRow->GetNextSibling();
  2927.             } else {
  2928.                 pNextRow = pRow->GetPreviousSibling();
  2929.             }
  2930.             if( pNextRow && pNextRow->IsTableRow() )
  2931.             {
  2932.                 if(bForward)
  2933.                 {
  2934.                     pNext = pNextRow->GetChild();
  2935.                     // Tell caller we wrapped to next row
  2936.                     if( pNext && pRowCounter )
  2937.                         (*pRowCounter)++;
  2938.  
  2939.                 } else {
  2940.                     pNext = pNextRow->GetLastChild();
  2941.                     // Tell caller we wrapped to previous row
  2942.                     if( pNext && pRowCounter )
  2943.                         (*pRowCounter)--;
  2944.                 }
  2945.             }
  2946.         }
  2947.     }
  2948.     if( pNext )
  2949.     {
  2950.         CEditInsertPoint insertPoint;
  2951.         insertPoint.m_pElement = pNext->GetFirstMostChild()->Leaf();
  2952.         if( insertPoint.m_pElement )
  2953.         {
  2954.             SetInsertPoint(insertPoint);
  2955.             return TRUE;
  2956.         }
  2957.     }
  2958.     return FALSE;
  2959. }
  2960.  
  2961. int32
  2962. CEditBuffer::GetDesiredX(CEditLeafElement* pEle, intn iOffset, XP_Bool bStickyAfter)
  2963. {
  2964.     XP_ASSERT(bStickyAfter == TRUE || bStickyAfter == FALSE);
  2965.     // m_iDesiredX is where we would move if we could.  This keeps the
  2966.     //  cursor moving, basically, straight up and down, even if there is
  2967.     //  no text or gaps
  2968.     if( m_iDesiredX == -1 ){
  2969.         LO_Element* pElement;
  2970.         int iLayoutOffset;
  2971.  
  2972.         // A break at offset 1 is the beginning of the next line.  We really want
  2973.         //  the end in this case.
  2974.         if( pEle->IsBreak() && iOffset == 1 ){
  2975.             iOffset = 0;
  2976.         }
  2977.  
  2978.         pEle->GetLOElementAndOffset( iOffset, bStickyAfter, pElement, iLayoutOffset );
  2979.         int32 x;
  2980.         int32 y;
  2981.         int32 width;
  2982.         int32 height;
  2983.         LO_GetEffectiveCoordinates(m_pContext, pElement, iLayoutOffset, &x, &y, &width, &height);
  2984.         m_iDesiredX = x;
  2985.     }
  2986.     return m_iDesiredX;
  2987. }
  2988.  
  2989.  
  2990. void CEditBuffer::NavigateChunk( XP_Bool bSelect, intn chunkType, XP_Bool bForward ){
  2991.     if ( chunkType == EDT_NA_UPDOWN ){
  2992.         UpDown(bSelect, bForward);
  2993.     }
  2994.     else {
  2995.         VALIDATE_TREE(this);
  2996.         ClearPhantomInsertPoint();
  2997.         ClearMove();    /* Arrow keys clear the up/down target position */
  2998.         BeginSelection();
  2999.         LO_NavigateChunk( m_pContext, chunkType, bSelect, bForward );
  3000.         EndSelection();
  3001.         DoneTyping();
  3002.     }
  3003. }
  3004.  
  3005. //cmanske: TEST FOR SELECTED TABLE/CELLS
  3006. void CEditBuffer::ClearMailQuote(){
  3007.     if ( ! m_pCurrent->InMungableMailQuote() ) {
  3008.         XP_ASSERT(FALSE);
  3009.         return;
  3010.     }
  3011.     // Outdent to get rid of MailQuote info.
  3012.     while( m_pCurrent->InMungableMailQuote() ) {
  3013.         if ( ! Outdent() ){
  3014.             /* Currently (4.0) we can't outdent in the presence of DIV tags. */
  3015.             return;
  3016.         }
  3017.     }
  3018.     // Get rid of paragraph style
  3019.     MorphContainer(P_NSDT);
  3020.     SetParagraphAlign(ED_ALIGN_DEFAULT);
  3021.     if ( m_pCurrent->IsText() ) {
  3022.         // Get rid of character styles
  3023.         EDT_CharacterData* pCharacterData = EDT_NewCharacterData();
  3024.         // We wanted to do ~0, but this causes an assert in SetCharacterData.
  3025.         pCharacterData->mask = ~(TF_SERVER|TF_SCRIPT|TF_STYLE);
  3026.         pCharacterData->values = 0; // Clear everything.
  3027.         SetCharacterData(pCharacterData);
  3028.         EDT_FreeCharacterData(pCharacterData);
  3029.     }
  3030. }
  3031.  
  3032. //
  3033. // Break current container into two containers.
  3034. //
  3035. EDT_ClipboardResult CEditBuffer::ReturnKey(XP_Bool bTyping){
  3036.     ClearTableAndCellSelection();
  3037.     EDT_ClipboardResult result = EDT_COP_OK;
  3038.     if ( bTyping ) {
  3039.         VALIDATE_TREE(this);
  3040. #ifdef DEBUG
  3041.         if ( m_pTestManager->ReturnKey() )
  3042.             return result;
  3043. #endif
  3044.         if( IsSelected() ){
  3045.             result = DeleteSelection();
  3046.             if ( result != EDT_COP_OK ) return result;
  3047.         }
  3048.         // Check if we are in a mail quote
  3049.         CEditInsertPoint ip;
  3050.         GetInsertPoint(ip);
  3051.         if ( ip.m_pElement->InMungableMailQuote() ) {
  3052.             // Are we at the end of the mail quote? If so, move beyond
  3053.             XP_Bool bAtEndOfMailQuote = FALSE;
  3054.             if ( ip.IsEndOfContainer() ) {
  3055.                 ip = ip.NextPosition();
  3056.                 if ( ! ip.m_pElement->InMailQuote() ) {
  3057.                     bAtEndOfMailQuote = TRUE;
  3058.                 }
  3059.             }
  3060.             result = InternalReturnKey(TRUE);
  3061.             if ( result == EDT_COP_OK ) {
  3062.                 if ( ! bAtEndOfMailQuote ) {
  3063.                     InternalReturnKey(TRUE);
  3064.                     UpDown(FALSE, FALSE); // go back up one line.
  3065.                 }
  3066.                 ClearMailQuote();
  3067.             }
  3068.         }
  3069.         else {
  3070.             result = InternalReturnKey(TRUE);
  3071.         }
  3072.     }
  3073.     else {
  3074.         result = InternalReturnKey(TRUE);
  3075.     }
  3076.     return result;
  3077. }
  3078.  
  3079. EDT_ClipboardResult CEditBuffer::InternalReturnKey(XP_Bool bUserTyped){
  3080.     EDT_ClipboardResult result = EDT_COP_OK;
  3081.  
  3082.     if( IsSelected() ){
  3083.         result = DeleteSelection();
  3084.         if ( result != EDT_COP_OK ) return result;
  3085.     }
  3086.  
  3087.     // ClearSelection();
  3088.     ClearPhantomInsertPoint();
  3089.     FixupInsertPoint();
  3090.  
  3091.     CEditElement *pChangedElement = m_pCurrent;
  3092.     CEditLeafElement *pNew,*pSplitAfter;
  3093.     CEditLeafElement *pCopyFormat;
  3094.     int iChangedOffset = m_iCurrentOffset;
  3095.  
  3096.     pSplitAfter = m_pCurrent;
  3097.     pCopyFormat = m_pCurrent;
  3098.  
  3099.     if( m_iCurrentOffset == 0 ){
  3100.         // split at beginning of element
  3101.         if( m_pCurrent->PreviousLeafInContainer() == 0){
  3102.             CEditTextElement *pNew;
  3103.             pNew = m_pCurrent->CopyEmptyText();
  3104.             pNew->InsertBefore( m_pCurrent );
  3105.             pChangedElement = pNew->FindContainer();
  3106.             pSplitAfter = pNew;
  3107.         }
  3108.  
  3109.     }
  3110.     else {
  3111.         pNew = m_pCurrent->Divide( m_iCurrentOffset )->Leaf();
  3112.         /* Get rid of space at start of new line if user typed. */
  3113.         if( bUserTyped && !pNew->InFormattedText()
  3114.                 && pNew->IsA(P_TEXT)
  3115.                 && pNew->Text()->GetLen()
  3116.                 && pNew->Text()->GetText()[0] == ' ' ){
  3117.             pNew->Text()->DeleteChar(m_pContext, 0);
  3118.             CEditLeafElement *pNext = 0;
  3119.             if( pNew->Text()->GetLen() == 0
  3120.                     && (pNext = pNew->TextInContainerAfter()) != 0 ){
  3121.                 pNew->Unlink();
  3122.                 // Should we delete pNew?
  3123.                 delete pNew;
  3124.                 pNew = pNext;
  3125.             }
  3126.         }
  3127.         m_pCurrent = pNew;
  3128.     }
  3129.     m_iCurrentOffset = 0;
  3130.     m_pCurrent->GetParent()->Split( pSplitAfter, 0,
  3131.                 &CEditElement::SplitContainerTest, 0 );
  3132.  
  3133.     if ( bUserTyped && m_pCurrent->GetLen() == 0 && m_pCurrent->TextInContainerAfter() == 0 ){
  3134.         // An empty paragraph. If the style is one of the
  3135.         // heading styles, set it back to normal
  3136.         CEditContainerElement* pContainer = m_pCurrent->FindContainer();
  3137.         TagType tagType = pContainer->GetType();
  3138.         // To do - use one of those fancy bit arrays.
  3139.         if ( tagType >= P_HEADER_1 && tagType <=  P_HEADER_6){
  3140. #if EDT_DDT
  3141.             pContainer->SetType(P_NSDT);
  3142. #else
  3143.             pContainer->SetType(P_DESC_TITLE);
  3144. #endif
  3145.         }
  3146.     }
  3147.  
  3148.     if ( bUserTyped ) {
  3149.         Reduce(m_pRoot); // Or maybe just the two containers?
  3150.     }
  3151.     
  3152.     // We currently cannot do a reflow here as the new text elements have not been
  3153.     // created to be reflowed...
  3154.     Relayout(pChangedElement, iChangedOffset,
  3155.         (pChangedElement != m_pCurrent ? m_pCurrent : 0 ));
  3156.  
  3157. #if 0
  3158.     // The bug is that after a return is pressed, the formatting is wrong
  3159.     //  for the new paragrah.  Presents a problem.  If we insert an empty
  3160.     //  text element here, we get messed up because we may not be able to
  3161.     //  position the caret.  We end up reducing and loosing a pointer to
  3162.     //  the layout element.  It is a hard problem.
  3163.  
  3164.     CEditTextElement *pNewText = pCopyFormat->CopyEmptyText();
  3165.     pNewText->InsertBefore(m_pCurrent);
  3166.     m_pCurrent = pNewText;
  3167.     m_iCurrentOffset = 0;
  3168. #endif
  3169.  
  3170.     return result;
  3171. }
  3172.  
  3173. //TODO: Should this be a preference?
  3174. #define EDT_SPACES_PER_TAB  4
  3175.  
  3176. EDT_ClipboardResult CEditBuffer::TabKey(XP_Bool bForward, XP_Bool bForceTabChar){
  3177.  
  3178.     ClearTableAndCellSelection();
  3179.     if( IsInsertPointInTable() && !bForceTabChar){
  3180.         if( NextTableCell(FALSE, bForward) ){
  3181.             // This selects contents of next cell:
  3182.             //cmanske: Other programs select the contents of a cell when
  3183.             //  tab into it, but lets not do that!
  3184.             // This function will now select the cell boundary, not contents
  3185. //            SelectTableCell();
  3186.         }
  3187.         else if ( bForward ) {
  3188.             // Tabbing past last cell creates a new row
  3189.             // back-tabbing (Shift-Tab) in first cell is ignored
  3190.             BeginBatchChanges(kInsertTableRowCommandID);
  3191.             // Save current insert point so we can return
  3192.             //  there when done
  3193.             CEditInsertPoint ip;
  3194.             GetTableInsertPoint(ip);
  3195.             InsertTableRows(NULL, TRUE, 1);
  3196.             // Restore previous insert point
  3197.             //  then move to first new cell
  3198.             SetInsertPoint(ip);
  3199.             NextTableCell(FALSE, bForward);
  3200.             EndBatchChanges();
  3201.         }
  3202.         return EDT_COP_OK;
  3203.     }
  3204.  
  3205.     // Tab = insert some extra spaces
  3206.     // TODO: SHOULD WE USE "SPACER" TAG INSTEAD?
  3207.     EDT_ClipboardResult result;
  3208.     for( int i = 0; i < EDT_SPACES_PER_TAB; i++ ){
  3209.         if( EDT_COP_OK != (result = InsertChar(' ', FALSE)) ){
  3210.             break;
  3211.         }
  3212.     }
  3213.     return result;
  3214. }
  3215.  
  3216. void CEditBuffer::IndentSelection(CEditSelection& selection)
  3217. {
  3218.     CEditLeafElement *pBegin, *pEnd, *pCurrent;
  3219.     ElementOffset iBeginPos, iEndPos;
  3220.     XP_Bool bFromStart;
  3221.  
  3222.     // Don't relayout if we got a cell selection passed in
  3223.     XP_Bool bRelayout = selection.IsEmpty();
  3224.  
  3225.     // Get data from supplied selection or get current selection if empty
  3226.     GetSelection(selection, pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3227.     pCurrent = pBegin;
  3228.     XP_Bool bDone = FALSE;
  3229.     CEditContainerElement* pLastContainer = 0;
  3230.     CEditContainerElement* pContainer = 0;
  3231.     CEditListElement* pList;
  3232.     do {
  3233.         pCurrent->FindList( pContainer, pList );
  3234.         if( pContainer != pLastContainer ){
  3235.             IndentContainer( pContainer, pList );
  3236.             pLastContainer = pContainer;
  3237.         }
  3238.         bDone = (pEnd == pCurrent );    // For most cases
  3239.         pCurrent = pCurrent->NextLeafAll();
  3240.         bDone = bDone || (iEndPos == 0 && pEnd == pCurrent ); // Pesky edge conditions!
  3241.     } while( pCurrent && !bDone );
  3242.  
  3243.     if( bRelayout )
  3244.     {
  3245.         // force layout stop displaying the current selection.
  3246. #ifdef LAYERS
  3247.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  3248. #else
  3249.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  3250. #endif
  3251.         // probably needs to be the common ancestor.
  3252.         Relayout( pBegin->FindContainer(), 0, pEnd, RELAYOUT_NOCARET );
  3253.         // Need to force selection.
  3254.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3255.     }
  3256. }
  3257.  
  3258. void CEditBuffer::Indent()
  3259. {
  3260.     VALIDATE_TREE(this);
  3261.     CEditSelection selection;
  3262.     if( IsSelected() )
  3263.     {
  3264.         IndentSelection(selection);
  3265.         return;
  3266.     }
  3267.     if( IsTableOrCellSelected() )
  3268.     {
  3269.         if( GetFirstCellSelection(selection) )
  3270.         {
  3271.             BeginBatchChanges(kChangeAttributesCommandID);
  3272.             IndentSelection(selection);
  3273.             while( GetNextCellSelection(selection) )
  3274.             {
  3275.                 IndentSelection(selection);
  3276.             }
  3277.             RelayoutSelectedTable();
  3278.             EndBatchChanges();
  3279.         }
  3280.         return;
  3281.     } 
  3282.  
  3283.     //
  3284.     // find where cont-cont has cont
  3285.     //
  3286.     CEditContainerElement* pContainer;
  3287.     CEditListElement* pList;
  3288.     m_pCurrent->FindList( pContainer, pList );
  3289.     IndentContainer( pContainer, pList );
  3290.     Relayout( pContainer, 0, pContainer->GetLastMostChild() );
  3291. }
  3292.  
  3293. void CEditBuffer::IndentContainer( CEditContainerElement *pContainer,
  3294.         CEditListElement *pList ){
  3295.     VALIDATE_TREE(this);
  3296.  
  3297.     CEditElement *pPrev = pContainer->GetPreviousSibling();
  3298.     CEditElement *pNext = pContainer->GetNextSibling();
  3299.  
  3300.  
  3301.     //
  3302.     // case 1
  3303.     //
  3304.     //     UL:
  3305.     //         LI:
  3306.     //     LI:         <- Indenting this guy
  3307.     //     LI:
  3308.     //     LI:
  3309.     //
  3310.     //  should result in
  3311.     //
  3312.     //     UL:
  3313.     //         LI:
  3314.     //         LI:     <-end up here
  3315.     //     LI:
  3316.     //     LI:
  3317.     //
  3318.     //
  3319.     //  We also need to handle the case
  3320.     //
  3321.     //      UL:
  3322.     //          LI:
  3323.     //      LI:         <- indent this guy
  3324.     //      UL:
  3325.     //          LI:
  3326.     //          LI:
  3327.     //
  3328.     //  and the second UL becomes redundant so we eliminate it.
  3329.     //
  3330.     //      UL:
  3331.     //          LI:
  3332.     //          LI:     <-Ends up here
  3333.     //          LI:     <-frm the redundant container
  3334.     //          LI:
  3335.     //
  3336.     if( pPrev && pPrev->IsList() ){
  3337.         CEditElement *pChild = pPrev->GetLastChild();
  3338.         if( pChild ){
  3339.             pContainer->Unlink();
  3340.             pContainer->InsertAfter( pChild );
  3341.         }
  3342.         else {
  3343.             pContainer->Unlink();
  3344.             pContainer->InsertAsFirstChild( pPrev );
  3345.         }
  3346.         if( pNext && pNext->IsList() ){
  3347.             pPrev->Merge( pNext );
  3348.         }
  3349.     }
  3350.     //
  3351.     // case 0
  3352.     //
  3353.     //      LI:             <- Indenting this guy
  3354.     //
  3355.     // should result in
  3356.     //
  3357.     //      UL:
  3358.     //          LI:
  3359.     //
  3360.     //
  3361.     else if( pList == 0 ){
  3362.         PA_Tag *pTag = XP_NEW( PA_Tag );
  3363.         XP_BZERO( pTag, sizeof( PA_Tag ) );
  3364.         pTag->type = P_UNUM_LIST;
  3365.         CEditElement *pEle = new CEditListElement( 0, pTag, GetRAMCharSetID() );
  3366.         PA_FreeTag( pTag );
  3367.  
  3368.         pEle->InsertAfter( pContainer );
  3369.         pContainer->Unlink();
  3370.         pContainer->InsertAsFirstChild( pEle );
  3371.         return;
  3372.     }
  3373.     //
  3374.     // case 2
  3375.     //
  3376.     //     UL:
  3377.     //          LI:
  3378.     //          LI:         <- Indenting this guy
  3379.     //          UL:
  3380.     //              LI:
  3381.     //          LI:
  3382.     //          LI:
  3383.     //
  3384.     //  should result in
  3385.     //
  3386.     //     UL:
  3387.     //          LI:
  3388.     //          UL:
  3389.     //              LI:     <- Ends up here.
  3390.     //              LI:
  3391.     //          LI:
  3392.     //          LI:
  3393.     //
  3394.     else if( pNext && pNext->IsList() ){
  3395.         CEditElement *pChild = pNext->GetChild();
  3396.         if( pChild ){
  3397.             pContainer->Unlink();
  3398.             pContainer->InsertBefore( pChild );
  3399.         }
  3400.         else {
  3401.             pContainer->Unlink();
  3402.             pContainer->InsertAsFirstChild( pPrev );
  3403.         }
  3404.     }
  3405.     //
  3406.     // case 3
  3407.     //
  3408.     //     UL:
  3409.     //          LI:
  3410.     //          LI:         <- Indenting this guy
  3411.     //          LI:
  3412.     //          LI:
  3413.     //
  3414.     //  should result in
  3415.     //
  3416.     //     UL:
  3417.     //          LI:
  3418.     //          UL:
  3419.     //              LI:     <- Ends up here.
  3420.     //          LI:
  3421.     //          LI:
  3422.     //
  3423.     else {
  3424.         CEditElement *pNewList;
  3425.         pNewList = pList->Clone(0);
  3426.  
  3427.  
  3428.         // insert the new cont-cont between the old one and the cont
  3429.         pNewList->InsertBefore( pContainer );
  3430.         pContainer->Unlink();
  3431.         pContainer->InsertAsFirstChild( pNewList);
  3432.     }
  3433. }
  3434.  
  3435. XP_Bool CEditBuffer::OutdentSelection(CEditSelection& selection)
  3436. {
  3437.     CEditLeafElement *pBegin, *pEnd, *pCurrent;
  3438.     ElementOffset iBeginPos, iEndPos;
  3439.     XP_Bool bFromStart;
  3440.  
  3441.     // Don't relayout if we got a cell selection passed in
  3442.     XP_Bool bRelayout = selection.IsEmpty();
  3443.  
  3444.     // Get data from supplied selection or get current selection if empty
  3445.     GetSelection(selection, pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3446.     pCurrent = pBegin;
  3447.     XP_Bool bDone = FALSE;
  3448.     CEditContainerElement* pLastContainer = 0;
  3449.     CEditContainerElement* pContainer = 0;
  3450.     CEditListElement* pList;
  3451.     do {
  3452.         pCurrent->FindList( pContainer, pList );
  3453.         if( pContainer != pLastContainer ){
  3454.             OutdentContainer( pContainer, pList );
  3455.             pLastContainer = pContainer;
  3456.         }
  3457.         bDone = (pEnd == pCurrent );    // For most cases
  3458.         pCurrent = pCurrent->NextLeafAll();
  3459.         bDone = bDone || (iEndPos == 0 && pEnd == pCurrent ); // Pesky edge conditions!
  3460.     } while( pCurrent && !bDone );
  3461.  
  3462.     if( bRelayout )
  3463.     {
  3464.         // force layout stop displaying the current selection.
  3465. #ifdef LAYERS
  3466.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  3467. #else
  3468.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  3469. #endif
  3470.         // probably needs to be the common ancestor.
  3471.         Relayout( pBegin->FindContainer(), 0, pEnd, RELAYOUT_NOCARET );
  3472.         // Need to force selection.
  3473.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3474.     }
  3475.     // How can we always return TRUE??
  3476.     return TRUE;
  3477. }
  3478.  
  3479. XP_Bool CEditBuffer::Outdent()
  3480. {
  3481.     CEditSelection selection;
  3482.     VALIDATE_TREE(this);
  3483.  
  3484.     if( IsSelected() ){
  3485.         return OutdentSelection(selection);
  3486.     }
  3487.     if( IsTableOrCellSelected() )
  3488.     {
  3489.         XP_Bool bResult = FALSE;
  3490.         if( GetFirstCellSelection(selection) )
  3491.         {
  3492.             BeginBatchChanges(kChangeAttributesCommandID);
  3493.             // Format first selected cell
  3494.             bResult = OutdentSelection(selection);
  3495.             // Select all other cells and format them
  3496.             while( GetNextCellSelection(selection) )
  3497.             {
  3498.                 bResult = OutdentSelection(selection);
  3499.             }
  3500.             RelayoutSelectedTable();
  3501.             EndBatchChanges();
  3502.         }
  3503.         return bResult;
  3504.     } 
  3505.  
  3506.     CEditContainerElement* pContainer = NULL;
  3507.     CEditListElement* pList = NULL;
  3508.     m_pCurrent->FindList( pContainer, pList );
  3509.     if ( ! pContainer || !pList ) {
  3510.         return FALSE;
  3511.     }
  3512.     OutdentContainer( pContainer, pList );
  3513.     Relayout( pContainer, 0, pContainer->GetLastMostChild() );
  3514.     return TRUE;
  3515. }
  3516.  
  3517. void CEditBuffer::OutdentContainer( CEditContainerElement *pContainer,
  3518.         CEditListElement *pList ){
  3519.  
  3520.     //
  3521.     // case 0
  3522.     //
  3523.     //      LI:     <-- outdent this guy.
  3524.     //
  3525.     if( pList == 0 ){
  3526.         return;                         // no work to do
  3527.     }
  3528.  
  3529.     CEditElement *pPrev = pContainer->GetPreviousSibling();
  3530.     CEditElement *pNext = pContainer->GetNextSibling();
  3531.  
  3532.     //
  3533.     // case 1
  3534.     //
  3535.     //      UL:
  3536.     //          LI:     <-Outdenting this guy
  3537.     //
  3538.     // should result in
  3539.     //
  3540.     //      LI:
  3541.     //
  3542.     // No previous or next siblings.  Just remove the List and
  3543.     //  put its container in its place.
  3544.     //
  3545.     if( pPrev == 0 && pNext == 0 ){
  3546.         pContainer->Unlink();
  3547.         pContainer->InsertAfter( pList );
  3548.         pList->Unlink();
  3549.         delete pList;
  3550.     }
  3551.  
  3552.  
  3553.     //
  3554.     // case 2
  3555.     //
  3556.     //      UL:
  3557.     //          LI:     <-Outdenting this guy
  3558.     //          LI:
  3559.     //
  3560.     //
  3561.     //      Results in:
  3562.     //
  3563.     //      LI:         <-Outdenting this guy
  3564.     //      UL:
  3565.     //          LI:
  3566.     //
  3567.     else if( pPrev == 0 ){
  3568.         pContainer->Unlink();
  3569.         pContainer->InsertBefore( pList );
  3570.     }
  3571.     //
  3572.     // case 3
  3573.     //
  3574.     //      UL:
  3575.     //          LI:
  3576.     //          LI:     <-Outdenting this guy
  3577.     //
  3578.     //
  3579.     //      Results in:
  3580.     //
  3581.     //      UL:
  3582.     //          LI:
  3583.     //      LI:         <-Outdenting this guy
  3584.     //
  3585.     else if( pNext == 0 ){
  3586.         pContainer->Unlink();
  3587.         pContainer->InsertAfter( pList );
  3588.     }
  3589.  
  3590.     //
  3591.     // case 4
  3592.     //
  3593.     //      UL:
  3594.     //          LI:
  3595.     //          LI:     <-Outdenting this guy
  3596.     //          LI:
  3597.     //
  3598.     //      Results in:
  3599.     //
  3600.     //      UL:
  3601.     //          LI:
  3602.     //      LI:         <-Outdenting this guy
  3603.     //      UL:
  3604.     //          LI:
  3605.     else {
  3606.         CEditElement *pNewList = pList->Clone(0);
  3607.         while( (pNext = pContainer->GetNextSibling()) != 0 ){
  3608.             pNext->Unlink();
  3609.             pNext->InsertAsLastChild( pNewList );
  3610.         }
  3611.         pContainer->Unlink();
  3612.         pContainer->InsertAfter( pList );
  3613.         pNewList->InsertAfter( pContainer );
  3614.     }
  3615. }
  3616.  
  3617. ED_ElementType CEditBuffer::GetCurrentElementType(){
  3618.     //cmanske: VERY RISKY! Need to test the effects of this thoroughly!
  3619.     // Table and cell selection occurs when there is still a caret 
  3620.     // (but must not have anything else selected),
  3621.     if( m_pSelectedEdTable )
  3622.         return ED_ELEMENT_TABLE;
  3623.  
  3624.     if( m_SelectedEdCells.Size() )
  3625.         return ED_ELEMENT_CELL;
  3626.     
  3627.     CEditLeafElement *pInsertPoint;
  3628.     ElementOffset iOffset;
  3629.     XP_Bool bSingleItem;
  3630.  
  3631.     bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  3632.  
  3633.     if( !bSingleItem ){
  3634.         if( IsSelected() ){
  3635.             return ED_ELEMENT_SELECTION;
  3636.         }
  3637.         else {
  3638.             return ED_ELEMENT_TEXT;
  3639.         }
  3640.     }
  3641.     else if( pInsertPoint->IsA(P_TEXT) ){
  3642.         // should never have single item text objects...
  3643.         XP_ASSERT(FALSE);
  3644.         return ED_ELEMENT_TEXT;
  3645.     }
  3646.     else if( pInsertPoint->IsImage() ){
  3647.         return ED_ELEMENT_IMAGE;
  3648.     }
  3649.     else if( pInsertPoint->IsA(P_HRULE) ){
  3650.         return ED_ELEMENT_HRULE;
  3651.     }
  3652.     else if( pInsertPoint->IsA(P_LINEBREAK )){
  3653.         return ED_ELEMENT_TEXT;
  3654.     }
  3655.     else if( pInsertPoint->GetElementType() == eTargetElement ){
  3656.         return ED_ELEMENT_TARGET;
  3657.     }
  3658.     else if( pInsertPoint->GetElementType() == eIconElement ){
  3659.         return ED_ELEMENT_UNKNOWN_TAG;
  3660.     }
  3661.     // current element type is unknown.
  3662.     XP_ASSERT( FALSE );
  3663.     return ED_ELEMENT_TEXT;     // so the compiler doesn't complain.
  3664. }
  3665.  
  3666. void CEditBuffer::MorphContainer( TagType t )
  3667. {
  3668.     VALIDATE_TREE(this);
  3669.  
  3670.     CEditSelection selection;
  3671.  
  3672.     // charley says this is happening.. Hmmmm...
  3673.     //XP_ASSERT( t != P_TEXT );
  3674.     if( t == P_TEXT ){
  3675.         // actually failed this assert.
  3676.         return;
  3677.     }
  3678.  
  3679.     // Only allow the type to be set to a text container type.
  3680.     // This is important. If we allow a different type to be
  3681.     // set, then CEditElement::SplitContainerTest will
  3682.     // potentially fail.
  3683.     if ( ! BitSet( edt_setTextContainer,  t  ) ) {
  3684.         XP_ASSERT(FALSE);
  3685.         return;
  3686.     }
  3687.  
  3688.     if( IsSelected() ){
  3689.         MorphContainerSelection(t, selection);
  3690.     }
  3691.     else if( IsTableOrCellSelected() )
  3692.     {
  3693.         if( GetFirstCellSelection(selection) )
  3694.         {
  3695.             BeginBatchChanges(kChangeAttributesCommandID);
  3696.             MorphContainerSelection(t, selection);
  3697.             while( GetNextCellSelection(selection) )
  3698.             {
  3699.                 MorphContainerSelection(t, selection);
  3700.             }
  3701.             RelayoutSelectedTable();
  3702.             EndBatchChanges();
  3703.         }
  3704.     } 
  3705.     else {
  3706.         CEditElement *pContainer = m_pCurrent->FindContainer();
  3707.         // HACKOLA: just poke in a new tag type.
  3708.         pContainer->SetType( t );
  3709.         // Need to relayout from previous line.  Line break distance is wrong.
  3710.         Relayout( pContainer, 0, 0 );
  3711.     }
  3712. }
  3713.  
  3714. void CEditBuffer::MorphContainerSelection( TagType t, CEditSelection& selection )
  3715. {
  3716.     CEditLeafElement *pBegin, *pEnd, *pCurrent;
  3717.     CEditContainerElement* pContainer = NULL;
  3718.     ElementOffset iBeginPos, iEndPos;
  3719.     XP_Bool bFromStart;
  3720.  
  3721.     // Don't relayout if we got a cell selection passed in
  3722.     XP_Bool bRelayout = selection.IsEmpty();
  3723.  
  3724.     // Get elements from supplied selection or get current selection
  3725.     GetSelection( selection, pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3726.  
  3727.     pCurrent = pBegin;
  3728.     XP_Bool bDone = FALSE;
  3729.     do {
  3730.         pContainer = pCurrent->FindContainer();
  3731.         pContainer->SetType( t );
  3732.  
  3733.         bDone = (pEnd == pCurrent );    // For most cases
  3734.         pCurrent = pCurrent->NextLeafAll();
  3735.         bDone = bDone || (iEndPos == 0 && pEnd == pCurrent ); // Pesky edge conditions!
  3736.     } while( pCurrent && !bDone );
  3737.  
  3738.     if( bRelayout )
  3739.     {
  3740.         // force layout stop displaying the current selection.
  3741. #ifdef LAYERS
  3742.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  3743. #else
  3744.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  3745. #endif
  3746.         // probably needs to be the common ancestor.
  3747.         Relayout( pBegin->FindContainer(), 0, pEnd, RELAYOUT_NOCARET );
  3748.         // Need to force selection.
  3749.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3750.     }
  3751. }
  3752.  
  3753.  
  3754. void CEditBuffer::MorphListContainer( TagType t )
  3755. {
  3756.     CEditSelection selection;
  3757.     if( IsTableOrCellSelected() )
  3758.     {
  3759.         if( GetFirstCellSelection(selection) )
  3760.         {
  3761.             BeginBatchChanges(kChangeAttributesCommandID);
  3762.             MorphListContainer2(t, selection);
  3763.             while( GetNextCellSelection(selection) )
  3764.             {
  3765.                 MorphListContainer2(t, selection);
  3766.             }
  3767.             RelayoutSelectedTable();
  3768.             EndBatchChanges();
  3769.         }
  3770.     }
  3771.     else
  3772.     {
  3773.         MorphListContainer2(t, selection);
  3774.     }
  3775. }
  3776.  
  3777. void CEditBuffer::MorphListContainer2( TagType t, CEditSelection& /*selection*/ )
  3778. {
  3779.     //TODO: HANDLE LISTS IN SELECTION
  3780.     EDT_ListData * pListData = GetListData();
  3781.     if( !pListData ){
  3782.         // We don't have container -- start one
  3783.         Indent();
  3784.         pListData = GetListData();
  3785.         // Block quote can be any paragraph style,
  3786.         // Description list should contain only <DT> and <DD>,
  3787.         //   others must be list item
  3788.     } else if( t != P_BLOCKQUOTE && t != P_DESC_LIST ){
  3789.         MorphContainer(P_LIST_ITEM);
  3790.     }
  3791.     if( pListData ){
  3792.         pListData->iTagType = t;
  3793.         if( t != P_BLOCKQUOTE ){
  3794.             pListData->eType = ED_LIST_TYPE_DEFAULT;
  3795.         }
  3796.         SetListData(pListData);
  3797.         EDT_FreeListData(pListData);
  3798.     }
  3799. }
  3800.  
  3801. void CEditBuffer::ToggleList(intn iTagType)
  3802. {
  3803.     CEditSelection selection;
  3804.     if( IsTableOrCellSelected() )
  3805.     {
  3806.         if( GetFirstCellSelection(selection) )
  3807.         {
  3808.             BeginBatchChanges(kChangeAttributesCommandID);
  3809.             ToggleList2(iTagType, selection);
  3810.             while( GetNextCellSelection(selection) )
  3811.             {
  3812.                 ToggleList2(iTagType, selection);
  3813.             }
  3814.             RelayoutSelectedTable();
  3815.             EndBatchChanges();
  3816.         }
  3817.     } else {
  3818.         ToggleList2(iTagType, selection);
  3819.     }
  3820. }
  3821.  
  3822. // This doesn't seem to pay attention to selection - probably source of some bugs
  3823. void CEditBuffer::ToggleList2(intn iTagType, CEditSelection& /*selection*/)
  3824. {
  3825.     EDT_ListData * pData = NULL;
  3826.     TagType nParagraphFormat = GetParagraphFormatting();
  3827.     XP_Bool bIsMyList = FALSE;
  3828.     XP_Bool bIsDescList = FALSE;
  3829.  
  3830.     if ( nParagraphFormat == P_LIST_ITEM || iTagType == P_DESC_LIST ) {
  3831.         pData = GetListData();
  3832.         bIsMyList = ( pData && pData->iTagType == iTagType );
  3833.     }
  3834.     bIsDescList = bIsMyList && (iTagType == P_DESC_LIST);
  3835.  
  3836.     if ( (nParagraphFormat == P_LIST_ITEM || bIsDescList)
  3837.          && bIsMyList ) {
  3838.         // WILL THIS ALONE UNDO THE LIST?
  3839.         MorphContainer(P_NSDT);
  3840.         Outdent();
  3841.     } else {
  3842.         if ( !pData ) {
  3843.             // Must be indendented
  3844.             Indent();
  3845.             // Create a numbered list item ONLY if not Description list
  3846.             if (nParagraphFormat != P_LIST_ITEM && iTagType != P_DESC_LIST) {
  3847.                 MorphContainer(P_LIST_ITEM);
  3848.             }
  3849.             pData = GetListData();
  3850.         } else if (nParagraphFormat == P_LIST_ITEM && iTagType == P_DESC_LIST ){
  3851.             // We are converting an existing list item into
  3852.             //   a description, so remove the list item
  3853.             MorphContainer(P_DESC_TITLE);
  3854.             pData = GetListData();
  3855.         }
  3856.  
  3857.  
  3858.         if ( pData && (pData->iTagType != iTagType) ){
  3859.             pData->iTagType = iTagType;
  3860.             pData->eType = ED_LIST_TYPE_DEFAULT;
  3861.             SetListData(pData);
  3862.         }
  3863.     }
  3864.     if ( pData ) {
  3865.         EDT_FreeListData(pData);
  3866.     }
  3867. }
  3868.  
  3869. void CEditBuffer::SetParagraphAlign( ED_Alignment eAlign ){
  3870.     VALIDATE_TREE(this);
  3871.     XP_Bool bDone;
  3872.  
  3873.     // To avoid extra HTML params, use default
  3874.     //  for left align since that's how layout will do it
  3875.     //cmanske: Windows FE was doing this - moved logic here for consistency
  3876.     // TODO: This is messy for table cells: We need to check if ROW
  3877.     //   has set alignment and NOT do this if it isn't already ED_ALIGN_DEFAULT,
  3878.     //   otherwise cell gets the row's alignment
  3879.     if( eAlign == ED_ALIGN_LEFT )
  3880.         eAlign = ED_ALIGN_DEFAULT;
  3881.  
  3882.     if( IsSelected() ){
  3883.         CEditLeafElement *pBegin, *pEnd, *pCurrent;
  3884.         CEditContainerElement *pContainer = NULL;
  3885.         ElementOffset iBeginPos, iEndPos;
  3886.         XP_Bool bFromStart;
  3887.         GetSelection( pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3888.         pCurrent = pBegin;
  3889.         bDone = FALSE;
  3890.         do {
  3891.             pContainer = pCurrent->FindContainer();
  3892.             pContainer->Container()->SetAlignment( eAlign );
  3893.  
  3894.             bDone = (pEnd == pCurrent );    // For most cases
  3895.             pCurrent = pCurrent->NextLeafAll();
  3896.             bDone = bDone || (iEndPos == 0 && pEnd == pCurrent ); // Pesky edge conditions!
  3897.         } while( pCurrent && !bDone );
  3898.  
  3899.         // force layout stop displaying the current selection.
  3900. #ifdef LAYERS
  3901.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  3902. #else
  3903.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  3904. #endif
  3905.         // probably needs to be the common ancestor.
  3906.         Relayout( pBegin->FindContainer(), 0, pEnd, RELAYOUT_NOCARET );
  3907.         // Need to force selection.
  3908.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  3909.     }
  3910.     else if( m_pSelectedEdTable )
  3911.     {
  3912.         SetTableAlign(eAlign);
  3913.     }
  3914.     else if( m_SelectedEdCells.Size() )
  3915.     {
  3916.         // Tables are weird - must use ABSCENTER
  3917.         if( eAlign == ED_ALIGN_CENTER )
  3918.             eAlign = ED_ALIGN_ABSCENTER;
  3919.  
  3920.         int iCount = m_SelectedEdCells.Size();
  3921.         for( int i = 0; i < iCount; i++ )
  3922.         {
  3923.             // TODO: CHECK FOR ROW AND SET ALIGNMENT THERE INSTEAD
  3924.             EDT_TableCellData *pData = m_SelectedEdCells[i]->GetData();
  3925.             if( pData )
  3926.             {
  3927.                 pData->align = eAlign;
  3928.                 m_SelectedEdCells[i]->SetData( pData );
  3929.                 EDT_FreeTableCellData(pData);
  3930.             }
  3931.         }
  3932.         CEditTableElement *pTable = m_SelectedEdCells[0]->GetTableIgnoreSubdoc();
  3933.         if( pTable )
  3934.             Relayout( pTable, 0 );
  3935.     }
  3936.     else {
  3937.         CEditElement *pContainer = m_pCurrent->FindContainer();
  3938.         // HACKOLA: just poke in a new tag type.
  3939.         pContainer->Container()->SetAlignment( eAlign );
  3940.         // Need to Relayout from previous line.  Line break distance is wrong.
  3941.         Relayout( pContainer, 0, pContainer->GetLastMostChild() );
  3942.     }
  3943. }
  3944.  
  3945. ED_Alignment CEditBuffer::GetParagraphAlign( ){
  3946.     CEditLeafElement *pInsertPoint;
  3947.     ElementOffset iOffset;
  3948.  
  3949.     GetPropertyPoint( &pInsertPoint, &iOffset );
  3950.     if( pInsertPoint ) {
  3951.         CEditContainerElement* pCont = pInsertPoint->FindContainer();
  3952.         if ( pCont != 0){
  3953.             return pCont->GetAlignment();
  3954.         }
  3955.     }
  3956.     return ED_ALIGN_DEFAULT;
  3957. }
  3958.  
  3959. void CEditBuffer::SetTableAlign( ED_Alignment eAlign )
  3960. {
  3961.     if( IsInsertPointInTable() )
  3962.     {
  3963.         // To avoid extra HTML params, use default
  3964.         //  for left align since that's how layout will do it
  3965.         if( eAlign == ED_ALIGN_LEFT )
  3966.             eAlign = ED_ALIGN_DEFAULT;
  3967.         
  3968.         // Tables need ABSCENTER or they don't center!
  3969.         if( eAlign == ED_ALIGN_CENTER )
  3970.             eAlign = ED_ALIGN_ABSCENTER;
  3971.  
  3972.         EDT_TableData* pData = GetTableData();
  3973.         if( pData )
  3974.         {
  3975.             if( eAlign != pData->align )
  3976.             {
  3977.                 pData->align = eAlign;
  3978.                 SetTableData(pData);
  3979.             }
  3980.             EDT_FreeTableData(pData);
  3981.         }
  3982.     }        
  3983. }
  3984.  
  3985. void CEditBuffer::SetFontPointSize( int iPoints )
  3986. {
  3987.     EDT_CharacterData * pData;
  3988.     CEditSelection selection;
  3989.  
  3990.     if( IsTableOrCellSelected() )
  3991.     {
  3992.         // Format each cell's contents
  3993.         if( GetFirstCellSelection(selection) )
  3994.         {
  3995.             BeginBatchChanges(kChangeAttributesCommandID);
  3996.             pData = GetCharacterDataSelection(0, selection);
  3997.             if( pData )
  3998.             {
  3999.                 pData->iPointSize = iPoints;
  4000.                 SetCharacterDataSelection(pData, selection, FALSE);
  4001.                 EDT_FreeCharacterData(pData);
  4002.                 while( GetNextCellSelection(selection) )
  4003.                 {
  4004.                     pData = GetCharacterDataSelection(pData, selection);
  4005.                     if( pData )
  4006.                     {
  4007.                         pData->iPointSize = iPoints;
  4008.                         SetCharacterDataSelection(pData, selection, FALSE);
  4009.                         EDT_FreeCharacterData(pData);
  4010.                     }
  4011.                 }
  4012.             }
  4013.             RelayoutSelectedTable();
  4014.             EndBatchChanges();
  4015.         }
  4016.         return;
  4017.     } 
  4018.  
  4019.     pData = GetCharacterData();
  4020.     if( pData ){
  4021.         pData->mask = pData->values = TF_FONT_POINT_SIZE;
  4022.         pData->iPointSize = (int16)iPoints;
  4023.         // This handles selected tables correctly
  4024.         SetCharacterData(pData);
  4025.         EDT_FreeCharacterData(pData);
  4026.     }
  4027. }
  4028.  
  4029. void CEditBuffer::SetFontSize( int iSize ){
  4030.     VALIDATE_TREE(this);
  4031.  
  4032.     CEditSelection selection;
  4033.     GetSelection(selection);
  4034.     if ( selection.IsContainerEnd() )
  4035.         return;
  4036.     else if( selection.AnyLeavesSelected() ){
  4037.         BeginBatchChanges(kChangeAttributesCommandID);
  4038.         SetFontSizeSelection( iSize, selection, TRUE );
  4039.         EndBatchChanges();
  4040.         return;
  4041.     }
  4042.     if( IsTableOrCellSelected() )
  4043.     {
  4044.         if( GetFirstCellSelection(selection) )
  4045.         {
  4046.             BeginBatchChanges(kChangeAttributesCommandID);
  4047.             // Format first selected cell
  4048.             SetFontSizeSelection( iSize, selection, FALSE );
  4049.             // Select all other cells and format them
  4050.             while( GetNextCellSelection(selection) )
  4051.             {
  4052.                 SetFontSizeSelection( iSize, selection, FALSE );
  4053.             }
  4054.             RelayoutSelectedTable();
  4055.             EndBatchChanges();
  4056.         }
  4057.         return;
  4058.     } 
  4059.  
  4060.     FixupInsertPoint();
  4061.     CEditLeafElement *pRight = m_pCurrent->Divide( m_iCurrentOffset )->Leaf();
  4062.     if( pRight->IsA(P_TEXT)
  4063.             && pRight->Text()->GetLen() == 0 ) {
  4064.     //    && m_pCurrent == pRight
  4065.     // Bug 27891, this term in the conditional test should not be here, e.g.
  4066.     // in CEditBuffer::SetCharacterData() it is not there.
  4067.         pRight->Text()->SetFontSize(iSize);
  4068.         m_pCurrent = pRight;
  4069.         m_iCurrentOffset = 0;
  4070.     }
  4071.     else {
  4072.         CEditTextElement *pNew = m_pCurrent->CopyEmptyText();
  4073.         pNew->InsertBefore(pRight);
  4074.         pNew->SetFontSize( iSize );
  4075.         m_pCurrent = pNew;
  4076.         m_iCurrentOffset = 0;
  4077.     }
  4078.     Relayout( m_pCurrent, 0, 0 );
  4079. }
  4080.  
  4081. void CEditBuffer::SetFontSizeSelection( int iSize, CEditSelection& selection, XP_Bool bRelayout)
  4082. {
  4083.     VALIDATE_TREE(this);
  4084.     CEditLeafElement *pCurrent, *pEnd, *pBegin, *pNext;
  4085.     ED_BufferOffset iBeginPos, iEndPos;
  4086.     XP_Bool bFromStart;
  4087.  
  4088.     // Get data from supplied selection or get current selection
  4089.     GetSelection( selection, pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  4090.  
  4091.     //
  4092.     // Guarantee that the text blocks of the beginning and end of selection
  4093.     //  are atomic.
  4094.     //
  4095.     MakeSelectionEndPoints(selection, pBegin, pEnd );
  4096.     pCurrent = pBegin;
  4097.  
  4098.     while( pCurrent != pEnd ){
  4099.         pNext = pCurrent->NextLeafAll();
  4100.         if( pCurrent->IsA( P_TEXT ) &&
  4101.             ED_IS_NOT_SCRIPT(pCurrent) ){
  4102.             pCurrent->Text()->SetFontSize(iSize);
  4103.         }
  4104.         pCurrent = pNext;
  4105.     }
  4106.     if( bRelayout )
  4107.     {
  4108.         // force layout stop displaying the current selection.
  4109. #ifdef LAYERS
  4110.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL);
  4111. #else
  4112.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  4113. #endif
  4114.         // probably needs to be the common ancestor.
  4115.         Relayout( pBegin, 0, pEnd, RELAYOUT_NOCARET );
  4116.         // Need to force selection.
  4117.         SelectRegion(pBegin, 0, pEnd, 0);
  4118.     }
  4119.     //TODO: Should we do this for cell "selection"?
  4120.     Reduce(pBegin->GetCommonAncestor(pEnd));
  4121. }
  4122.  
  4123. void CEditBuffer::SetFontColor( ED_Color iColor )
  4124. {
  4125.     // ToDo: Eliminate this function in favor of SetCharacterData
  4126.     VALIDATE_TREE(this);
  4127.  
  4128.     CEditSelection selection;
  4129.     GetSelection(selection);
  4130.     if ( selection.IsContainerEnd() )
  4131.         return;
  4132.     else if( selection.AnyLeavesSelected() )
  4133.     {
  4134.         BeginBatchChanges(kChangeAttributesCommandID);
  4135.         SetFontColorSelection( iColor, selection, TRUE );
  4136.         EndBatchChanges();
  4137.         return;
  4138.     }
  4139.     if( IsTableOrCellSelected() )
  4140.     {
  4141.         // Format each cell
  4142.         if( GetFirstCellSelection(selection) )
  4143.         {
  4144.             BeginBatchChanges(kChangeAttributesCommandID);
  4145.             // FALSE = don't relayout
  4146.             SetFontColorSelection( iColor, selection, FALSE );
  4147.             while( GetNextCellSelection(selection) )
  4148.             {
  4149.                 SetFontColorSelection( iColor, selection, FALSE );
  4150.             }
  4151.             RelayoutSelectedTable();
  4152.             EndBatchChanges();
  4153.         }
  4154.         return;
  4155.     } 
  4156.  
  4157.     FixupInsertPoint();
  4158.     CEditLeafElement *pRight = m_pCurrent->Divide( m_iCurrentOffset )->Leaf();
  4159.     if( pRight->IsA(P_TEXT)
  4160.             && pRight->Leaf()->GetLen() == 0 ) {
  4161.     //    && m_pCurrent == pRight
  4162.     // Bug 27891, this term in the conditional test should not be here, e.g.
  4163.     // in CEditBuffer::SetCharacterData() it is not there.
  4164.         pRight->Text()->SetColor( iColor );
  4165.         m_pCurrent = pRight;
  4166.         m_iCurrentOffset = 0;
  4167.     }
  4168.     else {
  4169.         CEditTextElement *pNew = m_pCurrent->CopyEmptyText();
  4170.         pNew->InsertBefore(pRight);
  4171.         pNew->SetColor( iColor );
  4172.         m_pCurrent = pNew;
  4173.         m_iCurrentOffset = 0;
  4174.     }
  4175.     Relayout( m_pCurrent, 0, 0 );
  4176. }
  4177.  
  4178. void CEditBuffer::SetFontColorSelection( ED_Color iColor, CEditSelection& selection, XP_Bool bRelayout )
  4179. {
  4180.     VALIDATE_TREE(this);
  4181.     CEditLeafElement *pCurrent, *pEnd, *pBegin, *pNext;
  4182.     ED_BufferOffset iBeginPos, iEndPos;
  4183.     XP_Bool bFromStart;
  4184.  
  4185.     // Get data from supplied selection or get current selection
  4186.     GetSelection( selection, pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  4187.  
  4188.     //
  4189.     // Guarantee that the text blocks of the beginning and end of selection
  4190.     //  are atomic.
  4191.     //
  4192.     MakeSelectionEndPoints(selection, pBegin, pEnd );
  4193.     pCurrent = pBegin;
  4194.  
  4195.     while( pCurrent != pEnd ){
  4196.         pNext = pCurrent->NextLeafAll();
  4197.         if( pCurrent->IsA( P_TEXT ) ){
  4198.             pCurrent->Text()->SetColor( iColor );
  4199.         }
  4200.         pCurrent = pNext;
  4201.     }
  4202.     if( bRelayout )
  4203.     {
  4204.         // force layout stop displaying the current selection.
  4205. #ifdef LAYERS
  4206.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL);
  4207. #else
  4208.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  4209. #endif
  4210.         // probably needs to be the common ancestor.
  4211.         Relayout( pBegin, 0, pEnd, RELAYOUT_NOCARET );
  4212.         // Need to force selection.
  4213.         SelectRegion(pBegin, 0, pEnd, 0);
  4214.     }
  4215.     Reduce(pBegin->GetCommonAncestor(pEnd));
  4216. }
  4217.  
  4218.  
  4219. void CEditBuffer::GetHREFData( EDT_HREFData *pData ){
  4220.     //TODO: Rewrite GetHREF to handle the target!
  4221.     ED_LinkId id = GetHREFLinkID();
  4222.     char* pURL = NULL;
  4223.     char* pExtra = NULL;
  4224.     if( id != ED_LINK_ID_NONE ){
  4225.         EDT_HREFData* pOriginal = linkManager.GetHREFData(id);
  4226.         if ( pOriginal ) {
  4227.             pURL = pOriginal->pURL;
  4228.             pExtra = pOriginal->pExtra;
  4229.         }
  4230.     }
  4231.     pData->pURL = pURL ? XP_STRDUP(pURL) : 0;
  4232.     pData->pExtra = pExtra ? XP_STRDUP(pExtra) : 0;
  4233. }
  4234.  
  4235. void CEditBuffer::SetHREFData( EDT_HREFData *pData ){
  4236.     SetHREF(pData->pURL, pData->pExtra);
  4237. }
  4238.  
  4239. void CEditBuffer::SetHREF( char *pHref, char *pExtra ){
  4240.     VALIDATE_TREE(this);
  4241.     ED_LinkId id;
  4242.     ED_LinkId oldId;
  4243.     CEditElement *pPrev, *pNext, *pStart, *pEnd;
  4244.     XP_Bool bSame;
  4245.     pStart = m_pCurrent;
  4246.     pEnd = 0;
  4247.  
  4248.     //
  4249.     // if we are setting link data and the current selection or word
  4250.     //  has extra data, we should preserve it.
  4251.     //
  4252.     if( pHref && *pHref!= '\0'){
  4253.         EDT_CharacterData* pCD = 0;
  4254.         if( pExtra == 0 ){
  4255.             pCD = GetCharacterData();
  4256.             if( pCD
  4257.                     && (pCD->mask & TF_HREF)
  4258.                     && (pCD->values & TF_HREF)
  4259.                     && pCD->pHREFData
  4260.                     && pCD->pHREFData->pExtra ){
  4261.                 pExtra = pCD->pHREFData->pExtra;
  4262.             }
  4263.         }
  4264.         id  = linkManager.Add( pHref, pExtra );
  4265.         if( pCD ){
  4266.             EDT_FreeCharacterData( pCD );
  4267.         }
  4268.     }
  4269.     else {
  4270.         id = ED_LINK_ID_NONE;
  4271.     }
  4272.  
  4273.     CEditSelection selection;
  4274.     GetSelection(selection);
  4275.     if ( selection.IsContainerEnd() )
  4276.         return;
  4277.     else if( selection.AnyLeavesSelected() ){
  4278.         BeginBatchChanges(kChangeAttributesCommandID);
  4279.         SetHREFSelection( id );
  4280.         EndBatchChanges();
  4281.     }
  4282.     else {
  4283.         BeginBatchChanges(kChangeAttributesCommandID);
  4284.         //FixupInsertPoint();
  4285.         // check all text around us.
  4286.         oldId = m_pCurrent->GetHREF();
  4287.         m_pCurrent->SetHREF( id );
  4288.         pPrev = m_pCurrent->PreviousLeafInContainer();
  4289.         bSame = TRUE;
  4290.         while( pPrev && bSame ){
  4291.             bSame = pPrev->Leaf()->GetHREF() == oldId;
  4292.             if( bSame ){
  4293.                 pPrev->Leaf()->SetHREF( id );
  4294.                 pStart = pPrev;
  4295.             }
  4296.             pPrev = pPrev->PreviousLeafInContainer();
  4297.         }
  4298.         pNext = m_pCurrent->LeafInContainerAfter();
  4299.         bSame = TRUE;
  4300.         while( pNext && bSame ){
  4301.             bSame = pNext->Leaf()->GetHREF() == oldId;
  4302.             if( bSame ){
  4303.                 pNext->Leaf()->SetHREF( id );
  4304.                 pEnd = pNext;
  4305.             }
  4306.             pNext = pNext->LeafInContainerAfter();
  4307.         }
  4308.         Relayout( pStart, 0, pEnd );
  4309.         EndBatchChanges();
  4310.     }
  4311.     if( id != ED_LINK_ID_NONE ){
  4312.         linkManager.Free(id);
  4313.     }
  4314. }
  4315.  
  4316. // Use this only when setting one attribute at a time
  4317. // Always make Superscript and Subscript mutually exclusive - very tricky!
  4318. void CEditBuffer::FormatCharacter( ED_TextFormat tf ){
  4319.     VALIDATE_TREE(this);
  4320.     CEditSelection selection;
  4321.     GetSelection(selection);
  4322.     if ( selection.IsContainerEnd() )
  4323.         return;
  4324.  
  4325.     if( selection.AnyLeavesSelected() ){
  4326.         BeginBatchChanges(kChangeAttributesCommandID);
  4327.         FormatCharacterSelection(tf, selection, TRUE);
  4328.         EndBatchChanges();
  4329.         return;
  4330.     }
  4331.     if( IsTableOrCellSelected() )
  4332.     {
  4333.         // Default is to clear existing attribute
  4334.         // Use this if ANY cell has ANY bold elements at all
  4335.         intn iSetFormat = ED_CLEAR_FORMAT;
  4336.  
  4337.         // Get each cell contents as a selection
  4338.         //   and determine if we will set or clear the attribute
  4339.         if( GetFirstCellSelection(selection) )
  4340.         {
  4341.             // FALSE means we don't need to save/restore insert point
  4342.             XP_Bool bSetFormat = GetSetStateForCharacterSelection(tf, selection);
  4343.             if( bSetFormat )
  4344.             {
  4345.                 XP_Bool bFoundCell; 
  4346.                 while( (bFoundCell = GetNextCellSelection(selection)) == TRUE )
  4347.                 {
  4348.                     // As soon as we hit a cell that wants us to clear format,
  4349.                     //  we are done -- we will clear format for ALL cells
  4350.                     if( !GetSetStateForCharacterSelection(tf, selection) )
  4351.                         break;
  4352.                 }
  4353.                 // If we ran through all cells without finding one
  4354.                 //   to clear, then we should SET the format for all cells
  4355.                 if( !bFoundCell )                
  4356.                     iSetFormat = ED_SET_FORMAT;
  4357.             }
  4358.  
  4359.             // Go through cells again and set the character format
  4360.             GetFirstCellSelection(selection);
  4361.             BeginBatchChanges(kChangeAttributesCommandID);
  4362.             // Format first cell
  4363.             FormatCharacterSelection(tf, selection, FALSE, iSetFormat );
  4364.             // Format all other cells
  4365.             while( GetNextCellSelection(selection) )
  4366.             {
  4367.                 FormatCharacterSelection(tf, selection, FALSE, iSetFormat);
  4368.             }
  4369.             RelayoutSelectedTable();
  4370.             EndBatchChanges();
  4371.         }
  4372.         return;
  4373.     } 
  4374.  
  4375.     FixupInsertPoint();
  4376.     CEditLeafElement *pRight = m_pCurrent->Divide( m_iCurrentOffset )->Leaf();
  4377.     if( pRight->IsA(P_TEXT) && pRight->Leaf()->GetLen() == 0 ){
  4378.         if( tf == TF_NONE ){
  4379.             pRight->Text()->ClearFormatting();
  4380.         }
  4381. #ifdef USE_SCRIPT
  4382.         else if( tf == TF_SERVER || tf == TF_SCRIPT || tf == TF_STYLE ){
  4383.             pRight->Text()->ClearFormatting();
  4384.             pRight->Text()->m_tf = tf;
  4385.         }
  4386. #endif
  4387.         // If we are in either Java Script type, IGNORE change in format
  4388.         else if( ED_IS_NOT_SCRIPT(pRight) ){
  4389.             // Make superscript and subscript mutually-exclusive
  4390.             // if already either one type, and we are setting the opposite,
  4391.             //  clear this bit first
  4392.             if( tf == TF_SUPER && (pRight->Text()->m_tf & TF_SUB) ){
  4393.                 pRight->Text()->m_tf &= ~TF_SUB;
  4394.             } else if( tf == TF_SUB && (pRight->Text()->m_tf & TF_SUPER) ){
  4395.                 pRight->Text()->m_tf &= ~TF_SUPER;
  4396.             }
  4397.             // Fixed font attribute overrides font face
  4398.             if( tf == TF_FIXED && (pRight->Text()->m_tf & TF_FONT_FACE) ){
  4399.                 pRight->Text()->m_tf &= ~TF_FONT_FACE;
  4400.             }
  4401.             pRight->Text()->m_tf ^= tf;
  4402.         }
  4403.         m_pCurrent = pRight;
  4404.         m_iCurrentOffset = 0;
  4405.     }
  4406.     else {
  4407.         // We are making new text element, so we don't need
  4408.         //   to worry about existing Java Script or Super/Subscript stuff
  4409.         CEditTextElement *pNew = m_pCurrent->CopyEmptyText();
  4410.         pNew->InsertBefore(pRight);
  4411.         if( tf == TF_NONE ){
  4412.             pNew->ClearFormatting();
  4413.         }
  4414.         else {
  4415.             if( tf == TF_SUPER && (pNew->Text()->m_tf & TF_SUB) ){
  4416.                 pNew->Text()->m_tf &= ~TF_SUB;
  4417.             } else if( tf == TF_SUB && (pNew->Text()->m_tf & TF_SUPER) ){
  4418.                 pNew->Text()->m_tf &= ~TF_SUPER;
  4419.             }
  4420.             pNew->m_tf ^= tf;
  4421.         }
  4422.         m_pCurrent = pNew;
  4423.         m_iCurrentOffset = 0;
  4424.     }
  4425.     Relayout( m_pCurrent, 0, 0 );
  4426. }
  4427.  
  4428. XP_Bool CEditBuffer::GetSetStateForCharacterSelection( ED_TextFormat tf, CEditSelection& selection )
  4429. {
  4430.     if( selection.IsEmpty() )
  4431.         return FALSE;
  4432.     //
  4433.     // Guarantee that the text blocks of the beginning and end of selection
  4434.     //  are atomic.
  4435.     //
  4436. //    MakeSelectionEndPoints(selection, pBegin, pEnd );
  4437.     // To be more efficient, do just what is necessary, since we only need the first element
  4438.     CEditLeafElement *pBegin = selection.m_start.m_pElement->Divide( selection.m_start.m_iPos )->Leaf();
  4439.  
  4440.     // We set the attribute if first element doesn't already have it,
  4441.     //   else we will clear existing attribute
  4442.     return !(pBegin->Text()->m_tf & tf);
  4443. }
  4444.  
  4445. void CEditBuffer::FormatCharacterSelection( ED_TextFormat tf, CEditSelection& selection,  XP_Bool bRelayout, intn iSetState )
  4446. {
  4447.     VALIDATE_TREE(this);
  4448.     CEditLeafElement *pCurrent, *pEnd, *pBegin, *pNext;
  4449.     XP_Bool bSet = (iSetState == ED_SET_FORMAT);
  4450.     // Passed in value determines if we figure out whether to set below,
  4451.     //   or just use state passed in.
  4452.     XP_Bool bHaveSet = (iSetState != ED_GET_FORMAT);
  4453.  
  4454.     // Get current selection if supplied with empty selection
  4455.     if( selection.IsEmpty() )
  4456.     {
  4457.         GetSelection(selection);
  4458.         bRelayout = TRUE;
  4459.     }
  4460.  
  4461.     //
  4462.     // Guarantee that the text blocks of the beginning and end of selection
  4463.     //  are atomic.
  4464.     //
  4465.     MakeSelectionEndPoints(selection, pBegin, pEnd );
  4466.  
  4467.     pCurrent = pBegin;
  4468.  
  4469.     while( pCurrent != pEnd && pCurrent )
  4470.     {
  4471.         pNext = pCurrent->NextLeafAll();
  4472.         XP_ASSERT(pNext);
  4473.  
  4474.         if( pCurrent->IsA( P_TEXT ) )
  4475.         {
  4476.             // figure out whether we are setting or clearing...
  4477.             if( !bHaveSet )
  4478.             {
  4479.                 bSet = !(pCurrent->Text()->m_tf & tf);
  4480.                 bHaveSet = TRUE;
  4481.             }
  4482.  
  4483.             if( tf == TF_NONE )
  4484.             {
  4485.                 pCurrent->Text()->ClearFormatting();
  4486.             }
  4487. #ifdef USE_SCRIPT
  4488.             else if (tf == TF_SERVER || tf == TF_SCRIPT || tf == TF_STYLE)
  4489.             {
  4490.                 pCurrent->Text()->ClearFormatting();
  4491.                 pCurrent->Text()->m_tf = tf;
  4492.             }
  4493. #endif
  4494.             else 
  4495.             {
  4496.                 if( bSet && pCurrent->IsA( P_TEXT ) )
  4497.                 {
  4498.                     // If we are in either Java Script type, IGNORE change in format
  4499.                     if( ED_IS_NOT_SCRIPT(pCurrent) )
  4500.                     {
  4501.                         // Make superscript and subscript mutually-exclusive
  4502.                         if( tf == TF_SUPER && (pCurrent->Text()->m_tf & TF_SUB) )
  4503.                         {
  4504.                             pCurrent->Text()->m_tf &= ~TF_SUB;
  4505.                         } else if( tf == TF_SUB && (pCurrent->Text()->m_tf & TF_SUPER) )
  4506.                         {
  4507.                             pCurrent->Text()->m_tf &= ~TF_SUPER;
  4508.                         }
  4509.                         pCurrent->Text()->m_tf |= tf;
  4510.                     }
  4511.                     // Fixed font attribute overrides font face
  4512.                     if( tf == TF_FIXED && (pCurrent->Text()->m_tf & TF_FONT_FACE) )
  4513.                     {
  4514.                         pCurrent->Text()->m_tf &= ~TF_FONT_FACE;
  4515.                     }
  4516.                 }
  4517.                 else 
  4518.                 {
  4519.                     pCurrent->Text()->m_tf &= ~tf;
  4520.                 }
  4521.             }
  4522.         }
  4523.         else if( pCurrent->IsImage() && tf == TF_NONE )
  4524.         {
  4525.             // Clear the link for the image within the selection
  4526.             pCurrent->Image()->SetHREF( ED_LINK_ID_NONE );
  4527.         }
  4528.         pCurrent = pNext;
  4529.     }
  4530.     CEditSelection tmp(pBegin, 0, pEnd, 0);
  4531.     
  4532.     // Only do this (which basically does relayout)
  4533.     //  if working with current selection, not cell selection
  4534.     if( bRelayout )
  4535.         RepairAndSet(tmp);
  4536. }
  4537.  
  4538. void CEditBuffer::SetCharacterData( EDT_CharacterData *pData )
  4539. {
  4540.     VALIDATE_TREE(this);
  4541.     CEditSelection selection;
  4542.     GetSelection(selection);
  4543.     if ( selection.IsContainerEnd() )
  4544.         return;
  4545.     else if( selection.AnyLeavesSelected() ){
  4546.         BeginBatchChanges(kChangeAttributesCommandID);
  4547.         SetCharacterDataSelection( pData, selection, TRUE );
  4548.         EndBatchChanges();
  4549.         return;
  4550.     }
  4551.     
  4552.     if( IsTableOrCellSelected() )
  4553.     {
  4554.         // Select each cell and format the contents
  4555.         if( GetFirstCellSelection(selection) )
  4556.         {
  4557.             BeginBatchChanges(kChangeAttributesCommandID);
  4558.             // Format first selected cell, don't relayout yet
  4559.             SetCharacterDataSelection(pData, selection, FALSE);
  4560.  
  4561.             // Select all other cells and format them
  4562.             while( GetNextCellSelection(selection) )
  4563.             {
  4564.                 SetCharacterDataSelection(pData, selection, FALSE);
  4565.             }
  4566.             RelayoutSelectedTable();
  4567.             EndBatchChanges();
  4568.         }
  4569.         return;
  4570.     } 
  4571.  
  4572.     FixupInsertPoint();
  4573.     CEditLeafElement *pRight = m_pCurrent->Divide( m_iCurrentOffset )->Leaf();
  4574.     if( pRight->IsA(P_TEXT) && pRight->Leaf()->GetLen() == 0 ){
  4575.         pRight->Text()->SetData( pData );
  4576.         m_pCurrent = pRight;
  4577.         m_iCurrentOffset = 0;
  4578.     }
  4579.     else {
  4580.         CEditTextElement *pNew = m_pCurrent->CopyEmptyText();
  4581.         pNew->InsertBefore(pRight);
  4582.         pNew->SetData( pData );
  4583.         m_pCurrent = pNew;
  4584.         m_iCurrentOffset = 0;
  4585.     }
  4586.     Relayout( m_pCurrent, 0, 0 );
  4587. }
  4588.  
  4589. void CEditBuffer::SetCharacterDataSelection( EDT_CharacterData *pData, CEditSelection& selection, XP_Bool bRelayout )
  4590. {
  4591.     VALIDATE_TREE(this);
  4592.     CEditLeafElement *pCurrent, *pEnd, *pBegin, *pNext;
  4593.  
  4594.     //
  4595.     // Guarantee that the text blocks of the beginning and end of selection
  4596.     //  are atomic.
  4597.     //
  4598.     MakeSelectionEndPoints( selection, pBegin, pEnd );
  4599.     pCurrent = pBegin;
  4600.  
  4601.     while( pCurrent != pEnd ){
  4602.         pNext = pCurrent->NextLeafAll();
  4603.         if( pCurrent->IsA( P_TEXT ) ){
  4604.             pCurrent->Text()->SetData( pData );
  4605.         }
  4606.         // Set just the HREF for an image
  4607.         else if( pCurrent->IsImage() &&
  4608.                  (pData->mask & TF_HREF) ){
  4609.             pCurrent->Image()->SetHREF( pData->linkId );
  4610.         }
  4611.         pCurrent = pNext;
  4612.     }
  4613.  
  4614.     if( bRelayout && !m_bNoRelayout )
  4615.     {
  4616.         // force layout stop displaying the current selection.
  4617.     #ifdef LAYERS
  4618.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  4619.     #else
  4620.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  4621.     #endif
  4622.         // probably needs to be the common ancestor.
  4623.         Relayout( pBegin, 0, pEnd, RELAYOUT_NOCARET );
  4624.         // Need to force selection.
  4625.         SelectRegion(pBegin, 0, pEnd, 0);
  4626.     }
  4627.     Reduce(pBegin->GetCommonAncestor(pEnd));
  4628. }
  4629.  
  4630. void CEditBuffer::SetCharacterDataAtOffset( EDT_CharacterData *pData,
  4631.                    ED_BufferOffset iBufOffset, int32 iLen ){
  4632.  
  4633. #if 0
  4634.     XP_ASSERT( m_bNoRelayout );  //put this back in when edt_paste does not relayout everything
  4635. #endif
  4636.     int32 i = iBufOffset+iLen;
  4637.     CPersistentEditSelection perSel;
  4638.     perSel = CPersistentEditSelection( CPersistentEditInsertPoint( iBufOffset ),
  4639.                 CPersistentEditInsertPoint( i ) );
  4640.  
  4641.     CEditSelection sel = PersistentToEphemeral( perSel );
  4642.  
  4643.     SetCharacterDataSelection( pData, sel, TRUE );
  4644. }
  4645.  
  4646. void CEditBuffer::SetRefresh( XP_Bool bRefreshOn ){
  4647.     if( bRefreshOn ){
  4648.         m_bNoRelayout = FALSE;
  4649.         RefreshLayout();
  4650.     }
  4651.     else {
  4652.         m_bNoRelayout = TRUE;
  4653.     }
  4654. }
  4655.  
  4656.  
  4657. // Separated from GetCharacterData
  4658. // If pData is supplied, then multiple-selections may be combined
  4659. EDT_CharacterData* CEditBuffer::GetCharacterDataSelection(EDT_CharacterData *pData, CEditSelection& selection)
  4660. {
  4661.     // Get current selection if not supplied
  4662.     if( selection.IsEmpty() )
  4663.         GetSelection( selection );
  4664.  
  4665.     CEditElement *pElement = selection.m_start.m_pElement;
  4666.  
  4667.     while( pElement && pElement != selection.m_end.m_pElement ){
  4668.         if( pElement->IsA( P_TEXT ) ){
  4669.             pElement->Text()->MaskData( pData );
  4670.         }
  4671.         else if( pElement->IsImage() ){
  4672.             // We report the HREF status if we find an image
  4673.             pElement->Image()->MaskData( pData );
  4674.         }
  4675.         pElement = pElement->NextLeaf();
  4676.     }
  4677.  
  4678.     // if the selection ends in the middle of a text block.
  4679.     if( pElement && selection.m_end.m_iPos != 0
  4680.             && selection.m_end.m_pElement->IsA(P_TEXT) ){
  4681.         pElement->Text()->MaskData( pData );
  4682.     }
  4683.  
  4684.     if( pData == 0 ){
  4685.         return EDT_NewCharacterData();
  4686.     }
  4687.     return pData;
  4688. }
  4689.  
  4690. EDT_CharacterData* CEditBuffer::GetCharacterData()
  4691. {
  4692.     CEditElement *pElement = m_pCurrent;
  4693.     CEditSelection selection;
  4694.  
  4695.     // The following assert fails once at program startup.
  4696.     // XP_ASSERT(pElement || IsSelected());
  4697.  
  4698.     //
  4699.     // While selecting, we may not have a valid region
  4700.     //
  4701.     if( IsSelecting() ){
  4702.         return EDT_NewCharacterData();
  4703.     }
  4704.  
  4705.     if( IsSelected() ){
  4706.         return GetCharacterDataSelection(0, selection);
  4707.     }
  4708.     if( IsTableOrCellSelected() )
  4709.     {
  4710.         EDT_CharacterData *pData = 0;
  4711.         if( GetFirstCellSelection(selection) )
  4712.         {
  4713.             // Start with data from first cell
  4714.             pData = GetCharacterDataSelection(pData, selection);
  4715.  
  4716.             // Pass in pData to combine with other cell's data
  4717.             while( GetNextCellSelection(selection) )
  4718.                 GetCharacterDataSelection(pData, selection);
  4719.         }
  4720.         return pData;
  4721.     } 
  4722.  
  4723.     if( pElement && pElement->IsA(P_TEXT ))
  4724.     {
  4725.         return pElement->Text()->GetData();;
  4726.     }
  4727.  
  4728.     // Try to find the previous text element and use its characterData
  4729.     if (pElement) {
  4730.         CEditTextElement *tElement = pElement->PreviousTextInContainer();
  4731.         if ( tElement){
  4732.             return tElement->GetData();
  4733.         }
  4734.     }
  4735.  
  4736.     // Create a dummy text element from scratch to guarantee
  4737.     // that we get the same EDT_CharacterData as when creating a new
  4738.     // CEditTextElement during typing.
  4739.     CEditTextElement dummy( (CEditElement *)0,0);
  4740.     return dummy.GetData();
  4741.  
  4742.     // return EDT_NewCharacterData();
  4743. }
  4744.  
  4745. char *CEditBuffer::GetTabDelimitedTextFromSelectedCells()
  4746. {
  4747.     char *pText = NULL;
  4748.     if( IsTableOrCellSelected() )
  4749.     {
  4750.         // Get each cell contents to examine - as if it were selected
  4751.         CEditTableCellElement *pCell = GetFirstSelectedCell();
  4752.         if( pCell )
  4753.         {
  4754.             // Get text from cell: TRUE = join paragraphs (no embeded CR/LF)
  4755.             pText = pCell->GetText(TRUE);
  4756.             if( pText )
  4757.             {
  4758.                 intn iPrevCounter = 0;
  4759.                 intn iRowCounter = 0;
  4760.                 while( (pCell = GetNextSelectedCell(&iRowCounter)) != NULL )
  4761.                 {
  4762.                     if( iRowCounter != iPrevCounter )
  4763.                     {
  4764.                         // We are in next row - so add row delimiter to previous text
  4765.                         pText = edt_AppendEndOfLine( pText);
  4766.                         iPrevCounter = iRowCounter;
  4767.                     } else {
  4768.                         // Next cell is in same row, so add TAB delimeter
  4769.                         pText = PR_sprintf_append( pText, "\t" );
  4770.                     }
  4771.  
  4772.                     // Append next cell's text
  4773.                     char *pCellText = pCell->GetText();
  4774.                     if( pCellText && *pCellText )
  4775.                         pText = PR_sprintf_append( pText, pCellText );
  4776.                 }
  4777.                 // Add last row delimiter at the end
  4778.                 pText = edt_AppendEndOfLine( pText);
  4779.             }
  4780.         }
  4781.     } 
  4782.     return pText;
  4783. }
  4784.  
  4785. // Convert Selected text into a table (put each paragraph in separate cell)
  4786. // Number of rows is automatic - creates as many as needed
  4787. void CEditBuffer::ConvertTextToTable(intn iColumns)
  4788. {
  4789.     CEditLeafElement *pBegin, *pEnd, *pCurrent;
  4790.     ElementOffset iBeginPos, iEndPos;
  4791.     XP_Bool bFromStart;
  4792.  
  4793.     // Get data from supplied selection or get current selection if empty
  4794.     CEditSelection s;
  4795.     GetSelection(s, pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  4796.     pCurrent = pBegin;
  4797.     XP_Bool bDone = FALSE;
  4798.     CEditContainerElement* pLastContainer = 0;
  4799.     CEditContainerElement* pContainer = s.m_start.m_pElement->FindContainer();
  4800.     // Insert a new table here first. Ask user how many columns.
  4801.  
  4802.     do {
  4803.         if( pContainer != pLastContainer ){
  4804.             // MOVE THIS CONTAINER (PARA) TO A NEW TABLE.
  4805.             pLastContainer = pContainer;
  4806.         }
  4807.         bDone = (pEnd == pCurrent );    // For most cases
  4808.         pCurrent = pCurrent->NextLeafAll();
  4809.         bDone = bDone || (iEndPos == 0 && pEnd == pCurrent ); // Pesky edge conditions!
  4810.     } while( pCurrent && !bDone );
  4811.  
  4812.     
  4813. }
  4814.  
  4815. // Convert the table into text - unravel existing paragraphs in cells
  4816. void CEditBuffer::ConvertTableToText()
  4817. {
  4818.     CEditTableElement *pTable;
  4819.     
  4820.     // Get the table to convert
  4821.     if( m_pSelectedEdTable )
  4822.     {
  4823.         pTable = m_pSelectedEdTable;
  4824.     }
  4825.     else
  4826.     {
  4827.         CEditInsertPoint ip;
  4828.         GetTableInsertPoint(ip);
  4829.         if( ip.m_pElement )
  4830.             pTable = ip.m_pElement->GetParentTable();
  4831.     }
  4832.     if( pTable )
  4833.     {
  4834.         // Be sure we don't have anything in this table selected
  4835.         ClearTableAndCellSelection();
  4836.  
  4837.         // Get the previous container to append to
  4838.         CEditContainerElement* pPrevContainer = pTable->PreviousContainer();
  4839.         CEditElement* pNextContainer = NULL;
  4840.         CEditElement* pLastContainer = NULL;
  4841.         if( !pPrevContainer )
  4842.             return; //TODO: CAN THIS HAPPEN??? HANLDE IT IF YES!
  4843.  
  4844.         CEditElement* pAppendContainer = pPrevContainer;
  4845.         CEditElement* pFirstContainer = NULL;
  4846.         CEditElement *pContainer = NULL;
  4847.  
  4848.         // Move all containers in each cell to previous container
  4849.         CEditTableCellElement *pCell = pTable->GetFirstCell();
  4850.         if( pCell )
  4851.         {
  4852.             BeginBatchChanges(kGroupOfChangesCommandID);
  4853.             while( pCell )
  4854.             {
  4855.                 // Get first container of the cell
  4856.                 pContainer = pCell->GetChild();
  4857.                 XP_ASSERT(pContainer->IsContainer());
  4858.  
  4859.                 // Save very first container while we're here
  4860.                 if( pFirstContainer == NULL )
  4861.                     pFirstContainer = pContainer;
  4862.  
  4863.                 // Move all containers
  4864.                 while( pContainer )
  4865.                 {
  4866.                     CEditElement* pNextContainer = pContainer->GetNextSibling();
  4867.                     pContainer->Unlink();
  4868.                     pContainer->InsertAfter(pAppendContainer);
  4869.                     pAppendContainer = pContainer;
  4870.  
  4871.                     // Save the last container moved
  4872.                     if( pContainer )
  4873.                         pLastContainer = pContainer;
  4874.  
  4875.                     pContainer = pNextContainer;
  4876.                 }
  4877.                 pCell = pTable->GetNextCellInTable();
  4878.             }
  4879.             
  4880.             // Connect the last moved container to
  4881.             //   the next one after table we are deleting
  4882.             if( pLastContainer )
  4883.             {
  4884.                 pNextContainer = pLastContainer->NextContainer();
  4885.             }
  4886.             delete pTable;
  4887.  
  4888.             if( pNextContainer )
  4889.                 pLastContainer->SetNextSibling(pNextContainer);
  4890.  
  4891.  
  4892.             if( pNextContainer && pLastContainer )
  4893.                 pLastContainer->SetNextSibling(pNextContainer);
  4894.  
  4895.             // Move insert point to first moved element
  4896.             if( pFirstContainer )
  4897.             {
  4898.                 CEditInsertPoint ip(pFirstContainer->GetFirstMostChild(), 0);
  4899.                 SetInsertPoint(ip);
  4900.             }        
  4901.             if( pNextContainer )
  4902.             {
  4903.                 Relayout(pPrevContainer->GetFirstMostChild(), 0, pNextContainer->GetFirstMostChild(), 0);
  4904.             } else {
  4905.                 // Deep doodoo if we didn't get all containers around the table!
  4906.                 XP_ASSERT(FALSE);
  4907.             }
  4908.  
  4909. #ifdef DEBUG
  4910.             m_pRoot->ValidateTree();
  4911. #endif
  4912.  
  4913.             EndBatchChanges();
  4914.         }
  4915.     }
  4916. }
  4917.  
  4918. // Save the character and paragraph style of selection or at caret
  4919. void CEditBuffer::CopyStyle()
  4920. {
  4921.     if( m_pCopyStyleCharacterData )
  4922.         EDT_FreeCharacterData(m_pCopyStyleCharacterData);
  4923.     
  4924.     m_pCopyStyleCharacterData = GetCharacterData();
  4925. }
  4926.  
  4927. // Apply the style to selection or at caret. Use bApplyStyle = FALSE to cancel
  4928. void CEditBuffer::PasteStyle(XP_Bool bApplyStyle)
  4929. {
  4930.     if( m_pCopyStyleCharacterData )
  4931.     {
  4932.         if( bApplyStyle )
  4933.         {
  4934.             SetCharacterData(m_pCopyStyleCharacterData);
  4935.         }
  4936.         EDT_FreeCharacterData(m_pCopyStyleCharacterData);
  4937.         m_pCopyStyleCharacterData = NULL;
  4938.     }
  4939. }
  4940.  
  4941. EDT_PageData* CEditBuffer::GetPageData(){
  4942.     EDT_PageData* pData = EDT_NewPageData();
  4943.  
  4944.     pData->pColorBackground = edt_MakeLoColor( m_colorBackground );
  4945.     pData->bBackgroundNoSave = m_bBackgroundNoSave;
  4946.     pData->pColorText = edt_MakeLoColor( m_colorText );
  4947.     pData->pColorLink = edt_MakeLoColor( m_colorLink );
  4948.     pData->pColorFollowedLink = edt_MakeLoColor( m_colorFollowedLink );
  4949.     pData->pColorActiveLink = edt_MakeLoColor( m_colorActiveLink );
  4950.  
  4951.     pData->pTitle = XP_STRDUP( ( m_pTitle ? m_pTitle : "" ) );
  4952.     pData->pBackgroundImage = ( m_pBackgroundImage
  4953.                     ? XP_STRDUP( m_pBackgroundImage )
  4954.                     : 0 );
  4955.     pData->pFontDefURL = ( m_pFontDefURL
  4956.                     ? XP_STRDUP( m_pFontDefURL )
  4957.                     : 0 );
  4958.     pData->bFontDefNoSave = m_bFontDefNoSave;
  4959.     return pData;
  4960. }
  4961.  
  4962. void CEditBuffer::FreePageData( EDT_PageData* pData ){
  4963.     if ( pData->pColorBackground ) XP_FREE( pData->pColorBackground );
  4964.     if ( pData->pColorText ) XP_FREE( pData->pColorText );
  4965.     if ( pData->pColorLink ) XP_FREE( pData->pColorLink );
  4966.     if ( pData->pColorFollowedLink ) XP_FREE( pData->pColorFollowedLink );
  4967.     if ( pData->pColorActiveLink ) XP_FREE( pData->pColorActiveLink );
  4968.     if ( pData->pTitle ) XP_FREE( pData->pTitle );
  4969.     if ( pData->pBackgroundImage ) XP_FREE( pData->pBackgroundImage );
  4970.     if ( pData->pFontDefURL ) XP_FREE( pData->pFontDefURL );
  4971.  
  4972.     XP_FREE( pData );
  4973. }
  4974.  
  4975. void CEditBuffer::SetPageData( EDT_PageData* pData ){
  4976.     m_colorBackground = EDT_LO_COLOR( pData->pColorBackground );
  4977.     m_colorLink = EDT_LO_COLOR( pData->pColorLink );
  4978.     m_colorText = EDT_LO_COLOR( pData->pColorText );
  4979.     m_colorFollowedLink = EDT_LO_COLOR( pData->pColorFollowedLink );
  4980.     m_colorActiveLink = EDT_LO_COLOR( pData->pColorActiveLink );
  4981.  
  4982.     // While we would like to change doc title only if it
  4983.     //   is really different, there is the case when both existing
  4984.     //   m_pTitle and the new pData->pTitle are NULL and we 
  4985.     //   need to always change the title in that case since
  4986.     //   we may be changing an existing page with no title
  4987.     //   into an "Untitled" page. This happens when we open a template file
  4988.     //   or we import a text file
  4989.     XP_Bool bNoChange = FALSE;
  4990.  
  4991.     if( m_pTitle && pData->pTitle && XP_STRCMP(m_pTitle, pData->pTitle) )
  4992.     {
  4993.         bNoChange = TRUE;
  4994.     }
  4995.  
  4996.     if( !bNoChange )
  4997.     {
  4998.         if( m_pTitle ) XP_FREE( m_pTitle );
  4999.         m_pTitle = (pData->pTitle ? XP_STRDUP( pData->pTitle ) : NULL);
  5000.         FE_SetDocTitle( m_pContext, m_pTitle );
  5001.     }
  5002.     
  5003.     //    If there was an image, and now there isn't remove it.   
  5004.     if(m_pBackgroundImage != NULL)  {
  5005.         XP_FREE(m_pBackgroundImage);
  5006.         m_pBackgroundImage = NULL;
  5007.     }
  5008.  
  5009.     if( pData->pBackgroundImage ){  
  5010.         m_pBackgroundImage = XP_STRDUP(pData->pBackgroundImage);  
  5011.     }  
  5012.     m_bBackgroundNoSave = pData->bBackgroundNoSave;
  5013.  
  5014.     // Font reference
  5015.     if(m_pFontDefURL != NULL)  {
  5016.         XP_FREE(m_pFontDefURL);
  5017.         m_pFontDefURL = NULL;
  5018.     }
  5019.     if( pData->pFontDefURL ){  
  5020.         m_pFontDefURL = XP_STRDUP(pData->pFontDefURL);  
  5021.     }  
  5022.     m_bFontDefNoSave = pData->bFontDefNoSave;
  5023.  
  5024.     // Note: all LO_SetDocumentColor calls
  5025.     //       and LO_SetBackgroundImage are done in RefreshLayout
  5026.  
  5027.     // Clear bit used to set color only once when multiple colors
  5028.     //   are in mail message. Fix for bug 64850
  5029.     // If we don't, color doesn't change when user wants to!
  5030.     lo_TopState *top_state = lo_FetchTopState(XP_DOCID(m_pContext));
  5031.     if( top_state ){
  5032.         top_state->body_attr &= ~BODY_ATTR_TEXT;
  5033.     }
  5034.     RefreshLayout();
  5035. }
  5036.  
  5037. void CEditBuffer::SetImageAsBackground()
  5038. {
  5039.     EDT_PageData * pPageData = GetPageData();
  5040.     if (pPageData == NULL) {
  5041.         return;
  5042.     }
  5043.     EDT_ImageData* pImageData = NULL;
  5044.     
  5045.     if( IsSelected() && GetCurrentElementType() == ED_ELEMENT_IMAGE ){
  5046.         pImageData = GetImageData();
  5047.         if( pImageData ){
  5048.             XP_FREEIF(pPageData->pBackgroundImage);
  5049.             pPageData->pBackgroundImage = XP_STRDUP(pImageData->pSrc);
  5050.  
  5051.             BeginBatchChanges(kGroupOfChangesCommandID);
  5052.             // Delete image from page
  5053. //            DeleteSelection();
  5054.             SetPageData(pPageData);
  5055.  
  5056.             EDT_FreeImageData(pImageData);
  5057.             EndBatchChanges();
  5058.         }
  5059.     }
  5060.     EDT_FreePageData(pPageData);
  5061. }
  5062.  
  5063. // MetaData
  5064.  
  5065. intn CEditBuffer::MetaDataCount( ){
  5066.     intn i = 0;
  5067.     intn iRetVal = 0;
  5068.     while( i < m_metaData.Size() ){
  5069.         if( m_metaData[i] != 0 ){
  5070.             iRetVal++;
  5071.         }
  5072.         i++;
  5073.     }
  5074.     return iRetVal;
  5075. }
  5076.  
  5077. intn CEditBuffer::FindMetaData( EDT_MetaData *pMetaData ){
  5078.     return FindMetaData(pMetaData->bHttpEquiv, pMetaData->pName);
  5079. }
  5080.  
  5081. intn CEditBuffer::FindMetaData(XP_Bool bHttpEquiv, char* pName){
  5082.     intn i = 0;
  5083.     intn iRetVal = 0;
  5084.     while( i < m_metaData.Size() ){
  5085.         if( m_metaData[i] != 0 ){
  5086.             if( (!!bHttpEquiv == !!m_metaData[i]->bHttpEquiv)
  5087.                     && XP_STRCMP( m_metaData[i]->pName, pName) == 0 ){
  5088.                 return iRetVal;
  5089.             }
  5090.             iRetVal++;
  5091.         }
  5092.         i++;
  5093.     }
  5094.     return -1;
  5095. }
  5096.  
  5097. intn CEditBuffer::FindMetaDataIndex( EDT_MetaData *pMetaData ){
  5098.     intn i = 0;
  5099.     while( i < m_metaData.Size() ){
  5100.         if( m_metaData[i] != 0 ){
  5101.             if( (!!pMetaData->bHttpEquiv == !!m_metaData[i]->bHttpEquiv)
  5102.                     && XP_STRCMP( m_metaData[i]->pName, pMetaData->pName) == 0 ){
  5103.                 return i;
  5104.             }
  5105.         }
  5106.         i++;
  5107.     }
  5108.     return -1;
  5109. }
  5110.  
  5111. EDT_MetaData* CEditBuffer::MakeMetaData( XP_Bool bHttpEquiv, char *pName, char*pContent ){
  5112.     EDT_MetaData *pData = XP_NEW( EDT_MetaData );
  5113.     XP_BZERO( pData, sizeof( EDT_MetaData ) );
  5114.  
  5115.     pData->bHttpEquiv = bHttpEquiv;
  5116.     pData->pName = (pName
  5117.                             ? XP_STRDUP(pName)
  5118.                             : 0);
  5119.     pData->pContent = (pContent
  5120.                             ? XP_STRDUP(pContent)
  5121.                             : 0);
  5122.     return pData;
  5123. }
  5124.  
  5125. EDT_MetaData* CEditBuffer::GetMetaData( int n ){
  5126.     intn i = 0;
  5127. //    intn iRetVal = 0;
  5128.     n++;
  5129.     while( n ){
  5130.         if ( i >= m_metaData.Size() ) {
  5131.             return NULL;
  5132.         }
  5133.         if( m_metaData[i] != 0 ){
  5134.             n--;
  5135.         }
  5136.         i++;
  5137.     }
  5138.     i--;
  5139.     if ( i < 0 ) {
  5140.         return NULL;
  5141.     }
  5142.     return MakeMetaData( m_metaData[i]->bHttpEquiv,
  5143.                          m_metaData[i]->pName,
  5144.                          m_metaData[i]->pContent );
  5145. }
  5146.  
  5147. void CEditBuffer::SetMetaData( EDT_MetaData *pData ){
  5148.     intn i = FindMetaDataIndex( pData );
  5149.     if ( ! (pData && pData->pContent && *(pData->pContent)) ) {
  5150.         // We've been asked to erase an entry
  5151.         if ( i == -1 ) {
  5152.             // We've been asked to erase an entry that we don't have. So just return.
  5153.             return;
  5154.         }
  5155.         else {
  5156.             // Erase the entry.
  5157.             FreeMetaData( m_metaData[i] );
  5158.             m_metaData[i] = NULL;
  5159.         }
  5160.     }
  5161.     else {
  5162.         EDT_MetaData* pNew = MakeMetaData( pData->bHttpEquiv, pData->pName, pData->pContent );
  5163.         if( i == -1 ){
  5164.             // Add an entry
  5165.             m_metaData.Add( pNew );
  5166.         }
  5167.         else {
  5168.             // Replace an entry
  5169.             FreeMetaData( m_metaData[i] );
  5170.             m_metaData[i] = pNew;
  5171.         }
  5172.     }
  5173. }
  5174.  
  5175. void CEditBuffer::DeleteMetaData( EDT_MetaData *pData ){
  5176.     intn i = FindMetaDataIndex( pData );
  5177.     if( i != -1 ){
  5178.         FreeMetaData( m_metaData[i] );
  5179.         m_metaData[i] = 0;
  5180.     }
  5181. }
  5182.  
  5183. void CEditBuffer::FreeMetaData( EDT_MetaData *pData ){
  5184.     if ( pData ) {
  5185.         if( pData->pName ) XP_FREE( pData->pName );
  5186.         if( pData->pContent ) XP_FREE( pData->pContent );
  5187.         XP_FREE( pData );
  5188.     }
  5189. }
  5190.  
  5191. void CEditBuffer::ParseMetaTag( PA_Tag *pTag ){
  5192.     XP_Bool bHttpEquiv = TRUE;
  5193.     char *pName;
  5194.     char *pContent;
  5195.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  5196.     int16 win_csid = INTL_GetCSIWinCSID(c);
  5197.  
  5198.     pContent = edt_FetchParamString( pTag, PARAM_CONTENT, win_csid );
  5199.     pName = edt_FetchParamString( pTag, PARAM_HTTP_EQUIV, win_csid );
  5200.  
  5201.     // if we didn't get http-equiv, try for name=
  5202.     if( pName == 0 ){
  5203.         bHttpEquiv = FALSE;
  5204.         pName = edt_FetchParamString( pTag, PARAM_NAME, win_csid );
  5205.     }
  5206.  
  5207.     // if we got one or the other, add it to the list of meta tags.
  5208.     if( pName ){
  5209.         EDT_MetaData *pData = MakeMetaData( bHttpEquiv, pName, pContent );
  5210.         SetMetaData( pData );
  5211.         FreeMetaData( pData );
  5212.     }
  5213.  
  5214.     if( pName ) XP_FREE( pName );
  5215.     if( pContent ) XP_FREE( pContent );
  5216. }
  5217.  
  5218.  
  5219. // Image
  5220.  
  5221. EDT_ImageData* CEditBuffer::GetImageData(){
  5222.     CEditLeafElement *pInsertPoint;
  5223.     ElementOffset iOffset;
  5224.     XP_Bool bSingleItem;
  5225.  
  5226.     bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  5227.     XP_ASSERT( bSingleItem );
  5228.  
  5229.     XP_ASSERT( pInsertPoint->IsA(P_IMAGE) );
  5230.  
  5231.     return pInsertPoint->Image()->GetImageData( );
  5232. }
  5233.  
  5234. int32 CEditBuffer::GetDefaultBorderWidth(){
  5235.     CEditLeafElement *pInsertPoint;
  5236.     ElementOffset iOffset;
  5237.     XP_Bool bSingleItem;
  5238.  
  5239.     // Just check for consistency, should both have a link or both not.
  5240.     bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  5241.     if (bSingleItem && pInsertPoint->IsA(P_IMAGE)) {
  5242.         ED_LinkId fromImage = pInsertPoint->Image()->GetHREF();
  5243.         char *fromEdtBuf = GetHREF();
  5244.         XP_ASSERT((fromImage && fromEdtBuf) || (!fromImage && !fromEdtBuf));
  5245.     }
  5246.  
  5247.     // Duplicating code in CEditImageElement::GetDefaultBorder, but at
  5248.     // least its all in the back end now.
  5249.     if (GetHREF()) {
  5250.         return 2;
  5251.     }
  5252.     else {
  5253.         return 0;
  5254.     }
  5255. }
  5256.  
  5257.  
  5258. #define XP_STRCMP_NULL( a, b ) XP_STRCMP( (a)?(a):"", (b)?(b):"" )
  5259.  
  5260. void CEditBuffer::SetImageData( EDT_ImageData* pData, XP_Bool bKeepImagesWithDoc ){
  5261.     VALIDATE_TREE(this);
  5262.     ClearSelection( TRUE, TRUE );
  5263.  
  5264.     XP_ASSERT( m_pCurrent->IsA(P_IMAGE) );
  5265.  
  5266.     // This function is only called when changing attributes of an image, not
  5267.     //   when inserting an image.
  5268.     //
  5269.     // There are 4 cases...
  5270.     //
  5271.     // 1) They change the src of the image, but not the size of the image
  5272.     //      (assume the dimensions of the new image).
  5273.     // 2) They change the src and the size of the image
  5274.     // 3) They change just the size of the image.
  5275.     // 4) They reset the size of the image.
  5276.     //
  5277.  
  5278.     CEditImageElement *pImage = m_pCurrent->Image();
  5279.     EDT_ImageData *pOldData = m_pCurrent->Image()->GetImageData();
  5280.  
  5281.     // Case 1, 2, or 4
  5282.     if( (XP_STRCMP( pOldData->pSrc, pData->pSrc ) != 0 )
  5283.             || XP_STRCMP_NULL( pOldData->pLowSrc, pData->pLowSrc )
  5284.             || (pData->iHeight == 0 || pData->iWidth== 0) ){
  5285.  
  5286.  
  5287.         // if they touched height or width, assume they know what they
  5288.         //  are doing.
  5289.         //
  5290.         if( pData->iHeight == pOldData->iHeight
  5291.                 && pData->iWidth == pOldData->iWidth ){
  5292.  
  5293.             // LoadImage will get the image size from the image.
  5294.             pData->iHeight = 0;
  5295.             pData->iWidth = 0;
  5296.         }
  5297.  
  5298.         LoadImage( pData, bKeepImagesWithDoc, TRUE );
  5299.         edt_FreeImageData( pOldData );
  5300.     }
  5301.     else {
  5302.         m_pCurrent->Image()->SetImageData( pData );
  5303.         Relayout( m_pCurrent->FindContainer(), 0, m_pCurrent, RELAYOUT_NOCARET );
  5304.         SelectCurrentElement();
  5305.     }
  5306.     // Also set the HREF if it exists
  5307.     if( pData->pHREFData ){
  5308.         if( !pData->pHREFData->pURL || XP_STRLEN(pData->pHREFData->pURL) == 0 ){
  5309.             pImage->SetHREF( ED_LINK_ID_NONE );
  5310.         } else {
  5311.             // Add link data to the image
  5312.             pImage->SetHREF( linkManager.Add( pData->pHREFData->pURL,
  5313.                                               pData->pHREFData->pExtra) );
  5314.         }
  5315.     }
  5316. }
  5317.  
  5318. void CEditBuffer::LoadImage( EDT_ImageData* pData, XP_Bool /*bKeepImagesWithDoc*/,
  5319.             XP_Bool bReplaceImage ){
  5320.  
  5321.     XP_Bool bMakeAbsolute = TRUE;
  5322.  
  5323.     if( m_pLoadingImage != 0 ){
  5324.         XP_ASSERT(0);       // can only be loading one image at a time.
  5325.         return;
  5326.     }
  5327.  
  5328.     if( bMakeAbsolute ){
  5329.         m_pLoadingImage = new CEditImageLoader( this, pData, bReplaceImage );
  5330.         m_pLoadingImage->LoadImage();
  5331.     }
  5332.  
  5333. }
  5334.  
  5335. //
  5336. // need to do a better job of splitting and inserting in the right place.
  5337. //
  5338. void CEditBuffer::InsertImage( EDT_ImageData* pData ){
  5339.     if( IsSelected() ){
  5340.         ClearSelection();
  5341.     }
  5342.     CEditImageElement *pImage = new CEditImageElement(0);
  5343.     pImage->SetImageData( pData );
  5344.     // Add link data to the image
  5345.     if( pData->pHREFData && pData->pHREFData->pURL &&
  5346.         XP_STRLEN(pData->pHREFData->pURL) > 0){
  5347.         pImage->SetHREF( linkManager.Add( pData->pHREFData->pURL,
  5348.                                           pData->pHREFData->pExtra) );
  5349.     }
  5350.     InsertLeaf( pImage );
  5351. }
  5352.  
  5353.  
  5354. void CEditBuffer::InsertLeaf( CEditLeafElement *pLeaf ){
  5355.     VALIDATE_TREE(this);
  5356.     FixupInsertPoint();
  5357.     if( m_iCurrentOffset == 0 ){
  5358.         pLeaf->InsertBefore( m_pCurrent );
  5359.     }
  5360.     else {
  5361.         m_pCurrent->Divide(m_iCurrentOffset);
  5362.         pLeaf->InsertAfter( m_pCurrent );
  5363.     }
  5364.     m_pCurrent = pLeaf;
  5365.     m_iCurrentOffset = 1;
  5366.  
  5367.     if ( ! m_bNoRelayout ){
  5368.         Reduce(pLeaf->FindContainer());
  5369.     }
  5370.  
  5371.     // we could be doing a better job here.  We probably repaint too much...
  5372.     CEditElement *pContainer = m_pCurrent->FindContainer();
  5373.     Relayout( pContainer, 0, pContainer->GetLastMostChild() );
  5374. }
  5375.  
  5376. void CEditBuffer::InsertNonLeaf( CEditElement *pElement){
  5377.     VALIDATE_TREE(this);
  5378.     FixupInsertPoint();
  5379.     XP_ASSERT( ! pElement->IsLeaf());
  5380. //    CEditElement* pStart = m_pCurrent;
  5381. //    int iStartOffset = m_iCurrentOffset;
  5382.     CEditInsertPoint start(m_pCurrent, m_iCurrentOffset);
  5383.     CEditLeafElement* pRight = NULL;
  5384.     InternalReturnKey(FALSE);
  5385.  
  5386.     pRight = m_pCurrent;
  5387.     CEditLeafElement* pLeft = pRight->PreviousLeaf(); // Will be NULL at start of document
  5388.     pElement->InsertBefore(pRight->FindContainer());
  5389.     pElement->FinishedLoad(this);
  5390.  
  5391. #ifdef DEBUG
  5392.     m_pRoot->ValidateTree();
  5393. #endif
  5394.     // Put cursor at the end of what has been inserted
  5395.  
  5396.     if ( pRight->IsEndOfDocument() ) {
  5397.         m_pCurrent = pRight->PreviousLeaf();
  5398.         m_iCurrentOffset = m_pCurrent->GetLen();
  5399.     }
  5400.     else {
  5401.         m_pCurrent = pRight;
  5402.         m_iCurrentOffset = 0;
  5403.    }
  5404.  
  5405.     Relayout( pElement, 0, pRight );
  5406. }
  5407.  
  5408. // Table stuff
  5409.  
  5410. void CEditBuffer::GetTableInsertPoint(CEditInsertPoint& ip){
  5411.     GetInsertPoint(ip);
  5412.     CEditSelection s;
  5413.     GetSelection(s);
  5414.     if ( ! s.IsInsertPoint() && s.m_end == ip && ip.IsStartOfContainer()){
  5415.         ip = ip.PreviousPosition();
  5416.     };
  5417. }
  5418.  
  5419. XP_Bool CEditBuffer::SetTableInsertPoint(CEditTableCellElement *pCell, XP_Bool bStartOfCell )
  5420. {
  5421.     // Move the insert point to new cell,
  5422.     if( pCell )
  5423.     {
  5424.         CEditElement *pChild;
  5425.         if( bStartOfCell )
  5426.             pChild = pCell->GetFirstMostChild();
  5427.         else
  5428.             pChild = pCell->GetLastMostChild();
  5429.         
  5430.         if( pChild && pChild->IsLeaf() )
  5431.         {
  5432.             CEditInsertPoint ip(pChild, bStartOfCell ? 0 : pChild->Leaf()->GetLen());
  5433.             SetInsertPoint(ip);
  5434.             return TRUE;
  5435. #ifdef DEBUG
  5436.         } else {
  5437.             XP_ASSERT(TRUE);
  5438. #endif
  5439.         }
  5440.     }
  5441.     return FALSE;
  5442. }
  5443.  
  5444. XP_Bool CEditBuffer::IsInsertPointInTable(){
  5445.     CEditInsertPoint ip;
  5446.     GetTableInsertPoint(ip);
  5447.     return ip.m_pElement->GetTableIgnoreSubdoc() != NULL;
  5448. }
  5449.  
  5450. XP_Bool CEditBuffer::IsInsertPointInNestedTable(){
  5451.     XP_Bool result = FALSE;
  5452.     CEditInsertPoint ip;
  5453.     GetTableInsertPoint(ip);
  5454.     CEditTableElement* pFirstTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5455.     if ( pFirstTable ) {
  5456.         CEditTableElement* pSecondTable = pFirstTable->GetParent()->GetTableIgnoreSubdoc();
  5457.         if ( pSecondTable ) {
  5458.             result = TRUE;
  5459.         }
  5460.     }
  5461.     return result;
  5462. }
  5463.  
  5464. EDT_TableData* CEditBuffer::GetTableData(){
  5465.     CEditInsertPoint ip;
  5466.     GetTableInsertPoint(ip);
  5467.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5468.     if ( pTable ){
  5469.         return pTable->GetData();
  5470.     }
  5471.     else {
  5472.         return NULL;
  5473.     }
  5474. }
  5475.  
  5476. void CEditBuffer::SetTableData(EDT_TableData *pData){
  5477.     VALIDATE_TREE(this);
  5478.     CEditInsertPoint ip;
  5479.     GetTableInsertPoint(ip);
  5480.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5481.     if ( pTable ){
  5482.         pTable->SetData( pData );
  5483.         Relayout( pTable, 0 );
  5484.         // Update the size data to reflect changes made by Layout
  5485.         pData->iWidth = pTable->GetWidthOrPercent();
  5486.         pData->iWidthPixels = pTable->GetWidth();
  5487.         pData->iHeight = pTable->GetHeightOrPercent();
  5488.         pData->iHeightPixels = pTable->GetHeight();
  5489.     }
  5490. }
  5491.  
  5492. void CEditBuffer::InsertTable(EDT_TableData *pData){
  5493.     CEditTableElement *pTable = new CEditTableElement(pData->iColumns, pData->iRows);
  5494.     if( pTable ){
  5495.         pTable->SetData( pData );
  5496.         pTable->FinishedLoad(this); // Sets default paragraphs for all the cells
  5497.         if( IsSelected() ){
  5498.             ClearSelection();
  5499.         }
  5500.         InsertNonLeaf(pTable);
  5501.         // Set insert point inside first cell in table
  5502.         CEditElement *pFirstChild = pTable->GetFirstMostChild();
  5503.         CEditInsertPoint ip(pFirstChild, 0);
  5504.         SetInsertPoint(ip);
  5505.     }
  5506. }
  5507.  
  5508. void CEditBuffer::DeleteTable(){
  5509.     VALIDATE_TREE(this);
  5510.     CEditInsertPoint ip;
  5511.     GetTableInsertPoint(ip);
  5512.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5513.     if ( pTable ){
  5514.         BeginBatchChanges(kGroupOfChangesCommandID);
  5515.         AdoptAndDo(new CDeleteTableCommand(this));
  5516.         EndBatchChanges();
  5517.     }
  5518. }
  5519.  
  5520. /* Use to enable/disable Merge Cells feature. 
  5521.  * Selected cells can be merged only if in a continuous set
  5522.  * within the same row or column
  5523. */
  5524. ED_MergeType CEditBuffer::GetMergeTableCellsType()
  5525. {
  5526.     // Need at least 2 cells to merge
  5527.     CEditTableCellElement* pTableCell = NULL;
  5528.     int iCount = m_SelectedEdCells.Size();
  5529.     if( iCount == 1 )
  5530.     {
  5531.         pTableCell = m_SelectedEdCells[0];
  5532.     } else if(iCount == 0 ){
  5533.         CEditInsertPoint ip;
  5534.         GetTableInsertPoint(ip);
  5535.         pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  5536.     }
  5537.     if( pTableCell && pTableCell->GetNextSibling() )
  5538.     {
  5539.         // We have a single cell with a neighbor to the right
  5540.         return ED_MERGE_NEXT_CELL;
  5541.     }
  5542.     
  5543.     // Nothing to merge
  5544.     if( iCount < 2 )
  5545.         return ED_MERGE_NONE;
  5546. #if 0
  5547. //  TODO: REVISIT THIS
  5548.     // If any selected cell is not inline with all others,
  5549.     //  don't allow merging
  5550.     int32 left = m_SelectedLoCells[0]->x;
  5551.     int32 top = m_SelectedLoCells[0]->y;
  5552.     XP_Bool bSameTop = (m_SelectedLoCells[1]->y == top);
  5553.     XP_Bool bSameLeft = (m_SelectedLoCells[1]->x == left);
  5554.  
  5555.     // Neither is the same - quit
  5556.     if( !bSameLeft && !bSameTop )
  5557.         return ED_MERGE_NONE;
  5558.  
  5559.     for( int i = 2; i < iCount; i++ )
  5560.     {
  5561.         if( (bSameLeft && m_SelectedLoCells[i]->x != left) || 
  5562.             (bSameTop &&  m_SelectedLoCells[i]->y != top) )
  5563.         {
  5564.             return ED_MERGE_NONE;
  5565.         }
  5566.     }
  5567. #endif
  5568.     return ED_MERGE_SELECTED_CELLS;
  5569. }
  5570.  
  5571. /* Use to enable/disable Split Cell feature. 
  5572.  * Current cell (containing caret) can be split 
  5573.  * only if it has COLSPAN or ROWSPAN
  5574. */
  5575. XP_Bool CEditBuffer::CanSplitTableCell()
  5576. {
  5577.     XP_Bool bResult = FALSE;
  5578.     // We can split a cell if single cell is selected
  5579.     // and/or caret is inside a cell with COLSPAN or ROWSPAN
  5580.     if( m_SelectedLoCells.Size() <= 1 )
  5581.     {
  5582.         EDT_TableCellData *pData = GetTableCellData();
  5583.         if( pData )
  5584.         {
  5585.             bResult = (pData->iColSpan > 1) || (pData->iRowSpan > 1);
  5586.             EDT_FreeTableCellData(pData);
  5587.         }
  5588.     }
  5589.     return bResult;
  5590. }
  5591.  
  5592. /* Set appropriate COLSPAN or ROWSPAN and move all
  5593.  * cell contents into first cell of set
  5594.  */
  5595. void CEditBuffer::MergeTableCells()
  5596. {
  5597.     // Don't attempt if not allowed
  5598.     ED_MergeType MergeType = GetMergeTableCellsType();
  5599.     //if( MergeType == ED_MERGE_NONE )
  5600.     //    return;
  5601.     int iCount = m_SelectedEdCells.Size();
  5602.  
  5603.     // Be sure selection is ordered properly
  5604.     SortSelectedCells();
  5605.     
  5606.     // Set beginning of UNDO block
  5607.     BeginBatchChanges(kGroupOfChangesCommandID);
  5608.  
  5609.     int32 x = 0;
  5610.     int32 y = 0;
  5611.  
  5612.     // This is the cell we will merge into
  5613.     CEditTableCellElement *pFirstCell = NULL;
  5614.     if( iCount > 0 )
  5615.     {
  5616.         // We have selected cell as first
  5617.         pFirstCell = m_SelectedEdCells[0];
  5618.         // Save the x and y of first cell
  5619.         //  to figure out COL or ROW span
  5620.         x = m_SelectedLoCells[0]->x;
  5621.         y = m_SelectedLoCells[0]->y;
  5622.     } else {
  5623.         // Get current cell at caret 
  5624.         CEditInsertPoint ip;
  5625.         GetTableInsertPoint(ip);
  5626.         pFirstCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  5627.         if(pFirstCell )
  5628.         {
  5629.             LO_CellStruct *pLoCell = GetLoCell(pFirstCell);
  5630.             if(pLoCell)
  5631.             {
  5632.                 x = pLoCell->x;
  5633.                 y = pLoCell->y;
  5634.             }
  5635.         }
  5636.     }
  5637.  
  5638.     EDT_TableCellData *pFirstCellData = pFirstCell->GetData();
  5639.     if(!pFirstCellData)
  5640.         return;
  5641.     EDT_TableCellData *pNextCellData = NULL;
  5642.  
  5643.     if( iCount > 1 )
  5644.     {
  5645.         // We are merging all selected cells
  5646.         for( int i = 1; i < iCount; i++ )
  5647.         {
  5648.             // We must add all COLSPANs of all cells to be merged
  5649.             pNextCellData = m_SelectedEdCells[i]->GetData();
  5650.             if( pNextCellData )
  5651.             {
  5652.                 // If y value is same, next cell is in same row, so do ColSpan
  5653.                 if( y == m_SelectedLoCells[i]->y )
  5654.                 {
  5655.                     pFirstCellData->iColSpan += pNextCellData->iColSpan;
  5656.                 }
  5657.  
  5658.                 // If x value is same, next cell is in same col, so do RowSpan
  5659.                 // Note: This will work for strange selection sets, but if all
  5660.                 //  cells in largest bounding rect aren't selected, we can get some strange results!
  5661.                 if( x == m_SelectedLoCells[i]->x )
  5662.                 {
  5663.                     pFirstCellData->iRowSpan += pNextCellData->iRowSpan;
  5664.                 }
  5665.                 EDT_FreeTableCellData(pNextCellData);
  5666.             }
  5667.             pFirstCell->MergeCells(m_SelectedEdCells[i]);
  5668.         }
  5669.     } else {
  5670.         // We are merging with just the next cell to the right
  5671.         CEditTableCellElement *pNextCell = (CEditTableCellElement*)pFirstCell->GetNextSibling();
  5672.         pNextCellData = pNextCell->GetData();
  5673.         if( pNextCellData )
  5674.         {
  5675.             pFirstCellData->iColSpan += pNextCellData->iColSpan;
  5676.             EDT_FreeTableCellData(pNextCellData);
  5677.         }
  5678.         pFirstCell->MergeCells( pNextCell );
  5679.     }
  5680.     // Set the SPAN data for the merged cell
  5681.     pFirstCell->SetData(pFirstCellData);
  5682.     EDT_FreeTableCellData(pFirstCellData);
  5683.  
  5684.     // Be sure insert point is in first cell, 
  5685.     //   not one of the cells that was deleted
  5686.     //   (else we crash in Relayout)
  5687.     SetTableInsertPoint(pFirstCell);
  5688.     
  5689.     ClearTableAndCellSelection();
  5690.     
  5691.     // Relayout the entire table
  5692.     Relayout(pFirstCell->GetParentTable(), 0);
  5693.     EndBatchChanges();
  5694. }
  5695.  
  5696. /* Separate paragraphs into sepaparate cells,
  5697.  * removing COLSPAN or ROWSPAN
  5698. */
  5699. void CEditBuffer::SplitTableCell()
  5700. {
  5701.     //TODO: WRITE THIS!
  5702. }
  5703.  
  5704.  
  5705. XP_Bool CEditBuffer::IsInsertPointInTableCaption(){
  5706.     CEditInsertPoint ip;
  5707.     GetTableInsertPoint(ip);
  5708.     // We allow the insert point to be in a table with a table caption.
  5709.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5710.     CEditCaptionElement* pTableCaption = pTable ? pTable->GetCaption() : 0;
  5711.     return pTableCaption != NULL;
  5712. }
  5713.  
  5714. EDT_TableCaptionData* CEditBuffer::GetTableCaptionData(){
  5715.     CEditInsertPoint ip;
  5716.     GetTableInsertPoint(ip);
  5717.     // We allow the insert point to be in a table with a table caption.
  5718.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5719.     CEditCaptionElement* pTableCaption = pTable ? pTable->GetCaption() : 0;
  5720.     if ( pTableCaption ){
  5721.         return pTableCaption->GetData();
  5722.     }
  5723.     else {
  5724.         return NULL;
  5725.     }
  5726. }
  5727.  
  5728. void CEditBuffer::SetTableCaptionData(EDT_TableCaptionData *pData){
  5729.     VALIDATE_TREE(this);
  5730.     CEditInsertPoint ip;
  5731.     GetTableInsertPoint(ip);
  5732.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5733.     CEditCaptionElement* pTableCaption = pTable ? pTable->GetCaption() : 0;
  5734.     if ( pTableCaption ){
  5735.         pTableCaption->SetData( pData );
  5736.         pTable->FinishedLoad(this); // Can move caption up or down.
  5737.         Relayout( pTable, 0 );
  5738.     }
  5739. }
  5740.  
  5741. void CEditBuffer::InsertTableCaption(EDT_TableCaptionData *pData){
  5742.     VALIDATE_TREE(this);
  5743.     CEditInsertPoint ip;
  5744.     GetTableInsertPoint(ip);
  5745.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5746.     if ( pTable ){
  5747.         if ( ! pTable->GetCaption() ) {
  5748.             // CInsertTableCaptionCommand actually performs the operation in the
  5749.             // constructor of the command. So in order for save-based undo to
  5750.             // work, we must wrap its constructor in BeginBatchChanges.
  5751.             BeginBatchChanges(kGroupOfChangesCommandID /*kInsertTableCaptionCommandID*/);
  5752.             AdoptAndDo(new CInsertTableCaptionCommand(this, pData));
  5753.             EndBatchChanges();
  5754.         }
  5755.     }
  5756. }
  5757.  
  5758. void CEditBuffer::DeleteTableCaption(){
  5759.     VALIDATE_TREE(this);
  5760.     CEditInsertPoint ip;
  5761.     GetTableInsertPoint(ip);
  5762.     CEditTableElement* pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  5763.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  5764.     if ( pTable ){
  5765.         if ( pTable->GetCaption() ) {
  5766.             BeginBatchChanges(kGroupOfChangesCommandID);
  5767.             AdoptAndDo(new CDeleteTableCaptionCommand(this));
  5768.             EndBatchChanges();
  5769.         }
  5770.     }
  5771. }
  5772.  
  5773. XP_Bool CEditBuffer::IsInsertPointInTableRow(){
  5774.     CEditInsertPoint ip;
  5775.     GetTableInsertPoint(ip);
  5776.     return ip.m_pElement->GetTableRowIgnoreSubdoc() != NULL;
  5777. }
  5778.  
  5779. EDT_TableRowData* CEditBuffer::GetTableRowData(){
  5780.     CEditInsertPoint ip;
  5781.     GetTableInsertPoint(ip);
  5782.     CEditTableRowElement* pTableRow = ip.m_pElement->GetTableRowIgnoreSubdoc();
  5783.     if ( pTableRow ){
  5784.         return pTableRow->GetData();
  5785.     }
  5786.     else {
  5787.         return NULL;
  5788.     }
  5789. }
  5790.  
  5791. void CEditBuffer::SetTableRowData(EDT_TableRowData *pData){
  5792.     VALIDATE_TREE(this);
  5793.     CEditInsertPoint ip;
  5794.     GetTableInsertPoint(ip);
  5795.     CEditTableRowElement* pTableRow = ip.m_pElement->GetTableRowIgnoreSubdoc();
  5796.     if ( pTableRow ){
  5797.         pTableRow->SetData( pData );
  5798.         Relayout( pTableRow, 0 );
  5799.     }
  5800. }
  5801.  
  5802. intn CEditBuffer::GetNumberOfSelectedRows()
  5803. {
  5804.     intn iRows = 0;
  5805.     int iSize = m_SelectedLoCells.Size();
  5806.     if( iSize && m_TableHitType == ED_HIT_SEL_ROW )
  5807.     {
  5808.         SortSelectedCells();
  5809.  
  5810.         iRows = 1;
  5811.         int32 iCurrentTop = m_SelectedLoCells[0]->y;
  5812.         for(int i = 2; i < iSize; i++ )
  5813.         {
  5814.             // Different row when y value changes            
  5815.             if( iCurrentTop != m_SelectedLoCells[i]->y )
  5816.             {
  5817.                 iRows++;
  5818.                 iCurrentTop = m_SelectedLoCells[i]->y;
  5819.             }
  5820.         }
  5821.     }
  5822.     return iRows;
  5823. }
  5824.  
  5825. intn CEditBuffer::GetNumberOfSelectedColumns()
  5826. {
  5827.     intn iCols = 0;
  5828.     int iSize = m_SelectedLoCells.Size();
  5829.     if( iSize && m_TableHitType == ED_HIT_SEL_COL )
  5830.     {
  5831.         // Finding number of columns is more complicated 
  5832.         //  than rows because ordering is rows first,
  5833.         //  and because of COLSPAN and ROWSPAN
  5834.         TXP_GrowableArray_LO_CellStruct  UniqueXCells;
  5835.         UniqueXCells.Add(m_SelectedLoCells[0]);
  5836.  
  5837.         for(int i=2; i < iSize; i++ )
  5838.         {
  5839.             intn j;
  5840.             // Check if next cell has diffferent x than others
  5841.             intn iUniqueCount = UniqueXCells.Size();
  5842.             for( j = 0; j < iUniqueCount; j++ )
  5843.             {
  5844.                 if( UniqueXCells[j]->x == m_SelectedLoCells[i]->x )
  5845.                     break;
  5846.             }
  5847.             // We didn't find it, so add to list
  5848.             if( j == iUniqueCount )
  5849.                 UniqueXCells.Add(m_SelectedLoCells[i]);
  5850.         }
  5851.         iCols = UniqueXCells.Size();
  5852.     }
  5853.     return iCols;
  5854. }
  5855.  
  5856. void CEditBuffer::InsertTableRows(EDT_TableRowData *pData, XP_Bool bAfterCurrentRow, intn number)
  5857. {
  5858.     VALIDATE_TREE(this);
  5859.  
  5860.     if( number <= 0 )
  5861.     {
  5862.         // Get number of rows from the table selection
  5863.         number = GetNumberOfSelectedRows();
  5864.  
  5865.         if( number )
  5866.         {        
  5867.             // Move to appropriate cell
  5868.             if( bAfterCurrentRow )
  5869.                 MoveToLastSelectedCell();
  5870.             else
  5871.                 MoveToFirstSelectedCell();
  5872.         }
  5873.     }
  5874.  
  5875.     // If none or just one selected cell, then insert 1 row
  5876.     if( number == 0 && m_SelectedLoCells.Size() <= 1 )
  5877.         number = 1;
  5878.  
  5879.     CEditInsertPoint ip;
  5880.     GetTableInsertPoint(ip);
  5881.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  5882.     if ( pTableCell ){
  5883.         // CInsertTableRowCommand actually performs the operation in the
  5884.         // constructor of the command. So in order for save-based undo to
  5885.         // work, we must wrap its constructor in BeginBatchChanges.
  5886.         BeginBatchChanges(kGroupOfChangesCommandID/*kInsertTableRowCommandID*/);
  5887.         ClearTableAndCellSelection();
  5888.         AdoptAndDo(new CInsertTableRowCommand(this, pData, bAfterCurrentRow, number));
  5889.         EndBatchChanges();
  5890.     }
  5891. }
  5892.  
  5893. void CEditBuffer::DeleteTableRows(intn number){
  5894.     VALIDATE_TREE(this);
  5895.     CEditInsertPoint ip;
  5896.     GetTableInsertPoint(ip);
  5897.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  5898.     if ( pTableCell ){
  5899.         BeginBatchChanges(kGroupOfChangesCommandID);
  5900.         AdoptAndDo(new CDeleteTableRowCommand(this, number));
  5901.         EndBatchChanges();
  5902.     }
  5903. }
  5904.  
  5905. void CEditBuffer::SyncCursor(CEditLayerElement* pLayer)
  5906. {
  5907.     CEditInsertPoint insertPoint;
  5908.     insertPoint.m_pElement = m_pRoot->GetFirstMostChild()->Leaf();
  5909.     if ( pLayer && pLayer->GetFirstMostChild()->IsLeaf() ) {
  5910.         insertPoint.m_pElement = pLayer->GetFirstMostChild()->Leaf();
  5911.     }
  5912.  
  5913.     SetInsertPoint(insertPoint);
  5914. }
  5915.  
  5916. XP_Bool CEditBuffer::IsInsertPointInTableCell(){
  5917.     CEditInsertPoint ip;
  5918.     GetTableInsertPoint(ip);
  5919.     return ip.m_pElement->GetTableCellIgnoreSubdoc() != NULL;
  5920. }
  5921.  
  5922. EDT_TableCellData* CEditBuffer::GetTableCellData()
  5923. {
  5924.     CEditInsertPoint ip;
  5925.     GetTableInsertPoint(ip);
  5926.     CEditTableCellElement* pCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  5927.     if ( pCell )
  5928.     {
  5929.         // Get data for the current cell (containing the caret)
  5930.         EDT_TableCellData *pData = pCell->GetData();
  5931.  
  5932.         XP_ASSERT(pData); //Should never fail
  5933.         if( !pData )
  5934.             return NULL;
  5935.  
  5936.         // Used by front end to figure modify Cell Properties
  5937.         //  dialog based on number of selected cells
  5938.         pData->iSelectedCount = m_SelectedEdCells.Size();
  5939.  
  5940.         if( pData->iSelectedCount > 1 )
  5941.         {
  5942.             ED_HitType iNewSelType = ED_HIT_NONE;
  5943.             XP_Bool bColSelected = FALSE;
  5944.             XP_Bool bRowSelected = FALSE;
  5945.  
  5946.             if( m_TableHitType == ED_HIT_SEL_ALL_CELLS )
  5947.             {
  5948.                 // To the front ends, all cells means "rows"
  5949.                 iNewSelType = ED_HIT_SEL_ROW;
  5950.             } 
  5951.             else if(m_TableHitType == ED_HIT_SEL_ROW || 
  5952.                     m_TableHitType == ED_HIT_SEL_COL )
  5953.             {
  5954.                 iNewSelType = m_TableHitType;
  5955.             }
  5956.  
  5957.             // Go through all selected cells to set mask bits 
  5958.             //  that tell what attributes are the same 
  5959.             //  for all selected cells
  5960.             for( intn i = 0; i < pData->iSelectedCount; i++ )
  5961.             {
  5962.                 // Skip current cell when comparing data
  5963.                 if( m_SelectedEdCells[i] != pCell )
  5964.                 {
  5965.                     m_SelectedEdCells[i]->MaskData(pData);
  5966.  
  5967.                     // Check if all cells in just rows or just column are selected
  5968.                     if( iNewSelType == ED_HIT_NONE )
  5969.                     {
  5970.                         if( lo_AllCellsSelectedInColumnOrRow( m_SelectedLoCells[i], TRUE ) ) // Column
  5971.                         {
  5972.                             if( bRowSelected )
  5973.                                 // Can't have both row and columns selected
  5974.                                 iNewSelType = ED_HIT_SEL_CELL;
  5975.                             else
  5976.                                 bColSelected = TRUE;
  5977.                         }
  5978.                         else if( lo_AllCellsSelectedInColumnOrRow( m_SelectedLoCells[i], FALSE ) ) // Row
  5979.                         {
  5980.                             if( bColSelected )
  5981.                                 // Can't have both row and columns selected
  5982.                                 iNewSelType = ED_HIT_SEL_CELL;
  5983.                             else
  5984.                                 bRowSelected = TRUE;
  5985.                         } else 
  5986.                         {
  5987.                             // Neither row or column is fully selected
  5988.                             iNewSelType = ED_HIT_SEL_CELL;
  5989.                         }
  5990.                     }
  5991.                     // Override ColSpan and RowSpan mask bit
  5992.                     //  to NOT allow changing it when > 1 cell is selected
  5993.                     // TODO: MAYBE ALLOW CHANGING THIS IF NOT A FULL ROW OR COL SELECTED???
  5994.                     pData->mask &= ~(CF_COLSPAN | CF_ROWSPAN);
  5995.                 }
  5996.                 if( iNewSelType == ED_HIT_NONE )
  5997.                 {
  5998.                     if( bRowSelected )
  5999.                         iNewSelType = ED_HIT_SEL_ROW;
  6000.                     else if( bColSelected )
  6001.                         iNewSelType = ED_HIT_SEL_COL;
  6002.                 }
  6003.             }
  6004.             pData->iSelectionType = iNewSelType;
  6005.         }
  6006.         return pData;
  6007.     }
  6008.     else {
  6009.         return NULL;
  6010.     }
  6011. }
  6012.  
  6013. static void edt_CopyLoColor( LO_Color **ppDestColor, LO_Color *pSourceColor )
  6014. {
  6015.     if( ppDestColor )
  6016.     {
  6017.         if( !pSourceColor )
  6018.         {
  6019.             XP_FREEIF(*ppDestColor);
  6020.             return;
  6021.         }
  6022.  
  6023.         // Create new struct if it doesn't exist        
  6024.         if( !*ppDestColor )
  6025.             *ppDestColor = XP_NEW(LO_Color);
  6026.         XP_ASSERT(*ppDestColor);
  6027.  
  6028.         if( *ppDestColor )
  6029.         {
  6030.             (*ppDestColor)->red = pSourceColor->red;
  6031.             (*ppDestColor)->green = pSourceColor->green;
  6032.             (*ppDestColor)->blue = pSourceColor->blue;
  6033.         }
  6034.     }
  6035. }
  6036.  
  6037. static void edt_CopyTableCellData( EDT_TableCellData *pDestData, EDT_TableCellData *pSourceData )
  6038. {
  6039.     if( pDestData && pSourceData )
  6040.     {
  6041.         // Change data only for attributes whose bit is set in data mask
  6042.         if( pSourceData->mask & CF_ALIGN )
  6043.             pDestData->align = pSourceData->align;
  6044.  
  6045.         if( pSourceData->mask & CF_VALIGN )
  6046.             pDestData->valign = pSourceData->valign;
  6047.  
  6048.         if( pSourceData->mask & CF_COLSPAN )
  6049.             pDestData->iColSpan = pSourceData->iColSpan;
  6050.  
  6051.         if( pSourceData->mask & CF_ROWSPAN )
  6052.             pDestData->iRowSpan = pSourceData->iRowSpan;
  6053.  
  6054.         if( pSourceData->mask & CF_HEADER )
  6055.             pDestData->bHeader = pSourceData->bHeader;
  6056.  
  6057.         if( pSourceData->mask & CF_NOWRAP )
  6058.             pDestData->bNoWrap = pSourceData->bNoWrap;
  6059.  
  6060.         if( pSourceData->mask & CF_BACK_NOSAVE )
  6061.             pDestData->bBackgroundNoSave = pSourceData->bBackgroundNoSave;
  6062.  
  6063.         if( pSourceData->mask & CF_WIDTH )
  6064.         {
  6065.             pDestData->bWidthDefined = pSourceData->bWidthDefined;
  6066.             pDestData->iWidth = pSourceData->iWidth;
  6067.             pDestData->bWidthPercent = pSourceData->bWidthPercent;
  6068.         }
  6069.  
  6070.         if( pSourceData->mask & CF_HEIGHT )
  6071.         {
  6072.             pDestData->bHeightDefined = pSourceData->bHeightDefined;
  6073.             pDestData->iHeight = pSourceData->iHeight;
  6074.             pDestData->bHeightPercent = pSourceData->bHeightPercent;
  6075.         }
  6076.  
  6077.         if( pSourceData->mask & CF_BACK_COLOR )
  6078.             edt_CopyLoColor( &pDestData->pColorBackground, pSourceData->pColorBackground);
  6079.  
  6080.         if( pSourceData->mask & CF_BACK_IMAGE )
  6081.         {
  6082.             XP_FREEIF(pDestData->pBackgroundImage);
  6083.             if( pSourceData->pBackgroundImage )
  6084.                 pDestData->pBackgroundImage = XP_STRDUP(pSourceData->pBackgroundImage);
  6085.         }
  6086.  
  6087.         if( pSourceData->mask & CF_EXTRA_HTML )
  6088.         {
  6089.             XP_FREEIF(pDestData->pExtra);
  6090.             if( pSourceData->pExtra )
  6091.                 pDestData->pExtra = XP_STRDUP(pSourceData->pExtra);
  6092.         }
  6093.     }
  6094. }
  6095.  
  6096. void CEditBuffer::ChangeTableSelection(ED_HitType iHitType, ED_MoveSelType iMoveType, EDT_TableCellData *pData)
  6097. {
  6098.     CEditInsertPoint ip;
  6099.     GetTableInsertPoint(ip);
  6100.     CEditTableCellElement* pCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  6101.     if( !pCell )
  6102.         return;
  6103.  
  6104.     CEditTableElement *pTable = pCell->GetTable();
  6105.     XP_ASSERT(pTable);
  6106.  
  6107.     // Clear existing selection - we will always reselect
  6108.     ClearTableAndCellSelection();
  6109.  
  6110.     // First see if we are changing the current selection type
  6111.     if( iHitType != ED_HIT_NONE && pData && iHitType != pData->iSelectionType )
  6112.     {
  6113.         // This is relatively simple as long as we can trust that the
  6114.         //   caret is in the "focus cell" 
  6115.         if( iHitType == ED_HIT_SEL_CELL )
  6116.         {
  6117.             SelectTableElement( pCell, iHitType );
  6118.         } else 
  6119.         {
  6120.             // Keep focus cell the same - just select the row or column it is in
  6121.             SelectTableElement( pCell, iHitType );
  6122.         }
  6123.     }
  6124.  
  6125.     CEditTableCellElement *pNextCell = NULL;
  6126.     CEditTableCellElement *pCurrentCell = pCell;
  6127.     int32 iCurrentX = pCell->GetX();
  6128.     int32 iCurrentY = pCell->GetY();
  6129.  
  6130.     if( iMoveType == ED_MOVE_PREV )
  6131.     {
  6132.         switch (iHitType )
  6133.         {
  6134.             case ED_HIT_SEL_CELL:
  6135.                 pCell = pCell->GetPreviousCellInTable();
  6136.                 if( !pCell )
  6137.                 {
  6138.                     pCell = pCurrentCell;
  6139.                     // We found beginning of table - find last cell in table
  6140.                     while( (pNextCell = pCell->GetNextCellInTable()) != NULL )
  6141.                     {
  6142.                         pCell = pNextCell;
  6143.                     }
  6144.                 }
  6145.                 break;
  6146.  
  6147.             case ED_HIT_SEL_COL:
  6148.                 // Previous column contains 
  6149.                 //   the previous cell in current row
  6150.                 pCell = pTable->GetPreviousCellInRow(pCurrentCell);
  6151.                 if( !pCell )
  6152.                     pCell = pTable->GetLastCellInRow(pCurrentCell);
  6153.                 break;
  6154.  
  6155.             case ED_HIT_SEL_ROW:
  6156.                 // Previous row contains 
  6157.                 //   the cell cell in current column
  6158.                 pCell = pTable->GetPreviousCellInColumn(pCurrentCell);
  6159.  
  6160.                 if( !pCell )
  6161.                     pCell = pTable->GetLastCellInColumn(pCurrentCell);
  6162.                 break;
  6163.         }
  6164.     } 
  6165.     else if( iMoveType == ED_MOVE_NEXT )
  6166.     {
  6167.         switch (iHitType )
  6168.         {
  6169.             case ED_HIT_SEL_CELL:
  6170.                 // Move to Next Cell
  6171.                 pCell = pCell->GetNextCellInTable();
  6172.                 if( !pCell )
  6173.                     // At end of table - wrap to first cell
  6174.                     pCell = pTable->GetFirstCell();
  6175.                 break;
  6176.  
  6177.             case ED_HIT_SEL_COL:
  6178.                 pCell = pTable->GetNextCellInRow(pCurrentCell);
  6179.                 if( !pCell )
  6180.                     // At last column -- wrap to first cell of current row
  6181.                     pCell = pTable->GetFirstCellInRow(pCurrentCell);
  6182.                 break;
  6183.  
  6184.             case ED_HIT_SEL_ROW:
  6185.                 pCell = pTable->GetNextCellInColumn(pCurrentCell);
  6186.                 if( !pCell )
  6187.                     // At last row end -- wrap to first cell of current column
  6188.                     pCell = pTable->GetFirstCellInColumn(pCurrentCell);
  6189.                 break;
  6190.         }
  6191.     }
  6192.  
  6193.     if( pCell )
  6194.     {
  6195.         if( iMoveType != ED_MOVE_NONE &&
  6196.             (iHitType == ED_HIT_SEL_ROW || iHitType == ED_HIT_SEL_COL) )
  6197.         {
  6198.             // We need to select the column or row we are moving to
  6199.             // NOTE: This will move caret to this cell
  6200.             SelectTableElement( pCell, iHitType );
  6201.         } else {
  6202.             // Move caret to new focus cell
  6203.             SetTableInsertPoint(pCell);
  6204.         }
  6205.  
  6206.         if( m_SelectedEdCells.Size() > 1 )
  6207.         {
  6208.             // NOTE: If previous selection was a set of cells or row or column,
  6209.             //   and new iHitType == ED_HIT_SEL_CELL,
  6210.             //   then the new selection will be the single cell after the
  6211.             //   focus cell of the current selection
  6212.             // This does NOT change selected cells, just marks
  6213.             //   the non-focus cells with LO_ELE_SELECTED_SPECIAL
  6214.             //   and updates iSelectionType and iSelectedCount in pData
  6215.             DisplaySpecialCellSelection(pCell, pData);
  6216.         } else 
  6217.         {
  6218.             // Select just one new cell in table
  6219.             SelectTableElement( pCell, ED_HIT_SEL_CELL );
  6220.             // Change cell data to reflect single cell selection
  6221.             if( pData )
  6222.             {
  6223.                 pData->iSelectionType = ED_HIT_SEL_CELL;
  6224.                 pData->iSelectedCount = 1;
  6225.             }
  6226.         }
  6227.  
  6228.         if( pData )
  6229.         {
  6230.             // Fill supplied struct with data for next cell
  6231.             EDT_TableCellData *pNextData = pCell->GetData();
  6232.             if( pNextData )
  6233.             {
  6234.                 // Set all bits in mask so all values are copied
  6235.                 // This does not change current pData->mask
  6236.                 pNextData->mask = -1;
  6237.                 edt_CopyTableCellData(pData, pNextData);
  6238.                 EDT_FreeTableCellData(pNextData);
  6239.             }
  6240.         }
  6241.     }
  6242. }
  6243.  
  6244. static void edt_SetTableCellData( CEditTableCellElement *pCell, EDT_TableCellData *pData )
  6245. {
  6246.     XP_ASSERT(pCell && pData);
  6247.     if(!pCell || !pData )
  6248.         return;
  6249.  
  6250.     // Get current data before setting new stuff
  6251.     EDT_TableCellData *pCurrentData = pCell->GetData();
  6252.     XP_ASSERT( pCurrentData );
  6253.     if( !pCurrentData )
  6254.         return;
  6255.  
  6256.     // Copy new data to current cell 
  6257.     edt_CopyTableCellData( pCurrentData, pData );
  6258.     // TEMP - TRYING TO TRACK DOWN GROWING CELL BUG 
  6259.     if( pCurrentData->bWidthDefined && !pCurrentData->bWidthPercent )
  6260.         pCurrentData->iWidthPixels = pCurrentData->iWidth;
  6261.     if( pCurrentData->bHeightDefined && !pCurrentData->bHeightPercent )
  6262.         pCurrentData->iHeightPixels = pCurrentData->iHeight;
  6263.  
  6264.     pCell->SetData( pCurrentData );
  6265.  
  6266.  
  6267.     // Try to be smart and automatically delete empty cells
  6268.     //  when user is increasing COLSPAN relative to previous value
  6269.     //TODO: NEED TO ACCOUNT FOR ROWSPAN AS WELL
  6270.     if( (pData->mask & CF_COLSPAN) && pData->iColSpan > 1 )
  6271.     {
  6272.         // Check ROWSPAN and COLSPAN
  6273.         if( pCurrentData )
  6274.         {
  6275.             int iDeleteCount = pData->iColSpan - pCurrentData->iColSpan;
  6276.             int i = 0;
  6277.  
  6278.             pCell = (CEditTableCellElement*)pCell->GetNextSibling();
  6279.             while( pCell && i < iDeleteCount )
  6280.             {
  6281.                 CEditTableCellElement *pNextCell = 
  6282.                         (CEditTableCellElement*)pCell->GetNextSibling();
  6283.  
  6284.                 if( pCell->IsEmpty() )
  6285.                 {
  6286.                     i++;
  6287.                     delete pCell;
  6288.                 }    
  6289.                 pCell = pNextCell;
  6290.             }
  6291.         }
  6292.     }
  6293.  
  6294.     EDT_FreeTableCellData(pCurrentData);
  6295. }
  6296.  
  6297. void CEditBuffer::SetTableCellData(EDT_TableCellData *pData)
  6298. {
  6299.     VALIDATE_TREE(this);
  6300.     CEditInsertPoint ip;
  6301.     GetTableInsertPoint(ip);
  6302.     CEditTableCellElement* pCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  6303.     if( !pCell )
  6304.         return;    
  6305.     CEditElement *pTable = pCell->GetTable();
  6306.     if( !pTable )
  6307.         return;
  6308.  
  6309.     BeginBatchChanges(kGroupOfChangesCommandID);
  6310.  
  6311.     XP_Bool bCurrentCellFound = FALSE;
  6312.     intn iCellCount = m_SelectedEdCells.Size();
  6313.     if( iCellCount > 1 )
  6314.     {
  6315.         // Set data for all selected cells
  6316.         for( intn i = 0; i < iCellCount; i++ )
  6317.         {
  6318.             edt_SetTableCellData( m_SelectedEdCells[i], pData );
  6319.             if( m_SelectedEdCells[i] == pCell )
  6320.                 bCurrentCellFound = TRUE;
  6321.         }
  6322.         // Current cell should alway be in selected cell set,
  6323.         //  so check for that
  6324.         XP_ASSERT( bCurrentCellFound );
  6325.     } else {
  6326.         // Set data for just current cell
  6327.         edt_SetTableCellData( pCell, pData );
  6328.     }
  6329.     Relayout( pTable, 0 );
  6330.  
  6331.     // Adjust caller's data - possibly different size data 
  6332.     //   as a result of Relayout()
  6333.     pData->iWidth = pCell->GetWidthOrPercent();
  6334.     pData->iWidthPixels = pCell->GetWidth();
  6335.     pData->iHeight = pCell->GetHeightOrPercent();
  6336.     pData->iHeightPixels = pCell->GetHeight();
  6337.  
  6338.     EndBatchChanges();
  6339. }
  6340.  
  6341. void CEditBuffer::InsertTableColumns(EDT_TableCellData *pData, XP_Bool bAfterCurrentCell, intn number){
  6342.     VALIDATE_TREE(this);
  6343.     if( number <= 0 )
  6344.     {
  6345.         // Get number of columns from the table selection
  6346.         number = GetNumberOfSelectedColumns();
  6347.         if( number )
  6348.         {        
  6349.             // Move to appropriate cell
  6350.             if( bAfterCurrentCell )
  6351.                 MoveToLastSelectedCell();
  6352.             else
  6353.                 MoveToFirstSelectedCell();
  6354.         }
  6355.     }
  6356.     // If none or just one selected cell, then insert 1 row
  6357.     if( number == 0 && m_SelectedLoCells.Size() <= 1 )
  6358.         number = 1;
  6359.  
  6360.     CEditInsertPoint ip;
  6361.     GetTableInsertPoint(ip);
  6362.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  6363.     if ( pTableCell ){
  6364.         // CInsertTableColumnCommand actually performs the operation in the
  6365.         // constructor of the command. So in order for save-based undo to
  6366.         // work, we must wrap its constructor in BeginBatchChanges.
  6367.         BeginBatchChanges(kGroupOfChangesCommandID/*kInsertTableColumnCommandID*/);
  6368.         ClearTableAndCellSelection();
  6369.         AdoptAndDo(new CInsertTableColumnCommand(this, pData, bAfterCurrentCell, number));
  6370.         EndBatchChanges();
  6371.     }
  6372. }
  6373.  
  6374. void CEditBuffer::DeleteTableColumns(intn number){
  6375.     VALIDATE_TREE(this);
  6376.     CEditInsertPoint ip;
  6377.     GetTableInsertPoint(ip);
  6378.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  6379.     if ( pTableCell ){
  6380.         BeginBatchChanges(kGroupOfChangesCommandID);
  6381.         AdoptAndDo(new CDeleteTableColumnCommand(this, number));
  6382.         EndBatchChanges();
  6383.     }
  6384. }
  6385.  
  6386. void CEditBuffer::InsertTableCells(EDT_TableCellData* /* pData */, XP_Bool bAfterCurrentCell, intn number){
  6387.     VALIDATE_TREE(this);
  6388.     CEditInsertPoint ip;
  6389.     GetTableInsertPoint(ip);
  6390.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  6391.     if ( pTableCell ){
  6392.         // CInsertTableCellCommand actually performs the operation in the
  6393.         // constructor of the command. So in order for save-based undo to
  6394.         // work, we must wrap its constructor in BeginBatchChanges.
  6395.         BeginBatchChanges(kGroupOfChangesCommandID /*kInsertTableCellCommandID*/);
  6396.         AdoptAndDo(new CInsertTableCellCommand(this, bAfterCurrentCell, number));
  6397.         EndBatchChanges();
  6398.     }
  6399. }
  6400.  
  6401. void CEditBuffer::DeleteTableCells(intn number){
  6402.     VALIDATE_TREE(this);
  6403.     CEditInsertPoint ip;
  6404.     GetTableInsertPoint(ip);
  6405.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  6406.     if ( pTableCell ){
  6407.         BeginBatchChanges(kGroupOfChangesCommandID);
  6408.         AdoptAndDo(new CDeleteTableCellCommand(this, number));
  6409.         EndBatchChanges();
  6410.     }
  6411. }
  6412.  
  6413. // Layer stuff
  6414.  
  6415. XP_Bool CEditBuffer::IsInsertPointInLayer(){
  6416.     CEditInsertPoint ip;
  6417.     GetInsertPoint(ip);
  6418.     return ip.m_pElement->GetLayerIgnoreSubdoc() != NULL;
  6419. }
  6420.  
  6421. EDT_LayerData* CEditBuffer::GetLayerData(){
  6422.     CEditInsertPoint ip;
  6423.     GetInsertPoint(ip);
  6424.     CEditLayerElement* pLayer = ip.m_pElement->GetLayerIgnoreSubdoc();
  6425.     if ( pLayer ){
  6426.         return pLayer->GetData();
  6427.     }
  6428.     else {
  6429.         return NULL;
  6430.     }
  6431. }
  6432.  
  6433. void CEditBuffer::SetLayerData(EDT_LayerData *pData){
  6434.     VALIDATE_TREE(this);
  6435.     CEditInsertPoint ip;
  6436.     GetInsertPoint(ip);
  6437.     CEditLayerElement* pLayer = ip.m_pElement->GetLayerIgnoreSubdoc();
  6438.     if ( pLayer ){
  6439.         pLayer->SetData( pData );
  6440.         Relayout( pLayer, 0 );
  6441.     }
  6442. }
  6443.  
  6444. void CEditBuffer::InsertLayer(EDT_LayerData *pData){
  6445.     CEditLayerElement *pLayer = new CEditLayerElement();
  6446.     if( pLayer ){
  6447.         pLayer->SetData( pData );
  6448.         pLayer->FinishedLoad(this); // Sets default paragraphs for all the cells
  6449.         InsertNonLeaf(pLayer);
  6450.         SyncCursor(pLayer);
  6451.     }
  6452. }
  6453.  
  6454. void CEditBuffer::DeleteLayer(){
  6455.     VALIDATE_TREE(this);
  6456.     CEditInsertPoint ip;
  6457.     GetInsertPoint(ip);
  6458.     CEditLayerElement* pLayer = ip.m_pElement->GetLayerIgnoreSubdoc();
  6459.     if ( pLayer ){
  6460.         CEditInsertPoint ip;
  6461.         GetInsertPoint(ip);
  6462.         CEditElement* pRefreshStart = pLayer->GetFirstMostChild()->PreviousLeaf();
  6463.         CEditInsertPoint replacePoint(pLayer->GetLastMostChild()->NextLeaf(), 0);
  6464.         SetInsertPoint(replacePoint);
  6465.         delete pLayer;
  6466.         Relayout(pRefreshStart, 0, replacePoint.m_pElement);
  6467.     }
  6468. }
  6469.  
  6470. EDT_HorizRuleData* CEditBuffer::GetHorizRuleData(){
  6471.     CEditLeafElement *pInsertPoint;
  6472.     ElementOffset iOffset;
  6473.     XP_Bool bSingleItem;
  6474.  
  6475.     bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  6476. //  CLM: This causes problems for dynamic sizing
  6477. //    XP_ASSERT( bSingleItem );
  6478.  
  6479.     XP_ASSERT( pInsertPoint->IsA(P_HRULE) );
  6480.     return pInsertPoint->HorizRule()->GetData( );
  6481. }
  6482.  
  6483. void CEditBuffer::SetHorizRuleData( EDT_HorizRuleData* pData ){
  6484.     ClearSelection( TRUE, TRUE );
  6485.     XP_ASSERT( m_pCurrent->IsA(P_HRULE) );
  6486.     m_pCurrent->HorizRule()->SetData( pData );
  6487.     Relayout( m_pCurrent->FindContainer(), 0, m_pCurrent, RELAYOUT_NOCARET );
  6488.     SelectCurrentElement();
  6489. }
  6490.  
  6491. void CEditBuffer::InsertHorizRule( EDT_HorizRuleData* pData ){
  6492.     if( IsSelected() ){
  6493.         ClearSelection();
  6494.     }
  6495.     BeginBatchChanges(kInsertHorizRuleCommandID);
  6496.     CEditHorizRuleElement *pHorizRule = new CEditHorizRuleElement(0);
  6497.     pHorizRule->SetData( pData );
  6498.     InsertLeaf( pHorizRule );
  6499.     EndBatchChanges();
  6500. }
  6501.  
  6502. char* CEditBuffer::GetTargetData(){
  6503.     CEditLeafElement *pInsertPoint;
  6504.     ElementOffset iOffset;
  6505.     XP_Bool bSingleItem;
  6506.  
  6507.     bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  6508.     XP_ASSERT( bSingleItem );
  6509.  
  6510.     XP_ASSERT( pInsertPoint->GetElementType() == eTargetElement );
  6511.     return pInsertPoint->Target()->GetName( );
  6512. }
  6513.  
  6514. void CEditBuffer::SetTargetData( char* pData ){
  6515.     ClearSelection( TRUE, TRUE );
  6516.     if ( m_pCurrent->GetElementType() == eTargetElement ){
  6517.         m_pCurrent->Target()->SetName( pData, GetRAMCharSetID() );
  6518.         Relayout( m_pCurrent->FindContainer(), 0, m_pCurrent, RELAYOUT_NOCARET );
  6519.         SelectCurrentElement();
  6520.     }
  6521.     else {
  6522.         XP_ASSERT(FALSE);
  6523.     }
  6524. }
  6525.  
  6526. void CEditBuffer::InsertTarget( char* pData ){
  6527.     if( IsSelected() ){
  6528.         ClearSelection(TRUE, TRUE);
  6529.     }
  6530.     int16 csid = GetRAMCharSetID();
  6531.     CEditTargetElement *pTarget = new CEditTargetElement(0, 0, csid);
  6532.     pTarget->SetName( pData, csid );
  6533.     InsertLeaf( pTarget );
  6534. }
  6535.  
  6536. char* CEditBuffer::GetAllDocumentTargets(){
  6537.     intn iSize = 500;
  6538.     int iCur = 0;
  6539.     char *pBuf = (char*)XP_ALLOC( iSize );
  6540.     CEditElement *pNext = m_pRoot;
  6541.  
  6542.     pBuf[0] = 0;
  6543.     pBuf[1] = 0;
  6544.  
  6545.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTarget, 0 )) ){
  6546.         char *pName = pNext->Target()->GetName();
  6547.         if( pName && *pName ){
  6548.             int iLen = XP_STRLEN( pName );
  6549.             if( iCur + iLen + 2 > iSize ){
  6550.                 iSize = iSize + iSize;
  6551.                 pBuf = (char*)XP_REALLOC( pBuf, iSize );
  6552.             }
  6553.             XP_STRCPY( &pBuf[iCur], pName );
  6554.             iCur += iLen+1;
  6555.         }
  6556.     }
  6557.     pBuf[iCur] = 0;
  6558.     return pBuf;
  6559. }
  6560.  
  6561. #define LINE_BUFFER_SIZE  4096
  6562.  
  6563. char* CEditBuffer::GetAllDocumentTargetsInFile(char *pHref){
  6564.     intn iSize = 500;
  6565.     int iCur = 0;
  6566.     char *pBuf = (char*)XP_ALLOC( iSize );
  6567.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  6568. //    CEditElement *pNext = m_pRoot;
  6569.     pBuf[0] = 0;
  6570.     pBuf[1] = 0;
  6571.  
  6572.     char *pFilename = NULL;
  6573.  
  6574. // The version for windows is the old bad code that only works
  6575. // right for windows.
  6576. // The UNIX/MAC code is XP and should really be used for
  6577. // windows also.  We're just scared to change it this late
  6578. // in the cycle. Bug 50888
  6579. #ifdef XP_WIN
  6580.     // First check if URL is a local file that exists
  6581.     XP_StatStruct stat;
  6582.     XP_Bool bFreeFilename = FALSE;
  6583.  
  6584. #if defined(XP_MAC) || defined(XP_UNIX) || defined(XP_OS2)
  6585.     if ( -1 != XP_Stat(pHref, &stat, xpURL) &&
  6586.         stat.st_mode & S_IFREG ) {
  6587. #else
  6588.     if ( -1 != XP_Stat(pHref, &stat, xpURL) &&
  6589.         stat.st_mode & _S_IFREG ) {
  6590. #endif
  6591.         // We can use unprocessed URL,
  6592.         //   and we don't need to free it
  6593.         pFilename = pHref;
  6594.     }
  6595.     else {
  6596.         // We probably have a URL,
  6597.         //  get absolute URL path then convert to local format
  6598.         char *pAbsolute = NET_MakeAbsoluteURL( LO_GetBaseURL(m_pContext ), pHref );
  6599.  
  6600.         if( !pAbsolute ||
  6601.             !XP_ConvertUrlToLocalFile(pAbsolute, &pFilename) ){
  6602.             if( pFilename ) XP_FREE(pFilename);
  6603.             if( pAbsolute ) XP_FREE(pAbsolute);
  6604.             return NULL;
  6605.         }
  6606.         XP_FREE(pAbsolute);
  6607.         // We need to free the filename made for us
  6608.         bFreeFilename = TRUE;
  6609.     }
  6610.  
  6611. #else
  6612.     // pFilename is in xpURL format.
  6613.  
  6614.     // First check if URL is a local file that exists
  6615.     char *pURL = XP_PlatformFileToURL(pHref);
  6616.     if (pURL && XP_ConvertUrlToLocalFile(pURL,NULL)) {
  6617.         pFilename = NET_ParseURL(pURL,GET_PATH_PART);
  6618.         XP_FREE(pURL);
  6619.     }
  6620.     else {
  6621.         XP_FREEIF(pURL);
  6622.  
  6623.         // We probably have a URL,
  6624.         //  get absolute URL path then convert to local format
  6625.         char *pAbsolute = NET_MakeAbsoluteURL( LO_GetBaseURL(m_pContext ), pHref );
  6626.  
  6627.         if( pAbsolute &&
  6628.             XP_ConvertUrlToLocalFile(pAbsolute,NULL) ) {
  6629.             pFilename = NET_ParseURL(pAbsolute,GET_PATH_PART);
  6630.         }
  6631.  
  6632.         XP_FREEIF(pAbsolute);
  6633.  
  6634.     }
  6635.     if (!pFilename) {
  6636.       return NULL;
  6637.     }
  6638. #endif
  6639.  
  6640.     // Open local file
  6641.     XP_File file = XP_FileOpen( pFilename, xpURL, XP_FILE_READ );
  6642.     if( !file ) {
  6643.  
  6644.         // Same story as above. Should use the non-windows version.
  6645. #ifdef XP_WIN
  6646.         if( pFilename && bFreeFilename ){
  6647.             XP_FREE(pFilename);
  6648.         }
  6649. #else
  6650.         XP_FREEIF(pFilename);
  6651. #endif
  6652.         return NULL;
  6653.     }
  6654.  
  6655.     char    pFileBuf[LINE_BUFFER_SIZE];
  6656.     char   *ptr, *pEnd, *pStart;
  6657.     size_t  count;
  6658.     PA_Tag *pTag;
  6659.     char   *pName;
  6660.  
  6661.     // Read unformated chunks
  6662.     while( 0 < (count = fread(pFileBuf, 1, LINE_BUFFER_SIZE, file)) ){
  6663.         // Scan from the end to find end of last tag in block
  6664.         ptr = pFileBuf + count -1;
  6665.         while( (ptr > pFileBuf) && (*ptr != '>') ) ptr--;
  6666.  
  6667.         //Bloody unlikely, but we didn't find a tag!
  6668.         if( ptr == pFileBuf ) continue;
  6669.  
  6670.  
  6671.         // Move file pointer back so next read starts
  6672.         //   1 char after the region just found
  6673.         int iBack = int(ptr - pFileBuf) -  int(count) + 1;
  6674.         fseek( file, iBack, SEEK_CUR );
  6675.  
  6676.         // Save the end of "tagged" region
  6677.         //  and reset to beginning
  6678.         pEnd = ptr;
  6679.         ptr = pFileBuf;
  6680.  
  6681. FIND_TAG:
  6682.         // Scan to beginning of any tag
  6683.         while( (ptr < pEnd) && (*ptr != '<') ) ptr++;
  6684.         if( ptr == pEnd ) continue;
  6685.  
  6686.         // Save start of tag
  6687.         pStart = ptr;
  6688.  
  6689.         // Skip over whitespace before tag name
  6690.         ptr++;
  6691.         while( (ptr < pEnd) && (XP_IS_SPACE(*ptr)) ) ptr++;
  6692.         if( ptr == pEnd ) continue;
  6693.  
  6694.         // Check for Anchor tag
  6695.         if( ((*ptr == 'a') || (*ptr == 'A')) &&
  6696.              XP_IS_SPACE(*(ptr+1)) ){
  6697.             // Find end of the tag
  6698.             while( (ptr < pEnd) && (*ptr != '>') ) ptr++;
  6699.             if( ptr == pEnd ) continue;
  6700.  
  6701.             // Parse into tag struct so we can use
  6702.             //   edt_FetchParamString to do the tricky stuff
  6703.             // Kludge city. pa_CreateMDLTag needs a pa_DocData solely to
  6704.             // look at the line count.
  6705.             {
  6706.                 pa_DocData doc_data;
  6707.                 doc_data.newline_count = 0;
  6708.                 pTag = pa_CreateMDLTag(&doc_data, pStart, (ptr - pStart)+1 );
  6709.             }
  6710.             if( pTag ){
  6711.                 if( pTag->type == P_ANCHOR ){
  6712.                     pName = edt_FetchParamString(pTag, PARAM_NAME, INTL_GetCSIWinCSID(c));
  6713.                     if( pName ){
  6714.                         // We found a Name
  6715.                         int iLen = XP_STRLEN( pName );
  6716.                         if( iCur + iLen + 2 > iSize ){
  6717.                             iSize = iSize + iSize;
  6718.                             pBuf = (char*)XP_REALLOC( pBuf, iSize );
  6719.                         }
  6720.                         XP_STRCPY( &pBuf[iCur], pName );
  6721.                         iCur += iLen+1;
  6722.                         XP_FREE(pName);
  6723.                     }
  6724.                 }
  6725.                 PA_FreeTag(pTag);
  6726.                 // Move past the tag we found and search again
  6727.                 ptr++;
  6728.                 goto FIND_TAG;
  6729.             }
  6730.             else {
  6731.                 // We couldn't find a complete tag, get another block
  6732.                 continue;
  6733.             }
  6734.         } else {
  6735.             // Not an anchor tag, try again
  6736.             ptr++;
  6737.             goto FIND_TAG;
  6738.         }
  6739.     }
  6740.     XP_FileClose(file);
  6741.  
  6742. // Same story as above. Should use the non-windows version.
  6743. #ifdef XP_WIN
  6744.     if( pFilename && bFreeFilename ){
  6745.         XP_FREE(pFilename);
  6746.     }
  6747. #else
  6748.     XP_FREEIF(pFilename);
  6749. #endif
  6750.  
  6751.     pBuf[iCur] = 0;
  6752.  
  6753.     return pBuf;
  6754. }
  6755.  
  6756. #ifdef FIND_REPLACE
  6757. XP_Bool CEditBuffer::FindAndReplace( EDT_FindAndReplaceData *pData ){
  6758.     return FALSE;
  6759. }
  6760. #endif
  6761.  
  6762.  
  6763.  
  6764. class CStretchBuffer {
  6765. private:
  6766.     char *m_pBuffer;
  6767.     intn m_iSize;
  6768.     intn m_iCur;
  6769.  
  6770. public:
  6771.     CStretchBuffer();
  6772.     ~CStretchBuffer(){}        // intentionall don't destroy the buffer.
  6773.     void Add( char *pString );
  6774.     char* GetBuffer(){ return m_pBuffer; }
  6775.     // if index non-NULL, return the zero-based index of the found
  6776.     // string.
  6777.     XP_Bool Contains( char* p, int *pIndex = NULL );
  6778. };
  6779.  
  6780. CStretchBuffer::CStretchBuffer(){
  6781.     m_pBuffer = (char*)XP_ALLOC( 512 );
  6782.     m_iSize = 512;
  6783.     m_iCur = 0;
  6784.     m_pBuffer[0] = 0;
  6785.     m_pBuffer[1] = 0;
  6786. }
  6787.  
  6788. void CStretchBuffer::Add( char *p ){
  6789.     if( p == 0 || *p == 0 ){
  6790.         return;
  6791.     }
  6792.     int iLen = XP_STRLEN( p );
  6793.     while( m_iCur + iLen + 2 > m_iSize ){
  6794.         m_iSize = m_iSize + m_iSize;
  6795.         m_pBuffer = (char*)XP_REALLOC( m_pBuffer, m_iSize );
  6796.     }
  6797.     XP_STRCPY( &m_pBuffer[m_iCur], p );
  6798.     m_iCur += iLen+1;
  6799.     m_pBuffer[m_iCur] = 0;
  6800. }
  6801.  
  6802. XP_Bool CStretchBuffer::Contains( char* p, int *pIndex ){
  6803.     if (pIndex) {
  6804.       *pIndex = 0;
  6805.     }
  6806.     char *s = GetBuffer();
  6807.     while( s && *s ){
  6808.         // Assume we are dealing with absolute URLs only.
  6809.         if( EDT_IsSameURL(s,p,NULL,NULL) ){
  6810.             return TRUE;
  6811.         }
  6812.         s += XP_STRLEN(s)+1;
  6813.         if (pIndex) {
  6814.           (*pIndex)++;
  6815.         }
  6816.     }
  6817.     return FALSE;
  6818. }
  6819.  
  6820.  
  6821. // Helper for CEditBuffer::GetAllDocumentFiles.  If pImageURL is not in buf, add it and add 
  6822. // element to the "selected" list.  If pImageURL is in buf and was previously unselected, we may
  6823. // now select it.
  6824. //
  6825. // Analogous to CEditSaveObject::CheckAddFile()
  6826. PRIVATE
  6827. void AddToBufferUnique(CStretchBuffer &buf, TXP_GrowableArray_int32 &selected,
  6828.             char *pBaseURL, char *pImageURL, XP_Bool bSelected) {
  6829.   //XP_Bool bSelected = !bNoSave;
  6830.   char *pFilename = NET_MakeAbsoluteURL( pBaseURL, pImageURL);
  6831.   if (pFilename) {
  6832.      int index;
  6833.     // Don't insert duplicates.
  6834.      if (!buf.Contains( pFilename, &index )) {
  6835.        // keep buf and selected the same size.
  6836.        buf.Add( pFilename );
  6837.        selected.Add((int32)bSelected); 
  6838.      }
  6839.      else if (bSelected) {
  6840.         // If wasn't selected before, it is now.
  6841.         selected[index] = (int32)bSelected;
  6842.      }
  6843.      XP_FREE(pFilename);
  6844.   }
  6845. }
  6846.  
  6847. // CEditBuffer::GetAllDocumentFiles() has been significantly changed.
  6848. // The returned files do not have to be in the same directory, and are not
  6849. // checked for existence.  hardts
  6850. //
  6851. // If ppSelected is passed in, it will be set to an array of length equal to the number
  6852. // of strings in the returned value.  For each image, it tells whether, by default, it should be 
  6853. // published/saved along with the document.  E.g. looks at the NOSAVE attribute.
  6854. // ppSelected may be NULL.
  6855. // If no images found, no memory will be allocated for ppSelected.
  6856. char* CEditBuffer::GetAllDocumentFiles(XP_Bool **ppSelected,XP_Bool bKeepImagesWithDoc){
  6857. //////// WARNING: if you change this function, fix CEditSaveObject::AddAllFiles() and CEditSaveObject::FixupLinks() also.
  6858.     TXP_GrowableArray_int32 selected;
  6859.  
  6860.     CEditElement *pNext = m_pRoot;
  6861.     CStretchBuffer buf;
  6862.     char *pRetVal = 0;
  6863.  
  6864.     char *pDocURL = edt_GetDocRelativeBaseURL(m_pContext);
  6865.     if( !pDocURL ){
  6866.         return NULL;
  6867.     }
  6868.  
  6869.     // Font Ref.
  6870.     if( m_pFontDefURL && *m_pFontDefURL) {
  6871.       AddToBufferUnique(buf,selected,pDocURL,m_pFontDefURL,
  6872.             bKeepImagesWithDoc && !m_bFontDefNoSave);
  6873.     }
  6874.  
  6875.     // Background image.
  6876.     if( m_pBackgroundImage && *m_pBackgroundImage) {
  6877.       AddToBufferUnique(buf,selected,pDocURL,m_pBackgroundImage,
  6878.             bKeepImagesWithDoc && !m_bBackgroundNoSave);
  6879.     }
  6880.  
  6881.     // Regular images.
  6882.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindImage, 0 )) ){
  6883.         EDT_ImageData *pData = pNext->Image()->GetImageData();
  6884.         if( pData && pData->pSrc && *pData->pSrc) {
  6885.           AddToBufferUnique(buf,selected,pDocURL,pData->pSrc,
  6886.                 bKeepImagesWithDoc && !pData->bNoSave);
  6887.         }
  6888.  
  6889.         if( pData && pData->pLowSrc && *pData->pLowSrc) {
  6890.           AddToBufferUnique(buf,selected,pDocURL,pData->pLowSrc,
  6891.                 bKeepImagesWithDoc && !pData->bNoSave);
  6892.         }
  6893.         EDT_FreeImageData( pData );
  6894.     }
  6895.  
  6896.     
  6897.     //// Sure would be nice to abstract adding all the different types of table
  6898.     //// backgrounds.
  6899.     // table backgrounds <table>
  6900.     pNext = m_pRoot;
  6901.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTable, 0 )) ){
  6902.       EDT_TableData *pData = ((CEditTableElement *)pNext)->GetData();
  6903.       if (pData) {
  6904.         if (pData->pBackgroundImage && *pData->pBackgroundImage) {
  6905.           AddToBufferUnique(buf,selected,pDocURL,pData->pBackgroundImage,
  6906.                   bKeepImagesWithDoc && !pData->bBackgroundNoSave);
  6907.         }
  6908.         CEditTableElement::FreeData(pData);
  6909.       }
  6910.     }
  6911.     // table row backgrounds <tr>
  6912.     pNext = m_pRoot;
  6913.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableRow, 0 )) ){
  6914.       EDT_TableRowData *pData = ((CEditTableRowElement *)pNext)->GetData();
  6915.       if (pData) {
  6916.         if (pData->pBackgroundImage && *pData->pBackgroundImage) {
  6917.           AddToBufferUnique(buf,selected,pDocURL,pData->pBackgroundImage,
  6918.                   bKeepImagesWithDoc && !pData->bBackgroundNoSave);
  6919.         }
  6920.         CEditTableRowElement::FreeData(pData);
  6921.       }
  6922.     }
  6923.     // table cell backgrounds <td> <th>
  6924.     pNext = m_pRoot;
  6925.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindTableCell, 0 )) ){
  6926.       EDT_TableCellData *pData = ((CEditTableCellElement *)pNext)->GetData();
  6927.       if (pData) {
  6928.         if (pData->pBackgroundImage && *pData->pBackgroundImage) {
  6929.           AddToBufferUnique(buf,selected,pDocURL,pData->pBackgroundImage,
  6930.                   bKeepImagesWithDoc && !pData->bBackgroundNoSave);
  6931.         }
  6932.         CEditTableCellElement::FreeData(pData);
  6933.       }
  6934.     }
  6935.  
  6936.  
  6937.     // UnknownHTML tags with LOCALDATA attribute.
  6938.     pNext = m_pRoot;
  6939.     while(NULL != (pNext = pNext->FindNextElement( &CEditElement::FindUnknownHTML, 0 )) ){
  6940.       CEditIconElement *pIcon = CEditIconElement::Cast(pNext);
  6941.       if (pIcon) {
  6942.         char **pMimeTypes;
  6943.         char **pURLs;
  6944.         int count = pIcon->ParseLocalData(&pMimeTypes,&pURLs);
  6945.  
  6946.         // Maybe should make a check that pURLs[n] is a relative URL
  6947.         // in current directory.
  6948.         for (int n = 0; n < count; n++) {
  6949.           AddToBufferUnique(buf,selected,pDocURL,pURLs[n],TRUE);
  6950.         }
  6951.         CEditIconElement::FreeLocalDataLists(pMimeTypes,pURLs,count);
  6952.       }
  6953.     } // while
  6954.  
  6955.     pRetVal = buf.GetBuffer();
  6956.  
  6957.     // If no files found, return NULL
  6958.     if( pRetVal && *pRetVal == '\0' ){
  6959.         XP_FREE(pRetVal);
  6960.         pRetVal = NULL;
  6961.     }
  6962.  
  6963.     // Create ppSelected list if it was passed in.
  6964.     if (ppSelected) {
  6965.       if (pRetVal) {
  6966.         // allocate memory.
  6967.         *ppSelected = (XP_Bool *)XP_ALLOC(selected.Size() * sizeof(XP_Bool));
  6968.         if (!*ppSelected) {
  6969.           // Out of memory, not really the best thing to do.
  6970.           return pRetVal;
  6971.         }
  6972.         // Fill the array.
  6973.         for (int n  = 0; n < selected.Size(); n++) {
  6974.           (*ppSelected)[n] = (XP_Bool)selected[n];
  6975.         }
  6976.       }
  6977.       // No images in doc, set ppSelected to NULL.
  6978.       else {
  6979.         *ppSelected = NULL;
  6980.       }
  6981.     }
  6982.  
  6983.     XP_FREE(pDocURL);
  6984.     return pRetVal;
  6985. }
  6986.  
  6987. char* CEditBuffer::GetUnknownTagData(){
  6988.     CEditLeafElement *pInsertPoint;
  6989.     ElementOffset iOffset;
  6990.     XP_Bool bSingleItem;
  6991.  
  6992.     bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  6993.     XP_ASSERT( bSingleItem );
  6994.  
  6995.     XP_ASSERT( pInsertPoint->IsIcon() );
  6996.     return pInsertPoint->Icon()->GetData( );
  6997. }
  6998.  
  6999. void CEditBuffer::SetUnknownTagData( char* pData ){
  7000.     ClearSelection( TRUE, TRUE );
  7001.     XP_ASSERT( m_pCurrent->GetElementType() == eIconElement );
  7002.     m_pCurrent->Icon()->SetData( pData );
  7003.     Relayout( m_pCurrent->FindContainer(), 0, m_pCurrent, RELAYOUT_NOCARET );
  7004.     SelectCurrentElement();
  7005. }
  7006.  
  7007. void CEditBuffer::InsertUnknownTag( char *pData ){
  7008.     if( IsSelected() ){
  7009.         ClearSelection();
  7010.     }
  7011.     if ( ! pData ) {
  7012.         XP_ASSERT(FALSE);
  7013.         return;
  7014.     }
  7015.  
  7016.     // This should probably be done elsewhere.  Remove trailing whitespace. HARDTS.
  7017.     int32 iLen = XP_STRLEN(pData);
  7018.     if (iLen > 0) {
  7019.       char *pLast = pData + (iLen - 1);
  7020.       while (pLast >= pData && XP_IS_SPACE(*pLast))
  7021.         pLast--;
  7022.       *(pLast+1) = '\0';
  7023.     }
  7024.  
  7025.     NormalizeEOLsInString(pData);
  7026.  
  7027.  
  7028.     XP_Bool bEndTag = ( iLen > 1 && pData[1] == '/' );
  7029.     CEditIconElement *pUnknownTag = new CEditIconElement(0,
  7030.         bEndTag ? EDT_ICON_UNSUPPORTED_END_TAG :EDT_ICON_UNSUPPORTED_TAG);
  7031.     pUnknownTag->SetData( pData );
  7032.     InsertLeaf( pUnknownTag );
  7033. }
  7034.  
  7035. EDT_ListData* CEditBuffer::GetListData(){
  7036.     CEditContainerElement *pContainer;
  7037.     CEditListElement *pList;
  7038.     if( IsSelected() ){
  7039.         CEditContainerElement *pEndContainer;
  7040.         CEditListElement *pEndList;
  7041.         CEditSelection selection;
  7042.  
  7043.         // LTNOTE: this is a hack.  It doesen't handle a bunch of cases.
  7044.         // It needs to be able to handle multiple lists selected.
  7045.         // It should work a little better.
  7046.         //cmanske: TODO: FIX LISTS TO HANDLE SELECTIONS
  7047.         GetSelection( selection );
  7048.         selection.m_start.m_pElement->FindList( pContainer, pList );
  7049.         selection.GetClosedEndContainer()->GetLastMostChild()->FindList( pEndContainer, pEndList );
  7050.         if( pList != pEndList ){
  7051.             return 0;
  7052.         }
  7053.     }
  7054.     else {
  7055.         m_pCurrent->FindList( pContainer, pList );
  7056.     }
  7057.     if( pList ){
  7058.         return pList->GetData( );
  7059.     }
  7060.     else {
  7061.         return 0;
  7062.     }
  7063. }
  7064.  
  7065. void CEditBuffer::SetListData( EDT_ListData* pData ){
  7066.     VALIDATE_TREE(this);
  7067.     CEditContainerElement *pContainer;
  7068.     CEditListElement *pList;
  7069.  
  7070.     if( IsSelected() ){
  7071.         CEditLeafElement *pBegin, *pEnd, *pCurrent;
  7072.         ElementOffset iBeginPos, iEndPos;
  7073.         XP_Bool bFromStart;
  7074.         GetSelection( pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  7075.         pCurrent = pBegin;
  7076.         XP_Bool bDone = FALSE;
  7077.         do {
  7078.             pCurrent->FindList( pContainer, pList );
  7079.             if( pList ){
  7080.                 pList->SetData( pData );
  7081.             }
  7082.  
  7083.             bDone = (pEnd == pCurrent );    // For most cases
  7084.             pCurrent = pCurrent->NextLeafAll();
  7085.             bDone = bDone || (iEndPos == 0 && pEnd == pCurrent ); // Pesky edge conditions!
  7086.         } while( pCurrent && !bDone );
  7087.  
  7088.         // force layout stop displaying the current selection.
  7089. #ifdef LAYERS
  7090.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL);
  7091. #else
  7092.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  7093. #endif
  7094.  
  7095.         CEditElement *pLast = pEnd;
  7096.         if( pEnd ){
  7097.             pEnd->FindList( pContainer, pList );
  7098.             if( pList ) pLast = pList->GetLastMostChild();
  7099.         }
  7100.  
  7101.         Relayout( pBegin->FindContainer(), 0, pLast, RELAYOUT_NOCARET );
  7102.         // Need to force selection.
  7103.         SelectRegion(pBegin, iBeginPos, pEnd, iEndPos, bFromStart );
  7104.     }
  7105.     else {
  7106.  
  7107.         m_pCurrent->FindList( pContainer, pList );
  7108.         if( pList ){
  7109.             pList->SetData( pData );
  7110.             Relayout( pList, 0, pList->GetLastMostChild() );
  7111.         }
  7112.     }
  7113. }
  7114.  
  7115. void CEditBuffer::InsertBreak( ED_BreakType eBreak, XP_Bool bTyping ){
  7116.     VALIDATE_TREE(this);
  7117.  
  7118.     if( IsSelected() ){
  7119.         // ToDo: Consider cutting the selection here, like InsertChar does.
  7120.         ClearSelection();
  7121.     }
  7122.  
  7123.     StartTyping(bTyping);
  7124.     PA_Tag *pTag = XP_NEW( PA_Tag );
  7125.     XP_BZERO( pTag, sizeof( PA_Tag ) );
  7126.     pTag->type = P_LINEBREAK;
  7127.  
  7128.     switch( eBreak ){
  7129.         case ED_BREAK_NORMAL:
  7130.             break;
  7131.         case ED_BREAK_LEFT:
  7132.             edt_SetTagData( pTag, "CLEAR=LEFT>" );
  7133.             break;
  7134.         case ED_BREAK_RIGHT:
  7135.             edt_SetTagData( pTag, "CLEAR=RIGHT>" );
  7136.             break;
  7137.         case ED_BREAK_BOTH:
  7138.             edt_SetTagData( pTag, "CLEAR=BOTH>" );
  7139.             break;
  7140.     }
  7141.     CEditBreakElement *pBreak = new CEditBreakElement( 0, pTag );
  7142.     InsertLeaf(pBreak);
  7143.     FixupSpace(bTyping);
  7144.     Reduce(pBreak->GetParent());
  7145. }
  7146.  
  7147. EDT_ClipboardResult CEditBuffer::CanCut(XP_Bool bStrictChecking){
  7148.     CEditSelection selection;
  7149.     GetSelection(selection);
  7150.     return CanCut(selection, bStrictChecking);
  7151. }
  7152.  
  7153. EDT_ClipboardResult CEditBuffer::CanCut(CEditSelection& selection, XP_Bool bStrictChecking)
  7154. {
  7155.     if( IsTableOrCellSelected() )
  7156.     {
  7157.         //TODO: MODIFY TO CHECK FOR CONTENTS TO COPY?
  7158.         return EDT_COP_OK;
  7159.     }
  7160.     EDT_ClipboardResult result = selection.IsInsertPoint() ? EDT_COP_SELECTION_EMPTY : EDT_COP_OK;
  7161.     if ( bStrictChecking && result == EDT_COP_OK
  7162.         && selection.CrossesSubDocBoundary() ) {
  7163.         result = EDT_COP_SELECTION_CROSSES_TABLE_DATA_CELL;
  7164.     }
  7165.     return result;
  7166. }
  7167.  
  7168. EDT_ClipboardResult CEditBuffer::CanCopy(XP_Bool bStrictChecking){
  7169.     return CanCut(bStrictChecking); /* In the future we may do something for read-only docs. */
  7170. }
  7171.  
  7172. EDT_ClipboardResult CEditBuffer::CanCopy(CEditSelection& selection, XP_Bool bStrictChecking){
  7173.     return CanCut(selection, bStrictChecking); /* In the future we may do something for read-only docs. */
  7174. }
  7175.  
  7176. EDT_ClipboardResult CEditBuffer::CanPaste(XP_Bool bStrictChecking){
  7177.     CEditSelection selection;
  7178.     GetSelection(selection);
  7179.     return CanPaste(selection, bStrictChecking);
  7180. }
  7181.  
  7182. EDT_ClipboardResult CEditBuffer::CanPaste(CEditSelection& selection, XP_Bool bStrictChecking){
  7183.     EDT_ClipboardResult result = EDT_COP_OK;
  7184.     if ( bStrictChecking
  7185.         && selection.CrossesSubDocBoundary() ) {
  7186.         result = EDT_COP_SELECTION_CROSSES_TABLE_DATA_CELL;
  7187.     }
  7188.     return result;
  7189. }
  7190.  
  7191. XP_Bool CEditBuffer::CanSetHREF(){
  7192.     if( IsSelected()
  7193.             || ( m_pCurrent && m_pCurrent->GetHREF() != ED_LINK_ID_NONE ) ){
  7194.         return TRUE;
  7195.     }
  7196.     else {
  7197.         return FALSE;
  7198.     }
  7199. }
  7200.  
  7201. //  This needs to be written to:
  7202. //    Return data in EDT_HREFData struct containing:
  7203. //    pTitle:   ALL the text of elements with same HREF
  7204. //              or the image name if only an image is selected
  7205. //    pHREF     what we are getting now
  7206. //    pTarget   what we are ignoring now!
  7207. //    pMocha    "
  7208.  
  7209. char *CEditBuffer::GetHREF(){
  7210.     ED_LinkId id = GetHREFLinkID();
  7211.     if( id != ED_LINK_ID_NONE ){
  7212.         return linkManager.GetHREF(id) ;
  7213.     }
  7214.     else {
  7215.         return 0;
  7216.     }
  7217. }
  7218.  
  7219. ED_LinkId CEditBuffer::GetHREFLinkID(){
  7220.     CEditLeafElement *pElement = m_pCurrent;
  7221.  
  7222.     if( IsSelecting() ){
  7223.         return ED_LINK_ID_NONE;
  7224.     }
  7225.  
  7226.     if( IsSelected() ){
  7227.         ElementOffset i,i1;
  7228.         CEditLeafElement *pEnd;
  7229.         XP_Bool bFromStart;
  7230.         GetSelection( pElement, i, pEnd, i1, bFromStart );
  7231.     }
  7232.     if( pElement ){
  7233.         return pElement->Leaf()->GetHREF();
  7234.     }
  7235.     return ED_LINK_ID_NONE;
  7236. }
  7237.  
  7238. char* CEditBuffer::GetHREFText(){
  7239.     CEditLeafElement *pElement = m_pCurrent;
  7240.     ED_LinkId id;
  7241.     char* pBuf = 0;
  7242.  
  7243.     if( pElement && pElement->IsText() &&
  7244.         (id = pElement->GetHREF()) != ED_LINK_ID_NONE ){
  7245.  
  7246.         // Move back to find start of contiguous elements
  7247.         //  with same HREF
  7248.         CEditLeafElement *pStartElement = pElement;
  7249.         while ( (pElement = (CEditLeafElement*)pElement->GetPreviousSibling()) != NULL &&
  7250.                  pElement->IsText() &&
  7251.                  pElement->GetHREF() == id ) {
  7252.             pStartElement = pElement;
  7253.         }
  7254.  
  7255.         // We now have starting EditText element
  7256.         pElement = pStartElement;
  7257.         char* pText = pStartElement->Text()->GetText();
  7258.         pBuf = pText ? XP_STRDUP( pText ) : 0;
  7259.         if( !pBuf ){
  7260.             return 0;
  7261.         }
  7262.  
  7263.         // Now scan forward to accumulate text
  7264.         while ( (pElement = (CEditLeafElement*)pElement->GetNextSibling()) != NULL &&
  7265.                 pElement && pElement->IsText() &&
  7266.                 pElement->GetHREF() == id ){
  7267.             // Cast to access text class (we always test element first)
  7268.             // CEditTextElement *pText = (CEditTextElement*)pElement;
  7269.  
  7270.             pBuf = (char*)XP_REALLOC(pBuf, XP_STRLEN(pBuf) + pElement->Text()->GetSize() + 1);
  7271.             if( !pBuf ){
  7272.                 return 0;
  7273.             }
  7274.             char* pText = pElement->Text()->GetText();
  7275.             if ( pText ) {
  7276.                 strcat( pBuf, pText );
  7277.             }
  7278.         }
  7279.    }
  7280.    return pBuf;
  7281. }
  7282.  
  7283. void CEditBuffer::SetCaret(){
  7284.     InternalSetCaret(TRUE);
  7285. }
  7286.  
  7287. void CEditBuffer::InternalSetCaret(XP_Bool bRevealPosition){
  7288.     LO_TextStruct* pText;
  7289.     int iLayoutOffset;
  7290.     int32 winHeight, winWidth;
  7291.     int32 topX, topY;
  7292.  
  7293.     if( m_bNoRelayout ){
  7294.         return;
  7295.     }
  7296.  
  7297.     // jhp Check m_pCurrent because IsSelected won't give accurate results when
  7298.     // the document is being layed out.
  7299.     if( !IsSelected() && m_pCurrent && !m_inScroll ){
  7300.  
  7301.         if( m_pCurrent->IsA( P_TEXT ) ){
  7302.             if( !m_pCurrent->Text()->GetLOTextAndOffset( m_iCurrentOffset,
  7303.                     m_bCurrentStickyAfter, pText, iLayoutOffset ) ){
  7304.                 return;
  7305.             }
  7306.         }
  7307.  
  7308.         FE_DestroyCaret(m_pContext);
  7309.  
  7310.         if ( bRevealPosition ) {
  7311.             RevealPosition(m_pCurrent, m_iCurrentOffset, m_bCurrentStickyAfter);
  7312.         }
  7313.         FE_GetDocAndWindowPosition( m_pContext, &topX, &topY, &winWidth, &winHeight );
  7314.         m_lastTopY = topY;
  7315.  
  7316.         if( m_pCurrent->IsA( P_TEXT ) ){
  7317.             if( !m_pCurrent->Text()->GetLOTextAndOffset( m_iCurrentOffset, m_bCurrentStickyAfter,
  7318.                                                     pText, iLayoutOffset ) ){
  7319.                 return;
  7320.             }
  7321.  
  7322.             FE_DisplayTextCaret( m_pContext, FE_VIEW, pText, iLayoutOffset );
  7323.         }
  7324.         else
  7325.         {
  7326.             LO_Position effectiveCaretPosition = GetEffectiveCaretLOPosition(m_pCurrent, m_iCurrentOffset, m_bCurrentStickyAfter);
  7327.  
  7328.             switch ( effectiveCaretPosition.element->type )
  7329.             {
  7330.                 case LO_IMAGE:
  7331.                     FE_DisplayImageCaret( m_pContext,
  7332.                                 & effectiveCaretPosition.element->lo_image,
  7333.                                 (ED_CaretObjectPosition)effectiveCaretPosition.position );
  7334.                     break;
  7335.                 default:
  7336.                     FE_DisplayGenericCaret( m_pContext,
  7337.                                 & effectiveCaretPosition.element->lo_any,
  7338.                                 (ED_CaretObjectPosition)effectiveCaretPosition.position );
  7339.                     break;
  7340.             }
  7341.         }
  7342.     }
  7343. }
  7344.  
  7345.  
  7346. LO_Position CEditBuffer::GetEffectiveCaretLOPosition(CEditElement* pElement, intn iOffset, XP_Bool bCurrentStickyAfter)
  7347. {
  7348.     LO_Position position;
  7349.     int iPos;
  7350.  
  7351.     XP_Bool bValid = pElement->Leaf()->GetLOElementAndOffset( iOffset, bCurrentStickyAfter, position.element, iPos);
  7352.     if ( ! bValid ) {
  7353.         position.element = 0;
  7354.         position.position = 0;
  7355.         return position;
  7356.     }
  7357.  
  7358.     XP_ASSERT(position.element);
  7359.     position.position = iPos;
  7360.  
  7361.     // Linefeeds only have a single position.  If you are at the end
  7362.     //  of a linefeed, you are really at the beginning of the next line.
  7363.     if( position.element && position.element->type == LO_LINEFEED && position.position == 1 ){
  7364.         //LO_Element *pNext = LO_NextEditableElement( pLoElement );
  7365.         LO_Element *pNext= position.element->lo_any.next;
  7366.  
  7367. #ifdef MQUOTE
  7368.          // Skip over bullets resulting from MQUOTE.
  7369.         while (pNext && pNext->type == LO_BULLET)
  7370.         {
  7371.             pNext = pNext->lo_any.next;
  7372.         }
  7373. #endif
  7374.  
  7375.         if( pNext ){
  7376.             position.element = pNext;
  7377.             position.position = 0;
  7378.         }
  7379.         else {
  7380.             position.position = 0;
  7381.         }
  7382.     }
  7383.  
  7384.     return position;
  7385. }
  7386.  
  7387. // A utility function for ensuring that a range is visible. This handles one
  7388. // axis; it's called twice, once for each dimension.
  7389. //
  7390. // All variable names are given in terms of the vertical case. The horizontal
  7391. // case is the same.
  7392. //
  7393. // top - the position of the top edge of the visible portion of the document,
  7394. // in the document's coordinate system.
  7395. //
  7396. // length - the length of the window, in the document's coordinate system.
  7397. //
  7398. // targetLow - the first position we desire to be visible.
  7399. //
  7400. // targetHigh - the last position we desire to be visible.
  7401. //
  7402. // In case both positions can't be made visible, targetLow wins.
  7403. //
  7404. // upMargin - if we have to move up, where to
  7405. // position the targetLow relative to top.
  7406. // downMargin - if we have to move down, where to position targetLow relative to top.
  7407. //
  7408. // The margins should be in the range 0..length - 1
  7409. //
  7410. // Returns TRUE if top changed.
  7411.  
  7412. PRIVATE
  7413. XP_Bool MakeVisible(int32& top, int32 length, int32 targetLow, int32 targetHigh, int32 upMargin, int32 downMargin)
  7414. {
  7415.     int32 newTop = top;
  7416.     if ( targetLow < newTop ) {
  7417.         // scroll up
  7418.         newTop = targetLow - upMargin;
  7419.         if ( newTop < 0 ) {
  7420.             newTop = 0;
  7421.         }
  7422.     }
  7423.     else if ( targetHigh >= (newTop + length) ) {
  7424.         // scroll down
  7425.         int32 potentialTop = targetHigh - downMargin;
  7426.         if ( potentialTop < 0 ) {
  7427.             potentialTop = 0;
  7428.         }
  7429.         if ( potentialTop < targetLow && targetLow < potentialTop + length ) {
  7430.             newTop = potentialTop;
  7431.         }
  7432.         else {
  7433.             // Too narrow to show all of cursor. Show just the top part.
  7434.             newTop = targetLow - upMargin;
  7435.             if ( newTop < 0 ) {
  7436.                 newTop = 0;
  7437.             }
  7438.         }
  7439.     }
  7440.  
  7441.     XP_Bool changed = newTop != top;
  7442.     top = newTop;
  7443.     return changed;
  7444. }
  7445.  
  7446. void CEditBuffer::RevealPosition(CEditElement* pElement, int iOffset, XP_Bool bStickyAfter)
  7447. {
  7448.     int32 winHeight, winWidth;
  7449.     int32 topX, topY;
  7450.     int32 targetX, targetYLow, targetYHigh;
  7451.  
  7452.     if ( ! pElement ) {
  7453.        XP_ASSERT(FALSE);
  7454.        return;
  7455.     }
  7456.     FE_GetDocAndWindowPosition( m_pContext, &topX, &topY, &winWidth, &winHeight );
  7457.  
  7458.     CEditInsertPoint ip(pElement, iOffset, bStickyAfter);
  7459.     if (IsPhantomInsertPoint(ip) ){
  7460.         pElement = pElement->GetPreviousSibling();
  7461.         if (pElement == NULL ) {
  7462.             return; // Shouldn't happen because phantom insert points are always after something.
  7463.         }
  7464.         iOffset = pElement->Leaf()->GetLen();
  7465.     }
  7466.  
  7467.  
  7468.     if ( ! GetLOCaretPosition(pElement, iOffset, bStickyAfter, targetX, targetYLow, targetYHigh) )
  7469.         return;
  7470.  
  7471.     // XP_TRACE(("top %d %d target %d %d %d", topX, topY, targetX, targetYLow, targetYHigh));
  7472.  
  7473.     // The visual position policy is that when moving up or down we stick close to
  7474.     // the existing margin, but when moving left or right we move to 2/3rds of the
  7475.     // way across the screen. (If we ever support right-to-left text we'll
  7476.     // have to revisit this.)
  7477.  
  7478.     const int32 kUpMargin = ( winHeight > 30 ) ? 10 : (winHeight / 3);
  7479.     const int32 kDownMargin = winHeight - kUpMargin;
  7480.     const int32 kLeftRightMargin = winWidth * 2 / 3;
  7481.     XP_Bool updateX = MakeVisible(topX, winWidth, targetX, targetX, kLeftRightMargin, kLeftRightMargin);
  7482.     XP_Bool updateY = MakeVisible(topY, winHeight, targetYLow, targetYHigh, kUpMargin, kDownMargin);
  7483.  
  7484.     if ( updateX || updateY ) {
  7485.         // XP_TRACE(("new top %d %d", topX, topY));
  7486.         XP_Bool bHaveCaret = ! IsSelected();
  7487.         if ( bHaveCaret){
  7488.             FE_DestroyCaret(m_pContext);
  7489.         }
  7490.         FE_SetDocPosition(m_pContext, FE_VIEW, topX, topY);
  7491.         if ( bHaveCaret){
  7492.             InternalSetCaret(FALSE);
  7493.         }
  7494.     }
  7495. }
  7496.  
  7497. XP_Bool CEditBuffer::GetLOCaretPosition(CEditElement* pElement, int iOffset, XP_Bool bStickyAfter,
  7498.     int32& targetX, int32& targetYLow, int32& targetYHigh)
  7499. {
  7500.     if ( ! pElement ) {
  7501.        XP_ASSERT(FALSE);
  7502.        return FALSE;
  7503.     }
  7504.     LO_Position position = GetEffectiveCaretLOPosition(pElement, iOffset, bStickyAfter);
  7505.     if ( position.element == NULL ) return FALSE;
  7506.     FE_GetCaretPosition(m_pContext, &position, &targetX, &targetYLow, &targetYHigh);
  7507.     return TRUE;
  7508. }
  7509.  
  7510.  
  7511. /* Begin of document check preference and if move curosr is enabled, 
  7512. we will move the cursor if not, we leave the cursor alone.
  7513. REQUIRES (Boolean to select or not), Valid editbuffer
  7514. RETURNS Nothing
  7515. NOTE: uses
  7516.     NavigateChunk( bSelect, LO_NA_DOCUMENT, FALSE ); 
  7517.     if we move the cursor.
  7518. and
  7519.     FE_ScrollDocTo (MWContext *context, int iLocation, int32 x,int32 y);
  7520.     if we do not
  7521. */
  7522. void CEditBuffer::NavigateDocument(XP_Bool bSelect, XP_Bool bForward )
  7523. {
  7524.     if (!m_bEdtBufPrefInitialized)
  7525.         CEditBuffer::InitializePrefs();//static function to initialize the prefs
  7526.     if (m_bMoveCursor==TRUE)
  7527.         NavigateChunk( bSelect, LO_NA_DOCUMENT, bForward ); 
  7528.     else
  7529.     {
  7530.         int32 winHeight, winWidth;
  7531.         int32 topX, topY;
  7532.  
  7533.         if( m_inScroll ){
  7534.             return;
  7535.         }
  7536.         m_inScroll = TRUE;
  7537.  
  7538.     
  7539.         FE_GetDocAndWindowPosition( m_pContext, &topX, &topY, &winWidth, &winHeight );
  7540.         CEditLeafElement *t_leaf;
  7541.         ElementOffset t_offset; //offset is used to reveal the position of the caret and to call GetLOElementAndOffset
  7542.         XP_Bool t_stickyafter; //I honestly dont know what this is yet, but we use it for the same reasons as t_offset
  7543.         long t_maxwidth,t_maxheight; //used for the TOTAL width and height of the document.
  7544.         if (m_pCurrent)
  7545.         {
  7546.             t_leaf= m_pCurrent;
  7547.             t_offset=m_iCurrentOffset;
  7548.             t_stickyafter=m_bCurrentStickyAfter;
  7549.         }
  7550.         else
  7551.             GetInsertPoint( &t_leaf, &t_offset, &t_stickyafter );//gets the insert point.
  7552.         CL_Layer *layer = LO_GetLayerFromId(m_pContext, LO_DOCUMENT_LAYER_ID); 
  7553.         t_maxheight = LO_GetLayerScrollHeight(layer); 
  7554.         t_maxwidth = LO_GetLayerScrollWidth(layer);         
  7555.         if (bForward)
  7556.             FE_ScrollDocTo (m_pContext, t_offset, topX, t_maxheight);
  7557.         else
  7558.             FE_ScrollDocTo (m_pContext, t_offset, topX, 0);
  7559.         m_inScroll = FALSE;
  7560.     }
  7561. }
  7562.  
  7563.  
  7564. /* PageUpDown
  7565. this function will scroll the CURSOR to the proper location depending on bForward definition
  7566. we have 2 parts.  move the cursor and make the window catch up. IFF the preference tells us
  7567. */
  7568. void CEditBuffer::PageUpDown(XP_Bool bSelect, XP_Bool bForward )
  7569. {
  7570.     if (!m_bEdtBufPrefInitialized)
  7571.         CEditBuffer::InitializePrefs();//static function to initialize the prefs
  7572.  
  7573.     LO_Element* pElement;
  7574.     int bCenterWindow; //0= no need, 1= bottom, -1 = top
  7575.     int iLayoutOffset;
  7576.     int32 winHeight, winWidth;
  7577.     int32 topX, topY;
  7578.     int32 iDesiredY;
  7579.     CEditLeafElement *t_leaf;
  7580.     ElementOffset t_offset; //offset is used to reveal the position of the caret and to call GetLOElementAndOffset
  7581.     XP_Bool t_stickyafter; //I honestly dont know what this is yet, but we use it for the same reasons as t_offset
  7582.  
  7583.     if( m_inScroll ){
  7584.         return;
  7585.     }
  7586.     m_inScroll = TRUE;
  7587.  
  7588.     
  7589.     FE_GetDocAndWindowPosition( m_pContext, &topX, &topY, &winWidth, &winHeight );
  7590.  
  7591.     if (m_pCurrent)
  7592.     {
  7593.         t_leaf= m_pCurrent;
  7594.         t_offset=m_iCurrentOffset;
  7595.         t_stickyafter=m_bCurrentStickyAfter;
  7596.     }
  7597.     else
  7598.         GetInsertPoint( &t_leaf, &t_offset, &t_stickyafter );//gets the insert point.
  7599.  
  7600.     t_leaf->GetLOElementAndOffset( t_offset, t_stickyafter, pElement, iLayoutOffset );
  7601.  
  7602.     if (m_bMoveCursor==TRUE)
  7603.     {
  7604.         ClearPhantomInsertPoint();
  7605.         ClearMove();    /* Arrow keys clear the up/down target position */
  7606.         BeginSelection();
  7607.         //is the cursor on the screen?? if not,  center scrollview on lead leaf element/cursor
  7608.         //if it is, just move the window +- one screen height
  7609.         if ( pElement->lo_any.y < topY ) 
  7610.           bCenterWindow = -1;//move window such that the TOP of the window shows the carret
  7611.         else if ((pElement->lo_any.y+pElement->lo_any.height)>(topY+winHeight))
  7612.         {
  7613.             bCenterWindow = 1;//move window such that the BOTTOM of the window shows the carret
  7614.         }
  7615.         else
  7616.           bCenterWindow = 0;
  7617.         // m_iDesiredX is where we would move if we could.  This keeps the
  7618.         //  cursor moving, basically, straight up and down, even if there is
  7619.         //  no text or gaps
  7620.         if( m_iDesiredX == -1 ){
  7621.           m_iDesiredX = pElement->lo_any.x +
  7622.                   (t_leaf->IsA(P_TEXT)
  7623.                       ? LO_TextElementWidth( m_pContext,
  7624.                               (LO_TextStruct*) pElement, iLayoutOffset)
  7625.                       : 0 );
  7626.         }
  7627.  
  7628.         if (bForward)
  7629.             iDesiredY = pElement->lo_any.y + winHeight;
  7630.         else
  7631.             iDesiredY = pElement->lo_any.y - winHeight;
  7632.         if (iDesiredY<0)
  7633.         {
  7634.             iDesiredY=0;
  7635.             m_iDesiredX=0;//x and y should go to beginnning
  7636.         }
  7637.  
  7638.         // what is location?? cant find it used. i will use t_offset???
  7639.         // FE_SetDocPosition(MWContext *context, int iLocation, int32 iX, int32 iY);
  7640.         if (bCenterWindow == -1)
  7641.             FE_SetDocPosition(m_pContext, t_offset, topX, iDesiredY);//keep x the same
  7642.         else if (bCenterWindow == 1)
  7643.         {
  7644.             int t_tempdesiredy=iDesiredY+pElement->lo_any.height-winHeight;
  7645.             if (t_tempdesiredy<0)
  7646.                 t_tempdesiredy=0;
  7647.             FE_SetDocPosition(m_pContext, t_offset, topX, t_tempdesiredy);
  7648.         }
  7649.         else
  7650.         {
  7651.           if (bForward)
  7652.               FE_SetDocPosition(m_pContext, t_offset, topX, topY+winHeight);
  7653.           else
  7654.           {
  7655.               int t_tempdesiredy=topY-winHeight;
  7656.               if (t_tempdesiredy<0)
  7657.                   t_tempdesiredy=0;
  7658.               FE_SetDocPosition(m_pContext, t_offset, topX, t_tempdesiredy);
  7659.           }
  7660.         }
  7661.  
  7662.         if (bSelect)
  7663.         {
  7664.             ExtendSelection( m_iDesiredX, iDesiredY );
  7665.         }
  7666.         else
  7667.         {
  7668.     #ifdef LAYERS
  7669.             LO_PositionCaret( m_pContext, m_iDesiredX, iDesiredY, NULL );
  7670.     #else
  7671.             LO_PositionCaret( m_pContext, m_iDesiredX, iDesiredY );
  7672.     #endif
  7673.         }
  7674.         m_lastTopY = topY;
  7675.         m_inScroll = FALSE;//do not forget this!  this allows the screen to refresh properly.  used only to prevent recursion
  7676.         EndSelection();
  7677.         DoneTyping();
  7678.     }
  7679.     else
  7680.     {
  7681.         if (bForward)
  7682.             iDesiredY = topY + winHeight;
  7683.         else
  7684.             iDesiredY = topY - winHeight;
  7685.         if (iDesiredY<0)
  7686.         {
  7687.             iDesiredY=0;
  7688.         }
  7689.         m_inScroll = FALSE;//do not forget this!  this allows the screen to refresh properly.  used only to prevent recursion
  7690.         FE_SetDocPosition(m_pContext, t_offset, topX, iDesiredY);//keep x the same
  7691.     }
  7692. }
  7693.  
  7694.  
  7695. void CEditBuffer::WindowScrolled(){
  7696.  
  7697.     if( m_inScroll ){
  7698.         return;
  7699.     }
  7700.     m_inScroll = TRUE;
  7701.     if( !IsSelected() ){
  7702.         InternalSetCaret(FALSE);
  7703.     }
  7704.  
  7705.     m_inScroll = FALSE;
  7706.  
  7707. #if 0
  7708. //TODO: Ask mjudge if we need to keep this for anything
  7709.  
  7710.     // This code keeps the cursor on the screen, which is pretty weird behavior for
  7711.     // a text editor. That's not how any WYSIWYG editor works -- I'm guessing it's
  7712.     // some crufty key-based editor feature. -- jhp
  7713.  
  7714.     LO_Element* pElement;
  7715.     int iLayoutOffset;
  7716.     int32 winHeight, winWidth;
  7717.     int32 topX, topY;
  7718.     int32 iDesiredY;
  7719.  
  7720.     if( m_inScroll ){
  7721.         return;
  7722.     }
  7723.     m_inScroll = TRUE;
  7724.  
  7725.  
  7726.     FE_GetDocAndWindowPosition( m_pContext, &topX, &topY, &winWidth, &winHeight );
  7727.  
  7728.  
  7729.     // if there is a current selection, we have nothing to do.
  7730.     if( !IsSelected() ){
  7731.  
  7732.         m_pCurrent->GetLOElementAndOffset( m_iCurrentOffset, m_bCurrentStickyAfter,
  7733.                                                     pElement, iLayoutOffset );
  7734.  
  7735.         // m_iDesiredX is where we would move if we could.  This keeps the
  7736.         //  cursor moving, basically, straight up and down, even if there is
  7737.         //  no text or gaps
  7738.         if( m_iDesiredX == -1 ){
  7739.             m_iDesiredX = pElement->lo_any.x +
  7740.                     (m_pCurrent->IsA(P_TEXT)
  7741.                         ? LO_TextElementWidth( m_pContext,
  7742.                                 (LO_TextStruct*) pElement, iLayoutOffset)
  7743.                         : 0 );
  7744.         }
  7745.  
  7746.  
  7747.         if( pElement->lo_any.y < topY ){
  7748.             iDesiredY = (pElement->lo_any.y - m_lastTopY) + topY;
  7749.             // caret is above the current window
  7750. #ifdef LAYERS
  7751.             LO_PositionCaret( m_pContext, m_iDesiredX, iDesiredY, NULL );
  7752. #else
  7753.             LO_PositionCaret( m_pContext, m_iDesiredX, iDesiredY );
  7754. #endif
  7755.         }
  7756.         else if( pElement->lo_any.y+pElement->lo_any.height > topY+winHeight ){
  7757.             // caret is below the current window
  7758.             iDesiredY = (pElement->lo_any.y - m_lastTopY) + topY;
  7759. #ifdef LAYERS
  7760.             LO_PositionCaret( m_pContext, m_iDesiredX, iDesiredY, NULL);
  7761. #else
  7762.             LO_PositionCaret( m_pContext, m_iDesiredX, iDesiredY);
  7763. #endif
  7764.         }
  7765.         else {
  7766.             SetCaret();
  7767.         }
  7768.     }
  7769.     m_lastTopY = topY;
  7770.     m_inScroll = FALSE;
  7771.    InternalSetCaret(FALSE);
  7772. #endif
  7773. }
  7774.  
  7775. void CEditBuffer::DebugPrintTree(CEditElement* pElement){
  7776.     const size_t kIndentBufSize = 256;
  7777.     char indent[kIndentBufSize];
  7778.     char *pData;
  7779.     char *pData1;
  7780.     PA_Tag *pTag;
  7781.  
  7782.     int indentSize = printState.m_iLevel*2;
  7783.     if ( indentSize >= kIndentBufSize ) {
  7784.         indentSize = kIndentBufSize - 1;
  7785.     }
  7786.  
  7787.     CEditElement *pNext = pElement->GetChild();
  7788.     int i;
  7789.     for(i = 0; i < indentSize; i++ ){
  7790.         indent[i] = ' ';
  7791.     }
  7792.     indent[i] = '\0';
  7793.  
  7794.     if( pElement->IsA(P_TEXT) ) {
  7795.         pData = pElement->Text()->GetText();
  7796.         pData1 = pElement->Text()->DebugFormat();
  7797.         pTag = 0;
  7798.     }
  7799.     else if( pElement->IsContainer() ){
  7800.         ED_Alignment alignment = pElement->Container()->GetAlignment();
  7801.         if ( alignment < LO_ALIGN_CENTER || alignment > ED_ALIGN_ABSTOP ) {
  7802.             pData = "Bad alignment.";
  7803.         }
  7804.         else {
  7805.             pData = lo_alignStrings[alignment];
  7806.         }
  7807.         pData1 = "";
  7808.         pTag = 0;
  7809.     }
  7810.     else {
  7811.         pData1 = "";
  7812.         pTag = pElement->TagOpen(0);
  7813.         if( pTag ){
  7814.             // no multiple tag fetches.
  7815.             //XP_ASSERT( pTag->next == 0 );
  7816.             PA_LOCK( pData, char*, pTag->data );
  7817.         }
  7818.         else {
  7819.             pData = 0;
  7820.         }
  7821.     }
  7822.  
  7823.     printState.m_pOut->Printf("\n");
  7824.     ElementIndex baseIndex = pElement->GetElementIndex();
  7825.     ElementIndex elementCount = pElement->GetPersistentCount();
  7826.     printState.m_pOut->Printf("0x%08x %6ld-%6ld", pElement, (long)baseIndex,
  7827.                 (long)baseIndex + elementCount);
  7828.     printState.m_pOut->Printf("%s %s: %s%c",
  7829.             indent,
  7830.             EDT_TagString(pElement->GetType()),
  7831.             pData1,
  7832.             (pData ? '"': ' ') );
  7833.     if( pData ){
  7834.         printState.m_pOut->Write( pData, XP_STRLEN(pData));
  7835.         printState.m_pOut->Write( "\"", 1 );
  7836.     }
  7837.  
  7838.     if( pTag ){
  7839.         PA_UNLOCK( pTag->data );
  7840.         PA_FreeTag( pTag );
  7841.     }
  7842.  
  7843.     while( pNext ){
  7844.         printState.m_iLevel++;
  7845.         DebugPrintTree( pNext );
  7846.         printState.m_iLevel--;
  7847.         pNext = pNext->GetNextSibling();
  7848.     }
  7849. }
  7850.  
  7851. void CEditBuffer::CheckAndPrintComment(CEditLeafElement* pElement, CEditSelection& selection, XP_Bool bEnd){
  7852.     CEditInsertPoint ip(pElement, bEnd ? pElement->GetLen() : 0);
  7853.     CheckAndPrintComment2(ip, selection, FALSE);
  7854.     CheckAndPrintComment2(ip, selection, TRUE);
  7855. }
  7856.  
  7857. void CEditBuffer::CheckAndPrintComment2(const CEditInsertPoint& where, CEditSelection& selection, XP_Bool bEnd){
  7858.     CEditInsertPoint* pSelEdge = selection.GetEdge(bEnd);
  7859.     if (where.m_pElement == pSelEdge->m_pElement && where.m_iPos == pSelEdge->m_iPos){
  7860.         printState.PrintSelectionComment(bEnd, pSelEdge->m_bStickyAfter);
  7861.     }
  7862. }
  7863.  
  7864. void CEditBuffer::PrintTree( CEditElement* pElement ){
  7865.     CEditElement* pNext = pElement->GetChild();
  7866.  
  7867.     // Note the hack here: text elements are responsible for
  7868.     // printing their own selection comments because the selection
  7869.     // edge may fall inside the text element. We could make all
  7870.     // elements handle their own selection, but that would be tedious.
  7871.  
  7872.     XP_Bool bCheckForSelectionAsComment = printState.m_bEncodeSelectionAsComment
  7873.         && pElement->IsLeaf() && ! pElement->IsText();
  7874.     if ( bCheckForSelectionAsComment){
  7875.         CheckAndPrintComment(pElement->Leaf(), printState.m_selection, FALSE);
  7876.     }
  7877.     pElement->PrintOpen( &printState );
  7878.  
  7879.     while( pNext ){
  7880.         PrintTree( pNext );
  7881.         pNext = pNext->GetNextSibling();
  7882.     }
  7883.     pElement->PrintEnd( &printState );
  7884.     if ( bCheckForSelectionAsComment){
  7885.         CheckAndPrintComment(pElement->Leaf(), printState.m_selection, TRUE);
  7886.     }
  7887. }
  7888.  
  7889.  
  7890. void CEditBuffer::StreamToPositionalText( CEditElement* pElement, IStreamOut* pOut ){
  7891.     CEditElement* pNext = pElement->GetChild();
  7892.  
  7893.     pElement->StreamToPositionalText( pOut, FALSE );
  7894.  
  7895.     while( pNext ){
  7896.         StreamToPositionalText( pNext, pOut );
  7897.         pNext = pNext->GetNextSibling();
  7898.     }
  7899.     pElement->StreamToPositionalText( pOut, TRUE );
  7900. }
  7901.  
  7902.  
  7903. void CEditBuffer::AppendTitle( char *pTitle ){
  7904.     if( m_pTitle == 0 ){
  7905.         m_pTitle = XP_STRDUP( pTitle );
  7906.     }
  7907.     else {
  7908.         int32 titleLen = XP_STRLEN(m_pTitle)+XP_STRLEN(pTitle)+1;
  7909.         m_pTitle = (char*)XP_REALLOC( m_pTitle,
  7910.                  titleLen);
  7911.         strcat( m_pTitle, pTitle );
  7912.     }
  7913. }
  7914.  
  7915. void CEditBuffer::PrintMetaData( CPrintState *pPrintState ){
  7916.     // According to RFC 2070, print the charset http-equiv as soon as possible.
  7917.     int contentTypeID = FindMetaData(TRUE, CONTENT_TYPE);
  7918.     if ( contentTypeID >= 0 ) {
  7919.         PrintMetaData(pPrintState, contentTypeID);
  7920.     }
  7921.     for( int i = 0; i < m_metaData.Size(); i++ ){
  7922.         if ( i != contentTypeID ) {
  7923.             PrintMetaData(pPrintState, i);
  7924.         }
  7925.     }
  7926. }
  7927.  
  7928. void CEditBuffer::PrintMetaData( CPrintState *pPrintState, int index ){
  7929.     if ( index < 0 || index >= m_metaData.Size() ) {
  7930.         return;
  7931.     }
  7932.     EDT_MetaData *pData = m_metaData[index];
  7933.     if( pData != 0  ){
  7934.         // LTNOTE:
  7935.         // You can't merge these into a single printf.  edt_MakeParamString
  7936.         //  uses a single static buffer.
  7937.         //
  7938.         pPrintState->m_pOut->Printf( "   <META %s=%s ",
  7939.                 (pData->bHttpEquiv ? "HTTP-EQUIV" : "NAME"),
  7940.                 edt_MakeParamString( pData->pName ) );
  7941.  
  7942.         pPrintState->m_pOut->Printf( "CONTENT=%s>\n",
  7943.                 edt_MakeParamString( pData->pContent ) );
  7944.     }
  7945. }
  7946.  
  7947. void CEditBuffer::PrintDocumentHead( CPrintState *pPrintState ){
  7948.     XP_Bool bPageComposer = ! IsComposeWindow();
  7949.  
  7950.     XP_Bool bHaveBaseTarget = m_pBaseTarget && *m_pBaseTarget;
  7951.     XP_Bool bHaveFontDefURL = m_pFontDefURL && *m_pFontDefURL;
  7952.     CParseState* pParseState = GetParseState();
  7953.     XP_Bool bHaveHeadTags = pParseState->m_pJavaScript != NULL;
  7954.     XP_Bool bNeedHead = bPageComposer
  7955.         || bHaveBaseTarget || bHaveFontDefURL
  7956.         || bHaveHeadTags;
  7957.     
  7958.     // Unfortunately, we don't conform to any standard DOCTYPE yet.
  7959.     // "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n"
  7960.  
  7961.     pPrintState->m_pOut->Printf("<HTML>\n");
  7962.     
  7963.     if ( bNeedHead ) {
  7964.         pPrintState->m_pOut->Printf("<HEAD>\n");
  7965.  
  7966.         if ( bPageComposer ) {
  7967.             // Print the MetaData first because it includes the charset, which can affect the way the
  7968.             // title is parsed. See RFC 2070.
  7969.             PrintMetaData( pPrintState );
  7970.  
  7971.             // Only print the title if it exists and is not empty.
  7972.  
  7973.             if ( m_pTitle && *m_pTitle ) {
  7974.                 pPrintState->m_pOut->Printf("   <TITLE>");
  7975.                 edt_PrintWithEscapes( pPrintState, m_pTitle, FALSE );
  7976.                 pPrintState->m_pOut->Printf(
  7977.                               "</TITLE>\n");
  7978.             }
  7979.         }
  7980.  
  7981.         // Print BASE TARGET if nescessary
  7982.         if ( bHaveBaseTarget ) {
  7983.             pPrintState->m_pOut->Printf("   <BASE TARGET=%s>\n",
  7984.                 edt_MakeParamString( m_pBaseTarget ));
  7985.         }
  7986.  
  7987.         // Print the font ref if nescessary
  7988.         if ( bHaveFontDefURL ){
  7989.             pPrintState->m_pOut->Printf("   <LINK REL=FONTDEF SRC=%s",
  7990.                 edt_MakeParamString( m_pFontDefURL ));
  7991.             if ( m_bFontDefNoSave ) {
  7992.                 pPrintState->m_pOut->Printf(" NOSAVE");
  7993.             }
  7994.             pPrintState->m_pOut->Printf(">\n");
  7995.         }
  7996.         if( bHaveHeadTags ){
  7997.             Write(pParseState->m_pJavaScript, pPrintState->m_pOut);
  7998.         }
  7999.         pPrintState->m_pOut->Printf("</HEAD>\n");
  8000.     }
  8001.  
  8002.     if ( IsBodyTagRequired() ) {
  8003.         // print the contents of the body tag.
  8004.         pPrintState->m_pOut->Printf("<BODY");
  8005.         if( m_colorText.IsDefined() ) {
  8006.             pPrintState->m_pOut->Printf(" TEXT=\"#%06lX\"", m_colorText.GetAsLong() );
  8007.         }
  8008.         if( m_colorBackground.IsDefined() ) {
  8009.             pPrintState->m_pOut->Printf(" BGCOLOR=\"#%06lX\"", m_colorBackground.GetAsLong() );
  8010.         }
  8011.         if( m_colorLink.IsDefined() ) {
  8012.             pPrintState->m_pOut->Printf(" LINK=\"#%06lX\"", m_colorLink.GetAsLong() );
  8013.         }
  8014.         if( m_colorFollowedLink.IsDefined() ) {
  8015.             pPrintState->m_pOut->Printf(" VLINK=\"#%06lX\"", m_colorFollowedLink.GetAsLong() );
  8016.         }
  8017.         if( m_colorActiveLink.IsDefined() ) {
  8018.             pPrintState->m_pOut->Printf(" ALINK=\"#%06lX\"", m_colorActiveLink.GetAsLong() );
  8019.         }
  8020.         if( m_pBackgroundImage && *m_pBackgroundImage) {
  8021.             pPrintState->m_pOut->Printf(" BACKGROUND=%s", edt_MakeParamString(m_pBackgroundImage) );
  8022.         }
  8023.         if ( m_bBackgroundNoSave ) {
  8024.             pPrintState->m_pOut->Printf(" NOSAVE");
  8025.         }
  8026.  
  8027.         if( m_pBodyExtra && *m_pBodyExtra ) {
  8028.             pPrintState->m_pOut->Printf(" %s", m_pBodyExtra );
  8029.         }
  8030.  
  8031.         pPrintState->m_pOut->Printf(">\n");
  8032.     }
  8033. }
  8034.  
  8035. XP_Bool CEditBuffer::IsBodyTagRequired(){
  8036.     return (! IsComposeWindow() )
  8037.         || m_colorText.IsDefined() 
  8038.         || m_colorBackground.IsDefined()
  8039.         || m_colorLink.IsDefined()
  8040.         || m_colorFollowedLink.IsDefined() 
  8041.         || m_colorActiveLink.IsDefined()
  8042.         || ( m_pBackgroundImage && *m_pBackgroundImage ) 
  8043.         || ( m_pBodyExtra && *m_pBodyExtra );
  8044. }
  8045.  
  8046. void CEditBuffer::PrintDocumentEnd( CPrintState *pPrintState ){
  8047.     if ( IsBodyTagRequired() ){
  8048.         pPrintState->m_pOut->Printf( "\n"
  8049.                 "</BODY>\n");
  8050.     }
  8051.     if( GetParseState()->m_pPostBody ){
  8052.         Write(GetParseState()->m_pPostBody, pPrintState->m_pOut);
  8053.     }
  8054.     pPrintState->m_pOut->Printf("</HTML>\n");
  8055. }
  8056.  
  8057. void CEditBuffer::Write(CStreamOutMemory *pSource, IStreamOut* pDest){
  8058.     // Copies entire contents of pSource to pDest.
  8059.     int32 iLen = pSource->GetLen();
  8060.     XP_HUGE_CHAR_PTR pScript = pSource->GetText();
  8061.     while( iLen ){
  8062.         int16 iToWrite = (int16) min( iLen, 0x4000 );
  8063.         pDest->Write( pScript, iToWrite );
  8064.         iLen -= iToWrite;
  8065.         pScript += iToWrite;
  8066.     }
  8067. }
  8068.  
  8069.  
  8070. class CEditSaveData {
  8071. public:
  8072.   CEditSaveData() {ppIncludedFiles = NULL;pSourceURL = NULL;pSaveToTempData = NULL;}
  8073.   ~CEditSaveData(); 
  8074.  
  8075. // Don't save CEditBuffer because it might have changed from undo/redo logic
  8076.   MWContext *pContext; 
  8077.  
  8078.   ED_SaveFinishedOption finishedOpt;
  8079.   char* pSourceURL;
  8080.   ITapeFileSystem *tapeFS;
  8081.   XP_Bool bSaveAs;
  8082.   XP_Bool bKeepImagesWithDoc;
  8083.   XP_Bool bAutoAdjustLinks;
  8084.   XP_Bool bAutoSave;
  8085.   char **ppIncludedFiles;
  8086.   CEditSaveToTempData *pSaveToTempData;
  8087. };
  8088.  
  8089. CEditSaveData::~CEditSaveData() {
  8090.   XP_FREEIF(pSourceURL);
  8091.   
  8092.   // In the success case, ppIncludedFiles and pSaveToTempData will
  8093.   // have been set to NULL in CEditBuffer::SaveFileReal(), so they don't
  8094.   // get freed here.
  8095.   CEditSaveObject::FreeList(ppIncludedFiles);
  8096.   if (pSaveToTempData) {
  8097.     // failure.
  8098.     (*pSaveToTempData->doneFn)(NULL,pSaveToTempData->hook);
  8099.     delete pSaveToTempData;
  8100.   }
  8101. }
  8102.  
  8103. // Callback from plugins to start saving process.
  8104. PRIVATE void edt_SaveFilePluginCB(EDT_ImageEncoderStatus status, void* pArg) {
  8105.   CEditSaveData *pData = (CEditSaveData *)pArg;
  8106.   XP_ASSERT(pData);
  8107.   XP_Bool bFileSaved = FALSE;
  8108.  
  8109.   if (status != ED_IMAGE_ENCODER_USER_CANCELED) {
  8110.     // OK or exception.  Note that we still do the save if an exception.
  8111.  
  8112.     CEditBuffer *pBuffer = LO_GetEDBuffer(pData->pContext);
  8113.     if (pBuffer) {
  8114.       pBuffer->SaveFileReal(pData);
  8115.       bFileSaved = TRUE;
  8116.       // ppIncludedFiles, and tapeFS will be deleted by CEditBuffer::SaveFileReal().
  8117.     }
  8118.     else {
  8119.       XP_ASSERT(0);
  8120.     }
  8121.   }
  8122.   else {
  8123.     // user canceled.
  8124.     pData->tapeFS->Complete( FALSE, NULL, NULL ); // Tell tapeFS to kill itself.
  8125.   }
  8126.   if( !bFileSaved )
  8127.   {
  8128.       //cmanske EXPERIMENTAL - This was set at start of file save
  8129.       // and must be cleared now if we really didn't do the save
  8130.       pData->pContext->edit_saving_url = FALSE;
  8131.   }
  8132.   
  8133.   delete pData;
  8134. }
  8135.  
  8136. // Setup to save file
  8137. // !!!!! All of the saving functions ultimately go through this one !!!!!
  8138. ED_FileError CEditBuffer::SaveFile( ED_SaveFinishedOption finishedOpt,
  8139.                                     char* pSourceURL,
  8140.                                     ITapeFileSystem *tapeFS,
  8141.                                     XP_Bool bSaveAs,
  8142.                                     XP_Bool bKeepImagesWithDoc,
  8143.                                     XP_Bool bAutoAdjustLinks,
  8144.                                     XP_Bool bAutoSave,
  8145.                                     char **ppIncludedFiles,
  8146.                                     CEditSaveToTempData *pSaveToTempData){
  8147.   // Create argument for asynchoronous call to plugin code.
  8148.   CEditSaveData *pData = new CEditSaveData;
  8149.   if (!pData) {
  8150.     XP_ASSERT(0);
  8151.     tapeFS->Complete( FALSE, NULL, NULL ); // Tell tapeFS to kill itself.
  8152.     CEditSaveObject::FreeList(ppIncludedFiles);
  8153.     if (pSaveToTempData) {
  8154.       // failure.
  8155.       (*pSaveToTempData->doneFn)(NULL,pSaveToTempData->hook);
  8156.       delete pSaveToTempData;
  8157.     }
  8158.     return ED_ERROR_BLOCKED;
  8159.   }
  8160.   // fill pData.
  8161.   pData->pContext = m_pContext;
  8162.   pData->finishedOpt = finishedOpt;
  8163.   pData->pSourceURL = XP_STRDUP(pSourceURL);
  8164.   pData->tapeFS = tapeFS;
  8165.   pData->bSaveAs = bSaveAs;
  8166.   pData->bKeepImagesWithDoc = bKeepImagesWithDoc;
  8167.   pData->bAutoAdjustLinks = bAutoAdjustLinks;
  8168.   pData->bAutoSave = bAutoSave;
  8169.   pData->ppIncludedFiles = ppIncludedFiles;
  8170.   pData->pSaveToTempData = pSaveToTempData;
  8171.  
  8172.   //cmanske - This is also set in constructor of CFileSave,
  8173.   //  but we must set it earlier because of asynchronous plugin --
  8174.   //  the front ends should stay in a wait loop while this is TRUE 
  8175.   //  so they do not continue until saving is completed
  8176.   m_pContext->edit_saving_url = TRUE;
  8177.  
  8178.   if (pSaveToTempData) {
  8179.     // Don't send event when saving temp file.
  8180.     // Call callback directly.
  8181.     edt_SaveFilePluginCB(ED_IMAGE_ENCODER_OK,(void *)pData);
  8182.   }
  8183.   else {
  8184.     // Build event name to pass to plugin code.
  8185.     char *pDestURL = tapeFS->GetDestAbsURL();
  8186.     switch (tapeFS->GetType()) {
  8187.       case ITapeFileSystem::File:
  8188.         if (bAutoSave) {
  8189.           EDT_PerformEvent(m_pContext,"autosave",pDestURL,TRUE,TRUE,
  8190.                            edt_SaveFilePluginCB,(void *)pData);    
  8191.         }
  8192.         else {
  8193.           EDT_PerformEvent(m_pContext,"save",pDestURL,TRUE,TRUE,
  8194.                            edt_SaveFilePluginCB,(void *)pData);    
  8195.         }
  8196.         break;
  8197.       case ITapeFileSystem::Publish:
  8198.         EDT_PerformEvent(m_pContext,"publish",pDestURL,TRUE,TRUE,
  8199.                          edt_SaveFilePluginCB,(void *)pData);     
  8200.         break;
  8201.       case ITapeFileSystem::MailSend:
  8202.         EDT_PerformEvent(m_pContext,"send",NULL,TRUE,TRUE,
  8203.                          edt_SaveFilePluginCB,(void *)pData);     
  8204.         // Don't give a URL.
  8205.         break;
  8206.       default:
  8207.         XP_ASSERT(0);
  8208.     }
  8209.     XP_FREEIF(pDestURL);
  8210.   }
  8211.  
  8212.   // I'm sick of loosing prefs, so save them as part of 
  8213.   //  file saving, since that will take some time anyway
  8214.   PREF_SavePrefFile();
  8215.   
  8216.   return ED_ERROR_NONE;
  8217. }
  8218.  
  8219.  
  8220. void CEditBuffer::SaveFileReal(CEditSaveData *pData) {
  8221.     if (pData->pSaveToTempData) {
  8222.       // Don't want to edit temp file after call to EDT_SaveToTempFile().
  8223.       pData->finishedOpt = ED_FINISHED_REVERT_BUFFER;
  8224.     }
  8225.     else {
  8226.       // We are going to ignore the finishedOpt flag and do the right thing
  8227.       // in the back end for saving and publishing.  Not for mailing.
  8228.       if (pData->tapeFS->GetType() == ITapeFileSystem::File ||
  8229.           pData->tapeFS->GetType() == ITapeFileSystem::Publish) {
  8230.         if (EDT_IS_NEW_DOCUMENT(m_pContext)) {
  8231.           // Source is a new document.
  8232.           pData->finishedOpt = ED_FINISHED_GOTO_NEW;
  8233.         }
  8234.         else {
  8235.           if (NET_URL_Type(pData->pSourceURL) != FILE_TYPE_URL) {
  8236.             // Source is a remote doc.
  8237.             pData->finishedOpt = ED_FINISHED_GOTO_NEW;
  8238.           }
  8239.           else {
  8240.             if (pData->tapeFS->GetType() == ITapeFileSystem::File) {
  8241.               // Source is a local file, dest is local file.
  8242.               pData->finishedOpt = ED_FINISHED_GOTO_NEW;
  8243.             }
  8244.             else {  
  8245.               XP_ASSERT(pData->tapeFS->GetType() == ITapeFileSystem::Publish);
  8246.               // Source is a local file, dest is remote file.
  8247.               pData->finishedOpt = ED_FINISHED_REVERT_BUFFER;
  8248.             }
  8249.           }
  8250.         }
  8251.       }
  8252.     }
  8253.  
  8254.     DoneTyping();
  8255.     CEditSaveObject *pSaveObject;
  8256.     if( m_pSaveObject != 0 ){
  8257.         // This could happen if user is in the middle of saving a file
  8258.         //  (such as at an overwrite or error dialog when inserting image)
  8259.         //  and then AutoSave triggers saving the file. So don't ASSERT
  8260.         // XP_ASSERT(0);           // shouldn't be returning a value...
  8261.         
  8262.         pData->tapeFS->Complete( FALSE, NULL, NULL ); // Tell tapeFS to kill itself.
  8263.         return;
  8264.     }
  8265.     // Set write time to 0 so we don't trigger reload before file is saved
  8266.     // (doesn't completely solve this problem)
  8267.     if( pData->bSaveAs) {
  8268.         m_iFileWriteTime = 0;
  8269.     }
  8270.  
  8271.     // Stamp the document character set into the document.
  8272.     SetEncoding(GetDocCharSetID());
  8273.  
  8274.     // tapeFS freed by CEditSaveObject (actually CFileSaveObject::~CFileSaveObject).
  8275.     m_pSaveObject = 
  8276.          pSaveObject = new CEditSaveObject( this, pData->finishedOpt, pData->pSourceURL, pData->tapeFS, 
  8277.                    pData->bSaveAs, pData->bKeepImagesWithDoc, pData->bAutoAdjustLinks, pData->bAutoSave,
  8278.                    pData->pSaveToTempData);
  8279.     pData->pSaveToTempData = NULL; // memory is now the responsibility of CEditSaveObject.
  8280.  
  8281.     pSaveObject->AddAllFiles(pData->ppIncludedFiles);
  8282.     pData->ppIncludedFiles = NULL;  // freed in AddAllFiles().
  8283.         
  8284.     // This may save 1 or more files and delete itself before
  8285.     //  returning here. Error status (m_status) is set to pSaveObject->m_status
  8286.     //  via CEditBuffer::FileFetchComplete(m_status)
  8287.     m_status = ED_ERROR_NONE;
  8288.     pSaveObject->SaveFiles();
  8289. }
  8290.  
  8291. int16 CEditBuffer::GetRAMCharSetID() {
  8292.     return m_originalWinCSID & (~CS_AUTO);
  8293. }
  8294.  
  8295. int16 CEditBuffer::GetDocCharSetID() {
  8296.     // Copied from layform.c
  8297.     INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(m_pContext);
  8298.     int16 doc_csid = INTL_GetCSIDocCSID(csi);
  8299.     if ( m_bForceDocCSID ) {
  8300.         // Used when changing a document's character set.
  8301.         doc_csid = m_forceDocCSID;
  8302.     }
  8303.     else if (doc_csid == CS_DEFAULT)
  8304.         doc_csid = INTL_DefaultDocCharSetID(m_pContext);
  8305.     doc_csid &= (~CS_AUTO);
  8306.     return doc_csid;
  8307. }
  8308.  
  8309. void CEditBuffer::ForceDocCharSetID(int16 csid){
  8310.     m_bForceDocCSID = TRUE;
  8311.     m_forceDocCSID = csid;
  8312. }
  8313.  
  8314. ED_FileError CEditBuffer::PublishFile( ED_SaveFinishedOption finishedOpt,
  8315.                            char *pSourceURL,
  8316.                            char **ppIncludedFiles,
  8317.                            char *pDestURL, /* Directories must have trailing slash, ie after HEAD call */
  8318.                            char *pUsername,
  8319.                            char *pPassword,
  8320.                            XP_Bool   bKeepImagesWithDoc,
  8321.                            XP_Bool   bAutoAdjustLinks ) {
  8322.     char *pDocTempDir = GetCommandLog()->GetDocTempDir();
  8323.     if (!pDocTempDir) {
  8324.       char *tmplate = XP_GetString(XP_EDT_ERR_PUBLISH_PREPARING_ROOT);
  8325.       if (tmplate) {
  8326.         char *msg = PR_smprintf(tmplate,pSourceURL);
  8327.         if (msg) {
  8328.           FE_Alert(m_pContext,msg);
  8329.           XP_FREE(msg);
  8330.         }
  8331.       }
  8332.       return ED_ERROR_FILE_OPEN;
  8333.     }
  8334.  
  8335.     // Create Abstract file system to write to remote server.
  8336.     ITapeFileSystem *tapeFS = 
  8337.       new CTapeFSPublish(m_pContext,pDestURL,pUsername,pPassword,pDocTempDir);
  8338.     XP_ASSERT(tapeFS);
  8339.     // tapeFS freed by CEditSaveObject.
  8340.  
  8341.     // saveAs set to TRUE, why not?
  8342.     return SaveFile( finishedOpt, pSourceURL, tapeFS, TRUE,
  8343.                      bKeepImagesWithDoc, bAutoAdjustLinks, 
  8344.                      FALSE,  // not auto-saving
  8345.                      ppIncludedFiles ); // explicitly specify which files to send with doc.
  8346. }
  8347.  
  8348. void CEditBuffer::InitEscapes(){
  8349.     // We escape character in the RAM character set, but we decide to escape depending upon the
  8350.     // document character set. So, for example if we're writing to SJIS, we don't escape,
  8351.     // even if we're originally in LATIN-1
  8352.  
  8353.     // Our strategy is:
  8354.     // For MacRoman or ISO_Latin_1, use entites for hi-bit characters.
  8355.     // For other character sets, minimize the number of entities we put out.
  8356.     // The minimum is:
  8357.     // & for ampersand
  8358.     // < for less-than
  8359.  
  8360.     int16 win_csid = GetRAMCharSetID();
  8361.     int16 doc_csid = GetDocCharSetID();
  8362.  
  8363.     XP_Bool bWinSimple = 
  8364.         win_csid == CS_MAC_ROMAN
  8365.         || win_csid == CS_LATIN1
  8366.         || win_csid == CS_ASCII;
  8367.  
  8368.     XP_Bool bDocSimple = 
  8369.         doc_csid == CS_MAC_ROMAN
  8370.         || doc_csid == CS_LATIN1
  8371.         || doc_csid == CS_ASCII;
  8372.  
  8373.     // Bug to fix after 4.0B3 -- can't quote if original csid is not 8-bit either.
  8374.     // The fix is to quote after transcoding, rather than before transcoding.
  8375.     XP_Bool bQuoteHiBits = bWinSimple && bDocSimple;
  8376.  
  8377.     edt_InitEscapes(win_csid, bQuoteHiBits);
  8378. }
  8379.  
  8380. // CM: Return 0 if OK, or -1 if device is full.
  8381. int CEditBuffer::WriteToFile( XP_File hFile ){ //char *pFileName ){
  8382.     int iResult = 0;
  8383.     CStreamOutFile *pOutFile;
  8384.     pOutFile = new CStreamOutFile(hFile, FALSE);
  8385.     CConvertCSIDStreamOut* pConverter = new CConvertCSIDStreamOut( GetRAMCharSetID(), GetDocCharSetID(), pOutFile);
  8386.     InitEscapes();
  8387.     printState.Reset( pConverter, this );
  8388.     Reduce( m_pRoot );
  8389.     PrintTree( m_pRoot );
  8390.     printState.Reset( 0, this );
  8391.     if( pOutFile->Status() != IStreamOut::EOS_NoError ){
  8392.         iResult = -1;
  8393.     }
  8394.     else {
  8395.         iResult = 0;
  8396.     }
  8397.     delete pConverter;
  8398.     // To Jack: this is a problem
  8399.     // When saving to a new file, this does nothing
  8400.     // 7/2/96
  8401.     //   edit_saving_url flag is set/cleared in CEditSaveObject
  8402.     //   and GetFileWriteTime() is done in that object's destructor
  8403.     // GetFileWriteTime();
  8404.  
  8405.     ////m_autoSaveTimer.Restart();  hardts. Moved to CEditBuffer::FileFetchComplete
  8406.     return iResult;
  8407. }
  8408.  
  8409. CEditDocState *CEditBuffer::RecordState() {
  8410.   CEditDocState *ret = new CEditDocState();
  8411.   if (!ret) {
  8412.     XP_ASSERT(0);
  8413.     return NULL;
  8414.   }
  8415.   
  8416.   WriteToBuffer(&ret->m_pBuffer,TRUE);
  8417.   ret->m_version = GetCommandLog()->GetVersion();
  8418.   return ret;
  8419. }
  8420.  
  8421. int32 CEditBuffer::WriteToBuffer( XP_HUGE_CHAR_PTR* ppBuffer, XP_Bool bEncodeSelectionAsComment ){
  8422.     CStreamOutMemory *pOutMemory = new CStreamOutMemory();
  8423.     CConvertCSIDStreamOut* pConverter = new CConvertCSIDStreamOut( GetRAMCharSetID(), GetDocCharSetID(), pOutMemory);
  8424.  
  8425.     InitEscapes();
  8426.     printState.Reset( pConverter, this );
  8427.     Reduce( m_pRoot );
  8428.     if ( m_bLayoutBackpointersDirty ) {
  8429.         bEncodeSelectionAsComment = FALSE;
  8430.     }
  8431.     printState.m_bEncodeSelectionAsComment = bEncodeSelectionAsComment;
  8432.     if ( printState.m_bEncodeSelectionAsComment ){
  8433.         GetSelection(printState.m_selection);
  8434.     }
  8435.     PrintTree( m_pRoot );
  8436.  
  8437.     printState.Reset( 0, this );
  8438.     pConverter->ForgetStream();
  8439.     delete pConverter;
  8440.  
  8441.     *ppBuffer = pOutMemory->GetText();
  8442.     int32 result = pOutMemory->GetLen();
  8443. #ifdef DEBUG_WRITETOBUFFER
  8444.     // Use XP_TRACE to print out the document one line at a time.
  8445.     // XP_TRACE has an internal limit of around 512 characters on
  8446.     // the ammount of characters it can print in one call.
  8447.     char* pBuffer = *ppBuffer;
  8448.     int32 bufLen = result;
  8449.     int32 lineStart = 0;
  8450.     const int MAXLINELENGTH = 130;
  8451.     for(int32 i = 0; i < bufLen; i++){
  8452.         char c = pBuffer[i];
  8453.         if ( c == '\n' || c == '\r' || i - lineStart > MAXLINELENGTH ) {
  8454.             pBuffer[i] = '\0';
  8455.             XP_TRACE(("%s", pBuffer + lineStart));
  8456.             pBuffer[i] = c;
  8457.             lineStart = i;
  8458.             if ( c == '\n' || c == '\r' ){
  8459.                 lineStart++;
  8460.                 if ( c == '\r' && pBuffer[lineStart] == '\n') {
  8461.                     i++;
  8462.                     lineStart++;
  8463.                 }
  8464.             }
  8465.         }
  8466.     }
  8467. #endif
  8468.     delete pOutMemory;
  8469.     return result;
  8470. }
  8471.  
  8472. XP_HUGE_CHAR_PTR CEditBuffer::GetPositionalText( ){
  8473.     CStreamOutMemory *pOutMemory = new CStreamOutMemory();
  8474.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  8475.     Reduce( m_pRoot );
  8476.     StreamToPositionalText( m_pRoot, pOutMemory );
  8477.     XP_HUGE_CHAR_PTR pRet = pOutMemory->GetText();
  8478.  
  8479.     // If the buffer is too large, display alert message and return NULL.
  8480.     // In win16 edt_RemoveNBSP() would loop for larger buffers.
  8481.     if (pOutMemory->GetLen() > MAX_POSITIONAL_TEXT) {
  8482.         char* tmplate = XP_GetString(XP_EDT_MSG_TEXT_BUFFER_TOO_LARGE);
  8483.         if (tmplate) {
  8484.             char* msg = PR_smprintf(tmplate, pOutMemory->GetLen(), MAX_POSITIONAL_TEXT);
  8485.             if (msg) {
  8486.                 FE_Alert(m_pContext, msg);
  8487.                 XP_FREE(msg);
  8488.             }
  8489.         }
  8490.         else
  8491.             XP_ASSERT(FALSE);
  8492.  
  8493.         XP_HUGE_FREE(pRet);
  8494.         pRet = NULL;
  8495.     }
  8496.     else
  8497.         edt_RemoveNBSP( INTL_GetCSIWinCSID(c), (char *)pRet );
  8498.  
  8499.     delete pOutMemory;
  8500.     return pRet;
  8501. }
  8502.  
  8503.  
  8504. void CEditBuffer::RestoreState(CEditDocState *pState) {
  8505.   if (!pState) return;
  8506.  
  8507.   // Maybe we should check that ReadFromBuffer() succeeded before setting the
  8508.   // version.
  8509.   GetCommandLog()->SetVersion(pState->m_version);
  8510.   ReadFromBuffer(pState->m_pBuffer);
  8511. }
  8512.  
  8513.  
  8514. void CEditBuffer::ReadFromBuffer(XP_HUGE_CHAR_PTR pBuffer){
  8515.     /* This starts an asynchronous process that causes the current buffer to be deleted,
  8516.      * and then a new CEditBuffer instance to be created for the same MWContext.
  8517.      */
  8518.     CGlobalHistoryGroup::GetGlobalHistoryGroup()->IgnoreNextDeleteOf(this);
  8519.     GetCommandLog()->SetInReload(TRUE);
  8520.  
  8521.     char *pDocURL = LO_GetBaseURL(m_pContext);
  8522.     URL_Struct *url_s = NET_CreateURLStruct(pDocURL, NET_DONT_RELOAD);
  8523.  
  8524.     if(!url_s){
  8525.         XP_TRACE(("Couldn't create the url struct."));
  8526.         return;
  8527.     }
  8528.  
  8529.     StrAllocCopy(url_s->content_type, TEXT_HTML);
  8530.     NET_StreamClass *stream =
  8531.         NET_StreamBuilder(FO_PRESENT, url_s, m_pContext);
  8532.     if(stream) {
  8533.         delete m_pRoot;
  8534.         m_pRoot = 0;
  8535.         m_pCurrent = 0;
  8536.         delete this; // See ya!
  8537.  
  8538.         // XP_STRCPY(buffer, XP_GetString(XP_HTML_MISSING_REPLYDATA));
  8539.         int32 length = XP_STRLEN(pBuffer);
  8540.         if ( length <= 0 ) {
  8541.             pBuffer = EDT_GetEmptyDocumentString();
  8542.             length = XP_STRLEN(pBuffer);
  8543.         }
  8544.  
  8545.         // Output buffer in smaller chunks. put_clock does not take XP_HUGE_CHAR_PTR.
  8546.         const int CHUNK_SIZE = 32000;
  8547.         char *pChunk = (char *)XP_ALLOC(CHUNK_SIZE);
  8548.         if (pChunk != NULL) {
  8549.             XP_HUGE_CHAR_PTR pSrc = pBuffer;
  8550.             char *pDest = pChunk;
  8551.             int count = 0;
  8552.             for (;;) {
  8553.                 if ((*pDest++ = *pSrc++) == 0) {
  8554.                     (*stream->put_block)(stream, pChunk, count);
  8555.                     break;
  8556.                 }                    
  8557.                 else if (++count == CHUNK_SIZE) {
  8558.                     (*stream->put_block)(stream, pChunk, count);
  8559.                     pDest = pChunk;
  8560.                     count = 0;
  8561.                 }
  8562.             }
  8563.         }
  8564.         else
  8565.             XP_ASSERT(FALSE);
  8566.  
  8567.         (*stream->complete)(stream);
  8568.         XP_FREE(stream);
  8569.         XP_FREEIF(pChunk);
  8570.     }
  8571.     NET_FreeURLStruct(url_s);
  8572. }
  8573.  
  8574. void CEditBuffer::WriteToStream( IStreamOut *pOut ) {
  8575.     InitEscapes();
  8576.     CConvertCSIDStreamOut converter( GetRAMCharSetID(), GetDocCharSetID(), pOut);
  8577.  
  8578.     printState.Reset( &converter, this );
  8579.     Reduce( m_pRoot );
  8580.     PrintTree( m_pRoot );
  8581.     printState.Reset( 0, this );
  8582.     converter.ForgetStream();
  8583. }
  8584.  
  8585. #ifdef XP_WIN16
  8586. // code segment is full, switch to a new segment
  8587. #pragma code_seg("EDTBUF2_TEXT","CODE")
  8588. #endif
  8589.  
  8590. #ifdef DEBUG
  8591. void CEditBuffer::DebugPrintState(IStreamOut& stream) {
  8592.     {
  8593.         // Print the document as HTML
  8594.         XP_HUGE_CHAR_PTR pData;
  8595.         WriteToBuffer(&pData, TRUE);
  8596.         XP_HUGE_CHAR_PTR b = pData;
  8597.         int lineLimit = 200;
  8598.         char* pLine = (char*) XP_ALLOC(lineLimit);
  8599.         while ( *b != '\0' ) {
  8600.             int i = 0;
  8601.             while ( *b != '\0' && *b != '\n'){
  8602.                 if ( i < lineLimit-1) {
  8603.                     pLine[i++] = *b;
  8604.                 }
  8605.                 b++;
  8606.             }
  8607.             pLine[i] = '\0';
  8608.             stream.Printf("%s\n", pLine);
  8609.             if ( *b == '\0') break;
  8610.             b++;
  8611.         }
  8612.         XP_FREE(pLine);
  8613.         XP_HUGE_FREE(pData);
  8614.     }
  8615.     {
  8616.         CEditSelection selection;
  8617.         GetSelection(selection);
  8618.         stream.Printf("\nCurrent selection: ");
  8619.         selection.Print(stream);
  8620.         stream.Printf("\n");
  8621.     }
  8622.     {
  8623.         CPersistentEditSelection selection;
  8624.         GetSelection(selection);
  8625.         stream.Printf("\nCurrent selection: ");
  8626.         selection.Print(stream);
  8627.         stream.Printf("\n");
  8628.     }
  8629.     if ( IsPhantomInsertPoint() ) {
  8630.         stream.Printf("   IsPhantomInsertPoint() = TRUE\n");
  8631.     }
  8632.     stream.Printf("Current typing command: ");
  8633.     stream.Printf("%s ", m_bTyping ? "true" : "false");
  8634.     stream.Printf("\n");
  8635.     stream.Printf("Document version: %d. Stored version %d.", GetCommandLog()->GetVersion(),
  8636.         GetCommandLog()->GetStoredVersion());
  8637.     stream.Printf("\n");
  8638.     GetCommandLog()->Print(stream);
  8639.     {
  8640.         stream.Printf("\n\nFiles asociated with the current Buffer:");
  8641.         char*p = GetAllDocumentFiles(NULL,TRUE);
  8642.         char *p1 = p;
  8643.         while( p && *p ){
  8644.             stream.Printf("\n  %s", p );
  8645.             p += XP_STRLEN( p );
  8646.         }
  8647.         if( p1) XP_FREE( p1 );
  8648.     }
  8649.  
  8650.     {
  8651.         stream.Printf("\n\nNamed Anchors in the current Buffer:");
  8652.         char*p = GetAllDocumentTargets();
  8653.         char *p1 = p;
  8654.         while( p && *p ){
  8655.             stream.Printf("\n  %s", p );
  8656.             p += XP_STRLEN( p );
  8657.         }
  8658.         if( p1 ) XP_FREE( p1 );
  8659.     }
  8660.     // lo_PrintLayout(m_pContext);
  8661. }
  8662. #endif
  8663.  
  8664. #ifdef DEBUG
  8665. void CEditBuffer::ValidateTree(){
  8666.     if ( m_bSkipValidation ) return;
  8667.     // Have the tree validate itself.
  8668.     m_pRoot->ValidateTree();
  8669.     if ( ! m_iSuppressPhantomInsertPointCheck ) {
  8670.         // Check that there are no phantom insert points other than at m_pCurrent
  8671.         CEditLeafElement* pElement;
  8672.         for ( pElement = m_pRoot->GetFirstMostChild()->Leaf();
  8673.             pElement;
  8674.             pElement = pElement->NextLeafAll() ){
  8675.             CEditInsertPoint ip(pElement, 0);
  8676.             if ( IsPhantomInsertPoint(ip) ){
  8677.                 if ( pElement != m_pCurrent ) {
  8678.                     XP_ASSERT(FALSE);
  8679.                 }
  8680.             }
  8681.         }
  8682.     }
  8683. }
  8684.  
  8685. void CEditBuffer::SuppressPhantomInsertPointCheck(XP_Bool bSuppress){
  8686.     if ( bSuppress )
  8687.         m_iSuppressPhantomInsertPointCheck++;
  8688.     else
  8689.         m_iSuppressPhantomInsertPointCheck--;
  8690. }
  8691. #endif
  8692.  
  8693. void CEditBuffer::DisplaySource( ){
  8694.     CStreamOutNet *pOutNet = new CStreamOutNet( m_pContext );
  8695.     CConvertCSIDStreamOut* pConverter = new CConvertCSIDStreamOut( GetRAMCharSetID(), GetDocCharSetID(), pOutNet);
  8696.     InitEscapes();
  8697.     printState.Reset( pConverter, this );
  8698.     PrintTree( m_pRoot );
  8699. #ifdef DEBUG
  8700.     DebugPrintTree( m_pRoot );
  8701.     DebugPrintState(*pConverter);
  8702. #endif
  8703.     printState.Reset( 0, this );
  8704.     delete pConverter;
  8705. }
  8706.  
  8707.  
  8708. //mjudge, this method will leave spaces instead of "\n"s unfortunately, 
  8709. //they wont go away.  I will look for another place to remove them nicely....
  8710. char* CEditBuffer::NormalizeText( char* pSrc ){
  8711.     char *pStr = pSrc;
  8712.     char *pDest;
  8713.     char lastChar = 1;
  8714.     pDest = pStr;
  8715.  
  8716.     if( GetParseState()->bLastWasSpace ){
  8717.         while( *pStr != '\0' && XP_IS_SPACE( *pStr ) ){
  8718.             lastChar = ' ';
  8719.             pStr++;
  8720.         }
  8721.     }
  8722.  
  8723.     //
  8724.     // Loop through convert all white space to a single white space.
  8725.     // leave \r\n and \n and \r and convert them to \n's to later parse out in edtele.cpp finishload
  8726.     while( *pStr != '\0'){
  8727.  
  8728.         if (  ( !*(pStr+1) && (*pStr== '\n' || *pStr== '\r') ) ||
  8729.                 ( *(pStr+1) && !*(pStr+2) && *pStr== '\r' && *pStr== '\n' )  ){
  8730.             lastChar = *pDest++= '\n';
  8731.             break;//out of while loop
  8732.             }
  8733.         else
  8734.             if( XP_IS_SPACE( *pStr ) ){
  8735.                 lastChar = *pDest++ = ' ';
  8736.                 while( *pStr != '\0' && XP_IS_SPACE( *pStr )){
  8737.                     pStr++;
  8738.                 }
  8739.         }
  8740.         else {
  8741.             lastChar = *pDest++ = *pStr++;
  8742.         }
  8743.     }
  8744.     *pDest = '\0';
  8745.  
  8746.     // if we actually coppied something, check the last character. happens when there is ANY text
  8747.     if( pSrc != pDest ){
  8748.         GetParseState()->bLastWasSpace = XP_IS_SPACE( lastChar );
  8749.     }
  8750.     return pSrc;
  8751. }
  8752.  
  8753.  
  8754.  
  8755. void CEditBuffer::RepairAndSet(CEditSelection& selection)
  8756. {
  8757.     // force layout stop displaying the current selection.
  8758. #ifdef LAYERS
  8759.     LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  8760. #else
  8761.     LO_StartSelectionFromElement( m_pContext, 0, 0);
  8762. #endif
  8763.     CPersistentEditSelection persel = EphemeralToPersistent(selection);
  8764.     Reduce(selection.GetCommonAncestor());
  8765.     selection = PersistentToEphemeral(persel);
  8766.     Relayout( selection.m_start.m_pElement, 0, selection.m_end.m_pElement, RELAYOUT_NOCARET );
  8767.     // Need to force selection.
  8768.     SetSelection(selection);
  8769. }
  8770.  
  8771. XP_Bool CEditBuffer::Reduce( CEditElement *pRoot ){
  8772.     // work backwards so when children go away, we don't have
  8773.     //  lossage.
  8774.     CEditElement *pChild, *pNext;
  8775.     pChild = pRoot->GetChild();
  8776.  
  8777.     while( pChild ) {
  8778.         pNext = pChild->GetNextSibling();
  8779.         if( Reduce( pChild )){
  8780.             pChild->Unlink();       // may already be unlinked
  8781.             delete pChild;
  8782.         }
  8783.         pChild = pNext;
  8784.     }
  8785.     return pRoot->Reduce( this );
  8786. }
  8787.  
  8788. //
  8789. //
  8790. //
  8791. void CEditBuffer::NormalizeTree(){
  8792.     CEditElement* pElement;
  8793.  
  8794.     // Make sure all text is in a container
  8795.     pElement = m_pRoot;
  8796.     while( (pElement = pElement->FindNextElement( &CEditElement::FindTextAll,0 ))
  8797.             != 0 ) {
  8798.         // this block of text does not have a container.
  8799.         if( pElement->FindContainer() == 0 ){
  8800.         }
  8801.     }
  8802.  
  8803. }
  8804.  
  8805. // Finish load timer.
  8806.  
  8807. CFinishLoadTimer::CFinishLoadTimer(){
  8808. }
  8809.  
  8810. void CFinishLoadTimer::OnCallback() {
  8811.     m_pBuffer->FinishedLoad2();
  8812. }
  8813.  
  8814. void CFinishLoadTimer::FinishedLoad(CEditBuffer* pBuffer)
  8815. {
  8816.     m_pBuffer = pBuffer;
  8817.     const uint32 kRelayoutDelay = 1;
  8818.     SetTimeout(kRelayoutDelay);
  8819. }
  8820.  
  8821. void CEditBuffer::FinishedLoad(){
  8822.     // To avoid race conditions, set up a timer.
  8823.     // An example of a race condition is when the
  8824.     // save dialog box goes up when the load is
  8825.     // finished, but the image
  8826.     // attributes haven't been set yet.
  8827.     m_finishLoadTimer.FinishedLoad(this);
  8828. }
  8829.  
  8830.  
  8831. #ifdef DEBUG_ltabb
  8832. int bNoAdjust = 0;
  8833. #endif
  8834.  
  8835.  
  8836. // Callback from plugins to start saving process.
  8837. PRIVATE void edt_OpenDoneCB(EDT_ImageEncoderStatus /*status*/, void* pArg) {
  8838.     MWContext *pContext = (MWContext *)pArg;
  8839.     // Notify front end that user interaction can resume
  8840.     FE_EditorDocumentLoaded( pContext );
  8841.  
  8842.     // Make current doc the most-recently-edited in prefs history list
  8843.     EDT_SyncEditHistory( pContext );
  8844. }
  8845.  
  8846. // Relayout the whole document.
  8847. void CEditBuffer::FinishedLoad2(){
  8848.  
  8849.     // In case this is a reload, restore the state of the wait flag.
  8850.     if ( EDT_IsPluginActive(m_pContext) ) {
  8851.         m_pContext->waitingMode = 1;
  8852.     }
  8853.  
  8854.     // Are there any extra levels on the Parse state?
  8855.     while ( m_parseStateStack.StackSize() > 1 ) {
  8856.         if ( GetParseState()->m_inJavaScript ) {
  8857.             WriteClosingScriptTag();
  8858.         }
  8859.         else {
  8860.             // XP_ASSERT(FALSE); // How did this happen?
  8861.         }
  8862.         PopParseState();
  8863.     }
  8864.     // And make sure the head has been finished.
  8865.     WriteClosingScriptTag();
  8866.  
  8867.     m_pCreationCursor = NULL;
  8868.  
  8869.     // Get rid of empty items.
  8870.     Reduce( m_pRoot );
  8871.  
  8872.     // Give everyone a chance to clean themselves up
  8873.  
  8874.     m_pRoot->FinishedLoad(this);
  8875.  
  8876. #ifdef EDT_DDT
  8877. #ifdef DEBUG_ltabb
  8878.     if( ! bNoAdjust ){
  8879.         m_pRoot->AdjustContainers(this);
  8880.     }
  8881. #else
  8882.     m_pRoot->AdjustContainers(this);
  8883. #endif
  8884.     // The AdjustContainer process may have produced empty paragraphs.
  8885.     // Calling FinishedLoad a second time cleans these up.
  8886.     m_pRoot->FinishedLoad(this);
  8887. #endif
  8888.  
  8889. #ifdef DEBUG
  8890.     m_pRoot->ValidateTree();
  8891. #endif
  8892.  
  8893.     // May have cleared out some more items.
  8894.     Reduce( m_pRoot );
  8895.  
  8896. #ifdef DEBUG
  8897.     m_pRoot->ValidateTree();
  8898. #endif
  8899.  
  8900.     // Temporarily set a legal selection.
  8901.     m_pCurrent = m_pRoot->NextLeafAll();
  8902.     while ( m_pCurrent && m_pCurrent->GetElementType() == eInternalAnchorElement ) {
  8903.         m_pCurrent = m_pCurrent->NextLeaf();
  8904.     }
  8905.     m_iCurrentOffset = 0;
  8906.  
  8907.     XP_Bool bShouldSendOpenEvent = !GetCommandLog()->InReload();
  8908.  
  8909.     // Set page properties (color, background, etc.)
  8910.     //  for a new document:
  8911.     XP_Bool bIsNewDocument = EDT_IS_NEW_DOCUMENT(m_pContext)
  8912.                                     && !GetCommandLog()->InReload();
  8913.  
  8914.     if( bIsNewDocument ){
  8915.         // This will call EDT_SetPageData(),
  8916.         // which calls RefreshLayout()
  8917.         FE_SetNewDocumentProperties(m_pContext);
  8918.         m_bDummyCharacterAddedDuringLoad = TRUE; /* Sometimes it's a no-break space. */
  8919.         // Get rid of extra characters.
  8920.     }
  8921.     RefreshLayout();
  8922.  
  8923.     // Add GENERATOR meta-data. This tells us who most recently edited the doucment.
  8924.     {
  8925.          char generatorValue[300];
  8926.         XP_SPRINTF(generatorValue, "%.90s/%.100s [%.100s]", XP_AppCodeName, XP_AppVersion, XP_AppName);
  8927.  
  8928.         EDT_MetaData *pData = MakeMetaData( FALSE, "GENERATOR", generatorValue);
  8929.  
  8930.         SetMetaData( pData );
  8931.         FreeMetaData( pData );
  8932.     }
  8933.  
  8934.     SetSelectionInNewDocument();
  8935.  
  8936.     m_bBlocked = FALSE;
  8937.     m_bReady = TRUE;
  8938.  
  8939.     if ( GetCommandLog()->InReload() ){
  8940.         GetCommandLog()->SetInReload(FALSE);
  8941.     }
  8942.     else {
  8943.         DocumentStored();
  8944.         GetCommandLog()->Trim();
  8945.     }
  8946.  
  8947. #ifdef DEBUG
  8948.     m_pRoot->ValidateTree();
  8949. #endif
  8950.  
  8951.     // Initialize correct witdth data for all tables and cells in document
  8952.     FixupTableData();
  8953.     
  8954.     if ( bShouldSendOpenEvent ) {
  8955.         History_entry * hist_entry = SHIST_GetCurrent(&(m_pContext->hist));
  8956.         char* pURL = hist_entry ? hist_entry->address : 0;
  8957.         EDT_PerformEvent(m_pContext, "open", pURL, TRUE, FALSE, edt_OpenDoneCB, m_pContext);
  8958.     }
  8959.     else {
  8960.         // Notify front end that user interaction can resume
  8961.         FE_EditorDocumentLoaded( m_pContext );
  8962.  
  8963.         // Make current doc the most-recently-edited in prefs history list
  8964.         EDT_SyncEditHistory( m_pContext );
  8965.     }
  8966. }
  8967.  
  8968. void CEditBuffer::SetSelectionInNewDocument(){
  8969.     VALIDATE_TREE(this);
  8970.     if ( ! m_pRoot )
  8971.         return;
  8972.  
  8973.     CEditInternalAnchorElement* pBeginSelection = m_pStartSelectionAnchor;
  8974.     CEditInternalAnchorElement* pEndSelection = m_pEndSelectionAnchor;
  8975.     XP_Bool bBeginStickyAfter = m_bStartSelectionStickyAfter;
  8976.     XP_Bool bEndStickyAfter = m_bEndSelectionStickyAfter;
  8977.  
  8978.     // At this point there is at most one selection comment of each type in
  8979.     // the document.
  8980.  
  8981.     // Error recovery - if we have either a begin selection or an end selection,
  8982.     // use it for the other edge.
  8983.     if ( pBeginSelection && ! pEndSelection){
  8984.         pEndSelection = pBeginSelection;
  8985.         bEndStickyAfter = bBeginStickyAfter;
  8986.     }
  8987.     else if ( ! pBeginSelection && pEndSelection){
  8988.         pBeginSelection = pEndSelection;
  8989.         bBeginStickyAfter = bEndStickyAfter;
  8990.     }
  8991.  
  8992.     if ( pBeginSelection && pEndSelection ){
  8993.         // We have an existing selection.
  8994.         // Move from the selection markers to the nearby leaf.
  8995.         CEditLeafElement* pB = (CEditLeafElement*) pBeginSelection->NextLeafAll(bBeginStickyAfter);
  8996.         if ( pB == NULL ){
  8997.             // At the edge of the document
  8998.             bBeginStickyAfter = ! bBeginStickyAfter;
  8999.             pB = (CEditLeafElement*) pBeginSelection->NextLeafAll(bBeginStickyAfter);
  9000.         }
  9001.         CEditLeafElement* pE = (CEditLeafElement*) pEndSelection->NextLeafAll(bEndStickyAfter);
  9002.         if ( pE == NULL ){
  9003.             // At the edge of the document
  9004.             bEndStickyAfter = ! bEndStickyAfter;
  9005.             pE = (CEditLeafElement*) pEndSelection->NextLeafAll(bEndStickyAfter);
  9006.         }
  9007.         if ( pB == pEndSelection ){ // The two comments are next to each other.
  9008.             pB = pE;
  9009.         }
  9010.         else if ( pE == pBeginSelection ){ // The two comments are reversed and next to each other.
  9011.             pE = pB;
  9012.         }
  9013.  
  9014.         if ( pB == pBeginSelection || pB == pEndSelection
  9015.             || pE == pBeginSelection || pE == pEndSelection ){
  9016.             // Degenerate case -- no other elements in the document.
  9017.             delete pBeginSelection;
  9018.             if ( pBeginSelection != pEndSelection ){
  9019.                 delete pEndSelection;
  9020.             }
  9021.             m_pRoot->FinishedLoad(this); // Fixes up container.
  9022.             m_pCurrent = m_pRoot->NextLeafAll();
  9023.             m_iCurrentOffset = 0;
  9024.             Relayout(m_pRoot, 0, m_pRoot->GetLastMostChild() );
  9025.        }
  9026.         else if ( pB != NULL && pE != NULL ){
  9027.             // Get rid of the comments
  9028.             delete pBeginSelection;
  9029.             if ( pBeginSelection != pEndSelection ){
  9030.                 delete pEndSelection;
  9031.             }
  9032.  
  9033.             // Swap if start happens to come after end.
  9034.             CEditInsertPoint start(pB, bBeginStickyAfter ? 0 : pB->GetLen(), bBeginStickyAfter);
  9035.             CEditInsertPoint end(pE, bEndStickyAfter ? 0 : pE->GetLen(), bEndStickyAfter);
  9036.             XP_Bool bFromStart = FALSE;
  9037.             if ( start > end ){
  9038.                 CEditInsertPoint temp = start;
  9039.                 start = end;
  9040.                 end = temp;
  9041.                 bFromStart = TRUE;
  9042.             }
  9043.             if ( start.m_pElement->IsEndOfDocument() ){
  9044.                 // Selection at the end of the document.
  9045.                 // So select the end of the previous element.
  9046.                 XP_Bool bInsertPoint = (pB == pE);
  9047.                 start.m_pElement = start.m_pElement->PreviousLeaf();
  9048.                 if ( start.m_pElement != NULL ) {
  9049.                     start.m_iPos = start.m_pElement->GetLen();
  9050.                 }
  9051.                 if ( bInsertPoint ) {
  9052.                     end = start;
  9053.                 }
  9054.             }
  9055.             if ( start.m_pElement != NULL && end.m_pElement != NULL ) {
  9056.                 CEditSelection selection(start, end, bFromStart);
  9057.                 SetSelection(selection);
  9058.                 // FinishedLoad can delete the selected items.
  9059.                 // Using a persistent selection is safe, because
  9060.                 // the worst that can happen is that the resulting
  9061.                 // selection is several characters off.
  9062.                 // (The right thing to do is make FinishedLoad
  9063.                 // preserve the selection. But this would be
  9064.                 // expensive to implement.)
  9065.                 CPersistentEditSelection persel = EphemeralToPersistent(selection);
  9066.                 LO_ClearSelection(m_pContext);
  9067.                 m_pRoot->FinishedLoad(this);
  9068.                 Reduce( m_pRoot );
  9069.                 Relayout(m_pRoot, 0, m_pRoot->GetLastMostChild(), RELAYOUT_NOCARET );
  9070.                 SetSelection(persel);
  9071.                 return;
  9072.             }
  9073.             // Otherwise fall through to default behavior
  9074.         }
  9075.     }
  9076.     //if ( pRelayoutFrom != NULL ) Relayout(pRelayoutFrom,0);
  9077.  
  9078.     // Default behavior
  9079.     m_pCurrent = m_pRoot->NextLeafAll();
  9080.     m_iCurrentOffset = 0;
  9081. }
  9082.  
  9083. void CEditBuffer::DummyCharacterAddedDuringLoad(){
  9084.     m_bDummyCharacterAddedDuringLoad = TRUE;
  9085. }
  9086.  
  9087. XP_Bool CEditBuffer::IsFileModified(){
  9088.     int32 last_write_time = m_iFileWriteTime;
  9089.  
  9090.     //Get current time and save in m_iFileWriteTime,
  9091.     // but NOT if in the process of saving a file
  9092.     if( m_pContext->edit_saving_url ){
  9093.         return FALSE;
  9094.     }
  9095.  
  9096.     GetFileWriteTime();
  9097.     // Skip if current_time is 0 -- first time through
  9098.     return( last_write_time != 0 && last_write_time != m_iFileWriteTime );
  9099. }
  9100.  
  9101. void CEditBuffer::GetFileWriteTime(){
  9102.     char *pDocURL = LO_GetBaseURL(m_pContext);
  9103.     char *pFilename = NULL;
  9104.  
  9105.     // Don't set the time if we are in the process of saving the file
  9106.     if( m_pContext->edit_saving_url ){
  9107.         return;
  9108.     }
  9109.  
  9110.     // Must be a local file type
  9111.     if( NET_IsLocalFileURL(pDocURL) &&
  9112.     // No longer uses XP_ConvertURLToLocalFile
  9113.         ( XP_STRCMP( pDocURL, XP_GetString(XP_EDIT_NEW_DOC_NAME) ) != 0 ) &&
  9114.         ((pFilename = NET_ParseURL(pDocURL, GET_PATH_PART)) != NULL ) ) {
  9115.         XP_StatStruct statinfo;
  9116.         if( -1 != XP_Stat(pFilename, &statinfo, xpURL) &&
  9117.             statinfo.st_mode & S_IFREG ){
  9118.             m_iFileWriteTime = statinfo.st_mtime;
  9119.         }
  9120.     }
  9121.     // pFilename is allocated even if XP_ConvertUrlToLocalFile returns FALSE
  9122.     if( pFilename ){
  9123.         XP_FREE(pFilename);
  9124.     }
  9125. }
  9126.  
  9127. static void SetDocColor(MWContext* pContext, int type, ED_Color& color)
  9128. {
  9129.     LO_Color *pColor = edt_MakeLoColor(color);
  9130.     // If pColor is NULL, this will use the "MASTER",
  9131.     //  (default Browser) color, and that's OK!
  9132.     LO_SetDocumentColor( pContext, type, pColor );
  9133.     if( pColor) XP_FREE(pColor);
  9134. }
  9135.  
  9136. void CEditBuffer::RefreshLayout(){
  9137.     VALIDATE_TREE(this);
  9138.  
  9139.     SetDocColor(m_pContext, LO_COLOR_BG, m_colorBackground);
  9140.     SetDocColor(m_pContext, LO_COLOR_FG, m_colorText);
  9141.     SetDocColor(m_pContext, LO_COLOR_LINK, m_colorLink);
  9142.     SetDocColor(m_pContext, LO_COLOR_VLINK, m_colorFollowedLink);
  9143.     SetDocColor(m_pContext, LO_COLOR_ALINK, m_colorActiveLink);
  9144.  
  9145.     // Fix for bug 42390.
  9146.     // Implementation of LO_SetBackgroundImage has changed, it now requires
  9147.     // an absolute URL.  Maybe we should require that m_pBackgroundImage is 
  9148.     // always absolute.
  9149.     if (m_pBackgroundImage && *m_pBackgroundImage) {
  9150.       char *pAbsolute = NET_MakeAbsoluteURL( LO_GetBaseURL(m_pContext ), m_pBackgroundImage );
  9151.       if (pAbsolute) {
  9152.         LO_SetBackgroundImage( m_pContext, pAbsolute );
  9153.         XP_FREE(pAbsolute);
  9154.       }
  9155.     }
  9156.     else {
  9157.       LO_SetBackgroundImage( m_pContext, m_pBackgroundImage );
  9158.     }
  9159.  
  9160.     Relayout(m_pRoot, 0, m_pRoot->GetLastMostChild() );
  9161. }
  9162.  
  9163. void CEditBuffer::SetDisplayParagraphMarks(XP_Bool bDisplay) {
  9164.     m_pContext->display_paragraph_marks = bDisplay;
  9165.     RefreshLayout();
  9166. }
  9167.  
  9168. XP_Bool CEditBuffer::GetDisplayParagraphMarks() {
  9169.     return m_pContext->display_paragraph_marks;
  9170. }
  9171.  
  9172.  
  9173. void CEditBuffer::SetDisplayTables(XP_Bool bDisplay) {
  9174.     m_pContext->display_table_borders = bDisplay;
  9175.     RefreshLayout();
  9176. }
  9177.  
  9178. XP_Bool CEditBuffer::GetDisplayTables() {
  9179.     return m_pContext->display_table_borders;
  9180. }
  9181.  
  9182. ED_TextFormat CEditBuffer::GetCharacterFormatting( ){
  9183.     CEditLeafElement *pElement = m_pCurrent;
  9184.  
  9185.     //
  9186.     // While selecting, we may not have a valid region
  9187.     //
  9188.     if( IsSelecting() ){
  9189.         return TF_NONE;
  9190.     }
  9191.  
  9192.     if( IsSelected() ){
  9193.         ElementOffset i,i1;
  9194.         CEditLeafElement *pEnd;
  9195.         XP_Bool bFromStart;
  9196.         GetSelection( pElement, i, pEnd, i1, bFromStart );
  9197.     }
  9198.  
  9199.     if( pElement && pElement->IsA(P_TEXT )){
  9200.         return pElement->Text()->m_tf;
  9201.     }
  9202.  
  9203.     return TF_NONE;
  9204. }
  9205.  
  9206. int CEditBuffer::GetFontSize( ){
  9207.     CEditLeafElement *pElement = m_pCurrent;
  9208.  
  9209.     if( IsSelecting() ){
  9210.         return 0;
  9211.     }
  9212.  
  9213.     if( IsSelected() ){
  9214.         ElementOffset i,i1;
  9215.         CEditLeafElement *pEnd;
  9216.         XP_Bool bFromStart;
  9217.         GetSelection( pElement, i, pEnd, i1, bFromStart );
  9218.     }
  9219.     if( pElement && pElement->IsA(P_TEXT )){
  9220.         return pElement->Text()->GetFontSize();
  9221.     }
  9222.  
  9223.     //
  9224.     // The following logic is similar to CEditBuffer::GetCharacterData().
  9225.     //
  9226.  
  9227.     // Try to find the previous text element and use its font size.
  9228.     if (pElement) {
  9229.         CEditTextElement *tElement = pElement->PreviousTextInContainer();
  9230.         if ( tElement){
  9231.             return tElement->GetFontSize();
  9232.         }
  9233.  
  9234.         // Get font size from parent of pElement.
  9235.         return pElement->GetDefaultFontSize();
  9236.     }
  9237.  
  9238.     return 0;
  9239. }
  9240.  
  9241. int CEditBuffer::GetFontPointSize( ){
  9242.     CEditLeafElement *pElement = m_pCurrent;
  9243.  
  9244.     if( IsSelecting() ){
  9245.         return 0;
  9246.     }
  9247.  
  9248.     if( IsSelected() ){
  9249.         ElementOffset i,i1;
  9250.         CEditLeafElement *pEnd;
  9251.         XP_Bool bFromStart;
  9252.         GetSelection( pElement, i, pEnd, i1, bFromStart );
  9253.     }
  9254.     if( pElement && pElement->IsA(P_TEXT )){
  9255.         return pElement->Text()->GetFontPointSize();
  9256.     }
  9257.  
  9258.     //
  9259.     // The following logic is similar to CEditBuffer::GetCharacterData().
  9260.     //
  9261.  
  9262.     // Try to find the previous text element and use its font size.
  9263.     if (pElement) {
  9264.         CEditTextElement *tElement = pElement->PreviousTextInContainer();
  9265.         if ( tElement){
  9266.             return tElement->GetFontPointSize();
  9267.         }
  9268.  
  9269.         // Get font size from parent of pElement.
  9270.         // There is only one default for all containers, unlike relative font size
  9271.         // return pElement->GetDefaultFontSize();
  9272.     }
  9273.  
  9274.     return ED_FONT_POINT_SIZE_DEFAULT;
  9275. }
  9276.  
  9277. int CEditBuffer::GetFontFaceIndex()
  9278. {
  9279.     int iReturn = -1;   // Default is "unknown"
  9280.  
  9281.     EDT_CharacterData * pData = GetCharacterData();
  9282.     if( pData ){
  9283.         if( pData->mask & TF_FONT_FACE ){
  9284.             // We are sure of the font state - see if it is set to something
  9285.             if( (pData->values & TF_FONT_FACE) && pData->pFontFace ){
  9286.                 iReturn = ED_FONT_LOCAL;
  9287.             } else if( pData->mask & TF_FIXED ){
  9288.                 // We don't have a NS Font - we have a default HTML font
  9289.                 //   and we are sure of the Fixed Width state
  9290.                 if( pData->values & TF_FIXED ){
  9291.                     iReturn = 1;   // We have default fixed-with font
  9292.                 } else {
  9293.                     iReturn = 0;   // We have default proportional font
  9294.                 }
  9295.             }
  9296.         }
  9297.         XP_FREE(pData);
  9298.     }
  9299.     return iReturn;
  9300. }
  9301.  
  9302. static char pFontFace[256] = "";
  9303.  
  9304. // Use this to search font faces in menu or dropdown list
  9305. char * CEditBuffer::GetFontFace()
  9306. {
  9307.     char * pFontFaces = EDT_GetFontFaces();
  9308.     if( !pFontFaces ){
  9309.         return NULL;
  9310.     }
  9311.     EDT_CharacterData * pData = GetCharacterData();
  9312.     if( !pData ){
  9313.         return NULL;
  9314.     }
  9315.     
  9316.     // Truncate static string we might return;
  9317.     *pFontFace = '\0';
  9318.     
  9319.     // Most of the time we copy font from EDT_CharacterData to this static
  9320.     char * pReturn = pFontFace;
  9321.  
  9322.     if( pData->mask & TF_FONT_FACE ){
  9323.         // We are sure of the font state - see if it is set to something
  9324.         if( (pData->values & TF_FONT_FACE) && pData->pFontFace ){
  9325.             // We are sure we have a NS Font - get the list to scan
  9326.  
  9327.             char * pFontTagList = EDT_GetFontFaceTags();
  9328.             if( pFontTagList ){
  9329.                 //  Start at the second string in the list ("Fixed Width")
  9330.                 char *pFontTag = pFontTagList + XP_STRLEN(pFontTagList) + 1;
  9331.                 char *pFace = pFontFaces + XP_STRLEN(pFontFaces) + 1;
  9332.                 if( *pFontTag != '\0' ) {
  9333.                     int iLen;
  9334.                     // Skip over fixed-width string for 1st real NS Font
  9335.                     pFontTag += XP_STRLEN(pFontTag) + 1;
  9336.                     pFace += XP_STRLEN(pFace) + 1;
  9337.                     int index = 2;
  9338.                     while( (iLen = XP_STRLEN(pFontTag)) > 0 ) {
  9339.                         if( 0 == XP_STRCMP( pFontTag, pData->pFontFace ) ){
  9340.                             // We found a "translated" font group,
  9341.                             //   return local font that corresponds
  9342.                             pReturn = pFace;
  9343.                             break;
  9344.                         }
  9345.                         // Next string
  9346.                         pFontTag += iLen+1;
  9347.                         pFace += XP_STRLEN(pFace) + 1; 
  9348.                         index++;
  9349.                     }
  9350.                 }
  9351.             }
  9352.             // If we didn't find it in the XP list, it must be
  9353.             //   a local system font, so just copy what's in character data
  9354.             if( !*pReturn ){
  9355.                 XP_STRCPY(pFontFace, pData->pFontFace);
  9356.             }
  9357.         } else if( pData->mask & TF_FIXED ){
  9358.             // We don't have a font face -- must be a default HTML font
  9359.             //   and we are sure of the Fixed Width state
  9360.             if( pData->values & TF_FIXED ){
  9361.                 // we are "Fixed Width" - its the second string
  9362.                 pReturn = pFontFaces + XP_STRLEN(pFontFaces) + 1;
  9363.             } else {
  9364.                 // we are "Variable Width"
  9365.                 pReturn = pFontFaces;
  9366.             }
  9367.         }
  9368.     }
  9369.  
  9370.     XP_FREE(pData);
  9371.  
  9372.     if(pReturn && !*pReturn){
  9373.         // Empty string - return NULL instead
  9374.         return NULL;
  9375.     }
  9376.     return pReturn;
  9377. }
  9378.  
  9379. ED_Color CEditBuffer::GetFontColor( ){
  9380.     CEditLeafElement *pElement = m_pCurrent;
  9381.  
  9382.     if( IsSelecting() ){
  9383.         return ED_Color::GetUndefined();
  9384.     }
  9385.  
  9386.     if( IsSelected() ){
  9387.         ElementOffset i,i1;
  9388.         CEditLeafElement *pEnd;
  9389.         XP_Bool bFromStart;
  9390.         GetSelection( pElement, i, pEnd, i1, bFromStart );
  9391.     }
  9392.     if( pElement && pElement->IsA(P_TEXT )){
  9393.         return pElement->Text()->GetColor();
  9394.     }
  9395.     return ED_Color::GetUndefined();
  9396. }
  9397.  
  9398. TagType CEditBuffer::GetParagraphFormattingSelection(CEditSelection& selection)
  9399. {
  9400.     TagType type = P_UNKNOWN;
  9401.  
  9402.     // Grab the current selection.
  9403. //    CEditSelection selection;
  9404.     if( selection.IsEmpty() )
  9405.         GetSelection(selection);
  9406.     CEditContainerElement* pStart = selection.m_start.m_pElement->FindContainer();
  9407.     CEditContainerElement* pEnd = selection.m_end.m_pElement->FindContainer();
  9408.     XP_Bool bUseEndContainer = !selection.EndsAtStartOfContainer();
  9409.  
  9410.     // Scan for all text elements and save first container type found
  9411.     // return that type only if all selected text elements have the same type
  9412.     while( pStart ){
  9413.         if( type == P_UNKNOWN ){
  9414.             // First time through
  9415.             type = pStart->GetType();
  9416.         } else if( type != pStart->GetType() ) {
  9417.             // Type is different - get out now
  9418.             return P_UNKNOWN;
  9419.         }
  9420.         // Start = end, we're done
  9421.         if( pStart == pEnd ){
  9422.             break;
  9423.         }
  9424.         CEditElement* pLastChild = pStart->GetLastMostChild();
  9425.         if ( ! pLastChild ) break;
  9426.         CEditElement* pNextChild = pLastChild->NextLeaf();
  9427.         if ( ! pNextChild ) break;
  9428.         pStart = pNextChild->FindContainer();
  9429.         if ( pStart == pEnd && ! bUseEndContainer ) break;
  9430.     }
  9431.     return type;
  9432. }
  9433.  
  9434. TagType CEditBuffer::GetParagraphFormatting()
  9435. {
  9436.     CEditContainerElement *pCont;
  9437.     // Empty selection -- will get current selection if exists
  9438.     CEditSelection selection;
  9439.     TagType type = P_UNKNOWN;
  9440.  
  9441.     if( IsSelected() ){
  9442.         return GetParagraphFormattingSelection(selection);
  9443.     }
  9444.     if( IsTableOrCellSelected() )
  9445.     {
  9446.         // Get each cell contents to examine - as if it were selected
  9447.         if( GetFirstCellSelection(selection) )
  9448.         {
  9449.             // Get format from first selected cell
  9450.             type = GetParagraphFormattingSelection(selection);
  9451.             if( type != P_UNKNOWN )
  9452.             {
  9453.                 while( GetNextCellSelection(selection) )
  9454.                 {
  9455.                     // All cells must have the same type, else stop now
  9456.                     if( type != GetParagraphFormattingSelection(selection) )
  9457.                     {
  9458.                         type = P_UNKNOWN;
  9459.                         break;
  9460.                     }
  9461.                 }
  9462.             }
  9463.         }
  9464.         return type;
  9465.     } 
  9466.     else {
  9467.         if( m_pCurrent && (pCont = m_pCurrent->FindContainer()) != 0){
  9468.             type = pCont->GetType();
  9469.         }
  9470.         else {
  9471.             type = P_UNKNOWN;
  9472.         }
  9473.     }
  9474.     return type;
  9475. }
  9476.  
  9477. void CEditBuffer::PositionCaret(int32 x, int32 y) {
  9478.     VALIDATE_TREE(this);
  9479.     ClearPhantomInsertPoint();
  9480.     DoneTyping();
  9481.     ClearMove();
  9482. #ifdef LAYERS
  9483.     LO_PositionCaret( m_pContext, x, y, NULL );
  9484. #else
  9485.     LO_PositionCaret( m_pContext, x, y );
  9486. #endif
  9487. }
  9488.  
  9489. // This is static so all windows may share current drag table data
  9490. EDT_DragTableData* CEditBuffer::m_pDragTableData = NULL;
  9491.  
  9492. XP_Bool CEditBuffer::StartDragTable(int32 /*x*/, int32 /*y*/)
  9493. {
  9494.     if( !m_pDragTableData )
  9495.     {
  9496.         m_pDragTableData = XP_NEW( EDT_DragTableData );
  9497.     }
  9498.     if( m_pDragTableData )
  9499.     {
  9500.         // Initialize data for possible dragging
  9501.         // This is used by PositionDropCaret()
  9502.         m_pDragTableData->iDropType = ED_DROP_NORMAL;
  9503.         m_pDragTableData->pDragOverCell = NULL;
  9504.         // Source that we are dragging
  9505.         m_pDragTableData->iSourceType = m_TableHitType;
  9506.  
  9507.         // Get first cell of selection - we ignore a drop on that cell
  9508.         if( m_pSelectedLoTable )
  9509.         {
  9510.             // Get first cell of entire table
  9511.             LO_Element *pLoCell = m_pSelectedLoTable->next;
  9512.             while( pLoCell && pLoCell->type != LO_CELL ) { pLoCell = pLoCell->lo_any.next; }
  9513.             m_pDragTableData->pFirstSelectedCell = pLoCell;
  9514.         }
  9515.         else {
  9516.             // Get first cell in selected LO_CELL list
  9517.             m_pDragTableData->pFirstSelectedCell = (LO_Element*)m_SelectedLoCells[0];
  9518.         }
  9519.         return TRUE;
  9520.     }
  9521.     return FALSE;
  9522. }
  9523.  
  9524. void CEditBuffer::StopDragTable()
  9525.     if( m_pDragTableData )
  9526.     {
  9527.         XP_FREE(m_pDragTableData);
  9528.         m_pDragTableData = NULL;
  9529.     }
  9530. }
  9531.  
  9532. /* Converts ("snaps") input X, Y (doc coordinates) to X, Y needed for drop caret 
  9533.  *  and calls appropriate front-end FE_Display<Text|Generic|Image>Caret to use show where
  9534.  *  a drop would occur. It does NOT change current selection or internal caret position
  9535.  * Also handles table selection
  9536. */
  9537. XP_Bool CEditBuffer::PositionDropCaret(int32 x, int32 y)
  9538. {
  9539. #ifdef XP_WIN
  9540.     // If we are dragging a table selection,
  9541.     //  get feedback data, except when entire table selected
  9542.     //   (use normal caret placement for that)
  9543.     if( m_pDragTableData && m_pDragTableData->iSourceType > ED_HIT_SEL_TABLE )
  9544.     {
  9545.         // Get where we currently are
  9546.         LO_Element *pLoCell = NULL;
  9547.         int32 iWidth, iHeight;
  9548.  
  9549.         // Get where user is currently dragging over
  9550.         ED_DropType iDropType = GetTableDropRegion(&x, &y, &iWidth, &iHeight, &pLoCell);
  9551.  
  9552.         // No point in dropping exactly on top of selection being dragged
  9553.         if( iDropType == ED_DROP_NONE )
  9554.         {
  9555.             FE_DestroyCaret(m_pContext);
  9556.             return FALSE;
  9557.         }
  9558.         
  9559.         if( m_pDragTableData->iDropType != ED_DROP_NORMAL )
  9560.         {
  9561.             // Call Front end to display drop feedback only if
  9562.             //  different from previous condition
  9563.             if( pLoCell != m_pDragTableData->pDragOverCell ||
  9564.                 iDropType != m_pDragTableData->iDropType )
  9565.             {
  9566.                 m_pDragTableData->iDropType = iDropType;
  9567.                 m_pDragTableData->pDragOverCell = pLoCell;
  9568.                 m_pDragTableData->X = x;
  9569.                 m_pDragTableData->Y = y;
  9570.                 m_pDragTableData->iWidth = iWidth;
  9571.                 m_pDragTableData->iHeight = iHeight;
  9572.                 if( m_pDragTableData->iDropType != ED_DROP_REPLACE_CELL )
  9573.                 {
  9574.                     FE_DestroyCaret(m_pContext);
  9575.                     FE_DisplayDropTableFeedback(m_pContext, m_pDragTableData);
  9576.                 } else {
  9577.                     // TODO: SET "REPLACE" BIT FOR ALL CELLS TO BE REPLACED
  9578.                     //   AND CALL FEs TO REDRAW EACH CELL
  9579.                 }
  9580.             }
  9581.             return TRUE;
  9582.         }
  9583.     }
  9584. #endif
  9585.     FE_DestroyCaret(m_pContext);
  9586.     lo_PositionDropCaret(m_pContext, x, y, NULL);
  9587.     return TRUE;
  9588. }
  9589.  
  9590. void CEditBuffer::DeleteSelectionAndPositionCaret( int32 x, int32 y ) {
  9591.     // Nothing selected - nothing to delete
  9592.     if( !IsSelected() ){
  9593.         PositionCaret(x,y);    
  9594.         return;
  9595.     }
  9596.  
  9597.     // Get where we would drop if not deleting anything
  9598.     int32 position;
  9599.     LO_Element * pLoElement = lo_PositionDropCaret(m_pContext, x, y, &position );
  9600.     if( pLoElement ){
  9601.         // Get the CEditObject at drop point and use it to create new insert point
  9602.         CPersistentEditSelection PSel;
  9603.         GetSelection(PSel);
  9604.         CEditElement * pEdElement = pLoElement->lo_any.edit_element;
  9605.         // Create an "ephemeral" insert point from the EditLeafObject
  9606.         CEditInsertPoint IP(pEdElement, pLoElement->lo_any.edit_offset + position /*???? offset */);
  9607.         CPersistentEditInsertPoint Insert = EphemeralToPersistent(IP);
  9608.  
  9609.         // Check if new insert point will be after the selection
  9610.         //  we will delete and adjust the index by the amount deleted
  9611.         if( Insert.m_index > PSel.m_start.m_index ){
  9612.             Insert.m_index -= PSel.m_end.m_index - PSel.m_start.m_index;
  9613.         }
  9614.         // Delete the selection
  9615.         DeleteSelection();
  9616.         SetInsertPoint(Insert);
  9617.     }
  9618. }
  9619.  
  9620.  // this routine is called when the mouse goes down.
  9621. //
  9622. void CEditBuffer::StartSelection( int32 x, int32 y, XP_Bool doubleClick ){
  9623.     VALIDATE_TREE(this);
  9624.     ClearPhantomInsertPoint();
  9625.     {
  9626.         // This is a hack to avoid auto-scrolling to the old selection.
  9627.         XP_Bool scrolling = m_inScroll;
  9628.         m_inScroll = TRUE;
  9629.         ClearSelection();
  9630.         m_inScroll = scrolling;
  9631.     }
  9632.  
  9633.     // Remove any existing table or cell selection
  9634.     // This is the best place to put this so all "normal" selection-setting
  9635.     //  clears the table selection, but it prevents moving caret without clearing table
  9636.     ClearTableAndCellSelection();
  9637.  
  9638.     FE_DestroyCaret(m_pContext);
  9639.     ClearMove(); // do this when moving caret and not up or down arrow
  9640.     DoneTyping();
  9641.     if ( doubleClick ) {
  9642. #ifdef LAYERS
  9643.         LO_DoubleClick( m_pContext, x, y, NULL );
  9644. #else
  9645.         LO_DoubleClick( m_pContext, x, y );
  9646. #endif
  9647.     }
  9648.     else {
  9649. #ifdef LAYERS
  9650.         LO_Click( m_pContext, x, y, FALSE, NULL );
  9651. #else
  9652.         LO_Click( m_pContext, x, y, FALSE );
  9653. #endif /* LAYERS */
  9654.     }
  9655. }
  9656.  
  9657. // Note: By using MoveAndHideCaretInTable,
  9658. //  we do NOT expose caret, scroll window etc.
  9659. void CEditBuffer::MoveToFirstSelectedCell()
  9660. {
  9661.     int iSize = m_SelectedLoCells.Size();
  9662.     if( iSize )
  9663.         MoveAndHideCaretInTable((LO_Element*)m_SelectedLoCells[0]);
  9664. }
  9665.  
  9666. void CEditBuffer::MoveToLastSelectedCell()
  9667. {
  9668.     int iSize = m_SelectedLoCells.Size();
  9669.     if( iSize )
  9670.         MoveAndHideCaretInTable((LO_Element*)m_SelectedLoCells[iSize-1]);
  9671. }
  9672.  
  9673. void CEditBuffer::MoveAndHideCaretInTable(LO_Element *pLoElement)
  9674. {
  9675.     if(!pLoElement)
  9676.         return;
  9677.     // From StartSelection()
  9678.     ClearPhantomInsertPoint();
  9679.     FE_DestroyCaret(m_pContext);
  9680.     // What do these do?
  9681.     ClearMove();
  9682.     DoneTyping();
  9683.     CEditElement *pLastElement = NULL;
  9684.  
  9685.     if( pLoElement->type == LO_TABLE )
  9686.     {
  9687.         // Get first cell
  9688.         while( pLoElement->type != LO_CELL )
  9689.             pLoElement = pLoElement->lo_any.next;
  9690.     }
  9691.  
  9692.     if( pLoElement->type != LO_CELL )
  9693.         return;
  9694.  
  9695.     // Search for the last element in the cell that 
  9696.     //   has edit pointer, and thus is a leaf
  9697.     LO_Element * pCellElement = pLoElement->lo_cell.cell_list;
  9698.     while( pCellElement )
  9699.     {
  9700.         if( pCellElement->lo_any.edit_element )
  9701.             pLastElement = pCellElement->lo_any.edit_element;
  9702.  
  9703.         pCellElement = pCellElement->lo_any.next;
  9704.     }
  9705.  
  9706.     if( pLastElement )
  9707.     {    
  9708.         // From SetInsertPoint()
  9709.     #ifdef LAYERS
  9710.         LO_StartSelectionFromElement( m_pContext, 0, 0, NULL );
  9711.     #else
  9712.         LO_StartSelectionFromElement( m_pContext, 0, 0);
  9713.     #endif
  9714.         m_pCurrent = pLastElement->Leaf();
  9715.         if( pLastElement->IsText() )
  9716.         {
  9717.             char *pText = pLastElement->Text()->m_pText;
  9718.             m_iCurrentOffset = pText ? XP_STRLEN(pText) : 0;
  9719.         } else {
  9720.             m_iCurrentOffset = 1;   //TODO: ASK JACK IF THIS IS OK FOR ALL NON-TEXT
  9721.         }
  9722.         m_bCurrentStickyAfter = FALSE;
  9723.     }
  9724. }
  9725.  
  9726. // Use only after deleting a cell, and before Relayout(),
  9727. //  so later doesn't crash because current element is deleted
  9728. void CEditBuffer::MoveToExistingCell(CEditTableElement *pTable, int32 X, int32 Y)
  9729. {
  9730.     if( !pTable )
  9731.         return;
  9732.     CEditInsertPoint insertPoint;
  9733.     LO_Element *pLoElement = NULL;
  9734.  
  9735.     // Get the cell under the supplied coordinates
  9736.     GetTableHitRegion(X, Y, &pLoElement);
  9737.     CEditElement *pTableCell = NULL;
  9738.     if( pLoElement && pLoElement->type == LO_CELL )
  9739.     {
  9740.         // Try to get cell at
  9741.         //   Current cursor location...
  9742.         pTableCell = GetTableElementFromLO_Element(pLoElement, LO_CELL);
  9743.         
  9744.         // ...First cell of current row...
  9745.         if( !pTableCell )
  9746.             pTableCell = pTable->GetFirstCellInRow(Y, TRUE);
  9747.  
  9748.         // ...or First cell in table
  9749.         if( !pTableCell )
  9750.             pTableCell = pTable->GetFirstCell();
  9751.     }
  9752.     if( pTableCell )
  9753.     {
  9754.         insertPoint.m_pElement = pTableCell->GetLastMostChild()->Leaf();
  9755.     } else {
  9756.         insertPoint.m_pElement = m_pRoot->GetFirstMostChild()->Leaf();
  9757.     }
  9758.     insertPoint.m_iPos = insertPoint.m_pElement->GetLen();
  9759.  
  9760.     CEditSelection selection = CEditSelection(insertPoint, insertPoint);
  9761.     SetSelection(selection);
  9762. }
  9763.  
  9764. // this routine is called when the right mouse goes down.
  9765. //
  9766. void CEditBuffer::SelectObject( int32 x, int32 y ){
  9767.     PositionCaret(x,y);
  9768.     ClearSelection();
  9769.     FE_DestroyCaret(m_pContext);
  9770. #ifdef LAYERS
  9771.     LO_SelectObject(m_pContext, x, y, NULL);
  9772. #else
  9773.     LO_SelectObject(m_pContext, x, y);
  9774. #endif
  9775. }
  9776.  
  9777. //
  9778. //
  9779. // this routine is called when the mouse is moved while down.
  9780. //  at this point we actually decide that we are inside a selection.
  9781. //
  9782. void CEditBuffer::ExtendSelection( int32 x, int32 y ){
  9783.     BeginSelection();
  9784.     LO_ExtendSelection( m_pContext, x, y );
  9785.  
  9786.     // may not result in an actual selection.
  9787.     //  if this is the case, just position the caret.
  9788.     if( ! IsSelected() ) {
  9789.         // If there's no selection, it is still possible that a
  9790.         // selection has been started.
  9791.         if ( ! LO_IsSelectionStarted( m_pContext ) ) {
  9792.             StartSelection( x, y, FALSE );
  9793.         }
  9794.         else
  9795.         {
  9796.             // The selection is the empty selection.
  9797.             //ClearSelection();
  9798.             //FE_DestroyCaret(m_pContext);
  9799.             //ClearMove();
  9800.         }
  9801.     }
  9802.     else {
  9803.         m_pCurrent = 0;
  9804.         m_iCurrentOffset = 0;
  9805.         // We really did extend selection - 
  9806.         // Remove any existing table or cell selection
  9807.         ClearTableAndCellSelection();
  9808.      }
  9809. }
  9810.  
  9811. void CEditBuffer::ExtendSelectionElement( CEditLeafElement *pEle, int iOffset, XP_Bool bStickyAfter ){
  9812.     int iLayoutOffset;
  9813.     LO_Element* pLayoutElement;
  9814.     BeginSelection();
  9815.  
  9816.     if( IsSelected() ){
  9817.         LO_Element *pStart, *pEnd;
  9818.         intn iStartPos, iEndPos;
  9819.         XP_Bool bFromStart;
  9820.         LO_GetSelectionEndPoints( m_pContext,
  9821.                     &pStart, &iStartPos,
  9822.                     &pEnd, &iEndPos,
  9823.                     &bFromStart, 0);
  9824.         if( !(pStart == pEnd && iStartPos == iEndPos) ){
  9825.             if( iOffset ) iOffset--;
  9826.         }
  9827.     }
  9828.     else {
  9829.         if( iOffset ) iOffset--;
  9830.     }
  9831.  
  9832.  
  9833.     // we need to convert a selection edit iOffset to a Selection
  9834.     //  offset.  This will only work when selecting to the right...
  9835.  
  9836.     pEle->GetLOElementAndOffset( iOffset, bStickyAfter,
  9837.                pLayoutElement, iLayoutOffset );
  9838.  
  9839.     LO_ExtendSelectionFromElement( m_pContext, pLayoutElement,
  9840.                 iLayoutOffset, FALSE );
  9841. }
  9842.  
  9843. void CEditBuffer::SelectAll(){
  9844.     VALIDATE_TREE(this);
  9845.     // Remove any existing table or cell selection
  9846.     ClearTableAndCellSelection();
  9847.     ClearPhantomInsertPoint();
  9848.     ClearMove();
  9849.     DoneTyping();
  9850.     // Clear selection (don't resync insertion point to avoid scrolling.)
  9851.     ClearSelection(FALSE);
  9852.     // Destroy the cursor. Perhaps this is something ClearSelection should do.
  9853.     FE_DestroyCaret(m_pContext);
  9854.     // Delegate actual selection to the layout engine
  9855.     if( LO_SelectAll(m_pContext) ) {
  9856.         // Clear insertion point
  9857.         m_pCurrent = 0;
  9858.         m_iCurrentOffset = 0;
  9859.     }
  9860.     RevealSelection();
  9861. }
  9862.  
  9863.  
  9864. // This is the old version 
  9865. void CEditBuffer::SelectTable(){
  9866.     VALIDATE_TREE(this);
  9867.     ClearPhantomInsertPoint();
  9868.     ClearMove();
  9869.     DoneTyping();
  9870.     CEditSelection selection;
  9871.     GetSelection(selection);
  9872.     CEditTableElement* pStartTable = selection.m_start.m_pElement->GetTableIgnoreSubdoc();
  9873.     CEditSelection startAll;
  9874.     if ( pStartTable ) {
  9875.         pStartTable->GetAll(startAll);
  9876.     }
  9877.     CEditTableElement* pEndTable = selection.m_end.m_pElement->GetTableIgnoreSubdoc();
  9878.     CEditSelection endAll;
  9879.     if ( pEndTable ) {
  9880.         pEndTable->GetAll(endAll);
  9881.     }
  9882.     if ( pStartTable ) {
  9883.         if ( pEndTable ) {
  9884.             // Both a start table and an end table.
  9885.             selection.m_start = startAll.m_start;
  9886.             selection.m_end = endAll.m_end;
  9887.         }
  9888.         else {
  9889.             // Just a start table.
  9890.             selection = startAll;
  9891.         }
  9892.     } else {
  9893.         if ( pEndTable ) {
  9894.             // Just an end table.
  9895.             selection = endAll;
  9896.         }
  9897.         else {
  9898.             // No table. Don't change selection.
  9899.             return;
  9900.         }
  9901.     }
  9902.  
  9903.     SetSelection(selection);
  9904. }
  9905.  
  9906. // Select the cell boundary of cell containing the current edit element
  9907. void CEditBuffer::SelectTableCell()
  9908. {
  9909.     CEditTableCellElement* pEdCell = m_pCurrent->GetTableCellIgnoreSubdoc();
  9910.     if( pEdCell )
  9911.     {
  9912.         LO_CellStruct *pLoCell = pEdCell->GetLoCell();
  9913.         if( pLoCell )
  9914.         {
  9915.             SelectCell(TRUE, pLoCell, pEdCell);
  9916.         }
  9917.     }
  9918. }
  9919.  
  9920.  
  9921. void CEditBuffer::BeginSelection( XP_Bool bExtend, XP_Bool bFromStart ){
  9922.     // Remove any existing table or cell selection
  9923.     ClearTableAndCellSelection();
  9924.  
  9925.     ClearMove(); // For side effect of flushing table formatting timer.
  9926.     if( !IsSelected() ){
  9927.         int iLayoutOffset;
  9928.         CEditLeafElement *pNext = 0;
  9929.         LO_Element* pLayoutElement;
  9930.  
  9931.         m_bSelecting = TRUE;
  9932.  
  9933.         FE_DestroyCaret( m_pContext );
  9934.  
  9935.         ClearPhantomInsertPoint();
  9936.  
  9937.         // If we're at the end of an element, and the next element isn't a break,
  9938.         // then move the cursor to the start of the next element.
  9939.         if ( m_pCurrent->GetLen() == m_iCurrentOffset
  9940.                 && (pNext = m_pCurrent->LeafInContainerAfter()) != 0
  9941.                 && ! m_pCurrent->CausesBreakAfter() && ! pNext->CausesBreakBefore()){
  9942.             XP_Bool good = pNext->GetLOElementAndOffset( 0, FALSE,
  9943.                     pLayoutElement, iLayoutOffset );
  9944.             if ( ! good ) {
  9945.                 XP_ASSERT(FALSE);
  9946.                 return;
  9947.             }
  9948.        }
  9949.         else {
  9950.             XP_Bool good = m_pCurrent->GetLOElementAndOffset( m_iCurrentOffset, m_bCurrentStickyAfter,
  9951.                         pLayoutElement, iLayoutOffset );
  9952.             if ( ! good ) {
  9953.                 XP_ASSERT(FALSE);
  9954.                 return;
  9955.             }
  9956.         }
  9957.  
  9958.         // if we're selecting forward, and
  9959.         // if we are before the space that cause the wrap, we should be
  9960.         //  starting selection from the line feed.
  9961.         if( ! bFromStart && pLayoutElement->type == LO_TEXT
  9962.                     && iLayoutOffset == pLayoutElement->lo_text.text_len )
  9963.         {
  9964.             if( pLayoutElement->lo_any.next &&
  9965.                 pLayoutElement->lo_any.next->type == LO_LINEFEED )
  9966.             {
  9967.                 pLayoutElement = pLayoutElement->lo_any.next;
  9968.                 iLayoutOffset = 0;
  9969.             }
  9970.             else
  9971.             {
  9972.                 // The layout element is shorter than we think it should
  9973.                 //  be.
  9974.                 // This appears to be an OK situation, now that we don't merge
  9975.                 // text elements. See bug 58763.
  9976.                 // XP_ASSERT( FALSE );
  9977.             }
  9978.         }
  9979.  
  9980.         XP_ASSERT( pLayoutElement );
  9981.  
  9982.         if ( bExtend ) {
  9983.             if ( ! LO_SelectElement( m_pContext, pLayoutElement,
  9984.                     iLayoutOffset, bFromStart ) ) {
  9985.                 SetCaret(); // If the selection moved off the end of the document, set the caret again.
  9986.             }
  9987.         }
  9988.         else {
  9989. #ifdef LAYERS
  9990.             LO_StartSelectionFromElement( m_pContext, pLayoutElement,
  9991.                     iLayoutOffset, NULL );
  9992. #else
  9993.             LO_StartSelectionFromElement( m_pContext, pLayoutElement,
  9994.                     iLayoutOffset);
  9995. #endif
  9996.         }
  9997.     }
  9998. }
  9999.  
  10000. void CEditBuffer::EndSelection(int32 /* x */, int32 /* y */){
  10001.     EndSelection();
  10002. }
  10003.  
  10004. void CEditBuffer::EndSelection( )
  10005. {
  10006.     // Clear the move just in case EndSelection is called out of order
  10007.     // (this seems to be happening in the WinFE when you click a lot.)
  10008.     // ClearMove has a side-effect of flushing the relayout timer.
  10009.     ClearMove();
  10010.  
  10011.     // Fix for the mouse move that does not move enough
  10012.     //   to show selection (kills caret)
  10013.  
  10014.     if( LO_IsSelectionStarted(m_pContext) ){
  10015.         SetCaret();
  10016.     }
  10017.     m_bSelecting = FALSE;
  10018.  
  10019.     // Make the active end visible
  10020.     RevealSelection();
  10021. }
  10022.  
  10023. void CEditBuffer::RevealSelection()
  10024. {
  10025.     if ( IsSelected() ) {
  10026.         CEditLeafElement* pStartElement;
  10027.         ElementOffset iStartOffset;
  10028.         CEditLeafElement* pEndElement;
  10029.         ElementOffset iEndOffset;
  10030.         XP_Bool bFromStart;
  10031.         GetSelection( pStartElement, iStartOffset, pEndElement, iEndOffset, bFromStart );
  10032.         if ( bFromStart ) {
  10033.             RevealPosition(pStartElement, iStartOffset, FALSE);
  10034.         }
  10035.         else {
  10036.             RevealPosition(pEndElement, iEndOffset, FALSE);
  10037.         }
  10038.     }
  10039.     else
  10040.     {
  10041.         if(m_pCurrent != NULL){
  10042.             RevealPosition(m_pCurrent, m_iCurrentOffset, m_bCurrentStickyAfter);
  10043.         }
  10044.     }
  10045. }
  10046.  
  10047. void CEditBuffer::SelectCurrentElement(){
  10048.     FE_DestroyCaret(m_pContext);
  10049.     SelectRegion( m_pCurrent, 0, m_pCurrent, 1,
  10050.         TRUE, TRUE );
  10051.     m_pCurrent = 0;
  10052.     m_iCurrentOffset = 0;
  10053. }
  10054.  
  10055.  
  10056. void CEditBuffer::ClearSelection( XP_Bool bResyncInsertPoint, XP_Bool bKeepLeft ){
  10057.  
  10058.     if( !bResyncInsertPoint ){
  10059. #ifdef LAYERS
  10060.        LO_StartSelectionFromElement( m_pContext, 0, 0, NULL);
  10061. #else
  10062.        LO_StartSelectionFromElement( m_pContext, 0, 0);
  10063. #endif
  10064.        return;
  10065.     }
  10066.  
  10067.     CEditSelection selection;
  10068.     GetSelection(selection);
  10069.     selection.ExcludeLastDocumentContainerEnd();
  10070.     SetInsertPoint(*selection.GetEdge( ! bKeepLeft ));
  10071. }
  10072.  
  10073.  
  10074. void CEditBuffer::GetInsertPoint( CEditLeafElement** ppEle, ElementOffset *pOffset, XP_Bool *pbStickyAfter){
  10075.     LO_Element *pStart, *pEnd;
  10076.     intn iStartPos, iEndPos;
  10077.     XP_Bool bFromStart;
  10078.  
  10079.     if( IsSelected() ){
  10080.         //
  10081.         // Grab the current selection.
  10082.         //
  10083.         LO_GetSelectionEndPoints( m_pContext,
  10084.                     &pStart, &iStartPos,
  10085.                     &pEnd, &iEndPos,
  10086.                     &bFromStart, 0 );
  10087.  
  10088.         XP_ASSERT( pStart );
  10089.         XP_ASSERT( pEnd );
  10090.  
  10091.         if( pStart == pEnd && iEndPos == iStartPos ){
  10092.             bFromStart = TRUE;
  10093.         }
  10094.  
  10095.         if( bFromStart ){
  10096.             *pbStickyAfter = iStartPos != 0;
  10097.             while( pStart && pStart->lo_any.edit_element == 0 ){
  10098.                 pStart = pStart->lo_any.next;
  10099.                 if( pStart &&pStart->type == LO_TEXT ){
  10100.                     iStartPos = pStart->lo_text.text_len;
  10101.                     if( iStartPos ) iStartPos--;
  10102.                 }
  10103.                 else {
  10104.                     iStartPos = 0;
  10105.                 }
  10106.             }
  10107.             if( pStart ){
  10108.                 *ppEle = pStart->lo_any.edit_element->Leaf(),
  10109.                 *pOffset = pStart->lo_any.edit_offset+iStartPos;
  10110.             }
  10111.             else{
  10112.                 // LTNOTE: NEED to implement end of document:
  10113.                 //Insert point is at end of document
  10114.                 //EndOfDocument( FALSE );
  10115.                 XP_ASSERT(FALSE);
  10116.             }
  10117.        }
  10118.         else {
  10119.             *pbStickyAfter = iEndPos != 0;
  10120.             while( pEnd && pEnd->lo_any.edit_element == 0 ){
  10121.                 pEnd = pEnd->lo_any.prev;
  10122.                 if( pEnd && pEnd->type == LO_TEXT ){
  10123.                     iEndPos = pEnd->lo_text.text_len;
  10124.                     if( iEndPos ) iEndPos--;
  10125.                 }
  10126.                 else {
  10127.                     iEndPos = 0;
  10128.                 }
  10129.             }
  10130.             if( pEnd ){
  10131.                 *ppEle = pEnd->lo_any.edit_element->Leaf(),
  10132.                 *pOffset = pEnd->lo_any.edit_offset+iEndPos;
  10133.             }
  10134.             else {
  10135.                 // LTNOTE: NEED to implement end of document:
  10136.                 //Insert point is at end of document.
  10137.                 //EndOfDocument( FALSE );
  10138.                 XP_ASSERT(FALSE);
  10139.             }
  10140.         }
  10141.  
  10142.     }
  10143.     else {
  10144.         *ppEle = m_pCurrent;
  10145.         *pOffset = m_iCurrentOffset;
  10146.         *pbStickyAfter = m_bCurrentStickyAfter;
  10147.     }
  10148.     if (ppEle && *ppEle && (*ppEle)->IsEndOfDocument()) {
  10149.         // End of document. Move back one edit position
  10150.         CEditInsertPoint ip(*ppEle, *pOffset, *pbStickyAfter);
  10151.         ip = ip.PreviousPosition();
  10152.         *ppEle = ip.m_pElement;
  10153.         *pOffset = ip.m_iPos;
  10154.         *pbStickyAfter = ip.m_bStickyAfter;
  10155.     }
  10156. }
  10157.  
  10158.  
  10159. //
  10160. // The 'PropertyPoint' is the beginning of a selection or current insert
  10161. //  point.
  10162. //
  10163. // Returns
  10164. //  TRUE if a single element is selected like an image or hrule
  10165. //  FALSE no selection or an extended selection.
  10166. //
  10167. XP_Bool CEditBuffer::GetPropertyPoint( CEditLeafElement**ppElement,
  10168.             ElementOffset* pOffset ){
  10169.     LO_Element *pStart, *pEnd;
  10170.     intn iStartPos, iEndPos;
  10171.     XP_Bool bFromStart;
  10172.     XP_Bool bSingleElementSelection;
  10173.  
  10174.  
  10175.     if( IsSelected() ){
  10176.         //
  10177.         // Grab the current selection.
  10178.         //
  10179.         LO_GetSelectionEndPoints( m_pContext,
  10180.                     &pStart, &iStartPos,
  10181.                     &pEnd, &iEndPos,
  10182.                     &bFromStart, &bSingleElementSelection );
  10183.  
  10184.         XP_ASSERT( pStart->lo_any.edit_element );
  10185.  
  10186.         //------------------------- Begin Removable code ------------------------
  10187.         //
  10188.         // LTNOTE:  We are doing this in GetSelectionEndpoints, but I
  10189.         //  don't think we need to be doing it.  If the above assert never
  10190.         //  happens remove this code
  10191.         //
  10192.         while( pStart && pStart->lo_any.edit_element == 0 ){
  10193.             pStart = pStart->lo_any.next;
  10194.             if( pStart &&pStart->type == LO_TEXT ){
  10195.                 iStartPos = pStart->lo_text.text_len;
  10196.                 if( iStartPos ) iStartPos--;
  10197.             }
  10198.             else {
  10199.                 iStartPos = 0;
  10200.             }
  10201.         }
  10202.         //------------------------- End Removable code ------------------------
  10203.  
  10204.         *ppElement = pStart->lo_any.edit_element->Leaf();
  10205.         *pOffset = pStart->lo_any.edit_offset+iStartPos;
  10206.         XP_ASSERT( *ppElement );
  10207.     }
  10208.     else {
  10209.         *ppElement = m_pCurrent;
  10210.         *pOffset = m_iCurrentOffset;
  10211.         bSingleElementSelection = FALSE;
  10212.     }
  10213.     return bSingleElementSelection;
  10214. }
  10215.  
  10216. void CEditBuffer::SelectRegion(CEditLeafElement *pBegin, intn iBeginPos,
  10217.             CEditLeafElement* pEnd, intn iEndPos, XP_Bool bFromStart, XP_Bool bForward  ){
  10218.     int iBeginLayoutOffset;
  10219.     LO_Element* pBeginLayoutElement;
  10220.     int iEndLayoutOffset;
  10221.     LO_Element* pEndLayoutElement;
  10222.  
  10223.     // Remove any existing table or cell selection???
  10224.     //ClearTableAndCellSelection();
  10225.  
  10226.     // If the start is an empty element, grab previous item.
  10227.     if( iBeginPos == 0 && pBegin->Leaf()->GetLen() == iBeginPos ){
  10228.         CEditLeafElement *pPrev = pBegin->PreviousLeafInContainer();
  10229.         if( pPrev ){
  10230.             pBegin = pPrev;
  10231.             iBeginPos = pPrev->GetLen();
  10232.         }
  10233.     }
  10234.  
  10235.     // if the end is a phantom insert point, grab the previous
  10236.     // item.
  10237.  
  10238.     if( iEndPos == 0 ){
  10239.         CEditLeafElement* pPrev = pEnd->PreviousLeafInContainer();
  10240.         if( pPrev ){
  10241.             pEnd = pPrev;
  10242.             iEndPos = pPrev->GetLen();
  10243.         }
  10244.     }
  10245.  
  10246.     XP_Bool startOK = pBegin->GetLOElementAndOffset( iBeginPos, FALSE,
  10247.             pBeginLayoutElement, iBeginLayoutOffset );
  10248.     XP_Bool endOK = pEnd->GetLOElementAndOffset( iEndPos, FALSE,
  10249.                pEndLayoutElement, iEndLayoutOffset );
  10250.  
  10251.     XP_ASSERT(startOK);
  10252.     XP_ASSERT(endOK);
  10253.     if (startOK && endOK ) {
  10254.         if ( ! IsSelected() ) {
  10255.             FE_DestroyCaret( m_pContext );
  10256.         }
  10257.         // Hack for end-of-document
  10258.         if ( iEndLayoutOffset < 0 ) iEndLayoutOffset = 0;
  10259.  
  10260.         /* LO_SelectRegion can call back to relayout the document under these circumstances:
  10261.          * If we've changed the size of an image, and we select or unselect the image, then
  10262.          * lo_HilightSelect calls CL_CompositeNow, which decides that the document bounds have
  10263.          * changed, and calls the front end to change the document dimensions.
  10264.          * This will eventually call EDT_RefreshLayout, which will
  10265.          * get confused and think that the document is not currently selected.
  10266.          */
  10267.         XP_Bool save_NoRelayout = m_bNoRelayout;
  10268.         m_bNoRelayout = TRUE;
  10269.  
  10270.         LO_SelectRegion( m_pContext, pBeginLayoutElement, iBeginLayoutOffset,
  10271.             pEndLayoutElement, iEndLayoutOffset, bFromStart, bForward );
  10272.  
  10273.         m_bNoRelayout = save_NoRelayout;
  10274.  
  10275. #ifdef DEBUG_EDITOR_LAYOUT
  10276.         // Verify that the selection is reversible.
  10277.         CEditSelection selection;
  10278.         GetSelection( selection );
  10279.         CEditInsertPoint a(pBegin, iBeginPos);
  10280.         CEditInsertPoint b(pEnd, iEndPos);
  10281.         CEditSelection original(a, b, bFromStart);
  10282.         XP_ASSERT(original == selection);
  10283. #endif
  10284.     }
  10285.  
  10286. //    EndSelection();
  10287. }
  10288.  
  10289. void CEditBuffer::SetSelection(CEditSelection& selection)
  10290. {
  10291.     if ( selection.IsInsertPoint() ) {
  10292.         SetInsertPoint(selection.m_start.m_pElement, selection.m_start.m_iPos, selection.m_start.m_bStickyAfter);
  10293.     }
  10294.     else {
  10295.         SelectRegion(selection.m_start.m_pElement, selection.m_start.m_iPos,
  10296.             selection.m_end.m_pElement, selection.m_end.m_iPos,
  10297.             selection.m_bFromStart);
  10298.     }
  10299. }
  10300.  
  10301. void CEditBuffer::SetInsertPoint(CEditInsertPoint& insertPoint) {
  10302.     CEditSelection selection(insertPoint, insertPoint, FALSE);
  10303.     SetSelection(selection);
  10304. }
  10305.  
  10306. void CEditBuffer::SetInsertPoint(CPersistentEditInsertPoint& insertPoint) {
  10307.     CEditInsertPoint p = PersistentToEphemeral(insertPoint);
  10308.     SetInsertPoint(p);
  10309. }
  10310.  
  10311. // Use data from supplied selection, or get from current selection if supplied is empty
  10312. void CEditBuffer::GetSelection( CEditSelection& selection, CEditLeafElement*& pStartElement, ElementOffset& iStartOffset,
  10313.                 CEditLeafElement*& pEndElement, ElementOffset& iEndOffset, XP_Bool& bFromStart )
  10314. {
  10315.     if( selection.m_start.m_pElement == 0 || selection.m_end.m_pElement == 0 )
  10316.     {
  10317.         // This version will get existing selection first
  10318.         GetSelection( pStartElement, iStartOffset, pEndElement, iEndOffset, bFromStart );
  10319.     }
  10320.     else
  10321.     {
  10322.         // Get data from supplied selection
  10323.         pStartElement = selection.m_start.m_pElement;
  10324.         iStartOffset = selection.m_start.m_iPos;
  10325.         pEndElement = selection.m_end.m_pElement;
  10326.         iEndOffset = selection.m_end.m_iPos;
  10327.         bFromStart = selection.m_bFromStart;
  10328.     }
  10329. }
  10330.  
  10331. void CEditBuffer::GetSelection( CEditLeafElement*& pStartElement, ElementOffset& iStartOffset,
  10332.                 CEditLeafElement*& pEndElement, ElementOffset& iEndOffset, XP_Bool& bFromStart ){
  10333.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  10334.     int16 win_csid = INTL_GetCSIWinCSID(c);
  10335.  
  10336.     //cmanske: IS THIS RISKY? WE ASSERT HERE IF WE DON'T RELAYOUT
  10337.     if( /*!m_bNoRelayout && */ m_bLayoutBackpointersDirty ) {
  10338.         XP_ASSERT(FALSE);
  10339.         return;
  10340.     }
  10341.     LO_Element *pStart, *pEnd;
  10342.     intn iStartPos, iEndPos;
  10343.  
  10344.  
  10345.     XP_ASSERT( IsSelected());
  10346.  
  10347.     //
  10348.     // Grab the current selection.
  10349.     //
  10350.     LO_GetSelectionEndPoints( m_pContext,
  10351.                 &pStart, &iStartPos,
  10352.                 &pEnd, &iEndPos,
  10353.                 &bFromStart, 0 );
  10354.  
  10355.      if ( ! pStart->lo_any.edit_element ) {
  10356.         XP_ASSERT(FALSE);
  10357.         // We should always be handed back an editable element.
  10358.         // LTNOTE: if we blow up here it is because we have searched to the end
  10359.         //  of document.
  10360.         while( pStart->lo_any.edit_element == 0 ){
  10361.             pStart = pStart->lo_any.next;
  10362.             iStartPos = 0;
  10363.         }
  10364.     }
  10365.  
  10366.     pStartElement = pStart->lo_any.edit_element->Leaf();
  10367.     iStartOffset = pStart->lo_any.edit_offset+iStartPos;
  10368.  
  10369.     if ( ! pEnd->lo_any.edit_element ) {
  10370.         XP_ASSERT(FALSE); // We should always be handed back an editable element.
  10371.         while( pEnd->lo_any.edit_element == 0 ){
  10372.             pEnd = pEnd->lo_any.prev;
  10373.             if( pEnd->type == LO_TEXT ){
  10374.                 iEndPos = pEnd->lo_text.text_len;
  10375.                 if( iEndPos ) iEndPos--;
  10376.             }
  10377.             else {
  10378.                 iEndPos = 0;
  10379.             }
  10380.         }
  10381.     }
  10382.  
  10383.     pEndElement = pEnd->lo_any.edit_element->Leaf();
  10384.     // jhp The simple way seems to work better.
  10385.     iEndOffset = pEnd->lo_any.edit_offset+iEndPos;
  10386.  
  10387.     if ( iEndOffset < 0 ) {
  10388.         iEndOffset = 0; // End-of-document flummery
  10389.     }
  10390.  
  10391.  
  10392.     // Normalize multibyte position,
  10393.     // make it always start from beginning of char, end of the beginning of next char
  10394.     if (pStart->type == LO_TEXT && pStart->lo_text.text
  10395.         && INTL_NthByteOfChar(win_csid, (char *) pStart->lo_text.text, iStartOffset+1) > 1) {
  10396.         XP_TRACE(("iStartOffset = %d which is wrong for multibyte", (int)iStartOffset));
  10397.         iStartOffset = INTL_NextCharIdx(win_csid, (unsigned char *) pStart->lo_text.text,iStartOffset);
  10398.     }
  10399.     if (pEnd->type == LO_TEXT && pEnd->lo_text.text
  10400.         && INTL_NthByteOfChar(win_csid, (char *) pEnd->lo_text.text, iEndOffset+1) > 1) {
  10401.         XP_TRACE(("iEndOffset = %d which is wrong for multibyte", (int)iEndOffset));
  10402.         iEndOffset = INTL_NextCharIdx(win_csid, (unsigned char *) pStart->lo_text.text,iEndOffset);
  10403.     }
  10404. }
  10405.  
  10406. void CEditBuffer::MakeSelectionEndPoints( CEditSelection& selection,
  10407.     CEditLeafElement*& pBegin, CEditLeafElement*& pEnd ){
  10408. #ifdef DEBUG
  10409.     // If the selection is busted, we're in big trouble.
  10410.     CPersistentEditSelection persel = EphemeralToPersistent(selection);
  10411.     XP_ASSERT ( persel.m_start.m_index <=  persel.m_end.m_index);
  10412. #endif
  10413.     pEnd = selection.m_end.m_pElement->Divide( selection.m_end.m_iPos )->Leaf();
  10414.     pBegin = selection.m_start.m_pElement->Divide( selection.m_start.m_iPos )->Leaf();
  10415.     
  10416.     //cmanske: Don't set this flag unless we really did divide into new elements
  10417.     if( pEnd != selection.m_end.m_pElement ||
  10418.         pBegin != selection.m_start.m_pElement )
  10419.     {
  10420.         m_bLayoutBackpointersDirty = TRUE;
  10421.     }
  10422. }
  10423.  
  10424. void CEditBuffer::MakeSelectionEndPoints( CEditLeafElement*& pBegin,
  10425.         CEditLeafElement*& pEnd ){
  10426.     CEditSelection selection;
  10427.     GetSelection(selection);
  10428.     MakeSelectionEndPoints(selection, pBegin, pEnd);
  10429. }
  10430.  
  10431. int CEditBuffer::Compare( CEditElement *p1, int i1Offset,
  10432.                            CEditElement *p2, int i2Offset ) {
  10433.  
  10434.     CEditPositionComparable *pPos1, *pPos2;
  10435.     int iRetVal;
  10436.  
  10437.     pPos1 = new CEditPositionComparable( p1, i1Offset);
  10438.     pPos2 = new CEditPositionComparable( p2, i2Offset);
  10439.  
  10440.     // find which one is really the beginning.
  10441.     iRetVal = pPos1->Compare( pPos2 );
  10442.     delete pPos1;
  10443.     delete pPos2;
  10444.     return iRetVal;
  10445. }
  10446.  
  10447. // Prevent recursive calls for autosaving
  10448. XP_Bool CEditBuffer::m_bAutoSaving = FALSE;
  10449.  
  10450. void CEditBuffer::AutoSaveCallback() {
  10451.     if ( m_bAutoSaving || IsBlocked() ) {
  10452.         XP_TRACE(("Auto Save ignored -- busy."));
  10453.         return;
  10454.     }
  10455.     if ( ! GetCommandLog()->IsDirty() ) {
  10456.         XP_TRACE(("Skipping AutoSave because we're not dirty."));
  10457.         return;
  10458.     }
  10459.     // Skip trying to save if we are already in the process of saving a file
  10460.     if( m_pContext->edit_saving_url ) {
  10461.         XP_TRACE(("Skipping AutoSave because we're not already saving a file."));
  10462.         return;
  10463.     }
  10464.     // This unnecessary on WinFE as FE_CheckAndAutoSaveDocument()
  10465.     // catches it.  But, avoids having to make the other FEs deal with it.
  10466.     if (m_pContext->type == MWContextMessageComposition) {
  10467.         XP_TRACE(("No auto-save for message composition"));
  10468.         return;
  10469.     }
  10470.     XP_TRACE(("Auto Save...."));
  10471.  
  10472.     m_bAutoSaving = TRUE;
  10473.     if( EDT_IS_NEW_DOCUMENT(m_pContext) ){
  10474.         // New document -- prompt front end to get filename and "save file as"
  10475.          if( !FE_CheckAndAutoSaveDocument(m_pContext) ){
  10476.             // User canceled the Autosave prompt, turn off autosave
  10477.             SetAutoSavePeriod(0);
  10478.          }
  10479.     } else {
  10480.         History_entry * hist_entry = SHIST_GetCurrent(&(m_pContext->hist));
  10481.  
  10482.         if(hist_entry && hist_entry->address)
  10483.         {
  10484.  
  10485.           if (NET_URL_Type(hist_entry->address) != FILE_TYPE_URL) {
  10486.             // Remote document -- prompt front end to get local filename and "save file as"
  10487.              if( !FE_CheckAndAutoSaveDocument(m_pContext) ){
  10488.                 // User canceled the Autosave prompt, turn off autosave
  10489.                 SetAutoSavePeriod(0);
  10490.              }
  10491.           }
  10492.           else {      
  10493.             // A local file, just save it.
  10494.             char *szLocalFile = NULL;
  10495.             if ( XP_ConvertUrlToLocalFile( hist_entry->address, &szLocalFile ) )
  10496.             {
  10497.                 char buf[300];
  10498.  
  10499.                 PR_snprintf(buf, sizeof(buf)-1, XP_GetString(XP_EDITOR_AUTO_SAVE), szLocalFile);
  10500.                 FE_Progress(m_pContext, buf);
  10501.  
  10502.  
  10503.               /////// ! Duplicating some code from EDT_SaveFile, but I didn't want to change
  10504.               /////// ! the interface for EDT_SaveFile(), but we still need to specify that we
  10505.               /////// ! are auto-saving.
  10506.               // Create Abstract file system to write to disk.
  10507.               char *pDestPathURL = edt_GetPathURL(hist_entry->address);
  10508.               XP_ASSERT(pDestPathURL);
  10509.               ITapeFileSystem *tapeFS = new CTapeFSFile(pDestPathURL,hist_entry->address);
  10510.               XP_ASSERT(tapeFS);
  10511.               XP_FREEIF(pDestPathURL);
  10512.               // tapeFS freed by CEditSaveObject.
  10513.  
  10514.               char **ppEmptyList = (char **)XP_ALLOC(sizeof(char *));
  10515.               XP_ASSERT(ppEmptyList);
  10516.               ppEmptyList[0] = NULL;
  10517.   
  10518.               // go to the newly saved document.
  10519.               SaveFile( ED_FINISHED_GOTO_NEW, hist_entry->address, tapeFS, 
  10520.                               FALSE, // bSaveAs 
  10521.                               FALSE, FALSE, // don't need to move images or adjust
  10522.                                             // links because document isn't moving.
  10523.                               TRUE, // is AutoSave
  10524.                               ppEmptyList); // Don't send along any files, even LOCALDATA.
  10525.             }
  10526.             if (szLocalFile){
  10527.                 XP_FREE(szLocalFile);
  10528.             }
  10529.           } // local file
  10530.         }
  10531.     } // not a new document
  10532.  
  10533.     m_bAutoSaving = FALSE;
  10534. }
  10535.  
  10536. void CEditBuffer::FileFetchComplete(ED_FileError status) {
  10537.     m_status = status;
  10538.     FE_Progress(m_pContext, NULL);
  10539.  
  10540.     // hardts.  Moved from CEditBuffer::WriteToFile()
  10541.     if (status == ED_ERROR_NONE) {
  10542.         m_autoSaveTimer.Restart();
  10543.     }
  10544. }
  10545.  
  10546. void CEditBuffer::SetAutoSavePeriod(int32 minutes) {
  10547.     m_autoSaveTimer.SetPeriod(minutes);
  10548. }
  10549.  
  10550. int32 CEditBuffer::GetAutoSavePeriod() {
  10551.     return m_autoSaveTimer.GetPeriod();
  10552. }
  10553.  
  10554. void CEditBuffer::SuspendAutoSave(){
  10555.     m_autoSaveTimer.Suspend();
  10556. }
  10557.  
  10558. void CEditBuffer::ResumeAutoSave(){
  10559.     m_autoSaveTimer.Resume();
  10560. }
  10561.  
  10562. void CEditBuffer::ClearMove(XP_Bool bFlushRelayout){
  10563.     if ( bFlushRelayout ) {
  10564.         m_relayoutTimer.Flush();
  10565.     }
  10566.     m_iDesiredX = -1;
  10567. }
  10568.  
  10569.  
  10570. void CEditBuffer::SetHREFSelection( ED_LinkId id ){
  10571.     VALIDATE_TREE(this);
  10572.     CEditLeafElement *pCurrent, *pEnd, *pBegin, *pNext;
  10573.  
  10574.     //
  10575.     // Guarantee that the text blocks of the beginning and end of selection
  10576.     //  are atomic.
  10577.     //
  10578.     MakeSelectionEndPoints( pBegin, pEnd );
  10579.     pCurrent = pBegin;
  10580.  
  10581.     while( pCurrent != pEnd ){
  10582.         pNext = pCurrent->NextLeafAll();
  10583.         pCurrent->Leaf()->SetHREF( id );
  10584.         pCurrent = pNext;
  10585.     }
  10586.  
  10587.     CEditSelection tmp(pBegin, 0, pEnd, 0);
  10588.     RepairAndSet(tmp);
  10589. }
  10590.  
  10591. EDT_ClipboardResult CEditBuffer::DeleteSelection(CEditSelection& selection, XP_Bool bCopyAppendAttributes){
  10592.     SetSelection(selection);
  10593.     return DeleteSelection(bCopyAppendAttributes);
  10594. }
  10595.  
  10596. EDT_ClipboardResult CEditBuffer::DeleteSelection(XP_Bool bCopyAppendAttributes){
  10597.     VALIDATE_TREE(this);
  10598.     EDT_ClipboardResult result = CanCut(TRUE);
  10599.     if ( result == EDT_COP_OK ){
  10600.         if ( IsSelected() ) {
  10601.             CEditLeafElement *pBegin;
  10602.             CEditLeafElement *pEnd;
  10603.  
  10604.             CEditSelection selection;
  10605.             GetSelection(selection);
  10606.             if ( selection.ContainsLastDocumentContainerEnd() ) {
  10607.                 selection.ExcludeLastDocumentContainerEnd();
  10608.                 SetSelection(selection);
  10609.             }
  10610.             if ( selection.IsInsertPoint() ) {
  10611.                 return result;
  10612.             }
  10613.             MakeSelectionEndPoints( selection, pBegin, pEnd );
  10614.             DeleteBetweenPoints( pBegin, pEnd, bCopyAppendAttributes );
  10615.         }
  10616.     }
  10617.     return result;
  10618. }
  10619.  
  10620. CPersistentEditSelection CEditBuffer::GetEffectiveDeleteSelection(){
  10621.     // If the selection includes the end of the document, then the effective selection is
  10622.     // less than that.
  10623.     CEditSelection selection;
  10624.     GetSelection(selection);
  10625.     selection.ExcludeLastDocumentContainerEnd();
  10626.     selection.ExpandToIncludeFragileSpaces();
  10627.     return EphemeralToPersistent(selection);
  10628. }
  10629.  
  10630. //
  10631. // Get comparable positions to the
  10632. //
  10633. void CEditBuffer::DeleteBetweenPoints( CEditLeafElement* pBegin, CEditLeafElement* pEnd,
  10634.      XP_Bool bCopyAppendAttributes){
  10635.     CEditLeafElement *pCurrent;
  10636.     CEditLeafElement *pNext;
  10637.  
  10638.     pCurrent = pBegin;
  10639.  
  10640.     XP_ASSERT( pEnd != 0 );
  10641.  
  10642.     CEditLeafElement* pPrev = pCurrent->PreviousLeafInContainer();
  10643.     CEditElement* pCommonAncestor = pBegin->GetCommonAncestor(pEnd);
  10644.     CEditElement* pContainer = pCurrent->FindContainer();
  10645.     CEditElement* pCurrentContainer;
  10646.     while( pCurrent != pEnd ){
  10647.         pNext = pCurrent->NextLeafAll();
  10648.  
  10649.         // if we've entered a new container, merge it.
  10650.         pCurrentContainer = pCurrent->FindContainer();
  10651.  
  10652.         // DeleteElement removes empty paragraphs so we don't have to do the merges.
  10653.         //
  10654.         //if( pContainer != pCurrentContainer ){
  10655.         //    pContainer->Merge( pCurrentContainer );
  10656.         //}
  10657.  
  10658. #ifdef DEBUG
  10659.          m_pRoot->ValidateTree();
  10660. #endif
  10661.  
  10662.         if( pCurrent->DeleteElement( pContainer )) {
  10663.             pContainer = pNext->FindContainer();
  10664.         }
  10665. #ifdef DEBUG
  10666.         m_pRoot->ValidateTree();
  10667. #endif
  10668.         pCurrent = pNext;
  10669.     }
  10670.  
  10671.     //
  10672.     // if the selection spans partial paragraphs, merge the remaining paragraphs
  10673.     //
  10674.     if( pPrev && pPrev->FindContainer() != pEnd->FindContainer() ){
  10675.         pPrev->FindContainer()->Merge( pEnd->FindContainer(), bCopyAppendAttributes );
  10676.     }
  10677.  
  10678.     m_pCurrent = pEnd->PreviousLeafInContainer();
  10679.     if( m_pCurrent != 0 ){
  10680.         m_iCurrentOffset = m_pCurrent->GetLen();
  10681.     }
  10682.     else {
  10683.         m_pCurrent = pEnd;
  10684.         m_iCurrentOffset = 0;
  10685.     }
  10686.  
  10687.     //FixupSpace();
  10688.  
  10689.     // probably needs to be the common ancestor.
  10690.     ClearSelection(FALSE);
  10691.     CEditInsertPoint tmp(pEnd,0);
  10692.     CPersistentEditInsertPoint end2 = EphemeralToPersistent(tmp);
  10693.     Reduce( pCommonAncestor );
  10694.     SetCaret();
  10695.     if ( pPrev && pPrev->FindContainer() != pContainer ) {
  10696.         Reduce( pPrev->FindContainer() );
  10697.     }
  10698.     CEditInsertPoint end3 = PersistentToEphemeral(end2);
  10699.     Relayout( pContainer, 0,  end3.m_pElement);
  10700. }
  10701.  
  10702. EDT_ClipboardResult CEditBuffer::PasteQuoteBegin( XP_Bool bHTML ){
  10703.     if ( m_bPasteQuoteMode != FALSE ) {
  10704.         XP_ASSERT(FALSE);
  10705.     }
  10706.  
  10707.     EDT_ClipboardResult result = CanPaste(TRUE);
  10708.     if ( result != EDT_COP_OK ) return result;
  10709.  
  10710.     m_bPasteQuoteMode = TRUE;
  10711.     BeginBatchChanges(kPasteTextCommandID);
  10712.     DeleteSelection();
  10713.     m_bPasteHTML = bHTML;
  10714.     if ( ! m_pPasteHTMLModeText ) {
  10715.         m_pPasteHTMLModeText = new CStreamOutMemory();
  10716.         m_pPasteTranscoder = NULL;
  10717.     }
  10718.     m_bAbortPasteQuote = FALSE;
  10719.     return result;
  10720. }
  10721.  
  10722. EDT_ClipboardResult CEditBuffer::PasteQuote(char* pText){
  10723.     // XP_ASSERT(FALSE); // Should convert to new code.
  10724.     return PasteQuoteINTL(pText, GetRAMCharSetID());
  10725. }
  10726.  
  10727. EDT_ClipboardResult CEditBuffer::PasteQuoteINTL(char* pText, int16 csid){
  10728.     EDT_ClipboardResult result = EDT_COP_OK;
  10729.     XP_Bool bBackwardsCompatible = FALSE;
  10730.     if ( m_bPasteQuoteMode != TRUE ) {
  10731.         //  XP_ASSERT(FALSE); // Mail guys forgot to call PasteQuoteBegin()
  10732.         bBackwardsCompatible = TRUE;
  10733.         result = PasteQuoteBegin(FALSE);
  10734.         if ( result != EDT_COP_OK ) {
  10735.             return result;
  10736.         }
  10737.     }
  10738.  
  10739.     // Stop if we are in "abort paste quote" mode
  10740.     if (m_bAbortPasteQuote)
  10741.         return result;
  10742.  
  10743.     // If the text is too long, abort paste operation.
  10744.     // An error message will be displayed in PasteQuoteEnd().
  10745.     if (XP_STRLEN(pText) >= MAX_PASTE_SIZE) {
  10746.         m_bAbortPasteQuote = TRUE;
  10747.     }
  10748.     else if ( m_bPasteHTML ) {
  10749.         // Check if we need to set up a new transcoder.
  10750.         if ( ! m_pPasteTranscoder || csid != m_pPasteTranscoder->GetOldCSID() ) {
  10751.             if ( m_pPasteTranscoder ) {
  10752.                 m_pPasteTranscoder->ForgetStream();
  10753.                 delete m_pPasteTranscoder;
  10754.             }
  10755.             m_pPasteTranscoder = new CConvertCSIDStreamOut( csid, GetRAMCharSetID(), m_pPasteHTMLModeText);
  10756.         }
  10757.         m_pPasteTranscoder->Write(pText, XP_STRLEN(pText));
  10758.     }
  10759.     else {
  10760.         result = PasteText(pText, TRUE, FALSE, csid, TRUE,TRUE);
  10761.     }
  10762.  
  10763.     if ( bBackwardsCompatible ) {
  10764.         result = PasteQuoteEnd();
  10765.     }
  10766.  
  10767.     return result;
  10768. }
  10769.  
  10770. EDT_ClipboardResult CEditBuffer::PasteQuoteEnd(){
  10771.     EDT_ClipboardResult result = EDT_COP_OK;
  10772.     XP_Bool bThisIsDeleted = FALSE;
  10773.     CEditCommandLog* pCommandLog = GetCommandLog();
  10774.     if ( m_bPasteQuoteMode != TRUE ) {
  10775.         XP_ASSERT(FALSE);
  10776.         return result;
  10777.     }
  10778.     if ( m_bPasteHTML && m_pPasteHTMLModeText ) {
  10779.         // Paste as HTML. The other 1/2 of this logic
  10780.         // is in CheckAndPrintComment2
  10781.         result = CanPaste(TRUE);
  10782.         // Save to delete after we've deleted "this".
  10783.         CStreamOutMemory* pPasteText = m_pPasteHTMLModeText;
  10784.         CConvertCSIDStreamOut* pPasteTextTranscoder = m_pPasteTranscoder;
  10785.  
  10786.         // Display error message if aborting paste operation.
  10787.         if (m_bAbortPasteQuote) {
  10788.             char* msg = XP_GetString(XP_EDT_MSG_CANNOT_PASTE);
  10789.             FE_Alert(m_pContext, msg);
  10790.         }
  10791.  
  10792.         if ( result == EDT_COP_OK && !m_bAbortPasteQuote) {
  10793.             if( IsSelected() ){
  10794.                 DeleteSelection();
  10795.             }
  10796.             m_bPasteHTMLWhenSavingDocument = TRUE;
  10797.             XP_HUGE_CHAR_PTR pData;
  10798.             WriteToBuffer(&pData, TRUE);
  10799.             m_bPasteHTMLWhenSavingDocument = FALSE;
  10800.             m_pPasteHTMLModeText = 0;  // so our destructor doesn't try to clean this up.
  10801.             m_pPasteTranscoder = 0;
  10802.             ReadFromBuffer(pData); // This deletes "this".
  10803.             bThisIsDeleted = TRUE;
  10804.             XP_HUGE_FREE(pData);
  10805.         }
  10806.         XP_HUGE_FREE(pPasteText->GetText());
  10807.         delete pPasteTextTranscoder; // Automaticly deletes pPasteText
  10808.         if ( ! bThisIsDeleted){
  10809.             m_pPasteHTMLModeText = 0;
  10810.             m_pPasteTranscoder = 0;
  10811.         }
  10812.     }
  10813.     pCommandLog->EndBatchChanges();
  10814.     if ( ! bThisIsDeleted ) {
  10815.         m_bPasteQuoteMode = FALSE;
  10816.     }
  10817.     return result;
  10818. }
  10819.  
  10820. void CEditBuffer::PasteHTMLHook(CPrintState* pPrintState){
  10821.     if (m_bPasteHTMLWhenSavingDocument ) {
  10822.         // Insert the paste text in-line, as we're writing out the document.
  10823.         XP_HUGE_CHAR_PTR pSource = m_pPasteHTMLModeText->GetText();
  10824.         int32 len = m_pPasteHTMLModeText->GetLen();
  10825.         const int32 kChunkSize = 4096; // Must be smaller than 64K for Win16.
  10826.         char* buf = (char*) XP_ALLOC(kChunkSize);
  10827.         while ( len > 0 ) {
  10828.             int32 chunk = len;
  10829.             if ( chunk > kChunkSize ){
  10830.                 chunk = kChunkSize;
  10831.             }
  10832.             len -= chunk;
  10833.             // Copy from the huge pointer to the regular pointer
  10834.             char* p = buf;
  10835.             for(int32 i = 0; i < chunk; i++ ){
  10836.                 *p++ = *pSource++;
  10837.             }
  10838.             pPrintState->m_pOut->Write(buf, chunk);
  10839.         }
  10840.         XP_FREE(buf);
  10841.     }
  10842. }
  10843.  
  10844. EDT_ClipboardResult CEditBuffer::PasteText( char *pText, XP_Bool bMailQuote, XP_Bool bIsContinueTyping, int16 csid, XP_Bool bRelayout , XP_Bool bReduce){
  10845.     int16 newcsid = GetRAMCharSetID();
  10846.     if ( csid != newcsid) {
  10847.         // Need to transcode pText to the current
  10848.         CStreamOutMemory memory;
  10849.         CConvertCSIDStreamOut transcoder( csid, newcsid, &memory);
  10850.         transcoder.Write(pText, XP_STRLEN(pText));
  10851.         XP_HUGE_CHAR_PTR pTranscoded = memory.GetText();
  10852.         EDT_ClipboardResult result = PasteText((char*) pTranscoded, bMailQuote, bIsContinueTyping, bRelayout, bReduce);
  10853.         XP_HUGE_FREE(pTranscoded);
  10854.         transcoder.ForgetStream();
  10855.         return result;
  10856.     }
  10857.     else {
  10858.         return PasteText(pText, bMailQuote, bIsContinueTyping, bRelayout, bReduce);
  10859.     }
  10860. }
  10861.  
  10862. //
  10863. // LTNOTE: this routine needs to be broken into PasteText and PasteFormattedText
  10864. //  It didn't happen because we were too close to ship.
  10865. //
  10866. EDT_ClipboardResult CEditBuffer::PasteText( char *pText, XP_Bool bMailQuote, XP_Bool bIsContinueTyping, XP_Bool bRelayout, XP_Bool bReduce)
  10867. {
  10868.     // If the text is too long, display alert message and return.
  10869.     if (XP_STRLEN(pText) >= MAX_PASTE_SIZE) {
  10870.         char* msg = XP_GetString(XP_EDT_MSG_CANNOT_PASTE);
  10871.         FE_Alert(m_pContext, msg);
  10872.         return EDT_COP_OK;
  10873.     }
  10874.  
  10875.     VALIDATE_TREE(this);
  10876.     SUPPRESS_PHANTOMINSERTPOINTCHECK(this);
  10877.  
  10878.     if ( !bIsContinueTyping ) {
  10879.         EDT_ClipboardResult result = CanPaste(TRUE);
  10880.         if ( result != EDT_COP_OK ) return result;
  10881.     }
  10882.  
  10883.     //
  10884.     // No one likes the other way of quoting, always use mailquote
  10885.     //
  10886.     bMailQuote = TRUE;
  10887.  
  10888.     m_bNoRelayout = TRUE;
  10889.     int iCharsOnLine = 0;
  10890.  
  10891.     if( IsSelected() ){
  10892.         DeleteSelection();
  10893.     }
  10894.     FixupInsertPoint();
  10895.  
  10896.     // If the pasted text starts with returns, the start element can end up
  10897.     // being moved down the document as paragraphs are inserted above it.
  10898.     // So we hold onto the start as a persistent insert point.
  10899.  
  10900.     CPersistentEditInsertPoint persistentStart;
  10901.     GetInsertPoint(persistentStart);
  10902.  
  10903.     // If we're in preformatted text, every return is a break.
  10904.     // If we're in normal text, single returns are spaces,
  10905.     // double returns are paragraph marks.
  10906.  
  10907.     //XP_Bool bInFormattedText = m_pCurrent->FindContainer()->GetType() == P_PREFORMAT;
  10908.     XP_Bool bInFormattedText = m_pCurrent->InFormattedText();
  10909.  
  10910.     XP_Bool bLastCharWasReturn = FALSE;
  10911.  
  10912.     while( *pText ){
  10913.         XP_Bool bReturn = FALSE;
  10914.         if( *pText == 0x0d ){
  10915.             if( *(pText+1) == 0xa ){
  10916.                 pText++;
  10917.             }
  10918.             bReturn = TRUE;
  10919.         }
  10920.         else if ( *pText == 0xa ){
  10921.             bReturn = TRUE;
  10922.         }
  10923.  
  10924.         if( bReturn ){
  10925.             if ( bInFormattedText ) {
  10926.                 InsertBreak( ED_BREAK_NORMAL, bIsContinueTyping );
  10927.                 iCharsOnLine = 0;
  10928.             } 
  10929.             else if ( bMailQuote ){
  10930.                 ReturnKey( bIsContinueTyping );
  10931. #ifdef EDT_DDT
  10932.                 MorphContainer( P_NSDT);
  10933. #else
  10934.                 MorphContainer( P_DESC_TITLE );
  10935. #endif
  10936.                 iCharsOnLine = 0;
  10937.             }
  10938.             else {
  10939.                 if ( bLastCharWasReturn ){
  10940.                     ReturnKey( bIsContinueTyping );
  10941.                     iCharsOnLine = 0;
  10942.                     bLastCharWasReturn = FALSE;
  10943.                 }
  10944.                 else {
  10945.                     // Remember this return for later. It will
  10946.                     // become either a space or a return.
  10947.                     bLastCharWasReturn = TRUE;
  10948.                 }
  10949.             }
  10950.         }
  10951.         else {
  10952.             if ( bLastCharWasReturn ) {
  10953.                 InsertChar( ' ', bIsContinueTyping );
  10954.                 bLastCharWasReturn = FALSE;
  10955.             }
  10956.  
  10957.             if ( *pText == '\t' ){
  10958.                 if ( bInFormattedText ) {
  10959.                     do {
  10960.                         InsertChar( ' ', bIsContinueTyping );
  10961.                         iCharsOnLine++;
  10962.                     } while( iCharsOnLine % DEF_TAB_WIDTH != 0 );
  10963.                 }
  10964.                 else {
  10965.                     InsertChar( ' ', bIsContinueTyping );
  10966.                     iCharsOnLine++;
  10967.                 }
  10968.             }
  10969.             else {
  10970.                 // Insert all the characters up to the next return or tab
  10971.                 int32 value = 0;
  10972.                 char old = 0;
  10973.                 while((old = pText[value]) != '\0'){
  10974.                     if ( old == '\t' || old == '\015' || old == '\012' ){
  10975.                         break;
  10976.                     }
  10977.                     value++;
  10978.                 }
  10979.                 if ( value > 0 ) {
  10980.                     pText[value] = '\0';
  10981.                     InsertChars( pText, bIsContinueTyping, bReduce); //adding this flag to stop the insertchars from reducing the tree. 
  10982.                     pText[value] = old;
  10983.                     pText += value - 1; /* -1 Because pText is incremented by one at the end of the loop. */
  10984.                     iCharsOnLine += value;
  10985.                 }
  10986.             }
  10987.             bLastCharWasReturn = FALSE;
  10988.         }
  10989.         pText++;
  10990.     }
  10991.  
  10992.     if ( bLastCharWasReturn ) {
  10993.         InsertChar( ' ', bIsContinueTyping );
  10994.     }
  10995.  
  10996.  
  10997.     if (bReduce)//dont reduce if bReduce is false
  10998.         Reduce(m_pRoot);
  10999.  
  11000.     CEditInsertPoint start = PersistentToEphemeral(persistentStart);
  11001.  
  11002.     m_bNoRelayout = FALSE;
  11003.  
  11004.     // We now suppress layout when pasting into tables:
  11005.     //  entire table will be layed out after all cell pasting is finished
  11006.     if( bRelayout )
  11007.     {
  11008.         // At one time pEnd was managed so that it pointed beyond the pasted text.
  11009.         // Aparently that code was lost in the mists of time. Now we just hope that
  11010.         // the next element is far enough. (Lloyd and I think it has to be.)
  11011.         CEditElement* pEnd = m_pCurrent->NextLeaf();
  11012.         // if the end of the paragraph ends in a break, we need to relayout further.
  11013.         if( pEnd && pEnd->IsBreak()
  11014.             || ( pEnd->GetNextSibling() && pEnd->GetNextSibling()->IsBreak() ) ){
  11015.             pEnd = m_pRoot->GetLastMostChild(); // This is almost always too far, but it is safe.
  11016.         }
  11017.  
  11018.         Reflow( start.m_pElement, start.m_iPos, pEnd );
  11019.         //
  11020.         if( bMailQuote ){
  11021.             CEditInsertPoint start = PersistentToEphemeral(persistentStart);
  11022.             //SetInsertPoint( start );
  11023.         }
  11024.     }
  11025.  
  11026.     return EDT_COP_OK;
  11027. }
  11028.  
  11029. EDT_ClipboardResult CEditBuffer::PasteHTML( char *pBuffer, XP_Bool bUndoRedo ){
  11030.  
  11031.     CStreamInMemory stream(pBuffer);
  11032.     return PasteHTML(stream, bUndoRedo);
  11033. }
  11034.  
  11035. EDT_ClipboardResult CEditBuffer::PasteHTML( IStreamIn& stream, XP_Bool /* bUndoRedo */ ){
  11036.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  11037.     VALIDATE_TREE(this);
  11038.     EDT_ClipboardResult result = CanPaste(TRUE);
  11039.     if ( result != EDT_COP_OK ) return result;
  11040.  
  11041.     // First thing in the buffer is some version info
  11042.     
  11043.     int32 signature = stream.ReadInt();
  11044.     if ( signature != GetClipboardSignature() ) {
  11045.         FE_Alert(m_pContext,XP_GetString(XP_EDT_BAD_CLIPBOARD_VERSION));
  11046.         return EDT_COP_CLIPBOARD_BAD;
  11047.     }
  11048.     int32 version = stream.ReadInt();
  11049.     if ( version != GetClipboardVersion() ) {
  11050.         FE_Alert(m_pContext,XP_GetString(XP_EDT_BAD_CLIPBOARD_VERSION));
  11051.         return EDT_COP_CLIPBOARD_BAD;
  11052.     }
  11053.     int32 clipcsid = stream.ReadInt();
  11054.     if ( clipcsid != INTL_GetCSIWinCSID(c) ) {
  11055.         /* In the future we could transcode. */
  11056.         FE_Alert(m_pContext,XP_GetString(XP_EDT_BAD_CLIPBOARD_ENCODING));
  11057.         return EDT_COP_CLIPBOARD_BAD;
  11058.     }
  11059.     // This is non-zero when stream contains just a table
  11060.     // It tells us whether it is an entire table or row(s), 
  11061.     //      column(s), or "loose cells" copying
  11062.     EEditCopyType iCopyType = (EEditCopyType)stream.ReadInt();
  11063.  
  11064.     // Pasting cells into existing table is entirely different
  11065.     //  if pasting rows, columns, or cells
  11066.     // Note: type = eEditCopyTable used for entire table,
  11067.     //  allowing an embeded table in existing table
  11068.     if( iCopyType > eCopyTable && IsInsertPointInTableCell() )
  11069.     {
  11070.         return PasteCellsIntoTable(stream, iCopyType);
  11071.     }
  11072.     
  11073.     // Don't relayout until all is pasted
  11074.     m_bNoRelayout = TRUE;
  11075.  
  11076.     // "Normal" HTML pasting at current caret location
  11077.     if( IsSelected() )
  11078.     {
  11079.         DeleteSelection();
  11080.     }
  11081. #ifdef DEBUG
  11082.     m_pRoot->ValidateTree();
  11083. #endif
  11084.     ClearPhantomInsertPoint();
  11085.     FixupInsertPoint();
  11086.  
  11087.     CEditElement* pStart = m_pCurrent;
  11088.     int iStartOffset = m_iCurrentOffset;
  11089.     CEditLeafElement* pRight = m_pCurrent->Divide(m_iCurrentOffset)->Leaf();
  11090.     XP_ASSERT(pRight->FindContainer());
  11091.     XP_Bool bAtStartOfParagraph = pRight->PreviousLeafInContainer() == NULL;
  11092.     CEditLeafElement* pLeft = pRight->PreviousLeaf(); // Will be NULL at start of document
  11093.  
  11094.     // The first thing in the buffer is a flag that tells us if we need to merge the end.
  11095.     int32 bMergeEnd = stream.ReadInt();
  11096.     // The buffer has zero or more elements. (The case where there's more than one is
  11097.     // the one where there are multiple paragraphs.)
  11098.     CEditElement* pFirstInserted = NULL;
  11099.     CEditElement* pLastInserted = NULL;
  11100.  
  11101.     {
  11102.         CEditElement* pElement;
  11103.         while ( NULL != (pElement = CEditElement::StreamCtor(&stream, this)) )
  11104.         {
  11105.             // We should always begin with a container
  11106.             if ( pElement->IsLeaf() )
  11107.             {
  11108.                 XP_ASSERT(FALSE);
  11109.                 delete pElement;
  11110.                 continue;
  11111.             }
  11112. #ifdef DEBUG
  11113.             pElement->ValidateTree();
  11114. #endif
  11115.             if ( ! pFirstInserted ) 
  11116.             {
  11117.                 pFirstInserted = pElement;
  11118.                 if ( ! bAtStartOfParagraph )
  11119.                 {
  11120.     XP_ASSERT(pRight->FindContainer());
  11121.                     InternalReturnKey(FALSE);
  11122.     XP_ASSERT(pRight->FindContainer());
  11123.                }
  11124.             }
  11125.             pLastInserted = pElement;
  11126.             pElement->InsertBefore(pRight->FindContainer());
  11127.  
  11128.             // Table seems to be a special case
  11129.             // If it is inserted NOT at beginning of existing paragraph, contents of
  11130.             //  first cell are moved into that paragraph (see below)
  11131.             //cmanske: KLUDGE - THIS WORKS, BUT IS IT REALLY OK???
  11132.             // (This BUG  exists in 4.0x)
  11133.             if( pElement->IsTable() )
  11134.             {
  11135.                 bAtStartOfParagraph = TRUE;
  11136.             }
  11137.  
  11138. #ifdef DEBUG
  11139.             m_pRoot->ValidateTree();
  11140. #endif
  11141.         }
  11142.     }
  11143.  
  11144. #ifdef DEBUG
  11145.     m_pRoot->ValidateTree();
  11146. #endif
  11147.  
  11148.     if ( pFirstInserted )
  11149.     {
  11150.         CEditElement* pLast = pLastInserted->GetLastMostChild()->NextLeafAll();
  11151.         // Get the children early, because if we paste a single container,
  11152.         // it will be deleted when we merge the left edge
  11153.         CEditLeafElement* pFirstMostNewChild = pFirstInserted->GetFirstMostChild()->Leaf();
  11154.         CEditLeafElement* pLastMostNewChild = pLastInserted->GetLastMostChild()->Leaf();
  11155.         m_pCurrent = pLastMostNewChild;
  11156.         m_iCurrentOffset = pLastMostNewChild->GetLen();
  11157.         ClearPhantomInsertPoint();
  11158.         if ( ! bAtStartOfParagraph )
  11159.         {
  11160.             if ( pLeft )
  11161.             {
  11162.                 CEditContainerElement* pLeftContainer = pLeft->FindContainer();
  11163.                 CEditContainerElement* pFirstNewContainer =
  11164.                     pFirstMostNewChild->FindContainer();
  11165.                 if ( pLeftContainer != pFirstNewContainer )
  11166.                 {
  11167.                     pLeftContainer->Merge(pFirstNewContainer);
  11168.                     // Now deleted in Merge delete pFirstNewContainer;
  11169.                     FixupInsertPoint();
  11170.                 }
  11171.             }
  11172.         }
  11173.         else {
  11174.             // The insert went into the container before us. Adjust the start.
  11175.             pStart = pFirstInserted->GetFirstMostChild();
  11176.             iStartOffset = 0;
  11177.         }
  11178.  
  11179.         if ( bMergeEnd )
  11180.         {
  11181.             CEditContainerElement* pRightContainer = pRight->FindContainer();
  11182.             CEditContainerElement* pLastNewContainer = pLastMostNewChild->FindContainer();
  11183.             if ( pRightContainer != pLastNewContainer )
  11184.             {
  11185.                 pLastNewContainer->Merge(pRightContainer);
  11186.                 // Now deleted in Merge delete pRightContainer;
  11187.                 FixupInsertPoint();
  11188.             }
  11189.         }
  11190.         else {
  11191.             // Move to beginning of next container
  11192.             m_pCurrent = pLastMostNewChild->NextContainer()->GetFirstMostChild()->Leaf();
  11193.             m_iCurrentOffset = 0;
  11194.         }
  11195.  
  11196.         // Have to FinishedLoad after merging because FinishedLoad is eager to
  11197.         // remove spaces from text element at the end of containers. If we let
  11198.         // FinishedLoad run earlier, we couldn't paste text that ended in white space.
  11199.  
  11200.         m_pRoot->FinishedLoad(this);
  11201.  
  11202. #ifdef DEBUG
  11203.         m_pRoot->ValidateTree();
  11204. #endif
  11205.  
  11206.         m_bNoRelayout = FALSE;
  11207.         //FixupSpace( );
  11208.         // We need to reduce before we relayout because otherwise
  11209.         // null insert points mess up the setting of end-of-paragraph
  11210.         // marks.
  11211.         CEditInsertPoint tmp(pLast,0);
  11212.         CPersistentEditInsertPoint end2 = EphemeralToPersistent(tmp);
  11213.         Reduce( m_pRoot );
  11214.         CEditInsertPoint end3 = PersistentToEphemeral(end2);
  11215.         Relayout( pStart, iStartOffset, end3.m_pElement );
  11216.     }
  11217.     else {
  11218.         m_bNoRelayout = FALSE;
  11219.     }
  11220.  
  11221.    return result;
  11222. }
  11223.  
  11224. EDT_ClipboardResult CEditBuffer::PasteCellsIntoTable( IStreamIn& stream, EEditCopyType iCopyType )
  11225. {
  11226.     m_bNoRelayout = FALSE;
  11227.     CEditTableCellElement* pTableCell = NULL;
  11228.     // Default for pasting is BEFORE
  11229.     // TODO: Should we ask user to insert AFTER if currently in last cell in row or column?
  11230.     XP_Bool bAfterCurrentCell = FALSE;
  11231.  
  11232.     // Shouldn't be here if this is "normal" or entire table
  11233.     XP_ASSERT(iCopyType > eCopyTable);
  11234.     
  11235.     // Move caret into target cell
  11236.     if( m_pDragTableData )
  11237.     {
  11238.         // We are dragging source cells
  11239.         XP_ASSERT(m_pDragTableData->pDragOverCell);
  11240.  
  11241.         // Dragging has more flexibility for where to paste
  11242.         bAfterCurrentCell = (m_pDragTableData->iDropType == ED_DROP_INSERT_AFTER || 
  11243.                              m_pDragTableData->iDropType == ED_DROP_INSERT_BELOW );
  11244.  
  11245.         CEditElement *pElement = GetTableElementFromLO_Element(m_pDragTableData->pDragOverCell, LO_CELL);
  11246.         if( pElement )
  11247.         {
  11248.             CEditElement *pChild = pElement->GetFirstMostChild();
  11249.             if( pChild )
  11250.             {
  11251.                 CEditInsertPoint ip(pChild, 0);
  11252.                 SetInsertPoint(ip);
  11253.             }
  11254.         }
  11255.     } else {
  11256.         // Pasting from clipboard
  11257.         // Get current insert point
  11258.         CEditInsertPoint ip;
  11259.         GetTableInsertPoint(ip);
  11260.         pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  11261.     }
  11262.  
  11263.     //Get the table from the stream
  11264.     int32 bMergeEnd = stream.ReadInt();
  11265.     CEditTableElement* pSourceTable =(CEditTableElement*)CEditElement::StreamCtor(&stream, this);
  11266.  
  11267.     XP_ASSERT(pSourceTable->IsTable());
  11268.  
  11269.     if( pTableCell && pSourceTable )
  11270.     {
  11271.         CEditTableElement* pTable = pTableCell->GetTable();
  11272.         if ( pTable )
  11273.         {
  11274.             intn iSourceCols = pSourceTable->GetColumns();
  11275.             int32 X = pTableCell->GetX();
  11276.             int32 Y = pTableCell->GetY();
  11277.             int32 iNewX = X + (bAfterCurrentCell ? pTableCell->GetWidth() : 0);
  11278.             // Try to place cursor in inserted cell
  11279.             int32 iCaretX = iNewX + (bAfterCurrentCell ? pTable->GetInterCellSpace() : 0);
  11280.  
  11281.             CEditTableRowElement* pCurrentRow = (CEditTableRowElement*)pTableCell->GetParent();
  11282.             XP_ASSERT(pCurrentRow->IsTableRow());
  11283.             
  11284.             intn iCurrentRows = pTable->GetRows();
  11285.             // Number of rows we may insert
  11286.             intn iTotalRows = pSourceTable->GetRows();
  11287.             // Current row index
  11288.             intn iRow = 0;
  11289.  
  11290.             CEditTableRowElement* pFirstRow = pTable->GetFirstRow();
  11291.             if( pFirstRow != pCurrentRow )
  11292.             {
  11293.                 //Insert empty cells for rows above target row
  11294.                 CEditTableRowElement* pTempRow = pFirstRow;
  11295.                 while( pTempRow != pCurrentRow )
  11296.                 {
  11297.                     pTempRow->InsertCells(X, iNewX, iSourceCols);
  11298.                     pTempRow = pTempRow->GetNextRow();
  11299.                     // Increment row where we will start to insert source cells
  11300.                     //   and also the total rows we may insert
  11301.                     iRow++;
  11302.                     iTotalRows++;
  11303.                 }
  11304.             }
  11305.             
  11306.             CEditTableRowElement* pSourceRow = NULL;
  11307.             CEditTableRowElement *pNextRow = NULL;
  11308.             // The number of columns we will have to insert if we
  11309.             //   need to add rows at bottom of table
  11310.             //   so overflow from inserted columns line up
  11311.             intn iColumnsBefore = pTable->GetColumnsSpanned(pTable->GetFirstColumnX(), X);
  11312.             for( ; iRow < iCurrentRows; iRow++ )
  11313.             {
  11314.                 if( !pCurrentRow )
  11315.                     break;
  11316.  
  11317.                 pSourceRow = pSourceTable->GetFirstRow();
  11318.                 // pSourceRow may be 0 if there are less rows in source
  11319.                 //   than in target, so this will just insert blank cell in that case
  11320.                 pCurrentRow->InsertCells(X, iNewX, iSourceCols, pSourceRow);
  11321.                 // Delete the row just used
  11322.                 if( pSourceRow )
  11323.                     delete pSourceRow;
  11324.  
  11325.                 // Next row to insert into
  11326.                 if( (pNextRow = pCurrentRow->GetNextRow()) != NULL )
  11327.                     pCurrentRow = pNextRow;
  11328.             }
  11329.             // There shouldn't be any rows left now
  11330.             //   (this is a check for GetRows();
  11331.             XP_ASSERT(pNextRow == NULL);
  11332.             
  11333.             // Let's be sure!
  11334.             pNextRow = NULL;
  11335.             
  11336.             // If needed, continue to insert more source rows,
  11337.             //  but we need to add new rows to current table first            
  11338.             // TODO: ASK USER IF THEY WANT TO APPEND EXTRA ROWS OR JUST SKIP THEM
  11339.             for( ; iRow < iTotalRows; iRow++ )
  11340.             {
  11341.                 pSourceRow = pSourceTable->GetFirstRow();
  11342.                 if( !pCurrentRow || !pSourceRow )
  11343.                 {
  11344.                     // We should always have source row,
  11345.                     //  else iSourceRows is not accurate
  11346.                     //  and we should always have a current row
  11347.                     XP_ASSERT(TRUE);
  11348.                     break;
  11349.                 }
  11350.                 if( iColumnsBefore )
  11351.                 {
  11352.                     // We need cells before the insert column,
  11353.                     //  so make a new row with blank cells
  11354.                     pNextRow = new CEditTableRowElement(iColumnsBefore);
  11355.                     if( pNextRow )
  11356.                     {
  11357.                         pNextRow->InsertAfter(pCurrentRow);
  11358.                         // Append the source cells to the the new row
  11359.                         pNextRow->AppendRow(pSourceRow);
  11360.                     }
  11361.                 } else {
  11362.                     // No extra columns needed,
  11363.                     //   just append source row
  11364.                     pSourceRow->Unlink();
  11365.                     pSourceRow->InsertAfter(pCurrentRow);
  11366.                     pNextRow = pSourceRow;
  11367.                 }
  11368.  
  11369.                 XP_ASSERT(pNextRow);
  11370.                 if( pNextRow )
  11371.                 {
  11372.                     // Fill in rest of row with empty cells
  11373.                     pNextRow->PadRowWithEmptyCells(pTable->GetMaxColumns() + iSourceCols);
  11374.                     // Be sure any empty cells have required empty text elements
  11375.                     pNextRow->FinishedLoad(this);
  11376.                 }
  11377.                 pCurrentRow = pNextRow;
  11378.             }
  11379.  
  11380.             // Delete whats left of the source table
  11381.             delete pSourceTable;
  11382.  
  11383.             Relayout(pTable, 0);
  11384.             MoveToExistingCell(pTable, iCaretX, Y);
  11385.         }
  11386. #ifdef DEBUG
  11387.         m_pRoot->ValidateTree();
  11388. #endif
  11389.     }
  11390.  
  11391.     //TODO: FINISH PASTECELLSINTOTABLE
  11392.     return EDT_COP_OK;
  11393. }
  11394.  
  11395.  
  11396. EDT_ClipboardResult CEditBuffer::PasteHREF( char **ppHref, char **ppTitle, int iCount){
  11397.     VALIDATE_TREE(this);
  11398.     SUPPRESS_PHANTOMINSERTPOINTCHECK(this);
  11399.     EDT_ClipboardResult result = CanPaste(TRUE);
  11400.     if ( result != EDT_COP_OK ) return result;
  11401.     m_bNoRelayout = TRUE;
  11402.     XP_Bool bFirst = TRUE;
  11403.  
  11404.     if( IsSelected() ){
  11405.         DeleteSelection();
  11406.     }
  11407.     FixupInsertPoint();
  11408.  
  11409.     FormatCharacter( TF_NONE );
  11410.     InsertChar(' ', FALSE);
  11411.  
  11412.     CEditElement *pStart = m_pCurrent;
  11413.     int iStartOffset = m_iCurrentOffset;
  11414.  
  11415.     m_pCurrent->Divide( m_iCurrentOffset );
  11416.  
  11417.     int i = 0;
  11418.     while( i < iCount ){
  11419.         char *pTitle = ppTitle[i];
  11420.  
  11421.         if( pTitle == 0 ){
  11422.             pTitle = ppHref[i];
  11423.         }
  11424.         else {
  11425.             // LTNOTE:
  11426.             // probably shouldn't be doing this to a buffer we were passed
  11427.             //  but what the hell.
  11428.             NormalizeText( pTitle );
  11429.  
  11430.             // kill any trailing spaces..
  11431.             int iLen = XP_STRLEN( pTitle )-1;
  11432.             while( iLen >= 0 && pTitle[iLen] == ' ' ){
  11433.                 pTitle[iLen] = 0;
  11434.                 iLen--;
  11435.             }
  11436.             if( *pTitle == 0 ){
  11437.                 pTitle = ppHref[i];
  11438.             }
  11439.         }
  11440.  
  11441.         CEditTextElement *pElement = new CEditTextElement( 0, pTitle );
  11442.         pElement->SetHREF( linkManager.Add( ppHref[i], 0 ));
  11443.  
  11444.         if( bFirst && m_iCurrentOffset == 0 ){
  11445.             pElement->InsertBefore( m_pCurrent );
  11446.             pStart = pElement;
  11447.         }
  11448.         else {
  11449.             pElement->InsertAfter( m_pCurrent );
  11450.         }
  11451.         if( bFirst ){
  11452.             //FixupSpace();
  11453.             bFirst = FALSE;
  11454.         }
  11455.         m_pCurrent = pElement;
  11456.         m_iCurrentOffset = pElement->Text()->GetLen();
  11457.         i++;
  11458.     }
  11459.  
  11460.     m_bNoRelayout = FALSE;
  11461.     //FixupSpace( );
  11462.     // Fixes bug 21920 - Crash when dropping link into blank document.
  11463.     // We might have an empty text container just after m_pCurrent.
  11464.     Reduce(m_pRoot);
  11465.     FormatCharacter( TF_NONE );
  11466.     InsertChar(' ', FALSE);
  11467.     Relayout( pStart, iStartOffset, m_pCurrent );
  11468.     Reduce( m_pRoot );
  11469.     return result;
  11470. }
  11471.  
  11472. EDT_ClipboardResult CEditBuffer::PasteTextAsNewTable(char *pText, intn iRows, intn iCols)
  11473. {
  11474.     if( iRows == 0 || iCols == 0 )
  11475.        CountRowsAndColsInPasteText(pText, &iRows, &iCols); 
  11476.  
  11477.     EDT_TableData *pData = EDT_NewTableData();
  11478.     if( pData )
  11479.     {
  11480.         pData->iRows = iRows;
  11481.         pData->iColumns = iCols;
  11482.         InsertTable(pData);
  11483.         EDT_FreeTableData(pData);
  11484.         intn iRow = 0;
  11485.         // We assume InsertTable will put caret in first cell of table
  11486.         CEditInsertPoint ip;
  11487.         GetTableInsertPoint(ip);
  11488.         CEditTableElement *pTable = ip.m_pElement->GetTableIgnoreSubdoc();
  11489.         if( pTable )
  11490.         {
  11491.             CEditTableCellElement* pNextCell = pTable->GetFirstCell();
  11492.             
  11493.             char *pCellText = pText;
  11494.             XP_Bool bDone = FALSE;
  11495.             XP_Bool bEndOfRow = FALSE;
  11496.  
  11497.             while(!bDone)
  11498.             {            
  11499.                 do {
  11500.                     char current = *pText;
  11501.                     if( current == 9)
  11502.                     {
  11503.                         // We found the end of the cell's text
  11504.                         *pText = '\0';
  11505.                         pText++;
  11506.                         break;
  11507.                     }
  11508.                     if( current == 13 || current == 10 || current == '\0' )
  11509.                     {
  11510.                         // We found the end of the cell's text
  11511.                         if( current != '\0' )
  11512.                         {
  11513.                             // Terminate text for this cell
  11514.                             *pText = '\0';
  11515.  
  11516.                             // Skip over other end-of-line characters
  11517.                             char next = *(pText+1);
  11518.                             if( (current == 13 && next == 10) || 
  11519.                                 (current == 10 && next == 13) )
  11520.                             {
  11521.                                 pText++;
  11522.                             }                    
  11523.                             pText++;
  11524.                         }
  11525.                         bEndOfRow = TRUE;
  11526.                         break;
  11527.                     }
  11528.                     pText++;
  11529.                 } while( pText );
  11530.  
  11531.                 if( pCellText && *pCellText )
  11532.                     PasteText(pCellText, FALSE, FALSE, FALSE, TRUE); //Last 2param = don't relayout but we want to reduce
  11533.  
  11534.                 intn iNextRow = iRow;
  11535.                 
  11536.                 // Move insert point to next cell
  11537.                 // If its in next row, iNextRow will be incremented
  11538.                 if( NextTableCell(FALSE, TRUE, &iNextRow) )
  11539.                 {
  11540.                     if(bEndOfRow)
  11541.                     {
  11542.                         // Next item should go into next row
  11543.                         iRow++;
  11544.                         // If we have a short row (fewer tabs than maximum),
  11545.                         //   skip to first cell of next row
  11546.                         XP_Bool bGetNext = TRUE;
  11547.                         while( bGetNext && iRow != iNextRow )
  11548.                         {
  11549.                             bGetNext = NextTableCell(FALSE, TRUE, &iNextRow);
  11550.                         }
  11551.                         bEndOfRow = FALSE;
  11552.                     } 
  11553.                     else if( iRow != iNextRow )
  11554.                     {
  11555.                         // Should never happen - we are in next row of new table,
  11556.                         //   but we're not at end of text "row"
  11557.                         XP_ASSERT(FALSE);
  11558.                     }
  11559.  
  11560.                     // The next cell item starts at next character
  11561.                     pCellText = pText;
  11562.  
  11563.                     if( *pText == '\0' )
  11564.                         bDone = TRUE;
  11565.                 }
  11566.                 else
  11567.                 {
  11568.                     //TODO: We should probably test if any text is left
  11569.                     //      here - there shouldn't be any
  11570.                     bDone = TRUE;
  11571.                 }
  11572.             } //while(!bDone)
  11573.  
  11574.             // Return insert point to the first cell
  11575.             SetInsertPoint(ip);
  11576.             // Relayout the entire table
  11577.             Relayout(pTable, 0);
  11578.         }
  11579.     }
  11580.  
  11581.     return EDT_COP_OK;
  11582. }
  11583.  
  11584. EDT_ClipboardResult CEditBuffer::PasteTextIntoTable(char *pText, XP_Bool /*bReplace*/, intn iRows, intn iCols)
  11585. {
  11586.     if( iRows == 0 || iCols == 0 )
  11587.        CountRowsAndColsInPasteText(pText, &iRows, &iCols); 
  11588.     //TODO: FINISH PASTETEXTINTOTABLE()
  11589.     return EDT_COP_OK;
  11590. }
  11591.  
  11592.  
  11593. //
  11594. // Make sure that there are not spaces next to each other after a delete,
  11595. //  or paste
  11596. //
  11597. void CEditBuffer::FixupSpace( XP_Bool /*bTyping*/){
  11598.     if( m_pCurrent->InFormattedText() ){
  11599.         return;
  11600.     }
  11601.  
  11602.     if( m_pCurrent->IsBreak() && m_iCurrentOffset == 1 ){
  11603.         // Can't have a space after a break.
  11604.         CEditLeafElement *pNext = m_pCurrent->TextInContainerAfter();
  11605.         if( pNext
  11606.                 && pNext->IsA(P_TEXT)
  11607.                 && pNext->Text()->GetLen() != 0
  11608.                 && pNext->Text()->GetText()[0] == ' '){
  11609.             pNext->Text()->DeleteChar(m_pContext, 0);
  11610.             return;
  11611.         }
  11612.     }
  11613.  
  11614.     if( !m_pCurrent->IsA(P_TEXT) ){
  11615.         return;
  11616.     }
  11617.  
  11618.     CEditTextElement *pText = m_pCurrent->Text();
  11619.  
  11620.     if( pText->GetLen() == 0 ){
  11621.         return;
  11622.     }
  11623.     if( m_iCurrentOffset > 0 && pText->GetText()[ m_iCurrentOffset-1 ] == ' ' ){
  11624.         if( m_iCurrentOffset == pText->GetLen() ){
  11625.             CEditLeafElement *pNext = pText->TextInContainerAfter();
  11626.             if( pNext
  11627.                     && pNext->IsA(P_TEXT)
  11628.                     && pNext->Text()->GetLen() != 0
  11629.                     && pNext->Text()->GetText()[0] == ' '){
  11630.                 pNext->Text()->DeleteChar(m_pContext, 0);
  11631.                 //m_iCurrentOffset--;  // WHy???
  11632.                 return;
  11633.             }
  11634.         }
  11635.         else if( m_iCurrentOffset < pText->GetLen() && pText->GetText()[m_iCurrentOffset] == ' ' ){
  11636.             pText->DeleteChar(m_pContext, m_iCurrentOffset);
  11637.             return;
  11638.         }
  11639.     }
  11640.     // check for beginning of paragraph with a space.
  11641.     else if( m_iCurrentOffset == 0 && pText->GetText()[0] == ' ' ){
  11642.         pText->DeleteChar(m_pContext, 0);
  11643.         CEditLeafElement *pNext = m_pCurrent->TextInContainerAfter();
  11644.         if( m_pCurrent->Text()->GetLen() == 0 && pNext ){
  11645.             m_pCurrent = pNext;
  11646.         }
  11647.     }
  11648.     // Can't reduce here because it might smash objects pointers we know about
  11649. }
  11650.  
  11651. EDT_ClipboardResult CEditBuffer::CutSelection( char **ppText, int32* pTextLen,
  11652.                     char **ppHtml, int32* pHtmlLen){
  11653.     VALIDATE_TREE(this);
  11654.     EDT_ClipboardResult result = CanCut(TRUE);
  11655.     if ( result != EDT_COP_OK ) return result;
  11656.  
  11657.     result = CopySelection( ppText, pTextLen, ppHtml, pHtmlLen );
  11658.     if ( result != EDT_COP_OK ) return result;
  11659.  
  11660.     CPersistentEditSelection selection = GetEffectiveDeleteSelection();
  11661.     BeginBatchChanges(kCutCommandID);
  11662.     result = DeleteSelection();
  11663.     EndBatchChanges();
  11664.  
  11665.     // Check to see if we trashed the document
  11666.     XP_ASSERT ( m_pCurrent && m_pCurrent->GetElementType() != eEndElement );
  11667.     return result;
  11668. }
  11669.  
  11670. XP_Bool CEditBuffer::CutSelectionContents( CEditSelection& selection,
  11671.                     char **ppHtml, int32* pHtmlLen ){
  11672.     XP_Bool result = CopySelectionContents( selection, ppHtml, pHtmlLen );
  11673.     if ( result ) {
  11674.         CEditLeafElement *pBegin;
  11675.         CEditLeafElement *pEnd;
  11676.         MakeSelectionEndPoints( selection, pBegin, pEnd );
  11677.  
  11678.         DeleteBetweenPoints( pBegin, pEnd );
  11679.     }
  11680.     return TRUE;
  11681. }
  11682.  
  11683.  
  11684. EDT_ClipboardResult CEditBuffer::CopySelection( char **ppText, int32* pTextLen,
  11685.                     char **ppHtml, int32* pHtmlLen )
  11686. {
  11687.     EDT_ClipboardResult result = CanCopy(TRUE);
  11688.     if ( result != EDT_COP_OK ) return result;
  11689.  
  11690.     //cmanske: Added table selection handling
  11691.     if ( ppText )
  11692.     {
  11693.         if( IsTableOrCellSelected() )
  11694.         {
  11695.             // Collect text from all cells in
  11696.             //  format ready to paste into spreadsheets
  11697.             *ppText = GetTabDelimitedTextFromSelectedCells();
  11698.         }
  11699.         else 
  11700.         {
  11701.             // This adds hard CR/LF at the end of each row
  11702.             // TODO: Shouldn't we "unwrap" paragraphs?
  11703.             *ppText = (char*) LO_GetSelectionText( m_pContext );
  11704.         }
  11705.     }
  11706.     if ( pTextLen ) *pTextLen = XP_STRLEN( *ppText );
  11707.     
  11708.     CEditSelection selection;
  11709.     EEditCopyType iCopyType;
  11710.     CEditTableElement *pTempTable = NULL;
  11711.     CEditRootDocElement *pTempRoot = NULL;
  11712.  
  11713.  
  11714.     CEditTableElement *pTable = NULL;    
  11715.  
  11716.     if( IsTableOrCellSelected() )
  11717.     {
  11718.         if( m_pSelectedEdTable )
  11719.         {
  11720.             // Make selection from the entire table
  11721.             m_pSelectedEdTable->GetAll(selection);
  11722.             iCopyType = eCopyTable;
  11723.             pTable = m_pSelectedEdTable;
  11724.         } else {
  11725.             // Create a new empty table
  11726.             pTempTable = new CEditTableElement(0,0);
  11727.  
  11728.             if(!pTempTable)
  11729.                 return EDT_COP_SELECTION_EMPTY;
  11730.  
  11731.             CEditTableRowElement *pRow = new CEditTableRowElement();
  11732.             if(!pRow)
  11733.             {
  11734.                 delete pTempTable;
  11735.                 return EDT_COP_SELECTION_EMPTY;
  11736.             }
  11737.             pRow->InsertAsFirstChild(pTempTable);
  11738.  
  11739.             intn iCurrentRow = 1;
  11740.             intn iRow = 1;
  11741.             intn iMaxCols = 1;
  11742.             intn iCols = 0;
  11743.  
  11744.             CEditTableCellElement *pCell = GetFirstSelectedCell();
  11745.             
  11746.             if(!pCell)
  11747.             {
  11748.             ABORT_COPY:
  11749.                 delete pTempTable; // This will delete row as well
  11750.                 return EDT_COP_SELECTION_EMPTY;
  11751.             }
  11752.             
  11753.             pTable = pCell->GetParentTable();
  11754.             
  11755.             // We need to make a temporary root to our table, 
  11756.             //    primarily to supply the current buffer
  11757.             //    needed by GetWinCSID() during tag parsing from stream
  11758.             pTempRoot = new CEditRootDocElement(this);
  11759.             if(!pTempRoot)
  11760.                 goto ABORT_COPY;
  11761.  
  11762.             pTempTable->InsertAsFirstChild(pTempRoot);
  11763.                 
  11764.             // Just in case something tries to relayout
  11765.             //    during this. We will be temporarily 
  11766.             //    mangling cell pointers, which will 
  11767.             //    definitely crash if layout occurs before 
  11768.             //    we are fully restored
  11769.             m_bNoRelayout = TRUE;
  11770.  
  11771.             while( pRow && pCell )
  11772.             {
  11773.                 if( iRow > iCurrentRow )
  11774.                 {
  11775.                     // We are on next row
  11776.                     
  11777.                     // Save maximum number of columns
  11778.                     if( iCols > iMaxCols )
  11779.                         iMaxCols = iCols;
  11780.                     
  11781.                     iCurrentRow = iRow;
  11782.  
  11783.                     // Add new row to table
  11784.                     pRow = new CEditTableRowElement();
  11785.                     if(pRow)
  11786.                         pRow->InsertAsLastChild(pTempTable);
  11787.  
  11788.                 } else {
  11789.                     iCols++;
  11790.                 }
  11791.                 // Switch cell to current row
  11792.                 if( pRow )
  11793.                     pCell->SwitchLinkage(pRow);
  11794.  
  11795.                 // Get next selected cell
  11796.                 pCell = GetNextSelectedCell(&iRow);
  11797.             }
  11798.             // cmanske: There is a problem with streaming out a table whose last element 
  11799.             //   is an empty cell - it fails "local.Intersects(selection)"
  11800.             //   in CEditElement::PartialStreamOut
  11801.             // I tried adding an extra container+text element after the table, but this didn't work
  11802.  
  11803.             // The copy type is the last-selected block???
  11804.             //TODO: WRITE SOMETHING TO DETECT SELECTED ROWS/COLS BY INSPECTION
  11805.             switch( m_TableHitType )
  11806.             {
  11807.                 case ED_HIT_SEL_ROW:
  11808.                     iCopyType = eCopyRows;
  11809.                     break;
  11810.                 case ED_HIT_SEL_COL:
  11811.                     iCopyType = eCopyColumns;
  11812.                     break;
  11813.                 default:
  11814.                     iCopyType = eCopyCells;
  11815.                     break;
  11816.             }
  11817.             // Set the number of columns and rows in table data
  11818.             // Note that we must supply csid:
  11819.             //   temp table can't get it 'cause its not part of doc.
  11820.             EDT_TableData *pData = pTempTable->GetData();
  11821.             if( pData )
  11822.             {
  11823.                 pData->iColumns = iMaxCols;
  11824.                 pData->iRows = iRow;
  11825.                 EDT_TableData *pOldData = pTable->GetData();
  11826.                 if( pOldData )
  11827.                 {
  11828.                     pData->bBorderWidthDefined = pOldData->bBorderWidthDefined;
  11829.                     pData->iBorderWidth = pOldData->iBorderWidth;
  11830.                     pData->iCellSpacing = pOldData->iCellSpacing;
  11831.                     pData->iCellPadding = pOldData->iCellPadding;
  11832.                 }
  11833.                 pTempTable->SetData(pData);
  11834.                 EDT_FreeTableData(pData);
  11835.             }
  11836.             // Make the selection from the temporary table
  11837.             pTempTable->GetAll(selection);
  11838.         }
  11839.     } else {
  11840.         // Get normal selection
  11841.         GetSelection(selection);
  11842.         iCopyType = eCopyNormal;
  11843.     }
  11844.     // Build the HTML stream from the selection
  11845.     if( !selection.IsEmpty() )
  11846.         CopySelectionContents( selection, ppHtml, pHtmlLen, iCopyType );
  11847.  
  11848.     if( pTempRoot )
  11849.     {
  11850.         // Restore original linkage for all selected cells
  11851.         CEditTableCellElement *pCell = GetFirstSelectedCell();
  11852.         while( pCell )
  11853.         {
  11854.             pCell->RestoreLinkage();
  11855.             pCell = GetNextSelectedCell();
  11856.         }
  11857.         // Deletes root and table under it
  11858.         delete pTempRoot;
  11859.  
  11860.         // Restore layout
  11861.         m_bNoRelayout = FALSE;
  11862.     }
  11863.     return result;
  11864. }
  11865.  
  11866. XP_Bool CEditBuffer::CopySelectionContents( CEditSelection& selection,
  11867.                                             char **ppHtml, int32* pHtmlLen, 
  11868.                                             EEditCopyType iCopyType )
  11869. {
  11870.     CStreamOutMemory stream;
  11871.  
  11872.     XP_Bool result = CopySelectionContents(selection, stream, iCopyType);
  11873.  
  11874.     *ppHtml = stream.GetText();
  11875.     *pHtmlLen = stream.GetLen();
  11876.  
  11877.     return result;
  11878. }
  11879.  
  11880. XP_Bool CEditBuffer::CopySelectionContents( CEditSelection& selection,
  11881.                                             IStreamOut& stream, EEditCopyType iCopyType )
  11882. {
  11883.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_pContext);
  11884.     stream.WriteInt(GetClipboardSignature());
  11885.     stream.WriteInt(GetClipboardVersion());
  11886.     stream.WriteInt(INTL_GetCSIWinCSID(c));
  11887.     //cmanske: New - contains info about what we are copying,
  11888.     //  used for tables
  11889.     int32 iTemp = iCopyType; // Lets be sure it ends up as an int32!
  11890.     stream.WriteInt(iTemp);
  11891.  
  11892.     // Never merge at the end of table
  11893.     //TODO: Problem: This will be TRUE if we have a "normal" selection
  11894.     //   that happens to end in a table, thus the last cell will be merged with
  11895.     //   next conainter after insert point
  11896.     int32 bMergeEnd = ( iCopyType == eCopyNormal ) ? !selection.EndsAtStartOfContainer() : 0;
  11897.     stream.WriteInt(bMergeEnd);
  11898.  
  11899.     // If streaming out "temporary" table for cell copying,
  11900.     //    we must use it as the root of stream creation since that is 
  11901.     //    where elements are examined if they contain the selection elements
  11902.     if( iCopyType > eCopyNormal )
  11903.     {
  11904.         CEditTableElement *pTable = selection.m_start.m_pElement->GetParentTable();
  11905.         if( pTable )
  11906.             pTable->PartialStreamOut(&stream, selection);
  11907. #ifdef DEBUG
  11908.         else XP_TRACE(("CEditBuffer::CopySelectionContents:  Failed to find parent table"));
  11909. #endif            
  11910.     } else {
  11911.         m_pRoot->PartialStreamOut(&stream, selection);
  11912.     }
  11913.     stream.WriteInt( (int32)eElementNone );
  11914.     return TRUE;
  11915. }
  11916.  
  11917. XP_Bool CEditBuffer::CopyBetweenPoints( CEditElement *pBegin,
  11918.                     CEditElement *pEnd, char **ppText, int32* pTextLen,
  11919.                     char **ppHtml, int32* pHtmlLen ){
  11920.     if ( ppText ) *ppText = (char*) LO_GetSelectionText( m_pContext );
  11921.     if ( pTextLen ) *pTextLen = XP_STRLEN( *ppText );
  11922.     CEditInsertPoint a(pBegin, 0);
  11923.     CEditInsertPoint b(pEnd, 0);
  11924.     CEditSelection s(a,b);
  11925.     return CopySelectionContents(s, ppHtml, pHtmlLen);
  11926. }
  11927.  
  11928. int32 CEditBuffer::GetClipboardSignature(){
  11929.     return 0xc43954af; /* Doesn't mean anything special, but must stay the same. */
  11930. }
  11931.  
  11932. int32 CEditBuffer::GetClipboardVersion(){
  11933.     return 0x040000; /* Should roughly match product version in binary-coded-decimal */
  11934. }
  11935.  
  11936.  
  11937.  
  11938. // New Table Cut/Paste routines
  11939. XP_Bool CEditBuffer::CountRowsAndColsInPasteText(char *pText, intn* pRows, intn* pCols)
  11940. {
  11941.     intn iRows = 1;
  11942.     intn iCols = 1;
  11943.     intn iMaxCols = 0;
  11944.     XP_Bool bColsOK = TRUE;
  11945.     while( *pText )
  11946.     {
  11947.         char current = *pText;
  11948.         char next = *(pText+1);
  11949.  
  11950.         if( current == 13 || current == 10 )
  11951.         {
  11952.             // We have CR            
  11953.             // Check if number of cells in this row is same as 
  11954.             //   previous. Return this to user
  11955.             if(iMaxCols > 0 && iMaxCols != iCols )
  11956.                 bColsOK = FALSE;
  11957.  
  11958.             // Save maximumn columns per row and setup for next row
  11959.             if( iCols > iMaxCols )
  11960.                 iMaxCols = iCols;
  11961.             iCols = 1;
  11962.             
  11963.             // Skip over other next-line chars
  11964.             if( (current == 13 && next == 10) || 
  11965.                 (current == 10 && next == 13) )
  11966.             {
  11967.                 pText++;
  11968.             }                    
  11969.             pText++;
  11970.  
  11971.             // Check if there's anything else after this,
  11972.             // Increment rows only there's something else in next row
  11973.             if(*pText)
  11974.                 iRows++;
  11975.         } else {
  11976.             if( current == 9)
  11977.                 iCols++;
  11978.  
  11979.             pText++;
  11980.         }
  11981.     }
  11982.     if( pRows )
  11983.         *pRows = iRows;
  11984.     if( pCols )
  11985.         *pCols = iMaxCols;
  11986.  
  11987.     return bColsOK;
  11988. }
  11989.  
  11990.  
  11991.  
  11992. //
  11993. // Used during parse phase.
  11994. //
  11995. ED_Alignment CEditBuffer::GetCurrentAlignment(){
  11996.     if( GetParseState()->m_formatAlignStack.IsEmpty()){
  11997.         return m_pCreationCursor->GetDefaultAlignment();
  11998.     }
  11999.     else {
  12000.         return GetParseState()->m_formatAlignStack.Top();
  12001.     }
  12002. }
  12003.  
  12004. void CEditBuffer::GetSelection( CEditSelection& selection ){
  12005.     if ( IsSelected() ) {
  12006.         GetSelection( selection.m_start.m_pElement, selection.m_start.m_iPos,
  12007.             selection.m_end.m_pElement, selection.m_end.m_iPos,
  12008.             selection.m_bFromStart);
  12009.     }
  12010.     else {
  12011.         GetInsertPoint( &selection.m_start.m_pElement, &selection.m_start.m_iPos, &selection.m_start.m_bStickyAfter);
  12012.         selection.m_end = selection.m_start;
  12013.         selection.m_bFromStart = FALSE;
  12014.     }
  12015. }
  12016.  
  12017. void CEditBuffer::GetSelection( CPersistentEditSelection& persistentSelection ){
  12018.     CEditSelection selection;
  12019.     GetSelection(selection);
  12020.     persistentSelection = EphemeralToPersistent(selection);
  12021. }
  12022.  
  12023. void CEditBuffer::GetInsertPoint(CEditInsertPoint& insertPoint){
  12024.     if ( ! IsSelected() )
  12025.     {
  12026.         GetInsertPoint( & insertPoint.m_pElement, & insertPoint.m_iPos, & insertPoint.m_bStickyAfter );
  12027.     }
  12028.     else
  12029.     {
  12030.         CEditSelection selection;
  12031.         GetSelection(selection);
  12032.         selection.ExcludeLastDocumentContainerEnd();
  12033.         insertPoint = *selection.GetActiveEdge();
  12034.     }
  12035. }
  12036.  
  12037. void CEditBuffer::GetInsertPoint(CPersistentEditInsertPoint& insertPoint){
  12038.     CEditInsertPoint pt;
  12039.     GetInsertPoint(pt);
  12040.     insertPoint = EphemeralToPersistent(pt);
  12041. }
  12042.  
  12043. void CEditBuffer::SetSelection(CPersistentEditSelection& persistentSelection){
  12044.     CEditSelection selection = PersistentToEphemeral(persistentSelection);
  12045.     SetSelection(selection);
  12046. }
  12047.  
  12048. void CEditBuffer::CopyEditText(CEditText& text){
  12049.     text.Clear();
  12050.     if ( IsSelected() ) {
  12051.         XP_Bool copyOK = CopySelection(NULL, NULL, text.GetPChars(), text.GetPLength());
  12052.         XP_ASSERT(copyOK);
  12053.     }
  12054. }
  12055.  
  12056. void CEditBuffer::CopyEditText(CPersistentEditSelection& persistentSelection, CEditText& text){
  12057.     text.Clear();
  12058.     if ( ! persistentSelection.IsInsertPoint() ) {
  12059.         CEditSelection selection = PersistentToEphemeral(persistentSelection);
  12060.         CopySelectionContents(selection, text.GetPChars(), text.GetPLength());
  12061.     }
  12062. }
  12063.  
  12064. void CEditBuffer::CutEditText(CEditText& text){
  12065.     text.Clear();
  12066.     CEditSelection selection;
  12067.     GetSelection(selection);
  12068.     if ( ! selection.IsInsertPoint() ) {
  12069.         CutSelectionContents(selection, text.GetPChars(), text.GetPLength());
  12070.     }
  12071. }
  12072.  
  12073. void CEditBuffer::PasteEditText(CEditText& text){
  12074.     if ( text.Length() > 0 )
  12075.         PasteHTML( text.GetChars(), TRUE);
  12076. }
  12077.  
  12078. // Persistent to regular selection conversion routines
  12079. CEditInsertPoint CEditBuffer::PersistentToEphemeral(CPersistentEditInsertPoint& persistentInsertPoint){
  12080.     CEditInsertPoint result = m_pRoot->IndexToInsertPoint(
  12081.         persistentInsertPoint.m_index, persistentInsertPoint.m_bStickyAfter);
  12082. #ifdef DEBUG_EDITOR_LAYOUT
  12083.     // Check for reversability
  12084.     CPersistentEditInsertPoint p2 = result.m_pElement->GetPersistentInsertPoint(result.m_iPos);
  12085.     XP_ASSERT(persistentInsertPoint == p2);
  12086.     // Check for legality.
  12087.     if ( result.m_pElement->IsEndOfDocument() &&
  12088.         result.m_iPos != 0){
  12089.         XP_ASSERT(FALSE);
  12090.         result.m_iPos = 0;
  12091.     }
  12092.  
  12093. #endif
  12094.     return result;
  12095. }
  12096.  
  12097. CPersistentEditInsertPoint CEditBuffer::EphemeralToPersistent(CEditInsertPoint& insertPoint){
  12098.     CPersistentEditInsertPoint result = insertPoint.m_pElement->GetPersistentInsertPoint(insertPoint.m_iPos);
  12099. #ifdef DEBUG_EDITOR_LAYOUT
  12100.     // Check for reversability
  12101.     CEditInsertPoint p2 = m_pRoot->IndexToInsertPoint(result.m_index);
  12102.     if ( ! ( p2 == insertPoint ||
  12103.         insertPoint.IsDenormalizedVersionOf(p2) ) ) {
  12104.         // One or the other is empty.
  12105.         if ( insertPoint.m_pElement->Leaf()->GetLen() == 0 ||
  12106.             p2.m_pElement->Leaf()->GetLen() == 0 ) {
  12107.             // OK, do they at least have the same persistent point?
  12108.             CPersistentEditInsertPoint pp2 = p2.m_pElement->GetPersistentInsertPoint(p2.m_iPos);
  12109.             XP_ASSERT(result == pp2);
  12110.         }
  12111.         else
  12112.         {
  12113.             // Just wrong.
  12114.             XP_ASSERT(FALSE);
  12115.         }
  12116.     }
  12117. #endif
  12118.     return result;
  12119. }
  12120.  
  12121. CEditSelection CEditBuffer::PersistentToEphemeral(CPersistentEditSelection& persistentSelection){
  12122.     CEditSelection selection(PersistentToEphemeral(persistentSelection.m_start),
  12123.         PersistentToEphemeral(persistentSelection.m_end),
  12124.         persistentSelection.m_bFromStart);
  12125.     XP_ASSERT(!selection.m_start.IsEndOfDocument());
  12126.     return selection;
  12127. }
  12128.  
  12129. CPersistentEditSelection CEditBuffer::EphemeralToPersistent(CEditSelection& selection){
  12130.     CPersistentEditSelection persistentSelection;
  12131.     XP_ASSERT(!selection.m_start.IsEndOfDocument());
  12132.     persistentSelection.m_start = EphemeralToPersistent(selection.m_start);
  12133.     persistentSelection.m_end = EphemeralToPersistent(selection.m_end);
  12134.     persistentSelection.m_bFromStart = selection.m_bFromStart;
  12135.     return persistentSelection;
  12136. }
  12137.  
  12138. void CEditBuffer::AdoptAndDo(CEditCommand* command) {
  12139.     DoneTyping();
  12140.     GetCommandLog()->AdoptAndDo(command);
  12141. }
  12142.  
  12143. void CEditBuffer::Undo() {
  12144.     DoneTyping();
  12145.     GetCommandLog()->Undo();
  12146. }
  12147.  
  12148. void CEditBuffer::Redo() {
  12149.     DoneTyping();
  12150.     GetCommandLog()->Redo();
  12151. }
  12152.  
  12153. void CEditBuffer::Trim() {
  12154.     DoneTyping();
  12155.     GetCommandLog()->Trim();
  12156. }
  12157.  
  12158. intn CEditBuffer::GetCommandHistoryLimit() {
  12159.     return GetCommandLog()->GetCommandHistoryLimit();
  12160. }
  12161.  
  12162. void CEditBuffer::SetCommandHistoryLimit(intn newLimit) {
  12163.     GetCommandLog()->SetCommandHistoryLimit(newLimit);
  12164. }
  12165.  
  12166. // Returns NULL if out of range
  12167. intn CEditBuffer::GetUndoCommand(intn n) {
  12168.     return GetCommandLog()->GetUndoCommand(n);
  12169. }
  12170.  
  12171. intn CEditBuffer::GetRedoCommand(intn n) {
  12172.     return GetCommandLog()->GetRedoCommand(n);
  12173. }
  12174.  
  12175. void CEditBuffer::BeginBatchChanges(intn id) {
  12176.     DoneTyping();
  12177.     GetCommandLog()->BeginBatchChanges(id);
  12178. }
  12179.  
  12180. void CEditBuffer::EndBatchChanges() {
  12181.     GetCommandLog()->EndBatchChanges();
  12182. }
  12183.  
  12184. XP_Bool CEditBuffer::IsWritable(){
  12185. #ifdef EDITOR_JAVA
  12186.     return ! ( EditorPluginManager_PluginsExist() 
  12187.         && EditorPluginManager_IsPluginActive(GetPlugins()));
  12188. #else
  12189.     return TRUE;
  12190. #endif
  12191. }
  12192.  
  12193. void CEditBuffer::StartTyping(XP_Bool bTyping){
  12194.     if ( bTyping && ! m_bTyping ) {
  12195.         GetCommandLog()->StartTyping(kTypingCommandID);
  12196.         m_bTyping = TRUE;
  12197.     }
  12198. }
  12199.  
  12200. void CEditBuffer::DoneTyping() {
  12201.     if ( m_bTyping ) {
  12202. #ifdef DEBUG_TYPING
  12203.         XP_TRACE(("Done typing."));
  12204. #endif
  12205.         GetCommandLog()->EndTyping();
  12206.     }
  12207.     m_bTyping = FALSE;
  12208. }
  12209.  
  12210. #if 0 
  12211. // NOT USED But may be useful?
  12212. // This must be fairly efficient, so minimal checking
  12213. // Find the cell whose X value is within a cell's horizontal location
  12214. // (IGNORE the Y value -- assumes we are already near top of table)
  12215. LO_Element* FindClosestCellToTableTop(LO_Element *pLoTable, LO_Element *pEndLoElement, int32 x)
  12216. {
  12217.     LO_Element *pLoCell = NULL;
  12218.     if( pLoTable && pEndLoElement)
  12219.     {
  12220.         LO_Element *pLoElement = pLoTable->lo_any.next;
  12221.         while(pLoElement != pEndLoElement)
  12222.         {
  12223.             if( pLoElement->lo_any.type == LO_CELL &&
  12224.                 x >= pLoElement->lo_any.x && x <= (pLoElement->lo_any.x + pLoElement->lo_any.width) )
  12225.             {
  12226.                 return pLoElement;
  12227.             }
  12228.             pLoElement = pLoElement->lo_any.next;
  12229.         }
  12230.     }
  12231.     return pLoCell;
  12232. }
  12233.  
  12234. // Find the cell whose Y value is within a cell's vertical location
  12235. // (IGNORE the X value -- assumes we are already near left edge of table)
  12236. PRIVATE
  12237. LO_Element* FindClosestCellToTableLeft(LO_Element *pLoTable, LO_Element *pEndLoElement, int32 y)
  12238. {
  12239.     LO_Element *pLoCell = NULL;
  12240.     if( pLoTable && pEndLoElement)
  12241.     {
  12242.         LO_Element *pLoElement = pLoTable->lo_any.next;
  12243.         while(pLoElement != pEndLoElement)
  12244.         {
  12245.             if( pLoElement->lo_any.type == LO_CELL &&
  12246.                 y >= pLoElement->lo_any.y && y <= (pLoElement->lo_any.y + pLoElement->lo_any.height) )
  12247.             {
  12248.                 return pLoElement;
  12249.             }
  12250.             pLoElement = pLoElement->lo_any.next;
  12251.         }
  12252.     }
  12253.     return pLoCell;
  12254. }
  12255. #endif
  12256.  
  12257. CEditTableCellElement* CEditBuffer::GetFirstCellInCurrentColumn()
  12258. {
  12259. //    if( !IsInsertPointInTable() )
  12260. //        return NULL;
  12261.  
  12262.     // Get current leaf element at caret or selection
  12263.     // Leaf elements are our only connection to coresponding LO_Elements
  12264.     CEditInsertPoint ip;
  12265.     GetTableInsertPoint(ip);
  12266.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  12267.     if( pTableCell )
  12268.     {
  12269.         // Returns cell with EXACT X value (don't include spanned cells)
  12270.         return pTableCell->GetFirstCellInColumn(pTableCell->GetX());
  12271.     }
  12272.     return NULL;
  12273. }
  12274.  
  12275. CEditTableCellElement* CEditBuffer::GetFirstCellInCurrentRow()
  12276. {
  12277.     CEditInsertPoint ip;
  12278.     GetTableInsertPoint(ip);
  12279.     CEditTableCellElement* pTableCell = ip.m_pElement->GetTableCellIgnoreSubdoc();
  12280.     if( pTableCell )
  12281.     {
  12282.         // Returns cell with EXACT Y value (don't include spanned cells)
  12283.         return pTableCell->GetFirstCellInRow(pTableCell->GetY());
  12284.     }
  12285.     return NULL;
  12286. }
  12287.  
  12288. PRIVATE
  12289. LO_Element *edt_XYToNestedTableAndCell(LO_Element *pStartCell, int32 x, int32 y, LO_Element **ppCellElement)
  12290. {
  12291.     if( pStartCell == NULL || pStartCell->type != LO_CELL )
  12292.         return NULL;
  12293.  
  12294.     // Check cell contents list for a nested table
  12295.     LO_Element *pTableElement = NULL;
  12296.     LO_Element *pCellElement = NULL;
  12297.     LO_Element *pCellList = pStartCell->lo_cell.cell_list;
  12298.     while( pCellList )
  12299.     {
  12300.         if( pCellList->type == LO_TABLE &&
  12301.             ((y >= pCellList->lo_any.y) &&
  12302.             (y < pCellList->lo_any.y + pCellList->lo_any.y_offset + pCellList->lo_any.height) &&
  12303.             (x >= pCellList->lo_any.x) &&
  12304.             (x < (pCellList->lo_any.x + pCellList->lo_any.x_offset + pCellList->lo_any.width))) )
  12305.         {
  12306.             // We are inside a nested table
  12307.             pTableElement = pCellList;
  12308.             
  12309.             // Get first cell in table and search through that cell list
  12310.             //   to find deepest nested table and cell enclosing cursor
  12311.             LO_Element *pNestedCell = pCellList->lo_any.next;
  12312.             XP_ASSERT(pNestedCell->type == LO_CELL);
  12313.             while( pNestedCell )
  12314.             {
  12315.                 if( (y >= pNestedCell->lo_any.y) &&
  12316.                     (y < pNestedCell->lo_any.y + pNestedCell->lo_any.y_offset + pNestedCell->lo_any.height) &&
  12317.                     (x >= pNestedCell->lo_any.x) &&
  12318.                     (x < (pNestedCell->lo_any.x + pNestedCell->lo_any.x_offset + pNestedCell->lo_any.width)) )
  12319.                 {
  12320.                     // We are inside a cell in a nested table
  12321.                     // We may stop here, but look recursively into its cell list first
  12322.                     pCellElement = pNestedCell;
  12323.  
  12324.                     LO_Element *pNestedCell2 = NULL;
  12325.                     LO_Element *pNestedTable2 = edt_XYToNestedTableAndCell(pNestedCell, x, y, &pNestedCell2);
  12326.                     if( pNestedTable2 )
  12327.                     {
  12328.                         // We found a deeper nested table
  12329.                         pTableElement = pNestedTable2;
  12330.                         // This may be NULL if we are in table and not inside a cell in that table
  12331.                         pCellElement = pNestedCell2;
  12332.                     }
  12333.                     // We found the enclosing cell, so stop searching nested table
  12334.                     break;
  12335.                 }
  12336.                 // Check all cells in the nested table
  12337.                 pNestedCell = pNestedCell->lo_any.next;
  12338.             }
  12339.             // Found an enclosing table - done scaning original cell list
  12340.             break;
  12341.         }
  12342.         // Next cell in list
  12343.         pCellList = pCellList->lo_any.next;
  12344.     }
  12345.     // Return deepest table and/or cell found
  12346.     if( ppCellElement )
  12347.         *ppCellElement = pCellElement;
  12348.     
  12349.     return pTableElement;
  12350. }
  12351.  
  12352. static void edt_SetHitLimits(LO_Element *pLoElement,
  12353.                              int32 *pLeftLimit, int32 *pTopLimit, int32 *pRightLimit, int32 *pBottomLimit,
  12354.                              int32 *pRight, int32 *pBottom, XP_Bool /*bDropLimits*/)
  12355. {
  12356.     int32 left = pLoElement->lo_any.x;
  12357.     int32 top = pLoElement->lo_any.y;
  12358.     int32 right = left + pLoElement->lo_any.width;
  12359.     int32 bottom = top + pLoElement->lo_any.height;
  12360.  
  12361.     // We use this for cell sizing detection in inter_cell_space
  12362.     if( pRight )
  12363.         *pRight = right;
  12364.  
  12365.     if( pBottom )
  12366.         *pBottom = bottom;
  12367.  
  12368.     if( pLoElement->type == LO_TABLE )
  12369.     {
  12370.         // We assume that we don't need to worry about too-small tables
  12371.         // Include the entire beveled border as hit region
  12372.         *pLeftLimit = left + max(ED_SIZING_BORDER, pLoElement->lo_table.border_left_width);
  12373.         *pRightLimit = right - max(ED_SIZING_BORDER, pLoElement->lo_table.border_left_width);
  12374.         
  12375.         *pTopLimit = top + max(ED_SIZING_BORDER, pLoElement->lo_table.border_top_width);
  12376.         *pBottomLimit = bottom - max(ED_SIZING_BORDER, pLoElement->lo_table.border_bottom_width);
  12377.     } else {
  12378.         // Figure sizing regions but reduce if cell is too small
  12379.         //  so side and top hit regions are at least ED_SIZING_BORDER wide
  12380.         //  This may eliminate corner hit regions if element rect is too small
  12381.     
  12382.         int32 border;
  12383.         if( pLoElement->lo_any.width < 3*ED_SIZING_BORDER )
  12384.         {
  12385.             border = pLoElement->lo_any.width > ED_SIZING_BORDER ? 
  12386.                                                   ((pLoElement->lo_any.width-ED_SIZING_BORDER)/2) : 0;
  12387.         } else {
  12388.         
  12389.             border = ED_SIZING_BORDER;
  12390.         }
  12391.         *pLeftLimit =  left + border;
  12392.  
  12393.         // This reduces the hit region within the cell if the inter-cell space is large enough
  12394.         //   to serve as the column sizing hit region
  12395.         *pRightLimit =  right - min(border, max(0, ED_SIZING_BORDER - pLoElement->lo_cell.inter_cell_space));
  12396.  
  12397.         if( pLoElement->lo_any.width < 3*ED_SIZING_BORDER )
  12398.         {
  12399.             border = pLoElement->lo_any.height > ED_SIZING_BORDER ? 
  12400.                                                   ((pLoElement->lo_any.width-ED_SIZING_BORDER)/2) : 0;
  12401.         } else {
  12402.             border = ED_SIZING_BORDER;
  12403.         }
  12404.  
  12405.         *pTopLimit = top + border;
  12406.         *pBottomLimit = bottom - border;
  12407.     }
  12408. }
  12409.  
  12410. // Find Table or cell element and specific mouse hit regions,
  12411. //  do not look into cells for other element types except another table
  12412. ED_HitType CEditBuffer::GetTableHitRegion(int32 x, int32 y, LO_Element **ppElement, XP_Bool bModifierKeyPressed)
  12413. {
  12414.     // Signal to use last-selected element and hit type
  12415.     if( x <= 0 )
  12416.     {
  12417.         if( ppElement )
  12418.             *ppElement = m_pSelectedTableElement;
  12419.         return m_TableHitType;
  12420.     }
  12421.  
  12422.     // Initialize in case we don't find any table element
  12423.     if( ppElement)
  12424.         *ppElement = NULL;
  12425.  
  12426.     // From LO_XYToElement
  12427.     int32 doc_id = XP_DOCID(m_pContext);
  12428.     lo_TopState *top_state = lo_FetchTopState(doc_id);
  12429.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  12430.         return ED_HIT_NONE;
  12431.  
  12432.     lo_DocState *state = top_state->doc_state;
  12433.  
  12434.     LO_Element *pLoElement = NULL;
  12435.     LO_Element *pEndLoElement = NULL;
  12436.     LO_Element *pTableElement = NULL;
  12437.     LO_Element *pCellElement = NULL;
  12438.     int32 left_limit, right_limit, top_limit, bottom_limit, right, bottom;
  12439.     int32 xPlus = x + ED_SIZING_BORDER;
  12440.     XP_Bool     bAboveOrBelowTable = FALSE;
  12441.  
  12442.     int32 line = lo_PointToLine(m_pContext, state, x, y);
  12443.     int32 lineBelow = lo_PointToLine(m_pContext, state, x, y + ED_SIZING_BORDER);
  12444.     int32 lineAbove = lo_PointToLine(m_pContext, state, x, y - ED_SIZING_BORDER);
  12445.   
  12446.     if( lineBelow != line || lineAbove != line )
  12447.     {
  12448.         // We are near boundary between two lines,
  12449.         //   check if we are above or below a table
  12450.         // THIS ASSUMES TABLE IS FIRST ELEMENT ON lineBelow or lineAbove
  12451.         //   AND IS TALLEST ELEMENT IN THE LINE
  12452.         if( lineBelow != line )
  12453.             lo_GetLineEnds(m_pContext, state, lineBelow, &pLoElement, &pEndLoElement);
  12454.         
  12455.         if( lineAbove != line )
  12456.             lo_GetLineEnds(m_pContext, state, lineAbove, &pLoElement, &pEndLoElement);
  12457.          
  12458.         if( pLoElement && pLoElement->type == LO_TABLE &&
  12459.             (xPlus >= pLoElement->lo_any.x) &&
  12460.             (x < (pLoElement->lo_any.x + pLoElement->lo_any.x_offset 
  12461.                     + pLoElement->lo_any.width + ED_SIZING_BORDER)) )
  12462.         {
  12463.             pTableElement = pLoElement;
  12464.             bAboveOrBelowTable = TRUE;
  12465.         }
  12466.     }
  12467.  
  12468.     if( !bAboveOrBelowTable )
  12469.     {
  12470.         lo_GetLineEnds(m_pContext, state, line, &pLoElement, &pEndLoElement);
  12471.  
  12472.         while (pLoElement != pEndLoElement)
  12473.         {
  12474.             if (pLoElement->type == LO_TABLE)
  12475.             {
  12476.                 if ((y >= pLoElement->lo_any.y) &&
  12477.                     (y <= pLoElement->lo_any.y + pLoElement->lo_any.y_offset + pLoElement->lo_any.height) &&
  12478.                     (xPlus >= pLoElement->lo_any.x) &&
  12479.                     (x <= (pLoElement->lo_any.x + pLoElement->lo_any.x_offset + pLoElement->lo_any.width + ED_SIZING_BORDER)))
  12480.                 {
  12481.                     // Save the table element
  12482.                     pTableElement = pLoElement;
  12483.                 }
  12484.             }
  12485.             else if( pLoElement->type == LO_CELL )
  12486.             {
  12487.                 // Note that we include the inter-cell space when testing if inside a cell
  12488.                 if ((y >= pLoElement->lo_cell.y) &&
  12489.                     (y <= pLoElement->lo_cell.y + pLoElement->lo_cell.y_offset + pLoElement->lo_any.height +
  12490.                             pLoElement->lo_cell.inter_cell_space ) &&
  12491.                     (x >= pLoElement->lo_cell.x) &&
  12492.                     (x <= (pLoElement->lo_cell.x + pLoElement->lo_cell.x_offset + pLoElement->lo_any.width +
  12493.                             pLoElement->lo_cell.inter_cell_space )))
  12494.                 {
  12495.  
  12496.                     pCellElement = pLoElement;
  12497.  
  12498.                     // Search its contents for nested table and cell
  12499.                     LO_Element *pNestedCell = NULL;
  12500.                     LO_Element *pNestedTable = edt_XYToNestedTableAndCell(pLoElement, x, y, &pNestedCell);
  12501.                     if( pNestedTable )
  12502.                     {
  12503.                         pTableElement = pNestedTable;
  12504.                         pCellElement = pNestedCell;
  12505.                     }
  12506.                     // We're done once we find the enclosing cell
  12507.                     break;
  12508.                 }
  12509.  
  12510.             }
  12511.             pLoElement = pLoElement->lo_any.next;
  12512.         }
  12513.     }
  12514.  
  12515.     // Now check where in Table and Cell the cursor is over
  12516.     if (pTableElement)
  12517.     {
  12518.         // First check for cell hit regions
  12519.         if(pCellElement)
  12520.         {
  12521.             // Return the cell found, even if result is ED_HIT_NONE
  12522.             if(ppElement) *ppElement = pCellElement;
  12523.             
  12524.             // Set test limits for Cell element
  12525.             edt_SetHitLimits(pCellElement, &left_limit, &top_limit, &right_limit, &bottom_limit, &right, &bottom, FALSE );
  12526.                         
  12527.             // We are in multiple cell selection mode,
  12528.             //  so select the cell automatically,
  12529.             //  but be sure we are not in inter-cell area (too confusing)
  12530.             //  Also leave area near bottom for drag selection
  12531.             if( m_SelectedLoCells.Size() && bModifierKeyPressed &&
  12532.                 x <= right && y <= bottom_limit )
  12533.             {
  12534.                 return ED_HIT_SEL_CELL;
  12535.             }
  12536.             
  12537.             if( x <= left_limit )
  12538.             {
  12539.                 // Leave left edge for placing cursor
  12540.                 return ED_HIT_NONE;
  12541.             }
  12542.             if( x >= right_limit )
  12543.             {
  12544.                 if( x >= right_limit ||  // Anywhere in inter-cell space to the right of the cell
  12545.                    (y >= top_limit && y <= bottom_limit) ) // Inside cell, near right edge, excluding corners
  12546.                 {
  12547.                     // If a selected cell - drag selection
  12548.                     if( pCellElement->lo_cell.ele_attrmask & LO_ELE_SELECTED )
  12549.                         return ED_HIT_DRAG_TABLE;
  12550.  
  12551.                     return ED_HIT_SIZE_COL;
  12552.                 }
  12553.                 
  12554.                 if( y >= bottom_limit )
  12555.                     // Lower right corner - leave for sizing object in cell
  12556.                     return ED_HIT_NONE;
  12557.             }
  12558.  
  12559.             if( y <= top_limit )
  12560.             {
  12561.                 // Along top edge
  12562.                 if( x <= right_limit )
  12563.                 {
  12564.                     // Near top border of cell, excluding corners
  12565.                     return ED_HIT_SEL_CELL;
  12566.                 }
  12567.                 // Upper right corner - leave for sizing image flush against borders
  12568.                 return ED_HIT_NONE;
  12569.             }
  12570.  
  12571.             if( y >= bottom_limit )  // Along bottom edge (including intercell region)
  12572.             {
  12573.                 // If a selected cell - drag selection
  12574.                 if( pCellElement->lo_cell.ele_attrmask & LO_ELE_SELECTED )
  12575.                     return ED_HIT_DRAG_TABLE;
  12576.                 
  12577.                 // Cell is not selected - size the row
  12578.                 return ED_HIT_SIZE_ROW;
  12579.             }
  12580.  
  12581.  
  12582.             // Inside of a cell and no hit regions
  12583.             return ED_HIT_NONE;
  12584.         }
  12585.  
  12586.         // Return the table element
  12587.         if( ppElement) *ppElement = pTableElement;
  12588.  
  12589.         // Set test limits for Table element
  12590.         edt_SetHitLimits(pTableElement, &left_limit, &top_limit, &right_limit, &bottom_limit, 0, 0, FALSE );
  12591.  
  12592.         if( x <= left_limit )
  12593.         {
  12594.             // Within left border of table
  12595.             if( y <= top_limit )
  12596.             {
  12597.                 // Upper left corner
  12598.                 if( bModifierKeyPressed )
  12599.                     return ED_HIT_SEL_ALL_CELLS;
  12600.  
  12601.                 return ED_HIT_SEL_TABLE; 
  12602.             }
  12603.             if( y >= bottom_limit )
  12604.                 // Lower left corner
  12605.                 return ED_HIT_ADD_ROWS;
  12606.             
  12607.             // Along left edge of table - select row
  12608.             if( ppElement )
  12609.             { 
  12610.                 *ppElement = lo_GetFirstCellInColumnOrRow(m_pContext, pTableElement, x, y, GET_ROW, NULL);
  12611.             }
  12612.             return ED_HIT_SEL_ROW;
  12613.         } 
  12614.         else if( x >= right_limit )
  12615.         {
  12616.             if( y >= bottom_limit )
  12617.                 // Lower right corner
  12618.                 return ED_HIT_ADD_COLS;
  12619.  
  12620.             // Along right edge
  12621.             return ED_HIT_SIZE_TABLE_WIDTH;
  12622.         }
  12623.         else if( y >= bottom_limit )
  12624.         {
  12625.             // Below bottom, excluding corners
  12626.             return ED_HIT_SIZE_TABLE_HEIGHT;
  12627.         }
  12628.  
  12629.         if( /*y >= top && */ y <= top_limit )    // CHECK THIS 
  12630.         {
  12631.             // Along top border - select column
  12632.             if( ppElement)
  12633.             { 
  12634.                 *ppElement = lo_GetFirstCellInColumnOrRow(m_pContext, pTableElement, x, y, GET_COL, NULL);
  12635.             }
  12636.             return ED_HIT_SEL_COL;
  12637.         }
  12638.     }
  12639.     return ED_HIT_NONE;
  12640. }
  12641.  
  12642. ED_DropType CEditBuffer::GetTableDropRegion(int32 *pX, int32 *pY, int32 *pWidth, int32 *pHeight, LO_Element **ppElement)
  12643. {
  12644.     if( !m_pDragTableData || !pX || !pY || !pWidth || !pHeight )
  12645.         return ED_DROP_NORMAL;
  12646.  
  12647.     ED_HitType  iSourceType = m_pDragTableData->iSourceType;
  12648.     ED_DropType iDropType = ED_DROP_NORMAL;
  12649.  
  12650.     // Initialize in case we don't find a table element
  12651.     if( ppElement)
  12652.         *ppElement = NULL;
  12653.  
  12654.     // Filter out types that are not applicable
  12655.     if( ! (iSourceType == ED_HIT_SEL_COL  ||
  12656.            iSourceType == ED_HIT_SEL_ROW  || 
  12657.            iSourceType == ED_HIT_SEL_CELL ||
  12658.            iSourceType == ED_HIT_SEL_ALL_CELLS) )
  12659.     {
  12660.         return ED_DROP_NORMAL;
  12661.     }
  12662.  
  12663.  
  12664.     // From LO_XYToElement
  12665.     int32 doc_id = XP_DOCID(m_pContext);
  12666.     lo_TopState *top_state = lo_FetchTopState(doc_id);
  12667.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  12668.         return ED_DROP_NORMAL;
  12669.  
  12670.     lo_DocState *state = top_state->doc_state;
  12671.  
  12672.     LO_Element *pLoElement = NULL;
  12673.     LO_Element *pEndLoElement;
  12674.     LO_Element *pTableElement = NULL;
  12675.     LO_Element *pCellElement = NULL;
  12676.     LO_Element *pFirstCell = NULL;
  12677.     XP_Bool     bLastCellInRow = FALSE;
  12678.     int32 x = *pX;
  12679.     int32 y = *pY;
  12680.     int32 line = lo_PointToLine(m_pContext, state, x, y);
  12681.  
  12682.     int32 iLeft, iTop, iRight, iBottom;
  12683.     int32 left_limit, right_limit, top_limit, bottom_limit, right, bottom;
  12684.   
  12685.     lo_GetLineEnds(m_pContext, state, line, &pLoElement, &pEndLoElement);
  12686.  
  12687.     while (pLoElement != pEndLoElement)
  12688.     {
  12689.         if (pLoElement->type == LO_TABLE)
  12690.         {
  12691.             // Save table if Y value is ANYWHERE inside the table
  12692.             // (if outside this, then there can't be a table on the line)
  12693.             if( (y >= pLoElement->lo_any.y) &&
  12694.                 (y <= pLoElement->lo_any.y + pLoElement->lo_any.y_offset + pLoElement->lo_any.height) )
  12695.             {
  12696.                 pTableElement = pLoElement;
  12697.                 LO_TableStruct *pTable = (LO_TableStruct*)pTableElement;
  12698.  
  12699.                 // Snap cursor coordinates to bounding rect of all cells in the table
  12700.                 //  so we find correct cell in first pass when cursor is to the left
  12701.                 //  or right of the this rect (in table borders or margins before and after table)
  12702.                 // (We will not find a cell only when there are ragged-right rows in the table)
  12703.  
  12704.                 iLeft = pTable->x + pTable->x_offset + pTable->border_left_width + pTable->inter_cell_space;
  12705.                 iRight = pTable->x + pTable->x_offset + pTable->width;
  12706.                 iTop = pTable->y + pTable->y_offset + pTable->border_top_width + pTable->inter_cell_space;
  12707.                 iBottom = pTable->y + pTable->y_offset + pTable->height;
  12708.  
  12709.                 x = min(iRight, max(x, iLeft));
  12710.                 y = min(iBottom, max(y, iTop));
  12711.             }
  12712.         }
  12713.         else if( pLoElement->type == LO_CELL )
  12714.         {
  12715.             // Save the first cell
  12716.             if( !pFirstCell )
  12717.                 pFirstCell = pLoElement;
  12718.  
  12719.             int32 iCellRight;
  12720.             LO_Element *pNextCell = pLoElement->lo_any.next;
  12721.             while( pNextCell && pNextCell->type != LO_CELL ) pNextCell = pNextCell->lo_any.next;
  12722.             
  12723.             if( pNextCell && pNextCell->lo_cell.x < pLoElement->lo_cell.x )
  12724.             {
  12725.                 // This is last cell in current row, so we can consider inserting at AFTER
  12726.                 bLastCellInRow = TRUE;
  12727.                 // Extend hit region to rightmost edge of cell area
  12728.                 iCellRight = iRight;
  12729.             }
  12730.             else 
  12731.             {
  12732.                 iCellRight = pLoElement->lo_cell.x + pLoElement->lo_cell.x_offset + pLoElement->lo_any.width;
  12733.             }   
  12734.  
  12735.             // Note that we include the inter-cell space BEFORE the cell when testing if inside a cell
  12736.             if ((y >= pLoElement->lo_cell.y - pLoElement->lo_cell.inter_cell_space) &&
  12737.                 (y <= pLoElement->lo_cell.y + pLoElement->lo_cell.y_offset + pLoElement->lo_any.height) &&
  12738.                 (x >= pLoElement->lo_cell.x - pLoElement->lo_cell.inter_cell_space) &&
  12739.                 (x <= iCellRight))
  12740.             {
  12741.  
  12742.                 pCellElement = pLoElement;
  12743.  
  12744.                 // Search its contents for nested table and cell
  12745.                 LO_Element *pNestedCell = NULL;
  12746.                 LO_Element *pNestedTable = edt_XYToNestedTableAndCell(pLoElement, x, y, &pNestedCell);
  12747.                 if( pNestedTable )
  12748.                 {
  12749.                     pTableElement = pNestedTable;
  12750.                     pCellElement = pNestedCell;
  12751.                 }
  12752.                 // We're done once we find the enclosing cell
  12753.                 break;
  12754.             }
  12755.         }
  12756.         pLoElement = pLoElement->lo_any.next;
  12757.     }
  12758.  
  12759.     if( pTableElement && pFirstCell )
  12760.     {
  12761. #if 0
  12762. // TODO: FINISH THIS!
  12763.         if( !pCellElement )
  12764.         {
  12765.             // Get bounding sides of all cells in table
  12766.             LO_Element *pLastCellInRow = NULL;
  12767.             LO_Element *pLastCellInCol = NULL;
  12768.             lo_GetFirstAndLastCellsInColumnOrRow(pCellElement, &pLastCellInRow, FALSE );
  12769.             lo_GetFirstAndLastCellsInColumnOrRow(pCellElement, &pLastCellInCol, TRUE );
  12770.             if( !pLastCellInRow || !pLastCellInCol )
  12771.                 return ED_DROP_NORMAL; 
  12772.             int32 iLeft = pFirstCell->lo_any.x;
  12773.             int32 iTop =  pFirstCell->lo_any.y;
  12774.             int32 iRight = pLastCellInRow->lo_any.x + pLastCellInRow->lo_any.width;
  12775.             int32 iBottom = pLastCellInRow->lo_any.y + pLastCellInRow->lo_any.height;
  12776.             
  12777.             XP_Bool bFindBefore = FALSE;
  12778.             XP_Bool bFindAfter = FALSE;
  12779.             XP_Bool bFindAbove = FALSE;
  12780.             XP_Bool bFindBelow = FALSE;
  12781.             if( iSourceType != ED_HIT_SEL_COL )
  12782.             {
  12783.                 if( y < pFirstCell->lo_any.y )
  12784.                     bFindAbove = TRUE;
  12785.                 else {
  12786.                     bFindBelow = TRUE;
  12787.             }
  12788.             if( iSourceType != ED_HIT_SEL_ROW )
  12789.             {
  12790.                 if( x < pFirstCell->lo_any.x )
  12791.                     bFindBefore = TRUE;
  12792.                 else
  12793.                     bFindAfter = TRUE;
  12794.             }
  12795.             if(! bFindBefore || !bFindAbove || !bFindAfter || !bFindBelow )
  12796.             {
  12797.                 if( 
  12798.             }
  12799.  
  12800.             LO_Element *pLoElement = pLoTable->lo_any.next;
  12801.             int32 iMaxX = 0;
  12802.             int32 iMaxY = 0;
  12803.  
  12804.             // Find cell closest to table edge we figured out above
  12805.             while( pLoElement && pLoElement->type != LO_LINEFEED)
  12806.             {
  12807.                 
  12808.                 if( pLoElement->lo_any.type == LO_CELL )
  12809.                 {
  12810.                     if( y >= pLoElement->lo_any.y && y <= (pLoElement->lo_any.y + pLoElement->lo_any.height) )
  12811.                     {
  12812.                         if( bFindBefore )
  12813.                         {
  12814.                             pCellElement = pLoElement;
  12815.                             break;
  12816.                         }
  12817.                         if( 
  12818.                     }
  12819.                 }
  12820.                 pLoElement = pLoElement->lo_any.next;
  12821.             }
  12822.             if( !pCellElement )
  12823.             {
  12824.             }
  12825.         }
  12826. #endif
  12827.         if(pCellElement)
  12828.         {
  12829.             if(ppElement) *ppElement = pCellElement;
  12830.  
  12831.             // Return signal that we shouldn't drop here,
  12832.             //   we are exactly over the source selection
  12833.             if( pCellElement == m_pDragTableData->pFirstSelectedCell )
  12834.                 return ED_DROP_NONE;
  12835.             
  12836.             LO_Element *pBefore = pCellElement->lo_any.prev;
  12837.             while( pBefore->type != LO_CELL && pBefore->type != LO_TABLE )
  12838.                 pBefore = pBefore->lo_any.prev;
  12839.  
  12840.             // Save cell before only if it is first cell of selection being dragged
  12841.             if( pBefore->type == LO_TABLE || 
  12842.                 (pBefore->type == LO_CELL && pBefore->lo_cell.x >= pCellElement->lo_cell.x) )
  12843.             {
  12844.                 pBefore = NULL;
  12845.             }
  12846.  
  12847.             // Also don't allow dropping under other circumstances
  12848.             if( iSourceType == ED_HIT_SEL_COL && 
  12849.                 (pBefore != NULL && pBefore == m_pDragTableData->pFirstSelectedCell) )
  12850.             {
  12851.                 return ED_DROP_NONE;        
  12852.             }
  12853.             
  12854.             // Set test limits for Cell element: TRUE = get limits for Drag/Drop
  12855.             edt_SetHitLimits(pCellElement, &left_limit, &top_limit, &right_limit, &bottom_limit, &right, &bottom, TRUE );
  12856.                         
  12857.             *pX = pCellElement->lo_cell.x;
  12858.             *pY = pCellElement->lo_cell.y;
  12859.             int32 iExtra = pCellElement->lo_cell.inter_cell_space - ED_SIZING_BORDER;
  12860.  
  12861.             if( iSourceType != ED_HIT_SEL_ROW && (x <= left_limit || x > right) )
  12862.             {
  12863.                 // Position the feedback in the center of intercell region
  12864.                 //   or just before cell if intercell is too narrow
  12865.                 *pWidth = ED_SIZING_BORDER;
  12866.                 *pHeight = pCellElement->lo_cell.height;
  12867.                 if( x > right )
  12868.                 {
  12869.                     iDropType = ED_DROP_INSERT_AFTER;
  12870.                     *pX += pCellElement->lo_cell.width;
  12871.                     if( iExtra > 1 )
  12872.                         *pX += iExtra/2;
  12873.                 } else {
  12874.                     iDropType = ED_DROP_INSERT_BEFORE;
  12875.                     if( iExtra > 1 )
  12876.                         *pX -= (iExtra/2 + ED_SIZING_BORDER);
  12877.                     else
  12878.                         *pX -= ED_SIZING_BORDER;
  12879.                 }
  12880.             }
  12881.             if( iDropType == ED_DROP_NORMAL && iSourceType != ED_HIT_SEL_COL && (y <= top_limit || y > bottom) )
  12882.             {
  12883.                 *pWidth = pCellElement->lo_cell.width;
  12884.                 *pHeight = ED_SIZING_BORDER;;
  12885.                 if( y > bottom )
  12886.                 {
  12887.                     iDropType = ED_DROP_INSERT_BELOW;
  12888.                     *pY += pCellElement->lo_cell.height;
  12889.                     if( iExtra > 1 )
  12890.                         *pY += iExtra/2;
  12891.                 } else {
  12892.                     iDropType = ED_DROP_INSERT_ABOVE;
  12893.                     if( iExtra > 1 )
  12894.                         *pY -= (iExtra/2 + ED_SIZING_BORDER);
  12895.                     else
  12896.                         *pY -= ED_SIZING_BORDER;
  12897.                 }
  12898.             }
  12899.             if( iDropType == ED_DROP_NORMAL )
  12900.             {
  12901.                 // Inside of a cell and no hit regions
  12902.                 iDropType = ED_DROP_REPLACE_CELL;
  12903.                 *pWidth = pCellElement->lo_cell.width;
  12904.                 *pHeight = pCellElement->lo_cell.height;
  12905.             }
  12906.         }
  12907.     }
  12908.     return iDropType;
  12909. }
  12910.  
  12911. //
  12912. // Table and Cell selection 
  12913. //
  12914.  
  12915. // Internal version to select table cell
  12916. XP_Bool CEditBuffer::SelectTableElement(CEditTableCellElement *pCell, ED_HitType iHitType)
  12917. {
  12918.     if( pCell )
  12919.     {
  12920.         return SelectTableElement( pCell->GetX(), pCell->GetY(), 
  12921.                                    (LO_Element*)GetLoCell((CEditElement*)pCell),
  12922.                                    iHitType, FALSE, FALSE );
  12923.     }
  12924.     return FALSE;
  12925. }
  12926.  
  12927. // The only table and cell selection method exposed to FEs
  12928. XP_Bool CEditBuffer::SelectTableElement( int32 x, int32 y, LO_Element *pLoElement, ED_HitType iHitType,
  12929.                                          XP_Bool bModifierKeyPressed, XP_Bool bExtendSelection )
  12930. {
  12931.     if( iHitType == ED_HIT_NONE )
  12932.         return FALSE;
  12933.  
  12934.     // We will drag existing selection - so don't change selection
  12935.     if( iHitType == ED_HIT_DRAG_TABLE )
  12936.         return TRUE;
  12937.  
  12938.     // If not supplied, try to get appropriate element 
  12939.     //   and coordinates from current edit element
  12940.     if( !pLoElement )
  12941.         pLoElement = GetCurrentLoTableElement( iHitType, &x, &y );
  12942.  
  12943.     if( !pLoElement )
  12944.     {
  12945.         return FALSE;
  12946.     }
  12947.  
  12948.     // Properly end current non-cell selection if in progress
  12949.     if( IsSelecting() )
  12950.         EndSelection();
  12951.  
  12952.     // Extend selection from previous cell element
  12953.     //  to new element if SHIFT key is pressed
  12954.     if( bExtendSelection &&
  12955.         (iHitType == ED_HIT_SEL_COL ||
  12956.          iHitType == ED_HIT_SEL_ROW ||
  12957.          iHitType == ED_HIT_SEL_CELL) )
  12958.     {
  12959.         return ED_HIT_NONE != ExtendTableCellSelection(x,y);
  12960.     }
  12961.     m_pSelectedTableElement = NULL;
  12962.     m_TableHitType = ED_HIT_NONE;
  12963.     m_pPrevExtendSelectionCell = NULL;
  12964.     
  12965.     if( pLoElement->type == LO_TABLE &&
  12966.         (iHitType == ED_HIT_SEL_TABLE  ||
  12967.          iHitType == ED_HIT_SIZE_TABLE_WIDTH ||
  12968.          iHitType == ED_HIT_ADD_ROWS   ||
  12969.          iHitType == ED_HIT_ADD_COLS) )
  12970.     {
  12971.         LO_TableStruct *pLoTable =  &pLoElement->lo_table;
  12972.         // Remove any existing table or cell selection
  12973.         ClearTableAndCellSelection();
  12974.  
  12975.         CEditTableElement *pEdTable = SelectTable(TRUE, pLoTable);
  12976.         if( pEdTable )
  12977.         {
  12978.             if( IsSelected() )
  12979.                 ClearSelection();
  12980.  
  12981.             // Move the caret to the first cell in table being selected
  12982.             CEditElement *pLeaf = pEdTable->FindNextElement(&CEditElement::FindLeafAll,0 );
  12983.             if( pLeaf )
  12984.             {
  12985.                 ClearPhantomInsertPoint();
  12986.                 ClearMove();
  12987.                 FE_DestroyCaret(m_pContext);
  12988.                 SetInsertPoint( pLeaf->Leaf(), 0, FALSE );
  12989.             }
  12990.  
  12991.             // Save selected element data
  12992.             m_pSelectedTableElement = pLoElement;
  12993.             m_TableHitType = ED_HIT_SEL_TABLE;
  12994.             return TRUE;
  12995.         } else {
  12996.             return FALSE;
  12997.         }
  12998.     }
  12999.     
  13000.     if( bModifierKeyPressed )
  13001.     {
  13002.         // Remove just the selection of a table
  13003.         //  since that can never be appended
  13004.         SelectTable(FALSE);
  13005.     } else {
  13006.         ClearTableAndCellSelection();
  13007.     }
  13008.  
  13009.     if( iHitType == ED_HIT_SEL_CELL )
  13010.     {
  13011.         if( pLoElement->type != LO_CELL )
  13012.             return FALSE;
  13013.  
  13014.         // We need to set this so mouse-move event
  13015.         //  doesn't wipe out appended cell selections
  13016.         m_pPrevExtendSelectionCell = pLoElement;
  13017.  
  13018.         // If cell was already selected and "Append" key is pressed,
  13019.         //  UNSELECT that cell (First param = FALSE)
  13020.         XP_Bool bSelect = !(bModifierKeyPressed && (pLoElement->lo_cell.ele_attrmask & LO_ELE_SELECTED));
  13021.         CEditTableCellElement *pEdCell = SelectCell( bSelect, &pLoElement->lo_cell, NULL );
  13022.         if( pEdCell )
  13023.         {
  13024.             if( IsSelected() )
  13025.                 ClearSelection();
  13026.  
  13027.             // Move the caret to the cell being selected
  13028.             SetTableInsertPoint(pEdCell);
  13029.             if( bSelect )
  13030.             {
  13031.                 // Save selected element data if selecting cell
  13032.                 m_pSelectedTableElement = pLoElement;
  13033.             }
  13034.             // Set selection type
  13035.             // Note: Unselecting a cell will discontinue existing row or column selection
  13036.             // For efficiency, don't try to figure out if extending cell selection
  13037.             //   now results in a full row or column -- do that in GetTableCellData();
  13038.             m_TableHitType = ED_HIT_SEL_CELL;
  13039.             
  13040.             SortSelectedCells();
  13041.             return TRUE;
  13042.         } else {
  13043.             return FALSE;
  13044.         }
  13045.     }
  13046.  
  13047.     XP_Bool bSelRow = iHitType == ED_HIT_SEL_ROW;
  13048.     XP_Bool bSelCol = iHitType == ED_HIT_SEL_COL;
  13049.  
  13050.     CEditTableCellElement * pEdCell = NULL;
  13051.  
  13052.     if( bSelCol || bSelRow )
  13053.     {
  13054.         // Find cell closest to the given X or Y so that column or row
  13055.         //   doesn't include ROWSPAN or COLSPAN "spillover" cells
  13056.         LO_Element *pLastCellElement = NULL;
  13057.         
  13058.         LO_Element *pElement = lo_GetFirstCellInColumnOrRow(m_pContext, pLoElement, x, y, bSelCol, &pLastCellElement );
  13059.  
  13060.         int32 iColLeft, iRowTop;
  13061.         if( !pElement )
  13062.             return FALSE;
  13063.         if( bSelCol )
  13064.             // Left side defines the column
  13065.             iColLeft = pElement->lo_any.x;
  13066.         else
  13067.             // Top defines the row
  13068.             iRowTop = pElement->lo_any.y;
  13069.  
  13070.         //while( pElement && pElement != pLastCellElement )
  13071.         XP_Bool bDone = FALSE;
  13072.         do {             
  13073.             if( pElement && pElement->lo_any.type == LO_CELL )
  13074.             {
  13075.                 // Select cells in a column (cells that share left border value)
  13076.                 //  or a row (all cells sharing top border value)
  13077.                 if( (bSelCol && iColLeft == pElement->lo_any.x) ||
  13078.                     (bSelRow && iRowTop == pElement->lo_any.y) )
  13079.                 {
  13080.                     // Select the cell - returns the corresponding Edit Cell object
  13081.                     CEditTableCellElement * pSelectedCell = SelectCell(TRUE, &pElement->lo_cell, NULL);
  13082.  
  13083.                     // Save the edit cell and other data 
  13084.                     if( pElement == pLoElement )
  13085.                     {
  13086.                         pEdCell = pSelectedCell;
  13087.                         m_pSelectedTableElement = pElement;
  13088.                         m_pPrevExtendSelectionCell = pElement;
  13089.                     }
  13090.                 }
  13091.             }
  13092.             bDone = (pElement == NULL) || (pElement == pLastCellElement);
  13093.             pElement = pElement->lo_any.next;
  13094.         } while( !bDone );
  13095.  
  13096.         if( IsSelected() )
  13097.             ClearSelection();
  13098.         
  13099.         if( pEdCell )
  13100.         {
  13101.             // Move caret to edit cell 
  13102.             SetTableInsertPoint(pEdCell);
  13103.  
  13104.             
  13105.             // Decide new selection type
  13106.             // This will not be entirely correct if
  13107.             //  we are appending to an existing selection
  13108.             if( bModifierKeyPressed && m_TableHitType != ED_HIT_NONE && 
  13109.                 m_TableHitType != iHitType )
  13110.             {
  13111.                 // We are appending to a previous selection
  13112.                 //  that is NOT of the same type,
  13113.                 //  then we can't assume total selection is all rows or all columns
  13114.                 // But leave "All cells" type as is
  13115.                 if( !(m_TableHitType == ED_HIT_SEL_ALL_CELLS &&
  13116.                      iHitType == ED_HIT_SEL_ROW ) )
  13117.                 {
  13118.                     m_TableHitType = ED_HIT_SEL_CELL;
  13119.                 }
  13120.             } else {
  13121.                 m_TableHitType = iHitType;
  13122.             }
  13123.  
  13124.             SortSelectedCells();
  13125.  
  13126.             return TRUE;
  13127.         }
  13128. #ifdef DEBUG_akkana
  13129.         printf("CEditBuffer::SelectTableElement(): Ugh -- falling through!\n");
  13130. #endif /* DEBUG_akkana */
  13131.     }
  13132.  
  13133.     if( iHitType == ED_HIT_SEL_ALL_CELLS )
  13134.     {
  13135.         LO_Element *pLastCellElement = NULL;
  13136.         LO_Element *pElement = lo_GetFirstAndLastCellsInTable(m_pContext, pLoElement, &pLastCellElement );
  13137.         
  13138.         XP_Bool bDone = FALSE;
  13139.         do {             
  13140.             if( pElement && pElement->lo_any.type == LO_CELL )
  13141.             {
  13142.                 // Select the cell - returns the corresponding Edit Cell object
  13143.                 CEditTableCellElement * pSelectedCell = SelectCell(TRUE, &pElement->lo_cell, NULL);
  13144.  
  13145.                 // Save the edit cell and other data 
  13146.                 if( pElement == pLoElement )
  13147.                 {
  13148.                     pEdCell = pSelectedCell;
  13149.                     m_pSelectedTableElement = pElement;
  13150.                     m_pPrevExtendSelectionCell = pElement;
  13151.                 }
  13152.             }
  13153.             bDone = (pElement == pLastCellElement);
  13154.             pElement = pElement->lo_any.next;
  13155.         } while( pElement && !bDone );
  13156.         
  13157.         if( pEdCell )
  13158.         {
  13159.             // Move caret to edit cell 
  13160.             SetTableInsertPoint(pEdCell);
  13161.             // Assume we are selected as "rows"
  13162.             m_TableHitType = ED_HIT_SEL_ALL_CELLS;
  13163.             SortSelectedCells();
  13164.             return TRUE;
  13165.         }
  13166.     }
  13167.  
  13168.     // Not likely - no hit types found
  13169.     return FALSE;
  13170. }
  13171.  
  13172. void CEditBuffer::SelectBlockOfCells(LO_CellStruct *pLastCell)
  13173. {
  13174.     if( !pLastCell )
  13175.         return;
  13176.  
  13177.     // Clear any existing selected cells
  13178.     for( int i = m_SelectedLoCells.Size()-1; i >= 0; i-- )
  13179.     {
  13180.         SelectCell(FALSE, m_SelectedLoCells[i], m_SelectedEdCells[i]);
  13181.     }
  13182.  
  13183.     if( m_pSelectedTableElement && m_pSelectedTableElement->type == LO_CELL )
  13184.     {
  13185.         LO_CellStruct *pFirstCell = (LO_CellStruct*)m_pSelectedTableElement;
  13186.         int32 xMin = min(pFirstCell->x, pLastCell->x);
  13187.         int32 yMin = min(pFirstCell->y, pLastCell->y);
  13188.         int32 xMax = max(pFirstCell->x, pLastCell->x);
  13189.         int32 yMax = max(pFirstCell->y, pLastCell->y);
  13190.     
  13191.         // Scan back to first cell in table
  13192.         LO_Element *pElement = m_pSelectedTableElement;
  13193.         do {
  13194.             pElement = pElement->lo_any.prev;
  13195.         } while (pElement->type != LO_TABLE ); 
  13196.     
  13197.         // Scan forward and select all cells within desired coordinates
  13198.         pElement = pElement->lo_any.next;
  13199.         do {
  13200.             LO_CellStruct *pLoCell = (LO_CellStruct*)pElement;
  13201.             if( pLoCell->x >= xMin && pLoCell->x <= xMax &&
  13202.                 pLoCell->y >= yMin && pLoCell->y <= yMax )
  13203.             {
  13204.                 // Select the cell (3rd param: find EditCell, 4th: append selection)
  13205.                 SelectCell(TRUE, pLoCell, NULL /*, TRUE*/);
  13206.             }
  13207.             pElement = pElement->lo_any.next;
  13208.         } while (pElement && pElement->type == LO_CELL); 
  13209.     }
  13210.     else 
  13211.     {
  13212.         // No previous selected cell - select just given cell
  13213.         SelectCell(TRUE, pLastCell, NULL/*, TRUE*/);
  13214.     }
  13215. }
  13216.  
  13217. ED_HitType CEditBuffer::ExtendTableCellSelection(int32 x, int32 y)
  13218. {
  13219.     LO_Element *pLoElement = NULL;
  13220.     // Get the cell we are currently over
  13221.     ED_HitType iHitType = GetTableHitRegion(x, y, &pLoElement, TRUE);
  13222.  
  13223.     // Eliminate incompatable conditions to extend the selection
  13224.     if( !pLoElement ||
  13225.         !(iHitType == ED_HIT_SEL_ROW ||
  13226.           iHitType == ED_HIT_SEL_COL ||
  13227.           iHitType == ED_HIT_SEL_CELL ) ||
  13228.         !(m_TableHitType == ED_HIT_SEL_ROW ||
  13229.           m_TableHitType == ED_HIT_SEL_COL ||
  13230.           m_TableHitType == ED_HIT_SEL_CELL ) ||
  13231.         (m_TableHitType == ED_HIT_SEL_ROW &&
  13232.          iHitType == ED_HIT_SEL_COL) ||
  13233.         (m_TableHitType == ED_HIT_SEL_COL &&
  13234.          iHitType == ED_HIT_SEL_ROW) ||
  13235.         (m_TableHitType == ED_HIT_SEL_CELL &&
  13236.          iHitType != ED_HIT_SEL_CELL) )
  13237.     {
  13238.         return ED_HIT_NONE;
  13239.     }
  13240.  
  13241.     // Range of selected cells
  13242.     LO_Element *pFirstLoCell = NULL;
  13243.     LO_Element *pLastLoCell = NULL;
  13244.  
  13245.     if( m_TableHitType == ED_HIT_SEL_CELL )
  13246.     {
  13247.         // We are extending a previous cell selection
  13248.         pFirstLoCell = pLastLoCell = pLoElement;
  13249.     }
  13250.     else
  13251.     {
  13252.         // Get the last cell of row or column
  13253.         pFirstLoCell = lo_GetFirstAndLastCellsInColumnOrRow(pLoElement, &pLastLoCell, m_TableHitType == ED_HIT_SEL_COL);
  13254.     }
  13255.  
  13256.     if( pFirstLoCell && pFirstLoCell != m_pPrevExtendSelectionCell )
  13257.     {
  13258.         // Reselect block starting from previous start to end of current addition
  13259.         SelectBlockOfCells( (LO_CellStruct*)pLastLoCell );
  13260.         
  13261.         // Remember the first cell
  13262.         // We must use first instead of last so SelectTableElement
  13263.         //   can use the result of GetTableHitRegion (which is first cell of col or row)
  13264.         //   to suppress conflicts between setting first selection and extending selections 
  13265.         m_pPrevExtendSelectionCell = pFirstLoCell;
  13266.     }
  13267.     // Return same select type as we used before
  13268.     return m_TableHitType;
  13269. }
  13270.  
  13271. LO_Element* CEditBuffer::GetCurrentLoTableElement(ED_HitType iHitType, int32 *pX, int32 *pY)
  13272. {
  13273.     // Get current leaf element at caret or selection
  13274.     CEditInsertPoint ip;
  13275.     GetTableInsertPoint(ip);
  13276.     if( ip.m_pElement && ip.m_pElement->GetLayoutElement() )
  13277.     {
  13278.         // Table select hit areas
  13279.         if( iHitType == ED_HIT_SEL_TABLE  ||
  13280.             iHitType == ED_HIT_SIZE_TABLE_WIDTH ||
  13281.             iHitType == ED_HIT_ADD_ROWS   ||
  13282.             iHitType == ED_HIT_ADD_COLS )
  13283.         {
  13284.             LO_TableStruct *pLoTable = lo_GetParentTable(m_pContext, ip.m_pElement->GetLayoutElement());
  13285.             if( pLoTable )
  13286.             {
  13287.                 *pX = pLoTable->x+1;
  13288.                 *pY = pLoTable->y+1;
  13289.             }
  13290.             return (LO_Element*)pLoTable;
  13291.         }
  13292.  
  13293.         LO_CellStruct *pLoCell = lo_GetParentCell(m_pContext, ip.m_pElement->GetLayoutElement());
  13294.         if( pLoCell )
  13295.         {
  13296.             if( iHitType == ED_HIT_SEL_CELL || iHitType == ED_HIT_SEL_ALL_CELLS )
  13297.             {
  13298.                 *pX = pLoCell->x+1;
  13299.                 *pY = pLoCell->y+1;
  13300.                 return (LO_Element*)pLoCell;
  13301.             }
  13302.             if( iHitType == ED_HIT_SEL_ROW || iHitType == ED_HIT_SEL_COL )
  13303.             {
  13304.                 // Find the first cell of the row or column
  13305.                 *pX = pLoCell->x+1;
  13306.                 *pY = pLoCell->y+1;
  13307.                 return (LO_Element*)pLoCell;
  13308.             }
  13309.         }
  13310.     }
  13311.     return NULL;
  13312. }
  13313.  
  13314. // Get the corresponding CEditTableElement if LoElementType == LO_TABLE,
  13315. //   or CEditTableCellElement if LoElementType = LO_CELL
  13316. CEditElement* CEditBuffer::GetTableElementFromLO_Element(LO_Element *pLoElement, int16 LoElementType)
  13317. {
  13318.     if( pLoElement &&
  13319.         (LoElementType == LO_TABLE ||
  13320.          LoElementType == LO_CELL) )
  13321.     {
  13322.         LO_Element *pTemp = pLoElement;
  13323.  
  13324.         // The editor assures that every cell has an editable element in it
  13325.         // so find the first CEditElement and get CEditTable or CEditTableCell
  13326.         //   object containing it
  13327.         while ( pTemp != NULL )
  13328.         {
  13329.             // Find first cell (should hit immediately if pLoElement is a cell,
  13330.             //   else looks for first cell if pLoElement is a table
  13331.             if( pTemp->lo_any.type == LO_CELL )
  13332.             {
  13333.                 LO_Element *pCellElement  = pTemp->lo_cell.cell_list;
  13334.  
  13335.                 // Find first editable element inside the cell
  13336.                 while( pCellElement )
  13337.                 {
  13338.                     if( pCellElement->lo_any.edit_element )
  13339.                         break;
  13340.                     // TODO: CHECK IF CELL CONTAINS ANOTHER TABLE HERE?
  13341.  
  13342.                     pCellElement = pCellElement->lo_any.next;
  13343.                 }
  13344.                 if(!pCellElement )
  13345.                 {
  13346.                     // None found - should not happen
  13347.                     XP_ASSERT(FALSE);
  13348.                     return NULL;
  13349.                 }
  13350.             
  13351.                 if( LoElementType == LO_TABLE )
  13352.                     return pCellElement->lo_any.edit_element->GetTableIgnoreSubdoc();
  13353.                 else
  13354.                     return pCellElement->lo_any.edit_element->GetTableCellIgnoreSubdoc();
  13355.             }
  13356.             
  13357.             pTemp = pTemp->lo_any.next;
  13358.         }
  13359.     }
  13360.     // We should always find an Edit element
  13361.     XP_ASSERT(FALSE);
  13362.     return NULL;
  13363. }
  13364.  
  13365. LO_CellStruct* CEditBuffer::GetLoCell(CEditElement *pEdElement)
  13366. {
  13367.     // Get closest leaf element so we can find the corresponding layout element
  13368.     if( !pEdElement || !pEdElement->IsInTableCell() )
  13369.         return NULL;
  13370.     if( !pEdElement->IsLeaf() )
  13371.     {
  13372.         pEdElement = pEdElement->FindNextElement(&CEditElement::FindLeafAll,0 );
  13373.     }
  13374.     if( pEdElement && pEdElement->Leaf()->GetLayoutElement() )
  13375.     {    
  13376.         return lo_GetParentCell(m_pContext, pEdElement->Leaf()->GetLayoutElement());
  13377.     }
  13378.     return NULL;
  13379. }
  13380.  
  13381. CEditTableElement* CEditBuffer::SelectTable(XP_Bool bSelect, LO_TableStruct *pLoTable,
  13382.                               CEditTableElement *pEdTable)
  13383. {
  13384.     // We can allow NULL pointer for table if we are just unselecting existing table
  13385.     if( bSelect && !pLoTable )
  13386.     {
  13387.         XP_ASSERT(FALSE);
  13388.         return NULL;
  13389.     }
  13390.     // Assume we are deselecting current table if no pointers supplied
  13391.     if( !bSelect && !pLoTable && !pEdTable )
  13392.     {
  13393.         pLoTable = m_pSelectedLoTable;
  13394.         pEdTable = m_pSelectedEdTable;
  13395.     }
  13396.     if( !pLoTable )
  13397.         return NULL;
  13398.  
  13399.     XP_Bool bWasSelected = bSelect ? pLoTable->ele_attrmask & LO_ELE_SELECTED : FALSE;
  13400.  
  13401.     // Clear existing selected table if different
  13402.     if( m_pSelectedLoTable && pLoTable != m_pSelectedLoTable )
  13403.     {
  13404.         m_pSelectedLoTable->ele_attrmask &= ~LO_ELE_SELECTED;
  13405.  
  13406.         FE_DisplayEntireTableOrCell(m_pContext, (LO_Element*)m_pSelectedLoTable);
  13407.         m_pSelectedLoTable = NULL;
  13408.  
  13409.         // We should always have the matching CEditTableElement
  13410.         XP_ASSERT(m_pSelectedEdTable);
  13411.  
  13412.         m_pSelectedEdTable = NULL;
  13413.     }
  13414.     if( !pLoTable )
  13415.         return NULL;
  13416.  
  13417.     XP_TRACE(("Table width = %d", pLoTable->width));
  13418.  
  13419.     // Get Edit table element if not supplied
  13420.     if( !pEdTable )
  13421.         pEdTable = (CEditTableElement*)GetTableElementFromLO_Element( (LO_Element*)pLoTable, LO_TABLE );
  13422.  
  13423.     XP_ASSERT(pEdTable);
  13424.     if( !pEdTable )
  13425.         return NULL;
  13426.  
  13427.     XP_Bool bChanged;
  13428.  
  13429.     if( bSelect )
  13430.     {
  13431.         // Select the table and save pointers
  13432.         pLoTable->ele_attrmask |= LO_ELE_SELECTED;
  13433.         m_pSelectedEdTable = pEdTable;
  13434.         m_pSelectedLoTable = pLoTable;
  13435.         bChanged = !bWasSelected;
  13436.     } 
  13437.     else 
  13438.     {
  13439.         // Unselect the table
  13440.         pLoTable->ele_attrmask &= ~LO_ELE_SELECTED;
  13441.         bChanged = bWasSelected;
  13442.     }        
  13443.  
  13444.     // Redisplay only if changed from previous state
  13445.     if( bChanged )
  13446.         FE_DisplayEntireTableOrCell(m_pContext, (LO_Element*)pLoTable);
  13447.     return pEdTable;
  13448. }
  13449.  
  13450. CEditTableCellElement* CEditBuffer::SelectCell(XP_Bool bSelect, LO_CellStruct *pLoCell, 
  13451.                              CEditTableCellElement *pEdCell)
  13452. {
  13453.     // We must have one type of cell or the other
  13454.     XP_ASSERT(pLoCell || pEdCell);
  13455.     if( !pLoCell )
  13456.     {
  13457.         pLoCell = GetLoCell((CEditElement*)pEdCell);
  13458.         if( !pLoCell )
  13459.             return NULL;
  13460.     }
  13461.  
  13462.     XP_Bool bWasSelected = (XP_Bool)pLoCell->ele_attrmask & LO_ELE_SELECTED;
  13463.  
  13464.     // Get corresponding Edit cell if not supplied
  13465.     if( !pEdCell )
  13466.         pEdCell = (CEditTableCellElement*)GetTableElementFromLO_Element( (LO_Element*)pLoCell, LO_CELL );
  13467.  
  13468.     if(bSelect && !pEdCell)
  13469.         return NULL;
  13470.  
  13471.     XP_Bool bChanged;
  13472.     int iSize = m_SelectedEdCells.Size();
  13473.  
  13474.     if( bSelect )
  13475.     {
  13476.         // Select the cell
  13477.         pLoCell->ele_attrmask |= LO_ELE_SELECTED;
  13478.  
  13479.         // Add pointers to lists if not already in the respective list
  13480.         int i;
  13481.         CEditTableCellElement *pNewEdCell = pEdCell;
  13482.         LO_CellStruct *pNewLoCell = pLoCell;
  13483.         for( i = 0; i < iSize; i++ )
  13484.         {
  13485.             if( m_SelectedEdCells[i] == pEdCell )
  13486.             {
  13487.                 pNewEdCell = NULL;
  13488.                 // Be sure the Layout array matches the EditArray in size
  13489.                 // A bit risky, but most efficient for relayout usage
  13490.                 m_SelectedLoCells.SetSize(iSize);
  13491.                 m_SelectedLoCells[i] = pLoCell;
  13492.                 break;
  13493.             }
  13494.         }
  13495.         for( i = 0; i < m_SelectedLoCells.Size(); i++ )
  13496.         {
  13497.             if( m_SelectedLoCells[i] == pLoCell )
  13498.             {
  13499.                 pNewLoCell = NULL;
  13500.                 break;
  13501.             }
  13502.         }
  13503.         if( pNewEdCell && pNewLoCell )
  13504.         {
  13505.             // Returns index to last element, add 1 for size
  13506.             iSize = m_SelectedEdCells.Add(pNewEdCell) + 1;
  13507.             m_SelectedLoCells.Add(pNewLoCell);
  13508.         }
  13509.  
  13510. #ifdef DEBUG
  13511.         // TRACE CELL
  13512. //        if( bSelect )
  13513. //            XP_TRACE(("Cell: Row= %d, X= %d, Y= %d, width= %d", pEdCell->GetRow(), pEdCell->GetX(), pEdCell->GetY(), pLoCell->width));
  13514. #endif
  13515.         bChanged = !bWasSelected;
  13516.     } 
  13517.     else 
  13518.     {
  13519.         // Delete existing cell from list
  13520.         iSize = m_SelectedEdCells.Delete(pEdCell) + 1;
  13521.         m_SelectedLoCells.Delete(pLoCell);
  13522.  
  13523.         // Unselect the cell, including "special" selection style
  13524.         pLoCell->ele_attrmask &= ~(LO_ELE_SELECTED | LO_ELE_SELECTED_SPECIAL);
  13525.         bChanged = bWasSelected;
  13526.     }        
  13527.  
  13528. #ifdef DEBUG
  13529.     if( iSize != m_SelectedLoCells.Size() )
  13530.         XP_TRACE(("Edit Array Size = %d, LO Array Size = %d",iSize, m_SelectedLoCells.Size()));
  13531. #endif
  13532.  
  13533.     // Redisplay only if changed from previous state
  13534.     //XP_TRACE(("Display Cell - Select=%d, Changed=%d",bSelect,bChanged));
  13535.     if( bChanged )
  13536.         FE_DisplayEntireTableOrCell(m_pContext, (LO_Element*)pLoCell);
  13537.     return pEdCell;    
  13538. }
  13539.  
  13540. void CEditBuffer::DisplaySpecialCellSelection( CEditTableCellElement *pFocusCell,  EDT_TableCellData *pCellData )
  13541. {
  13542.     XP_Bool bCellFound = FALSE;
  13543.     intn iSelectedCount = m_SelectedLoCells.Size(); 
  13544.  
  13545.     // PROBLEM: If we don't clear table selection, we end up with table and cell
  13546.     //  selected when examining table properties. Simplest solution is to 
  13547.     //  clear table selection
  13548.     if( m_pSelectedLoTable )
  13549.         SelectTable(FALSE);        
  13550.  
  13551.     for( intn i = 0; i < iSelectedCount; i++ )
  13552.     {
  13553.         LO_CellStruct *pLoCell = m_SelectedLoCells[i];
  13554.         CEditTableCellElement *pEdCell = m_SelectedEdCells[i];
  13555.         XP_ASSERT(pEdCell);
  13556.         XP_Bool bWasSpecial = (XP_Bool)pLoCell->ele_attrmask & LO_ELE_SELECTED_SPECIAL;
  13557.  
  13558.         if( pEdCell == pFocusCell )
  13559.         {
  13560.             // Remove the special selection from the focus cell
  13561.             if( bWasSpecial )
  13562.             {
  13563.                 pLoCell->ele_attrmask &= ~LO_ELE_SELECTED_SPECIAL;
  13564.                 FE_DisplayEntireTableOrCell(m_pContext, (LO_Element*)pLoCell);
  13565.             }
  13566.             bCellFound = TRUE;
  13567.         }
  13568.         else if( !bWasSpecial )
  13569.         {
  13570.             // Set special selection attribute in addition to existing "normal" selection
  13571.             pLoCell->ele_attrmask |= LO_ELE_SELECTED_SPECIAL;
  13572.             FE_DisplayEntireTableOrCell(m_pContext, (LO_Element*)pLoCell);
  13573.         }
  13574.     }
  13575.     // Select the focus cell if it was not found to be
  13576.     //  selected above
  13577.     if( pFocusCell && !bCellFound )
  13578.     {
  13579.         // If no other cells were selected,
  13580.         //   save single-cell selection type
  13581.         if( iSelectedCount == 0 )
  13582.             m_TableHitType = ED_HIT_SEL_CELL;
  13583.         SelectCell(TRUE, NULL, pFocusCell);
  13584.     }
  13585.  
  13586.     // Update cell data. Used primarily when EDT_StartSpecialCellSelection()
  13587.     //   is called when no cells were previously selected
  13588.     if( pCellData )
  13589.     {
  13590.         pCellData->iSelectionType = m_TableHitType;
  13591.         pCellData->iSelectedCount = m_SelectedLoCells.Size();
  13592.     }
  13593. }
  13594.  
  13595. // Start multi-cell selection highlighting with cell containing the caret
  13596. void CEditBuffer::StartSpecialCellSelection(EDT_TableCellData *pCellData)
  13597. {
  13598.     CEditTableCellElement* pEdCell = m_pCurrent->GetTableCellIgnoreSubdoc();
  13599.     if( pEdCell )
  13600.         DisplaySpecialCellSelection( pEdCell, pCellData );
  13601. }
  13602.  
  13603. static void edt_ClearSpecialCellSelection(MWContext *pContext, LO_CellStruct *pLoCell)
  13604. {
  13605.     if( pContext && pLoCell && (pLoCell->ele_attrmask & LO_ELE_SELECTED_SPECIAL) )
  13606.     {
  13607.         pLoCell->ele_attrmask &= ~LO_ELE_SELECTED_SPECIAL;
  13608.         FE_DisplayEntireTableOrCell(pContext, (LO_Element*)pLoCell);
  13609.     }
  13610. }
  13611.  
  13612. void CEditBuffer::ClearSpecialCellSelection()
  13613. {
  13614.     for( intn i = 0; i < m_SelectedLoCells.Size(); i++ )
  13615.     {
  13616.         edt_ClearSpecialCellSelection(m_pContext, m_SelectedLoCells[i]);
  13617.     }
  13618. }
  13619.  
  13620. // Sort cell lists so simple traversal through array 
  13621. //  moves row-wise from upper left corner to lower right corner of table
  13622. void CEditBuffer::SortSelectedCells()
  13623. {
  13624.     int iCount = m_SelectedLoCells.Size();
  13625.     if( iCount <= 1 )
  13626.         return;
  13627.     
  13628.     // Optimization for only two cells selected
  13629.     if( iCount == 2 )
  13630.     {
  13631.         if(m_SelectedLoCells[1]->x < m_SelectedLoCells[0]->x &&
  13632.            m_SelectedLoCells[1]->y <= m_SelectedLoCells[0]->y )
  13633.         {
  13634.             LO_CellStruct *pLoCell = m_SelectedLoCells[0];
  13635.             m_SelectedLoCells[0] = m_SelectedLoCells[1];
  13636.             m_SelectedLoCells[1] = pLoCell;   
  13637.  
  13638.             CEditTableCellElement *pEdElement = m_SelectedEdCells[0];
  13639.             m_SelectedEdCells[0] = m_SelectedEdCells[1];
  13640.             m_SelectedEdCells[1] = pEdElement;   
  13641.         }
  13642.         return;
  13643.     }
  13644.  
  13645.     XP_ASSERT(iCount == m_SelectedEdCells.Size());
  13646.  
  13647.     LO_CellStruct **pNewLoCells = (LO_CellStruct**)XP_ALLOC(iCount*sizeof(LO_CellStruct*));
  13648.     if( !pNewLoCells )
  13649.         return;
  13650.  
  13651.     CEditTableCellElement **pNewEdCells = (CEditTableCellElement**)XP_ALLOC(iCount*sizeof(CEditTableCellElement*));
  13652.     if( !pNewEdCells )
  13653.     {
  13654.         XP_FREE(pNewLoCells);
  13655.         return;
  13656.     }
  13657.  
  13658.     LO_Element *pLoElement = (LO_Element*)m_SelectedLoCells[0];
  13659.     LO_Element *pFirstSelected = NULL;
  13660.  
  13661.     // Scan backwards until we hit the table element to find-selected cell
  13662.     do {
  13663.         if( pLoElement->type == LO_CELL && (pLoElement->lo_cell.ele_attrmask & LO_ELE_SELECTED) )
  13664.             pFirstSelected = pLoElement;
  13665.  
  13666.         pLoElement = pLoElement->lo_any.prev;
  13667.     } while (pLoElement->type != LO_TABLE ); 
  13668.     
  13669.     
  13670.     if( pFirstSelected ) // Should never fail
  13671.     {
  13672.         pLoElement = pFirstSelected;
  13673.         int i = 0;
  13674.         do {
  13675.             if( pLoElement->lo_cell.ele_attrmask & LO_ELE_SELECTED )
  13676.             {
  13677.                 // Build new lists of both LO and EDIT elements
  13678.                 pNewLoCells[i] = (LO_CellStruct*)pLoElement;
  13679.                 
  13680.                 // Find the corresponding Edit element from the old lists
  13681.                 int iIndex = m_SelectedLoCells.Find(pLoElement);
  13682.                 //XP_ASSERT(iIndex >= 0);
  13683.                 if( iIndex >= 0 )
  13684.                     pNewEdCells[i] = m_SelectedEdCells[iIndex];
  13685.                 else
  13686.                     // THIS IS A PROBLEM -- WE SHOULD NEVER GET HERE
  13687.                     pNewEdCells[i] = (CEditTableCellElement*)GetTableElementFromLO_Element(pLoElement, LO_CELL);
  13688.                 i++;
  13689.             }
  13690.             pLoElement = pLoElement->lo_any.next;
  13691.         } while (pLoElement && pLoElement->type == LO_CELL ); 
  13692.     
  13693.         // Replace old lists with new sorted lists
  13694.         for( i = 0; i < iCount; i++ )
  13695.         {
  13696.             m_SelectedLoCells[i] = pNewLoCells[i];
  13697.             m_SelectedEdCells[i] = pNewEdCells[i];
  13698.         }
  13699.     }
  13700.     XP_FREE(pNewLoCells);
  13701.     XP_FREE(pNewEdCells);
  13702. }
  13703.  
  13704. void CEditBuffer::ClearTableAndCellSelection()
  13705. {
  13706.     m_pSelectedTableElement = NULL;
  13707.     m_pPrevExtendSelectionCell = NULL;
  13708.     m_TableHitType = ED_HIT_NONE;
  13709.  
  13710.     int iEdSize = m_SelectedEdCells.Size();
  13711.     int iLoSize = m_SelectedLoCells.Size();
  13712.     if( m_pSelectedEdTable == 0 &&
  13713.         iEdSize == 0 )
  13714.         // Nothing to do
  13715.         return;
  13716.  
  13717.     SelectTable(FALSE);
  13718.  
  13719.     // These arrays should always be in synch
  13720.     //XP_ASSERT( iEdSize == iLoSize );
  13721.     if( iEdSize != iLoSize )
  13722.         XP_TRACE(("EdSize=%d, LoSize=%d", iEdSize, iLoSize));
  13723.  
  13724.     // Remove selection for all existing cells
  13725.     //  (From the end is quicker - no pointer copying needed)
  13726.     for( int i = iEdSize-1; i >= 0; i-- )
  13727.     {
  13728.         SelectCell(FALSE, m_SelectedLoCells[i], m_SelectedEdCells[i]);
  13729.     }
  13730.     
  13731.     // Array of pointers should now be empty
  13732.     XP_ASSERT(m_SelectedLoCells.Size() == 0);
  13733.     XP_ASSERT(m_SelectedEdCells.Size() == 0);
  13734. }
  13735.  
  13736. void CEditBuffer::ClearCellAttrmask( LO_Element *pLoElement, uint16 attrmask )
  13737. {
  13738.     if( pLoElement )
  13739.     {
  13740.         while( pLoElement && pLoElement->type != LO_LINEFEED )
  13741.         {
  13742.             if( pLoElement->type == LO_CELL )
  13743.             {
  13744.                 XP_Bool bWasSet = pLoElement->lo_cell.ele_attrmask & attrmask;
  13745.                 // Clear the mask bit(s)
  13746.                 pLoElement->lo_cell.ele_attrmask &= ~attrmask;
  13747.                 // Tell front end to invalidate cell for redraw
  13748.                 if( bWasSet )
  13749.                     FE_DisplayEntireTableOrCell(m_pContext, pLoElement);
  13750.             }
  13751.             pLoElement = pLoElement->lo_any.next;
  13752.         }
  13753.     }
  13754. }
  13755.  
  13756. // Should be called after positioning caret on object
  13757. //   clicked on for context menu (right button on Windows)
  13758. void CEditBuffer::ClearCellSelectionIfNotInside()
  13759. {
  13760.     // Not in a table - clear table selection
  13761.     if( !IsInsertPointInTable() )
  13762.     {
  13763.         ClearTableAndCellSelection();
  13764.         return;
  13765.     }
  13766.     // Get current leaf element at caret or selection
  13767.     CEditInsertPoint ip;
  13768.     GetTableInsertPoint(ip);
  13769.     if( ip.m_pElement )
  13770.     {
  13771.         // Find the LO cell element and check if it is selected
  13772.         //  (No cell found if not within a table)
  13773.         LO_CellStruct *pLoCell = GetLoCell(ip.m_pElement);
  13774.         if( pLoCell && !(pLoCell->ele_attrmask & LO_ELE_SELECTED) )
  13775.         {
  13776.             ClearTableAndCellSelection();
  13777.         }
  13778.     }
  13779. }
  13780.  
  13781. void CEditBuffer::ClearTableIfContainsElement(CEditElement *pElement)
  13782. {
  13783.     if( pElement )
  13784.     {
  13785.         // Depend on member flag being set correctly
  13786.         if( pElement->IsSelected() )
  13787.         {
  13788.             if( pElement->IsTable() )
  13789.             {
  13790.                 SelectTable(FALSE, NULL, (CEditTableElement*)pElement);
  13791.                 return;
  13792.             }
  13793.             else if( pElement->IsTableCell() )
  13794.             {
  13795.                 SelectCell(FALSE, NULL, (CEditTableCellElement*)pElement);
  13796.                 return;
  13797.             }
  13798.         }
  13799.         // We are not a cell or table - check if containing layout cell or table is selected
  13800.         LO_CellStruct *pLoCell = GetLoCell(pElement);
  13801.         if( pLoCell )
  13802.         {
  13803.             // If selected, unselect
  13804.             if( pLoCell->ele_attrmask & LO_ELE_SELECTED )
  13805.             {
  13806.                 SelectCell(FALSE, pLoCell);
  13807.             } else {
  13808.                 // Cell not selected - check for table
  13809.                 LO_TableStruct *pLoTable = lo_GetParentTable(m_pContext, (LO_Element*)pLoCell);
  13810.                 if( pLoTable && pLoTable->ele_attrmask & LO_ELE_SELECTED )
  13811.                 {
  13812.                     SelectTable(FALSE, pLoTable);
  13813.                 }
  13814.             }
  13815.         }
  13816.     }
  13817. }
  13818.  
  13819. // Call this the first time, if result is TRUE then call 
  13820. //   GetNextCellSelection() and repeat formating or other action 
  13821. //    until it returns null. 
  13822. // Current insert point is saved during first time,
  13823. //   and restored after no more cells are left to process
  13824. XP_Bool CEditBuffer::GetFirstCellSelection(CEditSelection& selection)
  13825. {
  13826.     CEditTableCellElement *pCell = NULL;
  13827.  
  13828.     m_iNextSelectedCell = 0;
  13829.  
  13830.     if( m_pSelectedEdTable )
  13831.     {
  13832.         pCell = m_pSelectedEdTable->GetFirstCell();
  13833.     }
  13834.     else if( m_SelectedEdCells.Size() )
  13835.     {
  13836.         // Get first "selected" cell
  13837.         pCell = m_SelectedEdCells[0];
  13838.  
  13839.         // Set for next selected item
  13840.         m_iNextSelectedCell = 1;
  13841.     }
  13842.     if( pCell )
  13843.     {
  13844.         // Just return pointers and offsets of the cell contents as a selection
  13845.         pCell->GetAll(selection);
  13846.         selection.m_bFromStart = TRUE;
  13847.         EphemeralToPersistent(selection);
  13848.         return TRUE;
  13849.     }
  13850.     return FALSE;
  13851. }
  13852.  
  13853. XP_Bool CEditBuffer::GetNextCellSelection(CEditSelection& selection)
  13854. {
  13855.     CEditTableCellElement *pCell = NULL;
  13856.  
  13857.     if( m_pSelectedEdTable )
  13858.     {
  13859.         // Get next cell in entire table
  13860.         pCell = m_pSelectedEdTable->GetNextCellInTable();
  13861.     } 
  13862.     else if( m_iNextSelectedCell && m_iNextSelectedCell < m_SelectedEdCells.Size() )
  13863.     {
  13864.         // Get next "selected" cell
  13865.         pCell = m_SelectedEdCells[m_iNextSelectedCell++];
  13866.     }
  13867.     if( pCell )
  13868.     {
  13869.         pCell->GetAll(selection);
  13870.         selection.m_bFromStart = TRUE;
  13871.         EphemeralToPersistent(selection);
  13872.         return TRUE;
  13873.     }
  13874.     return FALSE;
  13875. }
  13876.  
  13877. // Extracted from old version of SelectTableCell
  13878. void CEditBuffer::SelectCellContents(CEditTableCellElement *pCell)
  13879. {
  13880.  
  13881.     CEditSelection selection;
  13882.     if ( pCell ) 
  13883.     {
  13884.         pCell->GetAll(selection);
  13885.         // KLUDGE ALERT: Without this, attempts to set cell data
  13886.         //  go to next cell. As it is, there is a bug in which
  13887.         //  caret is in next cell after unselecting this cell
  13888.         selection.m_bFromStart = TRUE;
  13889.         EphemeralToPersistent(selection);
  13890.         SetSelection(selection);
  13891.     }
  13892. }
  13893.  
  13894.  
  13895. CEditTableCellElement *CEditBuffer::GetFirstSelectedCell()
  13896. {
  13897.     CEditTableCellElement *pCell = NULL;
  13898.  
  13899.     m_iNextSelectedCell = 0;
  13900.  
  13901.     if( m_pSelectedEdTable )
  13902.     {
  13903.         pCell = m_pSelectedEdTable->GetFirstCell();
  13904.     }
  13905.     else if( m_SelectedEdCells.Size() )
  13906.     {
  13907.         // Get first "selected" cell
  13908.         pCell = m_SelectedEdCells[0];
  13909.  
  13910.         // Set for next selected item
  13911.         m_iNextSelectedCell = 1;
  13912.     }
  13913.     return pCell;
  13914. }
  13915.  
  13916. CEditTableCellElement *CEditBuffer::GetNextSelectedCell(intn* pRowCounter)
  13917. {
  13918.     CEditTableCellElement *pCell = NULL;
  13919.  
  13920.     if( m_pSelectedEdTable )
  13921.     {
  13922.         // Get next cell in entire table
  13923.         pCell = m_pSelectedEdTable->GetNextCellInTable(pRowCounter);
  13924.     } 
  13925.     else if( m_iNextSelectedCell && m_iNextSelectedCell < m_SelectedEdCells.Size() )
  13926.     {
  13927.         if( m_iNextSelectedCell < 1 )
  13928.             return GetFirstSelectedCell(); 
  13929.  
  13930.         // Get next "selected" cell
  13931.         pCell = m_SelectedEdCells[m_iNextSelectedCell];
  13932.         if( pRowCounter )
  13933.         {
  13934.             intn iPrevRow = m_SelectedEdCells[m_iNextSelectedCell - 1]->GetRow();
  13935.             // Increment the row counter if supplied
  13936.             intn iRow =  pCell->GetRow();
  13937.             if( iRow != iPrevRow )
  13938.                 (*pRowCounter)++;
  13939.         }
  13940.         m_iNextSelectedCell++;
  13941.     }
  13942.     return pCell;
  13943. }
  13944.  
  13945. // Dynamic object sizing
  13946.  
  13947. ED_SizeStyle CEditBuffer::CanSizeObject(LO_Element *pLoElement, int32 xVal, int32 yVal)
  13948. {
  13949.     // Table and Cells are special case - use more complicated hit testing
  13950.     if( pLoElement && (pLoElement->type == LO_TABLE || pLoElement->type == LO_CELL) )
  13951.     {
  13952.         ED_HitType iHitType = GetTableHitRegion(xVal, yVal, &pLoElement, FALSE);
  13953.         switch( iHitType )
  13954.         {
  13955.             case ED_HIT_SIZE_COL:
  13956.             case ED_HIT_SIZE_TABLE_WIDTH:
  13957.                 return ED_SIZE_RIGHT;
  13958.             case ED_HIT_SIZE_ROW:
  13959.             case ED_HIT_SIZE_TABLE_HEIGHT:
  13960.                 return ED_SIZE_BOTTOM;
  13961.             case ED_HIT_ADD_COLS:
  13962.                 return ED_SIZE_ADD_COLS;
  13963.             case ED_HIT_ADD_ROWS:
  13964.                 return ED_SIZE_ADD_ROWS;
  13965.             default:
  13966.                 return 0;
  13967.         }
  13968.     } 
  13969.  
  13970.     if( !pLoElement )
  13971.     {
  13972. #ifdef LAYERS
  13973.         pLoElement = LO_XYToElement(m_pContext, xVal, yVal, 0);
  13974. #else
  13975.         pLoElement = LO_XYToElement(m_pContext, xVal, yVal);
  13976. #endif
  13977.     }
  13978.     if( !pLoElement || pLoElement->lo_any.edit_offset < 0)
  13979.         return 0;
  13980.  
  13981.     XP_Bool bCanSizeWidth = FALSE;
  13982.     XP_Bool bCanSizeHeight = FALSE;
  13983.     XP_Bool bInHRule = pLoElement->type == LO_HRULE;
  13984.     XP_Bool bIsIcon = FALSE;
  13985.  
  13986.     LO_Any_struct * pAny = (LO_Any_struct*) pLoElement;
  13987.  
  13988.     // NOTE: NO CONVERSION DONE -- ASSUMES FE IS IN PIXELS
  13989.     int32 iXoffset = xVal - (pAny->x + pAny->x_offset);
  13990.     int32 iYoffset = yVal - (pAny->y + pAny->y_offset);
  13991.     int32 bottom_limit = pAny->height - ED_SIZING_BORDER;
  13992.     int32 right_limit =  pAny->width - ED_SIZING_BORDER;
  13993.  
  13994.     // Look for the elements that can be sized:
  13995.     switch ( pLoElement->type )
  13996.     {
  13997.         case LO_IMAGE:       // 4
  13998.         {
  13999.               CEditElement * pElement = pLoElement->lo_any.edit_element;
  14000.             if( !pElement ){
  14001.                 return 0;
  14002.             }
  14003.             if( pElement->IsIcon() ){
  14004.                  //  Only allow sizing width or height
  14005.                  //  if it already has the respective attribute set
  14006.                 bCanSizeWidth = pElement->GetWidth();
  14007.                 bCanSizeHeight = pElement->GetHeight();
  14008.                 bIsIcon = TRUE;
  14009.                 break;
  14010.             }
  14011.             if( !pElement->IsImage() )
  14012.                 return 0;
  14013.             // Adjust for image border
  14014.             iXoffset -= ((LO_ImageStruct*)pAny)->border_width;
  14015.             iYoffset -= ((LO_ImageStruct*)pAny)->border_width;
  14016.             // Fall through to allow sizing both width and height
  14017.         }
  14018.         case LO_HRULE:       // 3
  14019.             bCanSizeWidth = TRUE;
  14020.             bCanSizeHeight = TRUE;
  14021.             break;
  14022.         default:
  14023.             return 0;
  14024.     }
  14025.  
  14026.     // Can't size anything - get out now!
  14027.     if( !bCanSizeWidth && !bCanSizeHeight )
  14028.         return 0;
  14029.  
  14030.     if( iXoffset < 0 || iYoffset < 0 )
  14031.         return 0;
  14032.  
  14033.     // Note that TOP RIGHT is prefered in the case
  14034.     //   of small object (overlapping hits regions),
  14035.     //   and left/right sides are favored over bottom corners
  14036.     if( iXoffset >= right_limit )
  14037.     {
  14038.         if( !bCanSizeHeight || bInHRule )
  14039.         {
  14040.             return ED_SIZE_RIGHT;
  14041.         }
  14042.         if( iYoffset <= ED_SIZING_BORDER )
  14043.             return ED_SIZE_TOP_RIGHT;
  14044.         if( iYoffset < bottom_limit )
  14045.             return ED_SIZE_RIGHT;
  14046.  
  14047.         return ED_SIZE_BOTTOM_RIGHT;
  14048.  
  14049.     } else if( iXoffset <= ED_SIZING_BORDER )
  14050.     {
  14051.         if( !bCanSizeHeight || bInHRule )
  14052.             return ED_SIZE_LEFT;
  14053.  
  14054.         if( iYoffset <= ED_SIZING_BORDER )
  14055.             return ED_SIZE_TOP_LEFT;
  14056.  
  14057.         if( iYoffset < bottom_limit )
  14058.             return ED_SIZE_LEFT;
  14059.  
  14060.         return ED_SIZE_BOTTOM_LEFT;
  14061.  
  14062.     } else if( bCanSizeHeight  )
  14063.     {
  14064.         if( iYoffset >= bottom_limit )
  14065.             return ED_SIZE_BOTTOM;
  14066.  
  14067.         if( iYoffset <= ED_SIZING_BORDER )
  14068.             return ED_SIZE_TOP;
  14069.     }
  14070.     return 0;
  14071. }
  14072.  
  14073. ED_SizeStyle CEditBuffer::StartSizing(LO_Element *pLoElement, int32 xVal, int32 yVal,
  14074.                                       XP_Bool bLockAspect, XP_Rect *pRect){
  14075.     XP_ASSERT(pRect);
  14076.  
  14077.     if( m_pSizingObject ){
  14078.         // Improbable, but what the heck
  14079.         delete m_pSizingObject;
  14080.     }
  14081.     // Unselect table or cell(s)
  14082.     ClearTableAndCellSelection();
  14083.  
  14084.     // Force absolute measurements to avoid confusing behavior when using percents
  14085.     if( pLoElement->type == LO_TABLE || pLoElement->type == LO_CELL )
  14086.     {
  14087.         CEditTableElement *pTable = (CEditTableElement*)GetTableElementFromLO_Element( pLoElement, LO_TABLE );
  14088.         if( pTable )
  14089.         {
  14090.             int iMode = ED_TABLE_PIXELS | ED_TABLE_USE_COLS;
  14091.             // If sizing a table, remove all widths from cells
  14092.             if( pLoElement->type == LO_TABLE )
  14093.                 iMode |= ED_TABLE_NO_CELL_WIDTH;
  14094.             else 
  14095.                 iMode |= ED_TABLE_NO_CELL_WIDTH;
  14096.             pTable->SetTableMode(m_pContext, iMode);
  14097.         }
  14098.     }
  14099.  
  14100.     // Shouldn't be here without this, but check anyway
  14101.     int iStyle = CanSizeObject(pLoElement, xVal, yVal);
  14102.     if( iStyle ){
  14103.         m_pSizingObject = new CSizingObject();
  14104.         if( m_pSizingObject ){
  14105.             if( m_pSizingObject->Create(this, pLoElement, iStyle,
  14106.                                          xVal, yVal, bLockAspect, pRect) ){
  14107.                 return iStyle;
  14108.             } else {
  14109.                 delete m_pSizingObject;
  14110.                 m_pSizingObject = NULL;
  14111.             } 
  14112.         }
  14113.     }
  14114.     return 0;
  14115. }
  14116.  
  14117. XP_Bool CEditBuffer::GetSizingRect(int32 xVal, int32 yVal,
  14118.                                    XP_Bool bLockAspect, XP_Rect *pRect){
  14119.     XP_ASSERT(pRect);
  14120.     if( m_pSizingObject && pRect ){
  14121.         return m_pSizingObject->GetSizingRect(xVal, yVal, bLockAspect, pRect);
  14122.     }
  14123.     return FALSE;
  14124. }
  14125.  
  14126. void CEditBuffer::EndSizing(){
  14127.     if( ! m_pSizingObject ){
  14128.         return;
  14129.     }
  14130.     m_pSizingObject->ResizeObject();
  14131.  
  14132.     delete m_pSizingObject;
  14133.     m_pSizingObject = NULL;
  14134.  
  14135.     ResumeAutoSave();
  14136. }
  14137.  
  14138. void CEditBuffer::CancelSizing(){
  14139.     if( m_pSizingObject )
  14140.     {
  14141.         // Erase the drawing of added rows or columns
  14142.         m_pSizingObject->EraseAddRowsOrCols();
  14143.         delete m_pSizingObject;
  14144.         m_pSizingObject = NULL;
  14145.     }
  14146.     ResumeAutoSave();
  14147. }
  14148.  
  14149. #ifdef EDITOR_JAVA
  14150. EditorPluginManager CEditBuffer::GetPlugins(){
  14151.     return GetCommandLog()->GetPlugins();
  14152. }
  14153. #endif
  14154.  
  14155.  
  14156. //-----------------------------------------------------------------------------
  14157. // Spellcheck stuff
  14158. //-----------------------------------------------------------------------------
  14159.  
  14160. XP_Bool CEditBuffer::FindNextMisspelledWord( XP_Bool bFirst, 
  14161.         XP_Bool bSelect, CEditLeafElement** ppWordStart ){
  14162.     CEditLeafElement *pInsertPoint;
  14163.     XP_Bool bSingleItem;
  14164.     ElementOffset iOffset;
  14165.  
  14166.     if( ppWordStart && *ppWordStart ){
  14167.         pInsertPoint = *ppWordStart;
  14168.         iOffset = 0;
  14169.     }
  14170.     else {
  14171.         bSingleItem = GetPropertyPoint( &pInsertPoint, &iOffset );
  14172.     }
  14173.     while( pInsertPoint->IsMisspelled() && ! bFirst ){
  14174.         pInsertPoint = pInsertPoint->NextLeaf();
  14175.         if ( pInsertPoint && pInsertPoint->IsFirstInContainer() ) {
  14176.             break;
  14177.         }
  14178.     }
  14179.     while( pInsertPoint && !pInsertPoint->IsMisspelled() ){
  14180.         pInsertPoint = pInsertPoint->NextLeaf();
  14181.     }
  14182.  
  14183.     if( pInsertPoint ){
  14184.         CEditLeafElement *pEndInsertPoint = pInsertPoint;
  14185.         while( pEndInsertPoint && pEndInsertPoint->IsMisspelled() ){
  14186.             CEditLeafElement *pNext = pEndInsertPoint->LeafInContainerAfter();
  14187.             if ( pNext != NULL )
  14188.                 pEndInsertPoint = pNext;
  14189.             else
  14190.                 break;
  14191.         }
  14192.  
  14193.         if (pEndInsertPoint && pEndInsertPoint->IsMisspelled()){
  14194.             iOffset = pEndInsertPoint->GetLen();
  14195.         }
  14196.         else {
  14197.             iOffset = 0;
  14198.         }
  14199.  
  14200.         if( bSelect ){
  14201.             // force the window to scroll to the beginning of the selection.
  14202.             SetInsertPoint( pInsertPoint, 0, FALSE );
  14203.             SelectRegion( pInsertPoint, 0, pEndInsertPoint, iOffset, TRUE, TRUE );
  14204.         }
  14205.     }
  14206.  
  14207.     if( ppWordStart != 0 ) *ppWordStart = pInsertPoint;
  14208.  
  14209.     return pInsertPoint != 0;
  14210. }
  14211.  
  14212. static XP_HUGE_CHAR_PTR GetMisspelledWordText( CEditLeafElement *pLeaf ){
  14213.     CStreamOutMemory m;
  14214.     while( pLeaf && pLeaf->IsMisspelled() && pLeaf->IsText() ){
  14215.         m.Write( pLeaf->Text()->GetText(), pLeaf->Text()->GetLen() );
  14216.         pLeaf = pLeaf->LeafInContainerAfter();
  14217.     }
  14218.     // zero terminate the buffer
  14219.     m.Write( "", 1 );
  14220.     return m.GetText();
  14221. }
  14222.  
  14223. static void IgnoreMisspelledWord( CEditLeafElement *pLeaf ){
  14224.     while( pLeaf && pLeaf->IsMisspelled() && pLeaf->IsText() ){
  14225.         pLeaf->Text()->m_tf &= ~TF_SPELL;
  14226.         pLeaf = pLeaf->LeafInContainerAfter();
  14227.     }
  14228. }
  14229.  
  14230. static void ReplaceMisspelledWord( CEditLeafElement *pStart, char *pNewWord ){
  14231.     CEditLeafElement *pPrev;
  14232.  
  14233.     pStart->Text()->m_tf &= ~TF_SPELL;
  14234.  
  14235.     // hack to keep us from deleting the entire word
  14236.     if( pNewWord && *pNewWord ){
  14237.         pStart->Text()->SetText( pNewWord );
  14238.     }
  14239.     else {
  14240.         pStart->Text()->SetText(" ");
  14241.     }
  14242.  
  14243.     pStart = pStart->LeafInContainerAfter();
  14244.     while( pStart && pStart->IsMisspelled() ){
  14245.         pPrev = pStart;
  14246.         pStart = pStart->LeafInContainerAfter();
  14247.         pPrev->Unlink();
  14248.         delete pPrev;
  14249.     }
  14250. }
  14251.  
  14252. void CEditBuffer::IterateMisspelledWords( EMSW_FUNC eFunc, char* pOldWord, 
  14253.         char* pNewWord, XP_Bool bAll ){
  14254.  
  14255.     CEditLeafElement *pWordStart, *pBegin, *pEnd;
  14256.     XP_Bool bFirst = TRUE;
  14257.  
  14258.     // 
  14259.     // Find the next word (including the one we might be on), but don't select
  14260.     //  it and return the word
  14261.     //
  14262.  
  14263.     
  14264.     pBegin = 0;
  14265.     pEnd = 0;
  14266.     pWordStart = 0;
  14267.     do {
  14268.         FindNextMisspelledWord( bFirst, FALSE, &pWordStart );
  14269.         if( bFirst ){
  14270.             bFirst = FALSE;
  14271.             ClearSelection(TRUE,TRUE);       
  14272.         }
  14273.         if ( pWordStart ) {
  14274.             XP_HUGE_CHAR_PTR pThisWord = GetMisspelledWordText( pWordStart );
  14275.  
  14276.             // Ignore all words if no specific word is specified.
  14277.             if( (eFunc == EMSW_IGNORE && pOldWord == NULL) ||
  14278.                 XP_STRCMP( (char*) pThisWord, pOldWord ) == 0 ){
  14279.                 // do the actual replacing here
  14280.                 if( pBegin == 0 ) pBegin = pWordStart;
  14281.                 pEnd = pWordStart;
  14282.  
  14283.                 switch( eFunc ){
  14284.                 case EMSW_IGNORE:
  14285.                     IgnoreMisspelledWord( pWordStart );
  14286.                     break;
  14287.  
  14288.                 case EMSW_REPLACE:
  14289.                     ReplaceMisspelledWord( pWordStart, pNewWord );
  14290.                     break;
  14291.                 }
  14292.             }
  14293.  
  14294.             XP_HUGE_FREE( pThisWord );
  14295.         }
  14296.     } while( bAll && pWordStart );
  14297.  
  14298.     // if we actually did some work
  14299.     if( pBegin && pEnd ) {
  14300.         Relayout( pBegin, 0, pEnd );
  14301.     }
  14302. }
  14303.  
  14304. void CEditBuffer::SetBaseTarget(char* pTarget){
  14305.     XP_FREEIF(m_pBaseTarget);
  14306.     m_pBaseTarget = pTarget ? XP_STRDUP(pTarget) : 0;
  14307. }
  14308.  
  14309. char* CEditBuffer::GetBaseTarget(){
  14310.     return m_pBaseTarget;
  14311. }
  14312.  
  14313.  
  14314.  
  14315.  
  14316. XP_Bool
  14317. CEditBuffer::ReplaceOnce( char *pReplaceText, XP_Bool bRelayout, XP_Bool bReduce)
  14318. {/* This utility function assumes that the text 
  14319.     you want replaced has been selected already. */
  14320.  
  14321.     CEditSelection selection;
  14322.     int i;
  14323.     EDT_CharacterData  *formatting;  /* This will hold the formatting of the selected text */
  14324.  
  14325. #ifdef FORMATING_FIXED
  14326.     /* Get the formatting of the selected text */
  14327.     formatting = GetCharacterDataSelection(0, selection);
  14328.  
  14329.     /* Paste the new text over the selected text */
  14330.     PasteText( pReplaceText, FALSE, FALSE, bRelayout , bReduce);
  14331.  
  14332.     /* select the new text */
  14333.     for (i = XP_STRLEN( pReplaceText ); i > 0; i-- )
  14334.         SelectPreviousChar();
  14335.  
  14336.     /* apply the old formatting */
  14337.     SetCharacterData( formatting );
  14338. #else
  14339.     /* Paste the new text over the selected text */
  14340.     PasteText( pReplaceText, FALSE, FALSE, bRelayout , bReduce);
  14341. #endif
  14342.  
  14343.     return TRUE;
  14344. }
  14345.  
  14346.  
  14347.  
  14348. void
  14349. CEditBuffer::ReplaceLoop(char *pReplaceText, XP_Bool bReplaceAll,
  14350.                          char *pTextToLookFor, XP_Bool bCaseless,
  14351.                          XP_Bool bBackward, XP_Bool bDoWrap )
  14352. {
  14353.     int i;
  14354.     LO_Element *start_ele_loc, *end_ele_loc,
  14355.                *original_start_ele_loc, *original_end_ele_loc;
  14356.     int32       start_pos, end_pos,
  14357.                 original_start_pos, original_end_pos,
  14358.                 tlx, tly;
  14359.     CL_Layer*   layer;/* this will be ignored */
  14360.     MWContext*  tempContext = m_pContext;
  14361.     CEditLeafElement* origEditElement;//used for replace all to move insertion point back to the proper location
  14362.  
  14363.     BeginBatchChanges(kGroupOfChangesCommandID);
  14364.  
  14365.  
  14366.     /* Get the original starting and finising elements */
  14367.     LO_GetSelectionEndpoints( m_pContext,
  14368.                               &original_start_ele_loc, /* The element the selection begins in */
  14369.                               &original_end_ele_loc,   /* The element the selection ended in */
  14370.                               &original_start_pos,     /* Index into the starting element */
  14371.                               &original_end_pos,    /* Index into the ending element */
  14372.                               &layer);                 /* this will be ignored */
  14373.  
  14374.     if ( bReplaceAll )
  14375.     {/**/
  14376.         origEditElement = this->m_pCurrent;
  14377.  
  14378.         NavigateDocument( FALSE, FALSE );
  14379.         LO_GetSelectionEndpoints( m_pContext,
  14380.                               &start_ele_loc, /* The element the selection begins in */
  14381.                               &end_ele_loc,   /* The element the selection ended in */
  14382.                               &start_pos,     /* Index into the starting element */
  14383.                               &end_pos,       /* Index into the ending element */
  14384.                               &layer);                 /* this will be ignored */
  14385.     }
  14386.     else
  14387.     {/**/
  14388.         start_ele_loc = original_start_ele_loc;
  14389.         end_ele_loc = original_end_ele_loc;
  14390.         start_pos = original_start_pos;
  14391.         end_pos = original_end_pos;
  14392.     }
  14393.  
  14394.     XP_Bool     done = FALSE, Wrapped = FALSE;
  14395.     //if we are replacing all, start at top of doc. remember original insertion point.
  14396.     while ( !done )
  14397.     {
  14398.         XP_Bool found = LO_FindText(m_pContext, pTextToLookFor, &start_ele_loc,
  14399.                             &start_pos, &end_ele_loc, &end_pos, !bCaseless, !bBackward);
  14400.         if ( found )
  14401.         {
  14402.             LO_SelectText( m_pContext, start_ele_loc, start_pos,
  14403.                            end_ele_loc, end_pos, &tlx, &tly);
  14404.  
  14405.             ReplaceOnce( pReplaceText , !bReplaceAll, !bReplaceAll); //do not relayout if we are replacing all!
  14406.             //ReplaceOnce( pReplaceText , TRUE, TRUE); 
  14407.             if ( bReplaceAll )
  14408.             {/* We need to reset our starting position to our previous ending position */
  14409.                 //do not care to refresh anything now!!
  14410.                 m_bLayoutBackpointersDirty = FALSE;//trust me
  14411.                 start_ele_loc =  end_ele_loc;
  14412.                 start_pos = end_pos;
  14413.             }
  14414.             else
  14415.                 done=TRUE;
  14416.         }
  14417.         else if (!bReplaceAll) /* found == FALSE  and we are not replacing all.*/
  14418.         {/* If found == FALSE, and there isn't a selection, 
  14419.             then we need to wrap to check the portion before the insertion point. */
  14420.             if ( !Wrapped )/* We only want to Wrap ONCE */
  14421.             {
  14422.                 start_pos = 0;
  14423.                 end_pos = original_start_pos;
  14424.                 start_ele_loc = NULL;
  14425.                 end_ele_loc = original_start_ele_loc;
  14426.                 Wrapped = TRUE;
  14427.             }
  14428.             else
  14429.                 done = TRUE;
  14430.         }
  14431.         else
  14432.         {
  14433.             done = TRUE;
  14434.         }
  14435.     }
  14436.     if (bReplaceAll) //need to relayout now that we are done.
  14437.     {
  14438.         //set insertion point back to its old location, valid, or not.
  14439.         /*call Reflow( CEditElement* pStartElement,
  14440.                             int iEditOffset,
  14441.                             CEditElement *pEndElement,
  14442.                             intn relayoutFlags ){*/
  14443.         m_bLayoutBackpointersDirty = TRUE;//trust me
  14444.  
  14445.         SetInsertPoint(origEditElement, original_start_pos, FALSE);
  14446.         Reduce(this->m_pRoot->GetFirstMostChild());//we must finish what we have begun...
  14447.         // relay out the stream
  14448.         Relayout(this->m_pRoot->GetFirstMostChild(), 0);
  14449.     }
  14450. }
  14451.  
  14452.  
  14453.  
  14454.  
  14455. void CEditBuffer::ReplaceText( char *pReplaceText, XP_Bool bReplaceAll,
  14456.                                 char *pTextToLookFor, XP_Bool bCaseless,
  14457.                                 XP_Bool bBackward, XP_Bool bDoWrap )
  14458. {
  14459.     EDT_CharacterData *formatting;
  14460.     CEditSelection selection;
  14461.     int i;
  14462.  
  14463.     BeginBatchChanges(kGroupOfChangesCommandID);
  14464.     // This replaces the selected "find" text just fine,
  14465.     //  but it ignores character attributes of replaced text
  14466.     if( IsSelected() )
  14467.         formatting = GetCharacterDataSelection(0, selection);
  14468.     else
  14469.         formatting = NULL;
  14470.  
  14471.     PasteText(pReplaceText, FALSE, FALSE, TRUE, TRUE); 
  14472.     for (i = XP_STRLEN( pReplaceText ); i > 0; i-- )
  14473.         SelectPreviousChar();
  14474.     SetCharacterData( formatting );
  14475.     
  14476.     if ( bReplaceAll )
  14477.     {
  14478.         /* normal local variables */
  14479.         int32         start_position, end_position;
  14480.         LO_Element    *start_ele_loc = NULL, *end_ele_loc = NULL, 
  14481.                     *final_done_loc, *current_done_location;
  14482.         MWContext    *tempContext;
  14483.         CL_Layer    *layer;    // this will be ignored
  14484.          XP_Bool        found;
  14485.  
  14486.            /* use non-editor LO_* call */
  14487.         LO_GetSelectionEndpoints( m_pContext,
  14488.                                 &start_ele_loc,
  14489.                                 &end_ele_loc,
  14490.                                 &start_position,
  14491.                                 &end_position,
  14492.                                 &layer);
  14493.         final_done_loc = start_ele_loc;
  14494.         current_done_location = NULL;
  14495.         
  14496.         /* brade: as of today; the bDoWrap is not working correct so force it off for now */
  14497.         bDoWrap = FALSE;
  14498.         
  14499.            do {
  14500.             start_ele_loc = end_ele_loc;
  14501.             end_ele_loc = current_done_location;
  14502.             tempContext = m_pContext;
  14503.             found = LO_FindGridText( m_pContext,
  14504.                                 &tempContext,
  14505.                                 pTextToLookFor,
  14506.                                 &start_ele_loc,
  14507.                                 &start_position,
  14508.                                 &end_ele_loc,
  14509.                                 &end_position,
  14510.                                 !bCaseless,
  14511.                                 !bBackward);
  14512.             if ( found )
  14513.             {
  14514.                 int32        tlx, tly;
  14515.                 
  14516.                 LO_SelectText( m_pContext,
  14517.                     start_ele_loc,
  14518.                     start_position,
  14519.                     end_ele_loc,
  14520.                     end_position,
  14521.                     &tlx,
  14522.                     &tly);
  14523.                 
  14524.                 if( IsSelected() )
  14525.                     formatting = GetCharacterDataSelection(0, selection);
  14526.                 PasteText( pReplaceText, FALSE, FALSE, TRUE, TRUE);
  14527.                 for (i = XP_STRLEN( pReplaceText ); i > 0; i-- )
  14528.                     SelectPreviousChar();
  14529.                 SetCharacterData( formatting );
  14530.  
  14531.                    /* use non-editor LO_* call */
  14532.                 LO_GetSelectionEndpoints( m_pContext,
  14533.                                 &start_ele_loc,
  14534.                                 &end_ele_loc,
  14535.                                 &start_position,
  14536.                                 &end_position,
  14537.                                 &layer);
  14538.             }
  14539.             else if ( bDoWrap )
  14540.             {
  14541.                 /* not found; but we need to wrap */
  14542.                 bDoWrap = FALSE;    /* only wrap this once */
  14543.                 found = TRUE;        /* reset our flag so we keep looping! */
  14544.  
  14545.                 /* Try again from the beginning.  These are the values
  14546.                    LO_GetSelectionEndpoints returns if there is no selection */
  14547.                 end_ele_loc = NULL;        /* this will reset start_ele_loc above */
  14548.                 start_position = 0;
  14549.                 end_position = 0;
  14550.                 
  14551.                 current_done_location = final_done_loc;
  14552.             }
  14553.             
  14554.         } while ( found );
  14555.     }
  14556.     
  14557.     EndBatchChanges();
  14558. }
  14559.  
  14560. //-----------------------------------------------------------------------------
  14561. // CEditTagCursor
  14562. //-----------------------------------------------------------------------------
  14563. CEditTagCursor::CEditTagCursor( CEditBuffer* pEditBuffer,
  14564.             CEditElement *pElement, int iEditOffset, CEditElement* pEndElement ):
  14565.             m_pEditBuffer(pEditBuffer),
  14566.             m_pCurrentElement(pElement),
  14567.             m_endPos(pEndElement,0),
  14568.             m_tagPosition(tagOpen),
  14569.             m_stateDepth(0),
  14570.             m_currentStateDepth(0),
  14571.             m_iEditOffset( iEditOffset ),
  14572.             m_bClearRelayoutState(0),
  14573.             m_pContext( pEditBuffer->GetContext() ),
  14574.             m_pStateTags(0)
  14575.             {
  14576.  
  14577.     CEditElement *pContainer = m_pCurrentElement->FindContainer();
  14578.     if( (m_pCurrentElement->PreviousLeafInContainer() == 0 )
  14579.             && iEditOffset == 0
  14580.             && pContainer
  14581.             && pContainer->IsA( P_LIST_ITEM ) ){
  14582.         m_pCurrentElement = pContainer;
  14583.     }
  14584.  
  14585.     //m_bClearRelayoutState = (m_pCurrentElement->PreviousLeafInContainer() != 0 );
  14586.  
  14587.     //
  14588.     // If the element has a parent, setup the depth
  14589.     //
  14590.     pElement = m_pCurrentElement->GetParent();
  14591.  
  14592.     while( pElement ){
  14593.         PA_Tag *pTag = pElement->TagOpen( 0 );
  14594.         PA_Tag *pTagEnd = pTag;
  14595.  
  14596.         while( pTagEnd->next != 0 ) pTagEnd = pTag->next;
  14597.  
  14598.         pTagEnd->next = m_pStateTags;
  14599.         m_pStateTags = pTag;
  14600.         pElement = pElement->GetParent();
  14601.     }
  14602. }
  14603.  
  14604. CEditTagCursor::~CEditTagCursor(){
  14605.     EDT_DeleteTagChain( m_pStateTags );
  14606. }
  14607.  
  14608. CEditTagCursor* CEditTagCursor::Clone(){
  14609.     CEditTagCursor *pCursor = new CEditTagCursor( m_pEditBuffer,
  14610.                                                   m_pCurrentElement,
  14611.                                                   m_iEditOffset,
  14612.                                                   m_endPos.Element());
  14613.     return pCursor;
  14614. }
  14615.  
  14616. //
  14617. // We iterate through the edit tree generating tags.  The the goto code here
  14618. //  insures that after we deliver a tag, the m_pCurrentElement is always pointing
  14619. //  to the next element to be output
  14620. //
  14621. PA_Tag* CEditTagCursor::GetNextTag(){
  14622.     XP_Bool bDone = FALSE;
  14623.     PA_Tag* pTag = 0;
  14624.  
  14625.     // Check for end
  14626.     if( m_pCurrentElement == 0 ){
  14627.         return 0;
  14628.     }
  14629.  
  14630.     while( !bDone && m_pCurrentElement != 0 ){
  14631.         // we are either generating the beginning tag, content or end tags
  14632.         switch( m_tagPosition ){
  14633.  
  14634.         case tagOpen:            
  14635.             pTag = m_pCurrentElement->TagOpen( m_iEditOffset );
  14636.             bDone = (pTag != 0);
  14637.             m_iEditOffset = 0;  // only counts on the first tag.
  14638.             if( m_pCurrentElement->GetChild() ){
  14639.                 m_pCurrentElement = m_pCurrentElement->GetChild();
  14640.                 m_tagPosition = tagOpen;
  14641.             }
  14642.             else {
  14643.                 m_tagPosition = tagEnd;
  14644.                 if( !m_pCurrentElement->IsContainer() &&
  14645.                         !WriteTagClose(m_pCurrentElement->GetType()) 
  14646.                          ){
  14647.                     goto seek_next;
  14648.                 }
  14649.             }
  14650.             break;
  14651.  
  14652.         case tagEnd:
  14653.             pTag = m_pCurrentElement->TagEnd();
  14654.             bDone = (pTag != 0);
  14655.         seek_next:
  14656.             if( m_pCurrentElement->GetNextSibling()){
  14657.                 m_pCurrentElement = m_pCurrentElement->GetNextSibling();
  14658.                 m_tagPosition = tagOpen;
  14659.             }
  14660.             else {
  14661.                 //
  14662.                 // We've exausted all the elements at this level. Set the
  14663.                 //  current element to the end of our parent.  m_tagPosition is
  14664.                 //  already set to tagEnd, but lets be explicit
  14665.                 //
  14666.                 m_pCurrentElement = m_pCurrentElement->GetParent();
  14667.                 m_tagPosition = tagEnd;
  14668.                 if( m_pCurrentElement 
  14669.                         && !m_pCurrentElement->IsContainer() 
  14670.                         && !WriteTagClose(m_pCurrentElement->GetType()) 
  14671.                         ){
  14672.                     goto seek_next;
  14673.                 }
  14674.             }
  14675.             break;
  14676.         }
  14677.     }
  14678.  
  14679.     if( bDone ){
  14680.         return pTag;
  14681.     }
  14682.     else {
  14683.         XP_DELETE(pTag);
  14684.         return 0;
  14685.     }
  14686. }
  14687.  
  14688. PA_Tag* CEditTagCursor::GetNextTagState(){
  14689.     PA_Tag *pRet = m_pStateTags;
  14690.     if( m_pStateTags ){
  14691.         m_pStateTags = 0;
  14692.     }
  14693.     return pRet;
  14694. }
  14695.  
  14696. XP_Bool CEditTagCursor::AtBreak( XP_Bool* pEndTag ){
  14697.     XP_Bool bAtPara;
  14698.     XP_Bool bEndTag;
  14699.  
  14700.     if( m_pCurrentElement == 0 ){
  14701.         return FALSE;
  14702.     }
  14703.  
  14704.     if(  m_tagPosition == tagEnd ){
  14705.         bEndTag = TRUE;
  14706.         bAtPara = BitSet( edt_setTextContainer,  m_pCurrentElement->GetType()  );
  14707.     }
  14708.     else {
  14709.         bEndTag = FALSE;
  14710.         bAtPara = BitSet( edt_setParagraphBreak,  m_pCurrentElement->GetType()  );
  14711.     }
  14712.  
  14713.     if( bAtPara ){
  14714.         // force the layout engine to process this tag before computing line
  14715.         //  position.
  14716.         //if( m_pCurrentElement->IsA( P_LINEBREAK ){
  14717.         //    bEndTag = TRUE;
  14718.         //}
  14719.  
  14720.         // if there is an end position and the current position is before it
  14721.         //  ignore this break.
  14722.         CEditPosition p(m_pCurrentElement);
  14723.  
  14724.         if( m_endPos.IsPositioned() &&
  14725.                  m_endPos.Compare(&p) <= 0  ){
  14726.             return FALSE;
  14727.         }
  14728.         if( CurrentLine() != -1 ){
  14729.             *pEndTag = bEndTag;
  14730.             return TRUE;
  14731.         }
  14732.         else {
  14733.             //XP_ASSERT(FALSE)
  14734.         }
  14735.     }
  14736.     return FALSE;
  14737. }
  14738.  
  14739.  
  14740. int32 CEditTagCursor::CurrentLine(){
  14741.     CEditElement *pElement;
  14742.     LO_Element * pLayoutElement, *pLoStartLine;
  14743.     int32 iLineNum;
  14744.  
  14745.     //
  14746.     // Pop to the proper state
  14747.     //
  14748.     CEditElement *pSave = m_pCurrentElement;
  14749.     while( m_pCurrentElement && m_tagPosition == tagEnd ){
  14750.         PA_Tag* pTag = GetNextTag();
  14751.         EDT_DeleteTagChain( pTag );
  14752.         m_tagPosition = tagEnd;     // restore this.
  14753.     }
  14754.  
  14755.  
  14756.     // if we fell of the end of the document, then we are done.
  14757.     if( m_pCurrentElement == 0 ){
  14758.         m_pCurrentElement = pSave;
  14759.         return -1;
  14760.     }
  14761.  
  14762.     // LTNOTE: need to check to see if we are currently at a TextElement..
  14763.     pElement = m_pCurrentElement->NextLeaf();
  14764.     m_pCurrentElement = pSave;
  14765.  
  14766.     if( pElement == 0 ){
  14767.         return -1;
  14768.     }
  14769.  
  14770.     XP_ASSERT( pElement->IsLeaf() );
  14771.     pLayoutElement = pElement->Leaf()->GetLayoutElement();
  14772.     if( pLayoutElement == 0 ){
  14773.         return -1;
  14774.     }
  14775.  
  14776.     // Find the first element on this line.
  14777.     pLoStartLine = m_pEditBuffer->FirstElementOnLine( pLayoutElement, &iLineNum );
  14778.  
  14779.     // LTNOTE: this isn't true in delete cases.
  14780.     //XP_ASSERT( pLoStartLine == pLayoutElement );
  14781.  
  14782.     return iLineNum;
  14783. }
  14784.  
  14785. LO_Element* CEditBuffer::FirstElementOnLine(LO_Element* pTarget, int32* pLineNum){
  14786.     // LO_FirstElementOnLine does a binary search in Y to find the first element on a line for
  14787.     // a given (x,y) position. If the y coordinate of the position is right at the
  14788.     // top of a given line, and the previous line has an image, it is possible for
  14789.     // the search to return the previous line rather than the line than the
  14790.     // correct line. See bug 76945. A work-around is to start the search in the center of
  14791.     // the current element. (We don't calculate the exact center, since we don't
  14792.     // take the margin of an image into account, but it's close enough. Actually, probably just
  14793.     // adding 1 to the Y coordinate would be enough, but this way seems safer.
  14794.     //
  14795.     // I don't know why we don't just start at the current element and work
  14796.     // backwards through the linked list to find the first element on the line. That should
  14797.     // be faster and more accurate. ltabb might know why we do it this way instead.
  14798.  
  14799.     int32 x = pTarget->lo_any.x + pTarget->lo_any.width / 2;
  14800.     int32 y = pTarget->lo_any.y + pTarget->lo_any.height / 2;
  14801.     return LO_FirstElementOnLine(m_pContext, x, y, pLineNum);
  14802. }
  14803.  
  14804. void CEditBuffer::ChangeEncoding(int16 csid) {
  14805.     // NOTE: We no longer need to check and warn user if doc is
  14806.     // dirty because we are not reloading buffer from a URL.
  14807.     // Causes doc to be reread from temporary internal buffer.
  14808.  
  14809.     ForceDocCharSetID(csid); // Will be translated to this id when saved
  14810.     // Note: Why doesn't this allow Undo to work?
  14811.     BeginBatchChanges(0); // Marks document as dirty.
  14812.     SetEncoding(csid); // Will claim to be this id when saved.
  14813.     EndBatchChanges();
  14814.     CEditDocState *pState = RecordState(); // Actually translates.
  14815.     if (pState) {
  14816.         RestoreState(pState);
  14817.         // At this point this is deleted
  14818.         delete pState;
  14819.     }
  14820.     else {
  14821.         XP_ASSERT(0);
  14822.     }
  14823. }
  14824.  
  14825. // Add content-type meta-data. This tells the reader which character set was used to create the document.
  14826. // See RFC 2070, "Internationalization of the Hypertext Markup Language"
  14827. // http://ds.internic.net/rfc/rfc2070.txt
  14828. // <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-2022-JP">
  14829. void CEditBuffer::SetEncoding(int16 csid) {
  14830.      char contentValue[200];
  14831.     char charSet[100];
  14832.     int16 plainCSID = csid & ~CS_AUTO;
  14833.     INTL_CharSetIDToName(plainCSID, charSet);
  14834.     XP_SPRINTF(contentValue, "text/html; charset=%.100s", charSet);
  14835.  
  14836.     EDT_MetaData *pData = MakeMetaData( TRUE, CONTENT_TYPE, contentValue);
  14837.  
  14838.     SetMetaData( pData );
  14839.     FreeMetaData( pData );
  14840. }
  14841.  
  14842. XP_Bool CEditBuffer::HasEncoding() {
  14843.     return FindMetaData(TRUE, CONTENT_TYPE) >= 0;
  14844. }
  14845.  
  14846. // Used for QA only - Ctrl+Alt+Shift+N accelerator for automated testing
  14847. void EDT_SelectNextNonTextObject(MWContext *pContext)
  14848. {
  14849.     GET_EDIT_BUF_OR_RETURN(pContext, pEditBuffer);
  14850.     pEditBuffer->SelectNextNonTextObject();
  14851. }
  14852.  
  14853. void CEditBuffer::SelectNextNonTextObject()
  14854. {
  14855.     CEditLeafElement *pElement;
  14856.     ElementOffset iOffset;
  14857.     XP_Bool bStickyAfter;
  14858.  
  14859.     GetInsertPoint( &pElement, &iOffset, &bStickyAfter );
  14860.     XP_Bool bSelectObject = FALSE;
  14861.     XP_Bool bDone = FALSE;
  14862.  
  14863.     while( !bDone )
  14864.     {
  14865.         if( m_pSelectedEdTable )
  14866.         {
  14867.             // Last item selected was a table - clear it
  14868.             ClearTableAndCellSelection();
  14869.         } else {
  14870.             pElement = (CEditLeafElement*)pElement->FindNextElement(&CEditElement::FindLeafAll,0);
  14871.         }
  14872.  
  14873.         // No more to find
  14874.         if( ! pElement )
  14875.             return;
  14876.  
  14877.         // Test if we are inside a table
  14878.         CEditTableElement *pTable = pElement->GetParentTable();
  14879.  
  14880.         if( pTable && pTable != m_pNonTextSelectedTable )
  14881.         {
  14882.             // We found a table -- select it
  14883.             LO_TableStruct *pLoTable = pTable->GetLoTable();
  14884.             if( pLoTable )
  14885.             {
  14886.                 SelectTable(TRUE, pLoTable, pTable);
  14887.             }
  14888.             // That's all we need to do
  14889.             bDone = TRUE;
  14890.         }
  14891.         m_pNonTextSelectedTable = pTable;    
  14892.  
  14893.         if( !pTable )
  14894.         {
  14895.             EEditElementType eType = pElement->GetElementType();
  14896.             if( eType == eImageElement ||
  14897.                 eType == eHorizRuleElement ||
  14898.                 eType == eTargetElement )
  14899.             {
  14900.                 bSelectObject = TRUE;
  14901.                 bDone = TRUE;
  14902.             }
  14903.         }
  14904.     }
  14905.     
  14906.     if( pElement )
  14907.     {
  14908.         CEditInsertPoint ip(pElement, 0);
  14909.         SetInsertPoint(ip);
  14910.         if( bSelectObject )
  14911.         {
  14912.             LO_Element *pLoElement = pElement->GetLayoutElement();
  14913.             if( pLoElement )
  14914.             {
  14915.                 StartSelection( pLoElement->lo_any.x+5, pLoElement->lo_any.y+1 );
  14916.             }
  14917.         }
  14918.     }
  14919.     
  14920. }
  14921.  
  14922. #ifdef XP_WIN16
  14923. // code segment is full, switch to a new segment
  14924. #pragma code_seg("EDTSAVE3_TEXT","CODE")
  14925. #endif
  14926.  
  14927. #endif
  14928.